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