mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-25 14:39:12 +02:00
Compare commits
49 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
77bb5b7891 | ||
|
4b2944c39b | ||
|
4c29e6fbab | ||
|
a454e9d927 | ||
|
d2241f1931 | ||
|
0cd0dcd811 | ||
|
1c18812f47 | ||
|
28885db586 | ||
|
d423ae0a29 | ||
|
303a0a67d0 | ||
|
01b4cb9919 | ||
|
17b5bae86a | ||
|
6189c93f3d | ||
|
1de484262c | ||
|
b8d0ec29b2 | ||
|
79a47d133a | ||
|
c8bb4949b1 | ||
|
f587cb0dcc | ||
|
ea086fe33f | ||
|
c66cc73374 | ||
|
5a1dcf5079 | ||
|
e16dd58184 | ||
|
395e5a5639 | ||
|
ec939065bd | ||
|
77578cd0a4 | ||
|
04087a74b0 | ||
|
6701403745 | ||
|
95c62f92ac | ||
|
0f54be93b7 | ||
|
8db134843f | ||
|
06e83c6154 | ||
|
cbfbec4dc3 | ||
|
386ac87d21 | ||
|
533aa97011 | ||
|
d118b70b30 | ||
|
aa8ac7bbb8 | ||
|
ad71e18e02 | ||
|
a00c7cb639 | ||
|
112520cf45 | ||
|
11d6f09359 | ||
|
3ed42af8a1 | ||
|
482b4119f6 | ||
|
f1d6325bcd | ||
|
e48671694e | ||
|
338f13e57b | ||
|
6cbc312f11 | ||
|
7a96321e78 | ||
|
6cfb9e89e7 | ||
|
a37ce1cb2f |
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
purify/*.o
|
||||
purify/purify
|
||||
purify/analyze-gba
|
@@ -1,10 +1,12 @@
|
||||
include nall/Makefile
|
||||
|
||||
nes := nes
|
||||
nes := nes
|
||||
snes := snes
|
||||
gameboy := gameboy
|
||||
gb := gb
|
||||
gba := gba
|
||||
|
||||
profile := accuracy
|
||||
ui := ui
|
||||
target := ui
|
||||
|
||||
# options += console
|
||||
|
||||
@@ -12,7 +14,7 @@ ui := ui
|
||||
c := $(compiler) -std=gnu99
|
||||
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
|
||||
flags := -I. -O3 -fomit-frame-pointer
|
||||
link :=
|
||||
link := -s
|
||||
objects := libco
|
||||
|
||||
# profile-guided optimization mode
|
||||
@@ -28,16 +30,19 @@ endif
|
||||
|
||||
# platform
|
||||
ifeq ($(platform),x)
|
||||
link += -s -ldl -lX11 -lXext
|
||||
flags += -march=native
|
||||
link += -ldl -lX11 -lXext
|
||||
else ifeq ($(platform),osx)
|
||||
else ifeq ($(platform),win)
|
||||
link += $(if $(findstring console,$(options)),-mconsole,-mwindows)
|
||||
link += -mthreads -s -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32
|
||||
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 \
|
||||
@@ -88,7 +93,8 @@ sync:
|
||||
rm -r phoenix/nall
|
||||
rm -r phoenix/test
|
||||
|
||||
archive-all:
|
||||
tar -cjf bsnes.tar.bz2 base data gameboy libco nall nes obj out phoenix ruby snes ui ui-debugger ui-libsnes Makefile cc.bat clean.bat
|
||||
archive:
|
||||
if [ -f bsnes.tar.bz2 ]; then rm bsnes.tar.bz2; fi
|
||||
tar -cjf bsnes.tar.bz2 `ls`
|
||||
|
||||
help:;
|
||||
|
@@ -1,12 +1,13 @@
|
||||
#ifndef BASE_HPP
|
||||
#define BASE_HPP
|
||||
|
||||
const char Version[] = "086";
|
||||
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>
|
||||
@@ -55,15 +56,15 @@ template<typename R, typename... P> struct hook<R (P...)> {
|
||||
#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 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;
|
||||
@@ -89,15 +90,15 @@ 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 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;
|
||||
@@ -121,8 +122,9 @@ 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 varuint;
|
||||
typedef varuint_t<unsigned> varuint;
|
||||
|
||||
#endif
|
||||
|
@@ -1,18 +0,0 @@
|
||||
options += gameboy
|
||||
|
||||
gameboy_objects := gameboy-interface gameboy-system gameboy-scheduler
|
||||
gameboy_objects += gameboy-memory gameboy-cartridge
|
||||
gameboy_objects += gameboy-cpu gameboy-apu gameboy-lcd
|
||||
gameboy_objects += gameboy-cheat gameboy-video
|
||||
objects += $(gameboy_objects)
|
||||
|
||||
obj/gameboy-interface.o: $(gameboy)/interface/interface.cpp $(call rwildcard,$(gameboy)/interface/)
|
||||
obj/gameboy-system.o: $(gameboy)/system/system.cpp $(call rwildcard,$(gameboy)/system/)
|
||||
obj/gameboy-scheduler.o: $(gameboy)/scheduler/scheduler.cpp $(call rwildcard,$(gameboy)/scheduler/)
|
||||
obj/gameboy-cartridge.o: $(gameboy)/cartridge/cartridge.cpp $(call rwildcard,$(gameboy)/cartridge/)
|
||||
obj/gameboy-memory.o: $(gameboy)/memory/memory.cpp $(call rwildcard,$(gameboy)/memory/)
|
||||
obj/gameboy-cpu.o: $(gameboy)/cpu/cpu.cpp $(call rwildcard,$(gameboy)/cpu/)
|
||||
obj/gameboy-apu.o: $(gameboy)/apu/apu.cpp $(call rwildcard,$(gameboy)/apu/)
|
||||
obj/gameboy-lcd.o: $(gameboy)/lcd/lcd.cpp $(call rwildcard,$(gameboy)/lcd/)
|
||||
obj/gameboy-cheat.o: $(gameboy)/cheat/cheat.cpp $(call rwildcard,$(gameboy)/cheat/)
|
||||
obj/gameboy-video.o: $(gameboy)/video/video.cpp $(call rwildcard,$(gameboy)/video/)
|
@@ -1,60 +0,0 @@
|
||||
#ifndef GAMEBOY_HPP
|
||||
#define GAMEBOY_HPP
|
||||
|
||||
#include <base/base.hpp>
|
||||
|
||||
namespace GameBoy {
|
||||
namespace Info {
|
||||
static const char Name[] = "bgameboy";
|
||||
static const unsigned SerializerVersion = 3;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
bgameboy - Game Boy, Super Game Boy, and Game Boy Color emulator
|
||||
author: byuu
|
||||
license: GPLv3
|
||||
project started: 2010-12-27
|
||||
*/
|
||||
|
||||
#include <libco/libco.h>
|
||||
#include <nall/gameboy/cartridge.hpp>
|
||||
|
||||
namespace GameBoy {
|
||||
struct Processor {
|
||||
cothread_t thread;
|
||||
unsigned frequency;
|
||||
int64 clock;
|
||||
|
||||
inline void create(void (*entrypoint)(), unsigned frequency) {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(65536 * sizeof(void*), entrypoint);
|
||||
this->frequency = frequency;
|
||||
clock = 0;
|
||||
}
|
||||
|
||||
inline void serialize(serializer &s) {
|
||||
s.integer(frequency);
|
||||
s.integer(clock);
|
||||
}
|
||||
|
||||
inline Processor() : thread(nullptr) {
|
||||
}
|
||||
|
||||
inline ~Processor() {
|
||||
if(thread) co_delete(thread);
|
||||
}
|
||||
};
|
||||
|
||||
#include <gameboy/memory/memory.hpp>
|
||||
#include <gameboy/system/system.hpp>
|
||||
#include <gameboy/scheduler/scheduler.hpp>
|
||||
#include <gameboy/cartridge/cartridge.hpp>
|
||||
#include <gameboy/cpu/cpu.hpp>
|
||||
#include <gameboy/apu/apu.hpp>
|
||||
#include <gameboy/lcd/lcd.hpp>
|
||||
#include <gameboy/cheat/cheat.hpp>
|
||||
#include <gameboy/video/video.hpp>
|
||||
};
|
||||
|
||||
#endif
|
@@ -1,135 +0,0 @@
|
||||
#ifdef SYSTEM_CPP
|
||||
|
||||
//SHA256 = 4bf5021be357ce523a59ac5f4efff5d6371ae50112a6db0adf4a75916ad760a9
|
||||
const uint8_t System::BootROM::cgb[2048] = {
|
||||
0x31,0xfe,0xff,0x3e,0x02,0xc3,0x7c,0x00,0xd3,0x00,0x98,0xa0,0x12,0xd3,0x00,0x80,
|
||||
0x00,0x40,0x1e,0x53,0xd0,0x00,0x1f,0x42,0x1c,0x00,0x14,0x2a,0x4d,0x19,0x8c,0x7e,
|
||||
0x00,0x7c,0x31,0x6e,0x4a,0x45,0x52,0x4a,0x00,0x00,0xff,0x53,0x1f,0x7c,0xff,0x03,
|
||||
0x1f,0x00,0xff,0x1f,0xa7,0x00,0xef,0x1b,0x1f,0x00,0xef,0x1b,0x00,0x7c,0x00,0x00,
|
||||
0xff,0x03,0xce,0xed,0x66,0x66,0xcc,0x0d,0x00,0x0b,0x03,0x73,0x00,0x83,0x00,0x0c,
|
||||
0x00,0x0d,0x00,0x08,0x11,0x1f,0x88,0x89,0x00,0x0e,0xdc,0xcc,0x6e,0xe6,0xdd,0xdd,
|
||||
0xd9,0x99,0xbb,0xbb,0x67,0x63,0x6e,0x0e,0xec,0xcc,0xdd,0xdc,0x99,0x9f,0xbb,0xb9,
|
||||
0x33,0x3e,0x3c,0x42,0xb9,0xa5,0xb9,0xa5,0x42,0x3c,0x58,0x43,0xe0,0x70,0x3e,0xfc,
|
||||
0xe0,0x47,0xcd,0x75,0x02,0xcd,0x00,0x02,0x26,0xd0,0xcd,0x03,0x02,0x21,0x00,0xfe,
|
||||
0x0e,0xa0,0xaf,0x22,0x0d,0x20,0xfc,0x11,0x04,0x01,0x21,0x10,0x80,0x4c,0x1a,0xe2,
|
||||
0x0c,0xcd,0xc6,0x03,0xcd,0xc7,0x03,0x13,0x7b,0xfe,0x34,0x20,0xf1,0x11,0x72,0x00,
|
||||
0x06,0x08,0x1a,0x13,0x22,0x23,0x05,0x20,0xf9,0xcd,0xf0,0x03,0x3e,0x01,0xe0,0x4f,
|
||||
0x3e,0x91,0xe0,0x40,0x21,0xb2,0x98,0x06,0x4e,0x0e,0x44,0xcd,0x91,0x02,0xaf,0xe0,
|
||||
0x4f,0x0e,0x80,0x21,0x42,0x00,0x06,0x18,0xf2,0x0c,0xbe,0x20,0xfe,0x23,0x05,0x20,
|
||||
0xf7,0x21,0x34,0x01,0x06,0x19,0x78,0x86,0x2c,0x05,0x20,0xfb,0x86,0x20,0xfe,0xcd,
|
||||
0x1c,0x03,0x18,0x02,0x00,0x00,0xcd,0xd0,0x05,0xaf,0xe0,0x70,0x3e,0x11,0xe0,0x50,
|
||||
0x21,0x00,0x80,0xaf,0x22,0xcb,0x6c,0x28,0xfb,0xc9,0x2a,0x12,0x13,0x0d,0x20,0xfa,
|
||||
0xc9,0xe5,0x21,0x0f,0xff,0xcb,0x86,0xcb,0x46,0x28,0xfc,0xe1,0xc9,0x11,0x00,0xff,
|
||||
0x21,0x03,0xd0,0x0e,0x0f,0x3e,0x30,0x12,0x3e,0x20,0x12,0x1a,0x2f,0xa1,0xcb,0x37,
|
||||
0x47,0x3e,0x10,0x12,0x1a,0x2f,0xa1,0xb0,0x4f,0x7e,0xa9,0xe6,0xf0,0x47,0x2a,0xa9,
|
||||
0xa1,0xb0,0x32,0x47,0x79,0x77,0x3e,0x30,0x12,0xc9,0x3e,0x80,0xe0,0x68,0xe0,0x6a,
|
||||
0x0e,0x6b,0x2a,0xe2,0x05,0x20,0xfb,0x4a,0x09,0x43,0x0e,0x69,0x2a,0xe2,0x05,0x20,
|
||||
0xfb,0xc9,0xc5,0xd5,0xe5,0x21,0x00,0xd8,0x06,0x01,0x16,0x3f,0x1e,0x40,0xcd,0x4a,
|
||||
0x02,0xe1,0xd1,0xc1,0xc9,0x3e,0x80,0xe0,0x26,0xe0,0x11,0x3e,0xf3,0xe0,0x12,0xe0,
|
||||
0x25,0x3e,0x77,0xe0,0x24,0x21,0x30,0xff,0xaf,0x0e,0x10,0x22,0x2f,0x0d,0x20,0xfb,
|
||||
0xc9,0xcd,0x11,0x02,0xcd,0x62,0x02,0x79,0xfe,0x38,0x20,0x14,0xe5,0xaf,0xe0,0x4f,
|
||||
0x21,0xa7,0x99,0x3e,0x38,0x22,0x3c,0xfe,0x3f,0x20,0xfa,0x3e,0x01,0xe0,0x4f,0xe1,
|
||||
0xc5,0xe5,0x21,0x43,0x01,0xcb,0x7e,0xcc,0x89,0x05,0xe1,0xc1,0xcd,0x11,0x02,0x79,
|
||||
0xd6,0x30,0xd2,0x06,0x03,0x79,0xfe,0x01,0xca,0x06,0x03,0x7d,0xfe,0xd1,0x28,0x21,
|
||||
0xc5,0x06,0x03,0x0e,0x01,0x16,0x03,0x7e,0xe6,0xf8,0xb1,0x22,0x15,0x20,0xf8,0x0c,
|
||||
0x79,0xfe,0x06,0x20,0xf0,0x11,0x11,0x00,0x19,0x05,0x20,0xe7,0x11,0xa1,0xff,0x19,
|
||||
0xc1,0x04,0x78,0x1e,0x83,0xfe,0x62,0x28,0x06,0x1e,0xc1,0xfe,0x64,0x20,0x07,0x7b,
|
||||
0xe0,0x13,0x3e,0x87,0xe0,0x14,0xfa,0x02,0xd0,0xfe,0x00,0x28,0x0a,0x3d,0xea,0x02,
|
||||
0xd0,0x79,0xfe,0x01,0xca,0x91,0x02,0x0d,0xc2,0x91,0x02,0xc9,0x0e,0x26,0xcd,0x4a,
|
||||
0x03,0xcd,0x11,0x02,0xcd,0x62,0x02,0x0d,0x20,0xf4,0xcd,0x11,0x02,0x3e,0x01,0xe0,
|
||||
0x4f,0xcd,0x3e,0x03,0xcd,0x41,0x03,0xaf,0xe0,0x4f,0xcd,0x3e,0x03,0xc9,0x21,0x08,
|
||||
0x00,0x11,0x51,0xff,0x0e,0x05,0xcd,0x0a,0x02,0xc9,0xc5,0xd5,0xe5,0x21,0x40,0xd8,
|
||||
0x0e,0x20,0x7e,0xe6,0x1f,0xfe,0x1f,0x28,0x01,0x3c,0x57,0x2a,0x07,0x07,0x07,0xe6,
|
||||
0x07,0x47,0x3a,0x07,0x07,0x07,0xe6,0x18,0xb0,0xfe,0x1f,0x28,0x01,0x3c,0x0f,0x0f,
|
||||
0x0f,0x47,0xe6,0xe0,0xb2,0x22,0x78,0xe6,0x03,0x5f,0x7e,0x0f,0x0f,0xe6,0x1f,0xfe,
|
||||
0x1f,0x28,0x01,0x3c,0x07,0x07,0xb3,0x22,0x0d,0x20,0xc7,0xe1,0xd1,0xc1,0xc9,0x0e,
|
||||
0x00,0x1a,0xe6,0xf0,0xcb,0x49,0x28,0x02,0xcb,0x37,0x47,0x23,0x7e,0xb0,0x22,0x1a,
|
||||
0xe6,0x0f,0xcb,0x49,0x20,0x02,0xcb,0x37,0x47,0x23,0x7e,0xb0,0x22,0x13,0xcb,0x41,
|
||||
0x28,0x0d,0xd5,0x11,0xf8,0xff,0xcb,0x49,0x28,0x03,0x11,0x08,0x00,0x19,0xd1,0x0c,
|
||||
0x79,0xfe,0x18,0x20,0xcc,0xc9,0x47,0xd5,0x16,0x04,0x58,0xcb,0x10,0x17,0xcb,0x13,
|
||||
0x17,0x15,0x20,0xf6,0xd1,0x22,0x23,0x22,0x23,0xc9,0x3e,0x19,0xea,0x10,0x99,0x21,
|
||||
0x2f,0x99,0x0e,0x0c,0x3d,0x28,0x08,0x32,0x0d,0x20,0xf9,0x2e,0x0f,0x18,0xf3,0xc9,
|
||||
0x3e,0x01,0xe0,0x4f,0xcd,0x00,0x02,0x11,0x07,0x06,0x21,0x80,0x80,0x0e,0xc0,0x1a,
|
||||
0x22,0x23,0x22,0x23,0x13,0x0d,0x20,0xf7,0x11,0x04,0x01,0xcd,0x8f,0x03,0x01,0xa8,
|
||||
0xff,0x09,0xcd,0x8f,0x03,0x01,0xf8,0xff,0x09,0x11,0x72,0x00,0x0e,0x08,0x23,0x1a,
|
||||
0x22,0x13,0x0d,0x20,0xf9,0x21,0xc2,0x98,0x06,0x08,0x3e,0x08,0x0e,0x10,0x22,0x0d,
|
||||
0x20,0xfc,0x11,0x10,0x00,0x19,0x05,0x20,0xf3,0xaf,0xe0,0x4f,0x21,0xc2,0x98,0x3e,
|
||||
0x08,0x22,0x3c,0xfe,0x18,0x20,0x02,0x2e,0xe2,0xfe,0x28,0x20,0x03,0x21,0x02,0x99,
|
||||
0xfe,0x38,0x20,0xed,0x21,0xd8,0x08,0x11,0x40,0xd8,0x06,0x08,0x3e,0xff,0x12,0x13,
|
||||
0x12,0x13,0x0e,0x02,0xcd,0x0a,0x02,0x3e,0x00,0x12,0x13,0x12,0x13,0x13,0x13,0x05,
|
||||
0x20,0xea,0xcd,0x62,0x02,0x21,0x4b,0x01,0x7e,0xfe,0x33,0x20,0x0b,0x2e,0x44,0x1e,
|
||||
0x30,0x2a,0xbb,0x20,0x49,0x1c,0x18,0x04,0x2e,0x4b,0x1e,0x01,0x2a,0xbb,0x20,0x3e,
|
||||
0x2e,0x34,0x01,0x10,0x00,0x2a,0x80,0x47,0x0d,0x20,0xfa,0xea,0x00,0xd0,0x21,0xc7,
|
||||
0x06,0x0e,0x00,0x2a,0xb8,0x28,0x08,0x0c,0x79,0xfe,0x4f,0x20,0xf6,0x18,0x1f,0x79,
|
||||
0xd6,0x41,0x38,0x1c,0x21,0x16,0x07,0x16,0x00,0x5f,0x19,0xfa,0x37,0x01,0x57,0x7e,
|
||||
0xba,0x28,0x0d,0x11,0x0e,0x00,0x19,0x79,0x83,0x4f,0xd6,0x5e,0x38,0xed,0x0e,0x00,
|
||||
0x21,0x33,0x07,0x06,0x00,0x09,0x7e,0xe6,0x1f,0xea,0x08,0xd0,0x7e,0xe6,0xe0,0x07,
|
||||
0x07,0x07,0xea,0x0b,0xd0,0xcd,0xe9,0x04,0xc9,0x11,0x91,0x07,0x21,0x00,0xd9,0xfa,
|
||||
0x0b,0xd0,0x47,0x0e,0x1e,0xcb,0x40,0x20,0x02,0x13,0x13,0x1a,0x22,0x20,0x02,0x1b,
|
||||
0x1b,0xcb,0x48,0x20,0x02,0x13,0x13,0x1a,0x22,0x13,0x13,0x20,0x02,0x1b,0x1b,0xcb,
|
||||
0x50,0x28,0x05,0x1b,0x2b,0x1a,0x22,0x13,0x1a,0x22,0x13,0x0d,0x20,0xd7,0x21,0x00,
|
||||
0xd9,0x11,0x00,0xda,0xcd,0x64,0x05,0xc9,0x21,0x12,0x00,0xfa,0x05,0xd0,0x07,0x07,
|
||||
0x06,0x00,0x4f,0x09,0x11,0x40,0xd8,0x06,0x08,0xe5,0x0e,0x02,0xcd,0x0a,0x02,0x13,
|
||||
0x13,0x13,0x13,0x13,0x13,0xe1,0x05,0x20,0xf0,0x11,0x42,0xd8,0x0e,0x02,0xcd,0x0a,
|
||||
0x02,0x11,0x4a,0xd8,0x0e,0x02,0xcd,0x0a,0x02,0x2b,0x2b,0x11,0x44,0xd8,0x0e,0x02,
|
||||
0xcd,0x0a,0x02,0xc9,0x0e,0x60,0x2a,0xe5,0xc5,0x21,0xe8,0x07,0x06,0x00,0x4f,0x09,
|
||||
0x0e,0x08,0xcd,0x0a,0x02,0xc1,0xe1,0x0d,0x20,0xec,0xc9,0xfa,0x08,0xd0,0x11,0x18,
|
||||
0x00,0x3c,0x3d,0x28,0x03,0x19,0x20,0xfa,0xc9,0xcd,0x1d,0x02,0x78,0xe6,0xff,0x28,
|
||||
0x0f,0x21,0xe4,0x08,0x06,0x00,0x2a,0xb9,0x28,0x08,0x04,0x78,0xfe,0x0c,0x20,0xf6,
|
||||
0x18,0x2d,0x78,0xea,0x05,0xd0,0x3e,0x1e,0xea,0x02,0xd0,0x11,0x0b,0x00,0x19,0x56,
|
||||
0x7a,0xe6,0x1f,0x5f,0x21,0x08,0xd0,0x3a,0x22,0x7b,0x77,0x7a,0xe6,0xe0,0x07,0x07,
|
||||
0x07,0x5f,0x21,0x0b,0xd0,0x3a,0x22,0x7b,0x77,0xcd,0xe9,0x04,0xcd,0x28,0x05,0xc9,
|
||||
0xcd,0x11,0x02,0xfa,0x43,0x01,0xcb,0x7f,0x28,0x04,0xe0,0x4c,0x18,0x28,0x3e,0x04,
|
||||
0xe0,0x4c,0x3e,0x01,0xe0,0x6c,0x21,0x00,0xda,0xcd,0x7b,0x05,0x06,0x10,0x16,0x00,
|
||||
0x1e,0x08,0xcd,0x4a,0x02,0x21,0x7a,0x00,0xfa,0x00,0xd0,0x47,0x0e,0x02,0x2a,0xb8,
|
||||
0xcc,0xda,0x03,0x0d,0x20,0xf8,0xc9,0x01,0x0f,0x3f,0x7e,0xff,0xff,0xc0,0x00,0xc0,
|
||||
0xf0,0xf1,0x03,0x7c,0xfc,0xfe,0xfe,0x03,0x07,0x07,0x0f,0xe0,0xe0,0xf0,0xf0,0x1e,
|
||||
0x3e,0x7e,0xfe,0x0f,0x0f,0x1f,0x1f,0xff,0xff,0x00,0x00,0x01,0x01,0x01,0x03,0xff,
|
||||
0xff,0xe1,0xe0,0xc0,0xf0,0xf9,0xfb,0x1f,0x7f,0xf8,0xe0,0xf3,0xfd,0x3e,0x1e,0xe0,
|
||||
0xf0,0xf9,0x7f,0x3e,0x7c,0xf8,0xe0,0xf8,0xf0,0xf0,0xf8,0x00,0x00,0x7f,0x7f,0x07,
|
||||
0x0f,0x9f,0xbf,0x9e,0x1f,0xff,0xff,0x0f,0x1e,0x3e,0x3c,0xf1,0xfb,0x7f,0x7f,0xfe,
|
||||
0xde,0xdf,0x9f,0x1f,0x3f,0x3e,0x3c,0xf8,0xf8,0x00,0x00,0x03,0x03,0x07,0x07,0xff,
|
||||
0xff,0xc1,0xc0,0xf3,0xe7,0xf7,0xf3,0xc0,0xc0,0xc0,0xc0,0x1f,0x1f,0x1e,0x3e,0x3f,
|
||||
0x1f,0x3e,0x3e,0x80,0x00,0x00,0x00,0x7c,0x1f,0x07,0x00,0x0f,0xff,0xfe,0x00,0x7c,
|
||||
0xf8,0xf0,0x00,0x1f,0x0f,0x0f,0x00,0x7c,0xf8,0xf8,0x00,0x3f,0x3e,0x1c,0x00,0x0f,
|
||||
0x0f,0x0f,0x00,0x7c,0xff,0xff,0x00,0x00,0xf8,0xf8,0x00,0x07,0x0f,0x0f,0x00,0x81,
|
||||
0xff,0xff,0x00,0xf3,0xe1,0x80,0x00,0xe0,0xff,0x7f,0x00,0xfc,0xf0,0xc0,0x00,0x3e,
|
||||
0x7c,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x88,0x16,0x36,0xd1,0xdb,0xf2,0x3c,0x8c,
|
||||
0x92,0x3d,0x5c,0x58,0xc9,0x3e,0x70,0x1d,0x59,0x69,0x19,0x35,0xa8,0x14,0xaa,0x75,
|
||||
0x95,0x99,0x34,0x6f,0x15,0xff,0x97,0x4b,0x90,0x17,0x10,0x39,0xf7,0xf6,0xa2,0x49,
|
||||
0x4e,0x43,0x68,0xe0,0x8b,0xf0,0xce,0x0c,0x29,0xe8,0xb7,0x86,0x9a,0x52,0x01,0x9d,
|
||||
0x71,0x9c,0xbd,0x5d,0x6d,0x67,0x3f,0x6b,0xb3,0x46,0x28,0xa5,0xc6,0xd3,0x27,0x61,
|
||||
0x18,0x66,0x6a,0xbf,0x0d,0xf4,0x42,0x45,0x46,0x41,0x41,0x52,0x42,0x45,0x4b,0x45,
|
||||
0x4b,0x20,0x52,0x2d,0x55,0x52,0x41,0x52,0x20,0x49,0x4e,0x41,0x49,0x4c,0x49,0x43,
|
||||
0x45,0x20,0x52,0x7c,0x08,0x12,0xa3,0xa2,0x07,0x87,0x4b,0x20,0x12,0x65,0xa8,0x16,
|
||||
0xa9,0x86,0xb1,0x68,0xa0,0x87,0x66,0x12,0xa1,0x30,0x3c,0x12,0x85,0x12,0x64,0x1b,
|
||||
0x07,0x06,0x6f,0x6e,0x6e,0xae,0xaf,0x6f,0xb2,0xaf,0xb2,0xa8,0xab,0x6f,0xaf,0x86,
|
||||
0xae,0xa2,0xa2,0x12,0xaf,0x13,0x12,0xa1,0x6e,0xaf,0xaf,0xad,0x06,0x4c,0x6e,0xaf,
|
||||
0xaf,0x12,0x7c,0xac,0xa8,0x6a,0x6e,0x13,0xa0,0x2d,0xa8,0x2b,0xac,0x64,0xac,0x6d,
|
||||
0x87,0xbc,0x60,0xb4,0x13,0x72,0x7c,0xb5,0xae,0xae,0x7c,0x7c,0x65,0xa2,0x6c,0x64,
|
||||
0x85,0x80,0xb0,0x40,0x88,0x20,0x68,0xde,0x00,0x70,0xde,0x20,0x78,0x20,0x20,0x38,
|
||||
0x20,0xb0,0x90,0x20,0xb0,0xa0,0xe0,0xb0,0xc0,0x98,0xb6,0x48,0x80,0xe0,0x50,0x1e,
|
||||
0x1e,0x58,0x20,0xb8,0xe0,0x88,0xb0,0x10,0x20,0x00,0x10,0x20,0xe0,0x18,0xe0,0x18,
|
||||
0x00,0x18,0xe0,0x20,0xa8,0xe0,0x20,0x18,0xe0,0x00,0x20,0x18,0xd8,0xc8,0x18,0xe0,
|
||||
0x00,0xe0,0x40,0x28,0x28,0x28,0x18,0xe0,0x60,0x20,0x18,0xe0,0x00,0x00,0x08,0xe0,
|
||||
0x18,0x30,0xd0,0xd0,0xd0,0x20,0xe0,0xe8,0xff,0x7f,0xbf,0x32,0xd0,0x00,0x00,0x00,
|
||||
0x9f,0x63,0x79,0x42,0xb0,0x15,0xcb,0x04,0xff,0x7f,0x31,0x6e,0x4a,0x45,0x00,0x00,
|
||||
0xff,0x7f,0xef,0x1b,0x00,0x02,0x00,0x00,0xff,0x7f,0x1f,0x42,0xf2,0x1c,0x00,0x00,
|
||||
0xff,0x7f,0x94,0x52,0x4a,0x29,0x00,0x00,0xff,0x7f,0xff,0x03,0x2f,0x01,0x00,0x00,
|
||||
0xff,0x7f,0xef,0x03,0xd6,0x01,0x00,0x00,0xff,0x7f,0xb5,0x42,0xc8,0x3d,0x00,0x00,
|
||||
0x74,0x7e,0xff,0x03,0x80,0x01,0x00,0x00,0xff,0x67,0xac,0x77,0x13,0x1a,0x6b,0x2d,
|
||||
0xd6,0x7e,0xff,0x4b,0x75,0x21,0x00,0x00,0xff,0x53,0x5f,0x4a,0x52,0x7e,0x00,0x00,
|
||||
0xff,0x4f,0xd2,0x7e,0x4c,0x3a,0xe0,0x1c,0xed,0x03,0xff,0x7f,0x5f,0x25,0x00,0x00,
|
||||
0x6a,0x03,0x1f,0x02,0xff,0x03,0xff,0x7f,0xff,0x7f,0xdf,0x01,0x12,0x01,0x00,0x00,
|
||||
0x1f,0x23,0x5f,0x03,0xf2,0x00,0x09,0x00,0xff,0x7f,0xea,0x03,0x1f,0x01,0x00,0x00,
|
||||
0x9f,0x29,0x1a,0x00,0x0c,0x00,0x00,0x00,0xff,0x7f,0x7f,0x02,0x1f,0x00,0x00,0x00,
|
||||
0xff,0x7f,0xe0,0x03,0x06,0x02,0x20,0x01,0xff,0x7f,0xeb,0x7e,0x1f,0x00,0x00,0x7c,
|
||||
0xff,0x7f,0xff,0x3f,0x00,0x7e,0x1f,0x00,0xff,0x7f,0xff,0x03,0x1f,0x00,0x00,0x00,
|
||||
0xff,0x03,0x1f,0x00,0x0c,0x00,0x00,0x00,0xff,0x7f,0x3f,0x03,0x93,0x01,0x00,0x00,
|
||||
0x00,0x00,0x00,0x42,0x7f,0x03,0xff,0x7f,0xff,0x7f,0x8c,0x7e,0x00,0x7c,0x00,0x00,
|
||||
0xff,0x7f,0xef,0x1b,0x80,0x61,0x00,0x00,0xff,0x7f,0x00,0x7c,0xe0,0x03,0x1f,0x7c,
|
||||
0x1f,0x00,0xff,0x03,0x40,0x41,0x42,0x20,0x21,0x22,0x80,0x81,0x82,0x10,0x11,0x12,
|
||||
0x12,0xb0,0x79,0xb8,0xad,0x16,0x17,0x07,0xba,0x05,0x7c,0x13,0x00,0x00,0x00,0x00,
|
||||
};
|
||||
|
||||
#endif
|
@@ -1,23 +0,0 @@
|
||||
#ifdef SYSTEM_CPP
|
||||
|
||||
//SHA256 = cf053eccb4ccafff9e67339d4e78e98dce7d1ed59be819d2a1ba2232c6fce1c7
|
||||
const uint8_t System::BootROM::dmg[256] = {
|
||||
0x31,0xfe,0xff,0xaf,0x21,0xff,0x9f,0x32,0xcb,0x7c,0x20,0xfb,0x21,0x26,0xff,0x0e,
|
||||
0x11,0x3e,0x80,0x32,0xe2,0x0c,0x3e,0xf3,0xe2,0x32,0x3e,0x77,0x77,0x3e,0xfc,0xe0,
|
||||
0x47,0x11,0x04,0x01,0x21,0x10,0x80,0x1a,0xcd,0x95,0x00,0xcd,0x96,0x00,0x13,0x7b,
|
||||
0xfe,0x34,0x20,0xf3,0x11,0xd8,0x00,0x06,0x08,0x1a,0x13,0x22,0x23,0x05,0x20,0xf9,
|
||||
0x3e,0x19,0xea,0x10,0x99,0x21,0x2f,0x99,0x0e,0x0c,0x3d,0x28,0x08,0x32,0x0d,0x20,
|
||||
0xf9,0x2e,0x0f,0x18,0xf3,0x67,0x3e,0x64,0x57,0xe0,0x42,0x3e,0x91,0xe0,0x40,0x04,
|
||||
0x1e,0x02,0x0e,0x0c,0xf0,0x44,0xfe,0x90,0x20,0xfa,0x0d,0x20,0xf7,0x1d,0x20,0xf2,
|
||||
0x0e,0x13,0x24,0x7c,0x1e,0x83,0xfe,0x62,0x28,0x06,0x1e,0xc1,0xfe,0x64,0x20,0x06,
|
||||
0x7b,0xe2,0x0c,0x3e,0x87,0xe2,0xf0,0x42,0x90,0xe0,0x42,0x15,0x20,0xd2,0x05,0x20,
|
||||
0x4f,0x16,0x20,0x18,0xcb,0x4f,0x06,0x04,0xc5,0xcb,0x11,0x17,0xc1,0xcb,0x11,0x17,
|
||||
0x05,0x20,0xf5,0x22,0x23,0x22,0x23,0xc9,0xce,0xed,0x66,0x66,0xcc,0x0d,0x00,0x0b,
|
||||
0x03,0x73,0x00,0x83,0x00,0x0c,0x00,0x0d,0x00,0x08,0x11,0x1f,0x88,0x89,0x00,0x0e,
|
||||
0xdc,0xcc,0x6e,0xe6,0xdd,0xdd,0xd9,0x99,0xbb,0xbb,0x67,0x63,0x6e,0x0e,0xec,0xcc,
|
||||
0xdd,0xdc,0x99,0x9f,0xbb,0xb9,0x33,0x3e,0x3c,0x42,0xb9,0xa5,0xb9,0xa5,0x42,0x3c,
|
||||
0x21,0x04,0x01,0x11,0xa8,0x00,0x1a,0x13,0xbe,0x20,0xfe,0x23,0x7d,0xfe,0x34,0x20,
|
||||
0xf5,0x06,0x19,0x78,0x86,0x23,0x05,0x20,0xfb,0x86,0x20,0xfe,0x3e,0x01,0xe0,0x50,
|
||||
};
|
||||
|
||||
#endif
|
@@ -1,23 +0,0 @@
|
||||
#ifdef SYSTEM_CPP
|
||||
|
||||
//SHA256 = 0e4ddff32fc9d1eeaae812a157dd246459b00c9e14f2f61751f661f32361e360
|
||||
const uint8_t System::BootROM::sgb[256] = {
|
||||
0x31,0xfe,0xff,0x3e,0x30,0xe0,0x00,0xaf,0x21,0xff,0x9f,0x32,0xcb,0x7c,0x20,0xfb,
|
||||
0x21,0x26,0xff,0x0e,0x11,0x3e,0x80,0x32,0xe2,0x0c,0x3e,0xf3,0xe2,0x32,0x3e,0x77,
|
||||
0x77,0x3e,0xfc,0xe0,0x47,0x21,0x5f,0xc0,0x0e,0x08,0xaf,0x32,0x0d,0x20,0xfc,0x11,
|
||||
0x4f,0x01,0x3e,0xfb,0x0e,0x06,0xf5,0x06,0x00,0x1a,0x1b,0x32,0x80,0x47,0x0d,0x20,
|
||||
0xf8,0x32,0xf1,0x32,0x0e,0x0e,0xd6,0x02,0xfe,0xef,0x20,0xea,0x11,0x04,0x01,0x21,
|
||||
0x10,0x80,0x1a,0xcd,0xd3,0x00,0xcd,0xd4,0x00,0x13,0x7b,0xfe,0x34,0x20,0xf3,0x11,
|
||||
0xe6,0x00,0x06,0x08,0x1a,0x13,0x22,0x23,0x05,0x20,0xf9,0x3e,0x19,0xea,0x10,0x99,
|
||||
0x21,0x2f,0x99,0x0e,0x0c,0x3d,0x28,0x08,0x32,0x0d,0x20,0xf9,0x2e,0x0f,0x18,0xf3,
|
||||
0x3e,0x91,0xe0,0x40,0x21,0x00,0xc0,0x0e,0x00,0x3e,0x00,0xe2,0x3e,0x30,0xe2,0x06,
|
||||
0x10,0x1e,0x08,0x2a,0x57,0xcb,0x42,0x3e,0x10,0x20,0x02,0x3e,0x20,0xe2,0x3e,0x30,
|
||||
0xe2,0xcb,0x1a,0x1d,0x20,0xef,0x05,0x20,0xe8,0x3e,0x20,0xe2,0x3e,0x30,0xe2,0xcd,
|
||||
0xc2,0x00,0x7d,0xfe,0x60,0x20,0xd2,0x0e,0x13,0x3e,0xc1,0xe2,0x0c,0x3e,0x07,0xe2,
|
||||
0x18,0x3a,0x16,0x04,0xf0,0x44,0xfe,0x90,0x20,0xfa,0x1e,0x00,0x1d,0x20,0xfd,0x15,
|
||||
0x20,0xf2,0xc9,0x4f,0x06,0x04,0xc5,0xcb,0x11,0x17,0xc1,0xcb,0x11,0x17,0x05,0x20,
|
||||
0xf5,0x22,0x23,0x22,0x23,0xc9,0x3c,0x42,0xb9,0xa5,0xb9,0xa5,0x42,0x3c,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3e,0x01,0xe0,0x50,
|
||||
};
|
||||
|
||||
#endif
|
18
bsnes/gb/Makefile
Executable file
18
bsnes/gb/Makefile
Executable file
@@ -0,0 +1,18 @@
|
||||
options += gameboy
|
||||
|
||||
gb_objects := gb-interface gb-system gb-scheduler
|
||||
gb_objects += gb-memory gb-cartridge
|
||||
gb_objects += gb-cpu gb-apu gb-lcd
|
||||
gb_objects += gb-cheat gb-video
|
||||
objects += $(gb_objects)
|
||||
|
||||
obj/gb-interface.o: $(gb)/interface/interface.cpp $(call rwildcard,$(gb)/interface/)
|
||||
obj/gb-system.o: $(gb)/system/system.cpp $(call rwildcard,$(gb)/system/)
|
||||
obj/gb-scheduler.o: $(gb)/scheduler/scheduler.cpp $(call rwildcard,$(gb)/scheduler/)
|
||||
obj/gb-cartridge.o: $(gb)/cartridge/cartridge.cpp $(call rwildcard,$(gb)/cartridge/)
|
||||
obj/gb-memory.o: $(gb)/memory/memory.cpp $(call rwildcard,$(gb)/memory/)
|
||||
obj/gb-cpu.o: $(gb)/cpu/cpu.cpp $(call rwildcard,$(gb)/cpu/)
|
||||
obj/gb-apu.o: $(gb)/apu/apu.cpp $(call rwildcard,$(gb)/apu/)
|
||||
obj/gb-lcd.o: $(gb)/lcd/lcd.cpp $(call rwildcard,$(gb)/lcd/)
|
||||
obj/gb-cheat.o: $(gb)/cheat/cheat.cpp $(call rwildcard,$(gb)/cheat/)
|
||||
obj/gb-video.o: $(gb)/video/video.cpp $(call rwildcard,$(gb)/video/)
|
@@ -1,7 +1,7 @@
|
||||
#include <gameboy/gameboy.hpp>
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
#define APU_CPP
|
||||
namespace GameBoy {
|
||||
namespace GB {
|
||||
|
||||
#include "square1/square1.cpp"
|
||||
#include "square2/square2.cpp"
|
||||
@@ -49,7 +49,7 @@ void APU::main() {
|
||||
interface->audioSample(master.center, master.left, master.right);
|
||||
|
||||
clock += 1 * cpu.frequency;
|
||||
if(clock >= 0) co_switch(scheduler.active_thread = cpu.thread);
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(scheduler.active_thread = cpu.thread);
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
struct APU : Processor, MMIO {
|
||||
struct APU : Thread, MMIO {
|
||||
#include "square1/square1.hpp"
|
||||
#include "square2/square2.hpp"
|
||||
#include "wave/wave.hpp"
|
@@ -20,36 +20,18 @@ void APU::Master::run() {
|
||||
if(channel2_left_enable) sample += apu.square2.output;
|
||||
if(channel3_left_enable) sample += apu.wave.output;
|
||||
if(channel4_left_enable) sample += apu.noise.output;
|
||||
left = (sample * 512) - 16384;
|
||||
|
||||
switch(left_volume) {
|
||||
case 0: left >>= 3; break; // 12.5%
|
||||
case 1: left >>= 2; break; // 25.0%
|
||||
case 2: left = (left >> 2) + (left >> 3); break; // 37.5%
|
||||
case 3: left >>= 1; break; // 50.0%
|
||||
case 4: left = (left >> 1) + (left >> 3); break; // 62.5%
|
||||
case 5: left -= (left >> 2); break; // 75.0%
|
||||
case 6: left -= (left >> 3); break; // 87.5%
|
||||
//case 7: break; //100.0%
|
||||
}
|
||||
sample = (sample * 512) - 16384;
|
||||
sample = (sample * (left_volume + 1)) / 8;
|
||||
left = sample;
|
||||
|
||||
sample = 0;
|
||||
if(channel1_right_enable) sample += apu.square1.output;
|
||||
if(channel2_right_enable) sample += apu.square2.output;
|
||||
if(channel3_right_enable) sample += apu.wave.output;
|
||||
if(channel4_right_enable) sample += apu.noise.output;
|
||||
right = (sample * 512) - 16384;
|
||||
|
||||
switch(right_volume) {
|
||||
case 0: right >>= 3; break; // 12.5%
|
||||
case 1: right >>= 2; break; // 25.0%
|
||||
case 2: right = (right >> 2) + (right >> 3); break; // 37.5%
|
||||
case 3: right >>= 1; break; // 50.0%
|
||||
case 4: right = (right >> 1) + (right >> 3); break; // 62.5%
|
||||
case 5: right -= (right >> 2); break; // 75.0%
|
||||
case 6: right -= (right >> 3); break; // 87.5%
|
||||
//case 7: break; //100.0%
|
||||
}
|
||||
sample = (sample * 512) - 16384;
|
||||
sample = (sample * (right_volume + 1)) / 8;
|
||||
right = sample;
|
||||
}
|
||||
|
||||
void APU::Master::write(unsigned r, uint8 data) {
|
@@ -20,8 +20,11 @@ void APU::Noise::run() {
|
||||
}
|
||||
|
||||
void APU::Noise::clock_length() {
|
||||
if(counter && length) {
|
||||
if(--length == 0) enable = false;
|
||||
//if(counter && length) {
|
||||
// if(--length == 0) enable = false;
|
||||
//}
|
||||
if(enable && counter) {
|
||||
if(++length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +38,8 @@ void APU::Noise::clock_envelope() {
|
||||
|
||||
void APU::Noise::write(unsigned r, uint8 data) {
|
||||
if(r == 1) { //$ff20 NR41
|
||||
length = 64 - (data & 0x3f);
|
||||
//length = 64 - (data & 0x3f);
|
||||
length = data & 0x3f;
|
||||
}
|
||||
|
||||
if(r == 2) { //$ff21 NR42
|
||||
@@ -62,7 +66,7 @@ void APU::Noise::write(unsigned r, uint8 data) {
|
||||
lfsr = ~0U;
|
||||
envelope_period = envelope_frequency;
|
||||
volume = envelope_volume;
|
||||
if(length == 0) length = 64;
|
||||
//if(length == 0) length = 64;
|
||||
}
|
||||
}
|
||||
}
|
@@ -10,7 +10,7 @@ struct Noise {
|
||||
bool counter;
|
||||
|
||||
int16 output;
|
||||
unsigned length;
|
||||
uint6 length;
|
||||
uint3 envelope_period;
|
||||
uint4 volume;
|
||||
unsigned period;
|
@@ -1,7 +1,7 @@
|
||||
#ifdef APU_CPP
|
||||
|
||||
void APU::serialize(serializer &s) {
|
||||
Processor::serialize(s);
|
||||
Thread::serialize(s);
|
||||
|
||||
s.array(mmio_data);
|
||||
s.integer(sequencer_base);
|
@@ -39,8 +39,12 @@ void APU::Square1::sweep(bool update) {
|
||||
}
|
||||
|
||||
void APU::Square1::clock_length() {
|
||||
if(counter && length) {
|
||||
if(--length == 0) enable = false;
|
||||
//if(counter && length) {
|
||||
// if(--length == 0) enable = false;
|
||||
//}
|
||||
|
||||
if(counter && enable) {
|
||||
if(++length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +74,8 @@ void APU::Square1::write(unsigned r, uint8 data) {
|
||||
|
||||
if(r == 1) { //$ff11 NR11
|
||||
duty = data >> 6;
|
||||
length = 64 - (data & 0x3f);
|
||||
//length = 64 - (data & 0x3f);
|
||||
length = data & 0x3f;
|
||||
}
|
||||
|
||||
if(r == 2) { //$ff12 NR12
|
||||
@@ -91,6 +96,7 @@ void APU::Square1::write(unsigned r, uint8 data) {
|
||||
|
||||
if(initialize) {
|
||||
enable = dac_enable();
|
||||
period = 4 * (2048 - frequency);
|
||||
envelope_period = envelope_frequency;
|
||||
volume = envelope_volume;
|
||||
frequency_shadow = frequency;
|
||||
@@ -98,11 +104,9 @@ void APU::Square1::write(unsigned r, uint8 data) {
|
||||
sweep_enable = sweep_period || sweep_shift;
|
||||
sweep_negate = false;
|
||||
if(sweep_shift) sweep(0);
|
||||
if(length == 0) length = 64;
|
||||
//if(length == 0) length = 64;
|
||||
}
|
||||
}
|
||||
|
||||
period = 4 * (2048 - frequency);
|
||||
}
|
||||
|
||||
void APU::Square1::power() {
|
@@ -6,7 +6,7 @@ struct Square1 {
|
||||
uint3 sweep_shift;
|
||||
bool sweep_negate;
|
||||
uint2 duty;
|
||||
unsigned length;
|
||||
uint6 length;
|
||||
uint4 envelope_volume;
|
||||
bool envelope_direction;
|
||||
uint3 envelope_frequency;
|
@@ -23,8 +23,12 @@ void APU::Square2::run() {
|
||||
}
|
||||
|
||||
void APU::Square2::clock_length() {
|
||||
if(counter && length) {
|
||||
if(--length == 0) enable = false;
|
||||
//if(counter && length) {
|
||||
// if(--length == 0) enable = false;
|
||||
//}
|
||||
|
||||
if(counter && enable) {
|
||||
if(++length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +43,8 @@ void APU::Square2::clock_envelope() {
|
||||
void APU::Square2::write(unsigned r, uint8 data) {
|
||||
if(r == 1) { //$ff16 NR21
|
||||
duty = data >> 6;
|
||||
length = 64 - (data & 0x3f);
|
||||
//length = 64 - (data & 0x3f);
|
||||
length = (data & 0x3f);
|
||||
}
|
||||
|
||||
if(r == 2) { //$ff17 NR22
|
||||
@@ -60,13 +65,12 @@ void APU::Square2::write(unsigned r, uint8 data) {
|
||||
|
||||
if(initialize) {
|
||||
enable = dac_enable();
|
||||
period = 4 * (2048 - frequency);
|
||||
envelope_period = envelope_frequency;
|
||||
volume = envelope_volume;
|
||||
if(length == 0) length = 64;
|
||||
//if(length == 0) length = 64;
|
||||
}
|
||||
}
|
||||
|
||||
period = 4 * (2048 - frequency);
|
||||
}
|
||||
|
||||
void APU::Square2::power() {
|
@@ -2,7 +2,7 @@ struct Square2 {
|
||||
bool enable;
|
||||
|
||||
uint2 duty;
|
||||
unsigned length;
|
||||
uint6 length;
|
||||
uint4 envelope_volume;
|
||||
bool envelope_direction;
|
||||
uint3 envelope_frequency;
|
@@ -13,8 +13,11 @@ void APU::Wave::run() {
|
||||
}
|
||||
|
||||
void APU::Wave::clock_length() {
|
||||
if(counter && length) {
|
||||
if(--length == 0) enable = false;
|
||||
//if(counter && length) {
|
||||
// if(--length == 0) enable = false;
|
||||
//}
|
||||
if(enable && counter) {
|
||||
if(++length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +28,8 @@ void APU::Wave::write(unsigned r, uint8 data) {
|
||||
}
|
||||
|
||||
if(r == 1) { //$ff1b NR31
|
||||
length = 256 - data;
|
||||
//length = 256 - data;
|
||||
length = data;
|
||||
}
|
||||
|
||||
if(r == 2) { //$ff1c NR32
|
||||
@@ -48,12 +52,11 @@ void APU::Wave::write(unsigned r, uint8 data) {
|
||||
|
||||
if(initialize) {
|
||||
enable = dac_enable;
|
||||
period = 2 * (2048 - frequency);
|
||||
pattern_offset = 0;
|
||||
if(length == 0) length = 256;
|
||||
//if(length == 0) length = 256;
|
||||
}
|
||||
}
|
||||
|
||||
period = 2 * (2048 - frequency);
|
||||
}
|
||||
|
||||
void APU::Wave::write_pattern(unsigned p, uint8 data) {
|
@@ -8,7 +8,7 @@ struct Wave {
|
||||
uint8 pattern[32];
|
||||
|
||||
int16 output;
|
||||
unsigned length;
|
||||
uint8 length;
|
||||
unsigned period;
|
||||
uint5 pattern_offset;
|
||||
uint4 pattern_sample;
|
@@ -1,9 +1,7 @@
|
||||
#include <gameboy/gameboy.hpp>
|
||||
|
||||
#include <nall/crc32.hpp>
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
#define CARTRIDGE_CPP
|
||||
namespace GameBoy {
|
||||
namespace GB {
|
||||
|
||||
#include "mbc0/mbc0.cpp"
|
||||
#include "mbc1/mbc1.cpp"
|
||||
@@ -21,35 +19,36 @@ void Cartridge::load(System::Revision revision, const string &markup, const uint
|
||||
romdata = allocate<uint8>(romsize = size, 0xff);
|
||||
if(data) memcpy(romdata, data, size);
|
||||
|
||||
info.mapper = Mapper::Unknown;
|
||||
info.ram = false;
|
||||
info.battery = false;
|
||||
info.rtc = false;
|
||||
info.rumble = false;
|
||||
information.markup = markup;
|
||||
information.mapper = Mapper::Unknown;
|
||||
information.ram = false;
|
||||
information.battery = false;
|
||||
information.rtc = false;
|
||||
information.rumble = false;
|
||||
|
||||
info.romsize = 0;
|
||||
info.ramsize = 0;
|
||||
information.romsize = 0;
|
||||
information.ramsize = 0;
|
||||
|
||||
XML::Document document(markup);
|
||||
|
||||
auto &mapperid = document["cartridge"]["mapper"].data;
|
||||
if(mapperid == "none" ) info.mapper = Mapper::MBC0;
|
||||
if(mapperid == "MBC1" ) info.mapper = Mapper::MBC1;
|
||||
if(mapperid == "MBC2" ) info.mapper = Mapper::MBC2;
|
||||
if(mapperid == "MBC3" ) info.mapper = Mapper::MBC3;
|
||||
if(mapperid == "MBC5" ) info.mapper = Mapper::MBC5;
|
||||
if(mapperid == "MMM01") info.mapper = Mapper::MMM01;
|
||||
if(mapperid == "HuC1" ) info.mapper = Mapper::HuC1;
|
||||
if(mapperid == "HuC3" ) info.mapper = Mapper::HuC3;
|
||||
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;
|
||||
|
||||
info.rtc = document["cartridge"]["rtc"].data == "true";
|
||||
info.rumble = document["cartridge"]["rumble"].data == "true";
|
||||
information.rtc = document["cartridge"]["rtc"].data == "true";
|
||||
information.rumble = document["cartridge"]["rumble"].data == "true";
|
||||
|
||||
info.romsize = numeral(document["cartridge"]["rom"]["size"].data);
|
||||
info.ramsize = numeral(document["cartridge"]["ram"]["size"].data);
|
||||
info.battery = document["cartridge"]["ram"]["battery"].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(info.mapper) { default:
|
||||
switch(information.mapper) { default:
|
||||
case Mapper::MBC0: mapper = &mbc0; break;
|
||||
case Mapper::MBC1: mapper = &mbc1; break;
|
||||
case Mapper::MBC2: mapper = &mbc2; break;
|
||||
@@ -60,7 +59,7 @@ void Cartridge::load(System::Revision revision, const string &markup, const uint
|
||||
case Mapper::HuC3: mapper = &huc3; break;
|
||||
}
|
||||
|
||||
ramdata = new uint8_t[ramsize = info.ramsize]();
|
||||
ramdata = new uint8_t[ramsize = information.ramsize]();
|
||||
system.load(revision);
|
||||
|
||||
loaded = true;
|
||||
@@ -103,9 +102,9 @@ uint8 Cartridge::mmio_read(uint16 addr) {
|
||||
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;
|
||||
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];
|
@@ -21,7 +21,7 @@ struct Cartridge : MMIO, property<Cartridge> {
|
||||
};
|
||||
|
||||
struct Information {
|
||||
string xml;
|
||||
string markup;
|
||||
|
||||
Mapper mapper;
|
||||
bool ram;
|
||||
@@ -31,7 +31,7 @@ struct Cartridge : MMIO, property<Cartridge> {
|
||||
|
||||
unsigned romsize;
|
||||
unsigned ramsize;
|
||||
} info;
|
||||
} information;
|
||||
|
||||
readonly<bool> loaded;
|
||||
readonly<string> sha256;
|
@@ -1,7 +1,7 @@
|
||||
#ifdef CARTRIDGE_CPP
|
||||
|
||||
void Cartridge::serialize(serializer &s) {
|
||||
if(info.battery) s.array(ramdata, ramsize);
|
||||
if(information.battery) s.array(ramdata, ramsize);
|
||||
s.integer(bootrom_enable);
|
||||
|
||||
s.integer(mbc1.ram_enable);
|
@@ -1,6 +1,6 @@
|
||||
#include <gameboy/gameboy.hpp>
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
namespace GameBoy {
|
||||
namespace GB {
|
||||
|
||||
Cheat cheat;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#include <gameboy/gameboy.hpp>
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
#define CPU_CPP
|
||||
namespace GameBoy {
|
||||
namespace GB {
|
||||
|
||||
#include "core/core.cpp"
|
||||
#include "mmio/mmio.cpp"
|
@@ -1,4 +1,4 @@
|
||||
struct CPU : Processor, MMIO {
|
||||
struct CPU : Thread, MMIO {
|
||||
#include "core/core.hpp"
|
||||
#include "mmio/mmio.hpp"
|
||||
#include "timing/timing.hpp"
|
@@ -1,7 +1,7 @@
|
||||
#ifdef CPU_CPP
|
||||
|
||||
void CPU::serialize(serializer &s) {
|
||||
Processor::serialize(s);
|
||||
Thread::serialize(s);
|
||||
|
||||
s.array(wram);
|
||||
s.array(hram);
|
59
bsnes/gb/gb.hpp
Executable file
59
bsnes/gb/gb.hpp
Executable file
@@ -0,0 +1,59 @@
|
||||
#ifndef GB_HPP
|
||||
#define GB_HPP
|
||||
|
||||
#include <base/base.hpp>
|
||||
|
||||
namespace GB {
|
||||
namespace Info {
|
||||
static const char Name[] = "bgbc";
|
||||
static const unsigned SerializerVersion = 3;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
bgbc - Game Boy, Super Game Boy, and Game Boy Color emulator
|
||||
author: byuu
|
||||
license: GPLv3
|
||||
project started: 2010-12-27
|
||||
*/
|
||||
|
||||
#include <libco/libco.h>
|
||||
|
||||
namespace GB {
|
||||
struct Thread {
|
||||
cothread_t thread;
|
||||
unsigned frequency;
|
||||
int64 clock;
|
||||
|
||||
inline void create(void (*entrypoint)(), unsigned frequency) {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(65536 * sizeof(void*), entrypoint);
|
||||
this->frequency = frequency;
|
||||
clock = 0;
|
||||
}
|
||||
|
||||
inline void serialize(serializer &s) {
|
||||
s.integer(frequency);
|
||||
s.integer(clock);
|
||||
}
|
||||
|
||||
inline Thread() : thread(nullptr) {
|
||||
}
|
||||
|
||||
inline ~Thread() {
|
||||
if(thread) co_delete(thread);
|
||||
}
|
||||
};
|
||||
|
||||
#include <gb/memory/memory.hpp>
|
||||
#include <gb/system/system.hpp>
|
||||
#include <gb/scheduler/scheduler.hpp>
|
||||
#include <gb/cartridge/cartridge.hpp>
|
||||
#include <gb/cpu/cpu.hpp>
|
||||
#include <gb/apu/apu.hpp>
|
||||
#include <gb/lcd/lcd.hpp>
|
||||
#include <gb/cheat/cheat.hpp>
|
||||
#include <gb/video/video.hpp>
|
||||
};
|
||||
|
||||
#endif
|
@@ -1,6 +1,6 @@
|
||||
#include <gameboy/gameboy.hpp>
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
namespace GameBoy {
|
||||
namespace GB {
|
||||
|
||||
Interface *interface = nullptr;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include <gameboy/gameboy.hpp>
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
//LY = 0-153
|
||||
//Raster = 0-143
|
||||
@@ -7,7 +7,7 @@
|
||||
//LX = 0-455
|
||||
|
||||
#define LCD_CPP
|
||||
namespace GameBoy {
|
||||
namespace GB {
|
||||
|
||||
#include "dmg.cpp"
|
||||
#include "cgb.cpp"
|
@@ -1,4 +1,4 @@
|
||||
struct LCD : Processor, MMIO {
|
||||
struct LCD : Thread, MMIO {
|
||||
#include "mmio/mmio.hpp"
|
||||
|
||||
struct Status {
|
@@ -1,7 +1,7 @@
|
||||
#ifdef LCD_CPP
|
||||
|
||||
void LCD::serialize(serializer &s) {
|
||||
Processor::serialize(s);
|
||||
Thread::serialize(s);
|
||||
|
||||
s.array(screen);
|
||||
s.array(line);
|
@@ -1,7 +1,7 @@
|
||||
#include <gameboy/gameboy.hpp>
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
#define MEMORY_CPP
|
||||
namespace GameBoy {
|
||||
namespace GB {
|
||||
|
||||
Unmapped unmapped;
|
||||
Bus bus;
|
@@ -1,7 +1,7 @@
|
||||
#include <gameboy/gameboy.hpp>
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
#define SCHEDULER_CPP
|
||||
namespace GameBoy {
|
||||
namespace GB {
|
||||
|
||||
Scheduler scheduler;
|
||||
|
||||
@@ -16,11 +16,6 @@ void Scheduler::exit(ExitReason reason) {
|
||||
co_switch(host_thread);
|
||||
}
|
||||
|
||||
void Scheduler::swapto(Processor &p) {
|
||||
active_thread = p.thread;
|
||||
co_switch(active_thread);
|
||||
}
|
||||
|
||||
void Scheduler::init() {
|
||||
host_thread = co_active();
|
||||
active_thread = cpu.thread;
|
@@ -8,7 +8,6 @@ struct Scheduler : property<Scheduler> {
|
||||
|
||||
void enter();
|
||||
void exit(ExitReason);
|
||||
void swapto(Processor&);
|
||||
|
||||
void init();
|
||||
Scheduler();
|
@@ -1,11 +1,8 @@
|
||||
#include <gameboy/gameboy.hpp>
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
#define SYSTEM_CPP
|
||||
namespace GameBoy {
|
||||
namespace GB {
|
||||
|
||||
#include "bootrom-dmg.cpp"
|
||||
#include "bootrom-sgb.cpp"
|
||||
#include "bootrom-cgb.cpp"
|
||||
#include "serialization.cpp"
|
||||
System system;
|
||||
|
||||
@@ -21,8 +18,15 @@ 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() {
|
||||
@@ -35,6 +39,11 @@ void System::runthreadtosave() {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -54,4 +63,10 @@ void System::power() {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@@ -16,9 +16,9 @@ struct System : property<System> {
|
||||
inline bool cgb() const { return revision == Revision::GameBoyColor; }
|
||||
|
||||
struct BootROM {
|
||||
static const uint8 dmg[ 256];
|
||||
static const uint8 sgb[ 256];
|
||||
static const uint8 cgb[2048];
|
||||
uint8 dmg[ 256];
|
||||
uint8 sgb[ 256];
|
||||
uint8 cgb[2048];
|
||||
} bootROM;
|
||||
|
||||
void run();
|
||||
@@ -40,8 +40,10 @@ struct System : property<System> {
|
||||
void serialize(serializer&);
|
||||
void serialize_all(serializer&);
|
||||
void serialize_init();
|
||||
|
||||
System();
|
||||
};
|
||||
|
||||
#include <gameboy/interface/interface.hpp>
|
||||
#include <gb/interface/interface.hpp>
|
||||
|
||||
extern System system;
|
@@ -1,7 +1,7 @@
|
||||
#include <gameboy/gameboy.hpp>
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
#define VIDEO_CPP
|
||||
namespace GameBoy {
|
||||
namespace GB {
|
||||
|
||||
Video video;
|
||||
|
||||
@@ -65,7 +65,7 @@ void Video::generate(Format format) {
|
||||
}
|
||||
|
||||
Video::Video() {
|
||||
palette = new unsigned[1 << 15];
|
||||
palette = new unsigned[1 << 15]();
|
||||
}
|
||||
|
||||
Video::~Video() {
|
14
bsnes/gba/Makefile
Executable file
14
bsnes/gba/Makefile
Executable file
@@ -0,0 +1,14 @@
|
||||
gba_objects := gba-memory gba-interface gba-scheduler gba-system
|
||||
gba_objects += gba-video gba-cartridge
|
||||
gba_objects += gba-cpu gba-ppu gba-apu
|
||||
objects += $(gba_objects)
|
||||
|
||||
obj/gba-memory.o: $(gba)/memory/memory.cpp $(call rwildcard,$(gba)/memory)
|
||||
obj/gba-interface.o: $(gba)/interface/interface.cpp $(call rwildcard,$(gba)/interface)
|
||||
obj/gba-scheduler.o: $(gba)/scheduler/scheduler.cpp $(call rwildcard,$(gba)/scheduler)
|
||||
obj/gba-system.o: $(gba)/system/system.cpp $(call rwildcard,$(gba)/system)
|
||||
obj/gba-video.o: $(gba)/video/video.cpp $(call rwildcard,$(gba)/video)
|
||||
obj/gba-cartridge.o: $(gba)/cartridge/cartridge.cpp $(call rwildcard,$(gba)/cartridge)
|
||||
obj/gba-cpu.o: $(gba)/cpu/cpu.cpp $(call rwildcard,$(gba)/cpu)
|
||||
obj/gba-ppu.o: $(gba)/ppu/ppu.cpp $(call rwildcard,$(gba)/ppu)
|
||||
obj/gba-apu.o: $(gba)/apu/apu.cpp $(call rwildcard,$(gba)/apu)
|
98
bsnes/gba/apu/apu.cpp
Executable file
98
bsnes/gba/apu/apu.cpp
Executable file
@@ -0,0 +1,98 @@
|
||||
#include <gba/gba.hpp>
|
||||
|
||||
namespace GBA {
|
||||
|
||||
#include "registers.cpp"
|
||||
#include "mmio.cpp"
|
||||
#include "square.cpp"
|
||||
#include "square1.cpp"
|
||||
#include "square2.cpp"
|
||||
#include "wave.cpp"
|
||||
#include "noise.cpp"
|
||||
#include "sequencer.cpp"
|
||||
#include "fifo.cpp"
|
||||
#include "serialization.cpp"
|
||||
APU apu;
|
||||
|
||||
void APU::Enter() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
apu.main();
|
||||
}
|
||||
}
|
||||
|
||||
void APU::main() {
|
||||
for(unsigned n = 0; n < 128; n++) {
|
||||
runsequencer();
|
||||
}
|
||||
|
||||
signed lsample = regs.bias.level - 0x0200;
|
||||
signed rsample = regs.bias.level - 0x0200;
|
||||
|
||||
//(4-bit x 4 -> 6-bit) + 3-bit volume = 9-bit output
|
||||
if(sequencer.masterenable) {
|
||||
signed lsequence = 0;
|
||||
if(sequencer.lenable[0]) lsequence += square1.output;
|
||||
if(sequencer.lenable[1]) lsequence += square2.output;
|
||||
if(sequencer.lenable[2]) lsequence += wave.output;
|
||||
if(sequencer.lenable[3]) lsequence += noise.output;
|
||||
|
||||
signed rsequence = 0;
|
||||
if(sequencer.renable[0]) rsequence += square1.output;
|
||||
if(sequencer.renable[1]) rsequence += square2.output;
|
||||
if(sequencer.renable[2]) rsequence += wave.output;
|
||||
if(sequencer.renable[3]) rsequence += noise.output;
|
||||
|
||||
if(sequencer.volume < 3) {
|
||||
lsample += lsequence * (sequencer.lvolume + 1) >> (2 - sequencer.volume);
|
||||
rsample += rsequence * (sequencer.rvolume + 1) >> (2 - sequencer.volume);
|
||||
}
|
||||
}
|
||||
|
||||
//(8-bit x 2 -> 7-bit) + 1-bit volume = 10-bit output
|
||||
signed fifo0 = fifo[0].output + (1 << fifo[0].volume);
|
||||
signed fifo1 = fifo[1].output + (1 << fifo[1].volume);
|
||||
|
||||
if(fifo[0].lenable) lsample += fifo0;
|
||||
if(fifo[1].lenable) lsample += fifo1;
|
||||
|
||||
if(fifo[0].renable) rsample += fifo0;
|
||||
if(fifo[1].renable) rsample += fifo1;
|
||||
|
||||
lsample = sclamp<10>(lsample);
|
||||
rsample = sclamp<10>(rsample);
|
||||
|
||||
if(regs.bias.amplitude == 1) lsample &= ~3, rsample &= ~3;
|
||||
if(regs.bias.amplitude == 2) lsample &= ~7, rsample &= ~7;
|
||||
if(regs.bias.amplitude == 3) lsample &= ~15, rsample &= ~15;
|
||||
|
||||
if(cpu.regs.mode == CPU::Registers::Mode::Stop) lsample = 0, rsample = 0;
|
||||
interface->audioSample(sclamp<16>(lsample << 7), sclamp<16>(rsample << 7)); //should be <<5, use <<7 for added volume
|
||||
step(512);
|
||||
}
|
||||
|
||||
void APU::step(unsigned clocks) {
|
||||
clock += clocks;
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
||||
}
|
||||
|
||||
void APU::power() {
|
||||
create(APU::Enter, 16777216);
|
||||
|
||||
square1.power();
|
||||
square2.power();
|
||||
wave.power();
|
||||
noise.power();
|
||||
sequencer.power();
|
||||
fifo[0].power();
|
||||
fifo[1].power();
|
||||
|
||||
regs.bias = 0x0200;
|
||||
|
||||
for(unsigned n = 0x060; n <= 0x0a7; n++) bus.mmio[n] = this;
|
||||
}
|
||||
|
||||
}
|
17
bsnes/gba/apu/apu.hpp
Executable file
17
bsnes/gba/apu/apu.hpp
Executable file
@@ -0,0 +1,17 @@
|
||||
struct APU : Thread, MMIO {
|
||||
#include "registers.hpp"
|
||||
|
||||
static void Enter();
|
||||
void main();
|
||||
void step(unsigned clocks);
|
||||
|
||||
uint8 read(uint32 addr);
|
||||
void write(uint32 addr, uint8 byte);
|
||||
void power();
|
||||
|
||||
void runsequencer();
|
||||
|
||||
void serialize(serializer&);
|
||||
};
|
||||
|
||||
extern APU apu;
|
28
bsnes/gba/apu/fifo.cpp
Executable file
28
bsnes/gba/apu/fifo.cpp
Executable file
@@ -0,0 +1,28 @@
|
||||
void APU::FIFO::read() {
|
||||
if(size == 0) return;
|
||||
size--;
|
||||
output = sample[rdoffset++];
|
||||
}
|
||||
|
||||
void APU::FIFO::write(int8 byte) {
|
||||
if(size == 32) return;
|
||||
size++;
|
||||
sample[wroffset++] = byte;
|
||||
}
|
||||
|
||||
void APU::FIFO::reset() {
|
||||
for(auto &byte : sample) byte = 0;
|
||||
output = 0;
|
||||
|
||||
rdoffset = 0;
|
||||
wroffset = 0;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
void APU::FIFO::power() {
|
||||
reset();
|
||||
|
||||
lenable = 0;
|
||||
renable = 0;
|
||||
timer = 0;
|
||||
}
|
209
bsnes/gba/apu/mmio.cpp
Executable file
209
bsnes/gba/apu/mmio.cpp
Executable file
@@ -0,0 +1,209 @@
|
||||
uint8 APU::read(uint32 addr) {
|
||||
switch(addr) {
|
||||
|
||||
//NR10
|
||||
case 0x04000060: return square1.read(0);
|
||||
case 0x04000061: return 0u;
|
||||
|
||||
//NR11 + NR12
|
||||
case 0x04000062: return square1.read(1);
|
||||
case 0x04000063: return square1.read(2);
|
||||
|
||||
//NR13 + NR14
|
||||
case 0x04000064: return square1.read(3);
|
||||
case 0x04000065: return square1.read(4);
|
||||
|
||||
//NR21 + NR22
|
||||
case 0x04000068: return square2.read(1);
|
||||
case 0x04000069: return square2.read(2);
|
||||
|
||||
//NR23 + NR24
|
||||
case 0x0400006c: return square2.read(3);
|
||||
case 0x0400006d: return square2.read(4);
|
||||
|
||||
//NR30
|
||||
case 0x04000070: return wave.read(0);
|
||||
case 0x04000071: return 0u;
|
||||
|
||||
//NR31 + NR32
|
||||
case 0x04000072: return wave.read(1);
|
||||
case 0x04000073: return wave.read(2);
|
||||
|
||||
//NR33 + NR34
|
||||
case 0x04000074: return wave.read(3);
|
||||
case 0x04000075: return wave.read(4);
|
||||
|
||||
//NR41 + NR42
|
||||
case 0x04000078: return noise.read(1);
|
||||
case 0x04000079: return noise.read(2);
|
||||
|
||||
//NR43 + NR44
|
||||
case 0x0400007c: return noise.read(3);
|
||||
case 0x0400007d: return noise.read(4);
|
||||
|
||||
//NR50 + NR51
|
||||
case 0x04000080: return sequencer.read(0);
|
||||
case 0x04000081: return sequencer.read(1);
|
||||
|
||||
//NR52
|
||||
case 0x04000084: return sequencer.read(2);
|
||||
case 0x04000085: return 0u;
|
||||
|
||||
//SOUNDBIAS
|
||||
case 0x04000088: return regs.bias >> 0;
|
||||
case 0x04000089: return regs.bias >> 8;
|
||||
|
||||
//WAVE_RAM0_L
|
||||
case 0x04000090: return wave.readram( 0);
|
||||
case 0x04000091: return wave.readram( 1);
|
||||
|
||||
//WAVE_RAM0_H
|
||||
case 0x04000092: return wave.readram( 2);
|
||||
case 0x04000093: return wave.readram( 3);
|
||||
|
||||
//WAVE_RAM1_L
|
||||
case 0x04000094: return wave.readram( 4);
|
||||
case 0x04000095: return wave.readram( 5);
|
||||
|
||||
//WAVE_RAM1_H
|
||||
case 0x04000096: return wave.readram( 6);
|
||||
case 0x04000097: return wave.readram( 7);
|
||||
|
||||
//WAVE_RAM2_L
|
||||
case 0x04000098: return wave.readram( 8);
|
||||
case 0x04000099: return wave.readram( 9);
|
||||
|
||||
//WAVE_RAM2_H
|
||||
case 0x0400009a: return wave.readram(10);
|
||||
case 0x0400009b: return wave.readram(11);
|
||||
|
||||
//WAVE_RAM3_L
|
||||
case 0x0400009c: return wave.readram(12);
|
||||
case 0x0400009d: return wave.readram(13);
|
||||
|
||||
//WAVE_RAM3_H
|
||||
case 0x0400009e: return wave.readram(14);
|
||||
case 0x0400009f: return wave.readram(15);
|
||||
|
||||
}
|
||||
|
||||
return 0u;
|
||||
}
|
||||
|
||||
void APU::write(uint32 addr, uint8 byte) {
|
||||
switch(addr) {
|
||||
|
||||
//NR10
|
||||
case 0x04000060: return square1.write(0, byte);
|
||||
case 0x04000061: return;
|
||||
|
||||
//NR11 + NR12
|
||||
case 0x04000062: return square1.write(1, byte);
|
||||
case 0x04000063: return square1.write(2, byte);
|
||||
|
||||
//NR13 + NR14
|
||||
case 0x04000064: return square1.write(3, byte);
|
||||
case 0x04000065: return square1.write(4, byte);
|
||||
|
||||
//NR21 + NR22
|
||||
case 0x04000068: return square2.write(1, byte);
|
||||
case 0x04000069: return square2.write(2, byte);
|
||||
|
||||
//NR23 + NR24
|
||||
case 0x0400006c: return square2.write(3, byte);
|
||||
case 0x0400006d: return square2.write(4, byte);
|
||||
|
||||
//NR30
|
||||
case 0x04000070: return wave.write(0, byte);
|
||||
case 0x04000071: return;
|
||||
|
||||
//NR31 + NR32
|
||||
case 0x04000072: return wave.write(1, byte);
|
||||
case 0x04000073: return wave.write(2, byte);
|
||||
|
||||
//NR33 + NR34
|
||||
case 0x04000074: return wave.write(3, byte);
|
||||
case 0x04000075: return wave.write(4, byte);
|
||||
|
||||
//NR41 + NR42
|
||||
case 0x04000078: return noise.write(1, byte);
|
||||
case 0x04000079: return noise.write(2, byte);
|
||||
|
||||
//NR43 + NR44
|
||||
case 0x0400007c: return noise.write(3, byte);
|
||||
case 0x0400007d: return noise.write(4, byte);
|
||||
|
||||
//NR50 + NR51
|
||||
case 0x04000080: return sequencer.write(0, byte);
|
||||
case 0x04000081: return sequencer.write(1, byte);
|
||||
|
||||
//SOUND_CNT_H
|
||||
case 0x04000082:
|
||||
sequencer.volume = byte >> 0;
|
||||
fifo[0].volume = byte >> 2;
|
||||
fifo[1].volume = byte >> 3;
|
||||
return;
|
||||
case 0x04000083:
|
||||
fifo[0].renable = byte >> 0;
|
||||
fifo[0].lenable = byte >> 1;
|
||||
fifo[0].timer = byte >> 2;
|
||||
if(byte & 1 << 3) fifo[0].reset();
|
||||
fifo[1].renable = byte >> 4;
|
||||
fifo[1].lenable = byte >> 5;
|
||||
fifo[1].timer = byte >> 6;
|
||||
if(byte & 1 << 7) fifo[1].reset();
|
||||
return;
|
||||
|
||||
//NR52
|
||||
case 0x04000084: return sequencer.write(2, byte);
|
||||
case 0x04000085: return;
|
||||
|
||||
//SOUNDBIAS
|
||||
case 0x04000088: regs.bias = (regs.bias & 0xff00) | (byte << 0); return;
|
||||
case 0x04000089: regs.bias = (regs.bias & 0x00ff) | (byte << 8); return;
|
||||
|
||||
//WAVE_RAM0_L
|
||||
case 0x04000090: return wave.writeram( 0, byte);
|
||||
case 0x04000091: return wave.writeram( 1, byte);
|
||||
|
||||
//WAVE_RAM0_H
|
||||
case 0x04000092: return wave.writeram( 2, byte);
|
||||
case 0x04000093: return wave.writeram( 3, byte);
|
||||
|
||||
//WAVE_RAM1_L
|
||||
case 0x04000094: return wave.writeram( 4, byte);
|
||||
case 0x04000095: return wave.writeram( 5, byte);
|
||||
|
||||
//WAVE_RAM1_H
|
||||
case 0x04000096: return wave.writeram( 6, byte);
|
||||
case 0x04000097: return wave.writeram( 7, byte);
|
||||
|
||||
//WAVE_RAM2_L
|
||||
case 0x04000098: return wave.writeram( 8, byte);
|
||||
case 0x04000099: return wave.writeram( 9, byte);
|
||||
|
||||
//WAVE_RAM2_H
|
||||
case 0x0400009a: return wave.writeram(10, byte);
|
||||
case 0x0400009b: return wave.writeram(11, byte);
|
||||
|
||||
//WAVE_RAM3_L
|
||||
case 0x0400009c: return wave.writeram(12, byte);
|
||||
case 0x0400009d: return wave.writeram(13, byte);
|
||||
|
||||
//WAVE_RAM3_H
|
||||
case 0x0400009e: return wave.writeram(14, byte);
|
||||
case 0x0400009f: return wave.writeram(15, byte);
|
||||
|
||||
//FIFO_A_L
|
||||
//FIFO_A_H
|
||||
case 0x040000a0: case 0x040000a1:
|
||||
case 0x040000a2: case 0x040000a3:
|
||||
return fifo[0].write(byte);
|
||||
|
||||
//FIFO_B_L
|
||||
//FIFO_B_H
|
||||
case 0x040000a4: case 0x040000a5:
|
||||
case 0x040000a6: case 0x040000a7:
|
||||
return fifo[1].write(byte);
|
||||
}
|
||||
}
|
93
bsnes/gba/apu/noise.cpp
Executable file
93
bsnes/gba/apu/noise.cpp
Executable file
@@ -0,0 +1,93 @@
|
||||
unsigned APU::Noise::divider() const {
|
||||
if(divisor == 0) return 8;
|
||||
return divisor * 16;
|
||||
}
|
||||
|
||||
void APU::Noise::run() {
|
||||
if(period && --period == 0) {
|
||||
period = divider() << frequency;
|
||||
if(frequency < 14) {
|
||||
bool bit = (lfsr ^ (lfsr >> 1)) & 1;
|
||||
lfsr = (lfsr >> 1) ^ (bit << (narrowlfsr ? 6 : 14));
|
||||
}
|
||||
}
|
||||
|
||||
output = volume;
|
||||
if(enable == false || (lfsr & 1)) output = 0;
|
||||
}
|
||||
|
||||
void APU::Noise::clocklength() {
|
||||
if(enable && counter) {
|
||||
if(++length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Noise::clockenvelope() {
|
||||
if(enable && envelope.frequency && --envelope.period == 0) {
|
||||
envelope.period = envelope.frequency;
|
||||
if(envelope.direction == 0 && volume > 0) volume--;
|
||||
if(envelope.direction == 1 && volume < 15) volume++;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 APU::Noise::read(unsigned addr) const {
|
||||
switch(addr) {
|
||||
case 1: return (length << 0);
|
||||
case 2: return (envelope.frequency << 0) | (envelope.direction << 3) | (envelope.volume << 4);
|
||||
case 3: return (divisor << 0) | (narrowlfsr << 3) | (frequency << 4);
|
||||
case 4: return (counter << 6) | (initialize << 7);
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Noise::write(unsigned addr, uint8 byte) {
|
||||
switch(addr) {
|
||||
case 1: //NR41
|
||||
length = byte >> 0;
|
||||
break;
|
||||
|
||||
case 2: //NR42
|
||||
envelope.frequency = byte >> 0;
|
||||
envelope.direction = byte >> 3;
|
||||
envelope.volume = byte >> 4;
|
||||
if(envelope.dacenable() == false) enable = false;
|
||||
break;
|
||||
|
||||
case 3: //NR43
|
||||
divisor = byte >> 0;
|
||||
narrowlfsr = byte >> 3;
|
||||
frequency = byte >> 4;
|
||||
period = divider() << frequency;
|
||||
break;
|
||||
|
||||
case 4: //NR44
|
||||
counter = byte >> 6;
|
||||
initialize = byte >> 7;
|
||||
|
||||
if(initialize) {
|
||||
enable = envelope.dacenable();
|
||||
lfsr = ~0u;
|
||||
envelope.period = envelope.frequency;
|
||||
volume = envelope.volume;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Noise::power() {
|
||||
envelope.frequency = 0;
|
||||
envelope.direction = 0;
|
||||
envelope.volume = 0;
|
||||
envelope.period = 0;
|
||||
length = 0;
|
||||
divisor = 0;
|
||||
narrowlfsr = 0;
|
||||
frequency = 0;
|
||||
counter = 0;
|
||||
initialize = 0;
|
||||
enable = 0;
|
||||
lfsr = 0;
|
||||
output = 0;
|
||||
period = 0;
|
||||
volume = 0;
|
||||
}
|
12
bsnes/gba/apu/registers.cpp
Executable file
12
bsnes/gba/apu/registers.cpp
Executable file
@@ -0,0 +1,12 @@
|
||||
APU::Registers::SoundBias::operator uint16() const {
|
||||
return (
|
||||
(level << 0)
|
||||
| (amplitude << 14)
|
||||
);
|
||||
}
|
||||
|
||||
uint16 APU::Registers::SoundBias::operator=(uint16 source) {
|
||||
level = (source >> 0) & 1023;
|
||||
amplitude = (source >> 14) & 3;
|
||||
return operator uint16();
|
||||
}
|
158
bsnes/gba/apu/registers.hpp
Executable file
158
bsnes/gba/apu/registers.hpp
Executable file
@@ -0,0 +1,158 @@
|
||||
struct Registers {
|
||||
struct SoundBias {
|
||||
uint10 level;
|
||||
uint2 amplitude;
|
||||
|
||||
operator uint16() const;
|
||||
uint16 operator=(uint16 source);
|
||||
SoundBias& operator=(const SoundBias&) = delete;
|
||||
} bias;
|
||||
|
||||
unsigned clock;
|
||||
} regs;
|
||||
|
||||
struct Sweep {
|
||||
uint3 shift;
|
||||
uint1 direction;
|
||||
uint3 frequency;
|
||||
|
||||
uint1 enable;
|
||||
uint1 negate;
|
||||
uint3 period;
|
||||
};
|
||||
|
||||
struct Envelope {
|
||||
uint3 frequency;
|
||||
uint1 direction;
|
||||
uint4 volume;
|
||||
|
||||
uint3 period;
|
||||
|
||||
inline bool dacenable() const { return volume || direction; }
|
||||
};
|
||||
|
||||
struct Square {
|
||||
Envelope envelope;
|
||||
uint1 enable;
|
||||
uint6 length;
|
||||
uint2 duty;
|
||||
uint11 frequency;
|
||||
uint1 counter;
|
||||
uint1 initialize;
|
||||
|
||||
signed shadowfrequency;
|
||||
uint1 signal;
|
||||
uint4 output;
|
||||
unsigned period;
|
||||
uint3 phase;
|
||||
uint4 volume;
|
||||
|
||||
void run();
|
||||
void clocklength();
|
||||
void clockenvelope();
|
||||
};
|
||||
|
||||
struct Square1 : Square {
|
||||
Sweep sweep;
|
||||
|
||||
void runsweep(bool update);
|
||||
void clocksweep();
|
||||
uint8 read(unsigned addr) const;
|
||||
void write(unsigned addr, uint8 byte);
|
||||
void power();
|
||||
} square1;
|
||||
|
||||
struct Square2 : Square {
|
||||
uint8 read(unsigned addr) const;
|
||||
void write(unsigned addr, uint8 byte);
|
||||
void power();
|
||||
} square2;
|
||||
|
||||
struct Wave {
|
||||
uint1 mode;
|
||||
uint1 bank;
|
||||
uint1 dacenable;
|
||||
uint8 length;
|
||||
uint3 volume;
|
||||
uint11 frequency;
|
||||
uint1 counter;
|
||||
uint1 initialize;
|
||||
uint4 pattern[32];
|
||||
|
||||
uint1 enable;
|
||||
uint4 output;
|
||||
uint4 patternaddr;
|
||||
uint1 patternbank;
|
||||
uint4 patternsample;
|
||||
unsigned period;
|
||||
|
||||
void run();
|
||||
void clocklength();
|
||||
uint8 read(unsigned addr) const;
|
||||
void write(unsigned addr, uint8 byte);
|
||||
uint8 readram(unsigned addr) const;
|
||||
void writeram(unsigned addr, uint8 byte);
|
||||
void power();
|
||||
} wave;
|
||||
|
||||
struct Noise {
|
||||
Envelope envelope;
|
||||
uint6 length;
|
||||
uint3 divisor;
|
||||
uint1 narrowlfsr;
|
||||
uint4 frequency;
|
||||
uint1 counter;
|
||||
uint1 initialize;
|
||||
|
||||
uint1 enable;
|
||||
uint15 lfsr;
|
||||
uint4 output;
|
||||
unsigned period;
|
||||
uint4 volume;
|
||||
|
||||
unsigned divider() const;
|
||||
void run();
|
||||
void clocklength();
|
||||
void clockenvelope();
|
||||
uint8 read(unsigned addr) const;
|
||||
void write(unsigned addr, uint8 byte);
|
||||
void power();
|
||||
} noise;
|
||||
|
||||
struct Sequencer {
|
||||
uint2 volume;
|
||||
uint3 lvolume;
|
||||
uint3 rvolume;
|
||||
uint1 lenable[4];
|
||||
uint1 renable[4];
|
||||
uint1 enable[4];
|
||||
uint1 masterenable;
|
||||
|
||||
uint13 base;
|
||||
uint3 step;
|
||||
int16 lsample;
|
||||
int16 rsample;
|
||||
|
||||
uint8 read(unsigned addr) const;
|
||||
void write(unsigned addr, uint8 byte);
|
||||
void power();
|
||||
} sequencer;
|
||||
|
||||
struct FIFO {
|
||||
int8 sample[32];
|
||||
int8 output;
|
||||
|
||||
uint5 rdoffset;
|
||||
uint5 wroffset;
|
||||
uint6 size;
|
||||
|
||||
uint1 volume; //0 = 50%, 1 = 100%
|
||||
uint1 lenable;
|
||||
uint1 renable;
|
||||
uint1 timer;
|
||||
|
||||
void read();
|
||||
void write(int8 byte);
|
||||
void reset();
|
||||
void power();
|
||||
} fifo[2];
|
91
bsnes/gba/apu/sequencer.cpp
Executable file
91
bsnes/gba/apu/sequencer.cpp
Executable file
@@ -0,0 +1,91 @@
|
||||
void APU::runsequencer() {
|
||||
auto &r = sequencer;
|
||||
|
||||
if(r.base == 0) { //512hz
|
||||
if(r.step == 0 || r.step == 2 || r.step == 4 || r.step == 6) { //256hz
|
||||
square1.clocklength();
|
||||
square2.clocklength();
|
||||
wave.clocklength();
|
||||
noise.clocklength();
|
||||
}
|
||||
if(r.step == 2 || r.step == 6) { //128hz
|
||||
square1.clocksweep();
|
||||
}
|
||||
if(r.step == 7) { //64hz
|
||||
square1.clockenvelope();
|
||||
square2.clockenvelope();
|
||||
noise.clockenvelope();
|
||||
}
|
||||
r.step++;
|
||||
}
|
||||
r.base++;
|
||||
|
||||
if(r.enable[0]) square1.run();
|
||||
if(r.enable[1]) square2.run();
|
||||
if(r.enable[2]) wave.run();
|
||||
if(r.enable[3]) noise.run();
|
||||
}
|
||||
|
||||
uint8 APU::Sequencer::read(unsigned addr) const {
|
||||
switch(addr) {
|
||||
case 0: return (rvolume << 0) | (lvolume << 4);
|
||||
case 1: return (
|
||||
(renable[0] << 0)
|
||||
| (renable[1] << 1)
|
||||
| (renable[2] << 2)
|
||||
| (renable[3] << 3)
|
||||
| (lenable[0] << 4)
|
||||
| (lenable[1] << 5)
|
||||
| (lenable[2] << 6)
|
||||
| (lenable[3] << 7)
|
||||
);
|
||||
case 2: return (
|
||||
(enable[0] << 0)
|
||||
| (enable[1] << 1)
|
||||
| (enable[2] << 2)
|
||||
| (enable[3] << 3)
|
||||
| (masterenable << 7)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Sequencer::write(unsigned addr, uint8 byte) {
|
||||
switch(addr) {
|
||||
case 0: //NR50
|
||||
rvolume = byte >> 0;
|
||||
lvolume = byte >> 4;
|
||||
break;
|
||||
|
||||
case 1: //NR51
|
||||
renable[0] = byte >> 0;
|
||||
renable[1] = byte >> 1;
|
||||
renable[2] = byte >> 2;
|
||||
renable[3] = byte >> 3;
|
||||
lenable[0] = byte >> 4;
|
||||
lenable[1] = byte >> 5;
|
||||
lenable[2] = byte >> 6;
|
||||
lenable[3] = byte >> 7;
|
||||
break;
|
||||
|
||||
case 2: //NR52
|
||||
enable[0] = byte >> 0;
|
||||
enable[1] = byte >> 1;
|
||||
enable[2] = byte >> 2;
|
||||
enable[3] = byte >> 3;
|
||||
masterenable = byte >> 7;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Sequencer::power() {
|
||||
lvolume = 0;
|
||||
rvolume = 0;
|
||||
for(auto &n : lenable) n = 0;
|
||||
for(auto &n : renable) n = 0;
|
||||
for(auto &n : enable) n = 0;
|
||||
masterenable = 0;
|
||||
base = 0;
|
||||
step = 0;
|
||||
lsample = 0;
|
||||
rsample = 0;
|
||||
}
|
107
bsnes/gba/apu/serialization.cpp
Executable file
107
bsnes/gba/apu/serialization.cpp
Executable file
@@ -0,0 +1,107 @@
|
||||
void APU::serialize(serializer &s) {
|
||||
Thread::serialize(s);
|
||||
|
||||
s.integer(regs.bias.level);
|
||||
s.integer(regs.bias.amplitude);
|
||||
s.integer(regs.clock);
|
||||
|
||||
s.integer(square1.sweep.shift);
|
||||
s.integer(square1.sweep.direction);
|
||||
s.integer(square1.sweep.frequency);
|
||||
s.integer(square1.sweep.enable);
|
||||
s.integer(square1.sweep.negate);
|
||||
s.integer(square1.sweep.period);
|
||||
|
||||
s.integer(square1.envelope.frequency);
|
||||
s.integer(square1.envelope.direction);
|
||||
s.integer(square1.envelope.volume);
|
||||
s.integer(square1.envelope.period);
|
||||
|
||||
s.integer(square1.enable);
|
||||
s.integer(square1.length);
|
||||
s.integer(square1.duty);
|
||||
s.integer(square1.frequency);
|
||||
s.integer(square1.counter);
|
||||
s.integer(square1.initialize);
|
||||
s.integer(square1.shadowfrequency);
|
||||
s.integer(square1.signal);
|
||||
s.integer(square1.output);
|
||||
s.integer(square1.period);
|
||||
s.integer(square1.phase);
|
||||
s.integer(square1.volume);
|
||||
|
||||
s.integer(square2.envelope.frequency);
|
||||
s.integer(square2.envelope.direction);
|
||||
s.integer(square2.envelope.volume);
|
||||
s.integer(square2.envelope.period);
|
||||
|
||||
s.integer(square2.enable);
|
||||
s.integer(square2.length);
|
||||
s.integer(square2.duty);
|
||||
s.integer(square2.frequency);
|
||||
s.integer(square2.counter);
|
||||
s.integer(square2.initialize);
|
||||
s.integer(square2.shadowfrequency);
|
||||
s.integer(square2.signal);
|
||||
s.integer(square2.output);
|
||||
s.integer(square2.period);
|
||||
s.integer(square2.phase);
|
||||
s.integer(square2.volume);
|
||||
|
||||
s.integer(wave.mode);
|
||||
s.integer(wave.bank);
|
||||
s.integer(wave.dacenable);
|
||||
s.integer(wave.length);
|
||||
s.integer(wave.volume);
|
||||
s.integer(wave.frequency);
|
||||
s.integer(wave.counter);
|
||||
s.integer(wave.initialize);
|
||||
for(auto &value : wave.pattern) s.integer(value);
|
||||
s.integer(wave.enable);
|
||||
s.integer(wave.output);
|
||||
s.integer(wave.patternaddr);
|
||||
s.integer(wave.patternbank);
|
||||
s.integer(wave.patternsample);
|
||||
s.integer(wave.period);
|
||||
|
||||
s.integer(noise.envelope.frequency);
|
||||
s.integer(noise.envelope.direction);
|
||||
s.integer(noise.envelope.volume);
|
||||
s.integer(noise.envelope.period);
|
||||
|
||||
s.integer(noise.length);
|
||||
s.integer(noise.divisor);
|
||||
s.integer(noise.narrowlfsr);
|
||||
s.integer(noise.frequency);
|
||||
s.integer(noise.counter);
|
||||
s.integer(noise.initialize);
|
||||
s.integer(noise.enable);
|
||||
s.integer(noise.lfsr);
|
||||
s.integer(noise.output);
|
||||
s.integer(noise.period);
|
||||
s.integer(noise.volume);
|
||||
|
||||
s.integer(sequencer.volume);
|
||||
s.integer(sequencer.lvolume);
|
||||
s.integer(sequencer.rvolume);
|
||||
for(auto &flag : sequencer.lenable) s.integer(flag);
|
||||
for(auto &flag : sequencer.renable) s.integer(flag);
|
||||
for(auto &flag : sequencer.enable) s.integer(flag);
|
||||
s.integer(sequencer.masterenable);
|
||||
s.integer(sequencer.base);
|
||||
s.integer(sequencer.step);
|
||||
s.integer(sequencer.lsample);
|
||||
s.integer(sequencer.rsample);
|
||||
|
||||
for(auto &f : fifo) {
|
||||
for(auto &value : f.sample) s.integer(value);
|
||||
s.integer(f.output);
|
||||
s.integer(f.rdoffset);
|
||||
s.integer(f.wroffset);
|
||||
s.integer(f.size);
|
||||
s.integer(f.volume);
|
||||
s.integer(f.lenable);
|
||||
s.integer(f.renable);
|
||||
s.integer(f.timer);
|
||||
}
|
||||
}
|
30
bsnes/gba/apu/square.cpp
Executable file
30
bsnes/gba/apu/square.cpp
Executable file
@@ -0,0 +1,30 @@
|
||||
void APU::Square::run() {
|
||||
if(period && --period == 0) {
|
||||
period = 4 * (2048 - frequency);
|
||||
phase++;
|
||||
switch(duty) {
|
||||
case 0: signal = (phase == 6); break; //_____-_
|
||||
case 1: signal = (phase >= 6); break; //______--
|
||||
case 2: signal = (phase >= 4); break; //____----
|
||||
case 3: signal = (phase <= 5); break; //------__
|
||||
}
|
||||
}
|
||||
|
||||
uint4 sample = volume;
|
||||
if(enable == false || signal == false) sample = 0;
|
||||
output = sample;
|
||||
}
|
||||
|
||||
void APU::Square::clocklength() {
|
||||
if(enable && counter) {
|
||||
if(++length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Square::clockenvelope() {
|
||||
if(enable && envelope.frequency && --envelope.period == 0) {
|
||||
envelope.period = envelope.frequency;
|
||||
if(envelope.direction == 0 && volume > 0) volume--;
|
||||
if(envelope.direction == 1 && volume < 15) volume++;
|
||||
}
|
||||
}
|
104
bsnes/gba/apu/square1.cpp
Executable file
104
bsnes/gba/apu/square1.cpp
Executable file
@@ -0,0 +1,104 @@
|
||||
void APU::Square1::runsweep(bool update) {
|
||||
if(sweep.enable == false) return;
|
||||
|
||||
sweep.negate = sweep.direction;
|
||||
unsigned delta = shadowfrequency >> sweep.shift;
|
||||
signed updatefrequency = shadowfrequency + (sweep.negate ? -delta : delta);
|
||||
|
||||
if(updatefrequency > 2047) {
|
||||
enable = false;
|
||||
} else if(sweep.shift && update) {
|
||||
shadowfrequency = updatefrequency;
|
||||
frequency = updatefrequency;
|
||||
period = 4 * (2048 - frequency);
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Square1::clocksweep() {
|
||||
if(enable && sweep.frequency && --sweep.period == 0) {
|
||||
sweep.period = sweep.frequency;
|
||||
runsweep(1);
|
||||
runsweep(0);
|
||||
}
|
||||
}
|
||||
|
||||
uint8 APU::Square1::read(unsigned addr) const {
|
||||
switch(addr) {
|
||||
case 0: return (sweep.shift << 0) | (sweep.direction << 3) | (sweep.frequency << 4);
|
||||
case 1: return (length << 0) | (duty << 6);
|
||||
case 2: return (envelope.frequency << 0) | (envelope.direction << 3) | (envelope.volume << 4);
|
||||
case 3: return (frequency << 0);
|
||||
case 4: return (frequency >> 8) | (counter << 6) | (initialize << 7);
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Square1::write(unsigned addr, uint8 byte) {
|
||||
switch(addr) {
|
||||
case 0: //NR10
|
||||
if(sweep.negate && sweep.direction && !(byte & 0x08)) enable = false;
|
||||
sweep.shift = byte >> 0;
|
||||
sweep.direction = byte >> 3;
|
||||
sweep.frequency = byte >> 4;
|
||||
break;
|
||||
|
||||
case 1: //NR11
|
||||
length = byte >> 0;
|
||||
duty = byte >> 6;
|
||||
break;
|
||||
|
||||
case 2: //NR12
|
||||
envelope.frequency = byte >> 0;
|
||||
envelope.direction = byte >> 3;
|
||||
envelope.volume = byte >> 4;
|
||||
if(envelope.dacenable() == false) enable = false;
|
||||
break;
|
||||
|
||||
case 3: //NR13
|
||||
frequency = (frequency & 0xff00) | (byte << 0);
|
||||
break;
|
||||
|
||||
case 4: //NR14
|
||||
frequency = (frequency & 0x00ff) | (byte << 8);
|
||||
counter = byte >> 6;
|
||||
initialize = byte >> 7;
|
||||
|
||||
if(initialize) {
|
||||
enable = envelope.dacenable();
|
||||
period = 4 * (2048 - frequency);
|
||||
envelope.period = envelope.frequency;
|
||||
volume = envelope.volume;
|
||||
shadowfrequency = frequency;
|
||||
sweep.period = sweep.frequency;
|
||||
sweep.enable = sweep.period || sweep.shift;
|
||||
sweep.negate = false;
|
||||
if(sweep.shift) runsweep(0);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Square1::power() {
|
||||
envelope.frequency = 0;
|
||||
envelope.direction = 0;
|
||||
envelope.direction = 0;
|
||||
envelope.period = 0;
|
||||
sweep.shift = 0;
|
||||
sweep.direction = 0;
|
||||
sweep.frequency = 0;
|
||||
sweep.enable = 0;
|
||||
sweep.negate = 0;
|
||||
sweep.period = 0;
|
||||
enable = 0;
|
||||
length = 0;
|
||||
duty = 0;
|
||||
frequency = 0;
|
||||
counter = 0;
|
||||
initialize = 0;
|
||||
shadowfrequency = 0;
|
||||
signal = 0;
|
||||
output = 0;
|
||||
period = 0;
|
||||
phase = 0;
|
||||
volume = 0;
|
||||
}
|
61
bsnes/gba/apu/square2.cpp
Executable file
61
bsnes/gba/apu/square2.cpp
Executable file
@@ -0,0 +1,61 @@
|
||||
uint8 APU::Square2::read(unsigned addr) const {
|
||||
switch(addr) {
|
||||
case 1: return (length << 0) | (duty << 6);
|
||||
case 2: return (envelope.frequency << 0) | (envelope.direction << 3) | (envelope.volume << 4);
|
||||
case 3: return (frequency >> 0);
|
||||
case 4: return (frequency >> 8) | (counter << 6) | (initialize << 7);
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Square2::write(unsigned addr, uint8 byte) {
|
||||
switch(addr) {
|
||||
case 1: //NR21
|
||||
length = byte >> 0;
|
||||
duty = byte >> 6;
|
||||
break;
|
||||
|
||||
case 2: //NR22
|
||||
envelope.frequency = byte >> 0;
|
||||
envelope.direction = byte >> 3;
|
||||
envelope.volume = byte >> 4;
|
||||
if(envelope.dacenable() == false) enable = false;
|
||||
break;
|
||||
|
||||
case 3: //NR23
|
||||
frequency = (frequency & 0xff00) | (byte << 0);
|
||||
break;
|
||||
|
||||
case 4: //NR24
|
||||
frequency = (frequency & 0x00ff) | (byte << 8);
|
||||
counter = byte >> 6;
|
||||
initialize = byte >> 7;
|
||||
|
||||
if(initialize) {
|
||||
enable = envelope.dacenable();
|
||||
period = 4 * (2048 - frequency);
|
||||
envelope.period = envelope.frequency;
|
||||
volume = envelope.volume;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Square2::power() {
|
||||
envelope.frequency = 0;
|
||||
envelope.direction = 0;
|
||||
envelope.direction = 0;
|
||||
envelope.period = 0;
|
||||
enable = 0;
|
||||
length = 0;
|
||||
duty = 0;
|
||||
frequency = 0;
|
||||
counter = 0;
|
||||
initialize = 0;
|
||||
shadowfrequency = 0;
|
||||
signal = 0;
|
||||
output = 0;
|
||||
period = 0;
|
||||
phase = 0;
|
||||
volume = 0;
|
||||
}
|
95
bsnes/gba/apu/wave.cpp
Executable file
95
bsnes/gba/apu/wave.cpp
Executable file
@@ -0,0 +1,95 @@
|
||||
void APU::Wave::run() {
|
||||
if(period && --period == 0) {
|
||||
period = 2 * (2048 - frequency);
|
||||
patternsample = pattern[patternbank * 16 + patternaddr++];
|
||||
if(patternaddr == 0) patternbank ^= mode;
|
||||
}
|
||||
|
||||
output = patternsample;
|
||||
static unsigned multiplier[] = { 0, 4, 2, 1, 3, 3, 3, 3};
|
||||
output = (output * multiplier[volume]) / 4;
|
||||
if(enable == false) output = 0;
|
||||
}
|
||||
|
||||
void APU::Wave::clocklength() {
|
||||
if(enable && counter) {
|
||||
if(++length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 APU::Wave::read(unsigned addr) const {
|
||||
switch(addr) {
|
||||
case 0: return (mode << 5) | (bank << 6) | (dacenable << 7);
|
||||
case 1: return (length << 0);
|
||||
case 2: return (volume << 5);
|
||||
case 3: return (frequency >> 0);
|
||||
case 4: return (frequency >> 8) | (counter << 6) | (initialize << 7);
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Wave::write(unsigned addr, uint8 byte) {
|
||||
switch(addr) {
|
||||
case 0: //NR30
|
||||
mode = byte >> 5;
|
||||
bank = byte >> 6;
|
||||
dacenable = byte >> 7;
|
||||
if(dacenable == false) enable = false;
|
||||
break;
|
||||
|
||||
case 1: //NR31
|
||||
length = byte >> 0;
|
||||
break;
|
||||
|
||||
case 2: //NR32
|
||||
volume = byte >> 5;
|
||||
break;
|
||||
|
||||
case 3: //NR33
|
||||
frequency = (frequency & 0xff00) | (byte << 0);
|
||||
break;
|
||||
|
||||
case 4: //NR34
|
||||
frequency = (frequency & 0x00ff) | (byte << 8);
|
||||
counter = byte >> 6;
|
||||
initialize = byte >> 7;
|
||||
|
||||
if(initialize) {
|
||||
enable = dacenable;
|
||||
period = 2 * (2048 - frequency);
|
||||
patternaddr = 0;
|
||||
patternbank = mode ? (uint1)0 : bank;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 APU::Wave::readram(unsigned addr) const {
|
||||
uint8 byte = 0;
|
||||
byte |= pattern[addr * 2 + 0] << 0;
|
||||
byte |= pattern[addr * 2 + 1] << 4;
|
||||
return byte;
|
||||
}
|
||||
|
||||
void APU::Wave::writeram(unsigned addr, uint8 byte) {
|
||||
pattern[addr * 2 + 0] = byte >> 0;
|
||||
pattern[addr * 2 + 1] = byte >> 4;
|
||||
}
|
||||
|
||||
void APU::Wave::power() {
|
||||
mode = 0;
|
||||
bank = 0;
|
||||
dacenable = 0;
|
||||
length = 0;
|
||||
volume = 0;
|
||||
frequency = 0;
|
||||
counter = 0;
|
||||
initialize = 0;
|
||||
for(auto &sample : pattern) sample = 0;
|
||||
enable = 0;
|
||||
output = 0;
|
||||
patternaddr = 0;
|
||||
patternbank = 0;
|
||||
patternsample = 0;
|
||||
period = 0;
|
||||
}
|
130
bsnes/gba/cartridge/cartridge.cpp
Executable file
130
bsnes/gba/cartridge/cartridge.cpp
Executable file
@@ -0,0 +1,130 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
33
bsnes/gba/cartridge/cartridge.hpp
Executable file
33
bsnes/gba/cartridge/cartridge.hpp
Executable file
@@ -0,0 +1,33 @@
|
||||
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;
|
94
bsnes/gba/cartridge/eeprom.cpp
Executable file
94
bsnes/gba/cartridge/eeprom.cpp
Executable file
@@ -0,0 +1,94 @@
|
||||
bool Cartridge::EEPROM::read(unsigned addr) {
|
||||
return data[addr >> 3] & 0x80 >> (addr & 7);
|
||||
}
|
||||
|
||||
void Cartridge::EEPROM::write(unsigned addr, bool bit) {
|
||||
if(bit == 0) data[addr >> 3] &=~ (0x80 >> (addr & 7));
|
||||
if(bit == 1) data[addr >> 3] |= (0x80 >> (addr & 7));
|
||||
}
|
||||
|
||||
bool Cartridge::EEPROM::read() {
|
||||
bool bit = 1;
|
||||
|
||||
//EEPROM size auto-detection
|
||||
if(bits == 0 && mode == Mode::ReadAddress) {
|
||||
print("EEPROM address bits: ", --addressbits, "\n");
|
||||
bits = addressbits == 6 ? 6 : 14;
|
||||
size = 8192;
|
||||
mode = Mode::ReadData;
|
||||
offset = 0;
|
||||
//fallthrough
|
||||
}
|
||||
|
||||
if(mode == Mode::ReadData) {
|
||||
if(offset >= 4) bit = read(address * 64 + (offset - 4));
|
||||
if(++offset == 68) mode = Mode::Wait;
|
||||
}
|
||||
|
||||
return bit;
|
||||
}
|
||||
|
||||
void Cartridge::EEPROM::write(bool bit) {
|
||||
if(mode == Mode::Wait) {
|
||||
if(bit == 1) mode = Mode::Command;
|
||||
}
|
||||
|
||||
else if(mode == Mode::Command) {
|
||||
if(bit == 0) mode = Mode::WriteAddress;
|
||||
if(bit == 1) mode = Mode::ReadAddress;
|
||||
offset = 0;
|
||||
address = 0;
|
||||
addressbits = 0;
|
||||
}
|
||||
|
||||
else if(mode == Mode::ReadAddress) {
|
||||
address = (address << 1) | bit;
|
||||
addressbits++;
|
||||
if(++offset == bits) {
|
||||
mode = Mode::ReadValidate;
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
else if(mode == Mode::ReadValidate) {
|
||||
if(bit == 1); //invalid
|
||||
mode = Mode::ReadData;
|
||||
}
|
||||
|
||||
else if(mode == Mode::WriteAddress) {
|
||||
address = (address << 1) | bit;
|
||||
if(++offset == bits) {
|
||||
mode = Mode::WriteData;
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
else if(mode == Mode::WriteData) {
|
||||
write(address * 64 + offset, bit);
|
||||
if(++offset == 64) {
|
||||
mode = Mode::WriteValidate;
|
||||
}
|
||||
}
|
||||
|
||||
else if(mode == Mode::WriteValidate) {
|
||||
if(bit == 1); //invalid
|
||||
mode = Mode::Wait;
|
||||
}
|
||||
}
|
||||
|
||||
void Cartridge::EEPROM::power() {
|
||||
mode = Mode::Wait;
|
||||
offset = 0;
|
||||
address = 0;
|
||||
}
|
||||
|
||||
void Cartridge::EEPROM::serialize(serializer &s) {
|
||||
s.array(data, size);
|
||||
s.integer(size);
|
||||
s.integer(mask);
|
||||
s.integer(test);
|
||||
s.integer(bits);
|
||||
s.integer((unsigned&)mode);
|
||||
s.integer(offset);
|
||||
s.integer(address);
|
||||
}
|
100
bsnes/gba/cartridge/flashrom.cpp
Executable file
100
bsnes/gba/cartridge/flashrom.cpp
Executable file
@@ -0,0 +1,100 @@
|
||||
//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);
|
||||
}
|
45
bsnes/gba/cartridge/memory.hpp
Executable file
45
bsnes/gba/cartridge/memory.hpp
Executable file
@@ -0,0 +1,45 @@
|
||||
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;
|
5
bsnes/gba/cartridge/serialization.cpp
Executable file
5
bsnes/gba/cartridge/serialization.cpp
Executable file
@@ -0,0 +1,5 @@
|
||||
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);
|
||||
}
|
173
bsnes/gba/cpu/cpu.cpp
Executable file
173
bsnes/gba/cpu/cpu.cpp
Executable file
@@ -0,0 +1,173 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
43
bsnes/gba/cpu/cpu.hpp
Executable file
43
bsnes/gba/cpu/cpu.hpp
Executable file
@@ -0,0 +1,43 @@
|
||||
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;
|
60
bsnes/gba/cpu/dma.cpp
Executable file
60
bsnes/gba/cpu/dma.cpp
Executable file
@@ -0,0 +1,60 @@
|
||||
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;
|
||||
}
|
55
bsnes/gba/cpu/memory.cpp
Executable file
55
bsnes/gba/cpu/memory.cpp
Executable file
@@ -0,0 +1,55 @@
|
||||
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;
|
||||
}
|
316
bsnes/gba/cpu/mmio.cpp
Executable file
316
bsnes/gba/cpu/mmio.cpp
Executable file
@@ -0,0 +1,316 @@
|
||||
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;
|
||||
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user