diff --git a/makefile.burn_rules b/makefile.burn_rules
index b92d55481..7edce1745 100644
--- a/makefile.burn_rules
+++ b/makefile.burn_rules
@@ -1,5 +1,5 @@
alldir = burn burn/devices burn/snd burn/drv burn/drv/atari burn/drv/capcom burn/drv/cave burn/drv/channelf burn/drv/coleco burn/drv/cps3 burn/drv/dataeast \
- burn/drv/galaxian burn/drv/irem burn/drv/konami burn/drv/megadrive burn/drv/midway burn/drv/pce burn/drv/pst90s burn/drv/pre90s burn/drv/neogeo burn/drv/nes burn/drv/snes \
+ burn/drv/galaxian burn/drv/gba burn/drv/irem burn/drv/konami burn/drv/megadrive burn/drv/midway burn/drv/pce burn/drv/pst90s burn/drv/pre90s burn/drv/neogeo burn/drv/nes burn/drv/snes \
burn/drv/pgm burn/drv/pgm2 burn/drv/psikyo burn/drv/sega burn/drv/sg1000 burn/drv/sms burn/drv/msx burn/drv/spectrum burn/drv/taito \
burn/drv/toaplan cpu cpu/a68k cpu/arm cpu/arm7 cpu/arm9 cpu/e132xs cpu/f8 cpu/h6280 cpu/hd6309 cpu/hmcs40 cpu/i386 cpu/i8039 cpu/i8x41 cpu/i8051 cpu/i8085 cpu/adsp2100 cpu/konami cpu/m377 cpu/mips3 cpu/m68k \
cpu/mb88xx cpu/m6502 cpu/m6800 cpu/m6805 cpu/m6809 cpu/nec cpu/pic16c5x cpu/s2650 cpu/tlcs90 cpu/tlcs900 cpu/sh2 cpu/sh4 cpu/t11 cpu/tms32010 cpu/tms34 cpu/upd7725 cpu/upd7810 \
@@ -26,6 +26,8 @@ drvsrc = d_akkaarrh.o d_arcadecl.o d_atarig1.o d_atarisy1.o d_badlands.o d_batm
\
d_119.o d_galaxian.o \
\
+ d_gba.o gba.o \
+ \
d_m52.o d_m57.o d_m58.o d_m62.o d_m63.o d_m72.o d_m90.o d_m92.o d_m107.o d_travrusa.o d_vigilant.o \
\
d_88games.o d_ajax.o d_aliens.o d_asterix.o d_battlnts.o d_bishi.o d_bladestl.o d_blockhl.o d_bottom9.o d_chqflag.o d_circusc.o d_combatsc.o \
diff --git a/makefile.mingw b/makefile.mingw
index 382538704..7d20ab9a2 100644
--- a/makefile.mingw
+++ b/makefile.mingw
@@ -672,6 +672,9 @@ $(cave_tile_func.h): $(srcdir)dep/scripts/cave_tile_func.pl
cave_sprite.d cave_sprite.o: $(cave_sprite_func.h)
+gba.d gba.o: $(srcdir)burn/drv/gba/gba.h $(srcdir)burn/drv/gba/gba_impl.h $(srcdir)burn/drv/gba/host.h $(srcdir)burn/drv/gba/arm7.h $(srcdir)burn/drv/gba/bios.h $(srcdir)burn/drv/gba/rtc.h
+d_gba.d d_gba.o: $(srcdir)burn/drv/gba/gba.h
+
$(cave_sprite_func.h): $(srcdir)dep/scripts/cave_sprite_func.pl
@$(srcdir)dep/scripts/cave_sprite_func.pl -o $(cave_sprite_func.h)
diff --git a/meson_options.txt b/meson_options.txt
index 451209de7..12abb453b 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -21,6 +21,7 @@ option('driver_list',
'cps3',
'dataeast',
'galaxian',
+ 'gba',
'irem',
'konami',
'megadrive',
diff --git a/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj b/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj
index 43d4ebc60..494f7aa01 100644
--- a/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj
+++ b/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj
@@ -555,6 +555,8 @@ del %(Filename).exe
+
+
diff --git a/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj.filters b/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj.filters
index a2c576a2c..a1aacb8d1 100644
--- a/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj.filters
+++ b/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj.filters
@@ -418,6 +418,12 @@
burn\drv\galaxian
+
+ burn\drv\gba
+
+
+ burn\drv\gba
+
burn\drv\irem
@@ -3876,6 +3882,9 @@
{c4b93ffc-1f2a-4461-a73b-d7153fd1a1e6}
+
+ {6b17d54b-49ca-4e1f-9f6f-418472209f26}
+
{0937431a-c5c3-4079-86ff-f553d99ef136}
diff --git a/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj b/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj
index 61c52edbd..665cabf64 100644
--- a/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj
+++ b/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj
@@ -555,6 +555,8 @@ del %(Filename).exe
+
+
diff --git a/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj.filters b/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj.filters
index 3608d49de..babd9fe04 100644
--- a/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj.filters
+++ b/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj.filters
@@ -418,6 +418,12 @@
burn\drv\galaxian
+
+ burn\drv\gba
+
+
+ burn\drv\gba
+
burn\drv\irem
@@ -3876,6 +3882,9 @@
{c4b93ffc-1f2a-4461-a73b-d7153fd1a1e6}
+
+ {6b17d54b-49ca-4e1f-9f6f-418472209f26}
+
{0937431a-c5c3-4079-86ff-f553d99ef136}
diff --git a/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj b/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj
index 68839f7f0..114a0a613 100644
--- a/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj
+++ b/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj
@@ -555,6 +555,8 @@ del %(Filename).exe
+
+
diff --git a/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj.filters b/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj.filters
index add6249c0..492fccb19 100644
--- a/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj.filters
+++ b/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj.filters
@@ -418,6 +418,12 @@
burn\drv\galaxian
+
+ burn\drv\gba
+
+
+ burn\drv\gba
+
burn\drv\irem
@@ -3876,6 +3882,9 @@
{c4b93ffc-1f2a-4461-a73b-d7153fd1a1e6}
+
+ {6b17d54b-49ca-4e1f-9f6f-418472209f26}
+
{0937431a-c5c3-4079-86ff-f553d99ef136}
diff --git a/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj b/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj
index 9d622c8f4..269594b66 100644
--- a/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj
+++ b/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj
@@ -243,6 +243,8 @@
+
+
@@ -1443,7 +1445,7 @@ cd scripts
cl.exe /D "_UNICODE" %(Filename).cpp
-.%(Filename).exe >../../../projectfiles/visualstudio-2026/generated/%(Filename).h
+.\%(Filename).exe >../../../projectfiles/visualstudio-2026/generated/%(Filename).h
del %(Filename).obj
@@ -1462,7 +1464,7 @@ cd scripts
cl.exe /D "_UNICODE" %(Filename).cpp
-.%(Filename).exe >../../../projectfiles/visualstudio-2026/generated/%(Filename).h
+.\%(Filename).exe >../../../projectfiles/visualstudio-2026/generated/%(Filename).h
del %(Filename).obj
@@ -1481,7 +1483,7 @@ cd scripts
cl.exe /D "_UNICODE" /D "BUILD_X64_EXE" %(Filename).cpp
-.%(Filename).exe >../../../projectfiles/visualstudio-2026/generated/%(Filename).h
+.\%(Filename).exe >../../../projectfiles/visualstudio-2026/generated/%(Filename).h
del %(Filename).obj
@@ -1500,7 +1502,7 @@ cd scripts
cl.exe /D "_UNICODE" /D "BUILD_X64_EXE" %(Filename).cpp
-.%(Filename).exe >../../../projectfiles/visualstudio-2026/generated/%(Filename).h
+.\%(Filename).exe >../../../projectfiles/visualstudio-2026/generated/%(Filename).h
del %(Filename).obj
diff --git a/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj.filters b/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj.filters
index 4c719b447..5dd0f5c3b 100644
--- a/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj.filters
+++ b/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj.filters
@@ -637,6 +637,12 @@
burn\drv\galaxian
+
+ burn\drv\gba
+
+
+ burn\drv\gba
+
burn\drv\irem
@@ -3876,6 +3882,9 @@
{c4b93ffc-1f2a-4461-a73b-d7153fd1a1e6}
+
+ {6b17d54b-49ca-4e1f-9f6f-418472209f26}
+
{0937431a-c5c3-4079-86ff-f553d99ef136}
diff --git a/src/burn/burn.cpp b/src/burn/burn.cpp
index c0e10ae86..3674759df 100644
--- a/src/burn/burn.cpp
+++ b/src/burn/burn.cpp
@@ -1741,8 +1741,20 @@ extern struct MovieExtInfo MovieInfo; // from replay.cpp
struct MovieExtInfo MovieInfo = { 0, 0, 0, 0, 0, 0, 0 };
#endif
+static INT32 BurnDayOfYear(INT32 year, INT32 month, INT32 day)
+{
+ static const INT32 daysBeforeMonth[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+ INT32 result = daysBeforeMonth[month] + day - 1;
+ if (month > 1 && year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) result++;
+ return result;
+}
+
void BurnGetLocalTime(tm *nTime)
{
+ if (nTime == NULL) return;
+ memset(nTime, 0, sizeof(*nTime));
+ nTime->tm_isdst = -1;
+
if (is_netgame_or_recording()) {
if (is_netgame_or_recording() & 2) { // recording/playback
nTime->tm_sec = MovieInfo.second;
@@ -1752,19 +1764,27 @@ void BurnGetLocalTime(tm *nTime)
nTime->tm_wday = MovieInfo.dayofweek;
nTime->tm_mon = MovieInfo.month;
nTime->tm_year = MovieInfo.year;
+ if (nTime->tm_mon >= 0 && nTime->tm_mon < 12 && nTime->tm_mday > 0) {
+ nTime->tm_yday = BurnDayOfYear(nTime->tm_year + 1900, nTime->tm_mon, nTime->tm_mday);
+ }
} else {
- nTime->tm_sec = 0; // defaults for netgame
- nTime->tm_min = 0;
- nTime->tm_hour = 0;
- nTime->tm_mday = 1;
- nTime->tm_wday = 3;
- nTime->tm_mon = 6 - 1;
- nTime->tm_year = 2018;
+ nTime->tm_mday = 1; // 2018-06-01 00:00:00 for netgame
+ nTime->tm_wday = 5;
+ nTime->tm_yday = 151;
+ nTime->tm_mon = 5;
+ nTime->tm_year = 2018 - 1900;
}
} else {
time_t nLocalTime = time(NULL); // query current time from this machine
tm* tmLocalTime = localtime(&nLocalTime);
- memcpy(nTime, tmLocalTime, sizeof(tm));
+ if (tmLocalTime) {
+ memcpy(nTime, tmLocalTime, sizeof(*nTime));
+ } else {
+ nTime->tm_mday = 1;
+ nTime->tm_wday = 6;
+ nTime->tm_mon = 0;
+ nTime->tm_year = 2000 - 1900;
+ }
}
}
diff --git a/src/burn/burn.h b/src/burn/burn.h
index 77d01c72e..8f39d01ec 100644
--- a/src/burn/burn.h
+++ b/src/burn/burn.h
@@ -34,6 +34,7 @@
#define BUILD_CPS3
#define BUILD_DATAEAST
#define BUILD_GALAXIAN
+ #define BUILD_GBA
#define BUILD_IREM
#define BUILD_KONAMI
#define BUILD_MEGADRIVE
@@ -601,6 +602,7 @@ int BurnComputeSHA1(const UINT8 *buffer, int buffer_size, char *hash_str);
#define HARDWARE_PREFIX_SNES (0x22000000)
#define HARDWARE_PREFIX_IGS_PGM2 (0x23000000)
#define HARDWARE_PREFIX_ASTROHOME (0x24000000)
+#define HARDWARE_PREFIX_GBA (0x25000000)
#define HARDWARE_SNK_NGP (HARDWARE_PREFIX_NGP | 0x00000000)
#define HARDWARE_SNK_NGPC (HARDWARE_PREFIX_NGP | 0x00000001) // must not be 0x10000
@@ -852,6 +854,8 @@ int BurnComputeSHA1(const UINT8 *buffer, int buffer_size, char *hash_str);
#define HARDWARE_ASTROHOME (HARDWARE_PREFIX_ASTROHOME)
+#define HARDWARE_GBA (HARDWARE_PREFIX_GBA)
+
// flags for the genre member
#define GBF_HORSHOOT (1 << 0)
#define GBF_VERSHOOT (1 << 1)
diff --git a/src/burn/drv/gba/arm7.h b/src/burn/drv/gba/arm7.h
new file mode 100644
index 000000000..c06b03281
--- /dev/null
+++ b/src/burn/drv/gba/arm7.h
@@ -0,0 +1,2558 @@
+#pragma once
+
+#ifndef GBA_ARM7_H
+#define GBA_ARM7_H 1
+
+#include
+#include
+#include
+
+////////////////
+// Data Types //
+////////////////
+
+#define LR 14
+#define PC 15
+#define CPSR 16
+#define SPSR 17
+
+#define R13_fiq 22
+#define R13_irq 24
+#define R13_svc 26
+#define R13_abt 28
+#define R13_und 30
+
+#define R14_fiq 23
+#define R14_irq 25
+#define R14_svc 27
+#define R14_abt 29
+#define R14_und 31
+
+#define SPSR_fiq 32
+#define SPSR_irq 33
+#define SPSR_svc 34
+#define SPSR_abt 35
+#define SPSR_und 36
+
+#define UNINTIALIZED_PREFETCH_PC -3
+// Memory IO functions for the emulated CPU (these must be defined by the user)
+typedef UINT32 (*arm_read32_fn_t)(void* user_data, UINT32 address);
+typedef UINT32 (*arm_read16_fn_t)(void* user_data, UINT32 address);
+typedef UINT32 (*arm_read32_seq_fn_t)(void* user_data, UINT32 address,bool is_sequential);
+typedef UINT32 (*arm_read16_seq_fn_t)(void* user_data, UINT32 address,bool is_sequential);
+typedef UINT8 (*arm_read8_fn_t)(void* user_data, UINT32 address);
+typedef void (*arm_write32_fn_t)(void* user_data, UINT32 address, UINT32 data);
+typedef void (*arm_write16_fn_t)(void* user_data, UINT32 address, UINT16 data);
+typedef void (*arm_write8_fn_t)(void* user_data, UINT32 address, UINT8 data);
+typedef UINT32 (*arm_coproc_read_fn_t)(void* user_data, INT32 coproc, INT32 opcode, INT32 Cn, INT32 Cm, INT32 Cp);
+typedef void (*arm_coproc_write_fn_t)(void* user_data, INT32 coproc, INT32 opcode, INT32 Cn, INT32 Cm, INT32 Cp, UINT32 data);
+typedef void (*arm_trigger_breakpoint_fn_t)(void* user_data);
+
+
+#define ARM_DEBUG_BRANCH_RING_SIZE 32
+#define ARM_DEBUG_SWI_RING_SIZE 32
+typedef struct {
+ // Registers
+ /*
+ 0-15: R0-R15
+ 16: CPSR
+ 17-23: R8_fiq-R14_fiq
+ 24-25: R13_irq-R14_irq
+ 26-27: R13_svc-R14_svc
+ 28-29: R13_abt-R14_abt
+ 30-31: R13_und-R14_und
+ 32: SPSR_fiq
+ 33: SPSR_irq
+ 34: SPSR_svc
+ 35: SPSR_abt
+ 36: SPSR_und
+ */
+
+ UINT32 debug_branch_ring[ARM_DEBUG_BRANCH_RING_SIZE];
+ UINT32 debug_branch_ring_offset;
+ UINT32 debug_swi_ring[ARM_DEBUG_SWI_RING_SIZE];
+ UINT32 debug_swi_ring_times[ARM_DEBUG_SWI_RING_SIZE];
+ UINT32 debug_swi_ring_offset;
+ UINT32 prefetch_pc;
+ UINT32 step_instructions; //Instructions to step before triggering a breakpoint
+ UINT32 prefetch_opcode[5];
+ UINT32 i_cycles; //Executed i-cycles minus 1
+ bool next_fetch_sequential;
+ UINT32 registers[37];
+ UINT64 executed_instructions;
+ bool print_instructions;
+ void* user_data;
+ FILE* log_cmp_file;
+ arm_read32_fn_t read32;
+ arm_read16_fn_t read16;
+ arm_read32_seq_fn_t read32_seq;
+ arm_read16_seq_fn_t read16_seq;
+ arm_read8_fn_t read8;
+ arm_write32_fn_t write32;
+ arm_write16_fn_t write16;
+ arm_write8_fn_t write8;
+ arm_coproc_read_fn_t coprocessor_read;
+ arm_coproc_write_fn_t coprocessor_write;
+ arm_trigger_breakpoint_fn_t trigger_breakpoint;
+ bool wait_for_interrupt;
+ UINT32 irq_table_address;
+ UINT32 phased_opcode;
+ UINT32 phased_op_id;
+ UINT32 phase;
+ struct {
+ UINT32 addr;
+ UINT32 r15_off;
+ UINT32 last_bank;
+ UINT32 base_addr;
+ UINT32 cycle;
+ UINT32 num_regs;
+ } block;
+} arm7_t;
+
+typedef void (*arm7_handler_t)(arm7_t* cpu, UINT32 opcode);
+typedef struct {
+ arm7_handler_t handler;
+ char name[12];
+ char bitfield[33];
+} arm7_instruction_t;
+
+#define ARM_PHASED_NONE 0
+#define ARM_PHASED_FILL_PIPE 1
+#define ARM_PHASED_BLOCK_TRANSFER 2
+
+////////////////////////
+// User API Functions //
+////////////////////////
+
+// This function initializes the internal state needed for the arm7 core emulation
+static arm7_t arm7_init(void* user_data);
+static void arm7_exec_instruction(arm7_t* cpu);
+
+// Write the dissassembled opcode from mem_address into the out_disasm string up to out_size characters
+static void arm7_get_disasm(arm7_t * cpu, UINT32 mem_address, char* out_disasm, size_t out_size);
+// Used to send an interrupt to the emulated CPU. The n'th set bit triggers the n'th interrupt
+static void arm7_process_interrupts(arm7_t* cpu);
+///////////////////////////////////////////
+// Functions for Internal Implementation //
+///////////////////////////////////////////
+
+// ARM Instruction Implementations
+static void arm7_data_processing(arm7_t* cpu, UINT32 opcode);
+static void arm7_multiply(arm7_t* cpu, UINT32 opcode);
+static void arm7_multiply_long(arm7_t* cpu, UINT32 opcode);
+static void arm7_single_data_swap(arm7_t* cpu, UINT32 opcode);
+static void arm7_branch_exchange(arm7_t* cpu, UINT32 opcode);
+static void arm9_branch_link_exchange(arm7_t* cpu, UINT32 opcode);
+static void arm7_half_word_transfer(arm7_t* cpu, UINT32 opcode);
+static void arm7_single_word_transfer(arm7_t* cpu, UINT32 opcode);
+static void arm7_undefined(arm7_t* cpu, UINT32 opcode);
+static void arm7_block_transfer(arm7_t* cpu, UINT32 opcode);
+static void arm7_branch(arm7_t* cpu, UINT32 opcode);
+static void arm9_branch(arm7_t* cpu, UINT32 opcode);
+
+static void arm7_coproc_data_transfer(arm7_t* cpu, UINT32 opcode);
+static void arm7_coproc_data_op(arm7_t* cpu, UINT32 opcode);
+static void arm7_coproc_reg_transfer(arm7_t* cpu, UINT32 opcode);
+static void arm7_software_interrupt(arm7_t* cpu, UINT32 opcode);
+
+static void arm7_mrs(arm7_t* cpu, UINT32 opcode);
+static void arm7_msr(arm7_t* cpu, UINT32 opcode);
+
+static void arm9_clz(arm7_t* cpu, UINT32 opcode);
+static void arm9_double_word_transfer(arm7_t* cpu, UINT32 opcode);
+static FORCE_INLINE void arm9_qadd_qsub(arm7_t* cpu, UINT32 opcode);
+static FORCE_INLINE void arm9_signed_halfword_multiply(arm7_t* cpu, UINT32 opcode);
+static FORCE_INLINE void arm9_single_word_transfer(arm7_t* cpu, UINT32 opcode);
+static FORCE_INLINE void arm9_block_transfer(arm7_t* cpu, UINT32 opcode);
+// Thumb Instruction Implementations
+static void arm7t_mov_shift_reg(arm7_t* cpu, UINT32 opcode);
+static void arm7t_add_sub(arm7_t* cpu, UINT32 opcode);
+static void arm7t_mov_cmp_add_sub_imm(arm7_t* cpu, UINT32 opcode);
+static void arm7t_alu_op(arm7_t* cpu, UINT32 opcode);
+static void arm7t_hi_reg_op(arm7_t* cpu, UINT32 opcode);
+static void arm9t_hi_reg_op(arm7_t* cpu, UINT32 opcode);
+static void arm7t_pc_rel_ldst(arm7_t* cpu, UINT32 opcode);
+static void arm7t_reg_off_ldst(arm7_t* cpu, UINT32 opcode);
+static void arm7t_ldst_bh(arm7_t* cpu, UINT32 opcode);
+static void arm7t_imm_off_ldst(arm7_t* cpu, UINT32 opcode);
+static void arm7t_imm_off_ldst_bh(arm7_t* cpu, UINT32 opcode);
+static void arm7t_stack_off_ldst(arm7_t* cpu, UINT32 opcode);
+static void arm7t_load_addr(arm7_t* cpu, UINT32 opcode);
+static void arm7t_add_off_sp(arm7_t* cpu, UINT32 opcode);
+static void arm7t_push_pop_reg(arm7_t* cpu, UINT32 opcode);
+static void arm7t_mult_ldst(arm7_t* cpu, UINT32 opcode);
+static void arm7t_cond_branch(arm7_t* cpu, UINT32 opcode);
+static void arm7t_soft_interrupt(arm7_t* cpu, UINT32 opcode);
+static void arm7t_branch(arm7_t* cpu, UINT32 opcode);
+static void arm7t_long_branch_link(arm7_t* cpu, UINT32 opcode);
+static void arm7t_unknown(arm7_t* cpu, UINT32 opcode);
+
+static FORCE_INLINE void arm9t_mult_ldst(arm7_t* cpu, UINT32 opcode);
+static FORCE_INLINE void arm9t_push_pop_reg(arm7_t* cpu, UINT32 opcode);
+static FORCE_INLINE void arm9t_stack_off_ldst(arm7_t* cpu, UINT32 opcode);
+static FORCE_INLINE void arm9t_imm_off_ldst(arm7_t* cpu, UINT32 opcode);
+static FORCE_INLINE void arm9t_reg_off_ldst(arm7_t* cpu, UINT32 opcode);
+static FORCE_INLINE void arm9t_pc_rel_ldst(arm7_t* cpu, UINT32 opcode);
+// Internal functions
+static INT32 arm_lookup_arm_instruction_class(const arm7_instruction_t*instruction_table, UINT32 opcode_key);
+static INT32 arm_lookup_thumb_instruction_class(const arm7_instruction_t*instruction_table,UINT32 opcode_key);
+static FORCE_INLINE bool arm7_check_cond_code(arm7_t* cpu, UINT32 opcode);
+static FORCE_INLINE UINT32 arm7_reg_read(arm7_t*cpu, UINT32 reg);
+static FORCE_INLINE UINT32 arm7_reg_read_r15_adj(arm7_t*cpu, UINT32 reg, INT32 r15_off);
+static FORCE_INLINE void arm7_reg_write(arm7_t*cpu, UINT32 reg, UINT32 value);
+static FORCE_INLINE UINT32 arm7_reg_index(arm7_t* cpu, UINT32 reg);
+static FORCE_INLINE UINT32 arm7_shift(arm7_t* arm, UINT32 opcode, UINT64 value, UINT32 shift_value, INT32* carry);
+static FORCE_INLINE UINT32 arm7_load_shift_reg(arm7_t* arm, UINT32 opcode, INT32* carry);
+static FORCE_INLINE UINT32 arm7_rotr(UINT32 value, UINT32 rotate);
+static FORCE_INLINE bool arm7_get_thumb_bit(arm7_t* cpu);
+static FORCE_INLINE void arm7_set_thumb_bit(arm7_t* cpu, bool value);
+
+#define ARM7_BFE(VALUE, BITOFFSET, SIZE) (((VALUE) >> (BITOFFSET)) & ((1u << (SIZE)) - 1))
+
+// ARM7 ARM Classes
+const static arm7_instruction_t arm7_instruction_classes[] = {
+ { arm7_data_processing, "DP", "cccc0010oooSnnnnddddrrrrOOOOOOOO" },
+ { arm7_data_processing, "DP", "cccc00111ooSnnnnddddrrrrOOOOOOOO" },
+ { arm7_data_processing, "DP", "cccc00110oo1nnnnddddrrrrOOOOOOOO" },
+ //These duplications are to handle disambiguating bit 5 and 7 set to ones for DP
+ { arm7_data_processing, "DP", "cccc0000oooSnnnnddddsssssss0mmmm" },
+ { arm7_data_processing, "DP", "cccc0000oooSnnnnddddssss0tt1mmmm" },
+ //Handle TST, TEQ, CMP, CMN must set S case
+ { arm7_data_processing, "DP", "cccc00011ooSnnnnddddsssssss0mmmm" },
+ { arm7_data_processing, "DP", "cccc00011ooSnnnnddddssss0tt1mmmm" },
+ { arm7_data_processing, "DP", "cccc00010oo1nnnnddddssss0tt1mmmm" },
+ { arm7_data_processing, "DP", "cccc00010oo1nnnnddddsssssss0mmmm" },
+
+ { arm7_multiply, "MUL", "cccc000000ASddddnnnnssss1001mmmm" },
+ { arm7_multiply_long, "MLONG", "cccc00001UASddddnnnnssss1001mmmm" },
+ { arm7_single_data_swap, "SDS", "cccc00010B00nnnndddd00001001mmmm" },
+ { arm7_branch_exchange, "BX", "cccc000100101111111111110001nnnn" },
+
+ { arm7_undefined, "LDRD/STRD", "cccc000PUIW0nnnnddddoooo11S1oooo" },
+ { arm7_half_word_transfer, "HDT(h)", "cccc000PUIWLnnnndddd00001011mmmm" },
+ { arm7_half_word_transfer, "HDT(sb)", "cccc000PUIW1nnnnddddOOOO1101OOOO" },
+ { arm7_half_word_transfer, "HDT(sh)", "cccc000PUIW1nnnnddddOOOO1111OOOO" },
+ { arm7_single_word_transfer, "SDT", "cccc010PUBWLnnnnddddOOOOOOOOOOOO" },
+ { arm7_single_word_transfer, "SDT", "cccc011PUBWLnnnnddddOOOOOOO0mmmm" },
+
+ { arm7_undefined, "UDEF", "cccc011--------------------1----" },
+ { arm7_block_transfer, "BDT", "cccc100PUSWLnnnnllllllllllllllll" },
+ { arm7_branch, "B", "cccc1010OOOOOOOOOOOOOOOOOOOOOOOO" },
+ { arm7_branch, "BL", "cccc1011OOOOOOOOOOOOOOOOOOOOOOOO" },
+ { arm7_coproc_data_transfer, "CDT", "cccc110PUNWLnnnndddd####OOOOOOOO" },
+ { arm7_coproc_data_op, "CDO", "cccc1110oooonnnndddd####ppp0mmmm" },
+ { arm7_coproc_reg_transfer, "CRT", "cccc1110oooLnnnndddd####ppp1mmmm" },
+ { arm7_software_interrupt, "SWI", "cccc1111------------------------" },
+ { arm7_mrs, "MRS", "cccc00010P001111dddd000000000000" },
+ { arm7_msr, "MSR", "cccc00010P10100F111100000000mmmm" },
+ { arm7_msr, "MSR", "cccc00110P10100F1111oooooooooooo" },
+ { arm7_undefined, "UNKNOWN1", "cccc000001--------------1001----" },
+ { arm7_undefined, "UNKNOWN2", "cccc00011---------------1001----" },
+ { arm7_undefined, "UNKNOWN3", "cccc00010-1-------------1001----" },
+ { arm7_undefined, "UNKNOWN4", "cccc00010-01------------1001----" },
+ // Handle invalid opcode space in DP
+ { arm7_undefined, "UNKNOWN6", "cccc00010-00------------01-0----" },
+ { arm7_undefined, "UNKNOWN7", "cccc00010-00------------0010----" },
+
+ { arm7_undefined, "QADD/QSUB", "cccc00010oo0nnnndddd00000101mmmm" },
+ { arm7_undefined, "UNKNOWN8", "cccc00010-00------------00-1----" },
+ { arm7_undefined, "UNKNOWNN", "cccc00010-00------------0111----" },
+ { arm7_undefined, "SMLA", "cccc00010oo0ddddnnnnssss1yx0mmmm" },
+ { arm7_undefined, "UNKNOWNA", "cccc00010110------------01-0----" },
+ { arm7_undefined, "UNKNOWNB", "cccc00010110------------0010----" },
+ { arm7_undefined, "CLZ", "cccc000101101111DDDD11110001MMMM" },
+ { arm7_undefined, "UNKNOWNC", "cccc00010110------------0111----" },
+ { arm7_undefined, "UNKNOWND", "cccc00010110------------0011----" },
+ { arm7_undefined, "UNKNOWNE", "cccc00010010------------0111----" },
+ { arm7_undefined, "BLX", "cccc000100101111111111110011nnnn" },
+ { arm7_undefined, "UNKNOWNG", "cccc00010010------------01-0----" },
+ { arm7_undefined, "UNKNOWNH", "cccc00010010------------0010----" },
+ { arm7_undefined, "UNKNOWNI", "cccc00110-000000000000001-------" },
+ { arm7_undefined, "UNKNOWNJ", "cccc00110-0000000000000001------" },
+ { arm7_undefined, "UNKNOWNK", "cccc00110-00000000000000001-----" },
+ { arm7_undefined, "UNKNOWNL", "cccc00110-000000000000000001----" },
+ { arm7_undefined, "UNKNOWNM", "----00110-00------------0000----" },
+ {NULL},
+
+};
+
+// ARM7 ARM Classes
+const static arm7_instruction_t arm9_instruction_classes[]={
+ { arm7_data_processing, "DP", "cccc0010oooSnnnnddddrrrrOOOOOOOO" },
+ { arm7_data_processing, "DP", "cccc00111ooSnnnnddddrrrrOOOOOOOO" },
+ { arm7_data_processing, "DP", "cccc00110oo1nnnnddddrrrrOOOOOOOO" },
+ //These duplications are to handle disambiguating bit 5 and 7 set to ones for DP
+ { arm7_data_processing, "DP", "cccc0000oooSnnnnddddsssssss0mmmm" },
+ { arm7_data_processing, "DP", "cccc0000oooSnnnnddddssss0tt1mmmm" },
+ //Handle TST, TEQ, CMP, CMN must set S case
+ { arm7_data_processing, "DP", "cccc00011ooSnnnnddddsssssss0mmmm" },
+ { arm7_data_processing, "DP", "cccc00011ooSnnnnddddsssssss0mmmm" },
+ { arm7_data_processing, "DP", "cccc00011ooSnnnnddddssss0tt1mmmm" },
+ { arm7_data_processing, "DP", "cccc00010oo1nnnnddddssss0tt1mmmm" },
+ { arm7_data_processing, "DP", "cccc00010oo1nnnnddddsssssss0mmmm" },
+
+ { arm7_multiply, "MUL", "cccc000000ASddddnnnnssss1001mmmm" },
+ { arm7_multiply_long, "MLONG", "cccc00001UASddddnnnnssss1001mmmm" },
+ { arm7_single_data_swap, "SDS", "cccc00010B00nnnndddd00001001mmmm" },
+ { arm7_branch_exchange, "BX", "cccc000100101111111111110001nnnn" },
+
+ { arm9_double_word_transfer, "LDRD/STRD", "cccc000PUIW0nnnnddddoooo11S1oooo" },
+ { arm7_half_word_transfer, "HDT(h)", "cccc000PUIWLnnnndddd00001011mmmm" },
+ { arm7_half_word_transfer, "HDT(sb)", "cccc000PUIW1nnnnddddOOOO1101OOOO" },
+ { arm7_half_word_transfer, "HDT(sh)", "cccc000PUIW1nnnnddddOOOO1111OOOO" },
+ { arm9_single_word_transfer, "SDT", "cccc010PUBWLnnnnddddOOOOOOOOOOOO" },
+ { arm9_single_word_transfer, "SDT", "cccc011PUBWLnnnnddddOOOOOOO0mmmm" },
+
+ { arm7_undefined, "UDEF", "cccc011--------------------1----" },
+ { arm9_block_transfer, "BDT", "cccc100PUSWLnnnnllllllllllllllll" },
+ { arm9_branch, "B", "cccc1010OOOOOOOOOOOOOOOOOOOOOOOO" },
+ { arm9_branch, "BL", "cccc1011OOOOOOOOOOOOOOOOOOOOOOOO" },
+ { arm7_coproc_data_transfer, "CDT", "cccc110PUNWLnnnndddd####OOOOOOOO" },
+ { arm7_coproc_data_op, "CDO", "cccc1110oooonnnndddd####ppp0mmmm" },
+ { arm7_coproc_reg_transfer, "CRT", "cccc1110oooLnnnndddd####ppp1mmmm" },
+ { arm7_software_interrupt, "SWI", "cccc1111------------------------" },
+
+ { arm7_mrs, "MRS", "cccc00010P001111dddd000000000000" },
+ { arm7_msr, "MSR", "cccc00010P10100F111100000000mmmm" },
+ { arm7_msr, "MSR", "cccc00110P10100F1111oooooooooooo" },
+ { arm7_undefined, "UNKNOWN1", "cccc000001--------------1001----" },
+ { arm7_undefined, "UNKNOWN2", "cccc00011---------------1001----" },
+ { arm7_undefined, "UNKNOWN3", "cccc00010-1-------------1001----" },
+ { arm7_undefined, "UNKNOWN4", "cccc00010-01------------1001----" },
+ // Handle invalid opcode space in DP
+ { arm7_undefined, "UNKNOWN6", "cccc00010-00------------01-0----" },
+ { arm7_undefined, "UNKNOWN7", "cccc00010-00------------0010----" },
+
+ { arm9_qadd_qsub, "QADD/QSUB", "cccc00010oo0nnnndddd00000101mmmm" },
+ { arm7_undefined, "UNKNOWN8", "cccc00010-00------------00-1----" },
+ { arm7_undefined, "UNKNOWNN", "cccc00010-00------------0111----" },
+ { arm9_signed_halfword_multiply, "SMLA", "cccc00010oo0ddddnnnnssss1yx0mmmm" },
+ { arm7_undefined, "UNKNOWNA", "cccc00010110------------01-0----" },
+ { arm7_undefined, "UNKNOWNB", "cccc00010110------------0010----" },
+ { arm9_clz, "CLZ", "cccc000101101111DDDD11110001MMMM" },
+ { arm7_undefined, "UNKNOWNC", "cccc00010110------------0111----" },
+ { arm7_undefined, "UNKNOWND", "cccc00010110------------0011----" },
+ { arm7_undefined, "UNKNOWNE", "cccc00010010------------0111----" },
+ { arm9_branch_link_exchange, "BLX", "cccc000100101111111111110011nnnn" },
+ { arm7_undefined, "UNKNOWNG", "cccc00010010------------01-0----" },
+ { arm7_undefined, "UNKNOWNH", "cccc00010010------------0010----" },
+ { arm7_undefined, "UNKNOWNI", "cccc00110-000000000000001-------" },
+ { arm7_undefined, "UNKNOWNJ", "cccc00110-0000000000000001------" },
+ { arm7_undefined, "UNKNOWNK", "cccc00110-00000000000000001-----" },
+ { arm7_undefined, "UNKNOWNL", "cccc00110-000000000000000001----" },
+ { arm7_undefined, "UNKNOWNM", "----00110-00------------0000----" },
+ {NULL},
+};
+
+// ARM7 Thumb Classes
+const static arm7_instruction_t arm7t_instruction_classes[]={
+ { arm7t_mov_shift_reg, "LSL", "00000OOOOOsssddd" },
+ { arm7t_mov_shift_reg, "LSR", "00001OOOOOsssddd" },
+ { arm7t_mov_shift_reg, "ASR", "00010OOOOOsssddd" },
+ { arm7t_add_sub, "ADD", "00011I0nnnsssddd" },
+ { arm7t_add_sub, "SUB", "00011I1nnnsssddd" },
+ { arm7t_mov_cmp_add_sub_imm, "MCASIMM", "001oodddOOOOOOOO" },
+ { arm7t_alu_op, "ALU", "010000oooosssddd" },
+ { arm7t_hi_reg_op, "HROP", "010001oohHsssddd" },
+ { arm7t_pc_rel_ldst, "PCRLD", "01001dddOOOOOOOO" },
+ { arm7t_reg_off_ldst, "LDST[RD]", "0101LB0ooobbbddd" },
+ { arm7t_ldst_bh, "SLDST[RD]", "0101HS1ooobbbddd" },
+ { arm7t_imm_off_ldst, "LDST[IMM]", "011BLOOOOObbbddd" },
+ { arm7t_imm_off_ldst_bh, "SLDSTH[IMM]", "1000LOOOOObbbddd" },
+ { arm7t_stack_off_ldst, "LDST[SP]", "1001LdddOOOOOOOO" },
+ { arm7t_load_addr, "LDADDR", "1010SdddOOOOOOOO" },
+ { arm7t_add_off_sp, "SP+=OFF", "10110000SOOOOOOO" },
+ { arm7t_push_pop_reg, "PUSHPOPREG", "1011L10Rllllllll" },
+ { arm7t_mult_ldst, "MLDST", "1100Lbbbllllllll" },
+ // Conditional branches cant branch on condition 1111
+ { arm7t_cond_branch, "COND B", "11010cccOOOOOOOO" },
+ { arm7t_cond_branch, "COND B", "110110ccOOOOOOOO" },
+ { arm7t_cond_branch, "COND B", "1101110cOOOOOOOO" },
+ { arm7t_cond_branch, "COND B", "11011110OOOOOOOO" },
+ { arm7t_soft_interrupt, "SWI", "11011111OOOOOOOO" },
+ { arm7t_branch, "B", "11100OOOOOOOOOOO" },
+ { arm7t_long_branch_link, "BL", "1111HOOOOOOOOOOO" },
+ { arm7t_long_branch_link, "BLX", "11101OOOOOOOOOOO" },
+ //Empty Opcode Space
+ { arm7t_unknown, "UNKNOWN1", "1011--1---------" },
+ { arm7t_unknown, "UNKNOWN2", "10110001--------" },
+ { arm7t_unknown, "UNKNOWN3", "1011100---------" },
+ {NULL},
+};
+
+// ARM7 Thumb Classes
+const static arm7_instruction_t arm9t_instruction_classes[]={
+ { arm7t_mov_shift_reg, "LSL", "00000OOOOOsssddd" },
+ { arm7t_mov_shift_reg, "LSR", "00001OOOOOsssddd" },
+ { arm7t_mov_shift_reg, "ASR", "00010OOOOOsssddd" },
+ { arm7t_add_sub, "ADD", "00011I0nnnsssddd" },
+ { arm7t_add_sub, "SUB", "00011I1nnnsssddd" },
+ { arm7t_mov_cmp_add_sub_imm, "MCASIMM", "001oodddOOOOOOOO" },
+ { arm7t_alu_op, "ALU", "010000oooosssddd" },
+ { arm9t_hi_reg_op, "HROP", "010001oohHsssddd" },
+ { arm9t_pc_rel_ldst, "PCRLD", "01001dddOOOOOOOO" },
+ { arm9t_reg_off_ldst, "LDST[RD]", "0101LB0ooobbbddd" },
+ { arm7t_ldst_bh, "SLDST[RD]", "0101HS1ooobbbddd" },
+ { arm9t_imm_off_ldst, "LDST[IMM]", "011BLOOOOObbbddd" },
+ { arm7t_imm_off_ldst_bh, "SLDSTH[IMM]", "1000LOOOOObbbddd" },
+ { arm9t_stack_off_ldst, "LDST[SP]", "1001LdddOOOOOOOO" },
+ { arm7t_load_addr, "LDADDR", "1010SdddOOOOOOOO" },
+ { arm7t_add_off_sp, "SP+=OFF", "10110000SOOOOOOO" },
+ { arm9t_push_pop_reg, "PUSHPOPREG", "1011L10Rllllllll" },
+ { arm9t_mult_ldst, "MLDST", "1100Lbbbllllllll" },
+ // Conditional branches cant branch on condition 1111
+ { arm7t_cond_branch, "COND B", "11010cccOOOOOOOO" },
+ { arm7t_cond_branch, "COND B", "110110ccOOOOOOOO" },
+ { arm7t_cond_branch, "COND B", "1101110cOOOOOOOO" },
+ { arm7t_cond_branch, "COND B", "11011110OOOOOOOO" },
+ { arm7t_soft_interrupt, "SWI", "11011111OOOOOOOO" },
+ { arm7t_branch, "B", "11100OOOOOOOOOOO" },
+ { arm7t_long_branch_link, "BL", "1111HOOOOOOOOOOO" },
+ { arm7t_long_branch_link, "BLX", "11101OOOOOOOOOOO" },
+ //Empty Opcode Space
+ { arm7t_unknown, "UNKNOWN1", "1011--1---------" },
+ { arm7t_unknown, "UNKNOWN2", "10110001--------" },
+ { arm7t_unknown, "UNKNOWN3", "1011100---------" },
+ {NULL},
+};
+
+static arm7_handler_t arm7_lookup_table[4096] = { 0 };
+static arm7_handler_t arm9_lookup_table[4096] = { 0 };
+static arm7_handler_t arm7t_lookup_table[256] = { 0 };
+static arm7_handler_t arm9t_lookup_table[256] = { 0 };
+
+static const char* arm7_disasm_lookup_table[4096] = { 0 };
+static const char* arm9_disasm_lookup_table[4096] = { 0 };
+static const char* arm7t_disasm_lookup_table[256] = { 0 };
+static const char* arm9t_disasm_lookup_table[256] = { 0 };
+
+static FORCE_INLINE UINT32 arm7_reg_index(arm7_t* cpu, UINT32 reg)
+{
+ if (SB_LIKELY(reg < 8))
+ return reg;
+ INT32 mode = cpu->registers[CPSR] & 0xf;
+
+ const static INT8 lookup[10 * 16 + 8] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, //8 extra padding to remove the need to -8 from computation
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 16, //mode 0x0 (user)
+ 17, 18, 19, 20, 21, 22, 23, 15, 16, 32, //mode 0x1 (fiq)
+ 8, 9, 10, 11, 12, 24, 25, 15, 16, 33, //mode 0x2 (irq)
+ 8, 9, 10, 11, 12, 26, 27, 15, 16, 34, //mode 0x3 (svc)
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //mode 0x4 (inv)
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //mode 0x5 (inv)
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //mode 0x6 (inv)
+ 8, 9, 10, 11, 12, 28, 29, 15, 16, 35, //mode 0x7 (abt)
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //mode 0x8 (inv)
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //mode 0x9 (inv)
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //mode 0xA (inv)
+ 8, 9, 10, 11, 12, 30, 31, 15, 16, 36, //mode 0xB (undefined)
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //mode 0xC (inv)
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //mode 0xD (inv)
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //mode 0xE (inv)
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 16, //mode 0xF (system)
+ };
+ INT8 r = lookup[mode * 10 + reg];
+ if (SB_LIKELY(r != -1))return r;
+ if (cpu->trigger_breakpoint)cpu->trigger_breakpoint(cpu->user_data);
+ printf("Undefined ARM mode: %d\n", mode);
+ return 0;
+}
+
+static FORCE_INLINE void arm7_reg_write(arm7_t* cpu, UINT32 reg, UINT32 value)
+{
+ cpu->registers[arm7_reg_index(cpu, reg)] = value;
+}
+
+static FORCE_INLINE void arm9_reg_write_r15_thumb(arm7_t* cpu, UINT32 reg, UINT32 value)
+{
+ cpu->registers[arm7_reg_index(cpu, reg)] = value;
+ if (SB_UNLIKELY(reg == PC))arm7_set_thumb_bit(cpu, value & 1);
+}
+
+static FORCE_INLINE UINT32 arm7_reg_read(arm7_t* cpu, UINT32 reg)
+{
+ return cpu->registers[arm7_reg_index(cpu, reg)];
+}
+
+static FORCE_INLINE UINT32 arm7_reg_read_r15_adj(arm7_t* cpu, UINT32 reg, INT32 r15_off)
+{
+ UINT32 v = arm7_reg_read(cpu, reg);
+ if (SB_UNLIKELY(reg == PC)) {
+ v += r15_off;
+ if (arm7_get_thumb_bit(cpu))v -= 2;
+ }
+ return v;
+}
+
+static void arm7_print_binary(INT32 number, INT32 bits)
+{
+ for (INT32 i = 0; i < bits; ++i) {
+ bool v = (number >> (bits - i - 1)) & 1;
+ printf("%d", v);
+ }
+}
+
+static INT32 arm_lookup_arm_instruction_class(const arm7_instruction_t* instruction_table, UINT32 opcode_key)
+{
+ INT32 key_bits[] = { 4,5,6,7, 20,21,22,23,24,25,26,27 };
+ INT32 matched_class = -1;
+ for (INT32 c = 0; instruction_table[c].handler; ++c) {
+ bool matches = true;
+ for (INT32 bit = 0; bit < ARRAY_SIZE(key_bits); ++bit) {
+ bool bit_value = (opcode_key >> bit) & 1;
+ INT32 b_off = key_bits[bit];
+ matches &= instruction_table[c].bitfield[31 - b_off] != '1' || bit_value == true;
+ matches &= instruction_table[c].bitfield[31 - b_off] != '0' || bit_value == false;
+ if (!matches)
+ break;
+ }
+ if (matches) {
+ if (matched_class != -1) {
+ INT32 op_value = 0;
+ char opcode[33] = "00000000000000000000000000000000";
+ for (INT32 bit = 0; bit < ARRAY_SIZE(key_bits); ++bit) {
+ bool bit_value = (opcode_key >> bit) & 1;
+ if (bit_value) {
+ opcode[31 - key_bits[bit]] = '1';
+ op_value |= 1 << key_bits[bit];
+ }
+ }
+ printf("ARM7: Class %s and %s have ambiguous encodings for: %s %08x %d\n",
+ instruction_table[c].name,
+ instruction_table[matched_class].name,
+ opcode, op_value, opcode_key);
+ }
+ matched_class = c;
+ }
+ }
+ if (matched_class == -1) {
+ UINT32 op_value = 0;
+ char opcode[33] = "00000000000000000000000000000000";
+ for (INT32 bit = 0; bit < ARRAY_SIZE(key_bits); ++bit) {
+ bool bit_value = (opcode_key >> bit) & 1;
+ if (bit_value) {
+ opcode[31 - key_bits[bit]] = '1';
+ op_value |= 1 << key_bits[bit];
+ }
+ }
+ printf("ARM: No matching instruction class for key: %s %08x\n", opcode, op_value);
+ }
+ return matched_class;
+}
+
+static INT32 arm_lookup_thumb_instruction_class(const arm7_instruction_t* instruction_table, UINT32 opcode_key)
+{
+ INT32 key_bits[] = { 8,9,10,11,12,13,14,15 };
+ INT32 matched_class = -1;
+ for (INT32 c = 0; instruction_table[c].handler; ++c) {
+ bool matches = true;
+ for (INT32 bit = 0; bit < ARRAY_SIZE(key_bits); ++bit) {
+ bool bit_value = (opcode_key >> bit) & 1;
+ INT32 b_off = key_bits[bit];
+ matches &= instruction_table[c].bitfield[15 - b_off] != '1' || bit_value == true;
+ matches &= instruction_table[c].bitfield[15 - b_off] != '0' || bit_value == false;
+ if (!matches)
+ break;
+ }
+
+ if (matches) {
+ if (matched_class != -1) {
+ printf("ARM7t: Class %s and %s have ambiguous encodings\n",
+ instruction_table[c].name,
+ instruction_table[matched_class].name);
+ }
+ matched_class = c;
+ }
+ }
+ if (matched_class == -1) {
+ UINT32 op_value = 0;
+ char opcode[17] = "0000000000000000";
+ for (INT32 bit = 0; bit < ARRAY_SIZE(key_bits); ++bit) {
+ bool bit_value = (opcode_key >> bit) & 1;
+ if (bit_value) {
+ opcode[15 - key_bits[bit]] = '1';
+ op_value |= 1 << key_bits[bit];
+ }
+ }
+ printf("ARM7T: No matching instruction class for key: %s %04x\n", opcode, op_value);
+ }
+ return matched_class;
+}
+static arm7_t arm7_init(void* user_data)
+{
+ // Generate ARM lookup table
+ for (INT32 i = 0; i < 4096; ++i) {
+ INT32 inst_class = arm_lookup_arm_instruction_class(arm7_instruction_classes, i);
+ arm7_lookup_table[i] = inst_class == -1 ? NULL : arm7_instruction_classes[inst_class].handler;
+ arm7_disasm_lookup_table[i] = inst_class == -1 ? NULL : arm7_instruction_classes[inst_class].name;
+ }
+ for (INT32 i = 0; i < 4096; ++i) {
+ INT32 inst_class = arm_lookup_arm_instruction_class(arm9_instruction_classes, i);
+ arm9_lookup_table[i] = inst_class == -1 ? NULL : arm9_instruction_classes[inst_class].handler;
+ arm9_disasm_lookup_table[i] = inst_class == -1 ? NULL : arm9_instruction_classes[inst_class].name;
+ }
+ // Generate Thumb Lookup Table
+ for (INT32 i = 0; i < 256; ++i) {
+ INT32 inst_class = arm_lookup_thumb_instruction_class(arm7t_instruction_classes, i);
+ arm7t_lookup_table[i] = inst_class == -1 ? NULL : arm7t_instruction_classes[inst_class].handler;
+ arm7t_disasm_lookup_table[i] = inst_class == -1 ? NULL : arm7t_instruction_classes[inst_class].name;
+ }
+ // Generate Thumb Lookup Table
+ for (INT32 i = 0; i < 256; ++i) {
+ INT32 inst_class = arm_lookup_thumb_instruction_class(arm9t_instruction_classes, i);
+ arm9t_lookup_table[i] = inst_class == -1 ? NULL : arm9t_instruction_classes[inst_class].handler;
+ arm9t_disasm_lookup_table[i] = inst_class == -1 ? NULL : arm9t_instruction_classes[inst_class].name;
+ }
+ arm7_t arm = {};
+ arm.user_data = user_data;
+ arm.prefetch_pc = -1;
+ arm.phase = 0;
+ arm.phased_op_id = ARM_PHASED_FILL_PIPE;
+ return arm;
+
+}
+static FORCE_INLINE bool arm7_get_thumb_bit(arm7_t* cpu){return ARM7_BFE(cpu->registers[CPSR],5,1);}
+static FORCE_INLINE void arm7_set_thumb_bit(arm7_t* cpu, bool value) {
+ cpu->registers[CPSR] &= ~(1 << 5);
+ if (value)
+ cpu->registers[CPSR] |= 1 << 5;
+}
+static FORCE_INLINE void arm7_process_interrupts(arm7_t* cpu){
+ cpu->wait_for_interrupt=false;
+ UINT32 cpsr = cpu->registers[CPSR];
+ bool I = ARM7_BFE(cpsr,7,1);
+ if(I==0&&cpu->phased_op_id==0){
+ if(SB_UNLIKELY(cpu->log_cmp_file))return;
+ //Interrupts are enabled when I ==0
+ bool thumb = arm7_get_thumb_bit(cpu);
+ cpu->registers[R14_irq] = cpu->registers[PC]+4;
+ cpu->registers[PC] = cpu->irq_table_address+ 0x18;
+ cpu->registers[SPSR_irq] = cpsr;
+ //Update mode to IRQ
+ cpu->registers[CPSR] = (cpsr&0xffffffE0)| 0x12;
+ //Disable interrupts(set I bit)
+ cpu->registers[CPSR] |= 1<<7;
+ cpu->i_cycles+=1;
+ arm7_set_thumb_bit(cpu,false);
+ cpu->phased_op_id = ARM_PHASED_FILL_PIPE;
+ cpu->phase=0;
+ }
+}
+static void arm9_get_disasm(arm7_t* cpu, UINT32 mem_address, char* out_disasm, size_t out_size)
+{
+ out_disasm[0] = '\0';
+ const char* cond_code = "";
+ const char* name = "INVALID";
+ const char* key_str = NULL;
+ UINT32 opcode = 0;
+ if (arm7_get_thumb_bit(cpu) == false) {
+ opcode = cpu->read32(cpu->user_data, mem_address);
+
+ const char* cond_code_table[16] = { "EQ","NE","CS","CC","MI","PL","VS","VC","HI","LS","GE","LT","GT","LE","","INV" };
+ cond_code = cond_code_table[ARM7_BFE(opcode, 28, 4)];
+
+ UINT32 key = ((opcode >> 4) & 0xf) | ((opcode >> 16) & 0xff0);
+ INT32 instr_class = arm_lookup_arm_instruction_class(arm9_instruction_classes, key);
+ name = instr_class == -1 ? "INVALID" : arm9_instruction_classes[instr_class].name;
+ key_str = instr_class == -1 ? "" : arm9_instruction_classes[instr_class].bitfield;
+ // Get more information for the DP class instruction
+ if (strcmp(name, "DP") == 0) {
+ const char* op_name[] = { "AND", "EOR", "SUB", "RSB",
+ "ADD", "ADC", "SBC", "RSC",
+ "TST", "TEQ", "CMP", "CMN",
+ "ORR", "MOV", "BIC", "MVN" };
+ name = op_name[ARM7_BFE(opcode, 21, 4)];
+ }
+ } else {
+ opcode = cpu->read16(cpu->user_data, mem_address);
+ UINT32 key = ((opcode >> 8) & 0xff);
+ INT32 instr_class = arm_lookup_thumb_instruction_class(arm9t_instruction_classes, key);
+ name = instr_class == -1 ? "INVALID" : arm9t_instruction_classes[instr_class].name;
+ key_str = instr_class == -1 ? "" : arm9t_instruction_classes[instr_class].bitfield;
+ }
+ INT32 offset = snprintf(out_disasm, out_size, "%s%s ", name, cond_code);
+ bool letter_handled[256];
+ for (INT32 i = 0; i < 256; ++i)
+ letter_handled[i] = false;
+ letter_handled['1'] = true;
+ letter_handled['0'] = true;
+ letter_handled['c'] = true;
+ INT32 key_len = strlen(key_str);
+ INT32 key_off = 0;
+ while (key_str[key_off]) {
+ char letter = key_str[key_off];
+ INT32 value = 0;
+ INT32 key_off2 = key_off;
+ if (letter_handled[letter] == false) {
+ while (key_str[key_off2]) {
+ if (key_str[key_off2] == letter) {
+ value <<= 1;
+ value |= SB_BFE(opcode, key_len - 1 - key_off2, 1);
+ }
+ ++key_off2;
+ }
+ letter_handled[letter] = true;
+ offset += snprintf(out_disasm + offset, out_size - offset, "%c:%d ", letter, value);
+ }
+ ++key_off;
+ }
+ out_disasm[out_size - 1] = 0;
+ return;
+}
+
+static FORCE_INLINE bool arm7_check_cond_code(arm7_t* cpu, UINT32 opcode)
+{
+ UINT32 cond_code = ARM7_BFE(opcode, 28, 4);
+ if (SB_LIKELY(cond_code == 0xE))return true;
+ UINT32 cpsr = cpu->registers[CPSR];
+ bool N = ARM7_BFE(cpsr, 31, 1);
+ bool Z = ARM7_BFE(cpsr, 30, 1);
+ bool C = ARM7_BFE(cpsr, 29, 1);
+ bool V = ARM7_BFE(cpsr, 28, 1);
+ switch (cond_code) {
+ case 0x0: return Z; //EQ: Equal
+ case 0x1: return !Z; //NE: !Equal
+ case 0x2: return C; //CS: Unsigned >=
+ case 0x3: return !C; //CC: Unsigned <
+ case 0x4: return N; //MI: Negative
+ case 0x5: return !N; //PL: Positive or Zero
+ case 0x6: return V; //VS: Overflow
+ case 0x7: return !V; //VC: No Overflow
+ case 0x8: return C && !Z; //HI: Unsigned >
+ case 0x9: return !C || Z; //LS: Unsigned <=
+ case 0xA: return N == V; //GE: Signed >=
+ case 0xB: return N != V; //LT: Signed <
+ case 0xC: return !Z && (N == V); //GT: Signed >
+ case 0xD: return Z || (N != V); //LE: Signed <=
+ case 0xE: return true;
+ case 0xF: return true;
+ };
+ return false;
+}
+
+static void arm_check_log_file(arm7_t* cpu)
+{
+ bool thumb = arm7_get_thumb_bit(cpu);
+ fseek(cpu->log_cmp_file, (cpu->executed_instructions) * 18 * 4, SEEK_SET);
+ UINT32 cmp_regs[18];
+ if (fread(cmp_regs, 18 * 4, 1, cpu->log_cmp_file) == 1) {
+ UINT32 regs[18];
+ for (INT32 i = 0; i < 18; ++i)regs[i] = arm7_reg_read(cpu, i);
+ if (arm7_get_thumb_bit(cpu) == false) {
+ UINT32 oldpc = cpu->registers[PC];
+ cmp_regs[15] -= 8;
+ } else {
+ UINT32 oldpc = cpu->registers[PC];
+ cmp_regs[15] -= 4;
+ }
+ UINT32 cpsr = cmp_regs[16];
+ bool has_spsr = arm7_reg_index(cpu, SPSR) != CPSR;
+ if (!has_spsr) {
+ cmp_regs[SPSR] = arm7_reg_read(cpu, SPSR);
+ }
+ bool matches = true;
+ for (INT32 i = 0; i < 18; ++i)matches &= cmp_regs[i] == regs[i];
+ if (!matches) {
+ static INT32 last_pc_break = 0;
+ if (last_pc_break != cpu->registers[PC])
+ if (cpu->trigger_breakpoint)
+ cpu->trigger_breakpoint(cpu->user_data);
+ last_pc_break = cpu->registers[PC];
+ printf("Log mismatch detected\n");
+ printf("=====================\n");
+
+ printf("After %llu executed_instructions\n", cpu->executed_instructions);
+
+ char* rnames[18] = {
+ "R0","R1","R2", "R3", "R4", "R5", "R6", "R7",
+ "R8","R9","R10","R11","R12","R13","R14","R15",
+ "CPSR","SPSR"
+ };
+ INT32 last_instruction = cpu->executed_instructions - 1;
+ if (last_instruction < 0)last_instruction = 0;
+ fseek(cpu->log_cmp_file, (last_instruction) * 18 * 4, SEEK_SET);
+ UINT32 prev_regs[18];
+ fread(prev_regs, 18 * 4, 1, cpu->log_cmp_file);
+
+ for (INT32 i = 0; i < 18; ++i) {
+ if (regs[i] != cmp_regs[i])
+ printf("%s %d (%08x) Log Value = %d (%08x) Prev Value =%d (%08x)\n", rnames[i], regs[i], regs[i], cmp_regs[i], cmp_regs[i], prev_regs[i], prev_regs[i]);
+ else
+ printf("Matches %s %d (%08x) Prev Value =%d (%08x)\n", rnames[i], regs[i], regs[i], prev_regs[i], prev_regs[i]);
+ }
+ UINT32 log_cpsr = cmp_regs[16];
+ UINT32 prev_cpsr = prev_regs[16];
+
+ printf("N:%d LogN:%d PrevN:%d Z:%d LogZ:%d PrevZ:%d C:%d LogC:%d PrevC:%d V:%d LogV:%d PrevV:%d\n",
+ ARM7_BFE(cpsr, 31, 1), ARM7_BFE(log_cpsr, 31, 1), ARM7_BFE(prev_cpsr, 31, 1),
+ ARM7_BFE(cpsr, 30, 1), ARM7_BFE(log_cpsr, 30, 1), ARM7_BFE(prev_cpsr, 30, 1),
+ ARM7_BFE(cpsr, 29, 1), ARM7_BFE(log_cpsr, 29, 1), ARM7_BFE(prev_cpsr, 29, 1),
+ ARM7_BFE(cpsr, 28, 1), ARM7_BFE(log_cpsr, 28, 1), ARM7_BFE(prev_cpsr, 28, 1)
+ );
+ // Set CPSR first
+ arm7_reg_write(cpu, CPSR, cmp_regs[CPSR]);
+ for (INT32 i = 0; i < 18; ++i) {
+ arm7_reg_write(cpu, i, cmp_regs[i]);
+ }
+ INT32 log_mode = log_cpsr & 0x1f;
+ INT32 prev_mode = prev_cpsr & 0x1f;
+ if (log_mode == 0x12 && prev_mode != 0x12) {
+ printf("Interrupt!\n");
+ if (cpu->trigger_breakpoint)cpu->trigger_breakpoint(cpu->user_data);
+ }
+ thumb = arm7_get_thumb_bit(cpu);
+ if (thumb) {
+ cpu->registers[PC] &= ~1;
+ cpu->prefetch_opcode[0] = cpu->read16_seq(cpu->user_data, cpu->registers[PC] + 0, false);
+ cpu->prefetch_opcode[1] = cpu->read16_seq(cpu->user_data, cpu->registers[PC] + 2, true);
+ cpu->prefetch_opcode[2] = cpu->read16_seq(cpu->user_data, cpu->registers[PC] + 4, true);
+ }
+ else {
+ cpu->registers[PC] &= ~3;
+ cpu->prefetch_opcode[0] = cpu->read32_seq(cpu->user_data, cpu->registers[PC] + 0, false);
+ cpu->prefetch_opcode[1] = cpu->read32_seq(cpu->user_data, cpu->registers[PC] + 4, true);
+ cpu->prefetch_opcode[2] = cpu->read32_seq(cpu->user_data, cpu->registers[PC] + 8, true);
+ }
+ }
+ }
+ else {
+ printf("Log finished!\n");
+ fclose(cpu->log_cmp_file);
+ cpu->log_cmp_file = NULL;
+ }
+ UINT32 opcode = cpu->prefetch_opcode[0];
+ char disasm[128];
+ arm9_get_disasm(cpu, cpu->registers[PC], disasm, 128);
+ if (thumb == false) {
+ UINT32 key = ((opcode >> 4) & 0xf) | ((opcode >> 16) & 0xff0);
+ printf("ARM OP: %08x PC: %08x %s Binary: ", opcode, cpu->registers[PC], disasm);
+ arm7_print_binary(opcode, 32);
+ printf("\n");
+ } else {
+ UINT32 key = ((opcode >> 8) & 0xff);
+ printf("THUMB OP: %04x PC: %08x %s Binary: ", opcode, cpu->registers[PC], disasm);
+ arm7_print_binary(opcode, 16);
+ printf("\n");
+ }
+ cpu->executed_instructions++;
+}
+
+static FORCE_INLINE void arm7_fill_pipeline(arm7_t* cpu)
+{
+ bool thumb = arm7_get_thumb_bit(cpu);
+ if (thumb) {
+ cpu->registers[PC] &= ~1;
+ cpu->prefetch_opcode[cpu->phase] = cpu->read16_seq(cpu->user_data, cpu->registers[PC] + 2 * cpu->phase, cpu->phase != 0);
+ } else {
+ cpu->registers[PC] &= ~3;
+ cpu->prefetch_opcode[cpu->phase] = cpu->read32_seq(cpu->user_data, cpu->registers[PC] + 4 * cpu->phase, cpu->phase != 0);
+ }
+ ++cpu->phase;
+ if (cpu->phase != 2)
+ return;
+ cpu->debug_branch_ring[(cpu->debug_branch_ring_offset++) % ARM_DEBUG_BRANCH_RING_SIZE] = cpu->registers[PC];
+ cpu->phase = 0;
+ cpu->phased_op_id = 0;
+ cpu->prefetch_pc = cpu->registers[PC];
+ cpu->next_fetch_sequential = true;
+}
+
+static FORCE_INLINE bool arm7_run_phased_opcode(arm7_t* cpu)
+{
+ switch (cpu->phased_op_id) {
+ case ARM_PHASED_NONE:
+ return true;
+ case ARM_PHASED_FILL_PIPE:
+ arm7_fill_pipeline(cpu);
+ break;
+ case ARM_PHASED_BLOCK_TRANSFER:
+ arm7_block_transfer(cpu, cpu->phased_opcode);
+ break;
+ default:
+ cpu->phased_op_id = 0;
+ return true;
+ }
+ return false;
+}
+
+static FORCE_INLINE void arm7_exec_instruction(arm7_t* cpu)
+{
+ bool thumb = arm7_get_thumb_bit(cpu);
+ if (SB_LIKELY(arm7_run_phased_opcode(cpu))) {
+ if (SB_UNLIKELY(cpu->wait_for_interrupt)) {
+ cpu->i_cycles += 1;
+ return;
+ }
+ if (SB_UNLIKELY(cpu->log_cmp_file)) {
+ arm_check_log_file(cpu);
+ }
+ cpu->next_fetch_sequential = true;
+ UINT32 opcode = cpu->prefetch_opcode[0];
+ cpu->prefetch_opcode[0] = cpu->prefetch_opcode[1];
+ cpu->prefetch_opcode[1] = cpu->prefetch_opcode[2];
+ if (thumb == false) {
+ cpu->registers[PC] += 4;
+ cpu->prefetch_pc = cpu->registers[PC];
+ if (SB_LIKELY(arm7_check_cond_code(cpu, opcode))) {
+ UINT32 key = ((opcode >> 4) & 0xf) | ((opcode >> 16) & 0xff0);
+ arm7_lookup_table[key](cpu, opcode);
+ }
+ } else {
+ cpu->registers[PC] += 2;
+ cpu->prefetch_pc = cpu->registers[PC];
+ UINT32 key = ((opcode >> 8) & 0xff);
+ arm7t_lookup_table[key](cpu, opcode);
+ }
+ if (SB_UNLIKELY(cpu->step_instructions)) {
+ --cpu->step_instructions;
+ if (cpu->step_instructions == 0) {
+ if (cpu->trigger_breakpoint)
+ cpu->trigger_breakpoint(cpu->user_data);
+ }
+ }
+ }
+ if (SB_UNLIKELY(cpu->phased_op_id))
+ return;
+ if (thumb == false) {
+ if (SB_LIKELY(cpu->prefetch_pc == cpu->registers[PC]))
+ cpu->prefetch_opcode[2] = cpu->read32_seq(cpu->user_data, cpu->registers[PC] + 8, cpu->next_fetch_sequential);
+ else
+ cpu->phased_op_id = ARM_PHASED_FILL_PIPE;
+ } else {
+ if (SB_LIKELY(cpu->prefetch_pc == cpu->registers[PC]))
+ cpu->prefetch_opcode[2] = cpu->read16_seq(cpu->user_data, cpu->registers[PC] + 4, cpu->next_fetch_sequential);
+ else
+ cpu->phased_op_id = ARM_PHASED_FILL_PIPE;
+ }
+}
+
+static FORCE_INLINE void arm9_fill_pipeline(arm7_t* cpu)
+{
+ bool thumb = arm7_get_thumb_bit(cpu);
+ if (thumb) {
+ cpu->registers[PC] &= ~1;
+ cpu->prefetch_opcode[cpu->phase] = cpu->read16_seq(cpu->user_data, cpu->registers[PC] + 2 * cpu->phase, cpu->phase != 0);
+ } else {
+ cpu->registers[PC] &= ~3;
+ cpu->prefetch_opcode[cpu->phase] = cpu->read32_seq(cpu->user_data, cpu->registers[PC] + 4 * cpu->phase, cpu->phase != 0);
+ }
+ ++cpu->phase;
+ if (cpu->phase != 4)
+ return;
+ cpu->debug_branch_ring[(cpu->debug_branch_ring_offset++) % ARM_DEBUG_BRANCH_RING_SIZE] = cpu->registers[PC];
+ cpu->phase = 0;
+ cpu->phased_op_id = 0;
+ cpu->prefetch_pc = cpu->registers[PC];
+ cpu->next_fetch_sequential = true;
+}
+
+static FORCE_INLINE bool arm9_run_phased_opcode(arm7_t* cpu)
+{
+ switch (cpu->phased_op_id) {
+ case ARM_PHASED_NONE:
+ return true;
+ case ARM_PHASED_FILL_PIPE:
+ arm9_fill_pipeline(cpu);
+ break;
+ case ARM_PHASED_BLOCK_TRANSFER:
+ arm9_block_transfer(cpu, cpu->phased_opcode);
+ break;
+ default:
+ cpu->phased_op_id = 0;
+ return true;
+ }
+ return false;
+}
+
+static void arm9_exec_instruction(arm7_t* cpu) {
+ bool thumb = arm7_get_thumb_bit(cpu);
+ if (SB_LIKELY(arm9_run_phased_opcode(cpu))) {
+ if (cpu->wait_for_interrupt) {
+ cpu->i_cycles += 1;
+ return;
+ }
+
+ if (SB_UNLIKELY(cpu->log_cmp_file)) {
+ arm_check_log_file(cpu);
+ thumb = arm7_get_thumb_bit(cpu);
+ }
+
+ cpu->next_fetch_sequential = true;
+ UINT32 opcode = cpu->prefetch_opcode[0];
+ cpu->prefetch_opcode[0] = cpu->prefetch_opcode[1];
+ cpu->prefetch_opcode[1] = cpu->prefetch_opcode[2];
+ cpu->prefetch_opcode[2] = cpu->prefetch_opcode[3];
+ cpu->prefetch_opcode[3] = cpu->prefetch_opcode[4];
+ if (thumb == false) {
+ cpu->registers[PC] += 4;
+ cpu->prefetch_pc = cpu->registers[PC];
+ if (arm7_check_cond_code(cpu, opcode)) {
+ UINT32 key = ((opcode >> 4) & 0xf) | ((opcode >> 16) & 0xff0);
+ arm9_lookup_table[key](cpu, opcode);
+ }
+ } else {
+ cpu->registers[PC] += 2;
+ cpu->prefetch_pc = cpu->registers[PC];
+ UINT32 key = ((opcode >> 8) & 0xff);
+ arm9t_lookup_table[key](cpu, opcode);
+ }
+ if (SB_UNLIKELY(cpu->step_instructions)) {
+ --cpu->step_instructions;
+ if (cpu->step_instructions == 0) {
+ if (cpu->trigger_breakpoint)cpu->trigger_breakpoint(cpu->user_data);
+ }
+ }
+ }
+ if (SB_UNLIKELY(cpu->phased_op_id))
+ return;
+ if (thumb == false) {
+ if (SB_LIKELY(cpu->prefetch_pc == cpu->registers[PC]))
+ cpu->prefetch_opcode[2] = cpu->read32_seq(cpu->user_data, cpu->registers[PC] + 8, cpu->next_fetch_sequential);
+ else
+ cpu->phased_op_id = ARM_PHASED_FILL_PIPE;
+ } else {
+ if (SB_LIKELY(cpu->prefetch_pc == cpu->registers[PC]))
+ cpu->prefetch_opcode[2] = cpu->read16_seq(cpu->user_data, cpu->registers[PC] + 4, cpu->next_fetch_sequential);
+ else
+ cpu->phased_op_id = ARM_PHASED_FILL_PIPE;
+ }
+}
+
+static FORCE_INLINE UINT32 arm7_rotr(UINT32 value, UINT32 rotate)
+{
+ return ((UINT64)value >> (rotate & 31)) | ((UINT64)value << (32 - (rotate & 31)));
+}
+
+static FORCE_INLINE UINT32 arm7_load_shift_reg(arm7_t* arm, UINT32 opcode, INT32* carry)
+{
+ UINT32 value = arm7_reg_read(arm, ARM7_BFE(opcode, 0, 4));
+ UINT32 shift_value = 0;
+ if (ARM7_BFE(opcode, 4, 1) == true) {
+ INT32 rs = ARM7_BFE(opcode, 8, 4);
+ shift_value = arm7_reg_read(arm, rs);
+ } else {
+ shift_value = ARM7_BFE(opcode, 7, 5);
+ }
+ return arm7_shift(arm, opcode, value, shift_value, carry);
+}
+
+static FORCE_INLINE UINT32 arm7_shift(arm7_t* arm, UINT32 opcode, UINT64 value, UINT32 shift_value, INT32* carry)
+{
+ INT32 shift_type = ARM7_BFE(opcode, 5, 2);
+ // Shift value of 0 has special behavior from a register:
+ // If this byte is zero, the unchanged contents of Rm will be used as the second operand,
+ // and the old value of the CPSR C flag will be passed on as the shifter carry output.
+ if (shift_value == 0 && (ARM7_BFE(opcode, 4, 1) || shift_type == 0)) {
+ *carry = -1;
+ return value;
+ }
+ switch (shift_type) {
+ case 0:
+ if (shift_value > 32) {
+ *carry = 0;
+ value = 0;
+ } else {
+ *carry = shift_value == 0 ? -1 : ARM7_BFE(value, 32 - shift_value, 1);
+ value = value << shift_value;
+ }
+ break;
+ case 1:
+ if (shift_value > 32) {
+ *carry = 0;
+ value = 0;
+ } else {
+ if (shift_value == 0) { shift_value = 32; }
+ *carry = ARM7_BFE(value, shift_value - 1, 1);
+ value = value >> shift_value;
+ }
+ break;
+ case 2:
+ if (shift_value > 32) {
+ bool b31 = ARM7_BFE(value, 31, 1);
+ value = b31 ? 0xffffffff : 0;
+ *carry = b31;
+ } else {
+ if (shift_value == 0) { shift_value = 32; }
+ *carry = ARM7_BFE(value, shift_value - 1, 1);
+ value = (INT64)((INT32)value) >> shift_value;
+ }
+ break;
+ case 3:
+ if (shift_value == 0) {
+ UINT32 cpsr = arm->registers[CPSR];
+ INT32 C = ARM7_BFE(cpsr, 29, 1);
+ //Rotate Extended (RRX)
+ *carry = ARM7_BFE(value, 0, 1); value = (value >> 1) | (C << 31);
+
+ } else {
+ //Rotate
+ value = arm7_rotr(value, shift_value); *carry = ARM7_BFE(value, 31, 1);
+ }
+ break;
+ }
+ return value;
+}
+
+static FORCE_INLINE void arm7_data_processing(arm7_t* cpu, UINT32 opcode)
+{
+ // If it's used as anything but the shift amount in an operation with a register-specified shift, r15 will be PC + 12
+ // I.e. add r0, r15, r15, lsl r15 would set r0 to PC + 12 + ((PC + 12) << (PC + 8))
+ UINT64 Rd = ARM7_BFE(opcode, 12, 4);
+ INT32 S = ARM7_BFE(opcode, 20, 1);
+ INT32 op = ARM7_BFE(opcode, 21, 4);
+ INT32 r15_off = 4;
+ // Load Second Operand
+ UINT64 Rm = 0;
+ INT32 barrel_shifter_carry = -1;
+ if (opcode & ((1 << 25) | (0xff0))) {
+ INT32 I = ARM7_BFE(opcode, 25, 1);
+ if (I) {
+ UINT32 imm = ARM7_BFE(opcode, 0, 8);
+ UINT32 rot = ARM7_BFE(opcode, 8, 4) * 2;
+ Rm = arm7_rotr(imm, ARM7_BFE(opcode, 8, 4) * 2);
+ //C is preserved when rot ==0
+ barrel_shifter_carry = rot == 0 ? -1 : ARM7_BFE(Rm, 31, 1);
+ } else {
+ UINT32 shift_value = 0;
+ if (ARM7_BFE(opcode, 4, 1)) {
+ INT32 rs = ARM7_BFE(opcode, 8, 4);
+ // Only the first byte is used
+ shift_value = arm7_reg_read_r15_adj(cpu, rs, r15_off) & 0xff;
+ r15_off += 4; //Using r15 for a shift adds 4 cycles
+ cpu->i_cycles += 1;
+ } else shift_value = ARM7_BFE(opcode, 7, 5);
+ UINT32 value = arm7_reg_read_r15_adj(cpu, ARM7_BFE(opcode, 0, 4), r15_off);
+ Rm = arm7_shift(cpu, opcode, value, shift_value, &barrel_shifter_carry);
+ }
+ } else
+ Rm = arm7_reg_read_r15_adj(cpu, ARM7_BFE(opcode, 0, 4), r15_off);;
+
+ UINT64 Rn = arm7_reg_read_r15_adj(cpu, ARM7_BFE(opcode, 16, 4), r15_off);
+
+ UINT64 result = 0;
+ // Perform main operation
+ switch (op) {
+ /*AND*/ case 0: arm7_reg_write(cpu, Rd, result = Rn & Rm); break;
+ /*EOR*/ case 1: arm7_reg_write(cpu, Rd, result = Rn ^ Rm); break;
+ /*SUB*/ case 2: arm7_reg_write(cpu, Rd, result = Rn - Rm); break;
+ /*RSB*/ case 3: arm7_reg_write(cpu, Rd, result = Rm - Rn); break;
+ /*ADD*/ case 4: arm7_reg_write(cpu, Rd, result = Rn + Rm); break;
+ /*ADC*/ case 5: arm7_reg_write(cpu, Rd, result = Rn + Rm + ARM7_BFE(cpu->registers[CPSR], 29, 1)); break;
+ /*SBC*/ case 6: arm7_reg_write(cpu, Rd, result = Rn - Rm + ARM7_BFE(cpu->registers[CPSR], 29, 1) - 1); break;
+ /*RSC*/ case 7: arm7_reg_write(cpu, Rd, result = Rm - Rn + ARM7_BFE(cpu->registers[CPSR], 29, 1) - 1); break;
+ /*TST*/ case 8: result = Rn & Rm; break;
+ /*TEQ*/ case 9: result = Rn ^ Rm; break;
+ /*CMP*/ case 10: result = Rn - Rm; break;
+ /*CMN*/ case 11: result = Rn + Rm; break;
+ /*ORR*/ case 12: arm7_reg_write(cpu, Rd, result = Rn | Rm); break;
+ /*MOV*/ case 13: arm7_reg_write(cpu, Rd, result = Rm); break;
+ /*BIC*/ case 14: arm7_reg_write(cpu, Rd, result = Rn & ~Rm); break;
+ /*MVN*/ case 15: arm7_reg_write(cpu, Rd, result = ~Rm); break;
+ }
+ //Update flags
+ if (S) {
+ //Rd is not valid for TST, TEQ, CMP, or CMN
+ {
+ UINT32 cpsr = cpu->registers[CPSR];
+ bool C = ARM7_BFE(cpsr, 29, 1);
+ bool N = ARM7_BFE(result, 31, 1);
+ bool Z = (result & 0xffffffff) == 0;
+ bool V = ARM7_BFE(cpsr, 28, 1);
+
+ switch (op) {
+ // Logical Ops flags
+ /*AND*/ case 0:
+ /*EOR*/ case 1:
+ /*TST*/ case 8:
+ /*TEQ*/ case 9:
+ /*ORR*/ case 12:
+ /*MOV*/ case 13:
+ /*BIC*/ case 14:
+ /*MVN*/ case 15:
+ C = barrel_shifter_carry == -1 ? C : barrel_shifter_carry;
+ break;
+
+ /*SUB*/ case 2:
+ /*SBC*/ case 6:
+ /*CMP*/ case 10:
+ C = !ARM7_BFE(result, 32, 1);
+ // if (Rn has a different sign as Rm and result has a differnt sign to Rn)
+ V = (((Rn ^ Rm) & (Rn ^ result)) >> 31) & 1;
+ break;
+
+ /*RSB*/ case 3:
+ /*RSC*/ case 7:
+ C = !ARM7_BFE(result, 32, 1);
+ // if (Rm has a different sign as Rn and result has a differnt sign to Rm)
+ V = (((Rm ^ Rn) & (Rm ^ result)) >> 31) & 1;
+ break;
+
+ /*ADD*/ case 4:
+ /*ADC*/ case 5:
+ /*CMN*/ case 11:
+ C = ARM7_BFE(result, 32, 1);
+ // if (Rm has the same sign as Rn and result has a different sign to Rm)
+ V = (((Rm ^ ~Rn) & (Rm ^ result)) >> 31) & 1;
+ break;
+ }
+ cpsr &= 0x0fffffff;
+ cpsr |= (N ? 1 : 0) << 31;
+ cpsr |= (Z ? 1 : 0) << 30;
+ cpsr |= (C ? 1 : 0) << 29;
+ cpsr |= (V ? 1 : 0) << 28;
+ cpu->registers[CPSR] = cpsr;
+ }
+ if (Rd == 15) {
+ // When Rd is R15 and the S flag is set the result of the operation is placed in R15
+ // and the SPSR corresponding to the current mode is moved to the CPSR. This allows
+ // state changes which atomically restore both PC and CPSR. This form of instruction
+ // should not be used in User mode.
+ cpu->registers[CPSR] = arm7_reg_read(cpu, SPSR);
+ }
+ }
+}
+
+static FORCE_INLINE void arm7_multiply(arm7_t* cpu, UINT32 opcode)
+{
+ bool A = ARM7_BFE(opcode, 21, 1);
+ bool S = ARM7_BFE(opcode, 20, 1);
+ INT64 Rd = ARM7_BFE(opcode, 16, 4);
+ INT64 Rn = arm7_reg_read(cpu, ARM7_BFE(opcode, 12, 4));
+ INT64 Rs = arm7_reg_read(cpu, ARM7_BFE(opcode, 8, 4));
+ INT64 Rm = arm7_reg_read(cpu, ARM7_BFE(opcode, 0, 4));
+
+ if (SB_BFE(Rs, 8, 24) == 0 || SB_BFE(Rs, 8, 24) == 0x00ffffff)
+ cpu->i_cycles += 1;
+ else if (SB_BFE(Rs, 16, 16) == 0 || SB_BFE(Rs, 16, 16) == 0x0000ffff)
+ cpu->i_cycles += 2;
+ else if (SB_BFE(Rs, 24, 8) == 0 || SB_BFE(Rs, 24, 8) == 0x000000ff)
+ cpu->i_cycles += 3;
+ else
+ cpu->i_cycles += 4;
+
+ INT64 result = Rm * Rs;
+ if (A) { result += Rn; cpu->i_cycles += 1; }
+
+ arm7_reg_write(cpu, Rd, result);
+
+ if (S) {
+ UINT32 cpsr = cpu->registers[CPSR];
+ bool N = ARM7_BFE(result, 31, 1);
+ bool Z = (result & 0xffffffff) == 0;
+ bool C = ARM7_BFE(cpsr, 29, 1);
+ bool V = ARM7_BFE(cpsr, 28, 1);
+ cpsr &= 0x0ffffff;
+ cpsr |= (N ? 1u : 0u) << 31;
+ cpsr |= (Z ? 1 : 0) << 30;
+ cpsr |= (C ? 1 : 0) << 29;
+ cpsr |= (V ? 1 : 0) << 28;
+ cpu->registers[CPSR] = cpsr;
+ }
+}
+
+//SMULLxxx
+static FORCE_INLINE void arm9_signed_halfword_multiply(arm7_t* cpu, UINT32 opcode)
+{
+ INT32 op = ARM7_BFE(opcode, 21, 2);
+
+ INT64 Rd = ARM7_BFE(opcode, 16, 4);
+ INT64 Rn = (INT32)arm7_reg_read(cpu, ARM7_BFE(opcode, 12, 4));
+ INT64 Rs = (INT32)arm7_reg_read(cpu, ARM7_BFE(opcode, 8, 4));
+ INT64 Rm = (INT32)arm7_reg_read(cpu, ARM7_BFE(opcode, 0, 4));
+ bool y = ARM7_BFE(opcode, 6, 1);
+ bool x = ARM7_BFE(opcode, 5, 1);
+
+ INT64 result = 0;
+ const INT64 int32_max = (1ll << 31ll) - 1;
+ const INT64 int32_min = -(1ll << 31ll);
+ bool Q = false;
+ switch (op) {
+ case 0: //SMLAxy
+ Rs = (INT16)(y ? ARM7_BFE(Rs, 16, 16) : ARM7_BFE(Rs, 0, 16));
+ Rm = (INT16)(x ? ARM7_BFE(Rm, 16, 16) : ARM7_BFE(Rm, 0, 16));
+ result = Rs * Rm + Rn;
+ break;
+ case 1: //SMLAWy or SMULWy
+ Rs = (INT16)(y ? ARM7_BFE(Rs, 16, 16) : ARM7_BFE(Rs, 0, 16));
+ result = (Rs * Rm) >> 16;
+ // SMLAWy only
+ if (!x)result += Rn;
+ break;
+ case 2: {//SMLALxy
+ Rs = (INT16)(y ? ARM7_BFE(Rs, 16, 16) : ARM7_BFE(Rs, 0, 16));
+ Rm = (INT16)(x ? ARM7_BFE(Rm, 16, 16) : ARM7_BFE(Rm, 0, 16));
+ INT64 RdHi = arm7_reg_read(cpu, Rd);
+ INT64 RdLo = Rn;
+ INT64 RdHiLo = ((RdHi << 32) | RdLo);
+ result = Rs * Rm + RdHiLo;
+ RdHi = result >> 32;
+ RdLo = result & 0xffffffff;
+ arm7_reg_write(cpu, Rd, RdHi);
+ arm7_reg_write(cpu, ARM7_BFE(opcode, 12, 4), RdLo);
+ cpu->i_cycles += 1;
+ }
+ return;
+ case 3: //SMULxy
+ Rs = (INT16)(y ? ARM7_BFE(Rs, 16, 16) : ARM7_BFE(Rs, 0, 16));
+ Rm = (INT16)(x ? ARM7_BFE(Rm, 16, 16) : ARM7_BFE(Rm, 0, 16));
+ result = Rs * Rm;
+ break;
+ }
+ Q = result > int32_max || result < int32_min;
+ cpu->registers[CPSR] |= Q << 27;
+ arm7_reg_write(cpu, Rd, result);
+}
+
+static FORCE_INLINE void arm7_multiply_long(arm7_t* cpu, UINT32 opcode)
+{
+ bool U = ARM7_BFE(opcode, 22, 1);
+ bool A = ARM7_BFE(opcode, 21, 1);
+ bool S = ARM7_BFE(opcode, 20, 1);
+ INT64 RdHi = ARM7_BFE(opcode, 16, 4);
+ INT64 RdLo = ARM7_BFE(opcode, 12, 4);
+ INT64 Rs = arm7_reg_read(cpu, ARM7_BFE(opcode, 8, 4));
+ INT64 Rm = arm7_reg_read(cpu, ARM7_BFE(opcode, 0, 4));
+
+ INT64 RdHiLo = arm7_reg_read(cpu, RdHi);
+ RdHiLo = (RdHiLo << 32) | arm7_reg_read(cpu, RdLo);
+
+ if (U) {
+ Rm = (INT32)Rm;
+ Rs = (INT32)Rs;
+ if (SB_BFE(Rs, 8, 24) == 0 || SB_BFE(Rs, 8, 24) == 0x00ffffff)
+ cpu->i_cycles += 2;
+ else if (SB_BFE(Rs, 16, 16) == 0 || SB_BFE(Rs, 16, 16) == 0x0000ffff)
+ cpu->i_cycles += 3;
+ else if (SB_BFE(Rs, 24, 8) == 0 || SB_BFE(Rs, 24, 8) == 0x000000ff)
+ cpu->i_cycles += 4;
+ else
+ cpu->i_cycles += 5;
+ } else {
+ if (SB_BFE(Rs, 8, 24) == 0)
+ cpu->i_cycles += 2;
+ else if (SB_BFE(Rs, 16, 16) == 0)
+ cpu->i_cycles += 3;
+ else if (SB_BFE(Rs, 24, 8) == 0)
+ cpu->i_cycles += 4;
+ else
+ cpu->i_cycles += 5;
+ }
+
+
+ INT64 result = Rm * Rs;
+ if (A) { result += RdHiLo; cpu->i_cycles += 1; }
+
+ arm7_reg_write(cpu, RdHi, result >> 32);
+ arm7_reg_write(cpu, RdLo, result & 0xffffffff);
+
+ if (S) {
+ UINT32 cpsr = cpu->registers[CPSR];
+ bool N = ARM7_BFE(result, 63, 1);
+ bool Z = result == 0;
+ bool C = ARM7_BFE(cpsr, 29, 1);
+ bool V = ARM7_BFE(cpsr, 28, 1);
+ cpsr &= 0x0ffffff;
+ cpsr |= (N ? 1 : 0) << 31;
+ cpsr |= (Z ? 1 : 0) << 30;
+ cpsr |= (C ? 1 : 0) << 29;
+ cpsr |= (V ? 1 : 0) << 28;
+ cpu->registers[CPSR] = cpsr;
+ }
+}
+
+static FORCE_INLINE void arm7_single_data_swap(arm7_t* cpu, UINT32 opcode)
+{
+ bool B = ARM7_BFE(opcode, 22, 1);
+ UINT32 addr = arm7_reg_read_r15_adj(cpu, ARM7_BFE(opcode, 16, 4), 4);
+ UINT32 Rd = ARM7_BFE(opcode, 12, 4);
+ UINT32 Rm = ARM7_BFE(opcode, 0, 4);
+ // Load
+ UINT32 read_data = B ? cpu->read8(cpu->user_data, addr) : arm7_rotr(cpu->read32(cpu->user_data, addr), (addr & 0x3) * 8);
+
+ UINT32 store_data = arm7_reg_read_r15_adj(cpu, Rm, 8);
+ if (B == 1)
+ cpu->write8(cpu->user_data, addr, store_data);
+ else
+ cpu->write32(cpu->user_data, addr, store_data);
+
+ arm7_reg_write(cpu, Rd, read_data);
+ cpu->i_cycles += 1;
+}
+
+static FORCE_INLINE void arm7_branch_exchange(arm7_t* cpu, UINT32 opcode)
+{
+ INT32 v = arm7_reg_read_r15_adj(cpu, ARM7_BFE(opcode, 0, 4), 4);
+ bool thumb = (v & 1) == 1;
+ if (thumb)
+ cpu->registers[PC] = (v & ~1);
+ else
+ cpu->registers[PC] = (v & ~3);
+ cpu->prefetch_pc = -1;
+ arm7_set_thumb_bit(cpu, thumb);
+}
+
+static FORCE_INLINE void arm9_branch_link_exchange(arm7_t* cpu, UINT32 opcode)
+{
+ INT32 v = arm7_reg_read_r15_adj(cpu, ARM7_BFE(opcode, 0, 4), 4);
+ bool prev_thumb = arm7_get_thumb_bit(cpu);
+ arm7_reg_write(cpu, LR, cpu->registers[PC] | prev_thumb);
+ bool thumb = (v & 1) == 1;
+ if (thumb)
+ cpu->registers[PC] = (v & ~1);
+ else
+ cpu->registers[PC] = (v & ~3);
+ cpu->prefetch_pc = -1;
+ arm7_set_thumb_bit(cpu, thumb);
+}
+
+static FORCE_INLINE void arm7_half_word_transfer(arm7_t* cpu, UINT32 opcode)
+{
+ bool P = ARM7_BFE(opcode, 24, 1);
+ bool U = ARM7_BFE(opcode, 23, 1);
+ bool I = ARM7_BFE(opcode, 22, 1);
+ bool W = ARM7_BFE(opcode, 21, 1);
+ bool L = ARM7_BFE(opcode, 20, 1);
+ INT32 Rn = ARM7_BFE(opcode, 16, 4);
+
+ bool S = ARM7_BFE(opcode, 6, 1);
+ bool H = ARM7_BFE(opcode, 5, 1);
+
+ INT32 offset = I == 0 ?
+ arm7_reg_read(cpu, ARM7_BFE(opcode, 0, 4)) :
+ ((opcode >> 4) & 0xf0) | (opcode & 0xf);
+ UINT64 Rd = ARM7_BFE(opcode, 12, 4);
+ UINT32 addr = arm7_reg_read_r15_adj(cpu, Rn, 4);
+
+ INT32 increment = U ? offset : -offset;
+ if (P)
+ addr += increment;
+ // Store before writeback
+ if (L == 0) {
+ UINT32 data = arm7_reg_read_r15_adj(cpu, Rd, 8);
+ if (H == 1)
+ cpu->write16(cpu->user_data, addr, data);
+ else
+ cpu->write8(cpu->user_data, addr, data);
+ }
+ UINT32 write_back_addr = addr;
+ if (!P) {
+ write_back_addr += increment;
+ W = true;
+ }
+ if (W)
+ arm7_reg_write(cpu, Rn, write_back_addr);
+ if (L == 1) { // Load
+ UINT32 data = H ? arm7_rotr(cpu->read16(cpu->user_data, addr), (addr & 0x1) * 8) : cpu->read8(cpu->user_data, addr);
+ if (S) {
+ data &= 0xffff;
+ // Unaligned signed half words and signed byte loads sign extend the byte
+ if (H && !(addr & 1)) {
+ data |= 0xffff0000 * ARM7_BFE(data, 15, 1);
+ } else
+ data |= 0xffffff00 * ARM7_BFE(data, 7, 1);
+ }
+ arm7_reg_write(cpu, Rd, data);
+ cpu->i_cycles += 1;
+ }
+}
+
+static FORCE_INLINE void arm7_single_word_transfer(arm7_t* cpu, UINT32 opcode)
+{
+ bool I = ARM7_BFE(opcode, 25, 1);
+ bool P = ARM7_BFE(opcode, 24, 1);
+ bool U = ARM7_BFE(opcode, 23, 1);
+ bool B = ARM7_BFE(opcode, 22, 1);
+ bool W = ARM7_BFE(opcode, 21, 1);
+ bool L = ARM7_BFE(opcode, 20, 1);
+ INT32 Rn = ARM7_BFE(opcode, 16, 4);
+ INT32 carry;
+ INT32 offset = I == 0 ? ARM7_BFE(opcode, 0, 12) :
+ arm7_load_shift_reg(cpu, opcode, &carry);
+
+ UINT64 Rd = ARM7_BFE(opcode, 12, 4);
+ UINT32 addr = arm7_reg_read_r15_adj(cpu, Rn, 4);
+ INT32 increment = U ? offset : -offset;
+
+ if (P)
+ addr += increment;
+
+ // Store before write back
+ if (L == 0) {
+ UINT32 data = arm7_reg_read_r15_adj(cpu, Rd, 8);
+ if (B == 1)
+ cpu->write8(cpu->user_data, addr, data);
+ else
+ cpu->write32(cpu->user_data, addr, data);
+ }
+
+ //Write back address before load
+ UINT32 write_back_addr = addr;
+ if (!P) {
+ write_back_addr += increment;
+ W = true;
+ }
+ if (W)
+ arm7_reg_write(cpu, Rn, write_back_addr);
+
+ if (L == 1) { // Load
+ UINT32 data = B ? cpu->read8(cpu->user_data, addr) : arm7_rotr(cpu->read32(cpu->user_data, addr), (addr & 0x3) * 8);
+ arm7_reg_write(cpu, Rd, data);
+ cpu->i_cycles += 1;
+ }
+}
+
+static FORCE_INLINE void arm9_single_word_transfer(arm7_t* cpu, UINT32 opcode)
+{
+ bool I = ARM7_BFE(opcode, 25, 1);
+ bool P = ARM7_BFE(opcode, 24, 1);
+ bool U = ARM7_BFE(opcode, 23, 1);
+ bool B = ARM7_BFE(opcode, 22, 1);
+ bool W = ARM7_BFE(opcode, 21, 1);
+ bool L = ARM7_BFE(opcode, 20, 1);
+ INT32 Rn = ARM7_BFE(opcode, 16, 4);
+ INT32 carry;
+ INT32 offset = I == 0 ? ARM7_BFE(opcode, 0, 12) :
+ arm7_load_shift_reg(cpu, opcode, &carry);
+
+ UINT64 Rd = ARM7_BFE(opcode, 12, 4);
+ UINT32 addr = arm7_reg_read_r15_adj(cpu, Rn, 4);
+ INT32 increment = U ? offset : -offset;
+
+ if (P)
+ addr += increment;
+
+ // Store before write back
+ if (L == 0) {
+ UINT32 data = arm7_reg_read_r15_adj(cpu, Rd, 8);
+ if (B == 1)
+ cpu->write8(cpu->user_data, addr, data);
+ else
+ cpu->write32(cpu->user_data, addr, data);
+ }
+
+ //Write back address before load
+ UINT32 write_back_addr = addr;
+ if (!P) {
+ write_back_addr += increment;
+ W = true;
+ }
+ if (W)
+ arm7_reg_write(cpu, Rn, write_back_addr);
+
+ if (L == 1) { // Load
+ UINT32 data = B ? cpu->read8(cpu->user_data, addr) : arm7_rotr(cpu->read32(cpu->user_data, addr), (addr & 0x3) * 8);
+ arm9_reg_write_r15_thumb(cpu, Rd, data);
+ cpu->i_cycles += 1;
+ }
+}
+
+static FORCE_INLINE void arm9_double_word_transfer(arm7_t* cpu, UINT32 opcode)
+{
+ // cccc 000P UIW0 nnnn dddd oooo 11S1 oooo
+ bool P = ARM7_BFE(opcode, 24, 1);
+ bool U = ARM7_BFE(opcode, 23, 1);
+ bool I = ARM7_BFE(opcode, 22, 1);
+ bool W = ARM7_BFE(opcode, 21, 1);
+ bool S = ARM7_BFE(opcode, 5, 1);
+ INT32 Rn = ARM7_BFE(opcode, 16, 4);
+ INT32 carry;
+ INT32 offset = I == 0 ?
+ arm7_reg_read(cpu, ARM7_BFE(opcode, 0, 4)) :
+ ((opcode >> 4) & 0xf0) | (opcode & 0xf);
+
+ UINT64 Rd = ARM7_BFE(opcode, 12, 4);
+ UINT32 addr = arm7_reg_read_r15_adj(cpu, Rn, 4);
+ INT32 increment = U ? offset : -offset;
+
+ if (P)
+ addr += increment;
+
+ // Store before write back
+ if (S) {
+ UINT32 data0 = arm7_reg_read_r15_adj(cpu, Rd, 8);
+ UINT32 data1 = arm7_reg_read_r15_adj(cpu, Rd + 1, 8);
+ cpu->write32(cpu->user_data, addr, data0);
+ cpu->write32(cpu->user_data, addr + 4, data1);
+ }
+
+ //Write back address before load
+ UINT32 write_back_addr = addr;
+ if (!P) {
+ write_back_addr += increment;
+ W = true;
+ }
+ if (W)
+ arm7_reg_write(cpu, Rn, write_back_addr);
+
+ if (S == 0) { // Load
+ UINT32 data0 = cpu->read32(cpu->user_data, addr);
+ UINT32 data1 = cpu->read32(cpu->user_data, addr + 4);
+ arm9_reg_write_r15_thumb(cpu, Rd, data0);
+ arm9_reg_write_r15_thumb(cpu, Rd + 1, data1);
+ cpu->i_cycles += 1;
+ }
+}
+
+static FORCE_INLINE void arm7_undefined(arm7_t* cpu, UINT32 opcode)
+{
+ bool thumb = arm7_get_thumb_bit(cpu);
+ cpu->registers[R14_und] = cpu->registers[PC] - (thumb ? 0 : 4);
+ cpu->registers[PC] = cpu->irq_table_address + 0x4;
+ UINT32 cpsr = cpu->registers[CPSR];
+ cpu->registers[SPSR_und] = cpsr;
+ //Update mode to supervisor and block irqs
+ cpu->registers[CPSR] = (cpsr & 0xffffffE0) | 0x1b | 0x80;
+ arm7_set_thumb_bit(cpu, false);
+ printf("Unhandled Instruction Class (arm7_undefined) Opcode: %x PC:%08x\n", opcode, cpu->registers[R14_und]);
+ cpu->i_cycles += 1;
+}
+
+static FORCE_INLINE void arm9_clz(arm7_t* cpu, UINT32 opcode)
+{
+ INT32 Rd = ARM7_BFE(opcode, 12, 4);
+ INT32 Rs = ARM7_BFE(opcode, 0, 4);
+ UINT32 reg = arm7_reg_read_r15_adj(cpu, Rs, 4);
+ UINT32 r2 = reg;
+ INT32 count = 32;
+ while (reg) {
+ count--; reg >>= 1;
+ }
+ arm7_reg_write(cpu, Rd, count);
+}
+
+static FORCE_INLINE void arm9_qadd_qsub(arm7_t* cpu, UINT32 opcode) {
+ bool double_value = ARM7_BFE(opcode, 22, 1);
+ bool subtract = ARM7_BFE(opcode, 21, 1);
+ INT32 Rn = ARM7_BFE(opcode, 16, 4);
+ INT32 Rd = ARM7_BFE(opcode, 12, 4);
+ INT32 Rm = ARM7_BFE(opcode, 0, 4);
+
+ INT64 regA = (INT32)arm7_reg_read_r15_adj(cpu, Rm, 4);
+ INT64 regB = (INT32)arm7_reg_read_r15_adj(cpu, Rn, 4);
+ const INT64 int32_max = (1ll << 31ll) - 1;
+ const INT64 int32_min = -(1ll << 31ll);
+ bool Q = false;
+ if (double_value) {
+ regB *= 2;
+ if (regB > int32_max) { regB = int32_max; Q = true; }
+ if (regB < int32_min) { regB = int32_min; Q = true; }
+ }
+ INT64 result = subtract ? regA - regB : regA + regB;
+ if (result > int32_max) { result = int32_max; Q = true; }
+ if (result < int32_min) { result = int32_min; Q = true; }
+ cpu->registers[CPSR] |= Q << 27;
+ arm7_reg_write(cpu, Rd, result);
+}
+
+static FORCE_INLINE void arm7_block_transfer(arm7_t* cpu, UINT32 opcode)
+{
+ INT32 P = ARM7_BFE(opcode, 24, 1);
+ INT32 U = ARM7_BFE(opcode, 23, 1);
+ INT32 S = ARM7_BFE(opcode, 22, 1);
+ INT32 w = ARM7_BFE(opcode, 21, 1);
+ INT32 L = ARM7_BFE(opcode, 20, 1);
+ INT32 Rn = ARM7_BFE(opcode, 16, 4);
+ INT32 reglist = ARM7_BFE(opcode, 0, 16);
+
+ // Examples pushing R1, R5, R7
+ // P= 0(post) U = 0(dec)
+ // mem[Rn-8] = R1
+ // mem[Rn-4] = R5
+ // mem[Rn-0] = R7
+ // Rn-=12
+
+ // P= 0(post) U = 1(inc)
+ // mem[Rn] = R1
+ // mem[Rn+4] = R5
+ // mem[Rn+8] = R7
+ // Rn+=12
+
+ // P= 1(pre) U = 0(dec)
+ // mem[Rn-12] = R1
+ // mem[Rn-8] = R5
+ // mem[Rn-4] = R7
+ // Rn-=12
+
+ // P= 1(pre) U = 1(inc)
+ // mem[Rn+4] = R1
+ // mem[Rn+8] = R5
+ // mem[Rn+12] = R7
+ // Rn+=12
+
+ if (cpu->phase == 0) {
+ INT32 addr = arm7_reg_read_r15_adj(cpu, Rn, 4);
+ INT32 increment = U ? 4 : -4;
+ INT32 num_regs = 0;
+ for (INT32 i = 0; i < 16; ++i)
+ if (ARM7_BFE(reglist, i, 1) == 1)
+ num_regs += 1;
+ INT32 base_addr = addr;
+ if (reglist == 0) {
+ // Handle Empty Rlist case: R15 loaded/stored (ARMv4 only), and Rb=Rb+/-40h (ARMv4-v5).
+ reglist = 1 << 15;
+ num_regs = 16;
+ }
+ if (!U)
+ base_addr += (num_regs)*increment;
+ addr = base_addr;
+ if (U)
+ base_addr += (num_regs)*increment;
+ cpu->block.base_addr = base_addr;
+
+ if (!(P ^ U))
+ addr += 4;
+ cpu->block.cycle = 0;
+ cpu->block.addr = addr;
+ // TODO: For some reason r15 is only offset by 4 in thumb mode.
+ // Check if other people do this to.
+ cpu->block.r15_off = arm7_get_thumb_bit(cpu) ? 4 : 8;
+ // Address are word aligned
+ //addr&=~3;
+ cpu->block.last_bank = -1;
+
+ }
+ bool user_bank_transfer = S && (!L || !SB_BFE(reglist, 15, 1));
+
+ for (INT32 i = cpu->phase; i < 16; ++i) {
+ //Writeback happens on second cycle
+ //Todo, does post increment force writeback?
+
+ if (ARM7_BFE(reglist, i, 1) == 0)
+ continue;
+
+ // When S is set the registers are read from the user bank
+ INT32 reg_index = user_bank_transfer ? i : arm7_reg_index(cpu, i);
+ //Store happens before writeback
+ INT32 a = cpu->block.addr;
+ //Inexplicablly SRAM accesses are not DWORD aligned. GBA suite memory test can be used to verify this.
+ if ((a & 0xfe000000) != 0x0e000000)
+ a &= ~3;
+ if (!L)
+ cpu->write32(cpu->user_data, a, cpu->registers[reg_index] + (i == 15 ? cpu->block.r15_off : 0));
+
+ //Writeback happens on second cycle
+ if (++cpu->block.cycle == 1 && w) {
+ arm7_reg_write(cpu, Rn, cpu->block.base_addr);
+ }
+
+ // R15 is stored at PC+12
+ if (L) {
+ INT32 bank = ARM7_BFE(a, 24, 8);
+ cpu->registers[reg_index] = cpu->read32_seq(cpu->user_data, a, bank == cpu->block.last_bank);
+ cpu->block.last_bank = bank;
+ }
+
+ cpu->block.addr += 4;
+
+ // If the instruction is a LDM then SPSR_ is transferred to CPSR at
+ // the same time as R15 is loaded.
+ if (L && S && i == 15) {
+ cpu->registers[CPSR] = arm7_reg_read(cpu, SPSR);
+ }
+ cpu->phased_op_id = ARM_PHASED_BLOCK_TRANSFER;
+ cpu->phased_opcode = opcode;
+ cpu->phase = i + 1;
+ return;
+ }
+ if (L)
+ cpu->i_cycles += 1;
+ cpu->phase = 0;
+ cpu->phased_op_id = 0;
+}
+
+static FORCE_INLINE void arm9_block_transfer(arm7_t* cpu, UINT32 opcode)
+{
+ INT32 P = ARM7_BFE(opcode, 24, 1);
+ INT32 U = ARM7_BFE(opcode, 23, 1);
+ INT32 S = ARM7_BFE(opcode, 22, 1);
+ INT32 w = ARM7_BFE(opcode, 21, 1);
+ INT32 L = ARM7_BFE(opcode, 20, 1);
+ INT32 Rn = ARM7_BFE(opcode, 16, 4);
+ INT32 reglist = ARM7_BFE(opcode, 0, 16);
+
+ // Examples pushing R1, R5, R7
+ // P= 0(post) U = 0(dec)
+ // mem[Rn-8] = R1
+ // mem[Rn-4] = R5
+ // mem[Rn-0] = R7
+ // Rn-=12
+
+ // P= 0(post) U = 1(inc)
+ // mem[Rn] = R1
+ // mem[Rn+4] = R5
+ // mem[Rn+8] = R7
+ // Rn+=12
+
+ // P= 1(pre) U = 0(dec)
+ // mem[Rn-12] = R1
+ // mem[Rn-8] = R5
+ // mem[Rn-4] = R7
+ // Rn-=12
+
+ // P= 1(pre) U = 1(inc)
+ // mem[Rn+4] = R1
+ // mem[Rn+8] = R5
+ // mem[Rn+12] = R7
+ // Rn+=12
+ if (cpu->phase == 0) {
+ INT32 addr = arm7_reg_read_r15_adj(cpu, Rn, 4);
+ INT32 increment = U ? 4 : -4;
+ INT32 num_regs = 0;
+ for (INT32 i = 0; i < 16; ++i)
+ if (ARM7_BFE(reglist, i, 1) == 1)
+ num_regs += 1;
+ INT32 base_addr = addr;
+ if (reglist == 0) {
+ // Handle Empty Rlist case: R15 loaded/stored (ARMv4 only), and Rb=Rb+/-40h (ARMv4-v5).
+ num_regs = 16;
+ }
+ if (!U)
+ base_addr += (num_regs)*increment;
+ addr = base_addr;
+ if (U)
+ base_addr += (num_regs)*increment;
+
+ if (!(P ^ U))
+ addr += 4;
+
+ cpu->block.base_addr = base_addr;
+ cpu->block.cycle = 0;
+ cpu->block.addr = addr;
+ // TODO: For some reason r15 is only offset by 4 in thumb mode.
+ // Check if other people do this to.
+ cpu->block.r15_off = arm7_get_thumb_bit(cpu) ? 4 : 8;
+ cpu->block.last_bank = -1;
+ cpu->block.num_regs = num_regs;
+ }
+
+ bool user_bank_transfer = S && (!L || !SB_BFE(reglist, 15, 1));
+ for (INT32 i = cpu->phase; i < 16; ++i) {
+ //Writeback happens on second cycle
+ //Todo, does post increment force writeback?
+
+ if (ARM7_BFE(reglist, i, 1) == 0)
+ continue;
+
+ // When S is set the registers are read from the user bank
+ INT32 reg_index = user_bank_transfer ? i : arm7_reg_index(cpu, i);
+ //Store happens before writeback
+ INT32 a = cpu->block.addr;
+ if (!L)
+ cpu->write32(cpu->user_data, a, cpu->registers[reg_index] + (i == 15 ? cpu->block.r15_off : 0));
+
+ // R15 is stored at PC+12
+ if (L) {
+ INT32 bank = ARM7_BFE(a, 24, 8);
+ cpu->registers[reg_index] = cpu->read32_seq(cpu->user_data, a, bank == cpu->block.last_bank);
+ cpu->block.last_bank = bank;
+ if (PC == reg_index)
+ arm7_set_thumb_bit(cpu, cpu->registers[PC] & 1);
+ }
+ //Writeback happens on second cycle
+ if (++cpu->block.cycle == 1 && w && L) {
+ arm7_reg_write(cpu, Rn, cpu->block.base_addr);
+ }
+ cpu->block.addr += 4;
+
+ // If the instruction is a LDM then SPSR_ is transferred to CPSR at
+ // the same time as R15 is loaded.
+ if (L && S && i == 15) {
+ cpu->registers[CPSR] = arm7_reg_read(cpu, SPSR);
+ }
+ cpu->phased_op_id = ARM_PHASED_BLOCK_TRANSFER;
+ cpu->phased_opcode = opcode;
+ cpu->phase = i + 1;
+ return;
+ }
+ //Handle late writeback (required by Rockwrestler)
+ if ((!L || reglist >= (1 << (Rn + 1)) || cpu->block.num_regs <= 1 || reglist == 0) && w) {
+ arm7_reg_write(cpu, Rn, cpu->block.base_addr);
+ }
+ if (L)
+ cpu->i_cycles += 1;
+ cpu->phase = 0;
+ cpu->phased_op_id = 0;
+}
+
+static FORCE_INLINE void arm7_branch(arm7_t* cpu, UINT32 opcode)
+{
+ //Write Link Register if L=1
+ if (ARM7_BFE(opcode, 24, 1))
+ arm7_reg_write(cpu, LR, cpu->registers[PC]);
+ //Decode V and sign extend
+ INT32 v = ARM7_BFE(opcode, 0, 24);
+ if (ARM7_BFE(v, 23, 1))
+ v |= 0xff000000;
+ //Shift left and take into account prefetch
+ INT32 pc_off = (v << 2) + 4;
+ cpu->registers[PC] += pc_off;
+ cpu->prefetch_pc = -1;
+}
+
+static FORCE_INLINE void arm9_branch(arm7_t* cpu, UINT32 opcode)
+{
+ INT32 cond = ARM7_BFE(opcode, 28, 4);
+ //Decode V and sign extend
+ INT32 v = ARM7_BFE(opcode, 0, 24);
+ if (ARM7_BFE(v, 23, 1))
+ v |= 0xff000000;
+ //Shift left and take into account prefetch
+ INT32 pc_off = (v << 2) + 4;
+ //printf("BLX? cond: %x\n",cond);
+ if (cond == 0xf) {
+ bool H = ARM7_BFE(opcode, 24, 1);
+ pc_off += H * 2;
+ arm7_reg_write(cpu, LR, cpu->registers[PC]);
+ arm7_set_thumb_bit(cpu, true);
+ } else {
+ //Write Link Register if L=1
+ if (ARM7_BFE(opcode, 24, 1))
+ arm7_reg_write(cpu, LR, cpu->registers[PC]);
+ }
+ cpu->registers[PC] += pc_off;
+ cpu->prefetch_pc = -1;
+}
+
+static FORCE_INLINE void arm7_coproc_data_transfer(arm7_t* cpu, UINT32 opcode)
+{
+ printf("Unhandled Instruction Class (arm7_coproc_data_transfer) Opcode: %x\n", opcode);
+ if (cpu->trigger_breakpoint)
+ cpu->trigger_breakpoint(cpu->user_data);
+}
+
+static FORCE_INLINE void arm7_coproc_data_op(arm7_t* cpu, UINT32 opcode)
+{
+ printf("Unhandled Instruction Class (arm7_coproc_data_op) Opcode: %x\n", opcode);
+ if (cpu->trigger_breakpoint)
+ cpu->trigger_breakpoint(cpu->user_data);
+}
+
+static FORCE_INLINE void arm7_coproc_reg_transfer(arm7_t* cpu, UINT32 opcode)
+{
+ INT32 coprocessor_opcode = SB_BFE(opcode, 21, 3);
+ bool coprocessor_read = SB_BFE(opcode, 20, 1);
+ INT32 Cn = SB_BFE(opcode, 16, 4);
+ INT32 Rd = SB_BFE(opcode, 12, 4);
+ INT32 Pn = SB_BFE(opcode, 8, 4);
+ INT32 Cp = SB_BFE(opcode, 5, 3);
+ INT32 Cm = SB_BFE(opcode, 0, 4);
+ if (coprocessor_read) {
+ if (!cpu->coprocessor_read) {
+ printf("Coprocessor Read Issued without bound coprocessor_read handler: %x\n", opcode);
+ return;
+ }
+ UINT32 data = cpu->coprocessor_read(cpu->user_data, Pn, coprocessor_opcode, Cn, Cm, Cp);
+ arm7_reg_write(cpu, Rd, data);
+ } else {
+ if (!cpu->coprocessor_write) {
+ printf("Coprocessor Write Issued without bound coprocessor_write handler: %x\n", opcode);
+ return;
+ }
+ UINT32 data = arm7_reg_read_r15_adj(cpu, Rd, 8);
+ cpu->coprocessor_write(cpu->user_data, Pn, coprocessor_opcode, Cn, Cm, Cp, data);
+ }
+}
+
+static FORCE_INLINE void arm7_software_interrupt(arm7_t* cpu, UINT32 opcode)
+{
+ bool thumb = arm7_get_thumb_bit(cpu);
+ cpu->registers[R14_svc] = cpu->registers[PC];
+ cpu->registers[PC] = cpu->irq_table_address + 0x8;
+ UINT32 cpsr = cpu->registers[CPSR];
+ cpu->registers[SPSR_svc] = cpsr;
+ //Update mode to supervisor and block irqs
+ cpu->registers[CPSR] = (cpsr & 0xffffffE0) | 0x13 | 0x80;
+ UINT32 swi_number = SB_BFE(opcode, 0, 24);
+ if (arm7_get_thumb_bit(cpu))
+ swi_number = SB_BFE(opcode, 0, 8);
+ INT32 id = -1;
+ for (INT32 i = 0; i < ARM_DEBUG_SWI_RING_SIZE && i < cpu->debug_swi_ring_offset; ++i) {
+ if (cpu->debug_swi_ring[i] == swi_number) {
+ id = i;
+ break;
+ }
+ }
+ if (id == -1) {
+ id = (cpu->debug_swi_ring_offset++) % ARM_DEBUG_SWI_RING_SIZE;
+ cpu->debug_swi_ring_times[id] = 0;
+ }
+ cpu->debug_swi_ring[id] = swi_number;
+ cpu->debug_swi_ring_times[id]++;
+ arm7_set_thumb_bit(cpu, false);
+}
+
+static FORCE_INLINE void arm7_mrs(arm7_t* cpu, UINT32 opcode)
+{
+ INT32 P = ARM7_BFE(opcode, 22, 1);
+ INT32 Rd = ARM7_BFE(opcode, 12, 4);
+ INT32 data = arm7_reg_read(cpu, P ? SPSR : CPSR);
+ arm7_reg_write(cpu, Rd, data);
+}
+
+static FORCE_INLINE void arm7_msr(arm7_t* cpu, UINT32 opcode)
+{
+ INT32 P = ARM7_BFE(opcode, 22, 1);
+ INT32 flags_only = !ARM7_BFE(opcode, 16, 1);
+ INT32 I = ARM7_BFE(opcode, 25, 1);
+ INT32 data = 0;
+ INT32 dest_reg = P ? SPSR : CPSR;
+
+ // Mask behavior from: https://problemkaputt.de/gbatek.htm#armopcodespsrtransfermrsmsr
+ UINT32 mask = 0;
+ mask |= 0xff000000 * ARM7_BFE(opcode, 19, 1);
+ mask |= 0x00ff0000 * ARM7_BFE(opcode, 18, 1);
+ mask |= 0x0000ff00 * ARM7_BFE(opcode, 17, 1);
+ mask |= 0x000000ff * ARM7_BFE(opcode, 16, 1);
+
+ INT32 mode = cpu->registers[CPSR] & 0x1f;
+
+ // There is no SPSR in user or system mode
+ if (P && (mode == 0x10 || mode == 0x1f))
+ return;
+ //User mode can only change the flags
+ if (mode == 0x10)
+ mask &= 0xf0000000;
+
+ if (I) {
+ INT32 imm = ARM7_BFE(opcode, 0, 8);
+ INT32 rot = ARM7_BFE(opcode, 8, 4) * 2;
+ data = arm7_rotr(imm, rot);
+ } else
+ data = arm7_reg_read(cpu, ARM7_BFE(opcode, 0, 4));
+
+ INT32 old_data = arm7_reg_read(cpu, dest_reg);
+ data &= mask;
+ data |= old_data & ~mask;
+
+ arm7_reg_write(cpu, dest_reg, data);
+}
+
+// Thumb Instruction Implementations
+static FORCE_INLINE void arm7t_mov_shift_reg(arm7_t* cpu, UINT32 opcode)
+{
+ UINT32 op = ARM7_BFE(opcode, 11, 2);
+ UINT32 offset = ARM7_BFE(opcode, 6, 5);
+ UINT32 Rs = ARM7_BFE(opcode, 3, 3);
+ UINT32 Rd = ARM7_BFE(opcode, 0, 3);
+
+ opcode = (0xD << 21) | (1 << 20) | (Rd << 12) | (offset << 7) | (op << 5) | (Rs);
+ arm7_data_processing(cpu, opcode);
+}
+
+static FORCE_INLINE void arm7t_add_sub(arm7_t* cpu, UINT32 opcode)
+{
+ bool I = ARM7_BFE(opcode, 10, 1);
+ INT32 op = ARM7_BFE(opcode, 9, 1) ? /*Sub*/ 2 : /*Add*/ 4;
+ INT32 Rn = ARM7_BFE(opcode, 6, 3);
+ INT32 Rs = ARM7_BFE(opcode, 3, 3);
+ INT32 Rd = ARM7_BFE(opcode, 0, 3);
+ UINT32 arm_op = (I << 25) | (op << 21) | (1 << 20) | (Rs << 16) | (Rd << 12) | (Rn);
+ arm7_data_processing(cpu, arm_op);
+}
+
+static FORCE_INLINE void arm7t_mov_cmp_add_sub_imm(arm7_t* cpu, UINT32 opcode)
+{
+ INT32 op = ARM7_BFE(opcode, 11, 2);
+ INT32 Rd = ARM7_BFE(opcode, 8, 3);
+ INT32 imm = ARM7_BFE(opcode, 0, 8);
+ op = (0x24AD >> (op * 4)) & 0xf;/*MOV*//*CMP*//*ADD*//*SUB*/
+ UINT32 arm_op = (1 << 25) | (op << 21) | (1 << 20) | (Rd << 16) | (Rd << 12) | (imm);
+ arm7_data_processing(cpu, arm_op);
+}
+
+static FORCE_INLINE void arm7t_alu_op(arm7_t* cpu, UINT32 opcode)
+{
+ INT32 op = ARM7_BFE(opcode, 6, 4);
+ INT32 Rs = ARM7_BFE(opcode, 3, 3);
+ INT32 Rd = ARM7_BFE(opcode, 0, 3);
+ if (op == 13) {
+ UINT32 arm_op = (0xE << 28) | (1 << 20) | (Rd << 16) | (Rd << 8) | (9 << 4) | (Rs);
+ arm7_multiply(cpu, arm_op);
+ cpu->registers[CPSR] &= ~(1 << 29);
+ return;
+ }
+
+ INT32 op_mapping[16] = {
+ /*AND{S}*/ 0,
+ /*EOR{S}*/ 1,
+ /*LSL{S}*/ 13,
+ /*LSR{S}*/ 13,
+ /*ASR{S}*/ 13,
+ /*ADC{S}*/ 5,
+ /*SBC{S}*/ 6,
+ /*ROR{S}*/ 13,
+ /*TST */ 8,
+ /*NEG{S}*/ 3, /*ARM Op: RSBS Rd, Rs, #0*/
+ /*CMP */ 10,
+ /*CMN */ 11,
+ /*ORR{S}*/ 12,
+ /*MUL{S}*/ 0,
+ /*BIC{S}*/ 14,
+ /*MVN{S}*/ 15
+ };
+
+ INT32 shift_mapping[16] = {
+ /*AND{S}*/ 0,
+ /*EOR{S}*/ 0,
+ /*LSL{S}*/ 0,
+ /*LSR{S}*/ 1,
+ /*ASR{S}*/ 2,
+ /*ADC{S}*/ 0,
+ /*SBC{S}*/ 0,
+ /*ROR{S}*/ 3,
+ /*TST */ 0,
+ /*NEG{S}*/ 0, /*ARM Op: RSBS Rd, Rs, #0*/
+ /*CMP */ 0,
+ /*CMN */ 0,
+ /*ORR{S}*/ 0,
+ /*MUL{S}*/ 0,
+ /*BIC{S}*/ 0,
+ /*MVN{S}*/ 0
+ };
+ INT32 alu_op = (0xfe0cba38d65ddd10ULL >> (op * 4)) & 0xf;
+ INT32 shift_op = (0x0000000030021000ULL >> (op * 4)) & 0xf;
+ INT32 Rn = (op == 9) ? Rs : Rd;
+
+ UINT32 arm_op = (0xEu << 28) | (alu_op << 21) | (1 << 20) | (Rn << 16) | (Rd << 12) | (shift_op << 5);
+
+ if (alu_op == 13)
+ arm_op |= (Rs << 8) | (1 << 4) | Rd; // Special case shifts
+ else if (op == 9)
+ arm_op |= 1 << 25; // Special case NEG
+ else
+ arm_op |= Rs;
+ arm7_data_processing(cpu, arm_op);
+}
+
+static FORCE_INLINE void arm7t_hi_reg_op(arm7_t* cpu, UINT32 opcode)
+{
+ INT32 op = ARM7_BFE(opcode, 8, 2);
+ INT32 H1 = ARM7_BFE(opcode, 7, 1);
+ INT32 H2 = ARM7_BFE(opcode, 6, 1);
+ INT32 Rs = ARM7_BFE(opcode, 3, 3);
+ INT32 Rd = ARM7_BFE(opcode, 0, 3);
+
+ Rs |= H2 << 3;
+ Rd |= H1 << 3;
+
+ if (op == 3) {
+ // Only the Rs field is populated since that is all that is needed for
+ // arm7_branch_exchange
+ INT32 arm_op = Rs;
+ arm7_branch_exchange(cpu, arm_op);
+ } else {
+ INT32 S = op == 1;
+ INT32 op_mapping[3] = {/*Add*/4, /*CMP*/10,/*MOV*/13 };
+ op = op_mapping[op];
+ //cccc 001o oooS nnnn dddd rrrr OOOO OOOO
+ UINT32 arm_op = (op << 21) | (S << 20) | (op == 13 ? 0 : (Rd << 16)) | (Rd << 12) | (Rs << 0);
+ arm7_data_processing(cpu, arm_op);
+ }
+}
+
+static FORCE_INLINE void arm9t_hi_reg_op(arm7_t* cpu, UINT32 opcode)
+{
+ INT32 op = ARM7_BFE(opcode, 8, 2);
+ INT32 H1 = ARM7_BFE(opcode, 7, 1);
+ INT32 H2 = ARM7_BFE(opcode, 6, 1);
+ INT32 Rs = ARM7_BFE(opcode, 3, 3);
+ INT32 Rd = ARM7_BFE(opcode, 0, 3);
+
+ Rs |= H2 << 3;
+ Rd |= H1 << 3;
+
+ if (op == 3) {
+ // Only the Rs field is populated since that is all that is needed for
+ // arm7_branch_exchange
+ INT32 arm_op = Rs;
+ INT32 blx = H1;
+ if (blx)
+ arm9_branch_link_exchange(cpu, arm_op);
+ else
+ arm7_branch_exchange(cpu, arm_op);
+ } else {
+ INT32 S = op == 1;
+ INT32 op_mapping[3] = {/*Add*/4, /*CMP*/10,/*MOV*/13 };
+ op = op_mapping[op];
+ //cccc 001o oooS nnnn dddd rrrr OOOO OOOO
+ UINT32 arm_op = (op << 21) | (S << 20) | (op == 13 ? 0 : (Rd << 16)) | (Rd << 12) | (Rs << 0);
+ arm7_data_processing(cpu, arm_op);
+ }
+}
+
+static FORCE_INLINE void arm9t_pc_rel_ldst(arm7_t* cpu, UINT32 opcode)
+{
+ INT32 offset = ARM7_BFE(opcode, 0, 8) * 4;
+ INT32 Rd = ARM7_BFE(opcode, 8, 3);
+ UINT32 addr = (cpu->registers[PC] + offset + 2) & (~3);
+ UINT32 data = cpu->read32(cpu->user_data, addr);
+ arm9_reg_write_r15_thumb(cpu, Rd, data);
+ cpu->i_cycles++;
+}
+
+static FORCE_INLINE void arm7t_pc_rel_ldst(arm7_t* cpu, UINT32 opcode)
+{
+ INT32 offset = ARM7_BFE(opcode, 0, 8) * 4;
+ INT32 Rd = ARM7_BFE(opcode, 8, 3);
+ UINT32 addr = (cpu->registers[PC] + offset + 2) & (~3);
+ UINT32 data = cpu->read32(cpu->user_data, addr);
+ arm7_reg_write(cpu, Rd, data);
+ cpu->i_cycles++;
+}
+
+static FORCE_INLINE void arm7t_reg_off_ldst(arm7_t* cpu, UINT32 opcode)
+{
+ bool B = ARM7_BFE(opcode, 10, 1);
+ bool L = ARM7_BFE(opcode, 11, 1);
+ INT32 Ro = ARM7_BFE(opcode, 6, 3);
+ INT32 Rb = ARM7_BFE(opcode, 3, 3);
+ INT32 Rd = ARM7_BFE(opcode, 0, 3);
+
+ INT32 r15_off = 2;
+ Ro = arm7_reg_read_r15_adj(cpu, Ro, r15_off);
+ Rb = arm7_reg_read_r15_adj(cpu, Rb, r15_off);
+
+ UINT32 addr = Ro + Rb;
+ // Store before write back
+ if (L == 0) {
+ UINT32 data = arm7_reg_read_r15_adj(cpu, Rd, r15_off);
+ if (B == 1)
+ cpu->write8( cpu->user_data, addr, data);
+ else
+ cpu->write32(cpu->user_data, addr, data);
+ } else { // Load
+ UINT32 data = B ? cpu->read8(cpu->user_data, addr) : arm7_rotr(cpu->read32(cpu->user_data, addr), (addr & 0x3) * 8);
+ arm7_reg_write(cpu, Rd, data);
+ cpu->i_cycles++;
+ }
+}
+
+static FORCE_INLINE void arm9t_reg_off_ldst(arm7_t* cpu, UINT32 opcode)
+{
+ bool B = ARM7_BFE(opcode, 10, 1);
+ bool L = ARM7_BFE(opcode, 11, 1);
+ INT32 Ro = ARM7_BFE(opcode, 6, 3);
+ INT32 Rb = ARM7_BFE(opcode, 3, 3);
+ INT32 Rd = ARM7_BFE(opcode, 0, 3);
+
+ INT32 r15_off = 2;
+ Ro = arm7_reg_read_r15_adj(cpu, Ro, r15_off);
+ Rb = arm7_reg_read_r15_adj(cpu, Rb, r15_off);
+
+ UINT32 addr = Ro + Rb;
+ // Store before write back
+ if (L == 0) {
+ UINT32 data = arm7_reg_read_r15_adj(cpu, Rd, r15_off);
+ if (B == 1)
+ cpu->write8( cpu->user_data, addr, data);
+ else
+ cpu->write32(cpu->user_data, addr, data);
+ } else { // Load
+ UINT32 data = B ? cpu->read8(cpu->user_data, addr) : arm7_rotr(cpu->read32(cpu->user_data, addr), (addr & 0x3) * 8);
+ arm9_reg_write_r15_thumb(cpu, Rd, data);
+ cpu->i_cycles++;
+ }
+
+}
+
+static FORCE_INLINE void arm7t_ldst_bh(arm7_t* cpu, UINT32 opcode)
+{
+ INT32 op = ARM7_BFE(opcode, 10, 2);
+ INT32 Ro = ARM7_BFE(opcode, 6, 3);
+ INT32 Rb = ARM7_BFE(opcode, 3, 3);
+ INT32 Rd = ARM7_BFE(opcode, 0, 3);
+
+ INT32 r15_off = 2;
+ Ro = arm7_reg_read_r15_adj(cpu, Ro, r15_off);
+ Rb = arm7_reg_read_r15_adj(cpu, Rb, r15_off);
+
+ UINT32 addr = Ro + Rb;
+
+ UINT32 data;
+ switch (op) {
+ case 0: //Store Halfword
+ data = arm7_reg_read_r15_adj(cpu, Rd, r15_off);
+ break;
+ case 1: //Load Sign Extended Byte
+ data = cpu->read8(cpu->user_data, addr);
+ cpu->i_cycles++;
+ if (ARM7_BFE(data, 7, 1))
+ data |= 0xffffff00;
+ break;
+ case 2: //Load Halfword
+ data = arm7_rotr(cpu->read16(cpu->user_data, addr), (addr & 0x1) * 8);
+ cpu->i_cycles++;
+ break;
+ case 3: //Load Sign Extended Half
+ data = arm7_rotr(cpu->read16(cpu->user_data, addr), (addr & 0x1) * 8) & 0xffff;
+ cpu->i_cycles++;
+ //Unaligned halfwords sign extend the byte
+ if ((addr & 1) && ARM7_BFE(data, 7, 1))
+ data |= 0xffffff00;
+ else if (ARM7_BFE(data, 15, 1))
+ data |= 0xffff0000;
+ break;
+ }
+ if (op == 0)
+ cpu->write16(cpu->user_data, addr, data);
+ else
+ arm7_reg_write(cpu, Rd, data);
+}
+
+static FORCE_INLINE void arm7t_imm_off_ldst(arm7_t* cpu, UINT32 opcode) {
+ bool B = ARM7_BFE(opcode, 12, 1);
+ bool L = ARM7_BFE(opcode, 11, 1);
+ INT32 offset = ARM7_BFE(opcode, 6, 5);
+
+ UINT32 Rd = ARM7_BFE(opcode, 0, 3);
+ UINT32 Rb = ARM7_BFE(opcode, 3, 3);
+ UINT32 addr = arm7_reg_read_r15_adj(cpu, Rb, 4);
+ //Offset is in 4B increments for word loads
+ if (!B)
+ offset *= 4;
+ addr += offset;
+ if (L == 0) { // Store
+ UINT32 data = arm7_reg_read_r15_adj(cpu, Rd, 8);
+ if (B == 1)
+ cpu->write8( cpu->user_data, addr, data);
+ else
+ cpu->write32(cpu->user_data, addr, data);
+ } else { // Load
+ UINT32 data = B ? cpu->read8(cpu->user_data, addr) : arm7_rotr(cpu->read32(cpu->user_data, addr), (addr & 0x3) * 8);
+ cpu->i_cycles++;
+ arm7_reg_write(cpu, Rd, data);
+ }
+}
+
+static FORCE_INLINE void arm9t_imm_off_ldst(arm7_t* cpu, UINT32 opcode)
+{
+ bool B = ARM7_BFE(opcode, 12, 1);
+ bool L = ARM7_BFE(opcode, 11, 1);
+ INT32 offset = ARM7_BFE(opcode, 6, 5);
+
+ UINT32 Rd = ARM7_BFE(opcode, 0, 3);
+ UINT32 Rb = ARM7_BFE(opcode, 3, 3);
+ UINT32 addr = arm7_reg_read_r15_adj(cpu, Rb, 4);
+ //Offset is in 4B increments for word loads
+ if (!B)
+ offset *= 4;
+ addr += offset;
+ if (L == 0) { // Store
+ UINT32 data = arm7_reg_read_r15_adj(cpu, Rd, 8);
+ if (B == 1)
+ cpu->write8( cpu->user_data, addr, data);
+ else
+ cpu->write32(cpu->user_data, addr, data);
+ } else { // Load
+ UINT32 data = B ? cpu->read8(cpu->user_data, addr) : arm7_rotr(cpu->read32(cpu->user_data, addr), (addr & 0x3) * 8);
+ cpu->i_cycles++;
+ arm9_reg_write_r15_thumb(cpu, Rd, data);
+ }
+}
+
+static FORCE_INLINE void arm7t_imm_off_ldst_bh(arm7_t* cpu, UINT32 opcode)
+{
+ bool L = ARM7_BFE(opcode, 11, 1);
+ INT32 offset = ARM7_BFE(opcode, 6, 5);
+
+ UINT32 Rd = ARM7_BFE(opcode, 0, 3);
+ UINT32 addr = arm7_reg_read_r15_adj(cpu, ARM7_BFE(opcode, 3, 3), 4);
+
+ addr += offset * 2;
+ UINT32 data = 0;
+ if (L == 0) { // Store
+ data = arm7_reg_read_r15_adj(cpu, Rd, 8);
+ cpu->write16(cpu->user_data, addr, data);
+ } else { // Load
+ data = arm7_rotr(cpu->read16(cpu->user_data, addr), (addr & 0x1) * 8);
+ arm7_reg_write(cpu, Rd, data);
+ cpu->i_cycles++;
+ }
+}
+
+static FORCE_INLINE void arm7t_stack_off_ldst(arm7_t* cpu, UINT32 opcode)
+{
+ bool L = ARM7_BFE(opcode, 11, 1);
+ UINT64 Rd = ARM7_BFE(opcode, 8, 3);
+ INT32 offset = ARM7_BFE(opcode, 0, 8);
+ UINT32 addr = arm7_reg_read(cpu, 13);
+
+ addr += offset * 4;
+ UINT32 data;
+ if (L == 0) { // Store
+ data = arm7_reg_read_r15_adj(cpu, Rd, 8);
+ cpu->write32(cpu->user_data, addr, data);
+ } else { // Load
+ data = arm7_rotr(cpu->read32(cpu->user_data, addr), (addr & 0x3) * 8);
+ arm7_reg_write(cpu, Rd, data);
+ cpu->i_cycles++;
+ }
+}
+
+static FORCE_INLINE void arm9t_stack_off_ldst(arm7_t* cpu, UINT32 opcode)
+{
+ bool L = ARM7_BFE(opcode, 11, 1);
+ UINT64 Rd = ARM7_BFE(opcode, 8, 3);
+ INT32 offset = ARM7_BFE(opcode, 0, 8);
+ UINT32 addr = arm7_reg_read(cpu, 13);
+
+ addr += offset * 4;
+ UINT32 data;
+ if (L == 0) { // Store
+ data = arm7_reg_read_r15_adj(cpu, Rd, 8);
+ cpu->write32(cpu->user_data, addr, data);
+ } else { // Load
+ data = arm7_rotr(cpu->read32(cpu->user_data, addr), (addr & 0x3) * 8);
+ arm9_reg_write_r15_thumb(cpu, Rd, data);
+ cpu->i_cycles++;
+ }
+}
+
+static FORCE_INLINE void arm7t_load_addr(arm7_t* cpu, UINT32 opcode)
+{
+ bool SP = ARM7_BFE(opcode, 11, 1);
+ INT32 Rd = ARM7_BFE(opcode, 8, 3);
+ INT32 imm = ARM7_BFE(opcode, 0, 8) * 4;
+
+ UINT32 v = arm7_reg_read_r15_adj(cpu, SP ? 13 : 15, 4);
+ if (!SP)
+ v &= ~3; //Bit 1 of PC always read as 0
+ v += imm;
+ arm7_reg_write(cpu, Rd, v);
+}
+
+static FORCE_INLINE void arm7t_add_off_sp(arm7_t* cpu, UINT32 opcode)
+{
+ INT32 offset = ARM7_BFE(opcode, 0, 7) * 4;
+ INT32 sign = ARM7_BFE(opcode, 7, 1);
+ if (sign)
+ offset = -offset;
+ UINT32 value = arm7_reg_read(cpu, 13);
+ arm7_reg_write(cpu, 13, value + offset);
+}
+
+static FORCE_INLINE void arm7t_push_pop_reg(arm7_t* cpu, UINT32 opcode)
+{
+ bool push_or_pop = ARM7_BFE(opcode, 11, 1);
+ bool include_pc_lr = ARM7_BFE(opcode, 8, 1);
+ UINT32 r_list = ARM7_BFE(opcode, 0, 8);
+ INT32 P = !push_or_pop;
+ INT32 W = 1;
+ INT32 U = push_or_pop;
+
+ UINT32 arm_op = (0xe << 28) | (4 << 25) | (P << 24) | (U << 23) | (W << 21) | (push_or_pop << 20) | (13 << 16) | r_list;
+ if (include_pc_lr)
+ arm_op |= push_or_pop ? 0x8000 : 0x4000;
+ arm7_block_transfer(cpu, arm_op);
+}
+
+static FORCE_INLINE void arm9t_push_pop_reg(arm7_t* cpu, UINT32 opcode)
+{
+ bool push_or_pop = ARM7_BFE(opcode, 11, 1);
+ bool include_pc_lr = ARM7_BFE(opcode, 8, 1);
+ UINT32 r_list = ARM7_BFE(opcode, 0, 8);
+ INT32 P = !push_or_pop;
+ INT32 W = 1;
+ INT32 U = push_or_pop;
+
+ UINT32 arm_op = (0xe << 28) | (4 << 25) | (P << 24) | (U << 23) | (W << 21) | (push_or_pop << 20) | (13 << 16) | r_list;
+ if (include_pc_lr)
+ arm_op |= push_or_pop ? 0x8000 : 0x4000;
+ arm9_block_transfer(cpu, arm_op);
+}
+
+static FORCE_INLINE void arm7t_mult_ldst(arm7_t* cpu, UINT32 opcode)
+{
+ bool write_or_read = ARM7_BFE(opcode, 11, 1);
+ INT32 Rb = ARM7_BFE(opcode, 8, 3);
+ UINT32 r_list = ARM7_BFE(opcode, 0, 8);
+
+ INT32 P = 0;
+ INT32 U = 1;
+ INT32 W = 1;
+ // Maps to LDMIA, STMIA opcode
+ UINT32 arm_op = (0xe << 28) | (4 << 25) | (P << 24) | (U << 23) | (W << 21) | (write_or_read << 20) | (Rb << 16) | r_list;
+ arm7_block_transfer(cpu, arm_op);
+}
+
+static FORCE_INLINE void arm9t_mult_ldst(arm7_t* cpu, UINT32 opcode)
+{
+ bool write_or_read = ARM7_BFE(opcode, 11, 1);
+ INT32 Rb = ARM7_BFE(opcode, 8, 3);
+ UINT32 r_list = ARM7_BFE(opcode, 0, 8);
+
+ INT32 P = 0;
+ INT32 U = 1;
+ INT32 W = 1;
+ // Maps to LDMIA, STMIA opcode
+ UINT32 arm_op = (0xe << 28) | (4 << 25) | (P << 24) | (U << 23) | (W << 21) | (write_or_read << 20) | (Rb << 16) | r_list;
+ arm9_block_transfer(cpu, arm_op);
+}
+
+static FORCE_INLINE void arm7t_cond_branch(arm7_t* cpu, UINT32 opcode)
+{
+ INT32 cond = ARM7_BFE(opcode, 8, 4);
+ INT32 s_off = ARM7_BFE(opcode, 0, 8);
+ if (ARM7_BFE(s_off, 7, 1))
+ s_off |= 0xFFFFFF00;
+ //ARM equv: cccc 1010 OOOO OOOO OOOO OOOO OOOO OOOO
+ UINT32 arm_op = (cond << 28) | (0xA << 24);
+ if (arm7_check_cond_code(cpu, arm_op)) {
+ cpu->registers[PC] += s_off * 2 + 2;
+ cpu->prefetch_pc = -1;
+ }
+}
+
+static FORCE_INLINE void arm7t_soft_interrupt(arm7_t* cpu, UINT32 opcode)
+{
+ arm7_software_interrupt(cpu, opcode);
+}
+
+static FORCE_INLINE void arm7t_branch(arm7_t* cpu, UINT32 opcode) {
+ INT32 offset = ARM7_BFE(opcode, 0, 11) << 1;
+ if (ARM7_BFE(offset, 11, 1))
+ offset |= 0xfffff000;
+ cpu->registers[PC] += offset + 2;
+ cpu->prefetch_pc = -1;
+}
+
+static FORCE_INLINE void arm7t_long_branch_link(arm7_t* cpu, UINT32 opcode)
+{
+ bool H = ARM7_BFE(opcode, 11, 1);
+ INT32 offset = ARM7_BFE(opcode, 0, 11);
+ INT32 thumb_branch = ARM7_BFE(opcode, 12, 1);
+ UINT32 link_reg = arm7_reg_read(cpu, LR);
+ // TODO: Is this +4 supposed to be +2 ARM7TDMI page 5-40
+ if (H == 0) {
+ offset <<= 12;
+ if (offset & 0x400000)
+ offset |= 0xFF800000;
+ arm7_reg_write(cpu, LR, cpu->registers[PC] + offset + 2);
+ } else {
+ link_reg += (offset << 1);
+ UINT32 pc = cpu->registers[PC];
+ cpu->registers[PC] = link_reg;
+ arm7_set_thumb_bit(cpu, thumb_branch);
+ arm7_reg_write(cpu, LR, (pc | 1));
+ cpu->prefetch_pc = -1;
+ if (!thumb_branch)
+ arm7_set_thumb_bit(cpu, false);
+ }
+}
+
+static FORCE_INLINE void arm7t_unknown(arm7_t* cpu, UINT32 opcode)
+{
+ bool thumb = arm7_get_thumb_bit(cpu);
+ cpu->registers[R14_und] = cpu->registers[PC] - (thumb ? 0 : 4);
+ cpu->registers[PC] = cpu->irq_table_address + 0x4;
+ UINT32 cpsr = cpu->registers[CPSR];
+ cpu->registers[SPSR_und] = cpsr;
+ //Update mode to supervisor and block irqs
+ cpu->registers[CPSR] = (cpsr & 0xffffffE0) | 0x1b | 0x80;
+ arm7_set_thumb_bit(cpu, false);
+ printf("Unhandled Thumb Instruction Class: (arm7t_unknown) Opcode %x\n", opcode);
+ printf("PC: %08x\n", cpu->registers[PC]);
+ if (cpu->trigger_breakpoint)cpu->trigger_breakpoint(cpu->user_data);
+}
+
+#endif
diff --git a/src/burn/drv/gba/bios.h b/src/burn/drv/gba/bios.h
new file mode 100644
index 000000000..4136f1869
--- /dev/null
+++ b/src/burn/drv/gba/bios.h
@@ -0,0 +1,1384 @@
+////////////////////////////////////////////////////////////////////////
+// Cult-of-GBA replacement bios //
+// License: MIT //
+// Authors: DenSinH, Fleroviux, Sky //
+// Source: https://github.com/Cult-of-GBA/BIOS/ //
+// Commit-Hash: 0d9dd3e6283896eab340522d51234799d5c46626 //
+////////////////////////////////////////////////////////////////////////
+
+#pragma once
+
+#ifndef _GBA_BIOS_
+#define _GBA_BIOS_
+
+static const UINT8 gba_bios_bin[] = {
+ 0x06, 0x00, 0x00, 0xea, 0x5a, 0x00, 0x00, 0xea, 0x46, 0x00, 0x00, 0xea,
+ 0x59, 0x00, 0x00, 0xea, 0x58, 0x00, 0x00, 0xea, 0x57, 0x00, 0x00, 0xea,
+ 0x3c, 0x00, 0x00, 0xea, 0x55, 0x00, 0x00, 0xea, 0x01, 0x03, 0xa0, 0xe3,
+ 0x08, 0x00, 0xc0, 0xe5, 0x1f, 0xf0, 0x29, 0xe3, 0x26, 0x00, 0x00, 0xeb,
+ 0xff, 0x00, 0xa0, 0xe3, 0x19, 0x03, 0x00, 0xeb, 0xcc, 0x09, 0x00, 0xeb,
+ 0xff, 0x00, 0xa0, 0xe3, 0x16, 0x03, 0x00, 0xeb, 0x01, 0x03, 0xa0, 0xe3,
+ 0x1f, 0x00, 0x00, 0xeb, 0x01, 0x03, 0xa0, 0xe3, 0x98, 0x10, 0xa0, 0xe3,
+ 0xb2, 0x20, 0xd1, 0xe0, 0xb2, 0x30, 0xd1, 0xe0, 0xb2, 0x30, 0x80, 0xe1,
+ 0xb8, 0x00, 0x51, 0xe3, 0xfa, 0xff, 0xff, 0xba, 0xb4, 0x00, 0xc0, 0xe1,
+ 0xb8, 0x00, 0xc0, 0xe1, 0xba, 0x04, 0xc0, 0xe1, 0x82, 0x1f, 0x80, 0xe2,
+ 0xb0, 0x00, 0xc1, 0xe1, 0x1f, 0xf0, 0x29, 0xe3, 0x00, 0x00, 0xa0, 0xe3,
+ 0x00, 0x10, 0xa0, 0xe3, 0x00, 0x20, 0xa0, 0xe3, 0x00, 0x30, 0xa0, 0xe3,
+ 0x02, 0xe3, 0xa0, 0xe3, 0x1e, 0xff, 0x2f, 0xe1, 0x00, 0x00, 0x80, 0x00,
+ 0x20, 0x00, 0x00, 0x01, 0x26, 0x00, 0x00, 0x01, 0x30, 0x00, 0x00, 0x01,
+ 0x36, 0x00, 0x00, 0x01, 0x82, 0x00, 0x0e, 0x88, 0x88, 0x00, 0x00, 0x02,
+ 0x34, 0x01, 0x00, 0x80, 0x01, 0x03, 0xa0, 0xe3, 0x06, 0x20, 0x50, 0xe5,
+ 0x01, 0x00, 0x12, 0xe3, 0x02, 0xe3, 0xa0, 0x03, 0x02, 0xe4, 0xa0, 0x13,
+ 0x13, 0xf0, 0x29, 0xe3, 0x90, 0xdf, 0x9f, 0xe5, 0x00, 0xe0, 0xa0, 0xe3,
+ 0x0e, 0xf0, 0x69, 0xe1, 0x12, 0xf0, 0x21, 0xe3, 0x84, 0xdf, 0x9f, 0xe5,
+ 0x00, 0xe0, 0xa0, 0xe3, 0x0e, 0xf0, 0x69, 0xe1, 0x1f, 0xf0, 0x29, 0xe3,
+ 0x78, 0xdf, 0x9f, 0xe5, 0x78, 0x1f, 0x9f, 0xe5, 0x00, 0x20, 0xa0, 0xe3,
+ 0x01, 0x20, 0x80, 0xe7, 0x04, 0x10, 0x91, 0xe2, 0xfc, 0xff, 0xff, 0x1a,
+ 0xff, 0x1f, 0x10, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x0f, 0x50, 0x2d, 0xe9,
+ 0x01, 0x03, 0xa0, 0xe3, 0x00, 0xe0, 0x8f, 0xe2, 0x04, 0xf0, 0x10, 0xe5,
+ 0x0f, 0x50, 0xbd, 0xe8, 0x04, 0xf0, 0x5e, 0xe2, 0x00, 0x58, 0x2d, 0xe9,
+ 0x02, 0xc0, 0x5e, 0xe5, 0x44, 0xb0, 0x8f, 0xe2, 0x0c, 0xc1, 0x9b, 0xe7,
+ 0x00, 0xb0, 0x4f, 0xe1, 0x00, 0x08, 0x2d, 0xe9, 0x80, 0xb0, 0x0b, 0xe2,
+ 0x1f, 0xb0, 0x8b, 0xe3, 0x0b, 0xf0, 0x29, 0xe1, 0x04, 0x40, 0x2d, 0xe9,
+ 0x00, 0xe0, 0x8f, 0xe2, 0x1c, 0xff, 0x2f, 0xe1, 0x04, 0x40, 0xbd, 0xe8,
+ 0x93, 0xc0, 0xa0, 0xe3, 0x0c, 0xf0, 0x29, 0xe1, 0x00, 0x08, 0xbd, 0xe8,
+ 0x0b, 0xf0, 0x69, 0xe1, 0x00, 0x58, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1,
+ 0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 0xb8, 0x00, 0x00, 0x00,
+ 0xa0, 0x0c, 0x00, 0x00, 0xfc, 0x0b, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00,
+ 0x30, 0x0c, 0x00, 0x00, 0x28, 0x0c, 0x00, 0x00, 0x34, 0x07, 0x00, 0x00,
+ 0x28, 0x07, 0x00, 0x00, 0x98, 0x07, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00,
+ 0xec, 0x07, 0x00, 0x00, 0x34, 0x02, 0x00, 0x00, 0xc4, 0x02, 0x00, 0x00,
+ 0x2c, 0x02, 0x00, 0x00, 0x58, 0x0b, 0x00, 0x00, 0xe0, 0x0a, 0x00, 0x00,
+ 0x50, 0x04, 0x00, 0x00, 0x28, 0x03, 0x00, 0x00, 0xac, 0x03, 0x00, 0x00,
+ 0x8c, 0x05, 0x00, 0x00, 0x24, 0x06, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00,
+ 0xd0, 0x04, 0x00, 0x00, 0x08, 0x05, 0x00, 0x00, 0x54, 0x05, 0x00, 0x00,
+ 0x28, 0x02, 0x00, 0x00, 0xac, 0x0f, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00,
+ 0x28, 0x02, 0x00, 0x00, 0x40, 0x11, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00,
+ 0xa4, 0x0e, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00,
+ 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00,
+ 0x28, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x1c, 0x0c, 0x00, 0x00,
+ 0xd4, 0x10, 0x00, 0x00, 0xc0, 0x10, 0x00, 0x00, 0x08, 0x0f, 0x00, 0x00,
+ 0x1e, 0xff, 0x2f, 0xe1, 0x44, 0x0e, 0x9f, 0xe5, 0x1e, 0xff, 0x2f, 0xe1,
+ 0x18, 0x00, 0x2d, 0xe9, 0x82, 0x35, 0xa0, 0xe1, 0xa3, 0x35, 0xb0, 0xe1,
+ 0x1d, 0x00, 0x00, 0x0a, 0xa2, 0x2c, 0xb0, 0xe1, 0x01, 0x20, 0x82, 0x23,
+ 0x00, 0x40, 0x8f, 0xe2, 0x02, 0xf1, 0x94, 0xe7, 0x64, 0x02, 0x00, 0x00,
+ 0x7c, 0x02, 0x00, 0x00, 0x94, 0x02, 0x00, 0x00, 0xac, 0x02, 0x00, 0x00,
+ 0xb2, 0x40, 0xd0, 0xe0, 0xb2, 0x40, 0xc1, 0xe0, 0x01, 0x30, 0x53, 0xe2,
+ 0xfb, 0xff, 0xff, 0x1a, 0x18, 0x00, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1,
+ 0xb0, 0x40, 0xd0, 0xe1, 0xb2, 0x40, 0xc1, 0xe0, 0x01, 0x30, 0x53, 0xe2,
+ 0xfc, 0xff, 0xff, 0x1a, 0x18, 0x00, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1,
+ 0x10, 0x00, 0xb0, 0xe8, 0x10, 0x00, 0xa1, 0xe8, 0x01, 0x30, 0x53, 0xe2,
+ 0xfb, 0xff, 0xff, 0x1a, 0x18, 0x00, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1,
+ 0x00, 0x40, 0x90, 0xe5, 0x04, 0x40, 0x81, 0xe4, 0x01, 0x30, 0x53, 0xe2,
+ 0xfc, 0xff, 0xff, 0x1a, 0x18, 0x00, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1,
+ 0xf8, 0x0f, 0x2d, 0xe9, 0x82, 0x35, 0xa0, 0xe1, 0xa3, 0x35, 0xb0, 0xe1,
+ 0x12, 0x00, 0x00, 0x0a, 0x01, 0x04, 0x12, 0xe3, 0x05, 0x00, 0x00, 0x1a,
+ 0xf0, 0x0f, 0xb0, 0xe8, 0xf0, 0x0f, 0xa1, 0xe8, 0x08, 0x30, 0x53, 0xe2,
+ 0xfb, 0xff, 0xff, 0xca, 0xf8, 0x0f, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1,
+ 0x00, 0x40, 0x90, 0xe5, 0x04, 0x50, 0xa0, 0xe1, 0x04, 0x60, 0xa0, 0xe1,
+ 0x04, 0x70, 0xa0, 0xe1, 0x04, 0x80, 0xa0, 0xe1, 0x04, 0x90, 0xa0, 0xe1,
+ 0x04, 0xa0, 0xa0, 0xe1, 0x04, 0xb0, 0xa0, 0xe1, 0xf0, 0x0f, 0xa1, 0xe8,
+ 0x08, 0x30, 0x53, 0xe2, 0xfc, 0xff, 0xff, 0xca, 0xf8, 0x0f, 0xbd, 0xe8,
+ 0x1e, 0xff, 0x2f, 0xe1, 0x78, 0x00, 0x2d, 0xe9, 0x04, 0x20, 0x90, 0xe4,
+ 0x22, 0x24, 0xb0, 0xe1, 0x1a, 0x00, 0x00, 0x0a, 0x01, 0x30, 0xd0, 0xe4,
+ 0x01, 0x34, 0x83, 0xe3, 0x80, 0x00, 0x13, 0xe3, 0x06, 0x00, 0x00, 0x1a,
+ 0x01, 0x40, 0xd0, 0xe4, 0x01, 0x40, 0xc1, 0xe4, 0x01, 0x20, 0x52, 0xe2,
+ 0x12, 0x00, 0x00, 0x0a, 0x83, 0x30, 0xb0, 0xe1, 0xf7, 0xff, 0xff, 0x3a,
+ 0xf4, 0xff, 0xff, 0xea, 0x01, 0x40, 0xd0, 0xe4, 0x01, 0x50, 0xd0, 0xe4,
+ 0x04, 0x54, 0x85, 0xe1, 0x0f, 0x5a, 0xc5, 0xe3, 0x01, 0x50, 0x85, 0xe2,
+ 0x24, 0x42, 0xa0, 0xe1, 0x03, 0x40, 0x84, 0xe2, 0x05, 0x60, 0x51, 0xe7,
+ 0x01, 0x60, 0xc1, 0xe4, 0x01, 0x20, 0x52, 0xe2, 0x04, 0x00, 0x00, 0x0a,
+ 0x01, 0x40, 0x54, 0xe2, 0xf9, 0xff, 0xff, 0x1a, 0x83, 0x30, 0xb0, 0xe1,
+ 0xe7, 0xff, 0xff, 0x3a, 0xe4, 0xff, 0xff, 0xea, 0x78, 0x00, 0xbd, 0xe8,
+ 0x1e, 0xff, 0x2f, 0xe1, 0xf8, 0x00, 0x2d, 0xe9, 0x04, 0x20, 0x90, 0xe4,
+ 0x22, 0x24, 0xb0, 0xe1, 0x22, 0x00, 0x00, 0x0a, 0x01, 0x30, 0xd0, 0xe4,
+ 0x01, 0x34, 0x83, 0xe3, 0x80, 0x00, 0x13, 0xe3, 0x0a, 0x00, 0x00, 0x1a,
+ 0x01, 0x40, 0xd0, 0xe4, 0x01, 0x00, 0x11, 0xe3, 0x04, 0x70, 0xa0, 0x01,
+ 0x04, 0x74, 0x87, 0x11, 0xb0, 0x70, 0xc1, 0x11, 0x01, 0x10, 0x81, 0xe2,
+ 0x01, 0x20, 0x52, 0xe2, 0x16, 0x00, 0x00, 0x0a, 0x83, 0x30, 0xb0, 0xe1,
+ 0xf3, 0xff, 0xff, 0x3a, 0xf0, 0xff, 0xff, 0xea, 0x01, 0x40, 0xd0, 0xe4,
+ 0x01, 0x50, 0xd0, 0xe4, 0x04, 0x54, 0x85, 0xe1, 0x0f, 0x5a, 0xc5, 0xe3,
+ 0x01, 0x50, 0x85, 0xe2, 0x24, 0x42, 0xa0, 0xe1, 0x03, 0x40, 0x84, 0xe2,
+ 0x05, 0x60, 0x51, 0xe7, 0x01, 0x00, 0x11, 0xe3, 0x06, 0x70, 0xa0, 0x01,
+ 0x06, 0x74, 0x87, 0x11, 0xb0, 0x70, 0xc1, 0x11, 0x01, 0x10, 0x81, 0xe2,
+ 0x01, 0x20, 0x52, 0xe2, 0x04, 0x00, 0x00, 0x0a, 0x01, 0x40, 0x54, 0xe2,
+ 0xf5, 0xff, 0xff, 0x1a, 0x83, 0x30, 0xb0, 0xe1, 0xdf, 0xff, 0xff, 0x3a,
+ 0xdc, 0xff, 0xff, 0xea, 0xf8, 0x00, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1,
+ 0xfc, 0x1f, 0x2d, 0xe9, 0xb0, 0x30, 0xd2, 0xe1, 0x00, 0x00, 0x53, 0xe3,
+ 0xa0, 0x4c, 0xb0, 0x11, 0x18, 0x00, 0x00, 0x0a, 0x02, 0x40, 0xd2, 0xe5,
+ 0x03, 0x50, 0xd2, 0xe5, 0x04, 0x20, 0x92, 0xe5, 0xa2, 0x6f, 0xa0, 0xe1,
+ 0x02, 0x21, 0xc2, 0xe3, 0x01, 0xc0, 0xa0, 0xe3, 0x1c, 0xc4, 0x6c, 0xe0,
+ 0x00, 0x70, 0xa0, 0xe3, 0x00, 0x90, 0xa0, 0xe3, 0x01, 0x30, 0x53, 0xe2,
+ 0x0d, 0x00, 0x00, 0x4a, 0x01, 0xb0, 0xd0, 0xe4, 0x00, 0x80, 0xa0, 0xe3,
+ 0x08, 0x00, 0x58, 0xe3, 0xf9, 0xff, 0xff, 0xaa, 0x3b, 0xa8, 0x1c, 0xe0,
+ 0x00, 0x00, 0x56, 0x03, 0x02, 0xa0, 0x8a, 0x10, 0x1a, 0x79, 0x87, 0xe1,
+ 0x05, 0x90, 0x89, 0xe0, 0x1f, 0x90, 0x19, 0xe2, 0x04, 0x70, 0x81, 0x04,
+ 0x00, 0x70, 0xa0, 0x03, 0x04, 0x80, 0x88, 0xe0, 0xf3, 0xff, 0xff, 0xea,
+ 0xfc, 0x1f, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x1c, 0x00, 0x2d, 0xe9,
+ 0x04, 0x20, 0x90, 0xe4, 0x22, 0x24, 0xa0, 0xe1, 0x00, 0x00, 0x52, 0xe3,
+ 0xa0, 0x3c, 0xb0, 0x11, 0x05, 0x00, 0x00, 0x0a, 0x01, 0x30, 0xd0, 0xe4,
+ 0x01, 0x30, 0xc1, 0xe4, 0x01, 0x40, 0xd0, 0xe4, 0x04, 0x30, 0x83, 0xe0,
+ 0x01, 0x20, 0x52, 0xe2, 0xfa, 0xff, 0xff, 0xca, 0x1c, 0x00, 0xbd, 0xe8,
+ 0x1e, 0xff, 0x2f, 0xe1, 0x3c, 0x00, 0x2d, 0xe9, 0x04, 0x20, 0x90, 0xe4,
+ 0x22, 0x24, 0xa0, 0xe1, 0x00, 0x00, 0x52, 0xe3, 0xa0, 0x3c, 0xb0, 0x11,
+ 0x0a, 0x00, 0x00, 0x0a, 0x01, 0x30, 0xd0, 0xe4, 0x01, 0x40, 0xd0, 0xe4,
+ 0x04, 0x44, 0x83, 0xe1, 0x03, 0x44, 0x84, 0xe0, 0xb2, 0x40, 0xc1, 0xe0,
+ 0x01, 0x30, 0xd0, 0xe4, 0x24, 0x34, 0x83, 0xe0, 0xff, 0x30, 0x03, 0xe2,
+ 0x01, 0x40, 0xd0, 0xe4, 0x02, 0x20, 0x52, 0xe2, 0xf6, 0xff, 0xff, 0xca,
+ 0x3c, 0x00, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x1c, 0x00, 0x2d, 0xe9,
+ 0x04, 0x20, 0x90, 0xe4, 0x22, 0x24, 0xa0, 0xe1, 0x00, 0x00, 0x52, 0xe3,
+ 0xa0, 0x3c, 0xb0, 0x11, 0x05, 0x00, 0x00, 0x0a, 0xb2, 0x30, 0xd0, 0xe0,
+ 0xb2, 0x30, 0xc1, 0xe0, 0xb2, 0x40, 0xd0, 0xe0, 0x04, 0x30, 0x83, 0xe0,
+ 0x02, 0x20, 0x52, 0xe2, 0xfa, 0xff, 0xff, 0xca, 0x1c, 0x00, 0xbd, 0xe8,
+ 0x1e, 0xff, 0x2f, 0xe1, 0xfc, 0x0f, 0x2d, 0xe9, 0x04, 0x30, 0x90, 0xe4,
+ 0x23, 0x24, 0xb0, 0xe1, 0xa0, 0x2c, 0xb0, 0x11, 0x1e, 0x00, 0x00, 0x0a,
+ 0x0f, 0x20, 0x03, 0xe2, 0x23, 0x34, 0xa0, 0xe1, 0x01, 0x40, 0xd0, 0xe4,
+ 0x84, 0x40, 0x80, 0xe0, 0x01, 0x40, 0x84, 0xe2, 0x00, 0x50, 0xa0, 0xe3,
+ 0x00, 0x60, 0xa0, 0xe3, 0x00, 0x70, 0xa0, 0xe1, 0x20, 0x80, 0xa0, 0xe3,
+ 0x04, 0x90, 0x94, 0xe4, 0x01, 0x80, 0x58, 0xe2, 0xfb, 0xff, 0xff, 0xba,
+ 0x00, 0xa0, 0xd7, 0xe5, 0x3f, 0xb0, 0x0a, 0xe2, 0x01, 0xb0, 0x8b, 0xe2,
+ 0x01, 0x70, 0xc7, 0xe3, 0x89, 0x90, 0xb0, 0xe1, 0x8b, 0x70, 0xa7, 0xe0,
+ 0x8a, 0xa0, 0xa0, 0x21, 0x80, 0x00, 0x1a, 0xe3, 0xf4, 0xff, 0xff, 0x0a,
+ 0x00, 0x70, 0xd7, 0xe5, 0x17, 0x56, 0x85, 0xe1, 0x00, 0x70, 0xa0, 0xe1,
+ 0x02, 0x60, 0x86, 0xe0, 0x1f, 0x60, 0x16, 0xe2, 0xee, 0xff, 0xff, 0x1a,
+ 0x04, 0x50, 0x81, 0xe4, 0x00, 0x50, 0xa0, 0xe3, 0x04, 0x30, 0x53, 0xe2,
+ 0xea, 0xff, 0xff, 0xca, 0xfc, 0x0f, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1,
+ 0x1c, 0x00, 0x2d, 0xe9, 0x04, 0x20, 0x90, 0xe4, 0x22, 0x24, 0xa0, 0xe1,
+ 0x00, 0x00, 0x52, 0xe3, 0xa0, 0x3c, 0xb0, 0x11, 0x12, 0x00, 0x00, 0x0a,
+ 0x01, 0x30, 0xd0, 0xe4, 0x83, 0x3c, 0xb0, 0xe1, 0xa3, 0x3c, 0xa0, 0xe1,
+ 0x06, 0x00, 0x00, 0x3a, 0x01, 0x40, 0xd0, 0xe4, 0x03, 0x30, 0x83, 0xe2,
+ 0x03, 0x20, 0x42, 0xe0, 0x01, 0x40, 0xc1, 0xe4, 0x01, 0x30, 0x53, 0xe2,
+ 0xfc, 0xff, 0xff, 0xca, 0x05, 0x00, 0x00, 0xea, 0x01, 0x30, 0x83, 0xe2,
+ 0x03, 0x20, 0x42, 0xe0, 0x01, 0x40, 0xd0, 0xe4, 0x01, 0x40, 0xc1, 0xe4,
+ 0x01, 0x30, 0x53, 0xe2, 0xfb, 0xff, 0xff, 0xca, 0x00, 0x00, 0x52, 0xe3,
+ 0xec, 0xff, 0xff, 0xca, 0x1c, 0x00, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1,
+ 0x7c, 0x00, 0x2d, 0xe9, 0x04, 0x20, 0x90, 0xe4, 0x22, 0x24, 0xa0, 0xe1,
+ 0x00, 0x00, 0x52, 0xe3, 0xa0, 0x3c, 0xb0, 0x11, 0x1d, 0x00, 0x00, 0x0a,
+ 0x00, 0x50, 0xa0, 0xe3, 0x00, 0x60, 0xa0, 0xe3, 0x01, 0x30, 0xd0, 0xe4,
+ 0x83, 0x3c, 0xb0, 0xe1, 0xa3, 0x3c, 0xa0, 0xe1, 0x0a, 0x00, 0x00, 0x3a,
+ 0x01, 0x40, 0xd0, 0xe4, 0x03, 0x30, 0x83, 0xe2, 0x03, 0x20, 0x42, 0xe0,
+ 0x01, 0x30, 0x53, 0xe2, 0x10, 0x00, 0x00, 0xba, 0x14, 0x65, 0x86, 0xe1,
+ 0x08, 0x50, 0x35, 0xe2, 0xfa, 0xff, 0xff, 0x1a, 0xb2, 0x60, 0xc1, 0xe0,
+ 0x00, 0x60, 0xa0, 0xe3, 0xf7, 0xff, 0xff, 0xea, 0x01, 0x30, 0x83, 0xe2,
+ 0x03, 0x20, 0x42, 0xe0, 0x01, 0x30, 0x53, 0xe2, 0x06, 0x00, 0x00, 0xba,
+ 0x01, 0x40, 0xd0, 0xe4, 0x14, 0x65, 0x86, 0xe1, 0x08, 0x50, 0x35, 0xe2,
+ 0xf9, 0xff, 0xff, 0x1a, 0xb2, 0x60, 0xc1, 0xe0, 0x00, 0x60, 0xa0, 0xe3,
+ 0xf6, 0xff, 0xff, 0xea, 0x00, 0x00, 0x52, 0xe3, 0xe3, 0xff, 0xff, 0xca,
+ 0x7c, 0x00, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x01, 0x30, 0xa0, 0xe1,
+ 0x00, 0x10, 0xa0, 0xe1, 0x03, 0x00, 0xa0, 0xe1, 0x14, 0x00, 0x2d, 0xe9,
+ 0x01, 0x40, 0xb0, 0xe1, 0x00, 0x10, 0x71, 0x42, 0x13, 0x00, 0x00, 0x0a,
+ 0x40, 0x40, 0x34, 0xe0, 0x00, 0x00, 0x60, 0x22, 0x01, 0x20, 0xa0, 0xe3,
+ 0xa0, 0x00, 0x51, 0xe1, 0x82, 0x20, 0xa0, 0x91, 0x81, 0x10, 0xa0, 0x91,
+ 0xfb, 0xff, 0xff, 0x9a, 0x00, 0x30, 0xa0, 0xe3, 0x00, 0x00, 0x51, 0xe1,
+ 0x01, 0x00, 0x40, 0xd0, 0x02, 0x30, 0x83, 0xd0, 0xa1, 0x10, 0xa0, 0x11,
+ 0xa2, 0x20, 0xb0, 0x11, 0xf9, 0xff, 0xff, 0x1a, 0x44, 0x10, 0x30, 0xe0,
+ 0x00, 0x10, 0xa1, 0xe2, 0x44, 0x00, 0x33, 0xe0, 0x00, 0x00, 0xa0, 0xe2,
+ 0x14, 0x00, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0xfc, 0xff, 0xff, 0xea,
+ 0x1e, 0x00, 0x2d, 0xe9, 0x01, 0x10, 0xa0, 0xe3, 0x00, 0x20, 0xa0, 0xe1,
+ 0x22, 0x01, 0x51, 0xe1, 0x81, 0x10, 0xa0, 0x91, 0xa2, 0x20, 0xa0, 0x91,
+ 0xfb, 0xff, 0xff, 0x9a, 0xa1, 0x20, 0xa0, 0xe1, 0x02, 0x30, 0x81, 0xe0,
+ 0x93, 0x03, 0x04, 0xe0, 0x00, 0x00, 0x54, 0xe1, 0x02, 0x10, 0x81, 0x90,
+ 0xa2, 0x20, 0xb0, 0x11, 0xf9, 0xff, 0xff, 0x1a, 0x01, 0x00, 0xa0, 0xe1,
+ 0x1e, 0x00, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x00, 0x00, 0x40, 0x00,
+ 0x40, 0x00, 0x80, 0x00, 0x80, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0x00, 0x01,
+ 0x34, 0x40, 0x2d, 0xe9, 0x00, 0x20, 0xa0, 0xe1, 0x01, 0x30, 0xa0, 0xe1,
+ 0x00, 0x40, 0xa0, 0xe3, 0x00, 0x00, 0x51, 0xe3, 0x08, 0x40, 0x84, 0xb2,
+ 0x00, 0x30, 0x63, 0xb2, 0x40, 0x50, 0x31, 0xe0, 0x04, 0x40, 0x84, 0x42,
+ 0x00, 0x20, 0x62, 0x22, 0x03, 0x20, 0x52, 0xe0, 0x01, 0x30, 0xa0, 0x51,
+ 0x00, 0x10, 0xa0, 0x51, 0x03, 0x00, 0xa0, 0x51, 0x02, 0x50, 0x35, 0xe0,
+ 0x02, 0x40, 0x84, 0x42, 0x48, 0x38, 0x9f, 0xe5, 0xb4, 0x40, 0x93, 0xe1,
+ 0x00, 0x07, 0xa0, 0xe1, 0xbd, 0xff, 0xff, 0xeb, 0x3c, 0xe8, 0x9f, 0xe5,
+ 0x90, 0x00, 0x01, 0xe0, 0x41, 0x17, 0xa0, 0xe1, 0x00, 0x10, 0x61, 0xe2,
+ 0xa9, 0x30, 0xa0, 0xe3, 0x91, 0x03, 0x03, 0xe0, 0x43, 0x37, 0xa0, 0xe1,
+ 0x39, 0x3e, 0x83, 0xe2, 0x91, 0x03, 0x03, 0xe0, 0x43, 0x37, 0xa0, 0xe1,
+ 0x09, 0x3c, 0x83, 0xe2, 0x1c, 0x30, 0x83, 0xe2, 0x91, 0x03, 0x03, 0xe0,
+ 0x43, 0x37, 0xa0, 0xe1, 0x0f, 0x3c, 0x83, 0xe2, 0xb6, 0x30, 0x83, 0xe2,
+ 0x91, 0x03, 0x03, 0xe0, 0x43, 0x37, 0xa0, 0xe1, 0x16, 0x3c, 0x83, 0xe2,
+ 0xaa, 0x30, 0x83, 0xe2, 0x91, 0x03, 0x03, 0xe0, 0x43, 0x37, 0xa0, 0xe1,
+ 0x02, 0x3a, 0x83, 0xe2, 0x81, 0x30, 0x83, 0xe2, 0x91, 0x03, 0x03, 0xe0,
+ 0x43, 0x37, 0xa0, 0xe1, 0x36, 0x3c, 0x83, 0xe2, 0x51, 0x30, 0x83, 0xe2,
+ 0x91, 0x03, 0x03, 0xe0, 0x43, 0x37, 0xa0, 0xe1, 0xa2, 0x3c, 0x83, 0xe2,
+ 0xf9, 0x30, 0x83, 0xe2, 0x93, 0x00, 0x00, 0xe0, 0x40, 0x08, 0xa0, 0xe1,
+ 0x1e, 0xff, 0x2f, 0xe1, 0x42, 0x00, 0x30, 0xe0, 0x00, 0x00, 0xa0, 0xe2,
+ 0x04, 0x04, 0x80, 0xe0, 0x17, 0x3e, 0xa0, 0xe3, 0x34, 0x40, 0xbd, 0xe8,
+ 0x1e, 0xff, 0x2f, 0xe1, 0x00, 0x00, 0x92, 0x01, 0x23, 0x03, 0xb5, 0x04,
+ 0x45, 0x06, 0xd5, 0x07, 0x64, 0x09, 0xf1, 0x0a, 0x7c, 0x0c, 0x05, 0x0e,
+ 0x8c, 0x0f, 0x11, 0x11, 0x94, 0x12, 0x13, 0x14, 0x8f, 0x15, 0x08, 0x17,
+ 0x7d, 0x18, 0xef, 0x19, 0x5d, 0x1b, 0xc6, 0x1c, 0x2b, 0x1e, 0x8b, 0x1f,
+ 0xe7, 0x20, 0x3d, 0x22, 0x8e, 0x23, 0xda, 0x24, 0x1f, 0x26, 0x5f, 0x27,
+ 0x99, 0x28, 0xcd, 0x29, 0xfa, 0x2a, 0x21, 0x2c, 0x41, 0x2d, 0x5a, 0x2e,
+ 0x6b, 0x2f, 0x76, 0x30, 0x79, 0x31, 0x74, 0x32, 0x67, 0x33, 0x53, 0x34,
+ 0x36, 0x35, 0x12, 0x36, 0xe5, 0x36, 0xaf, 0x37, 0x71, 0x38, 0x2a, 0x39,
+ 0xda, 0x39, 0x82, 0x3a, 0x20, 0x3b, 0xb6, 0x3b, 0x42, 0x3c, 0xc5, 0x3c,
+ 0x3e, 0x3d, 0xae, 0x3d, 0x14, 0x3e, 0x71, 0x3e, 0xc5, 0x3e, 0x0e, 0x3f,
+ 0x4e, 0x3f, 0x84, 0x3f, 0xb1, 0x3f, 0xd3, 0x3f, 0xec, 0x3f, 0xfb, 0x3f,
+ 0x00, 0x40, 0xfb, 0x3f, 0xec, 0x3f, 0xd3, 0x3f, 0xb1, 0x3f, 0x84, 0x3f,
+ 0x4e, 0x3f, 0x0e, 0x3f, 0xc5, 0x3e, 0x71, 0x3e, 0x14, 0x3e, 0xae, 0x3d,
+ 0x3e, 0x3d, 0xc5, 0x3c, 0x42, 0x3c, 0xb6, 0x3b, 0x20, 0x3b, 0x82, 0x3a,
+ 0xda, 0x39, 0x2a, 0x39, 0x71, 0x38, 0xaf, 0x37, 0xe5, 0x36, 0x12, 0x36,
+ 0x36, 0x35, 0x53, 0x34, 0x67, 0x33, 0x74, 0x32, 0x79, 0x31, 0x76, 0x30,
+ 0x6b, 0x2f, 0x5a, 0x2e, 0x41, 0x2d, 0x21, 0x2c, 0xfa, 0x2a, 0xcd, 0x29,
+ 0x99, 0x28, 0x5f, 0x27, 0x1f, 0x26, 0xda, 0x24, 0x8e, 0x23, 0x3d, 0x22,
+ 0xe7, 0x20, 0x8b, 0x1f, 0x2b, 0x1e, 0xc6, 0x1c, 0x5d, 0x1b, 0xef, 0x19,
+ 0x7d, 0x18, 0x08, 0x17, 0x8f, 0x15, 0x13, 0x14, 0x94, 0x12, 0x11, 0x11,
+ 0x8c, 0x0f, 0x05, 0x0e, 0x7c, 0x0c, 0xf1, 0x0a, 0x64, 0x09, 0xd5, 0x07,
+ 0x45, 0x06, 0xb5, 0x04, 0x23, 0x03, 0x92, 0x01, 0x00, 0x00, 0x6e, 0xfe,
+ 0xdd, 0xfc, 0x4b, 0xfb, 0xbb, 0xf9, 0x2b, 0xf8, 0x9c, 0xf6, 0x0f, 0xf5,
+ 0x84, 0xf3, 0xfb, 0xf1, 0x74, 0xf0, 0xef, 0xee, 0x6c, 0xed, 0xed, 0xeb,
+ 0x71, 0xea, 0xf8, 0xe8, 0x83, 0xe7, 0x11, 0xe6, 0xa3, 0xe4, 0x3a, 0xe3,
+ 0xd5, 0xe1, 0x75, 0xe0, 0x19, 0xdf, 0xc3, 0xdd, 0x72, 0xdc, 0x26, 0xdb,
+ 0xe1, 0xd9, 0xa1, 0xd8, 0x67, 0xd7, 0x33, 0xd6, 0x06, 0xd5, 0xdf, 0xd3,
+ 0xbf, 0xd2, 0xa6, 0xd1, 0x95, 0xd0, 0x8a, 0xcf, 0x87, 0xce, 0x8c, 0xcd,
+ 0x99, 0xcc, 0xad, 0xcb, 0xca, 0xca, 0xee, 0xc9, 0x1b, 0xc9, 0x51, 0xc8,
+ 0x8f, 0xc7, 0xd6, 0xc6, 0x26, 0xc6, 0x7e, 0xc5, 0xe0, 0xc4, 0x4a, 0xc4,
+ 0xbe, 0xc3, 0x3b, 0xc3, 0xc2, 0xc2, 0x52, 0xc2, 0xec, 0xc1, 0x8f, 0xc1,
+ 0x3b, 0xc1, 0xf2, 0xc0, 0xb2, 0xc0, 0x7c, 0xc0, 0x4f, 0xc0, 0x2d, 0xc0,
+ 0x14, 0xc0, 0x05, 0xc0, 0x00, 0xc0, 0x05, 0xc0, 0x14, 0xc0, 0x2d, 0xc0,
+ 0x4f, 0xc0, 0x7c, 0xc0, 0xb2, 0xc0, 0xf2, 0xc0, 0x3b, 0xc1, 0x8f, 0xc1,
+ 0xec, 0xc1, 0x52, 0xc2, 0xc2, 0xc2, 0x3b, 0xc3, 0xbe, 0xc3, 0x4a, 0xc4,
+ 0xe0, 0xc4, 0x7e, 0xc5, 0x26, 0xc6, 0xd6, 0xc6, 0x8f, 0xc7, 0x51, 0xc8,
+ 0x1b, 0xc9, 0xee, 0xc9, 0xca, 0xca, 0xad, 0xcb, 0x99, 0xcc, 0x8c, 0xcd,
+ 0x87, 0xce, 0x8a, 0xcf, 0x95, 0xd0, 0xa6, 0xd1, 0xbf, 0xd2, 0xdf, 0xd3,
+ 0x06, 0xd5, 0x33, 0xd6, 0x67, 0xd7, 0xa1, 0xd8, 0xe1, 0xd9, 0x26, 0xdb,
+ 0x72, 0xdc, 0xc3, 0xdd, 0x19, 0xdf, 0x75, 0xe0, 0xd5, 0xe1, 0x3a, 0xe3,
+ 0xa3, 0xe4, 0x11, 0xe6, 0x83, 0xe7, 0xf8, 0xe8, 0x71, 0xea, 0xed, 0xeb,
+ 0x6c, 0xed, 0xef, 0xee, 0x74, 0xf0, 0xfb, 0xf1, 0x84, 0xf3, 0x0f, 0xf5,
+ 0x9c, 0xf6, 0x2b, 0xf8, 0xbb, 0xf9, 0x4b, 0xfb, 0xdd, 0xfc, 0x6e, 0xfe,
+ 0xf0, 0x1b, 0x2d, 0xe9, 0x98, 0xc5, 0x9f, 0xe5, 0x00, 0xb0, 0xa0, 0xe3,
+ 0x01, 0x20, 0x52, 0xe2, 0x16, 0x00, 0x00, 0xba, 0xf2, 0x40, 0xd0, 0xe0,
+ 0xf2, 0x60, 0xd0, 0xe0, 0xb4, 0x70, 0xd0, 0xe0, 0x27, 0x74, 0xa0, 0xe1,
+ 0x87, 0x80, 0xa0, 0xe1, 0x40, 0x90, 0x87, 0xe2, 0xff, 0x90, 0x09, 0xe2,
+ 0x89, 0x90, 0xa0, 0xe1, 0xf8, 0x80, 0x9c, 0xe1, 0xf9, 0x90, 0x9c, 0xe1,
+ 0x94, 0x08, 0x05, 0xe0, 0x45, 0x57, 0x4b, 0xe0, 0x99, 0x04, 0x04, 0xe0,
+ 0x44, 0x47, 0xa0, 0xe1, 0x96, 0x09, 0x07, 0xe0, 0x47, 0x77, 0xa0, 0xe1,
+ 0x98, 0x06, 0x06, 0xe0, 0x46, 0x67, 0xa0, 0xe1, 0xb3, 0x40, 0x81, 0xe0,
+ 0xb3, 0x50, 0x81, 0xe0, 0xb3, 0x60, 0x81, 0xe0, 0xb3, 0x70, 0x81, 0xe0,
+ 0xe6, 0xff, 0xff, 0xea, 0xf0, 0x1b, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1,
+ 0xf8, 0x1f, 0x2d, 0xe9, 0x20, 0xc5, 0x9f, 0xe5, 0x01, 0x20, 0x52, 0xe2,
+ 0x22, 0x00, 0x00, 0xba, 0x00, 0x0e, 0xb0, 0xe8, 0xf2, 0x30, 0xd0, 0xe0,
+ 0xf2, 0x50, 0xd0, 0xe0, 0xb4, 0x60, 0xd0, 0xe0, 0x26, 0x64, 0xa0, 0xe1,
+ 0x86, 0x70, 0xa0, 0xe1, 0x40, 0x80, 0x86, 0xe2, 0xff, 0x80, 0x08, 0xe2,
+ 0x88, 0x80, 0xa0, 0xe1, 0xf7, 0x70, 0x9c, 0xe1, 0xf8, 0x80, 0x9c, 0xe1,
+ 0x93, 0x07, 0x04, 0xe0, 0x44, 0x47, 0xa0, 0xe1, 0x98, 0x03, 0x03, 0xe0,
+ 0x43, 0x37, 0xa0, 0xe1, 0x95, 0x08, 0x06, 0xe0, 0x46, 0x67, 0xa0, 0xe1,
+ 0x97, 0x05, 0x05, 0xe0, 0x45, 0x57, 0xa0, 0xe1, 0x0b, 0x78, 0xa0, 0xe1,
+ 0x47, 0x78, 0xa0, 0xe1, 0x00, 0x70, 0x67, 0xe2, 0x4b, 0x88, 0xa0, 0xe1,
+ 0x97, 0x93, 0x29, 0xe0, 0x98, 0x94, 0x29, 0xe0, 0x97, 0xa5, 0x2a, 0xe0,
+ 0x00, 0x80, 0x68, 0xe2, 0x98, 0xa6, 0x2a, 0xe0, 0xb2, 0x30, 0xc1, 0xe0,
+ 0x00, 0x40, 0x64, 0xe2, 0xb2, 0x40, 0xc1, 0xe0, 0xb2, 0x50, 0xc1, 0xe0,
+ 0xb2, 0x60, 0xc1, 0xe0, 0x00, 0x06, 0xa1, 0xe8, 0xda, 0xff, 0xff, 0xea,
+ 0xf8, 0x1f, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x00, 0xb0, 0xa0, 0xe3,
+ 0x01, 0xc3, 0xa0, 0xe3, 0x01, 0xb3, 0xcc, 0xe5, 0x1e, 0xff, 0x2f, 0xe1,
+ 0x80, 0xb0, 0xa0, 0xe3, 0x01, 0xc3, 0xa0, 0xe3, 0x01, 0xb3, 0xcc, 0xe5,
+ 0x1e, 0xff, 0x2f, 0xe1, 0x01, 0xc3, 0xa0, 0xe3, 0x01, 0x23, 0xcc, 0xe5,
+ 0x1e, 0xff, 0x2f, 0xe1, 0x01, 0x00, 0xa0, 0xe3, 0x01, 0x10, 0xa0, 0xe3,
+ 0x10, 0x40, 0x2d, 0xe9, 0x00, 0x20, 0xa0, 0xe3, 0x01, 0x30, 0xa0, 0xe3,
+ 0x01, 0xc3, 0xa0, 0xe3, 0x00, 0x00, 0x50, 0xe3, 0x01, 0x00, 0x00, 0x1b,
+ 0x1c, 0xe0, 0x8f, 0xe2, 0x01, 0x23, 0xcc, 0xe5, 0x08, 0x22, 0xcc, 0xe5,
+ 0xb8, 0x40, 0x5c, 0xe1, 0x04, 0x00, 0x11, 0xe0, 0x00, 0x40, 0x24, 0x10,
+ 0xb8, 0x40, 0x4c, 0x11, 0x08, 0x32, 0xcc, 0xe5, 0x1e, 0xff, 0x2f, 0xe1,
+ 0xf6, 0xff, 0xff, 0x0a, 0x10, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07,
+ 0xff, 0xff, 0x80, 0x1f, 0x00, 0x01, 0x00, 0x60, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0xa0, 0xe1, 0x1e, 0x58, 0x2d, 0xe9, 0x00, 0x30, 0xa0, 0xe1,
+ 0xd8, 0x03, 0x9f, 0xe5, 0x80, 0x00, 0x13, 0xe3, 0x2c, 0x00, 0x00, 0x1a,
+ 0x40, 0x00, 0x13, 0xe3, 0x19, 0x00, 0x00, 0x1a, 0x20, 0x00, 0x13, 0xe3,
+ 0x0c, 0x00, 0x00, 0x1a, 0x1f, 0x30, 0x03, 0xe2, 0x00, 0x40, 0xa0, 0xe3,
+ 0xb8, 0xb3, 0x9f, 0xe5, 0xb8, 0xc3, 0x9f, 0xe5, 0xb8, 0xe3, 0x9f, 0xe5,
+ 0x02, 0x40, 0x84, 0xe2, 0xa3, 0x30, 0xb0, 0xe1, 0x32, 0x00, 0x00, 0x0a,
+ 0xfb, 0xff, 0xff, 0x3a, 0x84, 0x10, 0x9b, 0xe7, 0xb4, 0x20, 0x9c, 0xe1,
+ 0x01, 0x24, 0x82, 0xe3, 0x72, 0xfd, 0xff, 0xea, 0x98, 0x13, 0x9f, 0xe5,
+ 0x20, 0x20, 0xa0, 0xe3, 0x01, 0x24, 0x82, 0xe3, 0x6e, 0xfd, 0xff, 0xeb,
+ 0x01, 0x13, 0xa0, 0xe3, 0x01, 0x1c, 0x81, 0xe2, 0x02, 0x29, 0xa0, 0xe3,
+ 0xb4, 0x23, 0xc1, 0xe1, 0x07, 0x20, 0xa0, 0xe3, 0xb0, 0x24, 0xc1, 0xe1,
+ 0xe7, 0xff, 0xff, 0xea, 0x70, 0x13, 0x9f, 0xe5, 0x08, 0x20, 0xa0, 0xe3,
+ 0x01, 0x24, 0x82, 0xe3, 0x63, 0xfd, 0xff, 0xeb, 0x01, 0x13, 0xa0, 0xe3,
+ 0x80, 0x20, 0xa0, 0xe3, 0xb0, 0x18, 0xc1, 0xe1, 0xb2, 0x18, 0xc1, 0xe1,
+ 0xb4, 0x18, 0xc1, 0xe1, 0xb4, 0x28, 0xc1, 0xe1, 0xb8, 0x28, 0xd1, 0xe1,
+ 0x3f, 0x2b, 0xc2, 0xe3, 0xb8, 0x28, 0xc1, 0xe1, 0x70, 0x20, 0xa0, 0xe3,
+ 0xb0, 0x27, 0xc1, 0xe1, 0xb4, 0x18, 0xc1, 0xe1, 0xd4, 0xff, 0xff, 0xea,
+ 0x01, 0x13, 0xa0, 0xe3, 0x01, 0x24, 0xa0, 0xe3, 0x18, 0x20, 0x82, 0xe2,
+ 0x52, 0xfd, 0xff, 0xeb, 0x50, 0x10, 0x81, 0xe2, 0x50, 0xfd, 0xff, 0xeb,
+ 0xf0, 0x10, 0x81, 0xe2, 0x00, 0x40, 0xa0, 0xe3, 0x04, 0x40, 0x81, 0xe4,
+ 0x04, 0x40, 0x81, 0xe4, 0x00, 0x40, 0x81, 0xe5, 0x82, 0x1f, 0x41, 0xe2,
+ 0x01, 0x4c, 0xa0, 0xe3, 0xb0, 0x42, 0xc1, 0xe1, 0xb6, 0x42, 0xc1, 0xe1,
+ 0xb0, 0x43, 0xc1, 0xe1, 0xb6, 0x43, 0xc1, 0xe1, 0xc0, 0xff, 0xff, 0xea,
+ 0x1e, 0x58, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x00, 0x00, 0xa0, 0xe1,
+ 0x00, 0x00, 0xa0, 0xe1, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xe9, 0xea, 0xeb, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xdb, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xcb, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xba, 0xbb, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xab, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9a, 0x9b, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8a, 0x8b, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x7b, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x00, 0x00, 0x00, 0x80, 0x97, 0x7c, 0x9c, 0x87,
+ 0x1e, 0xd6, 0xac, 0x8f, 0x52, 0xf0, 0x37, 0x98, 0xcc, 0x17, 0x45, 0xa1,
+ 0x48, 0x08, 0xdc, 0xaa, 0x34, 0xf3, 0x04, 0xb5, 0xbb, 0x86, 0xc8, 0xbf,
+ 0x2a, 0xf5, 0x2f, 0xcb, 0xcb, 0xfc, 0x44, 0xd7, 0x3a, 0xf0, 0x11, 0xe4,
+ 0x39, 0xbf, 0xa1, 0xf1, 0x70, 0x00, 0x2d, 0xe9, 0x02, 0x2c, 0xa0, 0xe1,
+ 0xb2, 0x00, 0x51, 0xe3, 0x11, 0x00, 0x00, 0xca, 0xe4, 0x31, 0x9f, 0xe5,
+ 0xb4, 0x40, 0x83, 0xe2, 0x01, 0x50, 0xf3, 0xe7, 0x0f, 0x60, 0x05, 0xe2,
+ 0x06, 0x61, 0x94, 0xe7, 0x25, 0x52, 0xa0, 0xe1, 0x36, 0x65, 0xa0, 0xe1,
+ 0x01, 0x30, 0xd3, 0xe5, 0x0f, 0x10, 0x03, 0xe2, 0x01, 0x41, 0x94, 0xe7,
+ 0x23, 0x32, 0xa0, 0xe1, 0x34, 0x43, 0x66, 0xe0, 0x94, 0x32, 0x81, 0xe0,
+ 0x06, 0x10, 0x81, 0xe0, 0x04, 0x20, 0x90, 0xe5, 0x92, 0x31, 0x80, 0xe0,
+ 0x70, 0x00, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0xff, 0x24, 0xa0, 0xe3,
+ 0xb2, 0x10, 0xa0, 0xe3, 0xea, 0xff, 0xff, 0xea, 0x94, 0x21, 0x9f, 0xe5,
+ 0x23, 0x30, 0xa0, 0xe3, 0x03, 0x20, 0x80, 0xe7, 0x01, 0x30, 0x53, 0xe2,
+ 0xfc, 0xff, 0xff, 0xaa, 0x1e, 0xff, 0x2f, 0xe1, 0x28, 0x02, 0x00, 0x00,
+ 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00,
+ 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00,
+ 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00,
+ 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00,
+ 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00,
+ 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00,
+ 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00,
+ 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00,
+ 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00,
+ 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00,
+ 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00,
+ 0x28, 0x02, 0x00, 0x00, 0x10, 0x40, 0x2d, 0xe9, 0x00, 0x40, 0xa0, 0xe1,
+ 0x01, 0x13, 0xa0, 0xe3, 0xb6, 0x1c, 0xc1, 0xe1, 0xb2, 0x1d, 0xc1, 0xe1,
+ 0x8f, 0x20, 0xa0, 0xe3, 0xb4, 0x28, 0xc1, 0xe1, 0xb8, 0x2d, 0xdf, 0xe1,
+ 0xb2, 0x28, 0xc1, 0xe1, 0x89, 0x20, 0xd1, 0xe5, 0x40, 0x20, 0x82, 0xe3,
+ 0x89, 0x20, 0xc1, 0xe5, 0x35, 0x2e, 0x80, 0xe2, 0xbc, 0x20, 0x81, 0xe5,
+ 0xa0, 0x20, 0x81, 0xe2, 0xc0, 0x20, 0x81, 0xe5, 0x26, 0x2d, 0x80, 0xe2,
+ 0xc8, 0x20, 0x81, 0xe5, 0xa4, 0x20, 0x81, 0xe2, 0xcc, 0x20, 0x81, 0xe5,
+ 0xa8, 0x20, 0x9f, 0xe5, 0x00, 0x00, 0x82, 0xe5, 0x00, 0x00, 0xa0, 0xe3,
+ 0x01, 0x00, 0x2d, 0xe9, 0x04, 0x00, 0x4d, 0xe2, 0x04, 0x10, 0xa0, 0xe1,
+ 0xfb, 0x2f, 0xa0, 0xe3, 0x01, 0x24, 0x82, 0xe3, 0x84, 0xfc, 0xff, 0xeb,
+ 0x01, 0x00, 0xbd, 0xe8, 0x08, 0x00, 0xa0, 0xe3, 0x06, 0x00, 0xc4, 0xe5,
+ 0x0f, 0x00, 0xa0, 0xe3, 0x07, 0x00, 0xc4, 0xe5, 0xb4, 0x07, 0xdf, 0xe1,
+ 0x38, 0x00, 0x84, 0xe5, 0x70, 0x00, 0x9f, 0xe5, 0x28, 0x00, 0x84, 0xe5,
+ 0x2c, 0x00, 0x84, 0xe5, 0x30, 0x00, 0x84, 0xe5, 0x3c, 0x00, 0x84, 0xe5,
+ 0x60, 0x00, 0x9f, 0xe5, 0x34, 0x00, 0x84, 0xe5, 0x5c, 0x00, 0x9f, 0xe5,
+ 0x00, 0x00, 0x84, 0xe5, 0x10, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1,
+ 0xe0, 0x7f, 0x00, 0x03, 0xa0, 0x7f, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x03,
+ 0x00, 0xfe, 0xff, 0xff, 0x7f, 0x18, 0xae, 0xba, 0xdc, 0x07, 0x00, 0x00,
+ 0xc8, 0x08, 0x00, 0x00, 0xe0, 0x08, 0x00, 0x00, 0x78, 0x0c, 0x00, 0x00,
+ 0x78, 0x0c, 0x00, 0x00, 0x8e, 0x0c, 0x00, 0x00, 0xd8, 0x0c, 0x00, 0x00,
+ 0x20, 0x01, 0x00, 0x04, 0x60, 0x00, 0x00, 0x04, 0xc0, 0x0d, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0e, 0xa9, 0x00, 0x00, 0xf0, 0x7f, 0x00, 0x03,
+ 0x25, 0x24, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x20, 0x0f, 0x00, 0x00,
+ 0x53, 0x6d, 0x73, 0x68, 0xb6, 0x1c, 0xa0, 0xe3, 0x01, 0x03, 0xa0, 0xe3,
+ 0xb6, 0x1c, 0xc0, 0xe1, 0xb2, 0x1d, 0xc0, 0xe1, 0x1e, 0xff, 0x2f, 0xe1,
+ 0x10, 0x40, 0x2d, 0xe9, 0xb0, 0x40, 0x9f, 0xe5, 0x00, 0x40, 0x94, 0xe5,
+ 0x00, 0x10, 0x94, 0xe5, 0xa8, 0x00, 0x9f, 0xe5, 0x01, 0x00, 0x40, 0xe0,
+ 0x01, 0x00, 0x50, 0xe3, 0x10, 0x00, 0x00, 0x8a, 0x01, 0x10, 0x81, 0xe2,
+ 0x00, 0x10, 0x84, 0xe5, 0x01, 0x13, 0xa0, 0xe3, 0xb6, 0x1c, 0xc1, 0xe1,
+ 0xb2, 0x1d, 0xc1, 0xe1, 0x04, 0x10, 0xc4, 0xe5, 0x00, 0x00, 0xa0, 0xe3,
+ 0x01, 0x00, 0x2d, 0xe9, 0x04, 0x00, 0x4d, 0xe2, 0x35, 0x1e, 0x84, 0xe2,
+ 0xc6, 0x2f, 0xa0, 0xe3, 0x01, 0x24, 0x82, 0xe3, 0x42, 0xfc, 0xff, 0xeb,
+ 0x01, 0x00, 0xbd, 0xe8, 0x00, 0x10, 0x94, 0xe5, 0x01, 0x10, 0x41, 0xe2,
+ 0x00, 0x10, 0x84, 0xe5, 0x10, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1,
+ 0x48, 0x00, 0x9f, 0xe5, 0x00, 0x00, 0x90, 0xe5, 0x00, 0x10, 0x90, 0xe5,
+ 0x44, 0x20, 0x9f, 0xe5, 0x02, 0x00, 0x51, 0xe1, 0x0c, 0x00, 0x00, 0x1a,
+ 0x04, 0x10, 0xd0, 0xe5, 0x01, 0x10, 0x51, 0xe2, 0x04, 0x10, 0xc0, 0xe5,
+ 0x08, 0x00, 0x00, 0xca, 0x0b, 0x10, 0xd0, 0xe5, 0x04, 0x10, 0xc0, 0xe5,
+ 0x00, 0x00, 0xa0, 0xe3, 0xb6, 0x1c, 0xa0, 0xe3, 0x01, 0x23, 0xa0, 0xe3,
+ 0xb6, 0x0c, 0xc2, 0xe1, 0xb2, 0x0d, 0xc2, 0xe1, 0xb6, 0x1c, 0xc2, 0xe1,
+ 0xb2, 0x1d, 0xc2, 0xe1, 0x1e, 0xff, 0x2f, 0xe1, 0xf0, 0x7f, 0x00, 0x03,
+ 0x54, 0x6d, 0x73, 0x68, 0x53, 0x6d, 0x73, 0x68, 0x00, 0x00, 0xa0, 0xe1,
+ 0x30, 0x00, 0x30, 0x00, 0xff, 0x00, 0x9e, 0x00, 0x01, 0xf8, 0xf8, 0x80,
+ 0xfc, 0x80, 0x00, 0x82, 0x01, 0x8d, 0x00, 0x83, 0xfc, 0x03, 0x7c, 0x7c,
+ 0x01, 0x01, 0x93, 0x00, 0x02, 0x7c, 0x3c, 0x3e, 0x81, 0x7f, 0x00, 0x3e,
+ 0xb8, 0x00, 0x01, 0xff, 0xff, 0x80, 0x7f, 0x80, 0x00, 0x01, 0xff, 0xff,
+ 0x80, 0x7f, 0x8d, 0x00, 0x80, 0x3f, 0x82, 0x00, 0x80, 0x3f, 0xd3, 0x00,
+ 0x01, 0xc0, 0xc0, 0x81, 0xe0, 0x01, 0xff, 0x00, 0x82, 0xc3, 0x04, 0xe1,
+ 0xff, 0x00, 0x07, 0x07, 0x81, 0x03, 0x00, 0x0f, 0x85, 0x00, 0x05, 0xff,
+ 0xff, 0xf0, 0xf0, 0xf8, 0xf8, 0x81, 0xff, 0x81, 0xf0, 0x09, 0xff, 0xff,
+ 0x0f, 0x0f, 0x01, 0x01, 0x00, 0x00, 0x0f, 0x0f, 0x85, 0x00, 0x02, 0xff,
+ 0x7c, 0x7c, 0x80, 0x3c, 0x04, 0x3e, 0x3e, 0xff, 0x78, 0x78, 0x80, 0x7c,
+ 0x02, 0x3c, 0x3c, 0x0f, 0x93, 0x00, 0x00, 0xf0, 0x84, 0x00, 0x00, 0x3f,
+ 0x8d, 0x00, 0x07, 0xf8, 0xfc, 0xfc, 0x7e, 0x7e, 0xfe, 0xfe, 0xfc, 0x80,
+ 0xff, 0x04, 0xe7, 0x87, 0x07, 0x07, 0x0f, 0x8d, 0x00, 0x02, 0xfc, 0xf8,
+ 0xe0, 0x80, 0xc0, 0x04, 0xc3, 0xcf, 0x1f, 0x3f, 0x7f, 0x82, 0xff, 0x8d,
+ 0x00, 0x80, 0xff, 0x00, 0xfe, 0x81, 0xe0, 0x03, 0xff, 0x7f, 0x3f, 0x1f,
+ 0x80, 0x01, 0xb1, 0x00, 0x00, 0xf8, 0x80, 0xfe, 0x00, 0x1f, 0x80, 0x00,
+ 0x04, 0x03, 0x07, 0x0f, 0x8f, 0xcf, 0x80, 0x00, 0x04, 0x3e, 0x1f, 0x0f,
+ 0x0f, 0x07, 0x85, 0x00, 0x80, 0xfe, 0x00, 0xf8, 0x81, 0x00, 0x0a, 0xef,
+ 0xef, 0xf7, 0xfb, 0x78, 0x7c, 0xfe, 0xff, 0x03, 0x03, 0x01, 0x80, 0x00,
+ 0x01, 0x3f, 0x7f, 0x86, 0x00, 0x16, 0x80, 0xc0, 0xe0, 0xe0, 0xf0, 0xf8,
+ 0x7c, 0xef, 0xff, 0xf7, 0xf3, 0xf1, 0xe1, 0xe0, 0xc0, 0x7f, 0xff, 0xf9,
+ 0xf9, 0xff, 0x7f, 0x7f, 0x3f, 0xb0, 0x00, 0x04, 0xf0, 0xfc, 0xfe, 0xfe,
+ 0x7f, 0x80, 0x00, 0x04, 0x1f, 0x3f, 0x3f, 0x7e, 0x7e, 0x87, 0x00, 0x07,
+ 0x80, 0xf0, 0xfc, 0xfe, 0xfe, 0x7f, 0x3f, 0xbf, 0x81, 0xff, 0x09, 0xfe,
+ 0xfe, 0x7e, 0x3f, 0x3f, 0x1f, 0x07, 0x00, 0x7e, 0x7f, 0x85, 0x00, 0x07,
+ 0x3f, 0x3f, 0x7f, 0xff, 0xfe, 0xfe, 0xfc, 0xf0, 0x80, 0xfc, 0x81, 0xff,
+ 0x05, 0xbf, 0x3f, 0x3f, 0x1f, 0xff, 0xff, 0x80, 0x7f, 0xa8, 0x00, 0x04,
+ 0xfe, 0xfe, 0xff, 0xff, 0x7f, 0x95, 0x00, 0x02, 0x7f, 0x3f, 0x3f, 0xbe,
+ 0x00, 0x03, 0xc0, 0xe0, 0xf0, 0xf8, 0x81, 0x00, 0x03, 0x07, 0x07, 0x03,
+ 0x01, 0x8d, 0x00, 0x04, 0xf8, 0xfc, 0xfc, 0x7e, 0x7e, 0x80, 0x3f, 0x00,
+ 0x01, 0x94, 0x00, 0x01, 0x3f, 0x3f, 0x83, 0x1f, 0x95, 0x00, 0x07, 0x1f,
+ 0x3f, 0x3f, 0x3e, 0x3e, 0x7e, 0x7c, 0x7c, 0x99, 0x00, 0x03, 0xf0, 0xf0,
+ 0xe0, 0xe0, 0x81, 0x00, 0x03, 0x01, 0x01, 0x03, 0x03, 0x8d, 0x00, 0x00,
+ 0xe0, 0x84, 0xc0, 0x80, 0x07, 0x82, 0x0f, 0x8d, 0x00, 0x81, 0xc0, 0x80,
+ 0xe0, 0x00, 0xf0, 0x82, 0x0f, 0x80, 0x07, 0x8d, 0x00, 0x0a, 0xf0, 0xf0,
+ 0xf8, 0xf8, 0xfc, 0x7e, 0x3e, 0x1f, 0x03, 0x03, 0x01, 0xb5, 0x00, 0x80,
+ 0xe0, 0x01, 0xc6, 0xcf, 0x80, 0x00, 0x04, 0x07, 0x07, 0x03, 0x63, 0xfb,
+ 0x8d, 0x00, 0x80, 0xff, 0x0c, 0xc0, 0xf0, 0xf8, 0x7e, 0x7e, 0xfd, 0xff,
+ 0xff, 0x03, 0x07, 0x1f, 0x3f, 0x3e, 0x8d, 0x00, 0x01, 0x7c, 0x38, 0x83,
+ 0x00, 0x01, 0x3e, 0x1e, 0xb8, 0x00, 0x80, 0x80, 0x82, 0x00, 0x80, 0x0f,
+ 0x8d, 0x00, 0x80, 0x80, 0x82, 0xff, 0x80, 0x0f, 0x82, 0xff, 0x80, 0x00,
+ 0x82, 0x03, 0x85, 0x00, 0x83, 0x80, 0x01, 0x00, 0x00, 0x83, 0x0f, 0xd4,
+ 0x00, 0x02, 0xf8, 0xf8, 0xfc, 0x82, 0x00, 0x02, 0x01, 0x03, 0x03, 0x8d,
+ 0x00, 0x09, 0xfc, 0xfc, 0xf8, 0xf0, 0xf0, 0xfc, 0xff, 0x7f, 0x07, 0x07,
+ 0x80, 0x03, 0x00, 0x01, 0xd5, 0x00, 0x01, 0xfe, 0xfe, 0x83, 0x00, 0x01,
+ 0x1f, 0x1f, 0x8d, 0x00, 0x02, 0xfe, 0xfe, 0xff, 0x82, 0x00, 0x80, 0x0f,
+ 0xf4, 0x00, 0x05, 0x7e, 0xfe, 0xff, 0xff, 0xfe, 0x7e, 0xc0, 0x00, 0x04,
+ 0xc0, 0xe0, 0xe0, 0xf0, 0xf8, 0x80, 0x00, 0x04, 0x0f, 0x07, 0x07, 0x03,
+ 0x01, 0x8a, 0x00, 0x0b, 0x80, 0xc0, 0xc0, 0xfc, 0xfc, 0x7e, 0x3f, 0x3f,
+ 0x1f, 0x0f, 0x0f, 0x01, 0x8c, 0x00, 0x0b, 0xe0, 0xf0, 0xf8, 0xf8, 0xfc,
+ 0x7e, 0x7e, 0x3f, 0x07, 0x03, 0x01, 0x01, 0xb4, 0x00, 0x04, 0xc0, 0xf0,
+ 0xf8, 0xfc, 0xfc, 0x80, 0x00, 0x01, 0x3f, 0x7f, 0x80, 0xff, 0x83, 0x00,
+ 0x01, 0x01, 0x01, 0x85, 0x00, 0x02, 0xfe, 0xfe, 0x7e, 0x81, 0x7f, 0x01,
+ 0x3f, 0xfd, 0x83, 0xf8, 0x01, 0xfc, 0x01, 0x82, 0x03, 0x01, 0x01, 0x01,
+ 0x85, 0x00, 0x80, 0x7f, 0x0e, 0xff, 0xfe, 0xfe, 0xfc, 0xf8, 0xfc, 0xfc,
+ 0xfe, 0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x01, 0x01, 0xae, 0x00, 0x81, 0xf8,
+ 0x00, 0xfc, 0x80, 0x00, 0x00, 0x03, 0x81, 0x01, 0x8d, 0x00, 0x82, 0xfc,
+ 0x80, 0xfe, 0x80, 0x01, 0x92, 0x00, 0x00, 0xfe, 0x80, 0x7e, 0x80, 0x7f,
+ 0x00, 0x3f, 0xb8, 0x00, 0x04, 0xe0, 0xf0, 0xf0, 0xf8, 0xf8, 0x80, 0x00,
+ 0x01, 0x3f, 0x7f, 0x80, 0xff, 0x84, 0x00, 0x00, 0x01, 0x85, 0x00, 0x01,
+ 0x38, 0x18, 0x82, 0x00, 0x00, 0x80, 0x80, 0xfc, 0x04, 0xfe, 0xfe, 0x7f,
+ 0x7f, 0x3f, 0x80, 0x01, 0x8a, 0x00, 0x0a, 0xc0, 0xe0, 0xf0, 0xf0, 0xf8,
+ 0xfc, 0xfe, 0xff, 0x1f, 0x1f, 0x0f, 0x82, 0xff, 0x80, 0x00, 0x00, 0x01,
+ 0xac, 0x00, 0x04, 0xf0, 0xf8, 0xf8, 0xfc, 0xfc, 0x80, 0x00, 0x01, 0x3f,
+ 0x7f, 0x80, 0xff, 0x84, 0x00, 0x00, 0x01, 0x85, 0x00, 0x01, 0x1c, 0x00,
+ 0x83, 0xf0, 0x08, 0xfc, 0xfc, 0xff, 0xff, 0x3f, 0x3f, 0x7f, 0x7f, 0x01,
+ 0x8e, 0x00, 0x00, 0x0f, 0x81, 0xff, 0x02, 0xfe, 0x7f, 0x7e, 0x80, 0x7f,
+ 0x02, 0x3f, 0x1f, 0x0f, 0xb4, 0x00, 0x00, 0x80, 0x80, 0x00, 0x04, 0xfc,
+ 0xfc, 0xfe, 0xff, 0xff, 0x80, 0x00, 0x81, 0x01, 0x86, 0x00, 0x06, 0xc0,
+ 0xe0, 0xf0, 0xf0, 0xf8, 0xfc, 0x7e, 0x83, 0xff, 0x02, 0x7e, 0x7e, 0xff,
+ 0x84, 0x00, 0x00, 0x03, 0x85, 0x00, 0x81, 0xff, 0x81, 0x00, 0x81, 0xff,
+ 0x81, 0x3f, 0x80, 0x03, 0x00, 0x01, 0xac, 0x00, 0x82, 0xf0, 0x80, 0x00,
+ 0x82, 0xff, 0x80, 0x00, 0x00, 0x01, 0x89, 0x00, 0x83, 0xf8, 0x09, 0xdc,
+ 0x00, 0x01, 0x01, 0x0f, 0x3f, 0x3f, 0x7f, 0x7f, 0xff, 0x8f, 0x00, 0x01,
+ 0x0e, 0xfe, 0x80, 0xff, 0x08, 0xfc, 0xfe, 0xfe, 0xff, 0x7f, 0x7f, 0x3f,
+ 0x1f, 0x07, 0xb0, 0x00, 0x04, 0x80, 0xc0, 0xe0, 0xe0, 0xf0, 0x80, 0x00,
+ 0x04, 0x3f, 0x3f, 0x1f, 0x0f, 0x0f, 0x8d, 0x00, 0x0b, 0xf8, 0xfc, 0xfc,
+ 0xfe, 0xfe, 0xff, 0xff, 0x7f, 0x07, 0x03, 0x3f, 0x7f, 0x80, 0xff, 0x00,
+ 0xfc, 0x83, 0x00, 0x01, 0x01, 0x01, 0x85, 0x00, 0x80, 0x3f, 0x0e, 0xff,
+ 0xff, 0xfe, 0xfc, 0xf8, 0xfc, 0xfc, 0xfe, 0xff, 0xff, 0x7f, 0x3f, 0x0f,
+ 0x01, 0x01, 0xae, 0x00, 0x80, 0xfe, 0x01, 0xff, 0xff, 0x80, 0x00, 0x80,
+ 0xff, 0x01, 0x7f, 0x7f, 0x80, 0x00, 0x00, 0x01, 0x8a, 0x00, 0x03, 0x80,
+ 0x80, 0xc0, 0xc0, 0x80, 0xe0, 0x04, 0x7f, 0x3f, 0x3f, 0x1f, 0x1f, 0x80,
+ 0x0f, 0x8d, 0x00, 0x01, 0xf0, 0xf0, 0x80, 0xf8, 0x06, 0xfc, 0xfc, 0xfe,
+ 0x07, 0x07, 0x03, 0x03, 0x80, 0x01, 0xb1, 0x00, 0x04, 0xc0, 0xe0, 0xf0,
+ 0xf8, 0xf8, 0x80, 0x00, 0x82, 0xff, 0x81, 0x00, 0x00, 0x01, 0x80, 0x03,
+ 0x85, 0x00, 0x81, 0xf8, 0x04, 0xf0, 0xfc, 0xfe, 0xfe, 0xf1, 0x80, 0xff,
+ 0x06, 0x7f, 0xff, 0xff, 0xfc, 0x03, 0x03, 0x01, 0x80, 0x00, 0x01, 0x01,
+ 0x01, 0x85, 0x00, 0x80, 0x7f, 0x07, 0xff, 0xfe, 0xfe, 0xfc, 0xf0, 0xf8,
+ 0xf8, 0xfc, 0x80, 0xff, 0x01, 0x7f, 0x1f, 0x82, 0x01, 0xab, 0x00, 0x04,
+ 0xf0, 0xfc, 0xfe, 0xfe, 0xff, 0x80, 0x00, 0x04, 0x1f, 0x7f, 0x7f, 0xff,
+ 0xff, 0x8d, 0x00, 0x03, 0x7f, 0x7f, 0x3f, 0x3f, 0x80, 0xff, 0x00, 0xfe,
+ 0x81, 0xfc, 0x80, 0xff, 0x00, 0x7f, 0x81, 0x01, 0x89, 0x00, 0x0f, 0xfc,
+ 0xc0, 0xe0, 0xe0, 0xf0, 0xf8, 0xf8, 0xfc, 0x7f, 0x3f, 0x1f, 0x0f, 0x0f,
+ 0x07, 0x03, 0x01, 0xcd, 0x00, 0x00, 0xf8, 0x81, 0xfc, 0x00, 0xf8, 0x80,
+ 0x00, 0x81, 0x01, 0x92, 0x00, 0x05, 0x7e, 0xfe, 0xff, 0xff, 0xfe, 0x7e,
+ 0xb8, 0x00, 0x00, 0xe0, 0x81, 0xf0, 0x80, 0x00, 0x80, 0x07, 0x01, 0x0f,
+ 0x07, 0x8d, 0x00, 0x09, 0xe0, 0xc0, 0x00, 0x00, 0xf0, 0xf8, 0xfc, 0xfc,
+ 0x07, 0x03, 0x80, 0x00, 0x02, 0x01, 0x03, 0x03, 0x8d, 0x00, 0x09, 0xfc,
+ 0xfc, 0xf8, 0xf0, 0xf0, 0xfe, 0x7f, 0x1f, 0x07, 0x07, 0x80, 0x03, 0x00,
+ 0x01, 0xbc, 0x00, 0x02, 0xc0, 0xf8, 0xfe, 0x81, 0x00, 0x81, 0x03, 0x85,
+ 0x00, 0x02, 0xc0, 0xf0, 0xfe, 0x84, 0xff, 0x07, 0x3f, 0x0f, 0x01, 0x00,
+ 0x03, 0x1f, 0x03, 0x01, 0x8b, 0x00, 0x02, 0xfc, 0xe0, 0x80, 0x82, 0x00,
+ 0x05, 0x7f, 0xff, 0xff, 0xfc, 0xf0, 0x80, 0x80, 0x00, 0x82, 0x03, 0x00,
+ 0x02, 0xad, 0x00, 0x00, 0xff, 0x84, 0x00, 0x00, 0xff, 0x84, 0x00, 0x00,
+ 0x03, 0x85, 0x00, 0x81, 0xff, 0x01, 0x00, 0x00, 0x83, 0xff, 0x03, 0x00,
+ 0x00, 0xff, 0xff, 0x81, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x85, 0x00,
+ 0x80, 0xff, 0x82, 0x00, 0x80, 0xff, 0x82, 0x00, 0x80, 0x03, 0xae, 0x00,
+ 0x03, 0x03, 0x1f, 0x7f, 0xff, 0x84, 0x00, 0x00, 0x03, 0x8d, 0x00, 0x03,
+ 0xff, 0xfe, 0xf0, 0xc0, 0x80, 0x00, 0x0b, 0xe0, 0x0f, 0x3f, 0xff, 0xff,
+ 0xfe, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x01, 0x82, 0x03, 0x85, 0x00, 0x00,
+ 0xf8, 0x80, 0xff, 0x07, 0x3f, 0x0f, 0x01, 0x00, 0xff, 0x1f, 0x07, 0x01,
+ 0xb4, 0x00, 0x00, 0xfc, 0x80, 0xfe, 0x00, 0xff, 0x80, 0x00, 0x04, 0x07,
+ 0x0f, 0x0f, 0x1f, 0x1f, 0x8d, 0x00, 0x0d, 0xc7, 0xc3, 0xe0, 0xf0, 0xf8,
+ 0xfc, 0xfc, 0x7c, 0x1f, 0x1f, 0x0f, 0x0f, 0x07, 0x03, 0x8f, 0x00, 0x02,
+ 0x7e, 0x7e, 0x3e, 0x81, 0x7f, 0x00, 0x3e, 0x9a, 0x00, 0x02, 0x80, 0xc0,
+ 0xf0, 0x81, 0x00, 0x03, 0xfe, 0xff, 0xff, 0x3f, 0x81, 0x00, 0x03, 0x3f,
+ 0xff, 0xff, 0xfc, 0x83, 0x00, 0x13, 0x03, 0x07, 0xf8, 0xf8, 0x7c, 0x3e,
+ 0x9e, 0x9e, 0xcf, 0xcf, 0x03, 0xfc, 0xfe, 0xff, 0xbf, 0x1f, 0x0f, 0x07,
+ 0xe0, 0xff, 0x81, 0x7f, 0x04, 0x3e, 0x3e, 0x0f, 0x0f, 0x1f, 0x80, 0x1e,
+ 0x01, 0x1c, 0x3c, 0x84, 0xcf, 0x00, 0xdf, 0x81, 0x07, 0x32, 0x87, 0x87,
+ 0xff, 0xff, 0x3e, 0x3f, 0x1f, 0x1f, 0x9f, 0xdf, 0xff, 0xff, 0x1c, 0x1e,
+ 0x1e, 0x1f, 0x0f, 0x0f, 0x07, 0x03, 0xbe, 0x3e, 0xfc, 0xf8, 0xf0, 0xe0,
+ 0x80, 0x00, 0xff, 0x7f, 0x00, 0x03, 0x3f, 0xff, 0xff, 0xfe, 0xff, 0x7f,
+ 0x00, 0xc0, 0xfc, 0xff, 0xff, 0x7f, 0x01, 0x3c, 0x3f, 0x1f, 0x0f, 0x07,
+ 0x03, 0xa9, 0x00, 0x04, 0xfc, 0xfc, 0xfe, 0xfe, 0xff, 0x80, 0x00, 0x82,
+ 0x03, 0x86, 0x00, 0x06, 0x80, 0x80, 0xc0, 0xc0, 0xe0, 0xf0, 0xf0, 0x80,
+ 0xff, 0x04, 0xef, 0xef, 0xe7, 0xc7, 0xc3, 0x82, 0x07, 0x80, 0x0f, 0x85,
+ 0x00, 0x07, 0xf8, 0xf8, 0xfc, 0xfc, 0xfe, 0xfe, 0x7f, 0x7f, 0x80, 0xff,
+ 0x80, 0xc0, 0x03, 0x80, 0x80, 0x0f, 0x0f, 0x83, 0x1f, 0xa8, 0x00, 0x82,
+ 0xf8, 0x80, 0x00, 0x80, 0xff, 0x01, 0xfd, 0xe1, 0x80, 0x00, 0x04, 0x01,
+ 0x07, 0x07, 0x0f, 0x0f, 0x85, 0x00, 0x84, 0xfc, 0x03, 0xfe, 0xe1, 0xe1,
+ 0xfd, 0x80, 0xff, 0x09, 0xfc, 0xe0, 0x0f, 0x0f, 0x07, 0x03, 0x03, 0x07,
+ 0x0f, 0x0f, 0x85, 0x00, 0x80, 0xfe, 0x01, 0x7e, 0x7e, 0x80, 0xff, 0x80,
+ 0xc0, 0x01, 0xe0, 0xfc, 0x80, 0xff, 0x80, 0x1f, 0x80, 0x0f, 0x01, 0x07,
+ 0x01, 0xa9, 0x00, 0x03, 0xc0, 0xf0, 0xf8, 0xf8, 0x80, 0x00, 0x82, 0xff,
+ 0x80, 0x00, 0x81, 0x1f, 0x00, 0x0f, 0x85, 0x00, 0x00, 0xfc, 0x80, 0xfe,
+ 0x81, 0x7f, 0x02, 0x0f, 0x03, 0x01, 0x82, 0x00, 0x00, 0x0c, 0x8c, 0x00,
+ 0x00, 0xff, 0x80, 0xfe, 0x06, 0xfc, 0xf8, 0xf0, 0xc0, 0x00, 0x00, 0x83,
+ 0x82, 0xff, 0x01, 0x08, 0x0e, 0x80, 0x0f, 0x02, 0x07, 0x07, 0x01, 0xa8,
+ 0x00, 0x82, 0xf8, 0x80, 0x00, 0x82, 0xff, 0x80, 0x00, 0x04, 0x01, 0x07,
+ 0x0f, 0x1f, 0x3f, 0x85, 0x00, 0x83, 0xfc, 0x05, 0xfe, 0xfe, 0xe1, 0x81,
+ 0x01, 0x01, 0x81, 0x00, 0x00, 0x3f, 0x80, 0x7f, 0x03, 0x7e, 0x7e, 0x7f,
+ 0x7f, 0x85, 0x00, 0x04, 0xfe, 0xfe, 0x7e, 0xfe, 0xfe, 0x80, 0xff, 0x02,
+ 0x00, 0x80, 0xe0, 0x82, 0xff, 0x07, 0x7f, 0x7f, 0x3f, 0x3f, 0x1f, 0x0f,
+ 0x07, 0x01, 0xa8, 0x00, 0x82, 0xf8, 0x80, 0x00, 0x82, 0xff, 0x80, 0x00,
+ 0x00, 0x07, 0x81, 0x03, 0x85, 0x00, 0x84, 0xfc, 0x02, 0xfe, 0x01, 0x01,
+ 0x82, 0xff, 0x8e, 0x00, 0x04, 0xfe, 0xfe, 0x7e, 0xfe, 0xfe, 0x80, 0xff,
+ 0x80, 0x00, 0x82, 0xff, 0x80, 0x00, 0x81, 0x03, 0x00, 0x01, 0xa8, 0x00,
+ 0x82, 0xf8, 0x80, 0x00, 0x82, 0xff, 0x80, 0x00, 0x01, 0x01, 0x01, 0x88,
+ 0x00, 0x84, 0xfc, 0x02, 0xfe, 0x01, 0x01, 0x82, 0x3f, 0x8e, 0x00, 0x80,
+ 0xfe, 0x01, 0x7e, 0x7e, 0x80, 0x7f, 0xb9, 0x00, 0x03, 0xc0, 0xf0, 0xf8,
+ 0xfc, 0x80, 0x00, 0x82, 0xff, 0x80, 0x00, 0x81, 0x7f, 0x00, 0x3f, 0x85,
+ 0x00, 0x00, 0xfc, 0x80, 0xfe, 0x81, 0x7f, 0x02, 0x0f, 0x03, 0x01, 0x82,
+ 0xf0, 0x02, 0x30, 0x00, 0x00, 0x80, 0x3f, 0x01, 0x1f, 0x1f, 0x85, 0x00,
+ 0x00, 0xff, 0x80, 0xfe, 0x06, 0xfc, 0xf8, 0xf0, 0xc0, 0x80, 0x80, 0x83,
+ 0x82, 0xff, 0x82, 0x1f, 0x02, 0x0f, 0x0f, 0x03, 0xa8, 0x00, 0x82, 0xf8,
+ 0x80, 0x00, 0x80, 0x03, 0x01, 0x01, 0x01, 0x80, 0x00, 0x81, 0xf8, 0x00,
+ 0xfc, 0x80, 0x00, 0x00, 0x03, 0x81, 0x01, 0x84, 0xfc, 0x00, 0xfe, 0x80,
+ 0x01, 0x82, 0xff, 0x80, 0xfc, 0x82, 0xff, 0x80, 0x01, 0x82, 0x00, 0x80,
+ 0xfe, 0x01, 0x7e, 0x7e, 0x80, 0x7f, 0x85, 0x00, 0x00, 0xfe, 0x80, 0x7e,
+ 0x81, 0x7f, 0xa8, 0x00, 0x82, 0xf8, 0x80, 0x00, 0x80, 0x03, 0x01, 0x01,
+ 0x01, 0x8d, 0x00, 0x84, 0xfc, 0x00, 0xfe, 0x81, 0x01, 0x91, 0x00, 0x80,
+ 0xfe, 0x01, 0x7e, 0x7e, 0x80, 0x7f, 0x99, 0x00, 0x80, 0x80, 0x00, 0xc0,
+ 0x81, 0x00, 0x00, 0x3f, 0x80, 0x1f, 0x8d, 0x00, 0x83, 0xc0, 0x01, 0xe0,
+ 0xe0, 0x80, 0x1f, 0x82, 0x0f, 0x8d, 0x00, 0x81, 0xe0, 0x81, 0xf0, 0x01,
+ 0x0f, 0x0f, 0x83, 0x07, 0x8d, 0x00, 0x02, 0xf0, 0xf0, 0xf8, 0x81, 0xff,
+ 0x00, 0x7f, 0x81, 0x03, 0x01, 0x01, 0x01, 0xb2, 0x00, 0x82, 0xf8, 0x80,
+ 0x00, 0x04, 0x83, 0xc3, 0xc3, 0xe1, 0xf1, 0x80, 0x00, 0x04, 0xff, 0x7f,
+ 0x3f, 0x1f, 0x0f, 0x85, 0x00, 0x84, 0xfc, 0x0b, 0xfe, 0xf9, 0xfd, 0xff,
+ 0xff, 0x7f, 0xff, 0xff, 0xfe, 0x07, 0x03, 0x01, 0x81, 0x00, 0x00, 0x01,
+ 0x85, 0x00, 0x80, 0xfe, 0x01, 0x7e, 0x7e, 0x80, 0x7f, 0x0f, 0xfc, 0xfc,
+ 0xf8, 0xf0, 0xf0, 0xe0, 0xc0, 0x80, 0x03, 0x03, 0x07, 0x0f, 0x0f, 0x1f,
+ 0x3f, 0x3f, 0xa8, 0x00, 0x82, 0xf8, 0x80, 0x00, 0x01, 0x03, 0x03, 0x80,
+ 0x01, 0x8d, 0x00, 0x84, 0xfc, 0x00, 0xfe, 0x81, 0x01, 0x91, 0x00, 0x04,
+ 0xfe, 0xfe, 0x7e, 0xfe, 0xfe, 0x80, 0xff, 0x80, 0x00, 0x82, 0xff, 0x80,
+ 0x00, 0x82, 0x01, 0xa8, 0x00, 0x00, 0x80, 0x80, 0xc0, 0x00, 0xe0, 0x80,
+ 0x00, 0x01, 0x3f, 0x3f, 0x80, 0x7f, 0x80, 0x00, 0x04, 0x80, 0x80, 0xc0,
+ 0xe0, 0xe0, 0x80, 0x00, 0x81, 0x3f, 0x02, 0x7f, 0xe0, 0xe0, 0x80, 0xf0,
+ 0x80, 0xf8, 0x00, 0x7f, 0x81, 0xff, 0x08, 0xfb, 0xfb, 0xf9, 0xf0, 0xf0,
+ 0xf8, 0xf8, 0xfc, 0xfd, 0x84, 0x7f, 0x80, 0x7e, 0x80, 0xfc, 0x08, 0xfe,
+ 0xfe, 0x7e, 0x7f, 0x7f, 0xf1, 0xf1, 0xf0, 0xf0, 0x81, 0xe0, 0x07, 0x3f,
+ 0x3f, 0x1f, 0x1f, 0x0f, 0x07, 0x07, 0x03, 0x85, 0x7e, 0xa0, 0x00, 0x82,
+ 0xf8, 0x80, 0x00, 0x04, 0x03, 0x07, 0x07, 0x0f, 0x1f, 0x80, 0x00, 0x80,
+ 0xf8, 0x01, 0xfc, 0xfc, 0x80, 0x00, 0x82, 0x01, 0x84, 0xfc, 0x07, 0xfe,
+ 0x1f, 0x3f, 0x7f, 0x7f, 0xff, 0xfe, 0xfe, 0x82, 0xfc, 0x00, 0xfe, 0x80,
+ 0xff, 0x01, 0x01, 0x01, 0x83, 0x00, 0x80, 0xfe, 0x01, 0x7e, 0x7e, 0x80,
+ 0x7f, 0x07, 0xf8, 0xf8, 0xf0, 0xe0, 0xe0, 0xc0, 0x80, 0x80, 0x84, 0x7f,
+ 0x00, 0x3f, 0xa9, 0x00, 0x03, 0xc0, 0xf0, 0xf8, 0xfc, 0x80, 0x00, 0x82,
+ 0xff, 0x80, 0x00, 0x04, 0x07, 0x1f, 0x3f, 0x7f, 0x7f, 0x85, 0x00, 0x00,
+ 0xfc, 0x80, 0xfe, 0x81, 0x7f, 0x02, 0x87, 0x01, 0x01, 0x82, 0x00, 0x02,
+ 0xff, 0xfe, 0xfe, 0x80, 0xfc, 0x01, 0xfe, 0xfe, 0x80, 0x00, 0x01, 0x01,
+ 0x01, 0x80, 0x00, 0x00, 0xff, 0x80, 0xfe, 0x06, 0xfc, 0xf8, 0xf0, 0xc0,
+ 0x00, 0x00, 0xc3, 0x84, 0xff, 0x05, 0x7f, 0x7f, 0x3f, 0x1f, 0x07, 0x01,
+ 0xa8, 0x00, 0x82, 0xf8, 0x80, 0x00, 0x81, 0xff, 0x00, 0xf1, 0x80, 0x00,
+ 0x04, 0x01, 0x03, 0x07, 0x0f, 0x0f, 0x85, 0x00, 0x84, 0xfc, 0x00, 0xfe,
+ 0x80, 0xe1, 0x01, 0xf1, 0xfe, 0x80, 0xff, 0x81, 0x0f, 0x03, 0x07, 0x07,
+ 0x03, 0x01, 0x85, 0x00, 0x80, 0xfe, 0x01, 0x7e, 0x7e, 0x80, 0x7f, 0x99,
+ 0x00, 0x03, 0xc0, 0xf0, 0xf8, 0xf8, 0x80, 0x00, 0x82, 0xff, 0x80, 0x00,
+ 0x04, 0x07, 0x1f, 0x3f, 0x7f, 0x7f, 0x85, 0x00, 0x00, 0xfc, 0x80, 0xfe,
+ 0x81, 0x7f, 0x02, 0x87, 0x01, 0x01, 0x82, 0x00, 0x02, 0xff, 0xfe, 0xfe,
+ 0x80, 0xfc, 0x01, 0xfe, 0xfe, 0x81, 0x00, 0x05, 0x01, 0x01, 0x00, 0x00,
+ 0x7f, 0xff, 0x80, 0xfe, 0x05, 0xfc, 0xf8, 0xe0, 0x00, 0x00, 0x81, 0x82,
+ 0xff, 0x07, 0xfe, 0xff, 0x7f, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x85, 0x00,
+ 0x02, 0xc0, 0x80, 0x80, 0x82, 0x00, 0x81, 0xff, 0x0b, 0xfc, 0xf8, 0xe0,
+ 0x80, 0x00, 0x01, 0x07, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0xa8, 0x00, 0x82,
+ 0xf8, 0x80, 0x00, 0x80, 0xff, 0x01, 0xfd, 0xe1, 0x80, 0x00, 0x01, 0x03,
+ 0x07, 0x80, 0x0f, 0x85, 0x00, 0x84, 0xfc, 0x04, 0xfe, 0xc1, 0xc1, 0xe1,
+ 0xfd, 0x81, 0xff, 0x80, 0x1f, 0x04, 0x0f, 0x0f, 0x07, 0x01, 0x01, 0x85,
+ 0x00, 0x01, 0xfe, 0xfe, 0x80, 0x7e, 0x80, 0x7f, 0x0f, 0xfc, 0xf8, 0xf8,
+ 0xf0, 0xf0, 0xe0, 0xe0, 0xc0, 0x03, 0x03, 0x07, 0x07, 0x0f, 0x0f, 0x1f,
+ 0x1f, 0xa8, 0x00, 0x04, 0xe0, 0xf0, 0xf8, 0xfc, 0xfc, 0x80, 0x00, 0x00,
+ 0x7f, 0x81, 0xff, 0x81, 0x00, 0x01, 0x01, 0x01, 0x87, 0x00, 0x80, 0xfc,
+ 0x0c, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0xe1, 0x01, 0x03, 0x07, 0x0f, 0x1f,
+ 0x3f, 0x7f, 0x8e, 0x00, 0x01, 0x02, 0x0e, 0x81, 0xff, 0x02, 0xfc, 0x7f,
+ 0x7e, 0x81, 0x7f, 0x01, 0x3f, 0x0f, 0xb0, 0x00, 0x01, 0xfe, 0xfe, 0x80,
+ 0xff, 0x80, 0x00, 0x82, 0xff, 0x80, 0x00, 0x82, 0x1f, 0x85, 0x00, 0x80,
+ 0x80, 0x82, 0xc0, 0x00, 0x3f, 0x83, 0x1f, 0x00, 0x0f, 0x8d, 0x00, 0x01,
+ 0xc0, 0xc0, 0x83, 0xe0, 0x83, 0x0f, 0x01, 0x07, 0x07, 0xb0, 0x00, 0x81,
+ 0xfc, 0x00, 0xfe, 0x80, 0x00, 0x01, 0x01, 0x01, 0x83, 0x00, 0x80, 0xfc,
+ 0x01, 0xfe, 0xfe, 0x85, 0x00, 0x80, 0xfe, 0x80, 0x7e, 0x01, 0x7f, 0x7f,
+ 0x85, 0x00, 0x03, 0xfe, 0xfe, 0x7e, 0x7e, 0x81, 0x7f, 0x85, 0x00, 0x01,
+ 0x7f, 0x7f, 0x80, 0xfe, 0x05, 0xfc, 0xf8, 0xe0, 0x00, 0x80, 0xc1, 0x82,
+ 0xff, 0x80, 0x3f, 0x04, 0x1f, 0x1f, 0x0f, 0x07, 0x01, 0xa8, 0x00, 0x04,
+ 0x3f, 0x3f, 0x7f, 0x7e, 0x7e, 0x80, 0x00, 0x04, 0xe0, 0xf0, 0xf0, 0xf8,
+ 0xf8, 0x80, 0x00, 0x04, 0x0f, 0x07, 0x07, 0x03, 0x03, 0x85, 0x00, 0x02,
+ 0x7e, 0x7e, 0xfe, 0x84, 0xfc, 0x07, 0xfe, 0xfe, 0x7f, 0x7f, 0x3f, 0x3f,
+ 0x01, 0x01, 0x8b, 0x00, 0x81, 0xf8, 0x81, 0xf0, 0x07, 0x1f, 0x0f, 0x0f,
+ 0x07, 0x07, 0x03, 0x03, 0x01, 0xb0, 0x00, 0x00, 0x7f, 0x81, 0x7e, 0x80,
+ 0x00, 0x04, 0xe0, 0xe0, 0xf0, 0xf0, 0xf8, 0x80, 0x00, 0x81, 0x07, 0x00,
+ 0x0f, 0x80, 0x00, 0x05, 0xfc, 0xfe, 0xfe, 0x7f, 0x7f, 0xfe, 0x81, 0xfc,
+ 0x81, 0xf8, 0x0a, 0xfc, 0xfc, 0xfe, 0xfe, 0xff, 0xbf, 0x3f, 0x8f, 0x8f,
+ 0xcf, 0xdf, 0x81, 0xff, 0x09, 0x3f, 0x3f, 0x1f, 0x1f, 0x0f, 0x0f, 0x07,
+ 0x07, 0xf8, 0xf8, 0x81, 0xf0, 0x09, 0xe0, 0xe0, 0x1f, 0x1f, 0x0f, 0x0f,
+ 0x07, 0x07, 0x03, 0x01, 0x80, 0xff, 0x08, 0xfe, 0xfe, 0x7e, 0x7c, 0x3c,
+ 0x03, 0x03, 0x01, 0x01, 0xa4, 0x00, 0x04, 0xf8, 0xf0, 0xe0, 0xe0, 0xc0,
+ 0x80, 0x00, 0x04, 0x07, 0x0f, 0x0f, 0x1f, 0xbf, 0x80, 0x00, 0x04, 0xfc,
+ 0xfe, 0xfe, 0xff, 0x7f, 0x80, 0x00, 0x06, 0x07, 0x03, 0x01, 0x00, 0x00,
+ 0x80, 0x80, 0x82, 0x00, 0x00, 0x80, 0x81, 0xff, 0x0b, 0xfe, 0xfe, 0xff,
+ 0xff, 0x3f, 0x1f, 0x0f, 0x0f, 0x07, 0x03, 0x03, 0x07, 0x85, 0x00, 0x06,
+ 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0x81, 0xff, 0x0c, 0xef, 0xc7,
+ 0x83, 0x83, 0x01, 0x07, 0x0f, 0x0f, 0x1f, 0x3f, 0x3f, 0x7f, 0xff, 0xa8,
+ 0x00, 0x04, 0x3f, 0x3f, 0x7f, 0x7e, 0x7e, 0x80, 0x00, 0x04, 0xf0, 0xf8,
+ 0xf8, 0xfc, 0xfe, 0x80, 0x00, 0x04, 0x0f, 0x07, 0x03, 0x01, 0x01, 0x85,
+ 0x00, 0x00, 0xfe, 0x80, 0xfc, 0x0b, 0xf8, 0xf8, 0xf0, 0xf0, 0xfe, 0x7f,
+ 0x7f, 0x3f, 0x1f, 0x1f, 0x0f, 0x07, 0x8d, 0x00, 0x81, 0xf0, 0x81, 0xf8,
+ 0x01, 0x07, 0x07, 0x83, 0x03, 0xb0, 0x00, 0x80, 0xe0, 0x01, 0xf0, 0xf0,
+ 0x80, 0x00, 0x82, 0xff, 0x80, 0x00, 0x80, 0xff, 0x01, 0x7f, 0x3f, 0x80,
+ 0x00, 0x01, 0x03, 0x01, 0x87, 0x00, 0x0e, 0x80, 0xc0, 0xe0, 0xf0, 0xf8,
+ 0xfc, 0xfe, 0xff, 0x7f, 0x1f, 0x0f, 0x0f, 0x07, 0x03, 0x01, 0x87, 0x00,
+ 0x0a, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff, 0x3f, 0x1f, 0x0f,
+ 0x82, 0xff, 0x80, 0x00, 0x82, 0x3f, 0x89, 0x00, 0x81, 0xf0, 0x81, 0x00,
+ 0x81, 0x3f, 0x8d, 0x00, 0x00, 0xf0, 0x83, 0xf8, 0x01, 0xfc, 0x3f, 0x83,
+ 0x01, 0x8e, 0x00, 0x82, 0xfc, 0x02, 0x7c, 0x7e, 0x7e, 0x95, 0x00, 0x80,
+ 0x7e, 0x01, 0xfe, 0xfe, 0x80, 0xff, 0x80, 0x00, 0x82, 0x03, 0xb0, 0x00,
+ 0x04, 0x1f, 0x3f, 0x3e, 0x7e, 0x7c, 0x95, 0x00, 0x07, 0x7c, 0xfc, 0xf8,
+ 0xf8, 0xf0, 0xf0, 0xe0, 0xe0, 0x80, 0x00, 0x80, 0x01, 0x01, 0x03, 0x03,
+ 0x8d, 0x00, 0x02, 0xe0, 0xc0, 0xc0, 0x80, 0x80, 0x01, 0x00, 0x00, 0x80,
+ 0x07, 0x04, 0x0f, 0x0f, 0x1f, 0x1f, 0x3f, 0x91, 0x00, 0x81, 0xf0, 0x81,
+ 0x00, 0x81, 0x3f, 0x8d, 0x00, 0x02, 0xf0, 0x00, 0x00, 0x82, 0x80, 0x01,
+ 0x3f, 0x3f, 0x83, 0x1f, 0x8d, 0x00, 0x01, 0x80, 0x80, 0x83, 0xc0, 0x00,
+ 0x1f, 0x83, 0x0f, 0x00, 0x07, 0x8d, 0x00, 0x80, 0xe0, 0x80, 0xfe, 0x01,
+ 0xff, 0xff, 0x83, 0x07, 0x01, 0x03, 0x03, 0xaf, 0x00, 0x0d, 0xc0, 0xc0,
+ 0xe0, 0xe0, 0xf0, 0xf0, 0x00, 0x00, 0x0f, 0x1f, 0x1f, 0x3f, 0x3f, 0x7f,
+ 0x8d, 0x00, 0x0b, 0xf8, 0xf8, 0xfc, 0xfc, 0xfe, 0x7e, 0x7f, 0x00, 0x7f,
+ 0xff, 0xfd, 0xfc, 0x80, 0xf8, 0x81, 0x00, 0x03, 0x01, 0x01, 0x03, 0x03,
+ 0xff, 0x00, 0x8a, 0x00, 0x01, 0xff, 0xff, 0x83, 0x00, 0x01, 0xff, 0xff,
+ 0x83, 0x00, 0x01, 0x03, 0x03, 0xa6, 0x00, 0x04, 0x3f, 0x3e, 0x7c, 0x7c,
+ 0xf8, 0xff, 0x00, 0x95, 0x00, 0x07, 0xc0, 0xf8, 0xfc, 0xfe, 0xfe, 0xff,
+ 0x7f, 0x3f, 0x82, 0xff, 0x02, 0xf8, 0xfc, 0xfc, 0x84, 0x01, 0x86, 0x00,
+ 0x80, 0x3f, 0x03, 0xff, 0xff, 0xfe, 0xfe, 0x80, 0xfc, 0x00, 0xfe, 0x80,
+ 0xff, 0x01, 0x7f, 0x7f, 0xb0, 0x00, 0x82, 0xf8, 0x80, 0x00, 0x01, 0x03,
+ 0x03, 0x80, 0x01, 0x8d, 0x00, 0x01, 0xf8, 0xf8, 0x83, 0xfc, 0x82, 0xff,
+ 0x04, 0xf1, 0xf0, 0xf0, 0x00, 0x01, 0x80, 0x03, 0x80, 0x07, 0x85, 0x00,
+ 0x02, 0xfe, 0xfe, 0x7e, 0x82, 0xfe, 0x02, 0xf0, 0xf0, 0xf8, 0x80, 0xff,
+ 0x07, 0x7f, 0x3f, 0x07, 0x07, 0x03, 0x03, 0x01, 0x01, 0xc7, 0x00, 0x08,
+ 0xe0, 0xf8, 0xfc, 0xfe, 0xfe, 0xff, 0x7f, 0x3f, 0xff, 0x81, 0x7f, 0x00,
+ 0x60, 0x8f, 0x00, 0x0b, 0x3f, 0x3f, 0x7f, 0xff, 0xfe, 0xfe, 0xfc, 0xf0,
+ 0x00, 0x00, 0x70, 0x7f, 0x81, 0x3f, 0xb8, 0x00, 0x82, 0xf0, 0x80, 0x00,
+ 0x01, 0x07, 0x07, 0x80, 0x03, 0x85, 0x00, 0x07, 0xe0, 0xf8, 0xfc, 0xfe,
+ 0xfe, 0xff, 0x7f, 0x7f, 0x82, 0xff, 0x02, 0xf9, 0xf8, 0xf8, 0x80, 0x03,
+ 0x82, 0x01, 0x85, 0x00, 0x07, 0x3f, 0x7f, 0xff, 0xff, 0xfe, 0xfe, 0xfc,
+ 0xf0, 0x80, 0xfc, 0x82, 0xff, 0x01, 0x01, 0x01, 0xcb, 0x00, 0x0f, 0xf0,
+ 0xf8, 0xfc, 0xfe, 0xfe, 0x7f, 0xff, 0xff, 0x1f, 0x3f, 0x7f, 0xff, 0xff,
+ 0xfc, 0xff, 0xff, 0x8d, 0x00, 0x0f, 0xff, 0x3f, 0x3f, 0xff, 0xff, 0xfe,
+ 0xfc, 0xf8, 0xff, 0x00, 0x60, 0x7f, 0x7f, 0x3f, 0x3f, 0x1f, 0x91, 0x00,
+ 0x03, 0x80, 0xc0, 0xe0, 0xe0, 0x80, 0x00, 0x82, 0xff, 0x8d, 0x00, 0x01,
+ 0xe0, 0xe0, 0x80, 0xfe, 0x04, 0xff, 0xff, 0xf0, 0x0f, 0x07, 0x82, 0x3f,
+ 0x00, 0x03, 0x8d, 0x00, 0x01, 0xf0, 0xf0, 0x83, 0xf8, 0x80, 0x03, 0x82,
+ 0x01, 0x8d, 0x00, 0x85, 0xfc, 0x01, 0x01, 0x01, 0xb4, 0x00, 0x01, 0xf0,
+ 0xf8, 0x82, 0xfc, 0x00, 0x00, 0x81, 0xff, 0x03, 0xfc, 0xf8, 0xfc, 0x00,
+ 0x81, 0x07, 0x80, 0x01, 0x85, 0x00, 0x07, 0xfc, 0xf8, 0xf8, 0xfc, 0xfc,
+ 0xfe, 0xfe, 0xfc, 0x80, 0xff, 0x05, 0x3f, 0x01, 0x01, 0x1f, 0x7f, 0x01,
+ 0x8c, 0x00, 0x03, 0xfc, 0xfe, 0xff, 0x3f, 0x80, 0xff, 0x00, 0xfc, 0x80,
+ 0xff, 0x05, 0xfc, 0xfe, 0xff, 0xff, 0x3f, 0x00, 0x81, 0x01, 0xab, 0x00,
+ 0x00, 0xf0, 0x81, 0xf8, 0x80, 0x00, 0x01, 0x03, 0x03, 0x80, 0x01, 0x8d,
+ 0x00, 0x01, 0xf8, 0xf8, 0x83, 0xfc, 0x00, 0xfd, 0x81, 0xff, 0x03, 0xf3,
+ 0xf1, 0xf0, 0x01, 0x84, 0x03, 0x85, 0x00, 0x02, 0xfc, 0xfe, 0xfe, 0x81,
+ 0x7e, 0x02, 0x7f, 0xf0, 0xf0, 0x83, 0xf8, 0x81, 0x03, 0x81, 0x01, 0xa8,
+ 0x00, 0x81, 0xf8, 0x00, 0x70, 0x81, 0x00, 0x01, 0x01, 0x01, 0x8f, 0x00,
+ 0x80, 0xfc, 0x80, 0xfe, 0x01, 0x7e, 0x7e, 0x95, 0x00, 0x01, 0x7e, 0x7e,
+ 0x80, 0x7f, 0x80, 0x3f, 0x99, 0x00, 0x81, 0x80, 0x81, 0x00, 0x03, 0x0f,
+ 0x1f, 0x1f, 0x0f, 0x8e, 0x00, 0x81, 0xc0, 0x80, 0xe0, 0x00, 0x07, 0x83,
+ 0x0f, 0x00, 0x07, 0x8d, 0x00, 0x81, 0xe0, 0x81, 0xf0, 0x83, 0x07, 0x01,
+ 0x03, 0x03, 0x8d, 0x00, 0x07, 0xf0, 0xf0, 0xf8, 0xfe, 0xfe, 0xff, 0xff,
+ 0x7f, 0x81, 0x03, 0x01, 0x01, 0x01, 0xb2, 0x00, 0x00, 0xf8, 0x81, 0xfc,
+ 0x80, 0x00, 0x80, 0x01, 0x8f, 0x00, 0x80, 0xfc, 0x01, 0xfe, 0x7e, 0x80,
+ 0xfe, 0x0a, 0xf8, 0xfc, 0xfe, 0xff, 0x7f, 0x3f, 0x3f, 0x1f, 0x07, 0x03,
+ 0x01, 0x8a, 0x00, 0x02, 0xfe, 0xff, 0xff, 0x82, 0x3f, 0x07, 0x1f, 0x1f,
+ 0x3f, 0x7f, 0xff, 0xfe, 0xfc, 0xfc, 0x83, 0x00, 0x01, 0x01, 0x03, 0xa8,
+ 0x00, 0x80, 0xf8, 0x01, 0xfc, 0xfc, 0x80, 0x00, 0x82, 0x01, 0x8d, 0x00,
+ 0x81, 0xfc, 0x80, 0xfe, 0x00, 0x7e, 0x95, 0x00, 0x80, 0x7e, 0x01, 0x7f,
+ 0x7f, 0x80, 0x3f, 0xd5, 0x00, 0x01, 0xf8, 0xf8, 0x83, 0xfc, 0x82, 0xff,
+ 0x03, 0xfb, 0xf8, 0xf8, 0xfc, 0x81, 0xff, 0x04, 0xe7, 0xe3, 0xe1, 0x01,
+ 0x03, 0x83, 0x07, 0x02, 0xfc, 0xfe, 0xfe, 0x81, 0x7e, 0x00, 0x7f, 0x81,
+ 0xf8, 0x81, 0xfc, 0x83, 0xf1, 0x03, 0xf0, 0xf8, 0x07, 0x07, 0x83, 0x03,
+ 0xbd, 0x00, 0x01, 0xf8, 0xf8, 0x83, 0xfc, 0x82, 0xff, 0x03, 0xf1, 0xf0,
+ 0xf0, 0x01, 0x84, 0x03, 0x85, 0x00, 0x02, 0xfc, 0xfe, 0xfe, 0x81, 0x7e,
+ 0x02, 0x7f, 0xf0, 0xf0, 0x83, 0xf8, 0x81, 0x03, 0x81, 0x01, 0xc5, 0x00,
+ 0x09, 0xe0, 0xf8, 0xfc, 0xfe, 0xfe, 0xff, 0x7f, 0x3f, 0x3f, 0x7f, 0x80,
+ 0xff, 0x02, 0xfc, 0xf8, 0xf8, 0x80, 0x00, 0x80, 0x01, 0x01, 0x03, 0x03,
+ 0x85, 0x00, 0x10, 0x3f, 0x3f, 0x7f, 0xff, 0xfe, 0xfe, 0xfc, 0xf0, 0xf8,
+ 0xf8, 0xfc, 0xff, 0xff, 0x7f, 0x3f, 0x1f, 0x03, 0x80, 0x01, 0x8f, 0x00,
+ 0x01, 0xe0, 0xe0, 0x83, 0x00, 0x01, 0x07, 0x07, 0x8d, 0x00, 0x80, 0xe0,
+ 0x05, 0xf0, 0xf0, 0xf8, 0xfc, 0xfe, 0x07, 0x82, 0xff, 0x05, 0xe3, 0xc3,
+ 0x00, 0x01, 0x03, 0x07, 0x81, 0x0f, 0x85, 0x00, 0x02, 0xfe, 0xff, 0xff,
+ 0x82, 0xf8, 0x03, 0xc3, 0xc3, 0xe3, 0xf1, 0x81, 0xff, 0x81, 0x0f, 0x03,
+ 0x07, 0x07, 0x03, 0x01, 0x85, 0x00, 0x84, 0xfc, 0x02, 0xfe, 0xff, 0x01,
+ 0xb4, 0x00, 0x07, 0xe0, 0xf8, 0xfc, 0xfe, 0xfe, 0xff, 0x7f, 0x00, 0x82,
+ 0xff, 0x02, 0xfc, 0xfc, 0x00, 0x83, 0x01, 0x86, 0x00, 0x80, 0x3f, 0x00,
+ 0x7f, 0x80, 0xff, 0x00, 0xfe, 0x81, 0xfc, 0x03, 0xff, 0xff, 0x7f, 0x7f,
+ 0x8d, 0x00, 0x00, 0xfc, 0x84, 0x00, 0x01, 0x7f, 0x7e, 0x80, 0x7f, 0x80,
+ 0x3f, 0xcd, 0x00, 0x01, 0xfc, 0xfc, 0x81, 0xfe, 0x01, 0x7e, 0x7e, 0x81,
+ 0x3f, 0x02, 0x1f, 0x1c, 0x08, 0x8e, 0x00, 0x01, 0x7e, 0x7f, 0x83, 0x3f,
+ 0xd5, 0x00, 0x01, 0xf8, 0xfc, 0x82, 0xfe, 0x01, 0xfc, 0x0f, 0x81, 0x1f,
+ 0x02, 0x18, 0x01, 0x03, 0x8d, 0x00, 0x02, 0xf8, 0xf0, 0xc3, 0x81, 0xff,
+ 0x01, 0xfe, 0x07, 0x82, 0x0f, 0x01, 0x07, 0x03, 0xb2, 0x00, 0x80, 0xf0,
+ 0x82, 0x00, 0x02, 0x07, 0x03, 0x03, 0x8d, 0x00, 0x01, 0xfe, 0xfe, 0x80,
+ 0xff, 0x02, 0xf8, 0xf8, 0xfc, 0x81, 0x1f, 0x00, 0x0f, 0x80, 0x01, 0x8d,
+ 0x00, 0x83, 0xfc, 0x04, 0xf8, 0xf0, 0x01, 0x00, 0x0c, 0x82, 0x0f, 0xcd,
+ 0x00, 0x01, 0xfe, 0xfe, 0x81, 0x7e, 0x02, 0x7f, 0x7f, 0xf0, 0x84, 0xf8,
+ 0x80, 0x03, 0x82, 0x01, 0x85, 0x00, 0x02, 0x7f, 0x3f, 0x3f, 0x80, 0xff,
+ 0x04, 0xfe, 0xfe, 0xfc, 0xfc, 0xfe, 0x82, 0xff, 0x01, 0x01, 0x01, 0xcb,
+ 0x00, 0x00, 0x1f, 0x80, 0x3f, 0x00, 0x3e, 0x80, 0xfe, 0x08, 0xfc, 0xfc,
+ 0xfe, 0x7e, 0x3f, 0x3f, 0x1f, 0x1f, 0x01, 0x8c, 0x00, 0x81, 0xfc, 0x81,
+ 0xf8, 0x06, 0x0f, 0x0f, 0x07, 0x07, 0x03, 0x03, 0x01, 0xce, 0x00, 0x01,
+ 0x1f, 0x1f, 0x80, 0x3f, 0x05, 0xbe, 0xfe, 0xfe, 0x3c, 0x3e, 0x7e, 0x81,
+ 0x7f, 0x0a, 0xff, 0xf8, 0xfc, 0xfc, 0xfe, 0x7e, 0x3f, 0x3f, 0x1f, 0x01,
+ 0x01, 0x83, 0x00, 0x00, 0xfe, 0x81, 0xfc, 0x80, 0xf8, 0x80, 0xff, 0x0c,
+ 0xfb, 0xfb, 0xf1, 0xf1, 0xf0, 0x1f, 0x0f, 0x0f, 0x07, 0x07, 0x03, 0x01,
+ 0x01, 0xc5, 0x00, 0x0a, 0xf8, 0xf8, 0xf0, 0xf0, 0xe0, 0xc0, 0xc0, 0x80,
+ 0xe1, 0xf3, 0xfb, 0x80, 0xff, 0x06, 0x7f, 0x3f, 0x0f, 0x0f, 0x07, 0x03,
+ 0x01, 0x88, 0x00, 0x0f, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfc, 0xfe, 0x7f,
+ 0x3f, 0x7f, 0x7f, 0xff, 0xff, 0xfd, 0xf8, 0xf8, 0x82, 0x00, 0x02, 0x01,
+ 0x03, 0x03, 0xa6, 0x00, 0x01, 0x3f, 0x3f, 0x80, 0x7e, 0x0e, 0xfc, 0xfc,
+ 0x00, 0xf0, 0xf8, 0xf8, 0xfc, 0xfc, 0xfe, 0x7e, 0x00, 0x03, 0x03, 0x01,
+ 0x01, 0x88, 0x00, 0x02, 0xfc, 0xf8, 0xf8, 0x80, 0xf0, 0x09, 0xe0, 0xe0,
+ 0x7f, 0x3f, 0x3f, 0x1f, 0x1f, 0x0f, 0x0f, 0x07, 0x8d, 0x00, 0x0c, 0xf0,
+ 0xf0, 0xf8, 0xf8, 0xfc, 0xfc, 0xfe, 0x7e, 0x07, 0x03, 0x03, 0x01, 0x01,
+ 0xd0, 0x00, 0x81, 0xfc, 0x03, 0x00, 0x80, 0xc0, 0xe0, 0x80, 0xff, 0x06,
+ 0x7f, 0x7f, 0x3f, 0x1f, 0x0f, 0x01, 0x01, 0x8b, 0x00, 0x0c, 0xe0, 0xf0,
+ 0xf8, 0xf8, 0xfc, 0xfe, 0xff, 0xff, 0x0f, 0x07, 0x03, 0x03, 0xff, 0x80,
+ 0x7f, 0x91, 0x00, 0x03, 0xe0, 0xf0, 0xf8, 0xf8, 0x81, 0x00, 0x81, 0x0f,
+ 0x8d, 0x00, 0x82, 0xf8, 0x03, 0xfc, 0xff, 0xff, 0x0f, 0x83, 0x01, 0x8e,
+ 0x00, 0x04, 0xff, 0x7f, 0xff, 0xff, 0xfc, 0x80, 0xf8, 0x81, 0x00, 0x81,
+ 0x01, 0x8d, 0x00, 0x83, 0xf8, 0x01, 0xf0, 0xe0, 0x80, 0x01, 0x82, 0x0f,
+ 0x91, 0x00, 0x81, 0x1f, 0x95, 0x00, 0x85, 0x1f, 0x95, 0x00, 0x85, 0x1f,
+ 0x95, 0x00, 0x85, 0x1f, 0x99, 0x00, 0x00, 0x7f, 0x80, 0xff, 0x95, 0x00,
+ 0x01, 0xff, 0xfc, 0x83, 0xf8, 0x83, 0x01, 0x01, 0x07, 0x0f, 0x8d, 0x00,
+ 0x80, 0xf0, 0x82, 0xf8, 0x81, 0x0f, 0x00, 0x03, 0x80, 0x01, 0x8d, 0x00,
+ 0x02, 0xf8, 0xf8, 0xfc, 0x81, 0xff, 0x02, 0x7f, 0x01, 0x01, 0xd4, 0x00,
+ 0x01, 0xf8, 0xfe, 0x81, 0xff, 0x03, 0x03, 0x00, 0x00, 0x87, 0x82, 0xff,
+ 0x01, 0x00, 0x02, 0x82, 0x03, 0x00, 0x01, 0xc7, 0x00, 0x0d, 0xff, 0xff,
+ 0x0f, 0x1f, 0x1f, 0x3b, 0x00, 0x00, 0xff, 0xff, 0xf0, 0xf8, 0xf8, 0xdc,
+ 0x8d, 0x00, 0x0b, 0x7b, 0x73, 0xe3, 0xe3, 0xc3, 0xc3, 0xe3, 0xe3, 0xde,
+ 0xce, 0xc7, 0xc7, 0x80, 0xc3, 0x00, 0xc7, 0x8d, 0x00, 0x0f, 0xf3, 0x73,
+ 0x3b, 0x3f, 0x1f, 0x0f, 0xff, 0xff, 0xcf, 0xce, 0xdc, 0xfc, 0xf8, 0xf0,
+ 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x09, 0x00, 0x0a, 0x01,
+ 0x12, 0x02, 0x15, 0x01, 0x13, 0x01, 0x1a, 0x01, 0x19, 0x01, 0x0a, 0x02,
+ 0x0d, 0x02, 0x0c, 0xff, 0x12, 0x02, 0x15, 0x02, 0x0c, 0xff, 0x0f, 0x01,
+ 0x0a, 0x00, 0x14, 0xfe, 0x13, 0x01, 0x13, 0x05, 0x13, 0x00, 0x13, 0x01,
+ 0x13, 0x00, 0x13, 0x01, 0x13, 0x01, 0x13, 0x02, 0x13, 0x00, 0x13, 0x01,
+ 0x0a, 0x00, 0x0c, 0xff, 0x15, 0x02, 0x15, 0x02, 0x15, 0x02, 0x0e, 0x01,
+ 0x1f, 0x01, 0x16, 0x00, 0x16, 0x01, 0x16, 0x01, 0x19, 0x01, 0x14, 0x01,
+ 0x12, 0x01, 0x18, 0x01, 0x1b, 0x01, 0x0b, 0x01, 0x0e, 0xfe, 0x19, 0x01,
+ 0x12, 0x01, 0x21, 0x00, 0x1b, 0x01, 0x1a, 0x01, 0x15, 0x01, 0x1a, 0x01,
+ 0x17, 0x01, 0x11, 0x00, 0x17, 0x02, 0x1b, 0x02, 0x16, 0x02, 0x22, 0x01,
+ 0x1b, 0xff, 0x16, 0x02, 0x1a, 0xff, 0x0e, 0x00, 0x0f, 0x01, 0x0f, 0xfe,
+ 0x15, 0x02, 0x12, 0x00, 0x0b, 0x03, 0x13, 0x01, 0x13, 0x00, 0x11, 0x01,
+ 0x14, 0x01, 0x12, 0x01, 0x10, 0x00, 0x13, 0x00, 0x13, 0x00, 0x0a, 0x01,
+ 0x0d, 0xfe, 0x14, 0x01, 0x0a, 0x01, 0x1c, 0x00, 0x13, 0x00, 0x13, 0x01,
+ 0x15, 0x00, 0x13, 0x01, 0x0f, 0x01, 0x0d, 0x00, 0x0d, 0x00, 0x13, 0x01,
+ 0x12, 0x01, 0x1a, 0x01, 0x15, 0xfe, 0x12, 0x00, 0x11, 0x00, 0x0d, 0x01,
+ 0x09, 0x03, 0x0d, 0x01, 0x15, 0x02, 0x10, 0x00, 0xb9, 0x7e, 0x78, 0x7e,
+ 0x57, 0x7e, 0x36, 0x7e, 0x15, 0x7e, 0xd5, 0x7d, 0xb4, 0x7d, 0x93, 0x7d,
+ 0x72, 0x7d, 0x31, 0x7d, 0x11, 0x7d, 0xf0, 0x7c, 0xaf, 0x7c, 0x8e, 0x78,
+ 0x6e, 0x78, 0x4d, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xf0, 0xee,
+ 0x00, 0x00, 0xef, 0xde, 0x00, 0xf0, 0xef, 0xcd, 0x00, 0xf0, 0xde, 0xcc,
+ 0x00, 0xf0, 0xff, 0xff, 0xf0, 0xef, 0xee, 0xee, 0xef, 0xde, 0xdd, 0xdd,
+ 0xde, 0xdd, 0xcc, 0xcc, 0xdd, 0xcc, 0xbb, 0xbb, 0xcc, 0xbb, 0xaa, 0xaa,
+ 0xbc, 0xaa, 0x99, 0x99, 0xab, 0x9a, 0x88, 0x88, 0x00, 0xef, 0xdd, 0xbc,
+ 0x00, 0xef, 0xcd, 0xab, 0xf0, 0xde, 0xcd, 0xab, 0xf0, 0xde, 0xbc, 0x9a,
+ 0xf0, 0xde, 0xbc, 0x9a, 0xf0, 0xde, 0xbc, 0x9a, 0xf0, 0xde, 0xbc, 0x9a,
+ 0xf0, 0xde, 0xbc, 0x9a, 0x9a, 0x89, 0x78, 0x77, 0x9a, 0x78, 0x67, 0x66,
+ 0x89, 0x77, 0x56, 0x55, 0x88, 0x67, 0x55, 0x44, 0x78, 0x56, 0x45, 0x33,
+ 0x78, 0x56, 0x34, 0x22, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xff, 0xff, 0xff, 0x00,
+ 0x00, 0x00, 0x00, 0x7f, 0x7d, 0x7d, 0x7d, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c,
+ 0x00, 0x00, 0x00, 0x00, 0xe0, 0xf8, 0x1c, 0x1e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xc7, 0xcf, 0xde, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xcf, 0x8f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xe7, 0xf7, 0x00, 0x00, 0x00, 0x00,
+ 0x1e, 0xff, 0xf1, 0xe1, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf1, 0xf1,
+ 0x00, 0x00, 0x00, 0x00, 0xe0, 0xe0, 0xf1, 0xf1, 0xf0, 0xf0, 0xf0, 0x00,
+ 0x03, 0xf3, 0xf1, 0xf1, 0x01, 0x01, 0x01, 0x00, 0xf8, 0xf9, 0xf9, 0xf9,
+ 0x00, 0x00, 0x00, 0x00, 0xe0, 0xe0, 0xe0, 0xe0, 0x00, 0x00, 0x00, 0x00,
+ 0xf3, 0xf3, 0xe3, 0xc3, 0x00, 0x00, 0x00, 0x00, 0xe1, 0xe1, 0xf3, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x00, 0xf8, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x1e, 0xff, 0x1f, 0x1f,
+ 0x1e, 0x1e, 0x3c, 0xf8, 0xde, 0xdf, 0xc0, 0xc0, 0xde, 0xde, 0xcf, 0xc7,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xe0, 0xe0, 0x80, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xf3, 0x7f,
+ 0xf3, 0xe3, 0xe3, 0xc3, 0xc3, 0x81, 0x80, 0x80, 0xf1, 0xfb, 0x7b, 0x7b,
+ 0x3f, 0x3f, 0x1f, 0x1f, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe0, 0xe1, 0xff, 0x83, 0x03, 0x03, 0x83, 0xc3, 0xe3, 0xf3, 0xf3,
+ 0x7f, 0x3f, 0x3f, 0x7f, 0xff, 0xf3, 0xf1, 0xe1, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0x7c, 0x7c,
+ 0x7c, 0x7c, 0x7c, 0x7c, 0x7f, 0xff, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8,
+ 0x00, 0x00, 0x00, 0x01, 0xc1, 0xf1, 0x79, 0x79, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x1f, 0x3c, 0x78, 0x00, 0x00, 0x00, 0x00, 0x9f, 0xdf, 0x3f, 0x1f,
+ 0x00, 0x00, 0x00, 0x00, 0x1f, 0x7f, 0x7e, 0x7c, 0xfc, 0xfe, 0x3e, 0x3f,
+ 0xff, 0xfe, 0xfc, 0xf8, 0x3f, 0x3f, 0x38, 0x00, 0x01, 0x1f, 0x7f, 0xff,
+ 0xfc, 0xfc, 0x00, 0x00, 0xfc, 0xfc, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00,
+ 0x7c, 0xfc, 0xfc, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xf8, 0xf8,
+ 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0xfd, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0xff, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xff, 0x7f, 0x3f, 0x0f, 0xfd, 0xfd, 0x7d, 0x7d,
+ 0x78, 0x78, 0xf0, 0xe0, 0x7f, 0x7f, 0x00, 0x00, 0x78, 0x78, 0x3c, 0x0f,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x7c, 0x7c, 0x7c, 0x7c,
+ 0x7c, 0x7c, 0x7c, 0x7c, 0xc0, 0x00, 0x02, 0x0e, 0xfe, 0xfe, 0xfc, 0xf0,
+ 0xff, 0xfc, 0xf8, 0xf8, 0xff, 0xff, 0x7f, 0x1f, 0xfd, 0xfd, 0xfd, 0xfd,
+ 0xfd, 0xfc, 0xfc, 0xfc, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xfd, 0x7d, 0x7d, 0x7d,
+ 0x7d, 0x7d, 0x7d, 0x7d, 0xff, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x04, 0x01, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x80, 0x01, 0x01, 0x04,
+ 0x03, 0x00, 0x00, 0x80, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1,
+ 0x08, 0x20, 0x43, 0x55, 0x4c, 0x54, 0x2d, 0x47, 0x42, 0x41, 0x00, 0x00,
+ 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x40, 0x2d, 0xe9, 0x14, 0x01, 0x9f, 0xe5,
+ 0x02, 0x14, 0xa0, 0xe3, 0x1c, 0x00, 0x2d, 0xe9, 0x04, 0x20, 0x90, 0xe4,
+ 0x22, 0x24, 0xa0, 0xe1, 0xab, 0xf7, 0xff, 0xeb, 0x05, 0x04, 0xa0, 0xe3,
+ 0xbc, 0x1f, 0xdf, 0xe1, 0xb0, 0x10, 0xc0, 0xe1, 0xb8, 0x1f, 0xdf, 0xe1,
+ 0xb2, 0x10, 0xc0, 0xe1, 0xb6, 0x10, 0xc0, 0xe1, 0xb0, 0x1f, 0xdf, 0xe1,
+ 0xb4, 0x10, 0xc0, 0xe1, 0xb8, 0x10, 0xc0, 0xe1, 0x01, 0x03, 0xa0, 0xe3,
+ 0xb4, 0x1e, 0xdf, 0xe1, 0xb0, 0x10, 0xc0, 0xe1, 0x08, 0x10, 0xa0, 0xe3,
+ 0xb4, 0x10, 0xc0, 0xe1, 0xb8, 0x1d, 0xdf, 0xe1, 0xba, 0x14, 0xc0, 0xe1,
+ 0x07, 0x10, 0xa0, 0xe3, 0xb8, 0x10, 0xc0, 0xe1, 0xcc, 0x00, 0x9f, 0xe5,
+ 0xcc, 0x10, 0x9f, 0xe5, 0x08, 0x20, 0xa0, 0xe3, 0x04, 0x30, 0x90, 0xe4,
+ 0x04, 0x30, 0x81, 0xe4, 0x01, 0x20, 0x52, 0xe2, 0xfb, 0xff, 0xff, 0xca,
+ 0xb8, 0x10, 0x9f, 0xe5, 0x20, 0x20, 0xa0, 0xe3, 0x04, 0x30, 0x90, 0xe4,
+ 0x04, 0x30, 0x81, 0xe4, 0x01, 0x20, 0x52, 0xe2, 0xfb, 0xff, 0xff, 0xca,
+ 0xa4, 0x00, 0x9f, 0xe5, 0xa4, 0x10, 0x9f, 0xe5, 0x08, 0x20, 0xa0, 0xe3,
+ 0x04, 0x10, 0x80, 0xe4, 0x01, 0x20, 0x52, 0xe2, 0xfc, 0xff, 0xff, 0xca,
+ 0x00, 0x10, 0xa0, 0xe1, 0x90, 0x00, 0x9f, 0xe5, 0x90, 0x20, 0x9f, 0xe5,
+ 0x01, 0x30, 0xa0, 0xe3, 0xfc, 0x1f, 0x2d, 0xe9, 0xb0, 0x30, 0xd2, 0xe1,
+ 0x09, 0xf7, 0xff, 0xeb, 0x01, 0x30, 0x53, 0xe2, 0x7c, 0x20, 0x9f, 0x05,
+ 0xf9, 0xff, 0xff, 0x0a, 0x78, 0xc0, 0x9f, 0xe5, 0x78, 0xb0, 0x9f, 0xe5,
+ 0x40, 0xa0, 0xa0, 0xe3, 0x02, 0x40, 0xdb, 0xe4, 0x00, 0x00, 0xa0, 0xe3,
+ 0x00, 0x30, 0xa0, 0xe3, 0x03, 0x10, 0xdb, 0xe7, 0x81, 0x10, 0x6a, 0xe0,
+ 0xf1, 0x10, 0x9c, 0xe1, 0x41, 0x04, 0x80, 0xe0, 0xff, 0x10, 0x01, 0xe2,
+ 0x01, 0x00, 0x80, 0xe0, 0x01, 0x30, 0x83, 0xe2, 0x04, 0x00, 0x53, 0xe1,
+ 0xf6, 0xff, 0xff, 0xba, 0xa0, 0x00, 0xa0, 0xe1, 0x78, 0x00, 0x60, 0xe2,
+ 0x0f, 0x00, 0x00, 0xea, 0xa0, 0x11, 0x00, 0x00, 0x23, 0x7c, 0x00, 0x00,
+ 0xff, 0x7f, 0x00, 0x00, 0x5a, 0x6c, 0x00, 0x00, 0x40, 0x91, 0x00, 0x00,
+ 0x01, 0x30, 0x00, 0x00, 0x20, 0x24, 0x00, 0x00, 0x20, 0x02, 0x00, 0x05,
+ 0x80, 0x7f, 0x01, 0x06, 0x00, 0x40, 0x00, 0x06, 0x11, 0x11, 0x11, 0x11,
+ 0xc0, 0x24, 0x00, 0x00, 0x40, 0x27, 0x00, 0x00, 0x50, 0x27, 0x00, 0x00,
+ 0x60, 0x23, 0x00, 0x00, 0x60, 0x27, 0x00, 0x00, 0x01, 0x10, 0x5b, 0xe5,
+ 0x00, 0x30, 0xa0, 0xe3, 0x03, 0x20, 0xdb, 0xe7, 0x3b, 0x00, 0x00, 0xeb,
+ 0x82, 0x50, 0x6a, 0xe0, 0xf5, 0x50, 0x9c, 0xe1, 0x45, 0x04, 0x80, 0xe0,
+ 0xff, 0x50, 0x05, 0xe2, 0x05, 0x00, 0x80, 0xe0, 0x01, 0x30, 0x83, 0xe2,
+ 0x04, 0x00, 0x53, 0xe1, 0xf5, 0xff, 0xff, 0xba, 0x6c, 0x30, 0x9f, 0xe5,
+ 0x00, 0x40, 0xa0, 0xe3, 0xff, 0x5f, 0xa0, 0xe3, 0x01, 0x69, 0xa0, 0xe3,
+ 0x01, 0x20, 0xa0, 0xe1, 0x02, 0x00, 0x14, 0xe3, 0x10, 0x20, 0x82, 0x12,
+ 0xb2, 0x20, 0xc3, 0xe0, 0x04, 0x06, 0x86, 0xe1, 0x01, 0x00, 0x14, 0xe3,
+ 0x10, 0x00, 0x80, 0x12, 0xb2, 0x00, 0xc3, 0xe0, 0xb0, 0x04, 0xdf, 0xe1,
+ 0xb4, 0x00, 0xc3, 0xe0, 0x01, 0x40, 0x84, 0xe2, 0x04, 0x00, 0x54, 0xe3,
+ 0xf2, 0xff, 0xff, 0xba, 0x30, 0x00, 0x9f, 0xe5, 0x01, 0x10, 0xa0, 0xe3,
+ 0x05, 0x30, 0xa0, 0xe3, 0x10, 0x20, 0xa0, 0xe3, 0xb2, 0x10, 0xc0, 0xe0,
+ 0x01, 0x10, 0x81, 0xe2, 0x01, 0x20, 0x52, 0xe2, 0xfb, 0xff, 0xff, 0xca,
+ 0x20, 0x00, 0x80, 0xe2, 0x01, 0x30, 0x53, 0xe2, 0xf7, 0xff, 0xff, 0xca,
+ 0x02, 0x00, 0x00, 0xea, 0xe0, 0x03, 0x00, 0x07, 0xfc, 0x17, 0x00, 0x00,
+ 0x8e, 0x03, 0x00, 0x06, 0xf0, 0x80, 0xa0, 0xe3, 0x3c, 0xa0, 0x9f, 0xe5,
+ 0x01, 0xb0, 0xa0, 0xe3, 0xb2, 0xb0, 0xca, 0xe0, 0x99, 0xf8, 0xff, 0xeb,
+ 0x01, 0xb0, 0xa0, 0xe3, 0xb0, 0xb0, 0xca, 0xe1, 0x28, 0x30, 0x9f, 0xe5,
+ 0x04, 0x40, 0xa0, 0xe3, 0xb0, 0x10, 0xd3, 0xe1, 0x04, 0x10, 0x81, 0xe2,
+ 0xb8, 0x10, 0xc3, 0xe0, 0x01, 0x40, 0x54, 0xe2, 0xfa, 0xff, 0xff, 0xca,
+ 0x04, 0x80, 0x58, 0xe2, 0xf3, 0xff, 0xff, 0xca, 0x00, 0x40, 0xbd, 0xe8,
+ 0x1e, 0xff, 0x2f, 0xe1, 0x00, 0x02, 0x00, 0x04, 0xe2, 0x03, 0x00, 0x07,
+ 0x0f, 0x40, 0x2d, 0xe9, 0x03, 0x00, 0x2d, 0xe9, 0x02, 0x04, 0xa0, 0xe3,
+ 0x20, 0x20, 0x42, 0xe2, 0x82, 0x03, 0x80, 0xe0, 0x34, 0x10, 0x9f, 0xe5,
+ 0x83, 0x14, 0x81, 0xe0, 0x30, 0x20, 0x9f, 0xe5, 0x96, 0xf6, 0xff, 0xeb,
+ 0x03, 0x00, 0xbd, 0xe8, 0x07, 0x24, 0xa0, 0xe3, 0x83, 0x21, 0x82, 0xe0,
+ 0x02, 0x1b, 0x81, 0xe3, 0xb2, 0x10, 0xc2, 0xe0, 0x02, 0x09, 0x80, 0xe3,
+ 0xb2, 0x00, 0xc2, 0xe0, 0x03, 0x32, 0xa0, 0xe1, 0xb2, 0x30, 0xc2, 0xe0,
+ 0x0f, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x00, 0x00, 0x01, 0x06,
+ 0x50, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+UINT32 gba_bios_bin_len = 16384;
+
+#endif // !_GBA_BIOS_
diff --git a/src/burn/drv/gba/d_gba.cpp b/src/burn/drv/gba/d_gba.cpp
new file mode 100644
index 000000000..2457886ca
--- /dev/null
+++ b/src/burn/drv/gba/d_gba.cpp
@@ -0,0 +1,527 @@
+// FBNeo Game Boy Advance driver
+
+#include "burnint.h"
+#include "gba.h"
+
+static GbaCore* Gba;
+static UINT8* DrvRom;
+static UINT8* DrvBios;
+static UINT8* DrvState;
+static UINT8* DrvBattery;
+static INT32 DrvStateSize;
+static INT32 DrvBatterySize;
+
+static UINT8 DrvJoy[10];
+static UINT8 DrvReset;
+static UINT8 DrvRecalc;
+static UINT16 DrvSolar;
+static UINT8 DrvSolarUp;
+static UINT8 DrvSolarDown;
+static UINT8 DrvSolarUpLast;
+static UINT8 DrvSolarDownLast;
+static UINT16 DrvGyroZ;
+static UINT16 DrvTiltX;
+static UINT16 DrvTiltY;
+
+#define GBA_BUTTON_INPUTS \
+ { "P1 Up", BIT_DIGITAL, DrvJoy + GBA_BUTTON_UP, "p1 up" }, \
+ { "P1 Down", BIT_DIGITAL, DrvJoy + GBA_BUTTON_DOWN, "p1 down" }, \
+ { "P1 Left", BIT_DIGITAL, DrvJoy + GBA_BUTTON_LEFT, "p1 left" }, \
+ { "P1 Right", BIT_DIGITAL, DrvJoy + GBA_BUTTON_RIGHT, "p1 right" }, \
+ { "P1 Button A", BIT_DIGITAL, DrvJoy + GBA_BUTTON_A, "p1 fire 1" }, \
+ { "P1 Button B", BIT_DIGITAL, DrvJoy + GBA_BUTTON_B, "p1 fire 2" }, \
+ { "P1 Button L", BIT_DIGITAL, DrvJoy + GBA_BUTTON_L, "p1 fire 3" }, \
+ { "P1 Button R", BIT_DIGITAL, DrvJoy + GBA_BUTTON_R, "p1 fire 4" },
+
+#define GBA_RESET_INPUTS \
+ { "P1 Select", BIT_DIGITAL, DrvJoy + GBA_BUTTON_SELECT, "p1 select" }, \
+ { "P1 Start", BIT_DIGITAL, DrvJoy + GBA_BUTTON_START, "p1 start" }, \
+ { "Reset", BIT_DIGITAL, &DrvReset, "reset" },
+
+#define GBA_SOLAR_INPUTS \
+ { "Solar Brighter", BIT_DIGITAL, &DrvSolarUp, "p1 fire 5" }, \
+ { "Solar Darker", BIT_DIGITAL, &DrvSolarDown, "p1 fire 6" },
+
+#define GBA_GYRO_INPUTS \
+ { "Gyro Z", BIT_ANALOG_ABS, (UINT8*)&DrvGyroZ, "p1 gyro z-axis" },
+
+#define GBA_TILT_INPUTS \
+ { "Tilt X", BIT_ANALOG_ABS, (UINT8*)&DrvTiltX, "p1 tilt x-axis" }, \
+ { "Tilt Y", BIT_ANALOG_ABS, (UINT8*)&DrvTiltY, "p1 tilt y-axis" },
+
+static struct BurnInputInfo GbaInputList[] = {
+ GBA_BUTTON_INPUTS
+ GBA_RESET_INPUTS
+};
+
+STDINPUTINFO(Gba)
+
+static struct BurnInputInfo SolarInputList[] = { // Solar sensor
+ GBA_BUTTON_INPUTS
+ GBA_SOLAR_INPUTS
+ GBA_RESET_INPUTS
+};
+
+STDINPUTINFO(Solar)
+
+static struct BurnInputInfo GyroInputList[] = { // Gyroscope
+ GBA_BUTTON_INPUTS
+ GBA_GYRO_INPUTS
+ GBA_RESET_INPUTS
+};
+
+STDINPUTINFO(Gyro)
+
+static struct BurnInputInfo TiltInputList[] = { // Tilt sensor
+ GBA_BUTTON_INPUTS
+ GBA_TILT_INPUTS
+ GBA_RESET_INPUTS
+};
+
+STDINPUTINFO(Tilt)
+
+static struct BurnInputInfo AioInputList[] = { // All-in-one
+ GBA_BUTTON_INPUTS
+ GBA_SOLAR_INPUTS
+ GBA_GYRO_INPUTS
+ GBA_TILT_INPUTS
+ GBA_RESET_INPUTS
+};
+
+STDINPUTINFO(Aio)
+
+#undef GBA_BUTTON_INPUTS
+#undef GBA_SOLAR_INPUTS
+#undef GBA_GYRO_INPUTS
+#undef GBA_TILT_INPUTS
+#undef GBA_RESET_INPUTS
+
+static INT32 DrvDoReset()
+{
+ return GbaCoreReset(Gba);
+}
+
+static INT32 DrvInit()
+{
+ struct BurnRomInfo ri;
+ memset(&ri, 0, sizeof(ri));
+ if (BurnDrvGetRomInfo(&ri, 0) || ri.nLen == 0 || ri.nLen > 32 * 1024 * 1024)
+ return 1;
+
+ BurnSetRefreshRate(59.72750057);
+
+ UINT32 romSize = ri.nLen;
+ DrvRom = (UINT8 *)BurnMalloc((INT32)romSize);
+ if (DrvRom == NULL)
+ return 1;
+ if (BurnLoadRom(DrvRom, 0, 1))
+ return 1;
+ if (GbaCoreInit(&Gba))
+ return 1;
+
+ memset(&ri, 0, sizeof(ri));
+ if (BurnDrvGetRomInfo(&ri, 0x80) == 0 && ri.nType && ri.nLen == 0x4000) {
+ DrvBios = (UINT8 *)BurnMalloc(0x4000);
+ if (DrvBios && BurnLoadRom(DrvBios, 0x80, 1) == 0) {
+ if (GbaCoreLoadBios(Gba, DrvBios, 0x4000))
+ return 1;
+ } else {
+ BurnFree(DrvBios);
+ }
+ }
+
+ tm localTime;
+ memset(&localTime, 0, sizeof(localTime));
+ BurnGetLocalTime(&localTime);
+ GbaRtcSeed rtcSeed;
+ rtcSeed.year = (UINT16)(localTime.tm_year + 1900);
+ rtcSeed.month = (UINT8) (localTime.tm_mon + 1);
+ rtcSeed.day = (UINT8) localTime.tm_mday;
+ rtcSeed.weekday = (UINT8) localTime.tm_wday;
+ rtcSeed.hour = (UINT8) localTime.tm_hour;
+ rtcSeed.minute = (UINT8) localTime.tm_min;
+ rtcSeed.second = (UINT8) localTime.tm_sec;
+ bprintf(PRINT_NORMAL, _T("GBA RTC seed: %04d-%02d-%02d %02d:%02d:%02d (wday %d)\n"),
+ rtcSeed.year, rtcSeed.month, rtcSeed.day, rtcSeed.hour, rtcSeed.minute, rtcSeed.second, rtcSeed.weekday);
+ if (GbaCoreLoadRom(Gba, DrvRom, romSize, &rtcSeed)) return 1;
+
+ DrvStateSize = (INT32)GbaCoreStateSize();
+ DrvState = (UINT8*)BurnMalloc(DrvStateSize);
+ DrvBattery = (UINT8*)BurnMalloc((INT32)GbaCoreGetBatteryCapacity());
+ if (DrvState == NULL || DrvBattery == NULL)
+ return 1;
+
+ DrvBatterySize = (INT32)GbaCoreGetBatterySize(Gba);
+ bprintf(PRINT_NORMAL, _T("GBA cartridge features: %08x, battery: %d\n"), GbaCoreGetCartridgeFeatures(Gba), DrvBatterySize);
+ if (DrvBatterySize > 0) {
+ memcpy(DrvBattery, GbaCoreGetBatteryDataConst(Gba), DrvBatterySize);
+ }
+
+ GbaCoreClearAudio(Gba);
+ DrvRecalc = 1;
+ DrvSolar = 0;
+ DrvSolarUpLast = 0;
+ DrvSolarDownLast = 0;
+ DrvGyroZ = 0x8000;
+ DrvTiltX = 0x8000;
+ DrvTiltY = 0x8000;
+
+ return 0;
+}
+
+static INT32 DrvExit()
+{
+ GbaCoreExit(&Gba);
+ BurnFree(DrvRom);
+ BurnFree(DrvBios);
+ BurnFree(DrvState);
+ BurnFree(DrvBattery);
+
+ DrvStateSize = 0;
+ DrvBatterySize = 0;
+ DrvSolar = 0;
+ DrvSolarUpLast = 0;
+ DrvSolarDownLast = 0;
+ DrvGyroZ = 0x8000;
+ DrvTiltX = 0x8000;
+ DrvTiltY = 0x8000;
+
+ return 0;
+}
+
+static double DrvAudioRate()
+{
+ return nBurnSoundLen * (GBA_MASTER_CLOCK / 280896.0);
+}
+
+static INT32 DrvDraw()
+{
+ const UINT32 *framebuffer = GbaCoreGetFramebuffer(Gba);
+ if (framebuffer == NULL || pBurnDraw == NULL)
+ return 0;
+
+ for (INT32 y = 0; y < GBA_HEIGHT; y++) {
+ UINT8 *dest = pBurnDraw + y * nBurnPitch;
+ const UINT32 *source = framebuffer + y * GBA_WIDTH;
+ for (INT32 x = 0; x < GBA_WIDTH; x++) {
+ UINT32 pixel = source[x];
+ PutPix(dest + x * nBurnBpp, BurnHighCol(pixel & 0xff, (pixel >> 8) & 0xff, (pixel >> 16) & 0xff, 0));
+ }
+ }
+
+ return 0;
+}
+
+static INT32 DrvFrame()
+{
+ if (DrvReset && !bBurnRunAheadFrame && DrvDoReset()) return 1;
+
+ GbaInput input;
+ memset(&input, 0, sizeof(input));
+ input.gyroZ = GbaMotionAxisToInput(DrvGyroZ);
+ input.tiltX = GbaMotionAxisToInput(DrvTiltX);
+ input.tiltY = GbaMotionAxisToInput(DrvTiltY);
+ if (DrvSolarUp && !DrvSolarDown && !DrvSolarUpLast && DrvSolar < GBA_SOLAR_LEVEL_MAX) {
+ DrvSolar++;
+ } else if (DrvSolarDown && !DrvSolarUp && !DrvSolarDownLast && DrvSolar > 0) {
+ DrvSolar--;
+ }
+ DrvSolarUpLast = DrvSolarUp;
+ DrvSolarDownLast = DrvSolarDown;
+ input.solar = GbaSolarLevelToInput((UINT8)DrvSolar);
+ for (INT32 i = 0; i < 10; i++) {
+ input.buttons |= (DrvJoy[i] & 1) << i;
+ }
+ GbaCoreSetInput(Gba, &input);
+
+ INT32 runAhead = bBurnRunAheadFrame != 0;
+ double audioRate = nBurnSoundLen > 0 ? DrvAudioRate() : 0.0;
+ if (GbaCoreConfigureAudio(Gba, audioRate, nBurnSoundLen, !runAhead && nBurnSoundLen > 0))
+ return 1;
+ if (GbaCoreRunFrame(Gba))
+ return 1;
+ if (!runAhead && GbaCoreRenderAudio(Gba, pBurnSoundOut, nBurnSoundLen) != nBurnSoundLen)
+ return 1;
+
+ if (pBurnDraw) {
+ DrvDraw();
+ }
+
+ return 0;
+}
+
+static INT32 DrvScan(INT32 nAction, INT32 *pnMin)
+{
+ if (pnMin) {
+ *pnMin = 0x029698;
+ }
+
+ if ((nAction & ACB_VOLATILE) && Gba && DrvState) {
+ SCAN_VAR(DrvSolar);
+ if (nAction & ACB_WRITE) {
+ if (DrvSolar > GBA_SOLAR_LEVEL_MAX)
+ DrvSolar = GbaSolarLegacyToLevel(DrvSolar);
+ DrvSolarUpLast = DrvSolarUp;
+ DrvSolarDownLast = DrvSolarDown;
+ ScanVar(DrvState, DrvStateSize, "gba_machine");
+ if (GbaCoreLoadState(Gba, DrvState, DrvStateSize, (nAction & ACB_RUNAHEAD) != 0))
+ return 1;
+ } else {
+ if (GbaCoreSaveState(Gba, DrvState, DrvStateSize))
+ return 1;
+ ScanVar(DrvState, DrvStateSize, "gba_machine");
+ }
+ }
+
+ if ((nAction & ACB_NVRAM) && Gba && DrvBattery && DrvBatterySize > 0) {
+ if (nAction & ACB_WRITE) {
+ ScanVar(DrvBattery, DrvBatterySize, "gba_battery");
+ if (GbaCoreLoadBattery(Gba, DrvBattery, DrvBatterySize))
+ return 1;
+ } else {
+ memcpy(DrvBattery, GbaCoreGetBatteryDataConst(Gba), DrvBatterySize);
+ ScanVar(DrvBattery, DrvBatterySize, "gba_battery");
+ if (!(nAction & ACB_RUNAHEAD))
+ GbaCoreClearBatteryDirty(Gba);
+ }
+ }
+
+ return 0;
+}
+
+static INT32 GbaGetZipName(char **pszName, UINT32 i)
+{
+ static char szFilename[MAX_PATH];
+ char *pszGameName = NULL;
+
+ if (pszName == NULL)
+ return 1;
+
+ if (i == 0) {
+ pszGameName = BurnDrvGetTextA(DRV_NAME);
+ } else if (i == 1 && BurnDrvGetTextA(DRV_BOARDROM)) {
+ pszGameName = BurnDrvGetTextA(DRV_BOARDROM);
+ } else {
+ pszGameName = BurnDrvGetTextA(DRV_PARENT);
+ }
+
+ if (pszGameName == NULL || i > 2) {
+ *pszName = NULL;
+ return 1;
+ }
+
+ strncpy(szFilename, pszGameName, MAX_PATH - 1);
+ szFilename[MAX_PATH - 1] = 0;
+ if (strncmp(szFilename, "gba_", 4) == 0) {
+ memmove(szFilename, szFilename + 4, strlen(szFilename + 4) + 1);
+ }
+ *pszName = szFilename;
+
+ return 0;
+}
+
+static struct BurnRomInfo emptyRomDesc[] = {
+ { "", 0, 0, 0 },
+};
+
+static struct BurnRomInfo gba_gbaRomDesc[] = {
+ { "gba_gba.bin", 0x4000, 0x81977335, BRF_PRG | BRF_BIOS | BRF_OPT },
+};
+
+STD_ROM_PICK(gba_gba)
+STD_ROM_FN(gba_gba)
+
+struct BurnDriver BurnDrvgba_gba = {
+ "gba_gba", NULL, NULL, NULL, "2001",
+ "Game Boy Advance BIOS\0", "Optional BIOS, bundled replacement is used when absent", "Nintendo", "Game Boy Advance",
+ NULL, NULL, NULL, NULL,
+ BDF_BOARDROM, 0, HARDWARE_GBA, GBF_BIOS, 0,
+ GbaGetZipName, gba_gbaRomInfo, gba_gbaRomName, NULL, NULL, NULL, NULL, GbaInputInfo, NULL,
+ DrvInit, DrvExit, DrvFrame, DrvDraw, DrvScan, &DrvRecalc, 0,
+ GBA_WIDTH, GBA_HEIGHT, 3, 2
+};
+
+
+// =========================================================================================
+// Please use short names from the MAME softwarelist/gba set wherever possible.
+// Avoid excessively‑long titles that contradict the intent of the short‑name identifiers.
+// [Example] Fire Emblem : The Sacred Stones(USA, Australia)
+// ShortName: gba_firembssu
+// =========================================================================================
+
+
+// Fire Emblem: The Sacred Stones (USA, Australia)
+
+static struct BurnRomInfo gba_firembssuRomDesc[] = {
+ { "Fire Emblem - The Sacred Stones (U, A)(2005)(Nintendo).gba", 0x1000000, 0xa47246ae, BRF_ESS | BRF_PRG },
+};
+
+STDROMPICKEXT(gba_firembssu, gba_firembssu, gba_gba)
+STD_ROM_FN(gba_firembssu)
+
+struct BurnDriver BurnDrvgba_firembssu = {
+ "gba_firembssu", NULL, "gba_gba", NULL, "2005",
+ "Fire Emblem: The Sacred Stones (USA, Australia)\0", NULL, "Nintendo", "Game Boy Advance",
+ NULL, NULL, NULL, NULL,
+ BDF_GAME_WORKING, 1, HARDWARE_PREFIX_CARTRIDGE | HARDWARE_GBA, GBF_RPG | GBF_STRATEGY, 0,
+ GbaGetZipName, gba_firembssuRomInfo, gba_firembssuRomName, NULL, NULL, NULL, NULL, GbaInputInfo, NULL,
+ DrvInit, DrvExit, DrvFrame, DrvDraw, DrvScan, &DrvRecalc, 0,
+ GBA_WIDTH, GBA_HEIGHT, 3, 2
+};
+
+// Fire Emblem: Seima no Kouseki (Japan)
+
+static struct BurnRomInfo gba_firembssjRomDesc[] = {
+ { "Fire Emblem - Seima no Kouseki (J)(2004)(Nintendo).gba", 0x1000000, 0x9d76826f, BRF_ESS | BRF_PRG },
+};
+
+STDROMPICKEXT(gba_firembssj, gba_firembssj, gba_gba)
+STD_ROM_FN(gba_firembssj)
+
+struct BurnDriver BurnDrvgba_firembssj = {
+ "gba_firembssj", "gba_firembssu", "gba_gba", NULL, "2004",
+ "Fire Emblem: Seima no Kouseki (Japan)\0", NULL, "Nintendo", "Game Boy Advance",
+ L"Fire Emblem: Seima no Kouseki (Japan)\0\u30d5\u30a1\u30a4\u30a2\u30fc\u30a8\u30e0\u30d6\u30ec\u30e0 \u8056\u9b54\u306e\u5149\u77f3\0", NULL, NULL, NULL,
+ BDF_GAME_WORKING | BDF_CLONE, 1, HARDWARE_PREFIX_CARTRIDGE | HARDWARE_GBA, GBF_RPG | GBF_STRATEGY, 0,
+ GbaGetZipName, gba_firembssjRomInfo, gba_firembssjRomName, NULL, NULL, NULL, NULL, GbaInputInfo, NULL,
+ DrvInit, DrvExit, DrvFrame, DrvDraw, DrvScan, &DrvRecalc, 0,
+ GBA_WIDTH, GBA_HEIGHT, 3, 2
+};
+
+// Fire Emblem: Sheng Xie De Yi Zhi (Hack, v1.4)
+
+static struct BurnRomInfo gba_firembssxRomDesc[] = {
+ { "Fire Emblem - Sheng Xie De Yi Zhi (C)(2006)(ChinaFE & Wolf Group).gba", 0x1000000, 0x9cbb923e, BRF_ESS | BRF_PRG },
+};
+
+STDROMPICKEXT(gba_firembssx, gba_firembssx, gba_gba)
+STD_ROM_FN(gba_firembssx)
+
+struct BurnDriver BurnDrvgba_firembssx = {
+ "gba_firembssx", "gba_firembssu", "gba_gba", NULL, "2006",
+ "Fire Emblem: Sheng Xie De Yi Zhi (Hack, v1.4)\0", NULL, "ChinaFE & Wolf Group", "Game Boy Advance",
+ L"Fire Emblem: Sheng Xie De Yi Zhi (Hack, v1.4)\0\u706b\u7130\u4e4b\u7eb9\u7ae0: \u5723\u90aa\u7684\u610f\u5fd7\0", NULL, NULL, NULL,
+ BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 1, HARDWARE_PREFIX_CARTRIDGE | HARDWARE_GBA, GBF_RPG | GBF_STRATEGY, 0,
+ GbaGetZipName, gba_firembssxRomInfo, gba_firembssxRomName, NULL, NULL, NULL, NULL, GbaInputInfo, NULL,
+ DrvInit, DrvExit, DrvFrame, DrvDraw, DrvScan, &DrvRecalc, 0,
+ GBA_WIDTH, GBA_HEIGHT, 3, 2
+};
+
+// WarioWare: Twisted! (USA, Australia)
+// Gyro sensor
+
+static struct BurnRomInfo gba_wariotwsRomDesc[] = {
+ { "WarioWare ‑ Twisted! (U, A)(2005)(Nintendo).gba", 0x1000000, 0xcb4e844b, BRF_ESS | BRF_PRG },
+};
+
+STDROMPICKEXT(gba_wariotws, gba_wariotws, gba_gba)
+STD_ROM_FN(gba_wariotws)
+
+struct BurnDriver BurnDrvgba_wariotws = {
+ "gba_wariotws", NULL, "gba_gba", NULL, "2005",
+ "WarioWare: Twisted! (USA, Australia)\0", NULL, "Nintendo", "Game Boy Advance",
+ NULL, NULL, NULL, NULL,
+ BDF_GAME_WORKING, 1, HARDWARE_PREFIX_CARTRIDGE | HARDWARE_GBA, GBF_MISC, 0,
+ GbaGetZipName, gba_wariotwsRomInfo, gba_wariotwsRomName, NULL, NULL, NULL, NULL, GyroInputInfo, NULL,
+ DrvInit, DrvExit, DrvFrame, DrvDraw, DrvScan, &DrvRecalc, 0,
+ GBA_WIDTH, GBA_HEIGHT, 3, 2
+};
+
+// Mawaru: Made in Wario (Japan)
+// Gyro sensor
+
+static struct BurnRomInfo gba_wariotwjRomDesc[] = {
+ {"Mawaru - Made in Wario (J)(2005)(Nintendo).gba", 0x1000000, 0xe69964f1, BRF_ESS | BRF_PRG},
+};
+
+STDROMPICKEXT(gba_wariotwj, gba_wariotwj, gba_gba)
+STD_ROM_FN(gba_wariotwj)
+
+struct BurnDriver BurnDrvGbaMawaruWario = {
+ "gba_wariotwj", "gba_wariotws", "gba_gba", NULL, "2005",
+ "Mawaru: Made in Wario (Japan)\0", NULL, "Nintendo", "Game Boy Advance",
+ L"Mawaru: Made in Wario (Japan)\0\u307e\u308f\u308b\u30e1\u30a4\u30c9\u30a4\u30f3\u30ef\u30ea\u30aa\0", NULL, NULL, NULL,
+ BDF_GAME_WORKING | BDF_CLONE, 1, HARDWARE_PREFIX_CARTRIDGE | HARDWARE_GBA, GBF_MISC, 0,
+ GbaGetZipName, gba_wariotwjRomInfo, gba_wariotwjRomName, NULL, NULL, NULL, NULL, GyroInputInfo, NULL,
+ DrvInit, DrvExit, DrvFrame, DrvDraw, DrvScan, &DrvRecalc, 0,
+ GBA_WIDTH, GBA_HEIGHT, 3, 2
+};
+
+// Yoshi Topsy: Turvy (USA)
+
+static struct BurnRomInfo gba_yoshittRomDesc[] = {
+ { "Yoshi Topsy ‑ Turvy (U)(2005)(Nintendo).gba", 0x800000, 0xe64c265c, BRF_ESS | BRF_PRG },
+};
+
+STDROMPICKEXT(gba_yoshitt, gba_yoshitt, gba_gba)
+STD_ROM_FN(gba_yoshitt)
+
+struct BurnDriver BurnDrvgba_yoshitt = {
+ "gba_yoshitt", NULL, "gba_gba", NULL, "2005",
+ "Yoshi Topsy: Turvy (USA)\0", NULL, "Nintendo", "Game Boy Advance",
+ NULL, NULL, NULL, NULL,
+ BDF_GAME_WORKING, 1, HARDWARE_PREFIX_CARTRIDGE | HARDWARE_GBA, GBF_PLATFORM, 0,
+ GbaGetZipName, gba_yoshittRomInfo, gba_yoshittRomName, NULL, NULL, NULL, NULL, TiltInputInfo, NULL,
+ DrvInit, DrvExit, DrvFrame, DrvDraw, DrvScan, &DrvRecalc, 0,
+ GBA_WIDTH, GBA_HEIGHT, 3, 2
+};
+
+// Yoshi no Banyuuinryoku (Japan)
+// Tilt sensor
+
+static struct BurnRomInfo gba_yoshibanRomDesc[] = {
+ { "Yoshi no Banyuuinryoku (J)(2005)(Nintendo).gba", 0x800000, 0x31594b7a, BRF_ESS | BRF_PRG },
+};
+
+STDROMPICKEXT(gba_yoshiban, gba_yoshiban, gba_gba)
+STD_ROM_FN(gba_yoshiban)
+
+struct BurnDriver BurnDrvgba_yoshiban = {
+ "gba_yoshiban", "gba_yoshitt", "gba_gba", NULL, "2005",
+ "Yoshi no Banyuuinryoku (Japan)\0", NULL, "Nintendo", "Game Boy Advance",
+ L"Yoshi no Banyuuinryoku (Japan)\0\u30e8\u30c3\u30b7\u30fc\u306e\u4e07\u6709\u5f15\u529b\0", NULL, NULL, NULL,
+ BDF_GAME_WORKING | BDF_CLONE, 1, HARDWARE_PREFIX_CARTRIDGE | HARDWARE_GBA, GBF_PLATFORM, 0,
+ GbaGetZipName, gba_yoshibanRomInfo, gba_yoshibanRomName, NULL, NULL, NULL, NULL, TiltInputInfo, NULL,
+ DrvInit, DrvExit, DrvFrame, DrvDraw, DrvScan, &DrvRecalc, 0,
+ GBA_WIDTH, GBA_HEIGHT, 3, 2
+};
+
+// Boktai 2: Solar Boy Django (USA)
+// Solar sensor + RTC clock
+
+static struct BurnRomInfo gba_boktai2uRomDesc[] = {
+ { "Boktai 2 ‑ Solar Boy Django (U)(2004)(Konami).gba", 0x1000000, 0xe1ffb2d1, BRF_ESS | BRF_PRG },
+};
+
+STDROMPICKEXT(gba_boktai2u, gba_boktai2u, gba_gba)
+STD_ROM_FN(gba_boktai2u)
+
+struct BurnDriver BurnDrvgba_boktai2u = {
+ "gba_boktai2u", NULL, "gba_gba", NULL, "2004",
+ "Boktai 2: Solar Boy Django (USA)\0", NULL, "Konami", "Game Boy Advance",
+ NULL, NULL, NULL, NULL,
+ BDF_GAME_WORKING, 1, HARDWARE_PREFIX_CARTRIDGE | HARDWARE_GBA, GBF_ACTION | GBF_RPG, 0,
+ GbaGetZipName, gba_boktai2uRomInfo, gba_boktai2uRomName, NULL, NULL, NULL, NULL, SolarInputInfo, NULL,
+ DrvInit, DrvExit, DrvFrame, DrvDraw, DrvScan, &DrvRecalc, 0,
+ GBA_WIDTH, GBA_HEIGHT, 3, 2
+};
+
+// Zoku Bokura no Taiyou: Taiyou Shounen Django (Japan, Rev 1)
+// Solar light‑sensor + RTC clock
+
+static struct BurnRomInfo gba_boktai2jRomDesc[] = {
+ {"Zoku Bokura no Taiyou - Taiyou Shounen Django (J, Rev 1)(2004)(Konami).gba", 0x1000000, 0x71e2cd01, BRF_ESS | BRF_PRG},
+};
+
+STDROMPICKEXT(gba_boktai2j, gba_boktai2j, gba_gba)
+STD_ROM_FN(gba_boktai2j)
+
+struct BurnDriver BurnDrvGbaBoktai2j = {
+ "gba_boktai2j", "gba_boktai2u", "gba_gba", NULL, "2004",
+ "Zoku Bokura no Taiyou: Taiyou Shounen Django (Japan, Rev 1)\0", NULL, "Konami", "Game Boy Advance",
+ L"Zoku Bokura no Taiyou: Taiyou Shounen Django (Japan, Rev 1)\0\u7d9a \u30dc\u30af\u3089\u306e\u592a\u967d \u592a\u967d\u5c11\u5e74\u30b8\u30e3\u30f3\u30b4\0", NULL, NULL, NULL,
+ BDF_GAME_WORKING | BDF_CLONE, 1, HARDWARE_PREFIX_CARTRIDGE | HARDWARE_GBA, GBF_ACTION | GBF_RPG, 0,
+ GbaGetZipName, gba_boktai2jRomInfo, gba_boktai2jRomName, NULL, NULL, NULL, NULL, SolarInputInfo, NULL,
+ DrvInit, DrvExit, DrvFrame, DrvDraw, DrvScan, &DrvRecalc, 0,
+ GBA_WIDTH, GBA_HEIGHT, 3, 2
+};
diff --git a/src/burn/drv/gba/gba.cpp b/src/burn/drv/gba/gba.cpp
new file mode 100644
index 000000000..6dc258c7d
--- /dev/null
+++ b/src/burn/drv/gba/gba.cpp
@@ -0,0 +1,498 @@
+#ifndef GBA_STANDALONE
+#include "burnint.h"
+#else
+#include
+#define BurnMalloc(x) malloc(x)
+#define BurnFree(x) do { free(x); (x) = NULL; } while (0)
+#endif
+#include "gba.h"
+#include "gba_impl.h"
+
+struct GbaCore {
+ gba_t state;
+ gba_scratch_t scratch;
+ sb_emu_state_t host;
+ UINT8* rom;
+ size_t romSize;
+ bool ownsRom;
+ UINT8 externalBios[16 * 1024];
+ bool externalBiosLoaded;
+ UINT32 cartridgeFeatures;
+ UINT8 cartridgeBackupType;
+ double sourceRate;
+ INT32 outputFrames;
+};
+
+struct GbaCartridgeProfile {
+ UINT8 gameCode[4];
+ UINT32 features;
+ UINT8 backupType;
+};
+
+static const UINT8 GbaSolarCounts[GBA_SOLAR_LEVEL_MAX + 1] = {
+ 233, 228, 222, 215, 206, 191, 171, 149, 124, 94, 50
+};
+
+INT32 GbaMotionAxisToInput(UINT16 axis)
+{
+ return (INT32)(((INT64)(INT32(axis) - 0x8000)) * 0x10000);
+}
+
+UINT8 GbaSolarLevelToInput(UINT8 level)
+{
+ if (level > GBA_SOLAR_LEVEL_MAX)
+ level = GBA_SOLAR_LEVEL_MAX;
+ return (UINT8)(((233 - GbaSolarCounts[level]) * 255) / 183);
+}
+
+UINT8 GbaSolarLegacyToLevel(UINT16 legacy)
+{
+ UINT8 input = legacy >> 8;
+ INT32 count = 233 - (input * 183) / 255;
+ UINT8 closest = 0;
+ INT32 closestDistance = count - GbaSolarCounts[0];
+ if (closestDistance < 0)
+ closestDistance = -closestDistance;
+
+ for (UINT8 level = 1; level <= GBA_SOLAR_LEVEL_MAX; level++) {
+ INT32 distance = count - GbaSolarCounts[level];
+ if (distance < 0)
+ distance = -distance;
+ if (distance < closestDistance) {
+ closest = level;
+ closestDistance = distance;
+ }
+ }
+ return closest;
+}
+
+static const GbaCartridgeProfile GbaCartridgeProfiles[] = {
+ {{'U', '3', 'I', 'J'}, GBA_CART_RTC | GBA_CART_SOLAR, GBA_BACKUP_EEPROM },
+ {{'U', '3', 'I', 'E'}, GBA_CART_RTC | GBA_CART_SOLAR, GBA_BACKUP_EEPROM },
+ {{'U', '3', 'I', 'P'}, GBA_CART_RTC | GBA_CART_SOLAR, GBA_BACKUP_EEPROM },
+ {{'U', '3', '2', 'J'}, GBA_CART_RTC | GBA_CART_SOLAR, GBA_BACKUP_EEPROM },
+ {{'U', '3', '2', 'E'}, GBA_CART_RTC | GBA_CART_SOLAR, GBA_BACKUP_EEPROM },
+ {{'U', '3', '2', 'P'}, GBA_CART_RTC | GBA_CART_SOLAR, GBA_BACKUP_EEPROM },
+ {{'U', '3', '3', 'J'}, GBA_CART_RTC | GBA_CART_SOLAR, GBA_BACKUP_EEPROM },
+ {{'V', '4', '9', 'J'}, GBA_CART_RUMBLE, GBA_BACKUP_SRAM },
+ {{'V', '4', '9', 'E'}, GBA_CART_RUMBLE, GBA_BACKUP_SRAM },
+ {{'V', '4', '9', 'P'}, GBA_CART_RUMBLE, GBA_BACKUP_SRAM },
+ {{'2', 'G', 'B', 'P'}, GBA_CART_RUMBLE, GBA_BACKUP_SRAM },
+ {{'R', 'Z', 'W', 'J'}, GBA_CART_RUMBLE | GBA_CART_GYRO, GBA_BACKUP_SRAM },
+ {{'R', 'Z', 'W', 'E'}, GBA_CART_RUMBLE | GBA_CART_GYRO, GBA_BACKUP_SRAM },
+ {{'R', 'Z', 'W', 'P'}, GBA_CART_RUMBLE | GBA_CART_GYRO, GBA_BACKUP_SRAM },
+ {{'K', 'H', 'P', 'J'}, GBA_CART_TILT, GBA_BACKUP_EEPROM },
+ {{'K', 'Y', 'G', 'J'}, GBA_CART_TILT, GBA_BACKUP_EEPROM },
+ {{'K', 'Y', 'G', 'E'}, GBA_CART_TILT, GBA_BACKUP_EEPROM },
+ {{'K', 'Y', 'G', 'P'}, GBA_CART_TILT, GBA_BACKUP_EEPROM },
+ {{'B', 'L', 'J', 'J'}, GBA_CART_RTC, GBA_BACKUP_FLASH_64K },
+ {{'B', 'L', 'J', 'K'}, GBA_CART_RTC, GBA_BACKUP_FLASH_64K },
+ {{'B', 'L', 'V', 'J'}, GBA_CART_RTC, GBA_BACKUP_FLASH_64K },
+ {{'A', 'X', 'V', 'J'}, GBA_CART_RTC, GBA_BACKUP_FLASH_128K},
+ {{'A', 'X', 'V', 'E'}, GBA_CART_RTC, GBA_BACKUP_FLASH_128K},
+ {{'A', 'X', 'V', 'P'}, GBA_CART_RTC, GBA_BACKUP_FLASH_128K},
+ {{'A', 'X', 'V', 'I'}, GBA_CART_RTC, GBA_BACKUP_FLASH_128K},
+ {{'A', 'X', 'V', 'S'}, GBA_CART_RTC, GBA_BACKUP_FLASH_128K},
+ {{'A', 'X', 'V', 'D'}, GBA_CART_RTC, GBA_BACKUP_FLASH_128K},
+ {{'A', 'X', 'V', 'F'}, GBA_CART_RTC, GBA_BACKUP_FLASH_128K},
+ {{'A', 'X', 'P', 'J'}, GBA_CART_RTC, GBA_BACKUP_FLASH_128K},
+ {{'A', 'X', 'P', 'E'}, GBA_CART_RTC, GBA_BACKUP_FLASH_128K},
+ {{'A', 'X', 'P', 'P'}, GBA_CART_RTC, GBA_BACKUP_FLASH_128K},
+ {{'A', 'X', 'P', 'I'}, GBA_CART_RTC, GBA_BACKUP_FLASH_128K},
+ {{'A', 'X', 'P', 'S'}, GBA_CART_RTC, GBA_BACKUP_FLASH_128K},
+ {{'A', 'X', 'P', 'D'}, GBA_CART_RTC, GBA_BACKUP_FLASH_128K},
+ {{'A', 'X', 'P', 'F'}, GBA_CART_RTC, GBA_BACKUP_FLASH_128K},
+ {{'B', 'P', 'E', 'J'}, GBA_CART_RTC, GBA_BACKUP_FLASH_128K},
+ {{'B', 'P', 'E', 'E'}, GBA_CART_RTC, GBA_BACKUP_FLASH_128K},
+ {{'B', 'P', 'E', 'P'}, GBA_CART_RTC, GBA_BACKUP_FLASH_128K},
+ {{'B', 'P', 'E', 'I'}, GBA_CART_RTC, GBA_BACKUP_FLASH_128K},
+ {{'B', 'P', 'E', 'S'}, GBA_CART_RTC, GBA_BACKUP_FLASH_128K},
+ {{'B', 'P', 'E', 'D'}, GBA_CART_RTC, GBA_BACKUP_FLASH_128K},
+ {{'B', 'P', 'E', 'F'}, GBA_CART_RTC, GBA_BACKUP_FLASH_128K},
+ {{'B', 'R', '4', 'J'}, GBA_CART_RTC, GBA_BACKUP_FLASH_64K },
+ {{'B', 'K', 'A', 'J'}, GBA_CART_RTC, GBA_BACKUP_FLASH_128K},
+};
+
+static const GbaCartridgeProfile *GbaFindCartridgeProfile(const UINT8 *rom, size_t romSize)
+{
+ if (rom == NULL || romSize < 0xb3 || rom[0xb2] != 0x96)
+ return NULL;
+
+ for (UINT32 i = 0; i < sizeof(GbaCartridgeProfiles) / sizeof(GbaCartridgeProfiles[0]); i++) {
+ if (memcmp(rom + 0xac, GbaCartridgeProfiles[i].gameCode, 4) == 0) {
+ return &GbaCartridgeProfiles[i];
+ }
+ }
+ return NULL;
+}
+
+static UINT32 GbaDetectCartridgeFeatures(const UINT8 *rom, size_t romSize)
+{
+ const GbaCartridgeProfile *profile = GbaFindCartridgeProfile(rom, romSize);
+ return profile ? profile->features : 0;
+}
+
+static UINT8 GbaDetectCartridgeBackupType(const UINT8 *rom, size_t romSize)
+{
+ const GbaCartridgeProfile *profile = GbaFindCartridgeProfile(rom, romSize);
+ return profile ? profile->backupType : 0;
+}
+
+static void GbaCoreClearPresentation(GbaCore *core)
+{
+ if (core == NULL)
+ return;
+ core->host.audio_ring_buff.read_ptr = 0;
+ core->host.audio_ring_buff.write_ptr = 0;
+}
+
+static void GbaCoreApplyCartridgeFeatures(GbaCore *core)
+{
+ core->state.cart.features = core->cartridgeFeatures;
+ if (core->cartridgeBackupType != GBA_BACKUP_NONE) {
+ core->state.cart.backup_type = core->cartridgeBackupType;
+ gba_setup_flash_id(&core->state);
+ }
+ gba_gpio_update_rumble(&core->state);
+ core->host.joy.rumble = core->state.cart.gpio.rumble;
+}
+
+static void GbaCoreApplyInput(GbaCore *core, const GbaInput *input)
+{
+ memset(core->host.joy.inputs, 0, sizeof(core->host.joy.inputs));
+ core->host.joy.gyro_z = 0;
+ core->host.joy.tilt_x = 0;
+ core->host.joy.tilt_y = 0;
+ if (input == NULL)
+ return;
+ core->host.joy.inputs[SE_KEY_A ] = (input->buttons >> GBA_BUTTON_A ) & 1;
+ core->host.joy.inputs[SE_KEY_B ] = (input->buttons >> GBA_BUTTON_B ) & 1;
+ core->host.joy.inputs[SE_KEY_SELECT] = (input->buttons >> GBA_BUTTON_SELECT) & 1;
+ core->host.joy.inputs[SE_KEY_START ] = (input->buttons >> GBA_BUTTON_START ) & 1;
+ core->host.joy.inputs[SE_KEY_RIGHT ] = (input->buttons >> GBA_BUTTON_RIGHT ) & 1;
+ core->host.joy.inputs[SE_KEY_LEFT ] = (input->buttons >> GBA_BUTTON_LEFT ) & 1;
+ core->host.joy.inputs[SE_KEY_UP ] = (input->buttons >> GBA_BUTTON_UP ) & 1;
+ core->host.joy.inputs[SE_KEY_DOWN ] = (input->buttons >> GBA_BUTTON_DOWN ) & 1;
+ core->host.joy.inputs[SE_KEY_R ] = (input->buttons >> GBA_BUTTON_R ) & 1;
+ core->host.joy.inputs[SE_KEY_L ] = (input->buttons >> GBA_BUTTON_L ) & 1;
+ core->host.joy.solar_sensor = input->solar / 255.0f;
+ core->host.joy.gyro_z = input->gyroZ;
+ core->host.joy.tilt_x = input->tiltX;
+ core->host.joy.tilt_y = input->tiltY;
+}
+
+INT32 GbaCoreInit(GbaCore **core)
+{
+ if (core == NULL)
+ return 1;
+ *core = (GbaCore *)BurnMalloc(sizeof(GbaCore));
+ if (*core == NULL)
+ return 1;
+ memset(*core, 0, sizeof(GbaCore));
+ (*core)->host.render_frame = true;
+ (*core)->host.capture_audio = true;
+ return 0;
+}
+
+void GbaCoreExit(GbaCore **core)
+{
+ if (core == NULL || *core == NULL)
+ return;
+ gba_unload(&(*core)->state, &(*core)->scratch);
+ if ((*core)->ownsRom)
+ BurnFree((*core)->rom);
+ BurnFree(*core);
+}
+
+INT32 GbaCoreLoadRom(GbaCore *core, const UINT8 *rom, size_t romSize, const GbaRtcSeed *rtcSeed)
+{
+ if (core == NULL || rom == NULL || romSize == 0 || romSize > 32 * 1024 * 1024)
+ return 1;
+ const size_t allocSize = romSize < 0x100 ? 0x100 : romSize;
+ UINT8 *romCopy = (UINT8 *)BurnMalloc((INT32)allocSize);
+ if (romCopy == NULL)
+ return 1;
+ memset(romCopy, 0xff, allocSize);
+ memcpy(romCopy, rom, romSize);
+ if (core->ownsRom)
+ BurnFree(core->rom);
+ core->rom = romCopy;
+ core->romSize = romSize;
+ core->ownsRom = true;
+ core->cartridgeFeatures = GbaDetectCartridgeFeatures(core->rom, core->romSize);
+ core->cartridgeBackupType = GbaDetectCartridgeBackupType(core->rom, core->romSize);
+ rom = core->rom;
+ core->host.rom_data = core->rom;
+ core->host.rom_size = romSize;
+ core->host.bios_data = core->externalBiosLoaded ? core->externalBios : NULL;
+ core->host.bios_size = core->externalBiosLoaded ? sizeof(core->externalBios) : 0;
+ core->host.rom_loaded = true;
+ strcpy(core->host.rom_path, "fbneo.gba");
+ gba_host_loading = &core->host;
+ const bool loaded = gba_load_rom(&core->host, &core->state, &core->scratch);
+ gba_host_loading = NULL;
+ if (!loaded)
+ return 1;
+ gba_rtc_civil_t seed;
+ memset(&seed, 0, sizeof(seed));
+ if (rtcSeed) {
+ seed.year = rtcSeed->year;
+ seed.month = rtcSeed->month;
+ seed.day = rtcSeed->day;
+ seed.weekday = rtcSeed->weekday;
+ seed.hour = rtcSeed->hour;
+ seed.minute = rtcSeed->minute;
+ seed.second = rtcSeed->second;
+ }
+ gba_rtc_cold_init(&core->state.rtc, rtcSeed ? &seed : NULL);
+ GbaCoreApplyCartridgeFeatures(core);
+ GbaCoreRebind(core);
+ GbaCoreClearPresentation(core);
+ return 0;
+}
+
+INT32 GbaCoreLoadBios(GbaCore *core, const UINT8 *bios, size_t biosSize)
+{
+ if (core == NULL || bios == NULL || biosSize != sizeof(core->externalBios))
+ return 1;
+ memcpy(core->externalBios, bios, sizeof(core->externalBios));
+ core->externalBiosLoaded = true;
+ return 0;
+}
+
+INT32 GbaCoreReset(GbaCore *core)
+{
+ if (core == NULL || core->rom == NULL)
+ return 1;
+ UINT8 battery[GBA_BATTERY_CAPACITY];
+ INT64 rtcSeconds = core->state.rtc.rtc_seconds;
+ INT64 rtcHostSeconds = core->state.rtc.host_seconds;
+ UINT8 rtcStatus = core->state.rtc.status;
+ memcpy(battery, core->state.mem.cart_backup, sizeof(battery));
+ core->host.rom_data = core->rom;
+ core->host.rom_size = core->romSize;
+ core->host.bios_data = core->externalBiosLoaded ? core->externalBios : NULL;
+ core->host.bios_size = core->externalBiosLoaded ? sizeof(core->externalBios) : 0;
+ gba_host_loading = &core->host;
+ const bool loaded = gba_load_rom(&core->host, &core->state, &core->scratch);
+ gba_host_loading = NULL;
+ if (!loaded)
+ return 1;
+ memcpy(core->state.mem.cart_backup, battery, sizeof(battery));
+ core->state.rtc.rtc_seconds = rtcSeconds;
+ core->state.rtc.host_seconds = rtcHostSeconds;
+ core->state.rtc.status = rtcStatus;
+ gba_rtc_transport_reset(&core->state.rtc);
+ core->state.rtc.last_pins = 0;
+ GbaCoreApplyCartridgeFeatures(core);
+ GbaCoreRebind(core);
+ GbaCoreClearAudio(core);
+ return 0;
+}
+
+void GbaCoreSetInput(GbaCore *core, const GbaInput *input)
+{
+ if (core != NULL)
+ GbaCoreApplyInput(core, input);
+}
+
+INT32 GbaCoreConfigureAudio(GbaCore *core, double sourceRate, INT32 outputFrames, INT32 captureAudio)
+{
+ if (core == NULL)
+ return 1;
+ if (sourceRate < 0.0 || outputFrames < 0)
+ return 1;
+ if ((sourceRate <= 0.0) != (outputFrames <= 0))
+ return 1;
+ core->host.capture_audio = captureAudio != 0;
+ if (sourceRate != core->sourceRate || outputFrames != core->outputFrames) {
+ core->sourceRate = sourceRate;
+ core->outputFrames = outputFrames;
+ core->host.audio_sample_rate = sourceRate;
+ GbaCoreClearPresentation(core);
+ }
+ return 0;
+}
+
+INT32 GbaCoreRunFrame(GbaCore *core)
+{
+ if (core == NULL || core->rom == NULL)
+ return 1;
+ core->host.render_frame = true;
+ gba_tick(&core->host, &core->state, &core->scratch);
+ return 0;
+}
+
+UINT32 GbaCoreGetCartridgeFeatures(const GbaCore *core)
+{
+ return core == NULL ? 0 : core->cartridgeFeatures;
+}
+
+UINT8 GbaCoreGetRumbleOutput(const GbaCore *core)
+{
+ if (core == NULL || !(core->state.cart.features & GBA_CART_RUMBLE))
+ return 0;
+ return core->state.cart.gpio.rumble;
+}
+
+const UINT32 *GbaCoreGetFramebuffer(const GbaCore *core)
+{
+ return core == NULL ? NULL : (const UINT32 *)core->scratch.framebuffer;
+}
+
+UINT32 GbaCoreGetFramebufferPitch()
+{
+ return GBA_WIDTH * sizeof(UINT32);
+}
+
+static INT32 GbaCoreSourceFramesAvailable(GbaCore *core)
+{
+ return (INT32)(sb_ring_buffer_size(&core->host.audio_ring_buff) / 2);
+}
+
+INT32 GbaCoreRenderAudio(GbaCore *core, INT16 *stereo, INT32 frames)
+{
+ if (core == NULL || frames <= 0)
+ return 0;
+ if (core->sourceRate <= 0.0) {
+ if (stereo != NULL)
+ memset(stereo, 0, frames * 2 * sizeof(INT16));
+ return frames;
+ }
+
+ sb_ring_buffer_t *ring = &core->host.audio_ring_buff;
+ INT32 available = GbaCoreSourceFramesAvailable(core);
+ INT32 produced = available < frames ? available : frames;
+ for (INT32 i = 0; i < produced; i++) {
+ UINT32 left = ring->read_ptr++ % SB_AUDIO_RING_BUFFER_SIZE;
+ UINT32 right = ring->read_ptr++ % SB_AUDIO_RING_BUFFER_SIZE;
+ if (stereo != NULL) {
+ stereo[i * 2 + 0] = ring->data[left];
+ stereo[i * 2 + 1] = ring->data[right];
+ }
+ }
+ if (produced < frames) {
+ INT32 missing = frames - produced;
+ if (stereo != NULL)
+ memset(stereo + produced * 2, 0, missing * 2 * sizeof(INT16));
+ ring->read_ptr += (UINT32)missing * 2;
+ ring->write_ptr += (UINT32)missing * 2;
+ }
+ return frames;
+}
+
+void GbaCoreClearAudio(GbaCore *core)
+{
+ GbaCoreClearPresentation(core);
+}
+
+UINT8 *GbaCoreGetBatteryData(GbaCore *core)
+{
+ return core == NULL ? NULL : core->state.mem.cart_backup;
+}
+
+const UINT8 *GbaCoreGetBatteryDataConst(const GbaCore *core)
+{
+ return core == NULL ? NULL : core->state.mem.cart_backup;
+}
+
+size_t GbaCoreGetBatteryCapacity()
+{
+ return GBA_BATTERY_CAPACITY;
+}
+
+size_t GbaCoreGetBatterySize(const GbaCore *core)
+{
+ if (core == NULL) return 0;
+ switch (core->state.cart.backup_type) {
+ case GBA_BACKUP_EEPROM_512B: return 512;
+ case GBA_BACKUP_EEPROM:
+ case GBA_BACKUP_EEPROM_8KB: return 8 * 1024;
+ case GBA_BACKUP_SRAM: return 32 * 1024;
+ case GBA_BACKUP_FLASH_64K: return 64 * 1024;
+ case GBA_BACKUP_FLASH_128K: return 128 * 1024;
+ default: return 0;
+ }
+}
+
+INT32 GbaCoreLoadBattery(GbaCore *core, const UINT8 *data, size_t size)
+{
+ if (core == NULL || data == NULL || size > GBA_BATTERY_CAPACITY)
+ return 1;
+ memset(core->state.mem.cart_backup, 0xff, GBA_BATTERY_CAPACITY);
+ memcpy(core->state.mem.cart_backup, data, size);
+ core->state.cart.backup_is_dirty = false;
+ return 0;
+}
+
+INT32 GbaCoreBatteryDirty(const GbaCore *core)
+{
+ return core != NULL && core->state.cart.backup_is_dirty;
+}
+
+void GbaCoreClearBatteryDirty(GbaCore *core)
+{
+ if (core != NULL) core->state.cart.backup_is_dirty = false;
+}
+
+size_t GbaCoreStateSize()
+{
+ return sizeof(gba_t);
+}
+
+INT32 GbaCoreSaveState(const GbaCore *core, void *data, size_t size)
+{
+ if (core == NULL || data == NULL || size < sizeof(gba_t))
+ return 1;
+ memcpy(data, &core->state, sizeof(gba_t));
+ UINT8 *state = (UINT8 *)data;
+#define GBA_CLEAR_STATE_FIELD(type, base, field) memset(state + (base) + offsetof(type, field), 0, sizeof(((type *)0)->field))
+ const size_t mem = offsetof(gba_t, mem);
+ const size_t cpu = offsetof(gba_t, cpu);
+ GBA_CLEAR_STATE_FIELD(gba_mem_t, mem, bios);
+ GBA_CLEAR_STATE_FIELD(gba_mem_t, mem, cart_rom);
+ GBA_CLEAR_STATE_FIELD(gba_mem_t, mem, cart_backup);
+ GBA_CLEAR_STATE_FIELD(gba_t, 0, framebuffer);
+ GBA_CLEAR_STATE_FIELD(arm7_t, cpu, user_data);
+ GBA_CLEAR_STATE_FIELD(arm7_t, cpu, log_cmp_file);
+ GBA_CLEAR_STATE_FIELD(arm7_t, cpu, read8);
+ GBA_CLEAR_STATE_FIELD(arm7_t, cpu, read16);
+ GBA_CLEAR_STATE_FIELD(arm7_t, cpu, read32);
+ GBA_CLEAR_STATE_FIELD(arm7_t, cpu, read16_seq);
+ GBA_CLEAR_STATE_FIELD(arm7_t, cpu, read32_seq);
+ GBA_CLEAR_STATE_FIELD(arm7_t, cpu, write8);
+ GBA_CLEAR_STATE_FIELD(arm7_t, cpu, write16);
+ GBA_CLEAR_STATE_FIELD(arm7_t, cpu, write32);
+ GBA_CLEAR_STATE_FIELD(arm7_t, cpu, coprocessor_read);
+ GBA_CLEAR_STATE_FIELD(arm7_t, cpu, coprocessor_write);
+ GBA_CLEAR_STATE_FIELD(arm7_t, cpu, trigger_breakpoint);
+#undef GBA_CLEAR_STATE_FIELD
+ return 0;
+}
+
+INT32 GbaCoreLoadState(GbaCore *core, const void *data, size_t size, INT32 preserveAudio)
+{
+ if (core == NULL || data == NULL || size < sizeof(gba_t) || core->rom == NULL)
+ return 1;
+ UINT8 battery[GBA_BATTERY_CAPACITY];
+ memcpy(battery, core->state.mem.cart_backup, sizeof(battery));
+ memcpy(&core->state, data, sizeof(gba_t));
+ memcpy(core->state.mem.cart_backup, battery, sizeof(battery));
+ GbaCoreApplyCartridgeFeatures(core);
+ GbaCoreRebind(core);
+ if (!preserveAudio)
+ GbaCoreClearAudio(core);
+ return 0;
+}
+
+void GbaCoreRebind(GbaCore *core)
+{
+ if (core == NULL)
+ return;
+ core->host.rom_data = core->rom;
+ core->host.rom_size = core->romSize;
+ core->host.bios_data = core->externalBiosLoaded ? core->externalBios : NULL;
+ core->host.bios_size = core->externalBiosLoaded ? sizeof(core->externalBios) : 0;
+ gba_ptrs_init(&core->state, &core->scratch, core->rom);
+ core->state.cpu.trigger_breakpoint = gba_cpu_trigger_breakpoint;
+}
diff --git a/src/burn/drv/gba/gba.h b/src/burn/drv/gba/gba.h
new file mode 100644
index 000000000..217411d53
--- /dev/null
+++ b/src/burn/drv/gba/gba.h
@@ -0,0 +1,104 @@
+// FBNeo GBA core host API.
+// Core implementation derived from SkyEmu/Cult-of-GBA; see license.txt.
+
+#pragma once
+
+#ifndef GBA_CORE_H
+#define GBA_CORE_H
+
+#ifndef GBA_STANDALONE_TYPES
+#include "burn.h"
+#else
+#include
+typedef uint8_t UINT8;
+typedef int8_t INT8;
+typedef int16_t INT16;
+typedef uint16_t UINT16;
+typedef int32_t INT32;
+typedef uint32_t UINT32;
+typedef int64_t INT64;
+typedef uint64_t UINT64;
+#endif
+#include
+
+#define GBA_WIDTH 240
+#define GBA_HEIGHT 160
+#define GBA_MASTER_CLOCK 16777216
+#define GBA_BATTERY_CAPACITY (128 * 1024)
+#define GBA_SOLAR_LEVEL_MAX 10
+
+struct GbaCore;
+
+enum GbaButton {
+ GBA_BUTTON_A = 0,
+ GBA_BUTTON_B,
+ GBA_BUTTON_SELECT,
+ GBA_BUTTON_START,
+ GBA_BUTTON_RIGHT,
+ GBA_BUTTON_LEFT,
+ GBA_BUTTON_UP,
+ GBA_BUTTON_DOWN,
+ GBA_BUTTON_R,
+ GBA_BUTTON_L,
+};
+
+enum GbaCartridgeFeature {
+ GBA_CART_RTC = 1 << 0,
+ GBA_CART_SOLAR = 1 << 1,
+ GBA_CART_RUMBLE = 1 << 2,
+ GBA_CART_GYRO = 1 << 3,
+ GBA_CART_TILT = 1 << 4,
+};
+
+struct GbaInput {
+ UINT16 buttons;
+ UINT8 solar;
+ INT32 gyroZ;
+ INT32 tiltX;
+ INT32 tiltY;
+};
+
+INT32 GbaMotionAxisToInput(UINT16 axis);
+UINT8 GbaSolarLevelToInput(UINT8 level);
+UINT8 GbaSolarLegacyToLevel(UINT16 legacy);
+
+struct GbaRtcSeed {
+ UINT16 year;
+ UINT8 month;
+ UINT8 day;
+ UINT8 weekday;
+ UINT8 hour;
+ UINT8 minute;
+ UINT8 second;
+};
+
+INT32 GbaCoreInit(GbaCore **core);
+void GbaCoreExit(GbaCore **core);
+INT32 GbaCoreLoadRom(GbaCore *core, const UINT8 *rom, size_t romSize, const GbaRtcSeed *rtcSeed);
+INT32 GbaCoreLoadBios(GbaCore *core, const UINT8 *bios, size_t biosSize);
+INT32 GbaCoreReset(GbaCore *core);
+void GbaCoreSetInput(GbaCore *core, const GbaInput *input);
+INT32 GbaCoreConfigureAudio(GbaCore *core, double sourceRate, INT32 outputFrames, INT32 captureAudio);
+INT32 GbaCoreRunFrame(GbaCore *core);
+UINT32 GbaCoreGetCartridgeFeatures(const GbaCore *core);
+UINT8 GbaCoreGetRumbleOutput(const GbaCore *core);
+
+const UINT32* GbaCoreGetFramebuffer(const GbaCore* core);
+UINT32 GbaCoreGetFramebufferPitch();
+INT32 GbaCoreRenderAudio(GbaCore *core, INT16 *stereo, INT32 frames);
+void GbaCoreClearAudio(GbaCore *core);
+
+UINT8* GbaCoreGetBatteryData(GbaCore* core);
+const UINT8* GbaCoreGetBatteryDataConst(const GbaCore* core);
+size_t GbaCoreGetBatteryCapacity();
+size_t GbaCoreGetBatterySize(const GbaCore *core);
+INT32 GbaCoreLoadBattery(GbaCore *core, const UINT8 *data, size_t size);
+INT32 GbaCoreBatteryDirty(const GbaCore *core);
+void GbaCoreClearBatteryDirty(GbaCore *core);
+
+size_t GbaCoreStateSize();
+INT32 GbaCoreSaveState(const GbaCore *core, void *data, size_t size);
+INT32 GbaCoreLoadState(GbaCore *core, const void *data, size_t size, INT32 preserveAudio = 0);
+void GbaCoreRebind(GbaCore *core);
+
+#endif
diff --git a/src/burn/drv/gba/gba_impl.h b/src/burn/drv/gba/gba_impl.h
new file mode 100644
index 000000000..fe596f677
--- /dev/null
+++ b/src/burn/drv/gba/gba_impl.h
@@ -0,0 +1,4166 @@
+#pragma once
+
+#ifndef GBA_CORE_IMPL_H
+#define GBA_CORE_IMPL_H 1
+
+#include "host.h"
+#include
+#include
+#include "arm7.h"
+#include "bios.h"
+#include "rtc.h"
+#define LR 14
+#define PC 15
+#define CPSR 16
+#define SPSR 17
+#define GBA_LCD_W 240
+#define GBA_LCD_H 160
+#define GBA_SWAPCHAIN_SIZE 4
+#define GBA_AUDIO_DMA_ACTIVATE_THRESHOLD 12
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// MMIO Register listing from GBATEK (https://problemkaputt.de/gbatek.htm#gbamemorymap) //
+//////////////////////////////////////////////////////////////////////////////////////////
+// LCD MMIO Registers
+#define GBA_DISPCNT 0x4000000 /* R/W LCD Control */
+#define GBA_GREENSWP 0x4000002 /* R/W Undocumented - Green Swap */
+#define GBA_DISPSTAT 0x4000004 /* R/W General LCD Status (STAT,LYC) */
+#define GBA_VCOUNT 0x4000006 /* R Vertical Counter (LY) */
+#define GBA_BG0CNT 0x4000008 /* R/W BG0 Control */
+#define GBA_BG1CNT 0x400000A /* R/W BG1 Control */
+#define GBA_BG2CNT 0x400000C /* R/W BG2 Control */
+#define GBA_BG3CNT 0x400000E /* R/W BG3 Control */
+#define GBA_BG0HOFS 0x4000010 /* W BG0 X-Offset */
+#define GBA_BG0VOFS 0x4000012 /* W BG0 Y-Offset */
+#define GBA_BG1HOFS 0x4000014 /* W BG1 X-Offset */
+#define GBA_BG1VOFS 0x4000016 /* W BG1 Y-Offset */
+#define GBA_BG2HOFS 0x4000018 /* W BG2 X-Offset */
+#define GBA_BG2VOFS 0x400001A /* W BG2 Y-Offset */
+#define GBA_BG3HOFS 0x400001C /* W BG3 X-Offset */
+#define GBA_BG3VOFS 0x400001E /* W BG3 Y-Offset */
+#define GBA_BG2PA 0x4000020 /* W BG2 Rotation/Scaling Parameter A (dx) */
+#define GBA_BG2PB 0x4000022 /* W BG2 Rotation/Scaling Parameter B (dmx) */
+#define GBA_BG2PC 0x4000024 /* W BG2 Rotation/Scaling Parameter C (dy) */
+#define GBA_BG2PD 0x4000026 /* W BG2 Rotation/Scaling Parameter D (dmy) */
+#define GBA_BG2X 0x4000028 /* W BG2 Reference Point X-Coordinate */
+#define GBA_BG2Y 0x400002C /* W BG2 Reference Point Y-Coordinate */
+#define GBA_BG3PA 0x4000030 /* W BG3 Rotation/Scaling Parameter A (dx) */
+#define GBA_BG3PB 0x4000032 /* W BG3 Rotation/Scaling Parameter B (dmx) */
+#define GBA_BG3PC 0x4000034 /* W BG3 Rotation/Scaling Parameter C (dy) */
+#define GBA_BG3PD 0x4000036 /* W BG3 Rotation/Scaling Parameter D (dmy) */
+#define GBA_BG3X 0x4000038 /* W BG3 Reference Point X-Coordinate */
+#define GBA_BG3Y 0x400003C /* W BG3 Reference Point Y-Coordinate */
+#define GBA_WIN0H 0x4000040 /* W Window 0 Horizontal Dimensions */
+#define GBA_WIN1H 0x4000042 /* W Window 1 Horizontal Dimensions */
+#define GBA_WIN0V 0x4000044 /* W Window 0 Vertical Dimensions */
+#define GBA_WIN1V 0x4000046 /* W Window 1 Vertical Dimensions */
+#define GBA_WININ 0x4000048 /* R/W Inside of Window 0 and 1 */
+#define GBA_WINOUT 0x400004A /* R/W Inside of OBJ Window & Outside of Windows */
+#define GBA_MOSAIC 0x400004C /* W Mosaic Size */
+#define GBA_BLDCNT 0x4000050 /* R/W Color Special Effects Selection */
+#define GBA_BLDALPHA 0x4000052 /* R/W Alpha Blending Coefficients */
+#define GBA_BLDY 0x4000054 /* W Brightness (Fade-In/Out) Coefficient */
+
+// Sound Registers
+#define GBA_SOUND1CNT_L 0x4000060 /* R/W Channel 1 Sweep register (NR10) */
+#define GBA_SOUND1CNT_H 0x4000062 /* R/W Channel 1 Duty/Length/Envelope (NR11, NR12) */
+#define GBA_SOUND1CNT_X 0x4000064 /* R/W Channel 1 Frequency/Control (NR13, NR14) */
+#define GBA_SOUND2CNT_L 0x4000068 /* R/W Channel 2 Duty/Length/Envelope (NR21, NR22) */
+#define GBA_SOUND2CNT_H 0x400006C /* R/W Channel 2 Frequency/Control (NR23, NR24) */
+#define GBA_SOUND3CNT_L 0x4000070 /* R/W Channel 3 Stop/Wave RAM select (NR30) */
+#define GBA_SOUND3CNT_H 0x4000072 /* R/W Channel 3 Length/Volume (NR31, NR32) */
+#define GBA_SOUND3CNT_X 0x4000074 /* R/W Channel 3 Frequency/Control (NR33, NR34) */
+#define GBA_SOUND4CNT_L 0x4000078 /* R/W Channel 4 Length/Envelope (NR41, NR42) */
+#define GBA_SOUND4CNT_H 0x400007C /* R/W Channel 4 Frequency/Control (NR43, NR44) */
+#define GBA_SOUNDCNT_L 0x4000080 /* R/W Control Stereo/Volume/Enable (NR50, NR51) */
+#define GBA_SOUNDCNT_H 0x4000082 /* R/W Control Mixing/DMA Control */
+#define GBA_SOUNDCNT_X 0x4000084 /* R/W Control Sound on/off (NR52) */
+#define GBA_SOUNDBIAS 0x4000088 /* BIOS Sound PWM Control */
+#define GBA_WAVE_RAM 0x4000090 /* R/W Channel 3 Wave Pattern RAM (2 banks!!) */
+#define GBA_FIFO_A 0x40000A0 /* W Channel A FIFO, Data 0-3 */
+#define GBA_FIFO_B 0x40000A4 /* W Channel B FIFO, Data 0-3 */
+
+// DMA Transfer Channels
+#define GBA_DMA0SAD 0x40000B0 /* W DMA 0 Source Address */
+#define GBA_DMA0DAD 0x40000B4 /* W DMA 0 Destination Address */
+#define GBA_DMA0CNT_L 0x40000B8 /* W DMA 0 Word Count */
+#define GBA_DMA0CNT_H 0x40000BA /* R/W DMA 0 Control */
+#define GBA_DMA1SAD 0x40000BC /* W DMA 1 Source Address */
+#define GBA_DMA1DAD 0x40000C0 /* W DMA 1 Destination Address */
+#define GBA_DMA1CNT_L 0x40000C4 /* W DMA 1 Word Count */
+#define GBA_DMA1CNT_H 0x40000C6 /* R/W DMA 1 Control */
+#define GBA_DMA2SAD 0x40000C8 /* W DMA 2 Source Address */
+#define GBA_DMA2DAD 0x40000CC /* W DMA 2 Destination Address */
+#define GBA_DMA2CNT_L 0x40000D0 /* W DMA 2 Word Count */
+#define GBA_DMA2CNT_H 0x40000D2 /* R/W DMA 2 Control */
+#define GBA_DMA3SAD 0x40000D4 /* W DMA 3 Source Address */
+#define GBA_DMA3DAD 0x40000D8 /* W DMA 3 Destination Address */
+#define GBA_DMA3CNT_L 0x40000DC /* W DMA 3 Word Count */
+#define GBA_DMA3CNT_H 0x40000DE /* R/W DMA 3 Control */
+
+// Timer Registers
+#define GBA_TM0CNT_L 0x4000100 /* R/W Timer 0 Counter/Reload */
+#define GBA_TM0CNT_H 0x4000102 /* R/W Timer 0 Control */
+#define GBA_TM1CNT_L 0x4000104 /* R/W Timer 1 Counter/Reload */
+#define GBA_TM1CNT_H 0x4000106 /* R/W Timer 1 Control */
+#define GBA_TM2CNT_L 0x4000108 /* R/W Timer 2 Counter/Reload */
+#define GBA_TM2CNT_H 0x400010A /* R/W Timer 2 Control */
+#define GBA_TM3CNT_L 0x400010C /* R/W Timer 3 Counter/Reload */
+#define GBA_TM3CNT_H 0x400010E /* R/W Timer 3 Control */
+
+// Serial Communication (1)
+#define GBA_SIODATA32 0x4000120 /*R/W SIO Data (Normal-32bit Mode; shared with below) */
+#define GBA_SIOMULTI0 0x4000120 /*R/W SIO Data 0 (Parent) (Multi-Player Mode) */
+#define GBA_SIOMULTI1 0x4000122 /*R/W SIO Data 1 (1st Child) (Multi-Player Mode) */
+#define GBA_SIOMULTI2 0x4000124 /*R/W SIO Data 2 (2nd Child) (Multi-Player Mode) */
+#define GBA_SIOMULTI3 0x4000126 /*R/W SIO Data 3 (3rd Child) (Multi-Player Mode) */
+#define GBA_SIOCNT 0x4000128 /*R/W SIO Control Register */
+#define GBA_SIOMLT_SEND 0x400012A /*R/W SIO Data (Local of MultiPlayer; shared below) */
+#define GBA_SIODATA8 0x400012A /*R/W SIO Data (Normal-8bit and UART Mode) */
+
+// Keypad Input
+#define GBA_KEYINPUT 0x4000130 /* R Key Status */
+#define GBA_KEYCNT 0x4000132 /* R/W Key Interrupt Control */
+
+// Serial Communication (2)
+#define GBA_RCNT 0x4000134 /* R/W SIO Mode Select/General Purpose Data */
+#define GBA_IR 0x4000136 /* - Ancient - Infrared Register (Prototypes only) */
+#define GBA_JOYCNT 0x4000140 /* R/W SIO JOY Bus Control */
+#define GBA_JOY_RECV 0x4000150 /* R/W SIO JOY Bus Receive Data */
+#define GBA_JOY_TRANS 0x4000154 /* R/W SIO JOY Bus Transmit Data */
+#define GBA_JOYSTAT 0x4000158 /* R/? SIO JOY Bus Receive Status */
+
+// Interrupt, Waitstate, and Power-Down Control
+#define GBA_IE 0x4000200 /* R/W IE Interrupt Enable Register */
+#define GBA_IF 0x4000202 /* R/W IF Interrupt Request Flags / IRQ Acknowledge */
+#define GBA_WAITCNT 0x4000204 /* R/W WAITCNT Game Pak Waitstate Control */
+#define GBA_IME 0x4000208 /* R/W IME Interrupt Master Enable Register */
+#define GBA_POSTFLG 0x4000300 /* R/W POSTFLG Undocumented - Post Boot Flag */
+#define GBA_HALTCNT 0x4000301 /* W HALTCNT Undocumented - Power Down Control */
+// #define GBA_? 0x4000410 /* ? ? Undocumented - Purpose Unknown / Bug ??? 0FFh */
+// #define GBA_? 0x4000800 /* R/W ? Undocumented - Internal Memory Control (R/W) */
+// #define GBA_? 0x4xx0800 /* R/W ? Mirrors of 4000800h (repeated each 64K) */
+// #define GBA_(3DS) 0x4700000 /* W (3DS) Disable ARM7 bootrom overlay (3DS only) */
+
+mmio_reg_t gba_io_reg_desc[] = {
+ // Interrupt, Waitstate, and Power-Down Control
+ { GBA_IE, "IE", {
+ { 0, 1, "LCD V-Blank" },
+ { 1, 1, "LCD H-Blank" },
+ { 2, 1, "LCD V-Counter Match" },
+ { 3, 1, "Timer 0 Overflow" },
+ { 4, 1, "Timer 1 Overflow" },
+ { 5, 1, "Timer 2 Overflow" },
+ { 6, 1, "Timer 3 Overflow" },
+ { 7, 1, "Serial Communication" },
+ { 8, 1, "DMA 0" },
+ { 9, 1, "DMA 1" },
+ { 10, 1, "DMA 2" },
+ { 11, 1, "DMA 3" },
+ { 12, 1, "Keypad" },
+ { 13, 1, "Game Pak (ext)" },
+ } }, /* R/W IE Interrupt Enable Register */
+ { GBA_IF, "IF", {
+ { 0, 1, "LCD V-Blank" },
+ { 1, 1, "LCD H-Blank" },
+ { 2, 1, "LCD V-Counter Match" },
+ { 3, 1, "Timer 0 Overflow" },
+ { 4, 1, "Timer 1 Overflow" },
+ { 5, 1, "Timer 2 Overflow" },
+ { 6, 1, "Timer 3 Overflow" },
+ { 7, 1, "Serial Communication" },
+ { 8, 1, "DMA 0" },
+ { 9, 1, "DMA 1" },
+ { 10, 1, "DMA 2" },
+ { 11, 1, "DMA 3" },
+ { 12, 1, "Keypad" },
+ { 13, 1, "Game Pak (ext)" },
+ } }, /* R/W IF Interrupt Request Flags / IRQ Acknowledge */
+ { GBA_WAITCNT, "WAITCNT", {
+ { 0, 2, "SRAM Wait Control (0..3 = 4,3,2,8 cycles)" },
+ { 2, 2, "Wait State 0 First Access (0..3 = 4,3,2,8 cycles)" },
+ { 4, 1, "Wait State 0 Second Access (0..1 = 2,1 cycles)" },
+ { 5, 2, "Wait State 1 First Access (0..3 = 4,3,2,8 cycles)" },
+ { 7, 1, "Wait State 1 Second Access (0..1 = 4,1 cycles)" },
+ { 8, 2, "Wait State 2 First Access (0..3 = 4,3,2,8 cycles)" },
+ { 10, 1, "Wait State 2 Second Access (0..1 = 8,1 cycles)" },
+ { 11, 2, "PHI Terminal Output (0..3 = Disable, 4.19MHz, 8.38MHz, 16.78MHz)" },
+ { 14, 1, "Game Pak Prefetch Buffer (0=Disable, 1=Enable)" },
+ { 15, 1, "Game Pak Type Flag (0=GBA, 1=CGB) (IN35 signal)" },
+ } }, /* R/W WAITCNT Game Pak Waitstate Control */
+ { GBA_IME , "IME" , {0} }, /* R/W IME Interrupt Master Enable Register */
+ { GBA_POSTFLG, "POSTFLG", {0} }, /* R/W POSTFLG Undocumented - Post Boot Flag */
+ { GBA_HALTCNT, "HALTCNT", {0} },
+
+ { GBA_DISPCNT, "DISPCNT ", {
+ { 0, 3, "BG Mode (0-5=Video Mode 0-5, 6-7=Prohibited)" },
+ { 3, 1, "Reserved / CGB Mode (0=GBA, 1=CGB)" },
+ { 4, 1, "Display Frame Select (0-1=Frame 0-1)" },
+ { 5, 1, "H-Blank Interval Free (1=Allow access to OAM during H-Blank)" },
+ { 6, 1, "OBJ Character VRAM Mapping (0=2D, 1=1D)" },
+ { 7, 1, "Forced Blank (1=Allow FAST VRAM,Palette,OAM)" },
+ { 8, 1, "Screen Display BG0 (0=Off, 1=On)" },
+ { 9, 1, "Screen Display BG1 (0=Off, 1=On)" },
+ { 10, 1, "Screen Display BG2 (0=Off, 1=On)" },
+ { 11, 1, "Screen Display BG3 (0=Off, 1=On)" },
+ { 12, 1, "Screen Display OBJ (0=Off, 1=On)" },
+ { 13, 1, "Window 0 Display Flag (0=Off, 1=On)" },
+ { 14, 1, "Window 1 Display Flag (0=Off, 1=On)" },
+ { 15, 1, "OBJ Window Display Flag (0=Off, 1=On)" },
+ } },
+ { GBA_GREENSWP, "GREENSWP", {
+ { 0, 1, "Green Swap (0=Normal, 1=Swap)" },
+ } }, /* R/W Undocumented - Green Swap */
+ { GBA_DISPSTAT, "DISPSTAT", {
+ { 0, 1, "V-Blank flag (1=VBlank) (set in line 160..226; not 227)" },
+ { 1, 1, "H-Blank flag (1=HBlank) (toggled in all lines, 0..227)" },
+ { 2, 1, "V-Counter flag (1=Match) (set in selected line)" },
+ { 3, 1, "V-Blank IRQ Enable (1=Enable)" },
+ { 4, 1, "H-Blank IRQ Enable (1=Enable)" },
+ { 5, 1, "V-Counter IRQ Enable (1=Enable)" },
+ { 6, 1, "DSi: LCD Initialization Ready (0=Busy, 1=Ready)" },
+ { 7, 1, "NDS: MSB of V-Vcount Setting (LYC.Bit8) (0..262)" },
+ { 8, 8, "V-Count Setting (LYC) (0..227)" },
+ } }, /* R/W General LCD Status (STAT,LYC) */
+ { GBA_VCOUNT, "VCOUNT", { 0 } }, /* R Vertical Counter (LY) */
+ { GBA_BG0CNT, "BG0CNT", {
+ { 0, 2, "BG Priority (0-3, 0=Highest)" },
+ { 2, 2, "Character Base Block (0-3, in units of 16 KBytes) (=BG Tile Data)" },
+ { 4, 2, "NDS: MSBs of char base" },
+ { 6, 1, "Mosaic (0=Disable, 1=Enable)" },
+ { 7, 1, "Colors/Palettes (0=16/16, 1=256/1)" },
+ { 8, 5, "Screen Base Block (0-31, in units of 2 KBytes) (=BG Map Data)" },
+ { 13, 1, "BG0/BG1: (NDS: Ext Palette ) BG2/BG3: Overflow (0=Transp, 1=Wrap)" },
+ { 14, 2, "Screen Size (0-3)" },
+ } }, /* R/W BG0 Control */
+ { GBA_BG1CNT, "BG1CNT", {
+ { 0, 2, "BG Priority (0-3, 0=Highest)" },
+ { 2, 2, "Character Base Block (0-3, in units of 16 KBytes) (=BG Tile Data)" },
+ { 4, 2, "NDS: MSBs of char base" },
+ { 6, 1, "Mosaic (0=Disable, 1=Enable)" },
+ { 7, 1, "Colors/Palettes (0=16/16, 1=256/1)" },
+ { 8, 5, "Screen Base Block (0-31, in units of 2 KBytes) (=BG Map Data)" },
+ { 13, 1, "BG0/BG1: (NDS: Ext Palette ) BG2/BG3: Overflow (0=Transp, 1=Wrap)" },
+ { 14, 2, "Screen Size (0-3)" },
+ } }, /* R/W BG1 Control */
+ { GBA_BG2CNT, "BG2CNT", {
+ { 0, 2, "BG Priority (0-3, 0=Highest)" },
+ { 2, 2, "Character Base Block (0-3, in units of 16 KBytes) (=BG Tile Data)" },
+ { 4, 2, "NDS: MSBs of char base" },
+ { 6, 1, "Mosaic (0=Disable, 1=Enable)" },
+ { 7, 1, "Colors/Palettes (0=16/16, 1=256/1)" },
+ { 8, 5, "Screen Base Block (0-31, in units of 2 KBytes) (=BG Map Data)" },
+ { 13, 1, "BG0/BG1: (NDS: Ext Palette ) BG2/BG3: Overflow (0=Transp, 1=Wrap)" },
+ { 14, 2, "Screen Size (0-3)" },
+ } }, /* R/W BG2 Control */
+ { GBA_BG3CNT, "BG3CNT", {
+ { 0, 2, "BG Priority (0-3, 0=Highest)" },
+ { 2, 2, "Character Base Block (0-3, in units of 16 KBytes) (=BG Tile Data)" },
+ { 4, 2, "NDS: MSBs of char base" },
+ { 6, 1, "Mosaic (0=Disable, 1=Enable)" },
+ { 7, 1, "Colors/Palettes (0=16/16, 1=256/1)" },
+ { 8, 5, "Screen Base Block (0-31, in units of 2 KBytes) (=BG Map Data)" },
+ { 13, 1, "BG0/BG1: (NDS: Ext Palette ) BG2/BG3: Overflow (0=Transp, 1=Wrap)" },
+ { 14, 2, "Screen Size (0-3)" },
+ { 13, 1, "BG0/BG1: (NDS: Ext Palette ) BG2/BG3: Overflow (0=Transp, 1=Wrap)" },
+ { 14, 2, "Screen Size (0-3)"},
+ } }, /* R/W BG3 Control */
+ { GBA_BG0HOFS, "BG0HOFS", { 0 } }, /* W BG0 X-Offset */
+ { GBA_BG0VOFS, "BG0VOFS", { 0 } }, /* W BG0 Y-Offset */
+ { GBA_BG1HOFS, "BG1HOFS", { 0 } }, /* W BG1 X-Offset */
+ { GBA_BG1VOFS, "BG1VOFS", { 0 } }, /* W BG1 Y-Offset */
+ { GBA_BG2HOFS, "BG2HOFS", { 0 } }, /* W BG2 X-Offset */
+ { GBA_BG2VOFS, "BG2VOFS", { 0 } }, /* W BG2 Y-Offset */
+ { GBA_BG3HOFS, "BG3HOFS", { 0 } }, /* W BG3 X-Offset */
+ { GBA_BG3VOFS, "BG3VOFS", { 0 } }, /* W BG3 Y-Offset */
+ { GBA_BG2PA , "BG2PA" , { 0 } }, /* W BG2 Rotation/Scaling Parameter A (dx) */
+ { GBA_BG2PB , "BG2PB" , { 0 } }, /* W BG2 Rotation/Scaling Parameter B (dmx) */
+ { GBA_BG2PC , "BG2PC" , { 0 } }, /* W BG2 Rotation/Scaling Parameter C (dy) */
+ { GBA_BG2PD , "BG2PD" , { 0 } }, /* W BG2 Rotation/Scaling Parameter D (dmy) */
+ { GBA_BG2X , "BG2X" , { 0 } }, /* W BG2 Reference Point X-Coordinate */
+ { GBA_BG2Y , "BG2Y" , { 0 } }, /* W BG2 Reference Point Y-Coordinate */
+ { GBA_BG3PA , "BG3PA" , { 0 } }, /* W BG3 Rotation/Scaling Parameter A (dx) */
+ { GBA_BG3PB , "BG3PB" , { 0 } }, /* W BG3 Rotation/Scaling Parameter B (dmx) */
+ { GBA_BG3PC , "BG3PC" , { 0 } }, /* W BG3 Rotation/Scaling Parameter C (dy) */
+ { GBA_BG3PD , "BG3PD" , { 0 } }, /* W BG3 Rotation/Scaling Parameter D (dmy) */
+ { GBA_BG3X , "BG3X" , { 0 } }, /* W BG3 Reference Point X-Coordinate */
+ { GBA_BG3Y , "BG3Y" , { 0 } }, /* W BG3 Reference Point Y-Coordinate */
+ { GBA_WIN0H , "WIN0H" , {
+ { 0, 8, "X2, Rightmost coordinate of window, plus 1" },
+ { 8, 8, "X1, Leftmost coordinate of window" },
+ } }, /* W Window 0 Horizontal Dimensions */
+ { GBA_WIN1H, "WIN1H", {
+ { 0, 8, "X2, Rightmost coordinate of window, plus 1" },
+ { 8, 8, "X1, Leftmost coordinate of window" },
+ } }, /* W Window 1 Horizontal Dimensions */
+ { GBA_WIN0V, "WIN0V", {
+ { 0, 8, "Y2, Bottom-most coordinate of window, plus 1" },
+ { 8, 8, "Y1, Top-most coordinate of window" },
+ } }, /* W Window 0 Vertical Dimensions */
+ { GBA_WIN1V, "WIN1V", {
+ { 0, 8, "Y2, Bottom-most coordinate of window, plus 1" },
+ { 8, 8, "Y1, Top-most coordinate of window" },
+ } }, /* W Window 1 Vertical Dimensions */
+ { GBA_WININ, "WININ", {
+ { 0, 1, "Window 0 BG0 Enable Bits (0=No Display, 1=Display)" },
+ { 1, 1, "Window 0 BG1 Enable Bits (0=No Display, 1=Display)" },
+ { 2, 1, "Window 0 BG2 Enable Bits (0=No Display, 1=Display)" },
+ { 3, 1, "Window 0 BG3 Enable Bits (0=No Display, 1=Display)" },
+ { 4, 1, "Window 0 OBJ Enable Bit (0=No Display, 1=Display)" },
+ { 5, 1, "Window 0 Color Special Effect (0=Disable, 1=Enable)" },
+ { 8, 1, "Window 1 BG0 Enable Bits (0=No Display, 1=Display)" },
+ { 9, 1, "Window 1 BG1 Enable Bits (0=No Display, 1=Display)" },
+ { 10, 1, "Window 1 BG2 Enable Bits (0=No Display, 1=Display)" },
+ { 11, 1, "Window 1 BG3 Enable Bits (0=No Display, 1=Display)" },
+ { 12, 1, "Window 1 OBJ Enable Bit (0=No Display, 1=Display)" },
+ { 13, 1, "Window 1 Color Special Effect (0=Disable, 1=Enable)" },
+ } }, /* R/W Inside of Window 0 and 1 */
+ { GBA_WINOUT, "WINOUT", {
+ { 0, 1, "Window 0 BG0 Enable Bits (0=No Display, 1=Display)" },
+ { 1, 1, "Window 0 BG1 Enable Bits (0=No Display, 1=Display)" },
+ { 2, 1, "Window 0 BG2 Enable Bits (0=No Display, 1=Display)" },
+ { 3, 1, "Window 0 BG3 Enable Bits (0=No Display, 1=Display)" },
+ { 4, 1, "Window 0 OBJ Enable Bit (0=No Display, 1=Display)" },
+ { 5, 1, "Window 0 Color Special Effect (0=Disable, 1=Enable)" },
+ { 8, 1, "Window 1 BG0 Enable Bits (0=No Display, 1=Display)" },
+ { 9, 1, "Window 1 BG1 Enable Bits (0=No Display, 1=Display)" },
+ { 10, 1, "Window 1 BG2 Enable Bits (0=No Display, 1=Display)" },
+ { 11, 1, "Window 1 BG3 Enable Bits (0=No Display, 1=Display)" },
+ { 12, 1, "Window 1 OBJ Enable Bit (0=No Display, 1=Display)" },
+ { 13, 1, "Window 1 Color Special Effect (0=Disable, 1=Enable)" },
+ } }, /* R/W Inside of OBJ Window & Outside of Windows */
+ { GBA_MOSAIC, "MOSAIC", {
+ { 0, 4, "BG Mosaic H-Size (minus 1)" },
+ { 4, 4, "BG Mosaic V-Size (minus 1)" },
+ { 8, 4, "OBJ Mosaic H-Size (minus 1)" },
+ { 12, 4, "OBJ Mosaic V-Size (minus 1)" },
+ } }, /* W Mosaic Size */
+ { GBA_BLDCNT, "BLDCNT", {
+ { 0, 1, "BG0 1st Target Pixel (Background 0)" },
+ { 1, 1, "BG1 1st Target Pixel (Background 1)" },
+ { 2, 1, "BG2 1st Target Pixel (Background 2)" },
+ { 3, 1, "BG3 1st Target Pixel (Background 3)" },
+ { 4, 1, "OBJ 1st Target Pixel (Top-most OBJ pixel)" },
+ { 5, 1, "BD 1st Target Pixel (Backdrop)" },
+ { 6, 2, "Color Effect (0: None 1: Alpha 2: Lighten 3: Darken)" },
+ { 8, 1, "BG0 2nd Target Pixel (Background 0)" },
+ { 9, 1, "BG1 2nd Target Pixel (Background 1)" },
+ { 10, 1, "BG2 2nd Target Pixel (Background 2)" },
+ { 11, 1, "BG3 2nd Target Pixel (Background 3)" },
+ { 12, 1, "OBJ 2nd Target Pixel (Top-most OBJ pixel)" },
+ { 13, 1, "BD 2nd Target Pixel (Backdrop)" },
+ } }, /* R/W Color Special Effects Selection */
+ { GBA_BLDALPHA, "BLDALPHA", {
+ { 0, 4, "EVA Coef. (1st Target) (0..16 = 0/16..16/16, 17..31=16/16)" },
+ { 8, 4, "EVB Coef. (2nd Target) (0..16 = 0/16..16/16, 17..31=16/16)" },
+ } }, /* R/W Alpha Blending Coefficients */
+ { GBA_BLDY, "BLDY", { 0 } }, /* W Brightness (Fade-In/Out) Coefficient */
+
+ // Sound Registers
+ { GBA_SOUND1CNT_L, "SOUND1CNT_L", {
+ { 0, 3, "Number of sweep shift (n=0-7)" },
+ { 3, 1, "Sweep Frequency Direction (0=Increase, 1=Decrease)" },
+ { 4, 3, "Sweep Time; units of 7.8ms (0-7, min=7.8ms, max=54.7ms)" },
+ } }, /* R/W Channel 1 Sweep register (NR10) */
+ { GBA_SOUND1CNT_H, "SOUND1CNT_H", {
+ { 0, 6, "Sound length; units of (64-n)/256s (0-63)" },
+ { 6, 2, "Wave Pattern Duty (0-3, see below)" },
+ { 8, 3, "Envelope Step-Time; units of n/64s (1-7, 0=No Envelope)" },
+ { 11, 1, "Envelope Direction (0=Decrease, 1=Increase)" },
+ { 12, 4, "Initial Volume of envelope (1-15, 0=No Sound)" },
+ } }, /* R/W Channel 1 Duty/Length/Envelope (NR11, NR12) */
+ { GBA_SOUND1CNT_X, "SOUND1CNT_X", {
+ { 0, 11, "Frequency; 131072/(2048-n)Hz (0-2047)" },
+ { 14, 1, "Length Flag (1=Stop output when length in NR11 expires)" },
+ { 15, 1, "Initial (1=Restart Sound)" },
+ } }, /* R/W Channel 1 Frequency/Control (NR13, NR14) */
+ { GBA_SOUND2CNT_L, "SOUND2CNT_L", {
+ { 0, 6, "Sound length; units of (64-n)/256s (0-63)" },
+ { 6, 2, "Wave Pattern Duty (0-3, see below)" },
+ { 8, 3, "Envelope Step-Time; units of n/64s (1-7, 0=No Envelope)" },
+ { 11, 1, "Envelope Direction (0=Decrease, 1=Increase)" },
+ { 12, 4, "Initial Volume of envelope (1-15, 0=No Sound)" },
+ } }, /* R/W Channel 2 Duty/Length/Envelope (NR21, NR22) */
+ { GBA_SOUND2CNT_H, "SOUND2CNT_H", {
+ { 0, 11, "Frequency; 131072/(2048-n)Hz (0-2047)" },
+ { 14, 1, "Length Flag (1=Stop output when length in NR11 expires)" },
+ { 15, 1, "Initial (1=Restart Sound)" },
+ } }, /* R/W Channel 2 Frequency/Control (NR23, NR24) */
+ { GBA_SOUND3CNT_L, "SOUND3CNT_L", {
+ { 5, 1, "Wave RAM Dimension (0=One bank, 1=Two banks)" },
+ { 6, 1, "Wave RAM Bank Number (0-1, see below)" },
+ { 7, 1, "Sound Channel 3 Off (0=Stop, 1=Playback)" },
+ } }, /* R/W Channel 3 Stop/Wave RAM select (NR30) */
+ { GBA_SOUND3CNT_H, "SOUND3CNT_H", {
+ { 0, 8, "Sound length; units of (256-n)/256s (0-255)" },
+ { 13, 2, "Sound Volume (0=Mute/Zero, 1=100%, 2=50%, 3=25%)" },
+ { 15, 1, "Force Volume (0=Use above, 1=Force 75% regardless of above)" },
+ } }, /* R/W Channel 3 Length/Volume (NR31, NR32) */
+ { GBA_SOUND3CNT_X, "SOUND3CNT_X", {
+ { 0, 11, "Sample Rate; 2097152/(2048-n) Hz (0-2047)" },
+ { 14, 1, "Length Flag (1=Stop output when length in NR31 expires)" },
+ { 15, 1, "Initial (1=Restart Sound)" },
+ } }, /* R/W Channel 3 Frequency/Control (NR33, NR34) */
+ { GBA_SOUND4CNT_L, "SOUND4CNT_L", {
+ { 0, 6, "Sound length; units of (64-n)/256s (0-63)" },
+ { 8, 3, "Envelope Step-Time; units of n/64s (1-7, 0=No Envelope)" },
+ { 11, 1, "Envelope Direction (0=Decrease, 1=Increase)" },
+ { 12, 4, "Initial Volume of envelope (1-15, 0=No Sound)" },
+ } }, /* R/W Channel 4 Length/Envelope (NR41, NR42) */
+ { GBA_SOUND4CNT_H, "SOUND4CNT_H", {
+ { 0, 1, "Dividing Ratio of Frequencies (r)" },
+ { 3, 1, "Counter Step/Width (0=15 bits, 1=7 bits)" },
+ { 4, 1, "Shift Clock Frequency (s)" },
+ { 14, 1, "Length Flag (1=Stop output when length in NR41 expires)" },
+ { 15, 1, "Initial (1=Restart Sound)" },
+ } }, /* R/W Channel 4 Frequency/Control (NR43, NR44) */
+ { GBA_SOUNDCNT_L, "SOUNDCNT_L", {
+ { 0, 1, "Sound 1 Master Volume RIGHT" },
+ { 1, 1, "Sound 2 Master Volume RIGHT" },
+ { 2, 1, "Sound 3 Master Volume RIGHT" },
+ { 3, 1, "Sound 4 Master Volume RIGHT" },
+ { 4, 1, "Sound 1 Master Volume LEFT" },
+ { 5, 1, "Sound 2 Master Volume LEFT" },
+ { 6, 1, "Sound 3 Master Volume LEFT" },
+ { 7, 1, "Sound 4 Master Volume LEFT" },
+
+ { 8, 1, "Sound 1 Enable RIGHT" },
+ { 9, 1, "Sound 2 Enable RIGHT" },
+ { 10, 1, "Sound 3 Enable RIGHT" },
+ { 11, 1, "Sound 4 Enable RIGHT" },
+ { 12, 1, "Sound 1 Enable LEFT" },
+ { 13, 1, "Sound 2 Enable LEFT" },
+ { 14, 1, "Sound 3 Enable LEFT" },
+ { 15, 1, "Sound 4 Enable LEFT" },
+ } }, /* R/W Control Stereo/Volume/Enable (NR50, NR51) */
+ { GBA_SOUNDCNT_H, "SOUNDCNT_H", {
+ { 0, 2, "Sound # 1-4 Volume (0=25%, 1=50%, 2=100%, 3=Prohibited)" },
+ { 2, 1, "DMA Sound A Volume (0=50%, 1=100%)" },
+ { 3, 1, "DMA Sound B Volume (0=50%, 1=100%)" },
+ { 8, 1, "DMA Sound A Enable RIGHT (0=Disable, 1=Enable)" },
+ { 9, 1, "DMA Sound A Enable LEFT (0=Disable, 1=Enable)" },
+ { 10, 1, "DMA Sound A Timer Select (0=Timer 0, 1=Timer 1)" },
+ { 11, 1, "DMA Sound A Reset FIFO (1=Reset)" },
+ { 12, 1, "DMA Sound B Enable RIGHT (0=Disable, 1=Enable)" },
+ { 13, 1, "DMA Sound B Enable LEFT (0=Disable, 1=Enable)" },
+ { 14, 1, "DMA Sound B Timer Select (0=Timer 0, 1=Timer 1)" },
+ { 15, 1, "DMA Sound B Reset FIFO (1=Reset)" },
+ } }, /* R/W Control Mixing/DMA Control */
+ { GBA_SOUNDCNT_X, "SOUNDCNT_X", {
+ { 0, 1, "Sound 1 ON flag (Read Only)" },
+ { 1, 1, "Sound 2 ON flag (Read Only)" },
+ { 2, 1, "Sound 3 ON flag (Read Only)" },
+ { 3, 1, "Sound 4 ON flag (Read Only)" },
+ { 7, 1, "PSG/FIFO Master Enable (0=Disable, 1=Enable) (Read/Write)" },
+ } }, /* R/W Control Sound on/off (NR52) */
+ { GBA_SOUNDBIAS, "SOUNDBIAS", {
+ { 1, 9, "Bias Level (Default=100h, converting signed samples into unsigned)" },
+ { 14, 2, "Amplitude Resolution/Sampling Cycle (Default=0, see below)" },
+ } }, /* BIOS Sound PWM Control */
+ { GBA_WAVE_RAM, "WAVE_RAM" , { 0 } }, /* R/W Channel 3 Wave Pattern RAM (2 banks!!) */
+ { GBA_FIFO_A , "FIFO_A" , { 0 } }, /* W Channel A FIFO, Data 0-3 */
+ { GBA_FIFO_B , "FIFO_B" , { 0 } }, /* W Channel B FIFO, Data 0-3 */
+
+ // DMA Transfer Channels
+ { GBA_DMA0SAD , "DMA0SAD" , { 0 } }, /* W DMA 0 Source Address */
+ { GBA_DMA0DAD , "DMA0DAD" , { 0 } }, /* W DMA 0 Destination Address */
+ { GBA_DMA0CNT_L, "DMA0CNT_L", { 0 } }, /* W DMA 0 Word Count */
+ { GBA_DMA0CNT_H, "DMA0CNT_H", {
+ { 5, 2, "Dest Addr Control (0=Incr,1=Decr,2=Fixed,3=Incr/Reload)" },
+ { 7, 2, "Source Adr Control (0=Incr,1=Decr,2=Fixed,3=Prohibited)" },
+ { 9, 1, "DMA Repeat (0=Off, 1=On) (Must be zero if Bit 11 set)" },
+ { 10, 1, "DMA Transfer Type (0=16bit, 1=32bit)" },
+ { 12, 2, "DMA Start Timing (0=Immediately, 1=VBlank, 2=HBlank, 3=Prohibited)" },
+ { 14, 1, "IRQ upon end of Word Count (0=Disable, 1=Enable)" },
+ { 15, 1, "DMA Enable (0=Off, 1=On)" },
+ } }, /* R/W DMA 0 Control */
+ { GBA_DMA1SAD , "DMA1SAD" , { 0 } }, /* W DMA 1 Source Address */
+ { GBA_DMA1DAD , "DMA1DAD" , { 0 } }, /* W DMA 1 Destination Address */
+ { GBA_DMA1CNT_L, "DMA1CNT_L", { 0 } }, /* W DMA 1 Word Count */
+ { GBA_DMA1CNT_H, "DMA1CNT_H", {
+ { 5, 2, "Dest Addr Control (0=Incr,1=Decr,2=Fixed,3=Incr/Reload)" },
+ { 7, 2, "Source Adr Control (0=Incr,1=Decr,2=Fixed,3=Prohibited)" },
+ { 9, 1, "DMA Repeat (0=Off, 1=On) (Must be zero if Bit 11 set)" },
+ { 10, 1, "DMA Transfer Type (0=16bit, 1=32bit)" },
+ { 12, 2, "DMA Start Timing (0=Immediately, 1=VBlank, 2=HBlank, 3=Sound)" },
+ { 14, 1, "IRQ upon end of Word Count (0=Disable, 1=Enable)" },
+ { 15, 1, "DMA Enable (0=Off, 1=On)" },
+ } }, /* R/W DMA 1 Control */
+ { GBA_DMA2SAD , "DMA2SAD" , { 0 } }, /* W DMA 2 Source Address */
+ { GBA_DMA2DAD , "DMA2DAD" , { 0 } }, /* W DMA 2 Destination Address */
+ { GBA_DMA2CNT_L, "DMA2CNT_L", { 0 } }, /* W DMA 2 Word Count */
+ { GBA_DMA2CNT_H, "DMA2CNT_H", {
+ { 5, 2, "Dest Addr Control (0=Incr,1=Decr,2=Fixed,3=Incr/Reload)" },
+ { 7, 2, "Source Adr Control (0=Incr,1=Decr,2=Fixed,3=Prohibited)" },
+ { 9, 1, "DMA Repeat (0=Off, 1=On) (Must be zero if Bit 11 set)" },
+ { 10, 1, "DMA Transfer Type (0=16bit, 1=32bit)" },
+ { 12, 2, "DMA Start Timing (0=Immediately, 1=VBlank, 2=HBlank, 3=Sound)" },
+ { 14, 1, "IRQ upon end of Word Count (0=Disable, 1=Enable)" },
+ { 15, 1, "DMA Enable (0=Off, 1=On)" },
+ } }, /* R/W DMA 2 Control */
+ { GBA_DMA3SAD , "DMA3SAD" , { 0 } }, /* W DMA 3 Source Address */
+ { GBA_DMA3DAD , "DMA3DAD" , { 0 } }, /* W DMA 3 Destination Address */
+ { GBA_DMA3CNT_L, "DMA3CNT_L", { 0 } }, /* W DMA 3 Word Count */
+ { GBA_DMA3CNT_H, "DMA3CNT_H", {
+ { 5, 2, "Dest Addr Control (0=Incr,1=Decr,2=Fixed,3=Incr/Reload)" },
+ { 7, 2, "Source Adr Control (0=Incr,1=Decr,2=Fixed,3=Prohibited)" },
+ { 9, 1, "DMA Repeat (0=Off, 1=On) (Must be zero if Bit 11 set)" },
+ { 10, 1, "DMA Transfer Type (0=16bit, 1=32bit)" },
+ { 11, 1, "Game Pak DRQ (0=Normal, 1=DRQ Game Pak, DMA3)" },
+ { 12, 2, "DMA Start Timing (0=Immediately, 1=VBlank, 2=HBlank, 3=Video Capture)" },
+ { 14, 1, "IRQ upon end of Word Count (0=Disable, 1=Enable)" },
+ { 15, 1, "DMA Enable (0=Off, 1=On)" },
+ } }, /* R/W DMA 3 Control */
+
+ // Timer Registers
+ { GBA_TM0CNT_L, "TM0CNT_L", {0} }, /* R/W Timer 0 Counter/Reload */
+ { GBA_TM0CNT_H, "TM0CNT_H", {
+ { 0, 2, "Prescaler Selection (0=F/1, 1=F/64, 2=F/256, 3=F/1024)" },
+ { 2, 1, "Count-up (0=Normal, 1=Incr. on prev. Timer overflow)" },
+ { 6, 1, "Timer IRQ Enable (0=Disable, 1=IRQ on Timer overflow)" },
+ { 7, 1, "Timer Start/Stop (0=Stop, 1=Operate)" },
+ } }, /* R/W Timer 0 Control */
+ { GBA_TM1CNT_L, "TM1CNT_L", {0} }, /* R/W Timer 1 Counter/Reload */
+ { GBA_TM1CNT_H, "TM1CNT_H", {
+ { 0, 2, "Prescaler Selection (0=F/1, 1=F/64, 2=F/256, 3=F/1024)" },
+ { 2, 1, "Count-up (0=Normal, 1=Incr. on prev. Timer overflow)" },
+ { 6, 1, "Timer IRQ Enable (0=Disable, 1=IRQ on Timer overflow)" },
+ { 7, 1, "Timer Start/Stop (0=Stop, 1=Operate)" },
+ } }, /* R/W Timer 1 Control */
+ { GBA_TM2CNT_L, "TM2CNT_L", {0} }, /* R/W Timer 2 Counter/Reload */
+ { GBA_TM2CNT_H, "TM2CNT_H", {
+ { 0, 2, "Prescaler Selection (0=F/1, 1=F/64, 2=F/256, 3=F/1024)" },
+ { 2, 1, "Count-up (0=Normal, 1=Incr. on prev. Timer overflow)" },
+ { 6, 1, "Timer IRQ Enable (0=Disable, 1=IRQ on Timer overflow)" },
+ { 7, 1, "Timer Start/Stop (0=Stop, 1=Operate)" },
+ } }, /* R/W Timer 2 Control */
+ { GBA_TM3CNT_L, "TM3CNT_L", {0} }, /* R/W Timer 3 Counter/Reload */
+ { GBA_TM3CNT_H, "TM3CNT_H", {
+ { 0, 2, "Prescaler Selection (0=F/1, 1=F/64, 2=F/256, 3=F/1024)" },
+ { 2, 1, "Count-up (0=Normal, 1=Incr. on prev. Timer overflow)" },
+ { 6, 1, "Timer IRQ Enable (0=Disable, 1=IRQ on Timer overflow)" },
+ { 7, 1, "Timer Start/Stop (0=Stop, 1=Operate)" },
+ } }, /* R/W Timer 3 Control */
+
+ // Serial Communication (1)
+ { GBA_SIODATA32 , "SIODATA32" , {0} }, /*R/W SIO Data (Normal-32bit Mode; shared with below) */
+ { GBA_SIOMULTI0 , "SIOMULTI0" , {0} }, /*R/W SIO Data 0 (Parent) (Multi-Player Mode) */
+ { GBA_SIOMULTI1 , "SIOMULTI1" , {0} }, /*R/W SIO Data 1 (1st Child) (Multi-Player Mode) */
+ { GBA_SIOMULTI2 , "SIOMULTI2" , {0} }, /*R/W SIO Data 2 (2nd Child) (Multi-Player Mode) */
+ { GBA_SIOMULTI3 , "SIOMULTI3" , {0} }, /*R/W SIO Data 3 (3rd Child) (Multi-Player Mode) */
+ { GBA_SIOCNT , "SIOCNT" , {0} }, /*R/W SIO Control Register */
+ { GBA_SIOMLT_SEND, "SIOMLT_SEND", {0} }, /*R/W SIO Data (Local of MultiPlayer; shared below) */
+ { GBA_SIODATA8 , "SIODATA8" , {0} }, /*R/W SIO Data (Normal-8bit and UART Mode) */
+
+ // Keypad Input
+ { GBA_KEYINPUT, "GBA_KEYINPUT", {
+ { 0, 1, "Button A" },
+ { 1, 1, "Button B" },
+ { 2, 1, "Select" },
+ { 3, 1, "Start" },
+ { 4, 1, "Right" },
+ { 5, 1, "Left" },
+ { 6, 1, "Up" },
+ { 7, 1, "Down" },
+ { 8, 1, "Button R" },
+ { 9, 1, "Button L" },
+ } }, /* R Key Status */
+ { GBA_KEYCNT , "GBA_KEYCNT", {
+ { 0, 1, "Button A" },
+ { 1, 1, "Button B" },
+ { 2, 1, "Select" },
+ { 3, 1, "Start" },
+ { 4, 1, "Right" },
+ { 5, 1, "Left" },
+ { 6, 1, "Up" },
+ { 7, 1, "Down" },
+ { 8, 1, "Button R" },
+ { 9, 1, "Button L" },
+ { 14, 1, "Button IRQ Enable (0=Disable, 1=Enable)" },
+ { 15, 1, "Button IRQ Condition (0=OR, 1=AND)" },
+ } }, /* R/W Key Interrupt Control */
+
+ // Serial Communication (2)
+ { GBA_RCNT , "RCNT" , {0} }, /* R/W SIO Mode Select/General Purpose Data */
+ { GBA_IR , "IR" , {0} }, /* - Ancient - Infrared Register (Prototypes only) */
+ { GBA_JOYCNT , "JOYCNT" , {0} }, /* R/W SIO JOY Bus Control */
+ { GBA_JOY_RECV , "JOY_RECV" , {0} }, /* R/W SIO JOY Bus Receive Data */
+ { GBA_JOY_TRANS, "JOY_TRANS", {0} }, /* R/W SIO JOY Bus Transmit Data */
+ { GBA_JOYSTAT , "JOYSTAT" , {0} }, /* R/? SIO JOY Bus Receive Status */
+};
+
+// Interrupt sources
+#define GBA_INT_LCD_VBLANK 0
+#define GBA_INT_LCD_HBLANK 1
+#define GBA_INT_LCD_VCOUNT 2
+#define GBA_INT_TIMER0 3
+#define GBA_INT_TIMER1 4
+#define GBA_INT_TIMER2 5
+#define GBA_INT_TIMER3 6
+#define GBA_INT_SERIAL 7
+#define GBA_INT_DMA0 8
+#define GBA_INT_DMA1 9
+#define GBA_INT_DMA2 10
+#define GBA_INT_DMA3 11
+#define GBA_INT_KEYPAD 12
+#define GBA_INT_GAMEPAK 13
+
+#define GBA_BG_PALETTE 0x00000000
+#define GBA_OBJ_PALETTE 0x00000200
+#define GBA_OBJ_TILES0_2 0x00010000
+#define GBA_OBJ_TILES3_5 0x00014000
+#define GBA_OAM 0x07000000
+
+#define GBA_BACKUP_NONE 0
+#define GBA_BACKUP_EEPROM 1
+#define GBA_BACKUP_EEPROM_512B 2
+#define GBA_BACKUP_EEPROM_8KB 3
+#define GBA_BACKUP_SRAM 4
+#define GBA_BACKUP_FLASH_64K 5
+#define GBA_BACKUP_FLASH_128K 6
+
+#define GBA_REQ_1B 0x01
+#define GBA_REQ_2B 0x02
+#define GBA_REQ_4B 0x04
+#define GBA_REQ_DEBUG 0x20
+#define GBA_REQ_READ 0x40
+#define GBA_REQ_WRITE 0x80
+
+typedef struct {
+ UINT8* bios;
+ UINT8 wram0[256 * 1024];
+ UINT8 wram1[32 * 1024];
+ UINT8 io[1024];
+ UINT8 palette[1024];
+ UINT8 vram[128 * 1024];
+ UINT8 oam[1024];
+ UINT8* cart_rom;
+ UINT8 cart_backup[128 * 1024];
+ UINT8 flash_chip_id[4];
+ UINT32 openbus_word;
+ UINT32 eeprom_word;
+ UINT32 eeprom_addr;
+ UINT32 prefetch_en;
+ UINT32 prefetch_size;
+ UINT32 requests;
+ UINT32 bios_word;
+ UINT32 sram_word;
+ UINT32 mmio_word;
+ UINT8 wait_state_table[16 * 4];
+ UINT32 pipeline_bubble_shift_register;
+ UINT32 mmio_data_mask_lookup[256];
+ UINT8 mmio_reg_valid_lookup[256];
+ UINT8 mmio_debug_access_buffer[16 * 1024];
+} gba_mem_t;
+
+typedef struct {
+ UINT8 data;
+ UINT8 direction;
+ UINT8 control;
+ UINT8 input;
+ UINT8 rumble;
+} gba_gpio_t;
+
+typedef struct {
+ UINT32 rom_size;
+ UINT8 backup_type;
+ bool backup_is_dirty;
+ bool in_chip_id_mode;
+ INT32 flash_state;
+ INT32 flash_bank;
+ UINT32 features;
+ gba_gpio_t gpio;
+} gba_cartridge_t;
+
+typedef struct {
+ INT32 source_addr;
+ INT32 dest_addr;
+ INT32 length;
+ INT32 current_transaction;
+ bool last_enable;
+ bool last_vblank;
+ bool last_hblank;
+ UINT32 latched_transfer;
+ INT32 startup_delay;
+ bool activate_audio_dma;
+ bool video_dma_active;
+} gba_dma_t;
+
+typedef struct {
+ INT32 scan_clock;
+ bool last_vblank;
+ bool last_hblank;
+ INT32 last_lcd_y;
+ struct {
+ INT32 internal_bgx;
+ INT32 internal_bgy;
+ INT32 render_bgx;
+ INT32 render_bgy;
+ bool wrote_bgx;
+ bool wrote_bgy;
+ } aff[2];
+ UINT16 dispcnt_pipeline[3];
+ INT32 fast_forward_ticks;
+ float ghosting_strength;
+ UINT32 mosaic_y_counter;
+} gba_ppu_t;
+
+typedef struct {
+ bool last_enable;
+ UINT16 reload_value;
+ UINT16 pending_reload_value;
+ INT32 startup_delay;
+} gba_timer_t;
+
+typedef struct {
+ UINT32 step_counter;
+ INT32 length[4];
+ UINT32 volume[4];
+ UINT32 frequency[4];
+ INT32 env_direction[4]; //1: increase 0: nochange -1: decrease
+ UINT32 env_period[4];
+ UINT32 env_period_timer[4];
+ bool env_overflow[4];
+ //Only channel 1
+ UINT32 sweep_period;
+ UINT32 sweep_timer;
+ INT32 sweep_direction;
+ UINT32 sweep_shift;
+ bool sweep_enable;
+ bool sweep_subtracted;
+ bool use_length[4];
+ bool active[4];
+ bool powered[4];
+ float chan_t[4];
+ UINT16 lfsr4;
+} gba_frame_sequencer_t;
+
+typedef struct {
+ struct {
+ INT8 data[64];
+ INT32 read_ptr;
+ INT32 write_ptr;
+ } fifo[2];
+ double current_sim_time;
+ double current_sample_generated_time;
+ UINT16 wave_freq_timer;
+ UINT16 wave_sample_offset;
+ UINT8 curr_wave_sample;
+ UINT8 curr_wave_data;
+ float capacitor_l;
+ float capacitor_r;
+ gba_frame_sequencer_t sequencer;
+ UINT32 audio_clock;
+} gba_audio_t;
+
+typedef struct {
+ INT32 ticks_till_transfer_done;
+ bool last_active;
+} gba_sio_t;
+
+typedef struct {
+ UINT32 bess_version; //Versioning field must be 1
+ /*
+ r0-r15
+ CPSR 16
+ SPSR 17
+ R13_fiq 22
+ R13_irq 24
+ R13_svc 26
+ R13_abt 28
+ R13_und 30
+ R14_fiq 23
+ R14_irq 25
+ R14_svc 27
+ R14_abt 29
+ R14_und 31
+ SPSR_fiq 32
+ SPSR_irq 33
+ SPSR_svc 34
+ SPSR_abt 35
+ SPSR_und 36
+ */
+ UINT32 cpu_registers[37];
+ UINT32 wram0_seg;
+ UINT32 wram1_seg;
+ UINT32 io_seg;
+ UINT32 palette_seg;
+ UINT32 vram_seg;
+ UINT32 oam_seg;
+ UINT32 cart_backup_seg;
+ UINT16 timer_reload_values[4];
+ UINT32 padding[18];
+} gba_bess_info_t;
+
+typedef struct {
+ UINT16 dac;
+ UINT16 value;
+ UINT8 light_edge;
+ UINT8 pending_value;
+} gba_solar_sensor_t;
+
+typedef struct {
+ UINT16 pending_sample;
+ UINT16 shift_sample;
+ UINT8 clock_high;
+} gba_gyro_sensor_t;
+
+typedef struct {
+ UINT16 sample_x;
+ UINT16 sample_y;
+ UINT16 pending_x;
+ UINT16 pending_y;
+ UINT8 state;
+} gba_tilt_sensor_t;
+
+typedef struct gba_t {
+ gba_mem_t mem;
+ arm7_t cpu;
+ gba_cartridge_t cart;
+ gba_ppu_t ppu;
+ gba_rtc_t rtc;
+ gba_dma_t dma[4];
+ gba_sio_t sio;
+ gba_bess_info_t bess;
+ //There is a 2 cycle penalty when the CPU takes over from the DMA
+ bool last_transaction_dma;
+ bool activate_dmas;
+ bool dma_wait_ppu;
+ gba_timer_t timers[4];
+ UINT32 timer_ticks_before_event;
+ UINT32 deferred_timer_ticks;
+ UINT32 global_timer;
+ gba_audio_t audio;
+ bool prev_key_interrupt;
+ UINT32 first_target_buffer[GBA_LCD_W];
+ UINT32 second_target_buffer[GBA_LCD_W];
+ UINT8 window[GBA_LCD_W];
+ UINT8* framebuffer;
+ // Some HW has up to a 4 cycle delay before its IF propagates.
+ // This array acts as a FIFO to keep track of that.
+ UINT16 pipelined_if[5];
+ INT32 active_if_pipe_stages;
+ INT32 last_cpu_tick;
+ INT32 residual_dma_ticks;
+ bool stop_mode;
+ bool frame_in_progress;
+ bool pause_after_frame;
+ gba_solar_sensor_t solar_sensor;
+ gba_gyro_sensor_t gyro_sensor;
+ gba_tilt_sensor_t tilt_sensor;
+} gba_t;
+
+typedef struct {
+ UINT8 framebuffer[GBA_LCD_W * GBA_LCD_H * 4];
+ UINT8 bios[16 * 1024];
+ FILE* log_cmp_file;
+ bool skip_bios_intro;
+ char save_file_path[SB_FILE_PATH_SIZE];
+} gba_scratch_t;
+
+static void gba_process_audio_writes(gba_t* gba);
+static UINT8 gba_audio_process_byte_write(gba_t* gba, UINT32 addr, UINT8 value);
+static bool gba_run_ar_cheat(gba_t* gba, const UINT32* buffer, UINT32 size);
+static FORCE_INLINE void gba_recompute_waitstate_table(gba_t* gba, UINT16 waitcnt);
+static FORCE_INLINE UINT32 gba_read32(gba_t* gba, UINT32 baddr);
+static FORCE_INLINE void gba_store32(gba_t* gba, UINT32 baddr, UINT32 data);
+
+//Returns offset into savestate where bess info can be found
+static UINT32 gba_save_best_effort_state(gba_t* gba)
+{
+ gba->bess.bess_version = 1;
+ for (INT32 i = 0;i < 37;++i)
+ gba->bess.cpu_registers[i] = gba->cpu.registers[i];
+
+ gba->bess.wram0_seg = ((UINT8*)gba->mem.wram0 ) - (UINT8*)gba;
+ gba->bess.wram1_seg = ((UINT8*)gba->mem.wram1 ) - (UINT8*)gba;
+ gba->bess.io_seg = ((UINT8*)gba->mem.io ) - (UINT8*)gba;
+ gba->bess.palette_seg = ((UINT8*)gba->mem.palette ) - (UINT8*)gba;
+ gba->bess.vram_seg = ((UINT8*)gba->mem.vram ) - (UINT8*)gba;
+ gba->bess.oam_seg = ((UINT8*)gba->mem.oam ) - (UINT8*)gba;
+ gba->bess.cart_backup_seg = ((UINT8*)gba->mem.cart_backup) - (UINT8*)gba;
+ for (INT32 i = 0;i < 4;++i)
+ gba->bess.timer_reload_values[i] = gba->timers[i].reload_value;
+
+ return ((UINT8*)&gba->bess) - (UINT8*)(gba);
+}
+
+static bool gba_load_best_effort_state(gba_t* gba, UINT8* save_state_data, UINT32 size, UINT32 bess_offset)
+{
+ if (bess_offset + sizeof(gba_bess_info_t ) > size) return false;
+ gba_bess_info_t* bess = (gba_bess_info_t*)(save_state_data + bess_offset);
+ if (bess->bess_version != 1 ) return false;
+ if (bess->wram0_seg + sizeof(gba->mem.wram0 ) > size) return false;
+ if (bess->wram1_seg + sizeof(gba->mem.wram1 ) > size) return false;
+ if (bess->io_seg + sizeof(gba->mem.io ) > size) return false;
+ if (bess->palette_seg + sizeof(gba->mem.palette ) > size) return false;
+ if (bess->vram_seg + sizeof(gba->mem.vram ) > size) return false;
+ if (bess->oam_seg + sizeof(gba->mem.oam ) > size) return false;
+ if (bess->cart_backup_seg + sizeof(gba->mem.cart_backup) > size) return false;
+ for (INT32 i = 0;i < 37;++i) gba->cpu.registers[i] = bess->cpu_registers[i];
+ memcpy(gba->mem.wram0 , save_state_data + bess->wram0_seg , sizeof(gba->mem.wram0));
+ memcpy(gba->mem.wram1 , save_state_data + bess->wram1_seg , sizeof(gba->mem.wram1));
+ memcpy(gba->mem.io , save_state_data + bess->io_seg , sizeof(gba->mem.io));
+ memcpy(gba->mem.palette , save_state_data + bess->palette_seg , sizeof(gba->mem.palette));
+ memcpy(gba->mem.vram , save_state_data + bess->vram_seg , sizeof(gba->mem.vram));
+ memcpy(gba->mem.oam , save_state_data + bess->oam_seg , sizeof(gba->mem.oam));
+ memcpy(gba->mem.cart_backup, save_state_data + bess->cart_backup_seg, sizeof(gba->mem.cart_backup));
+ for (INT32 i = 0;i < 4;++i)
+ gba->timers[i].pending_reload_value = gba->timers[i].reload_value = bess->timer_reload_values[i];
+
+ gba_recompute_waitstate_table(gba, gba_read32(gba, GBA_WAITCNT));
+ return true;
+}
+
+static FORCE_INLINE sb_debug_mmio_access_t gba_debug_mmio_access(gba_t* gba, UINT32 baddr, INT32 trigger_breakpoint)
+{
+ baddr &= 0xffff;
+ baddr /= 4;
+
+ if (trigger_breakpoint != -1) {
+ gba->mem.mmio_debug_access_buffer[baddr] &= 0x7f;
+ if (trigger_breakpoint != 0)
+ gba->mem.mmio_debug_access_buffer[baddr] |= 0x80;
+ }
+ UINT8 flag = gba->mem.mmio_debug_access_buffer[baddr];
+
+ sb_debug_mmio_access_t access;
+ access.read_since_reset = flag & 0x01;
+ access.read_in_tick = flag & 0x02;
+ access.write_since_reset = flag & 0x10;
+ access.write_in_tick = flag & 0x20;
+ access.trigger_breakpoint = flag & 0x80;
+ return access;
+}
+
+static void gba_tick_keypad(sb_joy_t* joy, gba_t* gba);
+static void gba_compute_timers(gba_t* gba);
+static FORCE_INLINE void gba_tick_timers(gba_t* gba);
+static FORCE_INLINE void gba_send_interrupt(gba_t* gba, INT32 delay, INT32 if_bit);
+
+static FORCE_INLINE bool gba_gpio_active(const gba_t* gba)
+{
+ return gba->cart.features & (GBA_CART_RTC | GBA_CART_SOLAR | GBA_CART_RUMBLE | GBA_CART_GYRO);
+}
+
+static FORCE_INLINE UINT16 gba_rom_read16(const gba_t* gba, UINT32 address)
+{
+ UINT32 offset = (address & 0x1fffffe);
+ return gba->mem.cart_rom[offset] | (gba->mem.cart_rom[offset + 1] << 8);
+}
+
+static FORCE_INLINE bool gba_gpio_address(const gba_t* gba, UINT32 address)
+{
+ UINT32 region = address >> 24;
+ UINT32 offset = address & 0x1ffffff;
+ return gba_gpio_active(gba) && region >= 8 && region <= 0xd && offset >= 0xc4 && offset < 0xca;
+}
+
+static FORCE_INLINE UINT8 gba_gpio_pins(const gba_t* gba)
+{
+ UINT8 direction = gba->cart.gpio.direction & 0xf;
+ return ((gba->cart.gpio.data & direction) | (gba->cart.gpio.input & ~direction)) & 0xf;
+}
+
+static FORCE_INLINE bool gba_gpio_pin(const gba_t* gba, UINT32 pin)
+{
+ return SB_BFE(gba_gpio_pins(gba), pin, 1);
+}
+
+static FORCE_INLINE void gba_gpio_drive(gba_t* gba, UINT8 mask, UINT8 value)
+{
+ mask &= ~gba->cart.gpio.direction & 0xf;
+ gba->cart.gpio.input = (gba->cart.gpio.input & ~mask) | (value & mask);
+}
+
+static FORCE_INLINE void gba_gpio_update_rumble(gba_t* gba)
+{
+ gba->cart.gpio.rumble = (gba->cart.features & GBA_CART_RUMBLE) &&
+ (gba->cart.gpio.direction & 8) && (gba->cart.gpio.data & 8);
+}
+
+static FORCE_INLINE INT32 gba_motion_shift_floor(INT32 value, UINT32 shift)
+{
+ INT64 scale = (INT64)1 << shift;
+ if (value >= 0)
+ return value / (INT32)scale;
+ return (INT32)(-((-(INT64)value + scale - 1) / scale));
+}
+
+static FORCE_INLINE UINT16 gba_gyro_sample(INT32 gyro_z)
+{
+ return (UINT16)(0x700 + gba_motion_shift_floor(gyro_z, 21));
+}
+
+static FORCE_INLINE UINT16 gba_tilt_sample(INT32 axis)
+{
+ return (UINT16)(0x3a0 - gba_motion_shift_floor(axis, 22));
+}
+
+static FORCE_INLINE UINT16 gba_gpio_read16(gba_t* gba, UINT32 address)
+{
+ // CONTROL=0 (read-disable): GPIO registers are invisible; reads see ROM.
+ if (!gba->cart.gpio.control)
+ return gba_rom_read16(gba, address);
+
+ switch (address & 0x1fffffe) {
+ case 0xc4:return gba_gpio_pins(gba);
+ case 0xc6:return gba->cart.gpio.direction & 0xf;
+ case 0xc8:return gba->cart.gpio.control & 1;
+ }
+ return 0;
+}
+
+// Returns a pointer to the data backing the baddr (when not DWORD aligned, it
+// ignores the lowest 2 bits.
+static FORCE_INLINE UINT32* gba_dword_lookup(gba_t* gba, UINT32 baddr, INT32 req_type);
+static FORCE_INLINE UINT32 gba_read32(gba_t* gba, UINT32 baddr)
+{
+ if (gba_gpio_address(gba, baddr)) {
+ UINT32 address = baddr & ~3;
+ UINT32 low = gba_gpio_read16(gba, address);
+ if ((address & 0x1ffffff) == 0xc4)
+ return low | ((UINT32)gba_gpio_read16(gba, address + 2) << 16);
+ UINT32 rom = gba_rom_read16( gba, address + 2);
+ return low | (rom << 16);
+ }
+ return *gba_dword_lookup(gba, baddr, GBA_REQ_READ | GBA_REQ_4B);
+}
+
+static FORCE_INLINE UINT16 gba_read16(gba_t* gba, UINT32 baddr)
+{
+ if (gba_gpio_address(gba, baddr))
+ return gba_gpio_read16(gba, baddr);
+ UINT32* val = gba_dword_lookup(gba, baddr, GBA_REQ_READ | GBA_REQ_2B);
+ INT32 offset = SB_BFE(baddr, 1, 1);
+ return ((UINT16*)val)[offset];
+}
+
+static FORCE_INLINE UINT8 gba_read8(gba_t* gba, UINT32 baddr)
+{
+ if (gba_gpio_address(gba, baddr))
+ return (gba_gpio_read16(gba, baddr) >> ((baddr & 1) * 8)) & 0xff;
+ UINT32* val = gba_dword_lookup(gba, baddr, GBA_REQ_READ | GBA_REQ_1B);
+ INT32 offset = SB_BFE(baddr, 0, 2);
+ return ((UINT8*)val)[offset];
+}
+
+static FORCE_INLINE UINT8 gba_read8_debug(gba_t* gba, UINT32 baddr)
+{
+ if (gba_gpio_address(gba, baddr))
+ return (gba_gpio_read16(gba, baddr) >> ((baddr & 1) * 8)) & 0xff;
+ UINT32* val = gba_dword_lookup(gba, baddr, GBA_REQ_READ | GBA_REQ_1B | GBA_REQ_DEBUG);
+ INT32 offset = SB_BFE(baddr, 0, 2);
+ return ((UINT8*)val)[offset];
+}
+
+static FORCE_INLINE void gba_process_flash_state_machine(gba_t* gba, UINT32 baddr, UINT8 data)
+{
+#define FLASH_DEFAULT 0x00
+#define FLASH_RECV_AA 0x01
+#define FLASH_RECV_55 0x02
+#define FLASH_ERASE_RECV_AA 0x03
+#define FLASH_ERASE_RECV_55 0x04
+
+#define FLASH_ENTER_CHIP_ID 0x90
+#define FLASH_EXIT_CHIP_ID 0xF0
+#define FLASH_PREP_ERASE 0x80
+#define FLASH_ERASE_CHIP 0x10
+#define FLASH_ERASE_4KB 0x30
+#define FLASH_WRITE_BYTE 0xA0
+#define FLASH_SET_BANK 0xB0
+
+ INT32 state = gba->cart.flash_state;
+ gba->cart.flash_state = FLASH_DEFAULT;
+ baddr &= 0xffff;
+ switch (state) {
+ default:
+ printf("Unknown flash state %02x\n", gba->cart.flash_state);
+ case FLASH_DEFAULT:
+ if (baddr == 0x5555 && data == 0xAA) gba->cart.flash_state = FLASH_RECV_AA;
+ break;
+ case FLASH_RECV_AA:
+ if (baddr == 0x2AAA && data == 0x55) gba->cart.flash_state = FLASH_RECV_55;
+ break;
+ case FLASH_RECV_55:
+ if (baddr == 0x5555) {
+ // Process command
+ switch (data) {
+ case FLASH_ENTER_CHIP_ID:gba->cart.in_chip_id_mode = true; break;
+ case FLASH_EXIT_CHIP_ID: gba->cart.in_chip_id_mode = false; break;
+ case FLASH_PREP_ERASE: gba->cart.flash_state = FLASH_PREP_ERASE; break;
+ case FLASH_WRITE_BYTE: gba->cart.flash_state = FLASH_WRITE_BYTE; break;
+ case FLASH_SET_BANK: gba->cart.flash_state = FLASH_SET_BANK; break;
+ default: printf("Unknown flash command: %02x\n", data); break;
+ }
+ }
+ break;
+ case FLASH_PREP_ERASE:
+ if (baddr == 0x5555 && data == 0xAA) gba->cart.flash_state = FLASH_ERASE_RECV_AA;
+ break;
+ case FLASH_ERASE_RECV_AA:
+ if (baddr == 0x2AAA && data == 0x55) gba->cart.flash_state = FLASH_ERASE_RECV_55;
+ break;
+ case FLASH_ERASE_RECV_55:
+ if (baddr == 0x5555 || data == FLASH_ERASE_4KB) {
+ INT32 size = gba->cart.backup_type == GBA_BACKUP_FLASH_64K ? 64 * 1024 : 128 * 1024;
+ INT32 erase_4k_off = gba->cart.flash_bank * 64 * 1024 + SB_BFE(baddr, 12, 4) * 4096;
+ // Process command
+ switch (data) {
+ case FLASH_ERASE_CHIP:
+ printf("Erase Flash Chip %d bytes\n", size);
+ for (INT32 i = 0;i < size;++i) gba->mem.cart_backup[i] = 0xff;
+ break;
+ case FLASH_ERASE_4KB:
+ for (INT32 i = 0;i < 4096;++i) gba->mem.cart_backup[erase_4k_off + i] = 0xff;
+ break;
+ default:
+ printf("Unknown flash erase command: %02x\n", data);
+ break;
+ }
+ gba->cart.backup_is_dirty = true;
+ }
+ break;
+ case FLASH_WRITE_BYTE:
+ gba->mem.cart_backup[gba->cart.flash_bank * 64 * 1024 + baddr] &= data;
+ gba->cart.backup_is_dirty = true;
+ break;
+ case FLASH_SET_BANK:
+ gba->cart.flash_bank = data & 1;
+ break;
+ }
+}
+
+static FORCE_INLINE void gba_process_rtc_state_machine(gba_t* gba)
+{
+ UINT8 pins = gba_gpio_pins(gba);
+ UINT8 sio = gba_rtc_update_pins(&gba->rtc, pins);
+ gba_gpio_drive(gba, 2, sio << 1);
+}
+
+static FORCE_INLINE void gba_process_tilt_write(gba_t* gba, UINT32 baddr, UINT8 data)
+{
+ switch (baddr & 0xffff) {
+ case 0x8000:
+ if (data == 0x55) gba->tilt_sensor.state = 1;
+ break;
+ case 0x8100:
+ if (data == 0xaa && gba->tilt_sensor.state == 1) {
+ gba->tilt_sensor.state = 0;
+ gba->tilt_sensor.sample_x = gba->tilt_sensor.pending_x;
+ gba->tilt_sensor.sample_y = gba->tilt_sensor.pending_y;
+ }
+ break;
+ }
+}
+
+static FORCE_INLINE UINT8 gba_process_tilt_read(gba_t* gba, UINT32 baddr)
+{
+ switch (baddr & 0xffff) {
+ case 0x8200:return gba->tilt_sensor.sample_x & 0xff;
+ case 0x8300:return ((gba->tilt_sensor.sample_x >> 8) & 0xf) | 0x80;
+ case 0x8400:return gba->tilt_sensor.sample_y & 0xff;
+ case 0x8500:return ( gba->tilt_sensor.sample_y >> 8) & 0xf;
+ }
+ return 0xff;
+}
+
+static FORCE_INLINE void gba_process_backup_write(gba_t* gba, UINT32 baddr, UINT32 data)
+{
+ if (gba->cart.backup_type == GBA_BACKUP_FLASH_64K || gba->cart.backup_type == GBA_BACKUP_FLASH_128K) {
+ gba_process_flash_state_machine(gba, baddr, data);
+ } else if (gba->cart.backup_type == GBA_BACKUP_SRAM) {
+ if (gba->mem.cart_backup[baddr & 0x7fff] != (data & 0xff)) {
+ gba->mem.cart_backup[baddr & 0x7fff] = data & 0xff;
+ gba->cart.backup_is_dirty = true;
+ }
+ } else if (gba->cart.features & GBA_CART_TILT) {
+ gba_process_tilt_write(gba, baddr, data & 0xff);
+ }
+}
+
+static void gba_process_solar_sensor(gba_t* gba)
+{
+ bool clk = gba_gpio_pin(gba, 0);
+ bool rst = gba_gpio_pin(gba, 1);
+ bool cs = gba_gpio_pin(gba, 2);
+ if (cs) {
+ gba_gpio_drive(gba, 8, 0);
+ return;
+ }
+ if (rst) {
+ gba->solar_sensor.dac = 0;
+ gba->solar_sensor.light_edge = true;
+ gba->solar_sensor.value = gba->solar_sensor.pending_value;
+ }
+ if (clk && gba->solar_sensor.light_edge) {
+ gba->solar_sensor.dac++;
+ }
+ gba->solar_sensor.light_edge = !clk;
+ bool flag = gba->solar_sensor.dac >= gba->solar_sensor.value;
+ gba_gpio_drive(gba, 8, flag << 3);
+}
+
+static FORCE_INLINE void gba_process_gyro_sensor(gba_t* gba)
+{
+ bool clock = gba_gpio_pin(gba, 1);
+ bool output = gba->gyro_sensor.clock_high && !clock;
+ if (gba_gpio_pin(gba, 0)) {
+ gba->gyro_sensor.shift_sample = gba->gyro_sensor.pending_sample;
+ output = true;
+ }
+ if (output) {
+ UINT8 bit = gba->gyro_sensor.shift_sample >> 15;
+ gba->gyro_sensor.shift_sample <<= 1;
+ gba_gpio_drive(gba, 4, bit << 2);
+ }
+ gba->gyro_sensor.clock_high = clock;
+}
+
+static FORCE_INLINE void gba_gpio_update(gba_t* gba)
+{
+ if (gba->cart.features & GBA_CART_RTC ) gba_process_rtc_state_machine(gba);
+ if (gba->cart.features & GBA_CART_SOLAR) gba_process_solar_sensor(gba);
+ if (gba->cart.features & GBA_CART_GYRO ) gba_process_gyro_sensor(gba);
+ gba_gpio_update_rumble(gba);
+}
+
+static FORCE_INLINE void gba_gpio_write16(gba_t* gba, UINT32 address, UINT16 data)
+{
+ switch (address & 0x1fffffe) {
+ case 0xc4:gba->cart.gpio.data = data & 0xf; gba_gpio_update(gba); break;
+ case 0xc6:gba->cart.gpio.direction = data & 0xf; gba_gpio_update(gba); break;
+ case 0xc8:gba->cart.gpio.control = data & 1; break;
+ }
+}
+
+static FORCE_INLINE void gba_store32(gba_t* gba, UINT32 baddr, UINT32 data)
+{
+ if (baddr >= 0x08000000) {
+ //Mask is 0xfe to catch the sram mirror at 0x0f and 0x0e
+ if ((baddr & 0xfe000000) == 0xE000000) {
+ gba_process_backup_write(gba, baddr, data >> ((baddr & 3) * 8));
+ return;
+ }
+ if (gba_gpio_address(gba, baddr)) {
+ // Game Pak GPIO registers are 16-bit; 32-bit ROM stores do not access them.
+ return;
+ }
+ }
+ UINT32* val = gba_dword_lookup(gba, baddr, GBA_REQ_WRITE | GBA_REQ_4B);
+ *val = data;
+}
+
+static FORCE_INLINE void gba_store16(gba_t* gba, UINT32 baddr, UINT32 data)
+{
+ if (baddr >= 0x08000000) {
+ //Mask is 0xfe to catch the sram mirror at 0x0f and 0x0e
+ if ((baddr & 0xfe000000) == 0xE000000) {
+ gba_process_backup_write(gba, baddr, data >> ((baddr & 1) * 8));
+ return;
+ }
+ if (gba_gpio_address(gba, baddr)) {
+ gba_gpio_write16(gba, baddr & ~1, data);
+ return;
+ }
+ }
+ UINT32* val = gba_dword_lookup(gba, baddr, GBA_REQ_WRITE | GBA_REQ_2B);
+ INT32 offset = SB_BFE(baddr, 1, 1);
+ ((UINT16*)val)[offset] = data;
+}
+
+static FORCE_INLINE void gba_store8(gba_t* gba, UINT32 baddr, UINT32 data)
+{
+ if (baddr >= 0x05000000) {
+ // 8 bit stores to palette mirror across 8 bit halves
+ if ((baddr & 0xff000000) == 0x5000000) {
+ gba_store16(gba, baddr & ~1, (data & 0xff) * 0x0101);
+ return;
+ }
+ if (((baddr & 0xff000000) == 0x06000000) && ((baddr & 0x1ffff) <= 0x0013FFF)) {
+ gba_store16(gba, baddr & ~1, (data & 0xff) * 0x0101);
+ return;
+ }
+ //Mask is 0xfe to catch the sram mirror at 0x0f and 0x0e
+ if ((baddr & 0xfe000000) == 0xE000000) {
+ gba_process_backup_write(gba, baddr, data);
+ return;
+ }
+ // Remaining 8 bit ops are not supported on VRAM or ROM
+ return;
+ }
+ UINT32* val = gba_dword_lookup(gba, baddr, GBA_REQ_WRITE | GBA_REQ_1B);
+ INT32 offset = SB_BFE(baddr, 0, 2);
+ ((UINT8*)val)[offset] = data;
+}
+
+static FORCE_INLINE void gba_store8_debug(gba_t* gba, UINT32 baddr, UINT32 data)
+{
+ if (baddr >= 0x05000000) {
+ // 8 bit stores to palette mirror across 8 bit halves
+ if ((baddr & 0xff000000) == 0x5000000) {
+ gba_store16(gba, baddr & ~1, (data & 0xff) * 0x0101);
+ return;
+ }
+ if (((baddr & 0xff000000) == 0x06000000) && ((baddr & 0x1ffff) <= 0x0013FFF)) {
+ gba_store16(gba, baddr & ~1, (data & 0xff) * 0x0101);
+ return;
+ }
+ //Mask is 0xfe to catch the sram mirror at 0x0f and 0x0e
+ if ((baddr & 0xfe000000) == 0xE000000) {
+ gba_process_backup_write(gba, baddr, data);
+ return;
+ }
+ // Remaining 8 bit ops are not supported on VRAM or ROM
+ return;
+ }
+ UINT32* val = gba_dword_lookup(gba, baddr, GBA_REQ_WRITE | GBA_REQ_1B | GBA_REQ_DEBUG);
+ INT32 offset = SB_BFE(baddr, 0, 2);
+ ((UINT8*)val)[offset] = data;
+}
+
+static FORCE_INLINE void gba_io_store8(gba_t* gba, UINT32 baddr, UINT8 data)
+{
+ gba->mem.io[baddr & 0xfff] = data;
+}
+
+static FORCE_INLINE void gba_io_store16(gba_t* gba, UINT32 baddr, UINT16 data)
+{
+ *(UINT16*)(gba->mem.io + (baddr & 0xfff)) = data;
+}
+
+static FORCE_INLINE void gba_io_store32(gba_t* gba, UINT32 baddr, UINT32 data)
+{
+ *(UINT32*)(gba->mem.io + (baddr & 0xfff)) = data;
+}
+
+static FORCE_INLINE UINT8 gba_io_read8( gba_t* gba, UINT32 baddr)
+{
+ return gba->mem.io[baddr & 0xfff];
+}
+
+static FORCE_INLINE UINT16 gba_io_read16(gba_t* gba, UINT32 baddr)
+{
+ return *(UINT16*)(gba->mem.io + (baddr & 0xfff));
+}
+
+static FORCE_INLINE UINT32 gba_io_read32(gba_t* gba, UINT32 baddr)
+{
+ return *(UINT32*)(gba->mem.io + (baddr & 0xfff));
+}
+
+static FORCE_INLINE void gba_recompute_waitstate_table(gba_t* gba, UINT16 waitcnt)
+{
+ // TODO: Make the waitstate for the ROM configureable
+ const INT32 wait_state_table[16 * 4] = {
+ 1,1,1,1, //0x00 (bios)
+ 1,1,1,1, //0x01 (bios)
+ 3,3,6,6, //0x02 (256k WRAM)
+ 1,1,1,1, //0x03 (32k WRAM)
+ 1,1,1,1, //0x04 (IO)
+ 1,1,2,2, //0x05 (BG/OBJ Palette)
+ 1,1,2,2, //0x06 (VRAM)
+ 1,1,1,1, //0x07 (OAM)
+ 4,4,8,8, //0x08 (GAMEPAK ROM 0)
+ 4,4,8,8, //0x09 (GAMEPAK ROM 0)
+ 4,4,8,8, //0x0A (GAMEPAK ROM 1)
+ 4,4,8,8, //0x0B (GAMEPAK ROM 1)
+ 4,4,8,8, //0x0C (GAMEPAK ROM 2)
+ 4,4,8,8, //0x0D (GAMEPAK ROM 2)
+ 4,4,4,4, //0x0E (GAMEPAK SRAM)
+ 1,1,1,1, //0x0F (unused)
+ };
+ for (INT32 i = 0;i < 16 * 4;++i) {
+ gba->mem.wait_state_table[i] = wait_state_table[i];
+ }
+ UINT8 sram_wait = SB_BFE(waitcnt, 0, 2);
+ UINT8 wait_first[ 3];
+ UINT8 wait_second[3];
+
+ wait_first[ 0] = SB_BFE(waitcnt, 2, 2);
+ wait_second[0] = SB_BFE(waitcnt, 4, 1);
+ wait_first[ 1] = SB_BFE(waitcnt, 5, 2);
+ wait_second[1] = SB_BFE(waitcnt, 7, 1);
+ wait_first[ 2] = SB_BFE(waitcnt, 8, 2);
+ wait_second[2] = SB_BFE(waitcnt, 10, 1);
+ UINT8 prefetch_en = SB_BFE(waitcnt, 14, 1);
+
+ INT32 primary_table[4] = { 4,3,2,8 };
+
+ //Each waitstate is two entries in table
+ for (INT32 ws = 0;ws < 3;++ws) {
+ for (INT32 i = 0;i < 2;++i) {
+ UINT8 w_first = primary_table[wait_first[ws]];
+ UINT8 w_second = wait_second[ws] ? 1 : 2;
+ if (ws == 1)
+ w_second = wait_second[ws] ? 1 : 4;
+ if (ws == 2)
+ w_second = wait_second[ws] ? 1 : 8;
+ w_first += 1;w_second += 1;
+ //Wait 0
+ INT32 wait16b = w_second;
+ INT32 wait32b = w_second * 2;
+
+ INT32 wait16b_nonseq = w_first;
+ INT32 wait32b_nonseq = w_first + w_second;
+
+ gba->mem.wait_state_table[(0x08 + i + ws * 2) * 4 + 0] = wait16b;
+ gba->mem.wait_state_table[(0x08 + i + ws * 2) * 4 + 1] = wait16b_nonseq;
+ gba->mem.wait_state_table[(0x08 + i + ws * 2) * 4 + 2] = wait32b;
+ gba->mem.wait_state_table[(0x08 + i + ws * 2) * 4 + 3] = wait32b_nonseq;
+ }
+ }
+ gba->mem.prefetch_en = prefetch_en;
+ gba->mem.prefetch_size = 0;
+
+ //SRAM
+ gba->mem.wait_state_table[(0x0E * 4) + 0] = 1 + primary_table[sram_wait];
+ gba->mem.wait_state_table[(0x0E * 4) + 1] = 1 + primary_table[sram_wait];
+ gba->mem.wait_state_table[(0x0E * 4) + 2] = 1 + primary_table[sram_wait];
+ gba->mem.wait_state_table[(0x0E * 4) + 3] = 1 + primary_table[sram_wait];
+ waitcnt &= (1 << 15); // Force cartridge to report as GBA cart
+ gba_io_store16(gba, GBA_WAITCNT, waitcnt);
+}
+
+static FORCE_INLINE void gba_compute_access_cycles(gba_t* gba, UINT32 address, INT32 request_size /*0: 1B,1: 2B,3: 4B*/)
+{
+ INT32 bank = SB_BFE(address, 24, 4);
+ bool prefetch_en = gba->mem.prefetch_en;
+ if (SB_UNLIKELY(!prefetch_en)) {
+ if (gba->cpu.i_cycles)
+ request_size |= 1;
+ if (request_size & 1)
+ gba->cpu.next_fetch_sequential = false;
+ gba->mem.prefetch_size = 0;
+ }
+ UINT32 wait = gba->mem.wait_state_table[bank * 4 + request_size];
+ if (SB_LIKELY(prefetch_en)) {
+ gba->mem.prefetch_size += gba->cpu.i_cycles;
+ if (bank >= 0x08 && bank <= 0x0D) {
+ if (SB_UNLIKELY(request_size & 1)) {
+ UINT32 pc = gba->cpu.prefetch_pc;
+ if (pc >= 0x08000000) {
+ INT32 pc_bank = SB_BFE(pc, 24, 4);
+ INT32 prefetch_cycles = gba->mem.wait_state_table[pc_bank * 4];
+ INT32 prefetch_phase = (gba->mem.prefetch_size) % prefetch_cycles;
+ if (gba->mem.prefetch_size >
+ gba->cpu.i_cycles && prefetch_phase == prefetch_cycles - 1)wait += 1;
+ }
+ //Non sequential->reset prefetch buffer
+ gba->mem.prefetch_size = 0;
+ gba->cpu.next_fetch_sequential = false;
+ } else {
+ //Sequential fetch from prefetch buffer based on available wait states
+ if (gba->mem.prefetch_size >= wait) {
+ gba->mem.prefetch_size -= wait - 1;
+ wait = 1;
+ } else {
+ wait -= gba->mem.prefetch_size;
+ gba->mem.prefetch_size = 0;
+ }
+ }
+ } else gba->mem.prefetch_size += wait;
+ }
+ gba->mem.requests += wait;
+}
+
+static FORCE_INLINE UINT32 gba_compute_access_cycles_dma(gba_t* gba, UINT32 address, INT32 request_size/*0: 1B,1: 2B,3: 4B*/)
+{
+ INT32 bank = SB_BFE(address, 24, 4);
+ UINT32 wait = gba->mem.wait_state_table[bank * 4 + request_size];
+ return wait;
+}
+
+static FORCE_INLINE void gba_process_mmio_read(gba_t* gba, UINT32 address);
+
+// Memory IO functions for the emulated CPU
+static FORCE_INLINE UINT32 arm7_read32(void* user_data, UINT32 address)
+{
+ gba_compute_access_cycles((gba_t*)user_data, address, 3);
+ UINT32 value = gba_read32((gba_t*)user_data, address);
+ return value;
+}
+
+static FORCE_INLINE UINT32 arm7_read16(void* user_data, UINT32 address)
+{
+ gba_compute_access_cycles((gba_t*)user_data, address, 1);
+ UINT16 value = gba_read16((gba_t*)user_data, address);
+ return value;
+}
+
+static FORCE_INLINE UINT32 arm7_read32_seq(void* user_data, UINT32 address, bool seq)
+{
+ gba_compute_access_cycles((gba_t*)user_data, address, seq ? 2 : 3);
+ return gba_read32((gba_t*)user_data, address);
+}
+
+static FORCE_INLINE UINT32 arm7_read16_seq(void* user_data, UINT32 address, bool seq)
+{
+ gba_compute_access_cycles((gba_t*)user_data, address, seq ? 0 : 1);
+ return gba_read16((gba_t*)user_data, address);
+}
+
+void gba_cpu_breakpoint(void* user_data)
+{
+ gba_t* gba = (gba_t*)user_data;
+ gba->frame_in_progress = false;
+ gba->pause_after_frame = true;
+ printf("Hit ARM7 Breakpoint\n");
+}
+
+//Used to process special behavior triggered by MMIO write
+static bool gba_process_mmio_write(gba_t* gba, UINT32 address, UINT32 data, INT32 req_size_bytes);
+
+static FORCE_INLINE UINT8 arm7_read8(void* user_data, UINT32 address)
+{
+ gba_compute_access_cycles((gba_t*)user_data, address, 1);
+ return gba_read8((gba_t*)user_data, address);
+}
+
+static FORCE_INLINE void gba_dma_write32(gba_t* gba, UINT32 address, UINT32 data)
+{
+ if ((address & 0xfffffC00) == 0x04000000) {
+ if (gba_process_mmio_write(gba, address, data, 4))
+ return;
+ }
+ gba_store32(gba, address, data);
+}
+
+static FORCE_INLINE void gba_dma_write16(gba_t* gba, UINT32 address, UINT16 data)
+{
+ if ((address & 0xfffffC00) == 0x04000000) {
+ if (gba_process_mmio_write(gba, address, data, 2)) return;
+ }
+ gba_store16(gba, address, data);
+}
+
+static FORCE_INLINE void arm7_write32(void* user_data, UINT32 address, UINT32 data)
+{
+ gba_compute_access_cycles((gba_t*)user_data, address, 3);
+ gba_dma_write32((gba_t*)user_data, address, data);
+}
+
+static FORCE_INLINE void arm7_write16(void* user_data, UINT32 address, UINT16 data)
+{
+ gba_compute_access_cycles((gba_t*)user_data, address, 1);
+ gba_dma_write16((gba_t*)user_data, address, data);
+}
+
+static FORCE_INLINE void arm7_write8(void* user_data, UINT32 address, UINT8 data)
+{
+ gba_compute_access_cycles((gba_t*)user_data, address, 1);
+ if ((address & 0xfffff000) == 0x04000000) {
+ if (gba_process_mmio_write((gba_t*)user_data, address, data, 1))
+ return;
+ }
+ gba_store8((gba_t*)user_data, address, data);
+}
+
+// Try to load a GBA rom, return false on invalid rom
+bool gba_load_rom(sb_emu_state_t* emu, gba_t* gba, gba_scratch_t* scratch);
+
+static FORCE_INLINE UINT32* gba_dword_lookup(gba_t* gba, UINT32 addr, INT32 req_type)
+{
+ UINT32* ret = &gba->mem.openbus_word;
+ switch (addr >> 24) {
+ case 0x0:
+ if (addr < 0x4000) {
+ if (gba->cpu.registers[15] < 0x4000)
+ gba->mem.bios_word = *(UINT32*)(gba->mem.bios + (addr & ~3));
+ //else gba->mem.bios_word=0;
+ gba->mem.openbus_word = gba->mem.bios_word;
+ }
+ break;
+ case 0x1:
+ break;
+ case 0x2:
+ ret = (UINT32*)(gba->mem.wram0 + (addr & 0x3fffc));
+ gba->mem.openbus_word = *ret;
+ break;
+ case 0x3:
+ ret = (UINT32*)(gba->mem.wram1 + (addr & 0x7ffc));
+ gba->mem.openbus_word = *ret;
+ break;
+ case 0x4:
+ if (SB_LIKELY(addr <= 0x40003FF)) {
+ if (req_type & GBA_REQ_READ) {
+ INT32 io_reg = (addr >> 2) & 0xff;
+ if (SB_LIKELY(gba->mem.mmio_reg_valid_lookup[io_reg])) {
+ gba_process_mmio_read(gba, addr);
+ gba->mem.mmio_word = (*(UINT32*)(gba->mem.io + (addr & 0x3fc))) & gba->mem.mmio_data_mask_lookup[io_reg];
+ ret = &gba->mem.mmio_word;
+ }
+ } else ret = (UINT32*)(gba->mem.io + (addr & 0x3fc));
+ if (!(req_type & GBA_REQ_DEBUG)) {
+ gba->mem.mmio_debug_access_buffer[(addr & 0xffff) / 4] |= (req_type & GBA_REQ_WRITE) ? 0x70 : 0xf;
+ if (gba->mem.mmio_debug_access_buffer[(addr & 0xffff) / 4] & 0x80)
+ gba->cpu.trigger_breakpoint(gba);
+ }
+ }
+ break;
+ case 0x5:
+ ret = (UINT32*)(gba->mem.palette + (addr & 0x3fc));
+ gba->mem.openbus_word = *ret;
+ break;
+ case 0x6:
+ if (addr & 0x10000) {
+ ret = (UINT32*)(gba->mem.vram + (addr & 0x07ffc) + 0x10000);
+ gba->mem.openbus_word = *ret;
+ if (addr & 0x08000) {
+ UINT16 dispcnt = gba_io_read16(gba, GBA_DISPCNT);
+ INT32 bg_mode = SB_BFE(dispcnt, 0, 3);
+ //Don't allow writes to mirrored VRAM in bitmap mode. See also vram-mirror.gba
+ //Needed for Acrobat Kid. Still requires testing to verify correct behavior
+ if (bg_mode > 2 && !(addr & 0x04000)) {
+ ret = &gba->mem.openbus_word;*ret = 0;
+ }
+ }
+ } else ret = (UINT32*)(gba->mem.vram + (addr & 0x1fffc));
+ gba->mem.openbus_word = *ret;
+ break;
+ case 0x7:
+ ret = (UINT32*)(gba->mem.oam + (addr & 0x3fc));
+ gba->mem.openbus_word = *ret;
+ break;
+ case 0x8:
+ case 0x9:
+ case 0xA:
+ case 0xB:
+ case 0xC:
+ case 0xD: {
+ INT32 maddr = addr & 0x1fffffc;
+ if (SB_UNLIKELY(maddr >= gba->cart.rom_size)) {
+ gba->mem.openbus_word = ((maddr / 2) & 0xffff) | (((maddr / 2 + 1) & 0xffff) << 16);
+ // Return ready when done writting EEPROM (required by Minish Cap)
+ if (gba->cart.backup_type == GBA_BACKUP_EEPROM)
+ gba->mem.openbus_word = 1;
+ } else {
+ gba->mem.openbus_word = *(UINT32*)(gba->mem.cart_rom + maddr);
+ if (req_type & 0x3) {
+ UINT16 res16 = gba->mem.openbus_word >> (addr & 2) * 8;
+ gba->mem.openbus_word = res16 * 0x10001u;
+ }
+ }
+ }
+ break;
+ case 0xE:
+ case 0xF:
+ if (gba->cart.backup_type == GBA_BACKUP_SRAM) {
+ gba->mem.sram_word = (UINT32)gba->mem.cart_backup[addr & 0x7fff] * 0x01010101u;
+ ret = &gba->mem.sram_word;
+ } else if (gba->cart.features & GBA_CART_TILT) {
+ gba->mem.sram_word = (UINT32)gba_process_tilt_read(gba, addr) * 0x01010101u;
+ ret = &gba->mem.sram_word;
+ } else if (gba->cart.backup_type == GBA_BACKUP_EEPROM) {
+ ret = (UINT32*)&gba->mem.eeprom_word;
+ } else if (gba->cart.backup_type == GBA_BACKUP_NONE) {
+ gba->mem.sram_word = 0xffffffff;
+ ret = &gba->mem.sram_word;
+ } else {
+ //Flash
+ if (gba->cart.in_chip_id_mode && addr <= 0xE000001) {
+ gba->mem.openbus_word = *(UINT32*)gba->mem.flash_chip_id;
+ ret = &gba->mem.openbus_word;
+ } else {
+ gba->mem.sram_word = gba->mem.cart_backup[(addr & 0xffff) + gba->cart.flash_bank * 64 * 1024] * 0x01010101;
+ ret = &gba->mem.sram_word;
+ }
+ }
+ gba->mem.openbus_word = (*ret & 0xffff) * 0x10001;
+ break;
+ }
+ return ret;
+}
+
+static FORCE_INLINE void gba_audio_fifo_push(gba_t* gba, INT32 fifo, INT8 data)
+{
+ INT32 size = (gba->audio.fifo[fifo].write_ptr - gba->audio.fifo[fifo].read_ptr) & 0x1f;
+ if (size < 28) {
+ gba->audio.fifo[fifo].write_ptr = (gba->audio.fifo[fifo].write_ptr + 1) & 0x1f;
+ gba->audio.fifo[fifo].data[gba->audio.fifo[fifo].write_ptr] = data;
+ }
+}
+
+static void gba_recompute_mmio_mask_table(gba_t* gba) {
+ for (INT32 io_reg = 0; io_reg < 256;io_reg++) {
+ UINT32 dword_address = 0x04000000 + io_reg * 4;
+ UINT32 data_mask = 0xffffffff;
+ bool valid = true;
+ if (dword_address == 0x4000008)
+ data_mask &= 0xdfffdfff;
+ else if (dword_address == 0x4000048)
+ data_mask &= 0x3f3f3f3f;
+ else if (dword_address == 0x4000050)
+ data_mask &= 0x1f1f3fff;
+ else if (dword_address == 0x4000060)
+ data_mask &= 0xffc0007f;
+ else if (dword_address == 0x4000064 || dword_address == 0x400006c || dword_address == 0x4000074)
+ data_mask &= 0x00004000;
+ else if (dword_address == 0x4000068)
+ data_mask &= 0x0000ffc0;
+ else if (dword_address == 0x4000070)
+ data_mask &= 0xe00000e0;
+ else if (dword_address == 0x4000078)
+ data_mask &= 0x0000ff00;
+ else if (dword_address == 0x400007c)
+ data_mask &= 0x000040ff;
+ else if (dword_address == 0x4000080)
+ data_mask &= 0x770fff77;
+ else if (dword_address == 0x4000084)
+ data_mask &= 0x0000008f;
+ else if (dword_address == 0x4000088 || dword_address == 0x4000134 || dword_address == 0x4000140 || dword_address == 0x4000158 || dword_address == 0x4000204 || dword_address == 0x4000208)
+ data_mask = 0x0000ffff;
+ else if (dword_address == 0x40000b8 || dword_address == 0x40000c4 || dword_address == 0x40000d0)
+ data_mask &= 0xf7e00000;
+ else if (dword_address == 0x40000dc)
+ data_mask &= 0xffe00000;
+ else if ((dword_address >= 0x4000010 && dword_address <= 0x4000046) ||
+ (dword_address == 0x400004c) ||
+ (dword_address >= 0x4000054 && dword_address <= 0x400005e) ||
+ (dword_address == 0x400008c) ||
+ (dword_address >= 0x40000a0 && dword_address <= 0x40000b6) ||
+ (dword_address >= 0x40000bc && dword_address <= 0x40000c2) ||
+ (dword_address >= 0x40000c8 && dword_address <= 0x40000ce) ||
+ (dword_address >= 0x40000d4 && dword_address <= 0x40000da) ||
+ (dword_address >= 0x40000e0 && dword_address <= 0x40000fe) ||
+ (dword_address == 0x400100c))
+ valid = false;
+ gba->mem.mmio_data_mask_lookup[io_reg] = data_mask;
+ gba->mem.mmio_reg_valid_lookup[io_reg] = valid;
+ }
+}
+
+static FORCE_INLINE void gba_process_mmio_read(gba_t* gba, UINT32 address)
+{
+ // Force recomputing timers on timer read
+ if (address >= GBA_TM0CNT_L && address <= GBA_TM3CNT_H)
+ gba_compute_timers(gba);
+}
+
+static bool gba_process_mmio_write(gba_t* gba, UINT32 address, UINT32 data, INT32 req_size_bytes)
+{
+ UINT32 address_u32 = address & ~3;
+ UINT32 word_mask = 0xffffffff;
+ UINT32 word_data = data;
+ if (req_size_bytes == 2) {
+ word_data <<= (address & 2) * 8;
+ word_mask = 0x0000ffffu << ((address & 2) * 8u);
+ } else if (req_size_bytes == 1) {
+ word_data <<= (address & 3) * 8;
+ word_mask = 0x000000ffu << ((address & 3) * 8u);
+ }
+ word_data &= word_mask;
+
+ if (address_u32 == GBA_IE) {
+ UINT16 IE = gba_io_read16(gba, GBA_IE);
+ UINT16 IF = gba_io_read16(gba, GBA_IF);
+
+ IE = ((IE & ~word_mask) | (word_data & word_mask)) >> 0;
+ IF &= ~((word_data) >> 16);
+ gba_io_store16(gba, GBA_IE, IE);
+ gba_io_store16(gba, GBA_IF, IF);
+
+ return true;
+ } else if (address_u32 == GBA_SOUNDCNT_L) {
+ if (word_mask & 0xffff0000) {
+ UINT16 soundcnt_h = word_data >> 16;
+ for (INT32 i = 0;i < 2;++i) {
+ if (SB_BFE(soundcnt_h, 11 + i * 4, 1)) {
+ gba->audio.fifo[i].read_ptr = 0;
+ gba->audio.fifo[i].write_ptr = 0;
+ for (INT32 d = 0;d < 32;++d)
+ gba->audio.fifo[i].data[d] = 0;
+ word_data &= ~(1u << (27 + i * 4));
+ }
+ }
+ }
+ } else if (address_u32 == GBA_TM0CNT_L || address_u32 == GBA_TM1CNT_L || address_u32 == GBA_TM2CNT_L || address_u32 == GBA_TM3CNT_L) {
+ gba_compute_timers(gba);
+ INT32 timer_off = (address_u32 - GBA_TM0CNT_L) / 4;
+ if (word_mask & 0xffff) {
+ gba->timers[timer_off + 0].pending_reload_value = word_data & (word_mask & 0xffff);
+ }
+ if (word_mask & 0xffff0000) {
+ gba_store16(gba, address_u32 + 2, (word_data >> 16) & 0xffff);
+ gba->timers[timer_off + 0].reload_value = gba->timers[timer_off + 0].pending_reload_value;
+ }
+ gba->timer_ticks_before_event = 0;
+ return true;
+ } else if (address_u32 == GBA_POSTFLG) {
+ //Only BIOS can update Post Flag and haltcnt
+ if (gba->cpu.registers[15] < 0x4000) {
+ //Writes to haltcnt halt the CPU
+ if (word_mask & 0xff00) {
+ if (word_data & 0x8000) {
+ gba->stop_mode = true;
+ gba->frame_in_progress = false;
+ }
+ gba->cpu.wait_for_interrupt = true;
+ }
+ UINT32 data = gba_io_read32(gba, address_u32);
+ //POST can only be initialized once, then other writes are dropped.
+ if ((word_mask & 0xff) && (data & 0xff))
+ word_mask &= ~0xff;
+ data &= ~word_mask;
+ data |= word_data & word_mask;
+ gba_io_store32(gba, address_u32, data);
+ }
+ return true;
+ } else if (address_u32 == GBA_BG2X || address_u32 == GBA_BG3X) {
+ INT32 aff_bg = (address_u32 - GBA_BG2X) / 0x10;
+ gba->ppu.aff[aff_bg].wrote_bgx = true;
+ } else if (address_u32 == GBA_BG2Y || address_u32 == GBA_BG3Y) {
+ INT32 aff_bg = (address_u32 - GBA_BG2Y) / 0x10;
+ gba->ppu.aff[aff_bg].wrote_bgy = true;
+ } else if (address_u32 == GBA_DMA0CNT_L || address_u32 == GBA_DMA1CNT_L ||
+ address_u32 == GBA_DMA2CNT_L || address_u32 == GBA_DMA3CNT_L) {
+ gba->activate_dmas = true;
+ } else if (address_u32 == GBA_WAITCNT) {
+ UINT16 waitcnt = gba_io_read16(gba, GBA_WAITCNT);
+ waitcnt = ((waitcnt & ~word_mask) | (word_data & word_mask));
+ gba_recompute_waitstate_table(gba, waitcnt);
+ } else if (address_u32 == GBA_KEYINPUT) {
+ if (word_mask & 0xffff0000) {
+ gba_store16(gba, GBA_KEYINPUT, (word_data >> 16) & 0xffff);
+ }
+ gba_tick_keypad(NULL, gba);
+ } else if (address_u32 >= GBA_SOUND1CNT_L && address_u32 < GBA_WAVE_RAM) {
+ for (INT32 i = 0;i < 4;++i) {
+ if (word_mask & (0xff << (i * 8))) {
+ UINT8 data = gba_audio_process_byte_write(gba, address_u32 + i, SB_BFE(word_data, (i * 8), 8));
+ gba_io_store8(gba, address_u32 + i, data);
+ }
+ }
+ gba_process_audio_writes(gba);
+ return true;
+ }
+ return false;
+}
+
+INT32 gba_search_rom_for_backup_string(gba_t* gba)
+{
+ INT32 btype = GBA_BACKUP_NONE;
+ for (INT32 b = 0; b < gba->cart.rom_size;++b) {
+ const char* strings[] = { "EEPROM_", "SRAM_", "FLASH_", "FLASH512_","FLASH1M_" };
+ INT32 backup_type[] = { GBA_BACKUP_EEPROM, GBA_BACKUP_SRAM, GBA_BACKUP_FLASH_64K, GBA_BACKUP_FLASH_64K, GBA_BACKUP_FLASH_128K };
+ for (INT32 type = 0; type < ARRAY_SIZE(strings);++type) {
+ INT32 str_off = 0;
+ bool matches = true;
+ const char* str = strings[type];
+ while (str[str_off] && matches) {
+ if ( b + str_off >= gba->cart.rom_size) matches = false;
+ else if (str[str_off] != gba->mem.cart_rom[b + str_off]) matches = false;
+ ++str_off;
+ }
+ if (matches) {
+ if (btype != backup_type[type] && btype != GBA_BACKUP_NONE) {
+ printf("Found multiple backup types, defaulting to none\n");
+ return GBA_BACKUP_NONE;
+ }
+ btype = backup_type[type];
+ }
+ }
+ }
+ return btype;
+}
+
+static FORCE_INLINE void gba_setup_flash_id(gba_t* gba)
+{
+ // Not used unless the cartridge enters flash chip-id mode.
+ if (gba->cart.backup_type == GBA_BACKUP_FLASH_64K) {
+ gba->mem.flash_chip_id[1] = 0xd4;
+ gba->mem.flash_chip_id[0] = 0xbf;
+ } else {
+ gba->mem.flash_chip_id[1] = 0x13;
+ gba->mem.flash_chip_id[0] = 0x62;
+ }
+}
+
+void gba_unload(gba_t* gba, gba_scratch_t* scratch)
+{
+ printf("Unloading GBA\n");
+ if (scratch->log_cmp_file) fclose(scratch->log_cmp_file);
+ scratch->log_cmp_file = NULL;
+}
+
+bool gba_load_rom(sb_emu_state_t* emu, gba_t* gba, gba_scratch_t* scratch)
+{
+ memset(gba, 0, sizeof(gba_t));
+ memset(scratch, 0, sizeof(gba_scratch_t));
+ gba->solar_sensor.value = 0xff;
+ gba->solar_sensor.pending_value = 0xff;
+ gba->gyro_sensor.pending_sample = 0x700;
+ gba->tilt_sensor.sample_x = 0xfff;
+ gba->tilt_sensor.sample_y = 0xfff;
+ gba->tilt_sensor.pending_x = 0x3a0;
+ gba->tilt_sensor.pending_y = 0x3a0;
+ if (!sb_path_has_file_ext(emu->rom_path, ".gba"))
+ return false;
+
+ if (emu->rom_size > 32 * 1024 * 1024) {
+ printf("ROMs with sizes >32MB (%zu bytes) are too big for the GBA\n", emu->rom_size);
+ return false;
+ }
+
+ gba->mem.bios = scratch->bios;
+ bool loaded_bios = se_load_bios_file("GBA BIOS", emu->save_file_path, "gba_bios.bin", scratch->bios, 16 * 1024);
+ if (!loaded_bios) {
+ memcpy(scratch->bios, gba_bios_bin, sizeof(gba_bios_bin));
+ scratch->skip_bios_intro = true;
+ }
+ gba->cart.rom_size = emu->rom_size;
+ gba->mem.cart_rom = emu->rom_data;
+
+ gba->cart.backup_type = gba_search_rom_for_backup_string(gba);
+
+ size_t bytes = 0;
+ UINT8* data = sb_load_file_data(emu->save_file_path, &bytes);
+ if (data) {
+ printf("Loaded save file: %s, bytes: %zu\n", emu->save_file_path, bytes);
+ if (bytes >= 128 * 1024)
+ bytes = 128 * 1024;
+ memcpy(gba->mem.cart_backup, data, bytes);
+ sb_free_file_data(data);
+ } else {
+ printf("Could not find save file: %s\n", emu->save_file_path);
+ for (INT32 i = 0;i < sizeof(gba->mem.cart_backup);++i)
+ gba->mem.cart_backup[i] = 0xff;
+ }
+
+ // Setup flash chip id (this is not used if the cartridge does not have flash backup storage)
+ gba_setup_flash_id(gba);
+
+ gba->cpu = arm7_init(gba);
+
+ for (INT32 bg = 2;bg < 4;++bg) {
+ gba_io_store16(gba, GBA_BG2PA + (bg - 2) * 0x10, 1 << 8);
+ gba_io_store16(gba, GBA_BG2PB + (bg - 2) * 0x10, 0 << 8);
+ gba_io_store16(gba, GBA_BG2PC + (bg - 2) * 0x10, 0 << 8);
+ gba_io_store16(gba, GBA_BG2PD + (bg - 2) * 0x10, 1 << 8);
+ }
+ gba_store16(gba, 0x04000088, 512);
+ gba_store32(gba, 0x040000DC, 0x84000000);
+ gba_recompute_waitstate_table(gba, 0);
+ gba_recompute_mmio_mask_table(gba);
+
+ if (scratch->skip_bios_intro) {
+ printf("No GBA bios using bundled bios\n");
+ memcpy(gba->mem.bios, gba_bios_bin, sizeof(gba_bios_bin));
+ const UINT32 initial_regs[37] = {
+ 0x0 ,0x0,0x0 ,0x0,0x0,0x0 ,0x0,0x0,
+ 0x0 ,0x0,0x0 ,0x0,0x0,0x3007f00,0x0,0x8000000,
+ 0xdf ,0x0,0x0 ,0x0,0x0,0x0 ,0x0,0x0,
+ 0x3007fa0,0x0,0x3007fe0,0x0,0x0,0x0 ,0x0,0x0,
+ 0x0 ,0x0,0x0 ,0x0,0x0,
+ };
+ for (INT32 i = 0;i < 37;++i)
+ gba->cpu.registers[i] = initial_regs[i];
+
+ const UINT32 initial_mmio_writes[] = {
+ 0x4000000,0x80,
+ 0x4000004,0x7e0000,
+ 0x4000020,0x100,
+ 0x4000024,0x1000000,
+ 0x4000030,0x100,
+ 0x4000034,0x1000000,
+ 0x4000080,0xe0000,
+ 0x4000084,0xf,
+ 0x4000088,0x200,
+ 0x4000100,0xff8a,
+ 0x4000130,0x3ff,
+ 0x4000134,0x8000,
+ 0x4000300,0x1,
+ };
+ for (INT32 i = 0;i < ARRAY_SIZE(initial_mmio_writes);i += 2) {
+ UINT32 addr = initial_mmio_writes[i + 0];
+ UINT32 data = initial_mmio_writes[i + 1];
+ arm7_write32(gba, addr, data);
+ }
+ gba_store32(gba, GBA_IE, 0x1);
+ gba_store16(gba, GBA_DISPCNT, 0x9140);
+ } else {
+ gba->cpu.registers[PC ] = 0x00000000;
+ gba->cpu.registers[CPSR] = 0x000000d3;
+ }
+ if (gba->cpu.log_cmp_file) {
+ fclose(gba->cpu.log_cmp_file);
+ gba->cpu.log_cmp_file = NULL;
+ };
+ gba->cpu.log_cmp_file = se_load_log_file(scratch->save_file_path, "log.bin");
+ gba->cpu.executed_instructions += 2;
+ gba->audio.current_sample_generated_time = gba->audio.current_sim_time = 0;
+ return true;
+}
+
+#define GBA_LCD_HBLANK_END (295)
+#define GBA_LCD_HBLANK_START (GBA_LCD_W)
+#define GBA_LCD_VBLANK_START (GBA_LCD_H*1232)
+#define GBA_LCD_VBLANK_END (227*1232)
+
+//Returns true if the fast forward failed to be more efficient in main emu loop
+static FORCE_INLINE INT32 gba_ppu_compute_max_fast_forward(gba_t* gba, bool render)
+{
+ INT32 scanline_clock = (gba->ppu.scan_clock) % 1232;
+ //If inside hblank, can fastforward to outside of hblank
+ if (scanline_clock >= GBA_LCD_HBLANK_START * 4 && scanline_clock <= GBA_LCD_HBLANK_END * 4)
+ return GBA_LCD_HBLANK_END * 4 - scanline_clock - 1;
+ //If inside hrender, can fastforward to hblank if not the first pixel and not visible
+ bool not_visible = !render || gba->ppu.scan_clock > GBA_LCD_VBLANK_START;
+ if (not_visible && (scanline_clock >= 1 && scanline_clock <= GBA_LCD_HBLANK_START * 4))
+ return GBA_LCD_HBLANK_START * 4 - scanline_clock - 1;
+ return 3 - ((gba->ppu.scan_clock) % 4);
+}
+
+static FORCE_INLINE void gba_tick_ppu(gba_t* gba, bool render)
+{
+ if (SB_LIKELY(gba->ppu.fast_forward_ticks > 0)) {
+ gba->ppu.fast_forward_ticks--;
+ return;
+ }
+
+ if (gba->ppu.scan_clock >= 280896) gba->ppu.scan_clock -= 280896;
+ INT32 lcd_y = (gba->ppu.scan_clock) / 1232;
+ INT32 lcd_x = ((gba->ppu.scan_clock) % 1232) / 4;
+ gba->ppu.scan_clock++;
+ gba->ppu.fast_forward_ticks = gba_ppu_compute_max_fast_forward(gba, render) + 1;
+ gba->ppu.scan_clock += gba->ppu.fast_forward_ticks;
+ if (lcd_x == 0 || lcd_x == GBA_LCD_HBLANK_START || lcd_x == GBA_LCD_HBLANK_END) {
+ UINT16 disp_stat = gba_io_read16(gba, GBA_DISPSTAT) & ~0x7;
+ UINT16 vcount_cmp = SB_BFE(disp_stat, 8, 8);
+ INT32 vcount = (lcd_y + (lcd_x >= GBA_LCD_HBLANK_END)) % 228;
+ bool vblank = lcd_y >= 160 && lcd_y < 227;
+ bool hblank = lcd_x >= GBA_LCD_HBLANK_START && lcd_x < GBA_LCD_HBLANK_END;
+ disp_stat |= vblank ? 0x1 : 0;
+ disp_stat |= hblank ? 0x2 : 0;
+ disp_stat |= vcount == vcount_cmp ? 0x4 : 0;
+ gba_io_store16(gba, GBA_DISPSTAT, disp_stat);
+ gba_io_store16(gba, GBA_VCOUNT , vcount);
+ UINT32 new_if = 0;
+ if (hblank != gba->ppu.last_hblank) {
+ gba->ppu.last_hblank = hblank;
+ bool hblank_irq_en = SB_BFE(disp_stat, 4, 1);
+ if (hblank && hblank_irq_en)
+ new_if |= (1 << GBA_INT_LCD_HBLANK);
+ gba->activate_dmas |= gba->dma_wait_ppu;
+ if (!hblank) {
+ gba->ppu.dispcnt_pipeline[0] = gba->ppu.dispcnt_pipeline[1];
+ gba->ppu.dispcnt_pipeline[1] = gba->ppu.dispcnt_pipeline[2];
+ gba->ppu.dispcnt_pipeline[2] = gba_io_read16(gba, GBA_DISPCNT);
+ }
+ }
+ if (lcd_y != gba->ppu.last_lcd_y) {
+ if (vblank != gba->ppu.last_vblank) {
+ if (vblank)
+ gba->frame_in_progress = false;
+ gba->ppu.last_vblank = vblank;
+ bool vblank_irq_en = SB_BFE(disp_stat, 3, 1);
+ if (vblank && vblank_irq_en)
+ new_if |= (1 << GBA_INT_LCD_VBLANK);
+ gba->activate_dmas |= gba->dma_wait_ppu;
+ }
+ gba->ppu.last_lcd_y = lcd_y;
+ if (lcd_y == vcount_cmp) {
+ bool vcnt_irq_en = SB_BFE(disp_stat, 5, 1);
+ if (vcnt_irq_en)
+ new_if |= (1 << GBA_INT_LCD_VCOUNT);
+ }
+ }
+ gba_send_interrupt(gba, 3, new_if);
+ }
+
+ if (!render)
+ return;
+
+ if (lcd_x == GBA_LCD_HBLANK_START) {
+ UINT16 dispcnt = gba->ppu.dispcnt_pipeline[0];
+ INT32 bg_mode = SB_BFE(dispcnt, 0, 3);
+ if (bg_mode != 0) {
+ for (INT32 aff = 0; aff < 2; ++aff) {
+ bool bg_en = SB_BFE(dispcnt, 8 + aff + 2, 1);
+ if (!bg_en) continue;
+ INT32 b = (INT16)gba_io_read16(gba, GBA_BG2PB + (aff) * 0x10);
+ INT32 d = (INT16)gba_io_read16(gba, GBA_BG2PD + (aff) * 0x10);
+ UINT16 bgcnt = gba_io_read16(gba, GBA_BG2CNT + aff * 2);
+ bool mosaic = SB_BFE(bgcnt, 6, 1);
+ if (mosaic) {
+ UINT16 mos_reg = gba_io_read16(gba, GBA_MOSAIC);
+ INT32 mos_y = SB_BFE(mos_reg, 4, 4) + 1;
+ if ((lcd_y % mos_y) == 0) {
+ gba->ppu.aff[aff].render_bgx += b * mos_y;
+ gba->ppu.aff[aff].render_bgy += d * mos_y;
+ }
+ } else {
+ gba->ppu.aff[aff].render_bgx += b;
+ gba->ppu.aff[aff].render_bgy += d;
+ }
+ }
+ }
+ }
+ bool reload_ref_points = lcd_x == GBA_LCD_HBLANK_END || (lcd_y == 0 && lcd_x == 0);
+ if (reload_ref_points) {
+ //Latch BGX and BGY registers
+ for (INT32 aff = 0; aff < 2; ++aff) {
+ if (gba->ppu.aff[aff].wrote_bgx || (lcd_y == 0 && lcd_x == 0)) {
+ gba->ppu.aff[aff].render_bgx = gba_io_read32(gba, GBA_BG2X + (aff) * 0x10);
+ gba->ppu.aff[aff].render_bgx = SB_BFE(gba->ppu.aff[aff].render_bgx, 0, 28);
+ gba->ppu.aff[aff].render_bgx = ((INT32)(gba->ppu.aff[aff].render_bgx << 4)) >> 4;
+ gba->ppu.aff[aff].wrote_bgx = false;
+ }
+ if (gba->ppu.aff[aff].wrote_bgy || (lcd_y == 0 && lcd_x == 0)) {
+ gba->ppu.aff[aff].render_bgy = gba_io_read32(gba, GBA_BG2Y + (aff) * 0x10);
+ gba->ppu.aff[aff].render_bgy = SB_BFE(gba->ppu.aff[aff].render_bgy, 0, 28);
+ gba->ppu.aff[aff].render_bgy = ((INT32)(gba->ppu.aff[aff].render_bgy << 4)) >> 4;
+ gba->ppu.aff[aff].wrote_bgy = false;
+ }
+ }
+ }
+ UINT16 dispcnt = gba_io_read16(gba, GBA_DISPCNT);
+ INT32 bg_mode = SB_BFE(dispcnt, 0, 3);
+ INT32 obj_vram_map_2d = !SB_BFE(dispcnt, 6, 1);
+ INT32 forced_blank = SB_BFE(dispcnt, 7, 1);
+ bool visible = lcd_x < 240 && lcd_y < 160;
+ //Render sprites over scanline when it completes
+ if ((lcd_y < 159 || lcd_y == 227) && lcd_x == GBA_LCD_HBLANK_START) {
+ INT32 sprite_lcd_y = (lcd_y + 1) % 228;
+ UINT16 mos_reg = gba_io_read16(gba, GBA_MOSAIC);
+ INT32 mos_y = SB_BFE(mos_reg, 12, 4) + 1;
+ // Partial fix to https://github.com/skylersaleh/SkyEmu/issues/316
+ if (++gba->ppu.mosaic_y_counter >= mos_y || sprite_lcd_y == 0) {
+ gba->ppu.mosaic_y_counter = 0;
+ }
+ //Render sprites over scanline when it completes
+ UINT8 default_window_control = 0x3f; //bitfield [0-3:bg0-bg3 enable 4:obj enable, 5: special effect enable]
+ bool winout_enable = SB_BFE(dispcnt, 13, 3) != 0;
+ UINT16 WINOUT = gba_io_read16(gba, GBA_WINOUT);
+ if (winout_enable)
+ default_window_control = SB_BFE(WINOUT, 0, 8);
+
+ for (INT32 x = 0; x < 240; ++x) {
+ gba->window[x] = default_window_control;
+ }
+ UINT8 obj_window_control = default_window_control;
+ bool obj_window_enable = SB_BFE(dispcnt, 15, 1);
+ if (obj_window_enable)
+ obj_window_control = SB_BFE(WINOUT, 8, 6);
+ bool display_obj = SB_BFE(dispcnt, 12, 1);
+ if (display_obj) {
+ for (INT32 o = 0; o < 128; ++o) {
+ UINT16 attr0 = *(UINT16*)(gba->mem.oam + o * 8 + 0);
+ //Attr0
+ UINT8 y_coord = SB_BFE(attr0, 0, 8);
+ bool rot_scale = SB_BFE(attr0, 8, 1);
+ bool double_size = SB_BFE(attr0, 9, 1) && rot_scale;
+ bool obj_disable = SB_BFE(attr0, 9, 1) && !rot_scale;
+ if (obj_disable)
+ continue;
+
+ INT32 obj_mode = SB_BFE(attr0, 10, 2); //(0=Normal, 1=Semi-Transparent, 2=OBJ Window, 3=Prohibited)
+ bool mosaic = SB_BFE(attr0, 12, 1);
+ bool colors_or_palettes = SB_BFE(attr0, 13, 1);
+ INT32 obj_shape = SB_BFE(attr0, 14, 2); //(0=Square,1=Horizontal,2=Vertical,3=Prohibited)
+ UINT16 attr1 = *(UINT16*)(gba->mem.oam + o * 8 + 2);
+
+ INT32 rotscale_param = SB_BFE(attr1, 9, 5);
+ bool h_flip = SB_BFE(attr1, 12, 1) && !rot_scale;
+ bool v_flip = SB_BFE(attr1, 13, 1) && !rot_scale;
+ INT32 obj_size = SB_BFE(attr1, 14, 2);
+ // Size Square Horizontal Vertical
+ // 0 8x8 16x8 8x16
+ // 1 16x16 32x8 8x32
+ // 2 32x32 32x16 16x32
+ // 3 64x64 64x32 32x64
+ const INT32 xsize_lookup[16] = {
+ 8,16, 8, 0,
+ 16,32, 8, 0,
+ 32,32,16, 0,
+ 64,64,32, 0
+ };
+ const INT32 ysize_lookup[16] = {
+ 8, 8,16, 0,
+ 16, 8,32, 0,
+ 32,16,32, 0,
+ 64,32,64, 0
+ };
+
+ INT32 y_size = ysize_lookup[obj_size * 4 + obj_shape];
+
+ if (((sprite_lcd_y - y_coord) & 0xff) < y_size * (double_size ? 2 : 1)) {
+ INT16 x_coord = SB_BFE(attr1, 0, 9);
+ if (SB_BFE(x_coord, 8, 1))
+ x_coord |= 0xfe00;
+
+ INT32 x_size = xsize_lookup[obj_size * 4 + obj_shape];
+ INT32 x_start = x_coord >= 0 ? x_coord : 0;
+ INT32 x_end = x_coord + x_size * (double_size ? 2 : 1);
+ if (x_end >= 240)x_end = 240;
+ //Attr2
+ //Skip objects disabled by window
+ UINT16 attr2 = *(UINT16*)(gba->mem.oam + o * 8 + 4);
+ INT32 tile_base = SB_BFE(attr2, 0, 10);
+ // Always place sprites as the highest priority
+ INT32 priority = SB_BFE(attr2, 10, 2);
+ INT32 palette = SB_BFE(attr2, 12, 4);
+ for (INT32 x = x_start; x < x_end; ++x) {
+ INT32 sx = (x - x_coord);
+ INT32 sy = (sprite_lcd_y - y_coord) & 0xff;
+ if (mosaic) {
+ UINT16 mos_reg = gba_io_read16(gba, GBA_MOSAIC);
+ INT32 mos_x = SB_BFE(mos_reg, 8, 4) + 1;
+ INT32 mos_y = SB_BFE(mos_reg, 12, 4) + 1;
+ sx = ((x / mos_x) * mos_x - x_coord);
+ if (sx < 0)
+ sx = 0;
+ sy = (sprite_lcd_y - y_coord) & 0xff;
+ sy -= gba->ppu.mosaic_y_counter;
+ if (sy < 0) {
+ sy = 0;
+ }
+ }
+ if (rot_scale) {
+ UINT32 param_base = rotscale_param * 0x20;
+ INT32 a = *(INT16*)(gba->mem.oam + param_base + 0x6);
+ INT32 b = *(INT16*)(gba->mem.oam + param_base + 0xe);
+ INT32 c = *(INT16*)(gba->mem.oam + param_base + 0x16);
+ INT32 d = *(INT16*)(gba->mem.oam + param_base + 0x1e);
+
+ INT64 x1 = sx << 8;
+ INT64 y1 = sy << 8;
+ INT64 objref_x = (x_size << (double_size ? 8 : 7));
+ INT64 objref_y = (y_size << (double_size ? 8 : 7));
+
+ INT64 x2 = a * (x1 - objref_x) + b * (y1 - objref_y) + (x_size << 15);
+ INT64 y2 = c * (x1 - objref_x) + d * (y1 - objref_y) + (y_size << 15);
+
+ sx = (x2 >> 16);
+ sy = (y2 >> 16);
+ if (sx >= x_size || sy >= y_size || sx < 0 || sy < 0)
+ continue;
+ } else {
+ if (h_flip)
+ sx = x_size - sx - 1;
+ if (v_flip)
+ sy = y_size - sy - 1;
+ }
+ INT32 tx = sx % 8;
+ INT32 ty = sy % 8;
+
+ INT32 y_tile_stride = obj_vram_map_2d ? 32 : x_size / 8 * (colors_or_palettes ? 2 : 1);
+ INT32 tile = tile_base + (((sx / 8)) * (colors_or_palettes ? 2 : 1)) + (sy / 8) * y_tile_stride;
+ // Don't allow the column indices to overflow into the row indices in 2D mode.
+ // See: https://github.com/skylersaleh/SkyEmu/issues/13
+ if (obj_vram_map_2d) {
+ tile = (tile_base + (((sx / 8)) * (colors_or_palettes ? 2 : 1))) & 31;
+ tile |= (tile_base + (sy / 8) * y_tile_stride) & ~31;
+ }
+ //Tiles >511 are not rendered in bg_mode3-5 since that memory is used to store the bitmap graphics.
+ if (tile < 512 && bg_mode >= 3 && bg_mode <= 5)
+ continue;
+ UINT8 palette_id;
+ INT32 obj_tile_base = GBA_OBJ_TILES0_2;
+ bool transparent = false;
+ if (colors_or_palettes == false) {
+ palette_id = gba->mem.vram[obj_tile_base + tile * 8 * 4 + tx / 2 + ty * 4];
+ palette_id = (palette_id >> ((tx & 1) * 4)) & 0xf;
+ transparent = palette_id == 0;
+ palette_id += palette * 16;
+ } else {
+ palette_id = gba->mem.vram[obj_tile_base + tile * 8 * 4 + tx + ty * 8];
+ transparent = palette_id == 0;
+ }
+
+ UINT32 col = *(UINT16*)(gba->mem.palette + GBA_OBJ_PALETTE + palette_id * 2);
+ //Handle window objects(not displayed but control the windowing of other things)
+ if (obj_mode == 2 && !transparent) {
+ gba->window[x] = obj_window_control;
+ } else if (obj_mode != 3) {
+ INT32 type = 4;
+ col = col | (type << 17) | ((5 - priority) << 28) | ((0x7) << 25);
+ if (obj_mode == 1)
+ col |= 1 << 16;
+ if ((col >> 17) > (gba->first_target_buffer[x] >> 17)) {
+ if (transparent) {
+ //Update priority for transparent pixels (needed for golden sun)
+ if (SB_BFE(gba->first_target_buffer[x], 17, 3) != 5)
+ gba->first_target_buffer[x] = (gba->first_target_buffer[x] & (0x0fffffff)) | (col & 0xf0000000);
+ } else gba->first_target_buffer[x] = col;
+ }
+ }
+ }
+ }
+ }
+ }
+ INT32 enabled_windows = SB_BFE(dispcnt, 13, 3); // [0: win0, 1:win1, 2: objwin]
+ if (enabled_windows) {
+ for (INT32 win = 1; win >= 0; --win) {
+ bool win_enable = SB_BFE(dispcnt, 13 + win, 1);
+ if (!win_enable)
+ continue;
+ UINT16 WINH = gba_io_read16(gba, GBA_WIN0H + 2 * win);
+ UINT16 WINV = gba_io_read16(gba, GBA_WIN0V + 2 * win);
+ INT32 win_xmin = SB_BFE(WINH, 8, 8);
+ INT32 win_xmax = SB_BFE(WINH, 0, 8);
+ INT32 win_ymin = SB_BFE(WINV, 8, 8);
+ INT32 win_ymax = SB_BFE(WINV, 0, 8);
+ // Garbage values of X2>240 or X1>X2 are interpreted as X2=240.
+ // Garbage values of Y2>160 or Y1>Y2 are interpreted as Y2=160.
+ if (win_xmin > win_xmax)
+ win_xmax = 240;
+ if (win_ymin > win_ymax)
+ win_ymax = 161;
+ if (win_xmax > 240)
+ win_xmax = 240;
+ if (sprite_lcd_y < win_ymin || sprite_lcd_y >= win_ymax)
+ continue;
+ UINT16 winin = gba_io_read16(gba, GBA_WININ);
+ UINT8 win_value = SB_BFE(winin, win * 8, 6);
+ for (INT32 x = win_xmin; x < win_xmax; ++x)
+ gba->window[x] = win_value;
+ }
+ INT32 backdrop_type = 5;
+ UINT32 backdrop_col = (*(UINT16*)(gba->mem.palette + GBA_BG_PALETTE + 0 * 2)) | (backdrop_type << 17);
+ for (INT32 x = 0; x < 240; ++x) {
+ UINT8 window_control = gba->window[x];
+ if (SB_BFE(window_control, 4, 1) == 0)
+ gba->first_target_buffer[x] = backdrop_col;
+ }
+ }
+ }
+
+ if (visible) {
+ UINT8 window_control = gba->window[lcd_x];
+ if (bg_mode == 6 || bg_mode == 7) {
+ //Palette 0 is taken as the background
+ } else if (bg_mode <= 5) {
+ for (INT32 bg = 3; bg >= 0; --bg) {
+ UINT32 col = 0;
+ if ((bg < 2 && bg_mode == 2) || (bg == 3 && bg_mode == 1) || (bg != 2 && bg_mode >= 3))
+ continue;
+ bool bg_en = SB_BFE(dispcnt, 8 + bg, 1) && SB_BFE(gba->ppu.dispcnt_pipeline[0], 8 + bg, 1);
+ if (!bg_en || SB_BFE(window_control, bg, 1) == 0)
+ continue;
+
+ bool rot_scale = bg_mode >= 1 && bg >= 2;
+ UINT16 bgcnt = gba_io_read16(gba, GBA_BG0CNT + bg * 2);
+ INT32 priority = SB_BFE(bgcnt, 0, 2);
+ INT32 character_base = SB_BFE(bgcnt, 2, 2);
+ bool mosaic = SB_BFE(bgcnt, 6, 1);
+ bool colors = SB_BFE(bgcnt, 7, 1);
+ INT32 screen_base = SB_BFE(bgcnt, 8, 5);
+ bool display_overflow = SB_BFE(bgcnt, 13, 1);
+ INT32 screen_size = SB_BFE(bgcnt, 14, 2);
+
+ INT32 screen_size_x = (screen_size & 1) ? 512 : 256;
+ INT32 screen_size_y = (screen_size >= 2) ? 512 : 256;
+
+ INT32 bg_x = 0;
+ INT32 bg_y = 0;
+
+ if (rot_scale) {
+ screen_size_x = screen_size_y = (16 * 8) << screen_size;
+ if (bg_mode == 3 || bg_mode == 4) {
+ screen_size_x = 240;
+ screen_size_y = 160;
+ } else if (bg_mode == 5) {
+ screen_size_x = 160;
+ screen_size_y = 128;
+ }
+ colors = true;
+
+ INT32 bgx = gba->ppu.aff[bg - 2].render_bgx;
+ INT32 bgy = gba->ppu.aff[bg - 2].render_bgy;
+
+ INT32 a = (INT16)gba_io_read16(gba, GBA_BG2PA + (bg - 2) * 0x10);
+ INT32 c = (INT16)gba_io_read16(gba, GBA_BG2PC + (bg - 2) * 0x10);
+
+ // Shift lcd_coords into fixed point
+ INT64 x2 = a * lcd_x + (((INT64)bgx));
+ INT64 y2 = c * lcd_x + (((INT64)bgy));
+ if (mosaic) {
+ INT16 mos_reg = gba_io_read16(gba, GBA_MOSAIC);
+ INT32 mos_x = SB_BFE(mos_reg, 0, 4) + 1;
+ x2 = a * ((lcd_x / mos_x) * mos_x) + (((INT64)bgx));
+ y2 = c * ((lcd_x / mos_x) * mos_x) + (((INT64)bgy));
+ }
+
+
+ bg_x = (x2 >> 8);
+ bg_y = (y2 >> 8);
+
+ if (display_overflow == 0) {
+ if (bg_x < 0 || bg_x >= screen_size_x || bg_y < 0 || bg_y >= screen_size_y)
+ continue;
+ } else {
+ bg_x %= screen_size_x;
+ bg_y %= screen_size_y;
+ }
+ } else {
+ INT16 hoff = gba_io_read16(gba, GBA_BG0HOFS + bg * 4);
+ INT16 voff = gba_io_read16(gba, GBA_BG0VOFS + bg * 4);
+ hoff = (hoff << 7) >> 7;
+ voff = (voff << 7) >> 7;
+ bg_x = (hoff + lcd_x);
+ bg_y = (voff + lcd_y);
+ if (mosaic) {
+ UINT16 mos_reg = gba_io_read16(gba, GBA_MOSAIC);
+ INT32 mos_x = SB_BFE(mos_reg, 0, 4) + 1;
+ INT32 mos_y = SB_BFE(mos_reg, 4, 4) + 1;
+ bg_x = hoff + (lcd_x / mos_x) * mos_x;
+ bg_y = voff + (lcd_y / mos_y) * mos_y;
+ }
+ }
+ if (bg_mode == 3) {
+ INT32 p = bg_x + bg_y * 240;
+ INT32 addr = p * 2;
+ col = *(UINT16*)(gba->mem.vram + addr);
+ } else if (bg_mode == 4) {
+ INT32 p = bg_x + bg_y * 240;
+ INT32 frame_sel = SB_BFE(dispcnt, 4, 1);
+ INT32 addr = p * 1 + 0xA000 * frame_sel;
+ UINT8 pallete_id = gba->mem.vram[addr];
+ if (pallete_id == 0)
+ continue;
+ col = *(UINT16*)(gba->mem.palette + GBA_BG_PALETTE + pallete_id * 2);
+ } else if (bg_mode == 5) {
+ INT32 p = bg_x + bg_y * 160;
+ INT32 frame_sel = SB_BFE(dispcnt, 4, 1);
+ INT32 addr = p * 2 + 0xA000 * frame_sel;
+ col = *(UINT16*)(gba->mem.vram + addr);
+ } else {
+ bg_x = bg_x & (screen_size_x - 1);
+ bg_y = bg_y & (screen_size_y - 1);
+ INT32 bg_tile_x = bg_x / 8;
+ INT32 bg_tile_y = bg_y / 8;
+
+ INT32 tile_off = bg_tile_y * (screen_size_x / 8) + bg_tile_x;
+
+ INT32 screen_base_addr = screen_base * 2048;
+ INT32 character_base_addr = character_base * 16 * 1024;
+
+ UINT16 tile_data = 0;
+
+ INT32 px = bg_x % 8;
+ INT32 py = bg_y % 8;
+
+ if (rot_scale)tile_data = gba->mem.vram[screen_base_addr + tile_off];
+ else {
+ INT32 tile_off = (bg_tile_y % 32) * 32 + (bg_tile_x % 32);
+ if (bg_tile_x >= 32)
+ tile_off += 32 * 32;
+ if (bg_tile_y >= 32)
+ tile_off += 32 * 32 * (screen_size == 3 ? 2 : 1);
+ tile_data = *(UINT16*)(gba->mem.vram + screen_base_addr + tile_off * 2);
+
+ INT32 h_flip = SB_BFE(tile_data, 10, 1);
+ INT32 v_flip = SB_BFE(tile_data, 11, 1);
+ if (h_flip)
+ px = 7 - px;
+ if (v_flip)
+ py = 7 - py;
+ }
+ INT32 tile_id = SB_BFE(tile_data, 0, 10);
+ INT32 palette = SB_BFE(tile_data, 12, 4);
+
+ UINT8 tile_d = tile_id;
+ if (colors == false) {
+ INT32 addr = character_base_addr + tile_id * 8 * 4 + px / 2 + py * 4;
+ tile_d = gba->mem.vram[addr];
+ tile_d = (tile_d >> ((px & 1) * 4)) & 0xf;
+ //There is an undocumented GBA quirk where tiles over 64KB are not loaded
+ if (tile_d == 0 || SB_UNLIKELY(addr >= 0x10000))
+ continue;
+ tile_d += palette * 16;
+ } else {
+ //There is an undocumented GBA quirk where tiles over 64KB are not loaded
+ INT32 addr = character_base_addr + tile_id * 8 * 8 + px + py * 8;
+ tile_d = gba->mem.vram[addr];
+ if (tile_d == 0 || SB_UNLIKELY(addr >= 0x10000))
+ continue;
+ }
+ UINT8 pallete_id = tile_d;
+ col = *(UINT16*)(gba->mem.palette + GBA_BG_PALETTE + pallete_id * 2);
+ }
+ col |= (bg << 17) | ((5 - priority) << 28) | ((4 - bg) << 25);
+ if (col > gba->first_target_buffer[lcd_x]) {
+ UINT32 t = gba->first_target_buffer[lcd_x];
+ gba->first_target_buffer[lcd_x] = col;
+ col = t;
+ }
+ if (col > gba->second_target_buffer[lcd_x])
+ gba->second_target_buffer[lcd_x] = col;
+ }
+ }
+ UINT32 col = gba->first_target_buffer[lcd_x];
+ INT32 r = SB_BFE(col, 0, 5);
+ INT32 g = SB_BFE(col, 5, 5);
+ INT32 b = SB_BFE(col, 10, 5);
+ UINT32 type = SB_BFE(col, 17, 3);
+
+ bool effect_enable = SB_BFE(window_control, 5, 1);
+ UINT16 bldcnt = gba_io_read16(gba, GBA_BLDCNT);
+ INT32 mode = SB_BFE(bldcnt , 6, 2);
+
+ //Semitransparent objects are always selected for blending
+ if (SB_BFE(col, 16, 1)) {
+ UINT32 col2 = gba->second_target_buffer[lcd_x];
+ UINT32 type2 = SB_BFE(col2, 17, 3);
+ bool blend = SB_BFE(bldcnt, 8 + type2, 1);
+ if (blend) {
+ mode = 1;
+ effect_enable = true;
+ } else effect_enable &= SB_BFE(bldcnt, type, 1);
+ } else effect_enable &= SB_BFE(bldcnt, type, 1);
+ if (effect_enable) {
+ UINT16 bldy = gba_io_read16(gba, GBA_BLDY);
+ float evy = SB_BFE(bldy, 0, 5) / 16.;
+ if (evy > 1.0)
+evy = 1;
+ switch (mode) {
+ case 0:
+ break; //None
+ case 1: {
+ UINT32 col2 = gba->second_target_buffer[lcd_x];
+ UINT32 type2 = SB_BFE(col2, 17, 3);
+ bool blend = SB_BFE(bldcnt, 8 + type2, 1);
+ if (blend) {
+ UINT16 bldalpha = gba_io_read16(gba, GBA_BLDALPHA);
+ INT32 r2 = SB_BFE(col2, 0, 5);
+ INT32 g2 = SB_BFE(col2, 5, 5);
+ INT32 b2 = SB_BFE(col2, 10, 5);
+ INT32 eva = SB_BFE(bldalpha, 0, 5);
+ INT32 evb = SB_BFE(bldalpha, 8, 5);
+ if (eva > 16) eva = 16;
+ if (evb > 16) evb = 16;
+ r = (r * eva + r2 * evb) / 16;
+ g = (g * eva + g2 * evb) / 16;
+ b = (b * eva + b2 * evb) / 16;
+ if (r > 31) r = 31;
+ if (g > 31) g = 31;
+ if (b > 31) b = 31;
+ }
+ }
+ break; //Alpha Blend
+ case 2: //Lighten
+ r = r + (31 - r) * evy;
+ g = g + (31 - g) * evy;
+ b = b + (31 - b) * evy;
+ break;
+ case 3: //Darken
+ r = r - (r ) * evy;
+ g = g - (g ) * evy;
+ b = b - (b ) * evy;
+ break;
+ }
+ }
+ if (forced_blank) {
+ r = g = b = 255;
+ if (gba->stop_mode)
+ r = g = b = 0;
+ }
+
+ INT32 backdrop_type = 5;
+ UINT32 backdrop_col = (*(UINT16*)(gba->mem.palette + GBA_BG_PALETTE + 0 * 2)) | (backdrop_type << 17);
+ gba->first_target_buffer[ lcd_x] = backdrop_col;
+ gba->second_target_buffer[lcd_x] = backdrop_col;
+
+ INT32 p = (lcd_x + lcd_y * 240) * 4;
+ float screen_blend_factor = 0.3 * gba->ppu.ghosting_strength;
+ UINT16 green_swap = gba_io_read16(gba, GBA_GREENSWP);
+ gba->framebuffer[p + 0] = r * 8 * (1.0 - screen_blend_factor) + gba->framebuffer[p + 0] * screen_blend_factor;
+ gba->framebuffer[p + 2] = b * 8 * (1.0 - screen_blend_factor) + gba->framebuffer[p + 2] * screen_blend_factor;
+
+ if (green_swap & 1) {
+ if (p & 4)
+ gba->framebuffer[p + 1 - 4] = g * 8 * (1.0 - screen_blend_factor) + gba->framebuffer[p + 1 - 4] * screen_blend_factor;
+ else
+ gba->framebuffer[p + 1 + 4] = g * 8 * (1.0 - screen_blend_factor) + gba->framebuffer[p + 1 + 4] * screen_blend_factor;
+ } else {
+ gba->framebuffer[p + 1] = g * 8 * (1.0 - screen_blend_factor) + gba->framebuffer[p + 1] * screen_blend_factor;
+ }
+ }
+}
+
+static void gba_tick_keypad(sb_joy_t* joy, gba_t* gba)
+{
+ UINT16 reg_value = 0;
+ //Null joy updates are used to tick the joypad when mmios are set
+ if (joy) {
+ reg_value |= !(joy->inputs[SE_KEY_A ] > 0.3) << 0;
+ reg_value |= !(joy->inputs[SE_KEY_B ] > 0.3) << 1;
+ reg_value |= !(joy->inputs[SE_KEY_SELECT] > 0.3) << 2;
+ reg_value |= !(joy->inputs[SE_KEY_START ] > 0.3) << 3;
+ reg_value |= !(joy->inputs[SE_KEY_RIGHT ] > 0.3) << 4;
+ reg_value |= !(joy->inputs[SE_KEY_LEFT ] > 0.3) << 5;
+ reg_value |= !(joy->inputs[SE_KEY_UP ] > 0.3) << 6;
+ reg_value |= !(joy->inputs[SE_KEY_DOWN ] > 0.3) << 7;
+ reg_value |= !(joy->inputs[SE_KEY_R ] > 0.3) << 8;
+ reg_value |= !(joy->inputs[SE_KEY_L ] > 0.3) << 9;
+ gba_io_store16(gba, GBA_KEYINPUT, reg_value);
+ } else
+ reg_value = gba_io_read16(gba, GBA_KEYINPUT);
+
+ UINT16 keycnt = gba_io_read16(gba, GBA_KEYCNT);
+ bool irq_enable = SB_BFE(keycnt, 14, 1);
+ bool irq_condition = SB_BFE(keycnt, 15, 1); //[0: any key, 1: all keys]
+ INT32 if_bit = 0;
+ if (irq_enable || gba->stop_mode) {
+ UINT16 pressed = SB_BFE(reg_value, 0, 10) ^ 0x3ff;
+ UINT16 mask = SB_BFE(keycnt, 0, 10);
+
+ if (irq_condition && ((pressed & mask) == mask))
+ if_bit |= 1 << GBA_INT_KEYPAD;
+ if (!irq_condition && ((pressed & mask) != 0))
+ if_bit |= 1 << GBA_INT_KEYPAD;
+
+ if (if_bit)
+ gba->stop_mode = false;
+
+ if (if_bit && !gba->prev_key_interrupt && irq_enable) {
+ gba_send_interrupt(gba, 4, if_bit);
+ gba->prev_key_interrupt = true;
+ } else
+ gba->prev_key_interrupt = false;
+
+ }
+}
+
+UINT64 gba_read_eeprom_bitstream(gba_t* gba, UINT32 source_address, INT32 offset, INT32 size, INT32 elem_size, INT32 dir)
+{
+ UINT64 data = 0;
+ for (INT32 i = 0; i < size; ++i) {
+ data |= ((UINT64)(gba_read16(gba, source_address + (i + offset) * elem_size * dir) & 1)) << (size - i - 1);
+ }
+ return data;
+}
+
+void gba_store_eeprom_bitstream(gba_t* gba, UINT32 source_address, INT32 offset, INT32 size, INT32 elem_size, INT32 dir, UINT64 data)
+{
+ for (INT32 i = 0; i < size; ++i) {
+ gba_store16(gba, source_address + (i + offset) * elem_size * dir, data >> (size - i - 1) & 1);
+ }
+}
+
+static FORCE_INLINE INT32 gba_tick_dma(gba_t*gba, INT32 last_tick)
+{
+ INT32 ticks = 0;
+ gba->activate_dmas = false;
+ gba->dma_wait_ppu = false;
+ for (INT32 i = 0;i < 4;++i) {
+ UINT16 cnt_h = gba_io_read16(gba, GBA_DMA0CNT_H + 12 * i);
+ bool enable = SB_BFE(cnt_h, 15, 1);
+ if (enable) {
+ bool type = SB_BFE(cnt_h, 10, 1); // 0: 16b 1:32b
+
+ if (!gba->dma[i].last_enable) {
+ gba->dma[i].last_enable = enable;
+ gba->dma[i].source_addr = gba_io_read32(gba, GBA_DMA0SAD + 12 * i);
+ gba->dma[i].dest_addr = gba_io_read32(gba, GBA_DMA0DAD + 12 * i);
+ //GBA Suite says that these need to be force aligned
+ if (type) {
+ gba->dma[i].dest_addr &= ~3;
+ gba->dma[i].source_addr &= ~3;
+ } else {
+ gba->dma[i].dest_addr &= ~1;
+ gba->dma[i].source_addr &= ~1;
+ }
+ gba->dma[i].current_transaction = 0;
+ gba->dma[i].startup_delay = 2;
+ }
+ INT32 dst_addr_ctl = SB_BFE(cnt_h, 5, 2); // 0: incr 1: decr 2: fixed 3: incr reload
+ INT32 src_addr_ctl = SB_BFE(cnt_h, 7, 2); // 0: incr 1: decr 2: fixed 3: not allowed
+ bool dma_repeat = SB_BFE(cnt_h, 9, 1);
+ INT32 mode = SB_BFE(cnt_h, 12, 2);
+ bool irq_enable = SB_BFE(cnt_h, 14, 1);
+ bool force_first_write_sequential = false;
+ INT32 transfer_bytes = type ? 4 : 2;
+ bool skip_dma = false;
+ if (gba->dma[i].current_transaction == 0) {
+ if (mode == 3 && i == 0)
+ continue;
+ if (gba->dma[i].startup_delay >= 0) {
+ gba->dma[i].startup_delay -= last_tick;
+ if (gba->dma[i].startup_delay >= 0) {
+ gba->activate_dmas = true;
+ continue;
+ }
+ gba->dma[i].startup_delay = -1;
+ }
+ if (dst_addr_ctl == 3) {
+ gba->dma[i].dest_addr = gba_io_read32(gba, GBA_DMA0DAD + 12 * i);
+ }
+ bool last_vblank = gba->dma[i].last_vblank;
+ bool last_hblank = gba->dma[i].last_hblank;
+ gba->dma[i].last_vblank = gba->ppu.last_vblank;
+ gba->dma[i].last_hblank = gba->ppu.last_hblank;
+ if (mode == 1 && (!gba->ppu.last_vblank || last_vblank)) {
+ gba->dma_wait_ppu = true;
+ continue;
+ }
+ if (mode == 2) {
+ gba->dma_wait_ppu = true;
+ UINT16 vcount = gba_io_read16(gba, GBA_VCOUNT);
+ if (vcount >= 160 || !gba->ppu.last_hblank || last_hblank)
+ continue;
+ }
+ //Video dma
+ if (mode == 3 && i == 3) {
+ gba->dma_wait_ppu = true;
+ UINT16 vcount = gba_io_read16(gba, GBA_VCOUNT);
+ if (!gba->ppu.last_hblank || last_hblank)
+ continue;
+ //Video dma starts at scanline 2
+ if (vcount == 2) {
+ gba->dma[i].video_dma_active = true;
+ }
+ if (!gba->dma[i].video_dma_active)
+ continue;
+ if (vcount == 161) {
+ dma_repeat = false;
+ gba->dma[i].video_dma_active = false;
+ }
+ }
+
+ if (dst_addr_ctl == 3) {
+ gba->dma[i].dest_addr = gba_io_read32(gba, GBA_DMA0DAD + 12 * i);
+ //GBA Suite says that these need to be force aligned
+ if (type)
+ gba->dma[i].dest_addr &= ~3;
+ else
+ gba->dma[i].dest_addr &= ~1;
+ }
+ bool audio_dma = (mode == 3) && (i == 1 || i == 2);
+ if (audio_dma) {
+ if (gba->dma[i].activate_audio_dma == false)
+ continue;
+ gba->dma[i].activate_audio_dma = false;
+ }
+ if (gba->dma[i].source_addr >= 0x08000000 && gba->dma[i].dest_addr >= 0x08000000) {
+ force_first_write_sequential = true;
+ } else {
+ if (gba->dma[i].dest_addr >= 0x08000000) {
+ // Allow the in process prefetech to finish before starting DMA
+ if (!gba->mem.prefetch_size && gba->mem.prefetch_en)
+ ticks += gba_compute_access_cycles_dma(gba, gba->dma[i].dest_addr, 2) > 4;
+ }
+ }
+ if (gba->dma[i].source_addr >= 0x08000000) {
+ if (gba->mem.prefetch_en)
+ ticks += gba_compute_access_cycles_dma(gba, gba->dma[i].source_addr, 2) <= 4;
+ }
+ gba->last_transaction_dma = true;
+ UINT32 cnt = gba_io_read16(gba, GBA_DMA0CNT_L + 12 * i);
+
+ if (i != 3)
+ cnt &= 0x3fff;
+ if (cnt == 0)
+ cnt = i == 3 ? 0x10000 : 0x4000;
+
+ static const UINT32 src_mask[] = { 0x07FFFFFF, 0x0FFFFFFF, 0x0FFFFFFF, 0x0FFFFFFF };
+ static const UINT32 dst_mask[] = { 0x07FFFFFF, 0x07FFFFFF, 0x07FFFFFF, 0x0FFFFFFF };
+ gba->dma[i].source_addr &= src_mask[i];
+ gba->dma[i].dest_addr &= dst_mask[i];
+ gba_io_store16(gba, GBA_DMA0CNT_L + 12 * i, cnt);
+ }
+ const static INT32 dir_lookup[4] = { 1, -1, 0, 1 };
+ INT32 src_dir = dir_lookup[src_addr_ctl];;
+ INT32 dst_dir = dir_lookup[dst_addr_ctl];
+
+ UINT32 src = gba->dma[i].source_addr;
+ UINT32 dst = gba->dma[i].dest_addr;;
+ UINT32 cnt = gba_io_read16(gba, GBA_DMA0CNT_L + 12 * i);
+
+ // ROM ignores direction and always increments
+ if (src >= 0x08000000 && src < 0x0e000000)
+ src_dir = 1;
+ if (dst >= 0x08000000 && dst < 0x0e000000)
+ dst_dir = 1;
+
+ // EEPROM DMA transfers
+ if (i == 3 && gba->cart.backup_type == GBA_BACKUP_EEPROM) {
+ INT32 src_in_eeprom = (src & 0x1ffffff) >= gba->cart.rom_size || (src & 0x1ffffff) >= 0x01ffff00;
+ INT32 dst_in_eeprom = (dst & 0x1ffffff) >= gba->cart.rom_size || (dst & 0x1ffffff) >= 0x01ffff00;
+ src_in_eeprom &= src >= 0x8000000 && src <= 0xDFFFFFF;
+ dst_in_eeprom &= dst >= 0x8000000 && dst <= 0xDFFFFFF;
+ skip_dma = src_in_eeprom || dst_in_eeprom;
+ if (dst_in_eeprom) {
+ if (cnt == 73) {
+ // Write data 6 bit address
+ UINT32 addr = gba_read_eeprom_bitstream(gba, src, 2, 6, type ? 4 : 2, src_dir);
+ UINT64 data = gba_read_eeprom_bitstream(gba, src, 2 + 6, 64, type ? 4 : 2, src_dir);
+ ((UINT64*)gba->mem.cart_backup)[addr] = data;
+ gba->cart.backup_is_dirty = true;
+ } else if (cnt == 81) {
+ // Write data 14 bit address
+ UINT32 addr = gba_read_eeprom_bitstream(gba, src, 2, 14, type ? 4 : 2, src_dir) & 0x3ff;
+ UINT64 data = gba_read_eeprom_bitstream(gba, src, 2 + 14, 64, type ? 4 : 2, src_dir);
+ ((UINT64*)gba->mem.cart_backup)[addr] = data;
+ gba->cart.backup_is_dirty = true;
+ } else if (cnt == 9) {
+ // 2 bits "11" (Read Request)
+ // 6 bits eeprom address (MSB first)
+ // 1 bit "0"
+ // Write data 6 bit address
+ gba->mem.eeprom_addr = gba_read_eeprom_bitstream(gba, src, 2, 6, type ? 4 : 2, src_dir);
+ } else if (cnt == 17) {
+ // 2 bits "11" (Read Request)
+ // 14 bits eeprom address (MSB first)
+ // 1 bit "0"
+ // Write data 6 bit address
+ gba->mem.eeprom_addr = gba_read_eeprom_bitstream(gba, src, 2, 14, type ? 4 : 2, src_dir) & 0x3ff;
+ } else {
+ printf("Bad cnt: %d for eeprom write\n", cnt);
+ }
+ gba->dma[i].current_transaction = cnt;
+ }
+ if (src_in_eeprom) {
+ if (cnt == 68) {
+ UINT64 data = ((UINT64*)gba->mem.cart_backup)[gba->mem.eeprom_addr];
+ gba_store_eeprom_bitstream(gba, dst, 4, 64, type ? 4 : 2, dst_dir, data);
+ } else {
+ printf("Bad cnt: %d for eeprom read\n", cnt);
+ }
+ gba->dma[i].current_transaction = cnt;
+ }
+ }
+ bool audio_dma = (mode == 3) && (i == 1 || i == 2);
+ if (audio_dma) {
+ INT32 fifo = i - 1;
+ dst &= ~3;
+ src &= ~3;
+ for (INT32 x = 0;x < 4;++x) {
+ UINT32 src_addr = src + x * 4 * src_dir;
+ UINT32 data = gba_read32(gba, src_addr);
+ gba_audio_fifo_push(gba, fifo, SB_BFE(data, 0, 8));
+ gba_audio_fifo_push(gba, fifo, SB_BFE(data, 8, 8));
+ gba_audio_fifo_push(gba, fifo, SB_BFE(data, 16, 8));
+ gba_audio_fifo_push(gba, fifo, SB_BFE(data, 24, 8));
+ ticks += gba_compute_access_cycles_dma(gba, src_addr, x != 0 ? 2 : 3);
+ ticks += gba_compute_access_cycles_dma(gba, dst, x != 0 || force_first_write_sequential ? 2 : 3);
+ }
+ dst_addr_ctl = 2;
+ transfer_bytes = 4;
+ cnt = 4;
+ skip_dma = true;
+ gba->dma[i].current_transaction = cnt;
+ } else if (!skip_dma) {
+ if (gba->dma[i].current_transaction < cnt) {
+ INT32 x = gba->dma[i].current_transaction++;
+ INT32 dst_addr = dst + x * transfer_bytes * dst_dir;
+ INT32 src_addr = src + x * transfer_bytes * src_dir;
+ if (type) {
+ if (src_addr >= 0x02000000) {
+ gba->dma[i].latched_transfer = gba_read32(gba, src_addr);
+ ticks += gba_compute_access_cycles_dma(gba, src_addr, x != 0 ? 2 : 3);
+ }
+ gba_dma_write32(gba, dst_addr, gba->dma[i].latched_transfer);
+ ticks += gba_compute_access_cycles_dma(gba, dst_addr, x != 0 || force_first_write_sequential ? 2 : 3);
+ } else {
+ INT32 v = 0;
+ if (src_addr >= 0x02000000) {
+ v = gba->dma[i].latched_transfer = (gba_read16(gba, src_addr)) & 0xffff;
+ gba->dma[i].latched_transfer |= gba->dma[i].latched_transfer << 16;
+ ticks += gba_compute_access_cycles_dma(gba, src_addr, x != 0 ? 0 : 1);
+ } else
+ v = gba->dma[i].latched_transfer >> (((dst_addr) & 0x3) * 8);
+ gba_dma_write16(gba, dst_addr, v & 0xffff);
+ ticks += gba_compute_access_cycles_dma(gba, dst_addr, x != 0 || force_first_write_sequential ? 0 : 1);
+ }
+ }
+ }
+
+ if (gba->dma[i].current_transaction >= cnt) {
+ if (dst_addr_ctl == 0 || dst_addr_ctl == 3)
+ dst += cnt * transfer_bytes;
+ else if (dst_addr_ctl == 1)
+ dst -= cnt * transfer_bytes;
+ if (src_addr_ctl == 0)
+ src += cnt * transfer_bytes;
+ else if (src_addr_ctl == 1)
+ src -= cnt * transfer_bytes;
+
+ gba->dma[i].source_addr = src;
+ gba->dma[i].dest_addr = dst;
+
+ if (irq_enable) {
+ UINT16 if_bit = 1 << (GBA_INT_DMA0 + i);
+ gba_send_interrupt(gba, 4, if_bit);
+ }
+ if (!dma_repeat || mode == 0) {
+ cnt_h &= 0x7fff;
+ //gba_io_store16(gba, GBA_DMA0CNT_L+12*i,0);
+ //Reload on incr reload
+ enable = false;
+ gba_io_store16(gba, GBA_DMA0CNT_H + 12 * i, cnt_h);
+ } else {
+ gba->dma[i].current_transaction = 0;
+ }
+ }
+ }
+ gba->dma[i].last_enable = enable;
+ if (ticks)
+ break;
+ }
+ gba->activate_dmas |= ticks != 0;
+
+ if (gba->last_transaction_dma && ticks == 0) {
+ ticks += 2;
+ gba->last_transaction_dma = false;
+ }
+
+ return ticks;
+}
+
+static FORCE_INLINE void gba_tick_sio(gba_t* gba)
+{
+ //Just a stub for now;
+ UINT16 siocnt = gba_io_read16(gba, GBA_SIOCNT);
+ bool active = SB_BFE(siocnt, 7, 1);
+ bool irq_enabled = SB_BFE(siocnt, 14, 1);
+ if (active) {
+ if (gba->sio.last_active == false) {
+ gba->sio.last_active = true;
+ gba->sio.ticks_till_transfer_done = 8 * 8;
+ }
+ bool internal_clock = SB_BFE(siocnt, 0, 1);
+ if (internal_clock)
+ gba->sio.ticks_till_transfer_done--;
+ if (gba->sio.ticks_till_transfer_done <= 0) {
+ if (irq_enabled) {
+ UINT16 if_bit = 1 << (GBA_INT_SERIAL);
+ gba_send_interrupt(gba, 4, if_bit);
+ }
+ siocnt &= ~(1 << 7);
+ gba_io_store16(gba, GBA_SIOCNT, siocnt);
+ gba->sio.last_active = false;
+ gba_io_store8( gba, GBA_SIODATA8, 0);
+ gba_io_store32(gba, GBA_SIODATA32, 0);
+ }
+
+ }
+}
+
+static FORCE_INLINE void gba_tick_timers(gba_t* gba)
+{
+ gba->deferred_timer_ticks += 1;
+ if (SB_UNLIKELY(gba->deferred_timer_ticks >= gba->timer_ticks_before_event))
+ gba_compute_timers(gba);
+}
+
+static void gba_compute_timers(gba_t* gba)
+{
+ INT32 ticks = gba->deferred_timer_ticks;
+ UINT32 old_global_timer = gba->global_timer;
+ gba->global_timer += gba->deferred_timer_ticks;
+
+ gba->deferred_timer_ticks = 0;
+ INT32 last_timer_overflow = 0;
+ INT32 timer_ticks_before_event = 32768;
+ const INT32 prescaler_lookup[] = { 0,6,8,10 };
+ for (INT32 t = 0; t < 4; ++t) {
+ UINT16 tm_cnt_h = gba_io_read16(gba, GBA_TM0CNT_H + t * 4);
+ bool enable = SB_BFE(tm_cnt_h, 7, 1);
+ if (enable) {
+ INT32 compensated_ticks = ticks;
+ UINT16 prescale = SB_BFE(tm_cnt_h, 0, 2);
+ bool count_up = SB_BFE(tm_cnt_h, 2, 1) && t != 0;
+ bool irq_en = SB_BFE(tm_cnt_h, 6, 1);
+ UINT16 value = gba_io_read16(gba, GBA_TM0CNT_L + t * 4);
+ if (enable != gba->timers[t].last_enable && enable) {
+ gba->timers[t].startup_delay = 2;
+ value = gba->timers[t].reload_value;
+ gba_io_store16(gba, GBA_TM0CNT_L + t * 4, value);
+ }
+ if (gba->timers[t].startup_delay >= 0) {
+ gba->timers[t].startup_delay -= ticks;
+ gba->timers[t].last_enable = enable;
+ if (gba->timers[t].startup_delay >= 0) {
+ if (gba->timers[t].startup_delay < timer_ticks_before_event)timer_ticks_before_event = gba->timers[t].startup_delay;
+ continue;
+ }
+ compensated_ticks = -gba->timers[t].startup_delay;
+ gba->timers[t].startup_delay = -1;
+ }
+ if (count_up) {
+ if (last_timer_overflow) {
+ UINT32 v = value;
+ v += last_timer_overflow;
+ last_timer_overflow = 0;
+ while (v > 0xffff) {
+ v = (v + gba->timers[t].reload_value) - 0x10000;
+ last_timer_overflow++;
+ }
+ value = v;
+ }
+ } else {
+ last_timer_overflow = 0;
+ INT32 prescale_duty = prescaler_lookup[prescale];
+
+ INT32 increment = (gba->global_timer >> prescale_duty) - (old_global_timer >> prescale_duty);
+ INT32 v = value + increment;
+ while (v > 0xffff) {
+ v = (v + gba->timers[t].reload_value) - 0x10000;
+ last_timer_overflow++;
+ }
+ value = v;
+ INT32 ticks_before_overflow = (int)(0xffff - value) << (prescale_duty);
+ if (ticks_before_overflow < timer_ticks_before_event)
+ timer_ticks_before_event = ticks_before_overflow;
+ }
+ if (last_timer_overflow) {
+ UINT16 soundcnt_h = gba_io_read16(gba, GBA_SOUNDCNT_H);
+ if (t < 2) {
+ for (INT32 i = 0; i < 2; ++i) {
+ INT32 timer = SB_BFE(soundcnt_h, 10 + i * 4, 1);
+ if (timer != t)
+ continue;
+ INT32 samples_to_pop = last_timer_overflow;
+ INT32 size = (gba->audio.fifo[i].write_ptr - gba->audio.fifo[i].read_ptr) & 0x1f;
+ while (samples_to_pop-- && size) {
+ gba->audio.fifo[i].read_ptr = (gba->audio.fifo[i].read_ptr + 1) & 0x1f;
+ --size;
+ }
+ if (size < GBA_AUDIO_DMA_ACTIVATE_THRESHOLD)
+ gba->dma[i + 1].activate_audio_dma = gba->activate_dmas = true;
+ }
+ }
+ if (irq_en) {
+ UINT16 if_bit = 1 << (GBA_INT_TIMER0 + t);
+ gba_send_interrupt(gba, 4, if_bit);
+ }
+ }
+ gba->timers[t].reload_value = gba->timers[t].pending_reload_value;
+
+ gba_io_store16(gba, GBA_TM0CNT_L + t * 4, value);
+ } else
+ last_timer_overflow = 0;
+ gba->timers[t].last_enable = enable;
+ }
+ gba->timer_ticks_before_event = timer_ticks_before_event;
+}
+
+static FORCE_INLINE float gba_polyblep(float t, float dt)
+{
+ if (t <= dt) {
+ t = t / dt;
+ return t + t - t * t - 1.0;;
+ } else if (t >= 1 - dt) {
+ t = (t - 1.0) / dt;
+ return t * t + t + t + 1.0;
+ } else return 0;
+}
+
+static FORCE_INLINE float gba_bandlimited_square(float t, float duty_cycle, float dt)
+{
+ float t2 = t - duty_cycle;
+ if (t2 < 0.0)
+ t2 += 1.0;
+ float y = t < duty_cycle ? -1 : 1;
+ y -= gba_polyblep(t, dt);
+ y += gba_polyblep(t2, dt);
+ return y;
+}
+
+static FORCE_INLINE void gba_send_interrupt(gba_t* gba, INT32 delay, INT32 if_bit)
+{
+ if (if_bit) {
+ gba->active_if_pipe_stages |= 1 << delay;
+ gba->pipelined_if[delay] |= if_bit;
+ }
+}
+
+static FORCE_INLINE void gba_tick_interrupts(gba_t* gba)
+{
+ if (SB_UNLIKELY(gba->active_if_pipe_stages)) {
+ UINT16 if_bit = gba->pipelined_if[0];
+ if (if_bit) {
+ UINT16 if_val = gba_io_read16(gba, GBA_IF);
+ if_val |= if_bit;
+ gba_io_store16(gba, GBA_IF, if_val);
+ }
+ gba->pipelined_if[0] = gba->pipelined_if[1];
+ gba->pipelined_if[1] = gba->pipelined_if[2];
+ gba->pipelined_if[2] = gba->pipelined_if[3];
+ gba->pipelined_if[3] = gba->pipelined_if[4];
+ gba->pipelined_if[4] = 0;
+ gba->active_if_pipe_stages >>= 1;
+ }
+}
+
+UINT64 gba_decrypt_arv3(UINT64 code)
+{
+ const UINT32 S0 = 0x7AA9648F;
+ const UINT32 S1 = 0x7FAE6994;
+ const UINT32 S2 = 0xC0EFAAD5;
+ const UINT32 S3 = 0x42712C57;
+
+ UINT32 l = code >> 32;
+ UINT32 r = code & 0xFFFFFFFF;
+
+ UINT32 tmp = 0x9E3779B9 << 5;
+
+ for (INT32 i = 0; i < 32; i++) {
+ r -= ((l << 4) + S2) ^ (l + tmp) ^ ((l >> 5) + S3);
+ l -= ((r << 4) + S0) ^ (r + tmp) ^ ((r >> 5) + S1);
+ tmp -= 0x9E3779B9;
+ }
+
+ return ((UINT64)l << 32) | r;
+}
+
+bool gba_handle_ar_if_instruction(gba_t* gba, UINT32 left, UINT32 right)
+{
+ UINT32 address = ((left << 4) & 0x0F000000) | (left & 0x000FFFFF);
+ UINT8 current_code = (left >> 24) & 0xFF;
+ UINT32 left_compare = gba_read32(gba, address);
+ UINT32 right_compare = right;
+ INT32 left_compare_signed = 0;
+ INT32 right_compare_signed = 0;
+
+ switch (current_code & 0x6) {
+ case 0: {
+ left_compare &= 0xFF;
+ right_compare &= 0xFF;
+ left_compare_signed = (INT8)(left_compare);
+ right_compare_signed = (INT8)(right_compare);
+ break;
+ }
+ case 0x2: {
+ left_compare &= 0xFFFF;
+ right_compare &= 0xFFFF;
+ left_compare_signed = (INT16)(left_compare);
+ right_compare_signed = (INT16)(right_compare);
+ break;
+ }
+ case 0x4: {
+ left_compare_signed = (INT32)(left_compare);
+ right_compare_signed = (INT32)(right_compare);
+ break;
+ }
+ case 0x6: {
+ printf("Invalid AR if instruction\n");
+ return false;
+ }
+ }
+
+ switch (current_code & 0x38) {
+ case 0x8: {
+ // Equal
+ return left_compare == right_compare;
+ }
+ case 0x10: {
+ // Not equal
+ return left_compare != right_compare;
+ }
+ case 0x18: {
+ // Signed <
+ return left_compare_signed < right_compare_signed;
+ }
+ case 0x20: {
+ // Signed >
+ return left_compare_signed > right_compare_signed;
+ }
+ case 0x28: {
+ // Unsigned <
+ return left_compare < right_compare;
+ }
+ case 0x30: {
+ // Unsigned >
+ return left_compare > right_compare;
+ }
+ case 0x38: {
+ // Logical AND
+ return left_compare && right_compare;
+ }
+ }
+
+ return false;
+};
+
+bool gba_run_ar_cheat(gba_t* gba, const UINT32* buffer, UINT32 size)
+{
+ if (size % 2 != 0) {
+ printf("Invalid Action Replay cheat size:%d\n", size);
+ return false;
+ }
+
+ // stack for if statements
+ // the first element is always true
+ bool if_stack[32] = { true };
+ size_t if_stack_index = 0;
+ // Let's treat the AR button as always pressed for now
+ bool ar_button_pressed = true;
+
+ for (INT32 i = 0;i < size;i += 2) {
+ UINT64 code = gba_decrypt_arv3(buffer[i + 1] | ((UINT64)buffer[i] << 32));
+ UINT32 left = code >> 32;
+ UINT32 right = code & 0xFFFFFFFF;
+
+ if (!if_stack[if_stack_index])
+ continue;
+ if (right == 0x1DC0DE) {
+ continue;
+ }
+
+ if (left != 0) {
+ UINT8 current_code = (left >> 24) & 0xFF;
+ UINT32 address = ((left << 4) & 0x0F000000) | (left & 0x000FFFFF);
+
+ switch (current_code) {
+ case 0x00: {
+ UINT32 offset = right >> 8;
+ UINT8 data = right & 0xFF;
+ gba_store8(gba, address + offset, data);
+ break;
+ }
+ case 0x02: {
+ UINT32 offset = right >> 16;
+ UINT16 data = right & 0xFFFF;
+ gba_store16(gba, address + offset * 2, data);
+ break;
+ }
+ case 0x04: {
+ UINT32 data = right;
+ gba_store32(gba, address, data);
+ break;
+ }
+ case 0x40: {
+ UINT32 offset = right >> 8;
+ UINT8 data = right & 0xFF;
+ address = gba_read32(gba, address);
+ gba_store8(gba, address + offset, data);
+ break;
+ }
+ case 0x42: {
+ UINT32 offset = right >> 16;
+ UINT16 data = right & 0xFFFF;
+ address = gba_read32(gba, address);
+ gba_store16(gba, address + offset * 2, data);
+ break;
+ }
+ case 0x44: {
+ UINT32 data = right;
+ address = gba_read32(gba, address);
+ gba_store32(gba, address, data);
+ break;
+ }
+ case 0x80: {
+ UINT8 data = right & 0xFF;
+ UINT8 old_data = gba_read8(gba, address);
+ gba_store8(gba, address, old_data + data);
+ break;
+ }
+ case 0x82: {
+ UINT16 data = right & 0xFFFF;
+ UINT16 old_data = gba_read16(gba, address);
+ gba_store16(gba, address, old_data + data);
+ break;
+ }
+ case 0x84: {
+ UINT32 data = right;
+ UINT32 old_data = gba_read32(gba, address);
+ gba_store32(gba, address, old_data + data);
+ break;
+ }
+ case 0xC4: {
+ continue;
+ }
+ case 0xC6: {
+ UINT32 address = 0x4000000 | (left & 0xFFFFFF);
+ UINT16 data = right & 0xFFFF;
+ gba_store16(gba, address, data);
+ break;
+ }
+ case 0xC7: {
+ UINT32 address = 0x4000000 | (left & 0xFFFFFF);
+ UINT32 data = right;
+ gba_store32(gba, address, data);
+ break;
+ }
+ default: {
+ // if instruction
+ bool condition = gba_handle_ar_if_instruction(gba, left, right);
+
+ switch (current_code & 0xC0) {
+ case 0x00: {
+ if (!condition) {
+ // skip next instruction
+ i += 2;
+ continue;
+ }
+ }
+ case 0x40: {
+ if (!condition) {
+ // skip next two instructions
+ i += 4;
+ continue;
+ }
+ break;
+ }
+ case 0x80: {
+ break;
+ }
+ case 0xC0: {
+ if (!condition) {
+ // turn off all codes
+ return true;
+ }
+ break;
+ }
+ };
+
+ if_stack_index++;
+
+ if (if_stack_index == 32) {
+ printf("Action Replay if stack size exceeded\n");
+ return false;
+ };
+
+ if_stack[if_stack_index] = condition;
+ break;
+ }
+ }
+ } else {
+ UINT8 current_code = (right >> 24) & 0xFF;
+
+ if (right == 0) {
+ // end of code list
+ return true;
+ }
+
+ switch (current_code) {
+ case 0x60: {
+ // else
+ if_stack[if_stack_index] ^= true;
+ break;
+ }
+ case 0x40: {
+ // end if
+ if (if_stack_index == 0) {
+ printf("Unexpected Action Replay end if instruction\n");
+ return false;
+ }
+ if_stack_index--;
+ break;
+ }
+ case 0x08: {
+ // AR slowdown
+ // Probably has no effect on emulators
+ break;
+ }
+ case 0x18:
+ case 0x1A:
+ case 0x1C:
+ case 0x1E: {
+ if (i + 3 >= size)
+ return false;
+ UINT32 address = 0x8000000 | ((right & 0xFFFFFF) << 1);
+ UINT64 decrypted = gba_decrypt_arv3(buffer[i + 3] | ((UINT64)buffer[i + 2] << 32));
+ UINT16 data = decrypted >> 32;
+ gba->mem.cart_rom[(address + 0) & 0x1FFFFFF] = data;
+ gba->mem.cart_rom[(address + 1) & 0x1FFFFFF] = data >> 8;
+ i += 2;
+ break;
+ }
+ case 0x10: {
+ // IF AR_BUTTON THEN [a0aaaaa]=zz
+ if (i + 3 >= size)return false;
+ if (ar_button_pressed) {
+ UINT64 decrypted = gba_decrypt_arv3(buffer[i + 3] | ((UINT64)buffer[i + 2] << 32));
+ UINT32 address = ((right << 4) & 0x0F000000) | (right & 0x000FFFFF);
+ UINT8 data = decrypted >> 32;
+ gba_store8(gba, address, data);
+ }
+ i += 2;
+ break;
+ }
+ case 0x12: {
+ // IF AR_BUTTON THEN [a0aaaaa]=zzzz
+ if (i + 3 >= size)
+ return false;
+ if (ar_button_pressed) {
+ UINT64 decrypted = gba_decrypt_arv3(buffer[i + 3] | ((UINT64)buffer[i + 2] << 32));
+ UINT32 address = ((right << 4) & 0x0F000000) | (right & 0x000FFFFF);
+ UINT16 data = decrypted >> 32;;
+ gba_store16(gba, address, data);
+ }
+ i += 2;
+ break;
+ }
+ case 0x14: {
+ // IF AR_BUTTON THEN [a0aaaaa]=zzzzzzzz
+ if (i + 3 >= size)
+ return false;
+ if (ar_button_pressed) {
+ UINT64 decrypted = gba_decrypt_arv3(buffer[i + 3] | ((UINT64)buffer[i + 2] << 32));
+ UINT32 address = ((right << 4) & 0x0F000000) | (right & 0x000FFFFF);
+ UINT32 data = decrypted >> 32;
+ gba_store32(gba, address, data);
+ }
+ i += 2;
+ break;
+ }
+ case 0x80:
+ case 0x82:
+ case 0x84: {
+ // 00000000 8naaaaaa 000000yy ssccssss repeat cc times [a0aaaaa]=yy
+ // (with yy=yy+ss, a0aaaaa=a0aaaaa+ssss after each step)
+ if (i + 3 >= size)
+ return false;
+ UINT64 decrypted = gba_decrypt_arv3(buffer[i + 3] | ((UINT64)buffer[i + 2] << 32));
+ UINT32 address = ((right << 4) & 0x0F000000) | (right & 0x000FFFFF);
+ UINT8 repeat = (decrypted >> 16) & 0xFF;
+ UINT8 data_increment = (decrypted >> 24) & 0xFF;
+ UINT32 address_increment = decrypted & 0xFFFF;
+ UINT32 data = decrypted >> 32;
+
+ if ((current_code & 0xF) == 0x2) {
+ address_increment *= 2;
+ } else if ((current_code & 0xF) == 0x4) {
+ address_increment *= 4;
+ }
+
+ for (INT32 j = 0;j < repeat;j++) {
+ if ((current_code & 0xF) == 0x2) {
+ gba_store16(gba, address, data);
+ } else if ((current_code & 0xF) == 0x4) {
+ gba_store32(gba, address, data);
+ } else {
+ gba_store8(gba, address, data);
+ }
+ address += address_increment;
+ data += data_increment;
+ }
+
+ i += 2;
+ break;
+ }
+ default: {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+// BEGIN GB REUSE CODE SHIM
+#define sb_compute_next_sweep_freq gba_compute_next_sweep_freq
+#define sb_tick_frame_sweep gba_tick_frame_sweep
+#define sb_tick_frame_seq gba_tick_frame_seq
+#define sb_process_audio_writes gba_process_audio_writes
+#define sb_process_audio gba_tick_audio
+#define sb_frame_sequencer_t gba_frame_sequencer_t
+#define sb_audio_t gba_audio_t
+#define sb_gb_t gba_t
+#define sb_read8_io gba_audio_read8
+#define sb_store8_io gba_audio_store8
+#define sb_bandlimited_square gba_bandlimited_square
+#define sb_gbc_enable(a) (true)
+#define sb_read_wave_ram gba_read_wave_ram
+#define GBA_AUDIO 1
+
+#define SB_IO_AUD1_TONE_SWEEP 0xff10
+#define SB_IO_AUD1_LENGTH_DUTY 0xff11
+#define SB_IO_AUD1_VOL_ENV 0xff12
+#define SB_IO_AUD1_FREQ 0xff13
+#define SB_IO_AUD1_FREQ_HI 0xff14
+
+#define SB_IO_AUD2_LENGTH_DUTY 0xff16
+#define SB_IO_AUD2_VOL_ENV 0xff17
+#define SB_IO_AUD2_FREQ 0xff18
+#define SB_IO_AUD2_FREQ_HI 0xff19
+
+#define SB_IO_AUD3_POWER 0xff1A
+#define SB_IO_AUD3_LENGTH 0xff1B
+#define SB_IO_AUD3_VOL 0xff1C
+#define SB_IO_AUD3_FREQ 0xff1D
+#define SB_IO_AUD3_FREQ_HI 0xff1E
+#define SB_IO_AUD3_WAVE_BASE 0xff30
+
+#define SB_IO_AUD4_LENGTH 0xff20
+#define SB_IO_AUD4_VOL_ENV 0xff21
+#define SB_IO_AUD4_POLY 0xff22
+#define SB_IO_AUD4_COUNTER 0xff23
+#define SB_IO_MASTER_VOLUME 0xff24
+#define SB_IO_SOUND_OUTPUT_SEL 0xff25
+
+#define SB_IO_SOUND_ON_OFF 0xff26
+
+static INT32 gba_lookup_gb_reg(INT32 gb_reg)
+{
+ switch (gb_reg) {
+ case SB_IO_AUD1_TONE_SWEEP : return GBA_SOUND1CNT_L;
+ case SB_IO_AUD1_LENGTH_DUTY: return GBA_SOUND1CNT_H;
+ case SB_IO_AUD1_VOL_ENV : return GBA_SOUND1CNT_H + 1;
+ case SB_IO_AUD1_FREQ : return GBA_SOUND1CNT_X;
+ case SB_IO_AUD1_FREQ_HI : return GBA_SOUND1CNT_X + 1;
+ case SB_IO_AUD2_LENGTH_DUTY: return GBA_SOUND2CNT_L;
+ case SB_IO_AUD2_VOL_ENV : return GBA_SOUND2CNT_L + 1;
+ case SB_IO_AUD2_FREQ : return GBA_SOUND2CNT_H;
+ case SB_IO_AUD2_FREQ_HI : return GBA_SOUND2CNT_H + 1;
+ case SB_IO_AUD3_POWER : return GBA_SOUND3CNT_L;
+ case SB_IO_AUD3_LENGTH : return GBA_SOUND3CNT_H;
+ case SB_IO_AUD3_VOL : return GBA_SOUND3CNT_H + 1;
+ case SB_IO_AUD3_FREQ : return GBA_SOUND3CNT_X;
+ case SB_IO_AUD3_FREQ_HI : return GBA_SOUND3CNT_X + 1;
+ case SB_IO_AUD4_LENGTH : return GBA_SOUND4CNT_L;
+ case SB_IO_AUD4_VOL_ENV : return GBA_SOUND4CNT_L + 1;
+ case SB_IO_AUD4_POLY : return GBA_SOUND4CNT_H;
+ case SB_IO_AUD4_COUNTER : return GBA_SOUND4CNT_H + 1;
+ case SB_IO_MASTER_VOLUME : return GBA_SOUNDCNT_L;
+ case SB_IO_SOUND_OUTPUT_SEL: return GBA_SOUNDCNT_L + 1;
+ case SB_IO_SOUND_ON_OFF : return GBA_SOUNDCNT_X;
+ }
+ printf("Unknown GB register:%04x\n", gb_reg);
+ return 0;
+}
+
+static INT32 gba_inverse_lookup_gb_reg(INT32 gb_reg)
+{
+ switch (gb_reg) {
+ case GBA_SOUND1CNT_L : return SB_IO_AUD1_TONE_SWEEP;
+ case GBA_SOUND1CNT_H : return SB_IO_AUD1_LENGTH_DUTY;
+ case GBA_SOUND1CNT_H + 1: return SB_IO_AUD1_VOL_ENV;
+ case GBA_SOUND1CNT_X : return SB_IO_AUD1_FREQ;
+ case GBA_SOUND1CNT_X + 1: return SB_IO_AUD1_FREQ_HI;
+ case GBA_SOUND2CNT_L : return SB_IO_AUD2_LENGTH_DUTY;
+ case GBA_SOUND2CNT_L + 1: return SB_IO_AUD2_VOL_ENV;
+ case GBA_SOUND2CNT_H : return SB_IO_AUD2_FREQ;
+ case GBA_SOUND2CNT_H + 1: return SB_IO_AUD2_FREQ_HI;
+ case GBA_SOUND3CNT_L : return SB_IO_AUD3_POWER;
+ case GBA_SOUND3CNT_H : return SB_IO_AUD3_LENGTH;
+ case GBA_SOUND3CNT_H + 1: return SB_IO_AUD3_VOL;
+ case GBA_SOUND3CNT_X : return SB_IO_AUD3_FREQ;
+ case GBA_SOUND3CNT_X + 1: return SB_IO_AUD3_FREQ_HI;
+ case GBA_SOUND4CNT_L : return SB_IO_AUD4_LENGTH;
+ case GBA_SOUND4CNT_L + 1: return SB_IO_AUD4_VOL_ENV;
+ case GBA_SOUND4CNT_H : return SB_IO_AUD4_POLY;
+ case GBA_SOUND4CNT_H + 1: return SB_IO_AUD4_COUNTER;
+ case GBA_SOUNDCNT_L : return SB_IO_MASTER_VOLUME;
+ case GBA_SOUNDCNT_L + 1 : return SB_IO_SOUND_OUTPUT_SEL;
+ case GBA_SOUNDCNT_X : return SB_IO_SOUND_ON_OFF;
+ }
+ return 0;
+}
+static UINT8 gba_audio_read8(gba_t* gba, INT32 addr){
+ return gba_io_read8(gba,gba_lookup_gb_reg(addr));
+}
+
+static void gba_audio_store8(gba_t* gba, INT32 addr, UINT8 data)
+{
+ addr = gba_lookup_gb_reg(addr);
+ if (addr)gba_io_store8(gba, addr, data);
+}
+
+static UINT8 gba_audio_process_byte_write(gba_t* gba, UINT32 addr, UINT8 value)
+{
+ gba_t* gb = gba;
+ sb_frame_sequencer_t* seq = &gba->audio.sequencer;
+ addr = gba_inverse_lookup_gb_reg(addr);
+ INT32 i = (addr - SB_IO_AUD1_LENGTH_DUTY) / 5;
+ if (!addr)
+ return value;
+ if (addr == SB_IO_SOUND_ON_OFF) {
+ value &= 0xf0;
+ value |= sb_read8_io(gb, SB_IO_SOUND_ON_OFF) & 0xf;
+ }
+ if (addr >= SB_IO_AUD3_WAVE_BASE && addr < SB_IO_AUD3_WAVE_BASE + 16) {
+ bool wave_active = SB_BFE(sb_read8_io(gb, SB_IO_SOUND_ON_OFF), 2, 1);
+ if (wave_active) {
+ //Addr locked to the read pointer when the wave channel is active
+ addr = SB_IO_AUD3_WAVE_BASE + ((gb->audio.wave_sample_offset) % 32) / 2;
+ }
+ }
+ if (addr == SB_IO_AUD1_LENGTH_DUTY || addr == SB_IO_AUD2_LENGTH_DUTY || addr == SB_IO_AUD3_LENGTH || addr == SB_IO_AUD4_LENGTH) {
+ UINT8 length_duty = value;
+ if (i == 2)
+ seq->length[i] = 256 - SB_BFE(length_duty, 0, 8);
+ else
+ seq->length[i] = 64 - SB_BFE(length_duty, 0, 6);
+ } else if (addr == SB_IO_AUD1_VOL_ENV || addr == SB_IO_AUD2_VOL_ENV || addr == SB_IO_AUD4_VOL_ENV) {
+ bool power = SB_BFE(value, 3, 5) != 0;
+ seq->powered[i] = power;
+ seq->active[i] &= power;
+ seq->env_direction[i] = (SB_BFE(value, 3, 1) ? 1 : -1);
+ seq->env_period[i] = SB_BFE(value, 0, 3);
+ if (seq->env_period[i] == 0 && !seq->env_overflow[i]) {
+ seq->volume[i] = (seq->volume[i] + 1) & 0xf;
+ }
+ } else if (addr == SB_IO_AUD1_FREQ || addr == SB_IO_AUD1_FREQ_HI ||
+ addr == SB_IO_AUD2_FREQ || addr == SB_IO_AUD2_FREQ_HI ||
+ addr == SB_IO_AUD3_FREQ || addr == SB_IO_AUD3_FREQ_HI
+ ) {
+ sb_store8_io(gb, addr, value);
+ UINT8 freq_lo = sb_read8_io(gb, SB_IO_AUD1_FREQ + i * 5);
+ UINT8 freq_hi = sb_read8_io(gb, SB_IO_AUD1_FREQ_HI + i * 5);
+ seq->frequency[i] = freq_lo | ((int)(SB_BFE(freq_hi, 0, 3)) << 8u);
+ }
+ return value;
+};
+
+static UINT8 sb_read_wave_ram(sb_gb_t* gb, INT32 byte)
+{
+ return gba_io_read8(gb, GBA_WAVE_RAM + byte);
+}
+
+static INT32 sb_compute_next_sweep_freq(sb_frame_sequencer_t* seq)
+{
+ INT32 shift = seq->sweep_shift ? seq->sweep_shift : 8;
+ INT32 increment = (seq->frequency[0] >> shift) * seq->sweep_direction;
+ INT32 new_frequency = seq->frequency[0] + increment;
+ seq->sweep_subtracted |= seq->sweep_direction == -1;
+ return new_frequency;
+}
+
+static void sb_tick_frame_sweep(sb_frame_sequencer_t* seq)
+{
+ INT32 new_frequency = sb_compute_next_sweep_freq(seq);
+ if (new_frequency > 2047) {
+ seq->active[0] = false;
+ new_frequency = 2047;
+ } else if (new_frequency < 0)new_frequency = 0;
+ if (seq->sweep_shift) {
+ seq->frequency[0] = new_frequency;
+ new_frequency = sb_compute_next_sweep_freq(seq);
+ if (new_frequency > 2047) {
+ seq->active[0] = false;
+ new_frequency = 2047;
+ }
+ }
+}
+
+static void sb_tick_frame_seq(sb_gb_t* gb, sb_frame_sequencer_t* seq)
+{
+ INT32 step = (seq->step_counter++) % 8;
+ //Tick sweep
+ if (step == 2 || step == 6) {
+ if (seq->active[0] && seq->sweep_enable) {
+ if (seq->sweep_timer > 0)
+ seq->sweep_timer--;
+ if (seq->sweep_timer == 0) {
+ if (seq->sweep_period > 0) {
+ seq->sweep_timer = seq->sweep_period;
+ sb_tick_frame_sweep(seq);
+ } else
+ seq->sweep_timer = 8;
+ }
+ }
+ }
+ //Tick envelope
+ if (step == 7) {
+ for (INT32 i = 0; i < 4; ++i) {
+ if (i == 2)
+ continue;
+ if (seq->env_period[i]) {
+ if (seq->env_period_timer[i] > 0)
+ seq->env_period_timer[i]--;
+ if (seq->env_period_timer[i] == 0) {
+ seq->env_period_timer[i] = seq->env_period[i];
+ INT32 volume = seq->volume[i];
+ volume += seq->env_direction[i];
+ if (volume <= 0) {
+ volume = 0;
+ seq->env_overflow[i] = true;
+ }
+ if (volume > 0xF) {
+ volume = 0xF;
+ seq->env_overflow[i] = true;
+ };
+ seq->volume[i] = volume;
+ }
+ }
+ }
+ }
+ if ((step % 2) == 0) {
+ //Tick length
+ for (INT32 i = 0; i < 4; ++i) {
+ if (!seq->use_length[i])
+ continue;
+ if (seq->length[i] > 0)
+ seq->length[i]--;
+ if (seq->length[i] == 0) {
+ seq->active[i] = false;
+ seq->length[i] = i == 2 ? 256 : 64;
+ seq->use_length[i] = false;
+ }
+ }
+ }
+ INT32 nrf_52 = sb_read8_io(gb, SB_IO_SOUND_ON_OFF) & 0xf0;
+ for (INT32 i = 0; i < 4; ++i) {
+ seq->active[i] &= seq->powered[i];
+ bool active = seq->active[i];
+ nrf_52 |= active << i;
+ }
+ sb_store8_io(gb, SB_IO_SOUND_ON_OFF, nrf_52);
+}
+
+static void sb_process_audio_writes(sb_gb_t* gb)
+{
+ sb_audio_t* audio = &gb->audio;
+ sb_frame_sequencer_t* seq = &audio->sequencer;
+ INT32 nrf_52 = sb_read8_io(gb, SB_IO_SOUND_ON_OFF) & 0xf0;
+ bool master_enable = SB_BFE(nrf_52, 7, 1);
+ if (!master_enable) {
+ for (INT32 i = SB_IO_AUD1_TONE_SWEEP; i < SB_IO_SOUND_ON_OFF; ++i) {
+ sb_store8_io(gb, i, 0);
+ }
+ for (INT32 i = 0; i < 4; ++i) {
+ if (sb_gbc_enable(gb) || i != 3) {
+ seq->active[i ] = false;
+ seq->powered[i] = false;
+ seq->length[i ] = 0;
+ }
+ seq->use_length[i] = false;
+ }
+ } else {
+ UINT8 freq_sweep1 = sb_read8_io(gb, SB_IO_AUD1_TONE_SWEEP);
+ seq->sweep_period = SB_BFE(freq_sweep1, 4, 3);
+ seq->sweep_shift = SB_BFE(freq_sweep1, 0, 3);
+ seq->sweep_direction = SB_BFE(freq_sweep1, 3, 1) ? -1. : 1;
+ for (INT32 i = 0; i < 4; ++i) {
+ bool prev_length_en = seq->use_length[i];
+ UINT8 freq_hi = sb_read8_io(gb, SB_IO_AUD1_FREQ_HI + i * 5);
+ seq->use_length[i] = SB_BFE(freq_hi, 6, 1);
+ UINT8 vol_env = sb_read8_io(gb, SB_IO_AUD1_VOL_ENV + i * 5);
+ if (i == 2) {
+ bool power = SB_BFE(sb_read8_io(gb, SB_IO_AUD3_POWER), 7, 1);
+ seq->powered[i] = power;
+ }
+ if (i != 0) {
+ UINT8 freq_lo = sb_read8_io(gb, SB_IO_AUD1_FREQ + i * 5);
+ seq->frequency[i] = freq_lo | ((int)(SB_BFE(freq_hi, 0, 3)) << 8u);
+ }
+ if (i == 2) {
+ seq->env_direction[i] = 0;
+ seq->env_period[i ] = 0;
+ } else {
+ seq->env_direction[i] = (SB_BFE(vol_env, 3, 1) ? 1 : -1);
+ seq->env_period[i ] = SB_BFE(vol_env, 0, 3);
+ }
+ bool triggered = SB_BFE(freq_hi, 7, 1);
+ if (triggered) {
+ UINT8 length_duty = sb_read8_io(gb, SB_IO_AUD1_LENGTH_DUTY + i * 5);
+ UINT8 freq_lo = sb_read8_io(gb, SB_IO_AUD1_FREQ + i * 5);
+ seq->frequency[i] = freq_lo | ((int)(SB_BFE(freq_hi, 0, 3)) << 8u);
+ seq->volume[i ] = SB_BFE(vol_env, 4, 4);
+
+ if (seq->length[i] == 0)
+ seq->length[i] = i == 2 ? 256 : 64;
+ if (i == 3)
+ seq->lfsr4 = 0x7FFF;
+ if (i == 2) {
+ audio->wave_sample_offset = 31;
+ audio->wave_freq_timer = 4;
+ }
+ seq->env_period_timer[i] = 0;
+ seq->env_overflow[i ] = false;
+ seq->chan_t[i ] = 0;
+ seq->active[i ] = true;
+ if (i == 0) {
+ seq->sweep_subtracted = false;
+ seq->sweep_enable = seq->sweep_period || seq->sweep_shift;
+ seq->sweep_timer = seq->sweep_period;
+ if (seq->sweep_timer == 0)
+ seq->sweep_timer = 8;
+ if (seq->sweep_shift && sb_compute_next_sweep_freq(seq) > 2047) {
+ seq->active[0] = false;
+ }
+ seq->sweep_enable = seq->sweep_period > 0 || seq->sweep_shift > 0;
+ }
+ }
+ if (i == 0 && seq->sweep_subtracted && seq->sweep_direction != -1) {
+ seq->active[0] = false;
+ seq->sweep_enable = false;
+ }
+ if (seq->use_length[i] && !prev_length_en) {
+ bool second_half_of_length_period = (seq->step_counter & 1);
+ if (second_half_of_length_period) {
+ if (seq->length[i])
+ seq->length[i]--;
+ if (seq->length[i] == 0) {
+ if (triggered)
+ seq->length[i] = i == 2 ? 255 : 63;
+ else {
+ seq->active[i ] = false;
+ seq->use_length[i] = triggered && seq->use_length[i];
+ }
+ }
+ }
+ }
+ sb_store8_io(gb, SB_IO_AUD1_FREQ_HI + i * 5, freq_hi & 0x7f);
+ }
+ }
+ nrf_52 = sb_read8_io(gb, SB_IO_SOUND_ON_OFF) & 0xf0;
+ for (INT32 i = 0; i < 4; ++i) {
+ seq->active[i] &= seq->powered[i];
+ bool active = seq->active[i];
+ nrf_52 |= active << i;
+ }
+ sb_store8_io(gb, SB_IO_SOUND_ON_OFF, nrf_52);
+}
+
+static FORCE_INLINE void sb_process_audio(sb_gb_t *gb, sb_emu_state_t*emu, double delta_time, INT32 cycles)
+{
+ sb_audio_t* audio = &gb->audio;
+ sb_frame_sequencer_t* seq = &audio->sequencer;
+
+ if (delta_time > 1.0 / 60.)
+ delta_time = 1.0 / 60.;
+ audio->current_sim_time += delta_time;
+#ifdef GBA_AUDIO
+ UINT32 prev_audio_clock = audio->audio_clock;
+ audio->audio_clock += cycles;
+ cycles = (audio->audio_clock - (prev_audio_clock & ~3)) / 4;
+ UINT32 frame_cycles = (audio->audio_clock - (prev_audio_clock & ~32767)) / 32768;
+ while (frame_cycles--)
+ gba_tick_frame_seq(gb, seq);
+#endif
+
+ INT32 freq_tim = audio->wave_freq_timer;
+ freq_tim -= cycles;
+ if (freq_tim < 0) {
+ INT32 wave_inc_count = (-freq_tim - 1) / ((2048 - seq->frequency[2]) * 2) + 1;
+ audio->wave_sample_offset += wave_inc_count;
+ freq_tim += (2048 - seq->frequency[2]) * 2 * wave_inc_count;
+ UINT32 wav_samp = (audio->wave_sample_offset) % 32;
+ INT32 dat = sb_read_wave_ram(gb, wav_samp / 2);
+ audio->curr_wave_data = dat;
+ INT32 offset = (wav_samp & 1) ? 0 : 4;
+ audio->curr_wave_sample = ((dat >> offset) & 0xf);
+ }
+ audio->wave_freq_timer = freq_tim;
+
+ audio->current_sample_generated_time -= (int)(audio->current_sim_time);
+ audio->current_sim_time -= (int)(audio->current_sim_time);
+
+ if (audio->current_sample_generated_time > audio->current_sim_time)
+ return;
+
+ INT32 nrf_52 = sb_read8_io(gb, SB_IO_SOUND_ON_OFF) & 0xf0;
+
+ bool master_enable = SB_BFE(nrf_52, 7, 1);
+ if (!master_enable)
+ return;
+ if (emu->audio_sample_rate <= 0.0)
+ return;
+ float sample_delta_t = (float)(1.0 / emu->audio_sample_rate);
+
+ const static float duty_lookup[] = { 0.125, 0.25, 0.5, 0.75 };
+ UINT8 length_duty1 = sb_read8_io(gb, SB_IO_AUD1_LENGTH_DUTY);
+ float duty1 = duty_lookup[SB_BFE(length_duty1, 6, 2)];
+ UINT8 length_duty2 = sb_read8_io(gb, SB_IO_AUD2_LENGTH_DUTY);
+ float duty2 = duty_lookup[SB_BFE(length_duty2, 6, 2)];
+
+ UINT8 power3 = sb_read8_io(gb, SB_IO_AUD3_POWER);
+ UINT8 vol_env3 = sb_read8_io(gb, SB_IO_AUD3_VOL);
+ INT32 channel3_shift = SB_BFE(vol_env3, 5, 2) - 1;
+ if (SB_BFE(power3, 7, 1) == 0 || channel3_shift == -1)
+ channel3_shift = 4;
+
+ UINT8 poly4 = sb_read8_io(gb, SB_IO_AUD4_POLY);
+ float r4 = SB_BFE(poly4, 0, 3);
+ UINT8 s4 = SB_BFE(poly4, 4, 4);
+ bool sevenBit4 = SB_BFE(poly4, 3, 1);
+ if (r4 == 0)
+ r4 = 0.5;
+
+ UINT8 master_vol = sb_read8_io(gb, SB_IO_MASTER_VOLUME);
+ float master_left = SB_BFE(master_vol, 4, 3) / 7.;
+ float master_right = SB_BFE(master_vol, 0, 3) / 7.;
+
+ UINT8 chan_sel = sb_read8_io(gb, SB_IO_SOUND_OUTPUT_SEL);
+ //These are type int to allow them to be multiplied to enable/disable
+ float chan_l[6] = { 0 };
+ float chan_r[6] = { 0 };
+ for (INT32 i = 0;i < 4;++i) {
+ chan_l[i] = SB_BFE(chan_sel, i, 1);
+ chan_r[i] = SB_BFE(chan_sel, i + 4, 1);
+ }
+
+ #ifdef GBA_AUDIO
+ {
+ UINT16 soundcnt_h = gba_io_read16(gb, GBA_SOUNDCNT_H);
+ //These are type int to allow them to be multiplied to enable/disable
+ UINT16 chan_sel = gba_io_read16(gb, GBA_SOUNDCNT_L);
+ /* soundcnth
+ 0-1 R/W Sound # 1-4 Volume (0=25%, 1=50%, 2=100%, 3=Prohibited)
+ 2 R/W DMA Sound A Volume (0=50%, 1=100%)
+ 3 R/W DMA Sound B Volume (0=50%, 1=100%)
+ 4-7 - Not used
+ 8 R/W DMA Sound A Enable RIGHT (0=Disable, 1=Enable)
+ 9 R/W DMA Sound A Enable LEFT (0=Disable, 1=Enable)
+ 10 R/W DMA Sound A Timer Select (0=Timer 0, 1=Timer 1)
+ 11 W? DMA Sound A Reset FIFO (1=Reset)
+ 12 R/W DMA Sound B Enable RIGHT (0=Disable, 1=Enable)
+ 13 R/W DMA Sound B Enable LEFT (0=Disable, 1=Enable)
+ 14 R/W DMA Sound B Timer Select (0=Timer 0, 1=Timer 1)
+ 15 W? DMA Sound B Reset FIFO (1=Reset)*/
+ float psg_volume_lookup[4] = { 0.25,0.5,1.0,0. };
+ float psg_volume = psg_volume_lookup[SB_BFE(soundcnt_h, 0, 2)] * 0.25;
+
+ float r_vol = SB_BFE(chan_sel, 0, 3) / 7. * psg_volume;
+ float l_vol = SB_BFE(chan_sel, 4, 3) / 7. * psg_volume;
+ for (INT32 i = 0;i < 4;++i) {
+ chan_r[i] *= r_vol;
+ chan_l[i] *= l_vol;
+ }
+ // Channel volume for each FIFO
+ for (INT32 i = 0;i < 2;++i) {
+ // Volume
+ chan_r[i + 4] = chan_l[i + 4] = SB_BFE(soundcnt_h, 2 + i, 1) ? 1.0 : 0.5;
+ chan_r[i + 4] *= SB_BFE(soundcnt_h, 8 + i * 4, 1);
+ chan_l[i + 4] *= SB_BFE(soundcnt_h, 9 + i * 4, 1);
+ }
+ gba_io_store16(gb, GBA_SOUNDCNT_H, soundcnt_h & ~((1 << 11) | (1 << 15)));
+ master_left = master_right = 1;
+ }
+ #endif
+
+ float freq_hz[4];
+ for (INT32 i = 0;i < 2;++i) { freq_hz[i] = 131072. / (2048 - seq->frequency[i]); }
+ freq_hz[2] = (65536.) / (2048 - seq->frequency[2]);
+ freq_hz[3] = 524288.0 / r4 / pow(2.0, s4 + 1);
+ while (audio->current_sample_generated_time < audio->current_sim_time) {
+ audio->current_sample_generated_time += sample_delta_t;
+
+ bool buffer_full = sb_ring_buffer_size(&emu->audio_ring_buff) + 3 > SB_AUDIO_RING_BUFFER_SIZE;
+ if (emu->capture_audio && buffer_full)
+ continue;
+
+ //Advance each channel
+ for (INT32 i = 0; i < 4; ++i)
+ seq->chan_t[i] += sample_delta_t * freq_hz[i];
+ //Generate new noise value if needed
+ if (seq->chan_t[3] >= 1.0) {
+ INT32 bit = (seq->lfsr4 ^ (seq->lfsr4 >> 1)) & 1;
+ seq->lfsr4 >>= 1;
+ seq->lfsr4 |= bit << 14;
+ if (sevenBit4) {
+ seq->lfsr4 &= ~(1 << 7);
+ seq->lfsr4 |= bit << 6;
+ }
+ }
+
+ //Loopback
+ for (INT32 i = 0; i < 4; ++i)
+ seq->chan_t[i] -= (int)seq->chan_t[i];
+
+ //Compute and clamp Volume Envelopes
+ float v[4];
+ for (INT32 i = 0; i < 4; ++i)
+ v[i] = seq->active[i] ? seq->volume[i] / 15. : 0;
+ v[2] = 1.0;
+
+ INT32 dat = audio->curr_wave_sample >> channel3_shift;
+ INT32 wav_offset = 8 >> channel3_shift;
+
+ float channels[6];
+ channels[0] = sb_bandlimited_square(seq->chan_t[0], duty1, sample_delta_t * freq_hz[0]) * v[0];
+ channels[1] = sb_bandlimited_square(seq->chan_t[1], duty2, sample_delta_t * freq_hz[1]) * v[1];
+ channels[2] = (dat - wav_offset) / 8.;
+ channels[3] = ((seq->lfsr4 & 1) * 2. - 1.) * v[3];
+
+ #ifdef GBA_AUDIO
+ for (INT32 i = 0; i < 2; ++i)
+ channels[4 + i] = audio->fifo[i].data[audio->fifo[i].read_ptr & 0x1f] / 128.;
+ #else
+ for (INT32 i = 0; i < 2; ++i)
+ channels[4 + i] = 0;
+ #endif
+
+ //Mix channels
+ float sample_volume_l = 0;
+ float sample_volume_r = 0;
+ for (INT32 i = 0; i < 6; ++i) {
+ float l = channels[i] * chan_l[i];
+ float r = channels[i] * chan_r[i];
+ if (l >= -2. && l <= 2)
+ sample_volume_l += l;
+ if (r >= -2. && r <= 2)
+ sample_volume_r += r;
+ }
+
+ sample_volume_l *= 0.25;
+ sample_volume_r *= 0.25;
+ sample_volume_l *= master_left;
+ sample_volume_r *= master_right;
+
+ if (sample_volume_l > 1.0)
+ sample_volume_l = 1;
+ if (sample_volume_r > 1.0)
+ sample_volume_r = 1;
+ if (sample_volume_l < -1.0)
+ sample_volume_l = -1;
+ if (sample_volume_r < -1.0)
+ sample_volume_r = -1;
+ if (!(audio->capacitor_l<2 && audio->capacitor_l>-2))
+ audio->capacitor_l = 0;
+ if (!(audio->capacitor_r<2 && audio->capacitor_r>-2))
+ audio->capacitor_r = 0;
+ float out_l = sample_volume_l - audio->capacitor_l;
+ float out_r = sample_volume_r - audio->capacitor_r;
+ audio->capacitor_l = (sample_volume_l - out_l) * 0.996;
+ audio->capacitor_r = (sample_volume_r - out_r) * 0.996;
+ if (emu->capture_audio) {
+ UINT32 write_entry0 = (emu->audio_ring_buff.write_ptr++) % SB_AUDIO_RING_BUFFER_SIZE;
+ UINT32 write_entry1 = (emu->audio_ring_buff.write_ptr++) % SB_AUDIO_RING_BUFFER_SIZE;
+ emu->audio_ring_buff.data[write_entry0] = (INT16)(out_l * 32760);
+ emu->audio_ring_buff.data[write_entry1] = (INT16)(out_r * 32760);
+ }
+ }
+}
+
+#undef sb_compute_next_sweep_freq
+#undef sb_tick_frame_sweep
+#undef sb_tick_frame_seq
+#undef sb_process_audio_writes
+#undef sb_process_audio
+#undef sb_frame_sequencer_t
+#undef sb_audio_t
+#undef sb_gb_t
+#undef sb_read8_io
+#undef sb_store8_io
+#undef sb_bandlimited_square
+#undef sb_gbc_enable
+#undef sb_read_wave_ram
+
+#undef SB_IO_AUD1_TONE_SWEEP
+#undef SB_IO_AUD1_LENGTH_DUTY
+#undef SB_IO_AUD1_VOL_ENV
+#undef SB_IO_AUD1_FREQ
+#undef SB_IO_AUD1_FREQ_HI
+#undef SB_IO_AUD2_LENGTH_DUTY
+#undef SB_IO_AUD2_VOL_ENV
+#undef SB_IO_AUD2_FREQ
+#undef SB_IO_AUD2_FREQ_HI
+#undef SB_IO_AUD3_POWER
+#undef SB_IO_AUD3_LENGTH
+#undef SB_IO_AUD3_VOL
+#undef SB_IO_AUD3_FREQ
+#undef SB_IO_AUD3_FREQ_HI
+#undef SB_IO_AUD3_WAVE_BASE
+#undef SB_IO_AUD4_LENGTH
+#undef SB_IO_AUD4_VOL_ENV
+#undef SB_IO_AUD4_POLY
+#undef SB_IO_AUD4_COUNTER
+#undef SB_IO_MASTER_VOLUME
+#undef SB_IO_SOUND_OUTPUT_SEL
+#undef SB_IO_SOUND_ON_OFF
+
+#undef GBA_AUDIO
+
+// END GB REUSE CODE SHIM//
+
+void gba_cpu_trigger_breakpoint(void* data)
+{
+ gba_t* gba = (gba_t*)data;
+ gba->frame_in_progress = false;
+ gba->pause_after_frame = true;
+}
+
+void gba_ptrs_init(gba_t* gba, gba_scratch_t* scratch, UINT8* rom_data)
+{
+ gba->framebuffer = scratch->framebuffer;
+ gba->mem.bios = scratch->bios;
+ gba->mem.cart_rom = rom_data;
+ gba->cpu.log_cmp_file = scratch->log_cmp_file;
+ gba->cpu.read8 = arm7_read8;
+ gba->cpu.read16 = arm7_read16;
+ gba->cpu.read32 = arm7_read32;
+ gba->cpu.read16_seq = arm7_read16_seq;
+ gba->cpu.read32_seq = arm7_read32_seq;
+ gba->cpu.write8 = arm7_write8;
+ gba->cpu.write16 = arm7_write16;
+ gba->cpu.write32 = arm7_write32;
+ gba->cpu.user_data = gba;
+}
+
+void gba_tick(sb_emu_state_t* emu, gba_t* gba, gba_scratch_t* scratch)
+{
+ gba_ptrs_init(gba, scratch, emu->rom_data);
+ gba->cpu.user_data = gba;
+ gba->cpu.trigger_breakpoint = gba_cpu_trigger_breakpoint;
+
+ UINT64* d = (UINT64*)gba->mem.mmio_debug_access_buffer;
+ for (INT32 i = 0;i < sizeof(gba->mem.mmio_debug_access_buffer) / 8;++i) {
+ d[i] &= 0x9191919191919191ULL;
+ }
+
+ gba_tick_keypad(&emu->joy, gba);
+ gba->frame_in_progress = true;
+ float solar_value = emu->joy.solar_sensor;
+ if (!(solar_value < 1.00))
+ solar_value = 1.00;
+ if (!(solar_value > 0.00))
+ solar_value = 0.00;
+ gba->solar_sensor.pending_value = 0xE9 - solar_value * (0xE9 - 0x32); // latched into value when the game resets the sensor
+ gba->gyro_sensor.pending_sample = gba_gyro_sample(emu->joy.gyro_z);
+ gba->tilt_sensor.pending_x = gba_tilt_sample(emu->joy.tilt_x);
+ gba->tilt_sensor.pending_y = gba_tilt_sample(emu->joy.tilt_y);
+ gba->ppu.ghosting_strength = emu->screen_ghosting_strength;
+ while (gba->frame_in_progress) {
+ INT32 ticks = gba->activate_dmas ? gba_tick_dma(gba, gba->last_cpu_tick) : 0;
+ if (!ticks && gba->residual_dma_ticks) {
+ ticks = gba->residual_dma_ticks;
+ gba->residual_dma_ticks = 0;
+ }
+ if (!ticks) {
+ gba->cpu.i_cycles = 0;
+ gba->mem.requests = 0;
+ if (!gba->cpu.phased_op_id) {
+ UINT16 int_if = gba_io_read16(gba, GBA_IF);
+ if (SB_UNLIKELY(int_if)) {
+ int_if &= gba_io_read16(gba, GBA_IE);
+ UINT32 ime = gba_io_read32(gba, GBA_IME);
+ int_if *= SB_BFE(ime, 0, 1);
+ if (int_if)
+ arm7_process_interrupts(&gba->cpu);
+ }
+ }
+ arm7_exec_instruction(&gba->cpu);
+ gba->last_cpu_tick = ticks = gba->mem.requests + gba->cpu.i_cycles;
+ }
+ gba_tick_sio(gba);
+ INT32 ppu_fast_forward = gba->ppu.fast_forward_ticks;
+ INT32 timer_fast_forward = gba->timer_ticks_before_event - gba->deferred_timer_ticks;
+ INT32 fast_forward_ticks = ppu_fast_forward < timer_fast_forward ? ppu_fast_forward : timer_fast_forward;
+ if (fast_forward_ticks > ticks) {
+ if (gba->cpu.wait_for_interrupt)
+ ticks = fast_forward_ticks;
+ else
+ fast_forward_ticks = ticks;
+ }
+ if (SB_UNLIKELY(gba->active_if_pipe_stages)) {
+ for (INT32 i = 0;i < fast_forward_ticks;++i)
+ gba_tick_interrupts(gba);
+ }
+ // RTC advances with host wall time, not emulated master clocks
+ gba->deferred_timer_ticks += fast_forward_ticks;
+ gba->ppu.fast_forward_ticks -= fast_forward_ticks;
+ ticks -= fast_forward_ticks > ticks ? ticks : fast_forward_ticks;
+ double delta_t = ((double)ticks + fast_forward_ticks) / (16 * 1024 * 1024);
+ gba_tick_audio(gba, emu, delta_t, ticks + fast_forward_ticks);
+
+ bool last_activate_dmas = gba->activate_dmas;
+ for (INT32 t = 0;t < ticks;++t) {
+ if (gba->activate_dmas && !last_activate_dmas) {
+ gba->residual_dma_ticks = ticks - t - 1;
+ gba->last_cpu_tick = t + 1;
+ }
+ gba_tick_interrupts(gba);
+ gba_tick_timers(gba);
+ gba_tick_ppu(gba, emu->render_frame);
+ }
+ }
+ gba_gpio_update_rumble(gba);
+ emu->joy.rumble = gba->cart.gpio.rumble;
+ //LCD turns off in stop mode
+ if (gba->stop_mode)
+ memset(scratch->framebuffer, 0, sizeof(scratch->framebuffer));
+ if (gba->pause_after_frame) {
+ emu->run_mode = SB_MODE_PAUSE;
+ gba->pause_after_frame = false;
+ }
+}
+
+#endif
diff --git a/src/burn/drv/gba/host.h b/src/burn/drv/gba/host.h
new file mode 100644
index 000000000..7c42254e3
--- /dev/null
+++ b/src/burn/drv/gba/host.h
@@ -0,0 +1,162 @@
+#pragma once
+
+#ifndef GBA_HOST_H
+#define GBA_HOST_H
+
+#ifndef GBA_STANDALONE_TYPES
+#include "burn.h"
+#else
+#include "gba.h"
+#endif
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if defined(__GNUC__) || defined(__clang__)
+#define FORCE_INLINE inline __attribute__((always_inline))
+#define SB_UNLIKELY(x) __builtin_expect(!!(x), 0)
+#define SB_LIKELY(x) __builtin_expect(!!(x), 1)
+#elif defined(_MSC_VER)
+#define FORCE_INLINE __forceinline
+#define SB_UNLIKELY(x) (x)
+#define SB_LIKELY(x) (x)
+#else
+#define FORCE_INLINE inline
+#define SB_UNLIKELY(x) (x)
+#define SB_LIKELY(x) (x)
+#endif
+
+#define SB_FILE_PATH_SIZE 1024
+#define SB_BFE(VALUE, BITOFFSET, SIZE) (((VALUE) >> (BITOFFSET)) & ((1llu << (SIZE)) - 1))
+#define SB_AUDIO_RING_BUFFER_SIZE (2048 * 8)
+#define SE_NUM_KEYBINDS 36
+#define SE_KEY_A 0
+#define SE_KEY_B 1
+#define SE_KEY_X 2
+#define SE_KEY_Y 3
+#define SE_KEY_UP 4
+#define SE_KEY_DOWN 5
+#define SE_KEY_LEFT 6
+#define SE_KEY_RIGHT 7
+#define SE_KEY_L 8
+#define SE_KEY_R 9
+#define SE_KEY_START 10
+#define SE_KEY_SELECT 11
+#define SB_MODE_PAUSE 0
+
+typedef struct {
+ float inputs[SE_NUM_KEYBINDS];
+ float touch_pos[2];
+ float rumble;
+ float solar_sensor;
+ INT32 gyro_z;
+ INT32 tilt_x;
+ INT32 tilt_y;
+} sb_joy_t;
+
+typedef struct {
+ INT16 data[SB_AUDIO_RING_BUFFER_SIZE];
+ UINT32 read_ptr;
+ UINT32 write_ptr;
+} sb_ring_buffer_t;
+
+static FORCE_INLINE UINT32 sb_ring_buffer_size(sb_ring_buffer_t* buff)
+{
+ if (buff->read_ptr > SB_AUDIO_RING_BUFFER_SIZE) {
+ buff->write_ptr -= SB_AUDIO_RING_BUFFER_SIZE;
+ buff->read_ptr -= SB_AUDIO_RING_BUFFER_SIZE;
+ }
+ return (buff->write_ptr - buff->read_ptr) % SB_AUDIO_RING_BUFFER_SIZE;
+}
+
+typedef struct {
+ INT32 run_mode;
+ INT32 step_instructions;
+ INT32 step_frames;
+ bool rom_loaded;
+ INT32 system;
+ sb_joy_t joy;
+ sb_joy_t prev_frame_joy;
+ INT32 frame;
+ bool render_frame;
+ bool capture_audio;
+ double audio_sample_rate;
+ sb_ring_buffer_t audio_ring_buff;
+ UINT32 frames_since_rewind_push;
+ char save_file_path[SB_FILE_PATH_SIZE];
+ float screen_ghosting_strength;
+ size_t rom_size;
+ UINT8* rom_data;
+ const UINT8* bios_data;
+ size_t bios_size;
+ char rom_path[SB_FILE_PATH_SIZE];
+} sb_emu_state_t;
+
+typedef struct {
+ bool read_since_reset;
+ bool read_in_tick;
+ bool write_since_reset;
+ bool write_in_tick;
+ bool trigger_breakpoint;
+} sb_debug_mmio_access_t;
+
+typedef struct {
+ UINT32 addr;
+ const char* name;
+ struct {
+ UINT8 start;
+ UINT8 size;
+ const char* name;
+ } bits[32];
+} mmio_reg_t;
+
+static inline bool sb_path_has_file_ext(const char* path, const char* ext)
+{
+ if (ext[0] == '*')
+ ext++;
+ if (ext[0] == '.')
+ ext++;
+ if (ext[0] == '*')
+ return true;
+ const size_t extLen = strlen(ext);
+ const size_t pathLen = strlen(path);
+ if (pathLen < extLen)
+ return false;
+ for (size_t i = 0; i < extLen; i++) {
+ if (tolower((unsigned char)path[pathLen - extLen + i]) != tolower((unsigned char)ext[i]))
+ return false;
+ }
+ return true;
+}
+
+static UINT8* sb_load_file_data(const char*, size_t* fileSize)
+{
+ if (fileSize)
+ *fileSize = 0;
+ return NULL;
+}
+
+static void sb_free_file_data(UINT8 *data)
+{
+ free(data);
+}
+
+static sb_emu_state_t *gba_host_loading;
+
+static bool se_load_bios_file(const char*, const char*, const char*, UINT8* data, size_t dataSize)
+{
+ if (gba_host_loading == NULL || gba_host_loading->bios_data == NULL || gba_host_loading->bios_size != dataSize)
+ return false;
+ memcpy(data, gba_host_loading->bios_data, dataSize);
+ return true;
+}
+
+static FILE* se_load_log_file(const char*, const char*)
+{
+ return NULL;
+}
+
+#endif
diff --git a/src/burn/drv/gba/license.txt b/src/burn/drv/gba/license.txt
new file mode 100644
index 000000000..17d822f57
--- /dev/null
+++ b/src/burn/drv/gba/license.txt
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Skyler "Sky" Saleh
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/src/burn/drv/gba/meson.build b/src/burn/drv/gba/meson.build
new file mode 100644
index 000000000..e477c128d
--- /dev/null
+++ b/src/burn/drv/gba/meson.build
@@ -0,0 +1,9 @@
+# Nintendo Game Boy Advance drivers
+
+group_desc = 'Nintendo Game Boy Advance drivers'
+group_srcs = [
+ 'gba.cpp',
+]
+drivers = {
+ 'd_gba.cpp': [],
+}
diff --git a/src/burn/drv/gba/rtc.h b/src/burn/drv/gba/rtc.h
new file mode 100644
index 000000000..205376159
--- /dev/null
+++ b/src/burn/drv/gba/rtc.h
@@ -0,0 +1,366 @@
+#pragma once
+
+#ifndef GBA_RTC_H
+#define GBA_RTC_H
+
+#include
+
+#define GBA_RTC_DAYS_1970_TO_2000 10957
+#define GBA_RTC_STATUS_MASK 0x6a
+
+#if defined(GBA_STANDALONE)
+extern INT64 gba_rtc_test_now;
+#define GBA_RTC_NOW_SECONDS() (gba_rtc_test_now)
+#else
+#define GBA_RTC_NOW_SECONDS() ((INT64)time(NULL))
+#endif
+
+enum gba_rtc_phase {
+ GBA_RTC_IDLE = 0,
+ GBA_RTC_COMMAND,
+ GBA_RTC_RECEIVE,
+ GBA_RTC_SEND,
+ GBA_RTC_COMPLETE,
+};
+
+enum gba_rtc_register {
+ GBA_RTC_RESET = 0,
+ GBA_RTC_UNUSED = 1,
+ GBA_RTC_DATE_TIME = 2,
+ GBA_RTC_FORCE_IRQ = 3,
+ GBA_RTC_STATUS = 4,
+ GBA_RTC_UNUSED2 = 5,
+ GBA_RTC_TIME = 6,
+ GBA_RTC_UNUSED3 = 7,
+};
+
+typedef struct {
+ UINT16 year;
+ UINT8 month;
+ UINT8 day;
+ UINT8 weekday;
+ UINT8 hour;
+ UINT8 minute;
+ UINT8 second;
+} gba_rtc_civil_t;
+
+typedef struct {
+ INT64 rtc_seconds;
+ INT64 host_seconds;
+ UINT8 status;
+ UINT8 phase;
+ UINT8 command;
+ UINT8 command_register;
+ UINT8 command_read;
+ UINT8 bit_index;
+ UINT8 byte_index;
+ UINT8 byte_count;
+ UINT8 buffer[7];
+ UINT8 last_pins;
+ UINT8 sio_out;
+} gba_rtc_t;
+
+// Howard Hinnant's days_from_civil / civil_from_days (public domain). Proleptic
+// Gregorian, handles any date including 2100 (not a leap year).
+static FORCE_INLINE INT32 gba_rtc_days_from_civil(INT32 y, UINT32 m, UINT32 d)
+{
+ y -= (m <= 2);
+ INT32 era = (y >= 0 ? y : y - 399) / 400;
+ UINT32 yoe = (UINT32)(y - era * 400);
+ UINT32 doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1;
+ UINT32 doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
+ return era * 146097 + (INT32)doe - 719468;
+}
+
+static FORCE_INLINE void gba_rtc_civil_from_days(INT32 z, INT32 *y, UINT32 *m, UINT32 *d)
+{
+ z += 719468;
+ INT32 era = (z >= 0 ? z : z - 146096) / 146097;
+ UINT32 doe = (UINT32)(z - era * 146097);
+ UINT32 yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
+ UINT32 y0 = yoe + (UINT32)era * 400;
+ UINT32 doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
+ UINT32 mp = (5 * doy + 2) / 153;
+ *d = doy - (153 * mp + 2) / 5 + 1;
+ *m = mp + (mp < 10 ? 3 : -9);
+ *y = (INT32)y0 + (*m <= 2);
+}
+
+static FORCE_INLINE INT64 gba_rtc_civil_to_seconds(const gba_rtc_civil_t *c)
+{
+ INT32 days = gba_rtc_days_from_civil((INT32)c->year, c->month, c->day) - GBA_RTC_DAYS_1970_TO_2000;
+ return (INT64)days * 86400 + c->hour * 3600 + c->minute * 60 + c->second;
+}
+
+static FORCE_INLINE void gba_rtc_seconds_to_civil(INT64 sec, gba_rtc_civil_t *c)
+{
+ INT64 day = sec / 86400;
+ INT32 into_day = (INT32)(sec - day * 86400);
+ if (into_day < 0) { into_day += 86400; day -= 1; }
+ INT32 y;
+ UINT32 m, d;
+ gba_rtc_civil_from_days((INT32)day + GBA_RTC_DAYS_1970_TO_2000, &y, &m, &d);
+ if (y < 2000) y = 2000 + ((y - 2000) % 100 + 100) % 100;
+ else if (y > 2099) y = 2000 + ((y - 2000) % 100 + 100) % 100;
+ c->year = (UINT16)y;
+ c->month = (UINT8)m;
+ c->day = (UINT8)d;
+ c->weekday = (UINT8)(((day % 7) + 7 + 6) % 7); // 2000-01-01 = Saturday (6, 0=Sun)
+ c->hour = (UINT8)(into_day / 3600);
+ c->minute = (UINT8)((into_day % 3600) / 60);
+ c->second = (UINT8)(into_day % 60);
+}
+
+static FORCE_INLINE INT64 gba_rtc_current_seconds(const gba_rtc_t *rtc)
+{
+ return rtc->rtc_seconds + (GBA_RTC_NOW_SECONDS() - rtc->host_seconds);
+}
+
+static FORCE_INLINE UINT8 gba_rtc_bcd_encode(UINT8 value)
+{
+ return (value % 10) | ((value / 10) << 4);
+}
+
+static FORCE_INLINE INT32 gba_rtc_bcd_decode(UINT8 value, UINT8 maximum, UINT8 *result)
+{
+ UINT8 low = value & 0x0f;
+ UINT8 high = (value >> 4) & 0x0f;
+ if (low > 9 || high > 9) return 1;
+ UINT8 decoded = high * 10 + low;
+ if (decoded > maximum) return 1;
+ *result = decoded;
+ return 0;
+}
+
+static FORCE_INLINE UINT8 gba_rtc_hour_encode(UINT8 hour_24, UINT8 status)
+{
+ if (status & 0x40) return gba_rtc_bcd_encode(hour_24);
+ return gba_rtc_bcd_encode(hour_24 % 12) | (hour_24 >= 12 ? 0x80 : 0);
+}
+
+static FORCE_INLINE INT32 gba_rtc_hour_decode(UINT8 status, UINT8 value, UINT8 *hour)
+{
+ if (status & 0x40) {
+ if (value & 0xc0) return 1;
+ return gba_rtc_bcd_decode(value, 23, hour);
+ }
+ UINT8 decoded;
+ if (gba_rtc_bcd_decode(value & 0x3f, 11, &decoded)) return 1;
+ *hour = decoded + ((value & 0x80) ? 12 : 0);
+ return 0;
+}
+
+static FORCE_INLINE void gba_rtc_transport_reset(gba_rtc_t *rtc)
+{
+ rtc->phase = GBA_RTC_IDLE;
+ rtc->command = 0;
+ rtc->command_register = 0;
+ rtc->command_read = 0;
+ rtc->bit_index = 0;
+ rtc->byte_index = 0;
+ rtc->byte_count = 0;
+ memset(rtc->buffer, 0, sizeof(rtc->buffer));
+ rtc->sio_out = 1;
+}
+
+static FORCE_INLINE void gba_rtc_cold_init(gba_rtc_t *rtc, const gba_rtc_civil_t *seed)
+{
+ memset(rtc, 0, sizeof(*rtc));
+ gba_rtc_civil_t fallback = { 2000, 1, 1, 0, 0, 0, 0 };
+ const gba_rtc_civil_t *s = (seed && seed->year >= 2000 && seed->year <= 2099 &&
+ seed->month >= 1 && seed->month <= 12 && seed->day >= 1 &&
+ seed->day <= 31 && seed->hour <= 23 && seed->minute <= 59 && seed->second <= 59) ? seed : &fallback;
+ rtc->rtc_seconds = gba_rtc_civil_to_seconds(s);
+ rtc->host_seconds = GBA_RTC_NOW_SECONDS();
+ rtc->status = 0x40;
+ gba_rtc_transport_reset(rtc);
+}
+
+static FORCE_INLINE void gba_rtc_reanchor(gba_rtc_t *rtc, INT64 rtc_seconds)
+{
+ rtc->rtc_seconds = rtc_seconds;
+ rtc->host_seconds = GBA_RTC_NOW_SECONDS();
+}
+
+static FORCE_INLINE void gba_rtc_latch_read(gba_rtc_t *rtc)
+{
+ memset(rtc->buffer, 0xff, sizeof(rtc->buffer));
+ switch (rtc->command_register) {
+ case GBA_RTC_STATUS:
+ rtc->byte_count = 1;
+ rtc->buffer[0] = rtc->status;
+ break;
+ case GBA_RTC_DATE_TIME: {
+ gba_rtc_civil_t c;
+ gba_rtc_seconds_to_civil(gba_rtc_current_seconds(rtc), &c);
+ rtc->byte_count = 7;
+ rtc->buffer[0] = gba_rtc_bcd_encode((UINT8)(c.year % 100));
+ rtc->buffer[1] = gba_rtc_bcd_encode(c.month);
+ rtc->buffer[2] = gba_rtc_bcd_encode(c.day);
+ rtc->buffer[3] = gba_rtc_bcd_encode(c.weekday);
+ rtc->buffer[4] = gba_rtc_hour_encode(c.hour, rtc->status);
+ rtc->buffer[5] = gba_rtc_bcd_encode(c.minute);
+ rtc->buffer[6] = gba_rtc_bcd_encode(c.second);
+ break;
+ }
+ case GBA_RTC_TIME: {
+ gba_rtc_civil_t c;
+ gba_rtc_seconds_to_civil(gba_rtc_current_seconds(rtc), &c);
+ rtc->byte_count = 3;
+ rtc->buffer[0] = gba_rtc_hour_encode(c.hour, rtc->status);
+ rtc->buffer[1] = gba_rtc_bcd_encode(c.minute);
+ rtc->buffer[2] = gba_rtc_bcd_encode(c.second);
+ break;
+ }
+ default:
+ rtc->byte_count = 0;
+ break;
+ }
+}
+
+static FORCE_INLINE INT32 gba_rtc_commit_datetime(gba_rtc_t *rtc)
+{
+ gba_rtc_civil_t c;
+ gba_rtc_seconds_to_civil(gba_rtc_current_seconds(rtc), &c);
+ UINT8 year;
+ if (gba_rtc_bcd_decode(rtc->buffer[0], 99, &year)) return 1;
+ c.year = 2000 + year;
+ if (gba_rtc_bcd_decode(rtc->buffer[1], 12, &c.month) || c.month == 0) return 1;
+ if (gba_rtc_bcd_decode(rtc->buffer[2], 31, &c.day) || c.day == 0) return 1;
+ if (gba_rtc_bcd_decode(rtc->buffer[3], 6, &c.weekday)) return 1;
+ if (gba_rtc_hour_decode(rtc->status, rtc->buffer[4], &c.hour)) return 1;
+ if (gba_rtc_bcd_decode(rtc->buffer[5], 59, &c.minute)) return 1;
+ if (gba_rtc_bcd_decode(rtc->buffer[6], 59, &c.second)) return 1;
+ if (c.year < 2000 || c.year > 2099 || c.month < 1 || c.month > 12 ||
+ c.day < 1 || c.day > 31 || c.hour > 23) return 1;
+ gba_rtc_reanchor(rtc, gba_rtc_civil_to_seconds(&c));
+ return 0;
+}
+
+static FORCE_INLINE INT32 gba_rtc_commit_time(gba_rtc_t *rtc)
+{
+ gba_rtc_civil_t c;
+ gba_rtc_seconds_to_civil(gba_rtc_current_seconds(rtc), &c);
+ if (gba_rtc_hour_decode(rtc->status, rtc->buffer[0], &c.hour)) return 1;
+ if (gba_rtc_bcd_decode(rtc->buffer[1], 59, &c.minute)) return 1;
+ if (gba_rtc_bcd_decode(rtc->buffer[2], 59, &c.second)) return 1;
+ gba_rtc_reanchor(rtc, gba_rtc_civil_to_seconds(&c));
+ return 0;
+}
+
+static FORCE_INLINE void gba_rtc_commit_write(gba_rtc_t *rtc)
+{
+ switch (rtc->command_register) {
+ case GBA_RTC_STATUS:
+ rtc->status = rtc->buffer[0] & GBA_RTC_STATUS_MASK;
+ break;
+ case GBA_RTC_DATE_TIME:
+ gba_rtc_commit_datetime(rtc);
+ break;
+ case GBA_RTC_TIME:
+ gba_rtc_commit_time(rtc);
+ break;
+ }
+}
+
+static FORCE_INLINE UINT8 gba_rtc_reverse8(UINT8 value)
+{
+ value = (value >> 4) | (value << 4);
+ value = ((value & 0x33) << 2) | ((value & 0xcc) >> 2);
+ return ((value & 0x55) << 1) | ((value & 0xaa) >> 1);
+}
+
+static FORCE_INLINE void gba_rtc_decode_command(gba_rtc_t *rtc)
+{
+ UINT8 command = rtc->command;
+ if ((command & 0x0f) != 0x06 && (command & 0xf0) == 0x60) command = gba_rtc_reverse8(command);
+ if ((command & 0x0f) != 0x06) {
+ rtc->phase = GBA_RTC_COMPLETE;
+ return;
+ }
+
+ rtc->command = command;
+ rtc->command_register = (command >> 4) & 7;
+ rtc->command_read = (command >> 7) & 1;
+ rtc->bit_index = 0;
+ rtc->byte_index = 0;
+ memset(rtc->buffer, 0, sizeof(rtc->buffer));
+
+ if (rtc->command_register == GBA_RTC_RESET) {
+ if (!rtc->command_read) rtc->status = 0;
+ rtc->phase = GBA_RTC_COMPLETE;
+ return;
+ }
+ if (rtc->command_register == GBA_RTC_FORCE_IRQ || rtc->command_register == GBA_RTC_UNUSED ||
+ rtc->command_register == GBA_RTC_UNUSED2 || rtc->command_register == GBA_RTC_UNUSED3) {
+ rtc->phase = GBA_RTC_COMPLETE;
+ return;
+ }
+
+ if (rtc->command_read) {
+ gba_rtc_latch_read(rtc);
+ rtc->phase = rtc->byte_count ? GBA_RTC_SEND : GBA_RTC_COMPLETE;
+ } else {
+ rtc->byte_count = rtc->command_register == GBA_RTC_STATUS ? 1 :
+ rtc->command_register == GBA_RTC_DATE_TIME ? 7 :
+ rtc->command_register == GBA_RTC_TIME ? 3 : 0;
+ rtc->phase = rtc->byte_count ? GBA_RTC_RECEIVE : GBA_RTC_COMPLETE;
+ }
+}
+
+static FORCE_INLINE void gba_rtc_sample_input(gba_rtc_t *rtc, UINT8 sio)
+{
+ if (rtc->phase == GBA_RTC_COMMAND) {
+ rtc->command |= (sio & 1) << rtc->bit_index;
+ if (++rtc->bit_index == 8) gba_rtc_decode_command(rtc);
+ return;
+ }
+ if (rtc->phase != GBA_RTC_RECEIVE) return;
+
+ rtc->buffer[rtc->byte_index] |= (sio & 1) << rtc->bit_index;
+ if (++rtc->bit_index < 8) return;
+ rtc->bit_index = 0;
+ if (++rtc->byte_index < rtc->byte_count) return;
+ gba_rtc_commit_write(rtc);
+ rtc->phase = GBA_RTC_COMPLETE;
+}
+
+static FORCE_INLINE void gba_rtc_shift_output(gba_rtc_t *rtc)
+{
+ if (rtc->phase != GBA_RTC_SEND) {
+ rtc->sio_out = 1;
+ return;
+ }
+ rtc->sio_out = (rtc->buffer[rtc->byte_index] >> rtc->bit_index) & 1;
+ if (++rtc->bit_index < 8) return;
+ rtc->bit_index = 0;
+ if (++rtc->byte_index < rtc->byte_count) return;
+ rtc->phase = GBA_RTC_COMPLETE;
+}
+
+static FORCE_INLINE UINT8 gba_rtc_update_pins(gba_rtc_t *rtc, UINT8 pins)
+{
+ UINT8 old = rtc->last_pins;
+ UINT8 cs = (pins >> 2) & 1;
+ UINT8 old_cs = (old >> 2) & 1;
+ UINT8 sck = pins & 1;
+ UINT8 old_sck = old & 1;
+
+ if (!cs) {
+ gba_rtc_transport_reset(rtc);
+ rtc->last_pins = pins;
+ return rtc->sio_out;
+ }
+ if (!old_cs) {
+ gba_rtc_transport_reset(rtc);
+ rtc->phase = GBA_RTC_COMMAND;
+ rtc->last_pins = pins;
+ return rtc->sio_out;
+ }
+ if (!old_sck && sck) gba_rtc_sample_input(rtc, (old >> 1) & 1);
+ if (old_sck && !sck) gba_rtc_shift_output(rtc);
+ rtc->last_pins = pins;
+ return rtc->sio_out;
+}
+
+#endif
diff --git a/src/dep/visualstudio/pre.bat b/src/dep/visualstudio/pre.bat
index 8299c85a9..647f86ae2 100644
--- a/src/dep/visualstudio/pre.bat
+++ b/src/dep/visualstudio/pre.bat
@@ -8,5 +8,5 @@ perl cave_tile_func.pl -o ../../../projectfiles/visualstudio-2010/generated/cave
perl neo_sprite_func.pl -o ../../../projectfiles/visualstudio-2010/generated/neo_sprite_func.h
perl psikyo_tile_func.pl -o ../../../projectfiles/visualstudio-2010/generated/psikyo_tile_func.h
perl toa_gp9001_func.pl -o ../../../projectfiles/visualstudio-2010/generated/toa_gp9001_func.h
-perl gamelist.pl -o ../../../projectfiles/visualstudio-2010/generated/driverlist.h -l ../../../projectfiles/visualstudio-2010/generated/gamelist.txt ../../burn/drv/atari ../../burn/drv/capcom ../../burn/drv/cave ../../burn/drv/coleco ../../burn/drv/cps3 ../../burn/drv/dataeast ../../burn/drv/galaxian ../../burn/drv/irem ../../burn/drv/konami ../../burn/drv/megadrive ../../burn/drv/midway ../../burn/drv/msx ../../burn/drv/neogeo ../../burn/drv/pce ../../burn/drv/pgm ../../burn/drv/pst90s ../../burn/drv/pre90s ../../burn/drv/psikyo ../../burn/drv/sega ../../burn/drv/sg1000 ../../burn/drv/spectrum ../../burn/drv/taito ../../burn/drv/toaplan ../../burn/drv/sms ../../burn/drv/nes ../../burn/drv/snes ../../burn/drv
+perl gamelist.pl -o ../../../projectfiles/visualstudio-2010/generated/driverlist.h -l ../../../projectfiles/visualstudio-2010/generated/gamelist.txt ../../burn/drv/atari ../../burn/drv/capcom ../../burn/drv/cave ../../burn/drv/coleco ../../burn/drv/cps3 ../../burn/drv/dataeast ../../burn/drv/galaxian ../../burn/drv/gba ../../burn/drv/irem ../../burn/drv/konami ../../burn/drv/megadrive ../../burn/drv/midway ../../burn/drv/msx ../../burn/drv/neogeo ../../burn/drv/pce ../../burn/drv/pgm ../../burn/drv/pst90s ../../burn/drv/pre90s ../../burn/drv/psikyo ../../burn/drv/sega ../../burn/drv/sg1000 ../../burn/drv/spectrum ../../burn/drv/taito ../../burn/drv/toaplan ../../burn/drv/sms ../../burn/drv/nes ../../burn/drv/snes ../../burn/drv
diff --git a/src/dep/visualstudio/pre2015.bat b/src/dep/visualstudio/pre2015.bat
index 7ce0b0a95..eb8a75a48 100644
--- a/src/dep/visualstudio/pre2015.bat
+++ b/src/dep/visualstudio/pre2015.bat
@@ -8,4 +8,4 @@ perl cave_tile_func.pl -o ../../../projectfiles/visualstudio-2015/generated/cave
perl neo_sprite_func.pl -o ../../../projectfiles/visualstudio-2015/generated/neo_sprite_func.h
perl psikyo_tile_func.pl -o ../../../projectfiles/visualstudio-2015/generated/psikyo_tile_func.h
perl toa_gp9001_func.pl -o ../../../projectfiles/visualstudio-2015/generated/toa_gp9001_func.h
-perl gamelist.pl -o ../../../projectfiles/visualstudio-2015/generated/driverlist.h -l ../../../projectfiles/visualstudio-2015/generated/gamelist.txt ../../burn/drv/atari ../../burn/drv/capcom ../../burn/drv/cave ../../burn/drv/coleco ../../burn/drv/cps3 ../../burn/drv/dataeast ../../burn/drv/galaxian ../../burn/drv/irem ../../burn/drv/konami ../../burn/drv/megadrive ../../burn/drv/midway ../../burn/drv/msx ../../burn/drv/neogeo ../../burn/drv/pce ../../burn/drv/pgm ../../burn/drv/pgm2 ../../burn/drv/pst90s ../../burn/drv/pre90s ../../burn/drv/psikyo ../../burn/drv/sega ../../burn/drv/sg1000 ../../burn/drv/spectrum ../../burn/drv/taito ../../burn/drv/toaplan ../../burn/drv/sms ../../burn/drv/nes ../../burn/drv/snes ../../burn/drv/channelf ../../burn/drv
\ No newline at end of file
+perl gamelist.pl -o ../../../projectfiles/visualstudio-2015/generated/driverlist.h -l ../../../projectfiles/visualstudio-2015/generated/gamelist.txt ../../burn/drv/atari ../../burn/drv/capcom ../../burn/drv/cave ../../burn/drv/coleco ../../burn/drv/cps3 ../../burn/drv/dataeast ../../burn/drv/galaxian ../../burn/drv/gba ../../burn/drv/irem ../../burn/drv/konami ../../burn/drv/megadrive ../../burn/drv/midway ../../burn/drv/msx ../../burn/drv/neogeo ../../burn/drv/pce ../../burn/drv/pgm ../../burn/drv/pgm2 ../../burn/drv/pst90s ../../burn/drv/pre90s ../../burn/drv/psikyo ../../burn/drv/sega ../../burn/drv/sg1000 ../../burn/drv/spectrum ../../burn/drv/taito ../../burn/drv/toaplan ../../burn/drv/sms ../../burn/drv/nes ../../burn/drv/snes ../../burn/drv/channelf ../../burn/drv
\ No newline at end of file
diff --git a/src/dep/visualstudio/pre2019.bat b/src/dep/visualstudio/pre2019.bat
index 3a2fb2a20..251e7eacf 100644
--- a/src/dep/visualstudio/pre2019.bat
+++ b/src/dep/visualstudio/pre2019.bat
@@ -8,4 +8,4 @@ perl cave_tile_func.pl -o ../../../projectfiles/visualstudio-2019/generated/cave
perl neo_sprite_func.pl -o ../../../projectfiles/visualstudio-2019/generated/neo_sprite_func.h
perl psikyo_tile_func.pl -o ../../../projectfiles/visualstudio-2019/generated/psikyo_tile_func.h
perl toa_gp9001_func.pl -o ../../../projectfiles/visualstudio-2019/generated/toa_gp9001_func.h
-perl gamelist.pl -o ../../../projectfiles/visualstudio-2019/generated/driverlist.h -l ../../../projectfiles/visualstudio-2019/generated/gamelist.txt ../../burn/drv/atari ../../burn/drv/capcom ../../burn/drv/cave ../../burn/drv/coleco ../../burn/drv/cps3 ../../burn/drv/dataeast ../../burn/drv/galaxian ../../burn/drv/irem ../../burn/drv/konami ../../burn/drv/megadrive ../../burn/drv/midway ../../burn/drv/msx ../../burn/drv/neogeo ../../burn/drv/pce ../../burn/drv/pgm ../../burn/drv/pgm2 ../../burn/drv/pst90s ../../burn/drv/pre90s ../../burn/drv/psikyo ../../burn/drv/sega ../../burn/drv/sg1000 ../../burn/drv/spectrum ../../burn/drv/taito ../../burn/drv/toaplan ../../burn/drv/sms ../../burn/drv/nes ../../burn/drv/snes ../../burn/drv/channelf ../../burn/drv
\ No newline at end of file
+perl gamelist.pl -o ../../../projectfiles/visualstudio-2019/generated/driverlist.h -l ../../../projectfiles/visualstudio-2019/generated/gamelist.txt ../../burn/drv/atari ../../burn/drv/capcom ../../burn/drv/cave ../../burn/drv/coleco ../../burn/drv/cps3 ../../burn/drv/dataeast ../../burn/drv/galaxian ../../burn/drv/gba ../../burn/drv/irem ../../burn/drv/konami ../../burn/drv/megadrive ../../burn/drv/midway ../../burn/drv/msx ../../burn/drv/neogeo ../../burn/drv/pce ../../burn/drv/pgm ../../burn/drv/pgm2 ../../burn/drv/pst90s ../../burn/drv/pre90s ../../burn/drv/psikyo ../../burn/drv/sega ../../burn/drv/sg1000 ../../burn/drv/spectrum ../../burn/drv/taito ../../burn/drv/toaplan ../../burn/drv/sms ../../burn/drv/nes ../../burn/drv/snes ../../burn/drv/channelf ../../burn/drv
\ No newline at end of file
diff --git a/src/dep/visualstudio/pre2022.bat b/src/dep/visualstudio/pre2022.bat
index 9dfc31444..76f778571 100644
--- a/src/dep/visualstudio/pre2022.bat
+++ b/src/dep/visualstudio/pre2022.bat
@@ -8,4 +8,4 @@ perl cave_tile_func.pl -o ../../../projectfiles/visualstudio-2022/generated/cave
perl neo_sprite_func.pl -o ../../../projectfiles/visualstudio-2022/generated/neo_sprite_func.h
perl psikyo_tile_func.pl -o ../../../projectfiles/visualstudio-2022/generated/psikyo_tile_func.h
perl toa_gp9001_func.pl -o ../../../projectfiles/visualstudio-2022/generated/toa_gp9001_func.h
-perl gamelist.pl -o ../../../projectfiles/visualstudio-2022/generated/driverlist.h -l ../../../projectfiles/visualstudio-2022/generated/gamelist.txt ../../burn/drv/atari ../../burn/drv/capcom ../../burn/drv/cave ../../burn/drv/coleco ../../burn/drv/cps3 ../../burn/drv/dataeast ../../burn/drv/galaxian ../../burn/drv/irem ../../burn/drv/konami ../../burn/drv/megadrive ../../burn/drv/midway ../../burn/drv/msx ../../burn/drv/neogeo ../../burn/drv/pce ../../burn/drv/pgm ../../burn/drv/pgm2 ../../burn/drv/pst90s ../../burn/drv/pre90s ../../burn/drv/psikyo ../../burn/drv/sega ../../burn/drv/sg1000 ../../burn/drv/spectrum ../../burn/drv/taito ../../burn/drv/toaplan ../../burn/drv/sms ../../burn/drv/nes ../../burn/drv/snes ../../burn/drv/channelf ../../burn/drv
\ No newline at end of file
+perl gamelist.pl -o ../../../projectfiles/visualstudio-2022/generated/driverlist.h -l ../../../projectfiles/visualstudio-2022/generated/gamelist.txt ../../burn/drv/atari ../../burn/drv/capcom ../../burn/drv/cave ../../burn/drv/coleco ../../burn/drv/cps3 ../../burn/drv/dataeast ../../burn/drv/galaxian ../../burn/drv/gba ../../burn/drv/irem ../../burn/drv/konami ../../burn/drv/megadrive ../../burn/drv/midway ../../burn/drv/msx ../../burn/drv/neogeo ../../burn/drv/pce ../../burn/drv/pgm ../../burn/drv/pgm2 ../../burn/drv/pst90s ../../burn/drv/pre90s ../../burn/drv/psikyo ../../burn/drv/sega ../../burn/drv/sg1000 ../../burn/drv/spectrum ../../burn/drv/taito ../../burn/drv/toaplan ../../burn/drv/sms ../../burn/drv/nes ../../burn/drv/snes ../../burn/drv/channelf ../../burn/drv
\ No newline at end of file
diff --git a/src/dep/visualstudio/pre2026.bat b/src/dep/visualstudio/pre2026.bat
index 3aad97c85..9ad60471e 100644
--- a/src/dep/visualstudio/pre2026.bat
+++ b/src/dep/visualstudio/pre2026.bat
@@ -8,4 +8,4 @@ perl cave_tile_func.pl -o ../../../projectfiles/visualstudio-2026/generated/cave
perl neo_sprite_func.pl -o ../../../projectfiles/visualstudio-2026/generated/neo_sprite_func.h
perl psikyo_tile_func.pl -o ../../../projectfiles/visualstudio-2026/generated/psikyo_tile_func.h
perl toa_gp9001_func.pl -o ../../../projectfiles/visualstudio-2026/generated/toa_gp9001_func.h
-perl gamelist.pl -o ../../../projectfiles/visualstudio-2026/generated/driverlist.h -l ../../../projectfiles/visualstudio-2026/generated/gamelist.txt ../../burn/drv/atari ../../burn/drv/capcom ../../burn/drv/cave ../../burn/drv/coleco ../../burn/drv/cps3 ../../burn/drv/dataeast ../../burn/drv/galaxian ../../burn/drv/irem ../../burn/drv/konami ../../burn/drv/megadrive ../../burn/drv/midway ../../burn/drv/msx ../../burn/drv/neogeo ../../burn/drv/pce ../../burn/drv/pgm ../../burn/drv/pgm2 ../../burn/drv/pst90s ../../burn/drv/pre90s ../../burn/drv/psikyo ../../burn/drv/sega ../../burn/drv/sg1000 ../../burn/drv/spectrum ../../burn/drv/taito ../../burn/drv/toaplan ../../burn/drv/sms ../../burn/drv/nes ../../burn/drv/snes ../../burn/drv/channelf ../../burn/drv
\ No newline at end of file
+perl gamelist.pl -o ../../../projectfiles/visualstudio-2026/generated/driverlist.h -l ../../../projectfiles/visualstudio-2026/generated/gamelist.txt ../../burn/drv/atari ../../burn/drv/capcom ../../burn/drv/cave ../../burn/drv/coleco ../../burn/drv/cps3 ../../burn/drv/dataeast ../../burn/drv/galaxian ../../burn/drv/gba ../../burn/drv/irem ../../burn/drv/konami ../../burn/drv/megadrive ../../burn/drv/midway ../../burn/drv/msx ../../burn/drv/neogeo ../../burn/drv/pce ../../burn/drv/pgm ../../burn/drv/pgm2 ../../burn/drv/pst90s ../../burn/drv/pre90s ../../burn/drv/psikyo ../../burn/drv/sega ../../burn/drv/sg1000 ../../burn/drv/spectrum ../../burn/drv/taito ../../burn/drv/toaplan ../../burn/drv/sms ../../burn/drv/nes ../../burn/drv/snes ../../burn/drv/channelf ../../burn/drv
\ No newline at end of file