diff --git a/src/agb/GBA-arm.cpp b/src/agb/GBA-arm.cpp new file mode 100644 index 00000000..711ab63f --- /dev/null +++ b/src/agb/GBA-arm.cpp @@ -0,0 +1,2966 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005-2006 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include +#include +#include +#include + +#include "GBA.h" +#include "GBAcpu.h" +#include "GBAinline.h" +#include "../Globals.h" +#include "../EEprom.h" +#include "../Flash.h" +#include "../Sound.h" +#include "../Sram.h" +#include "../bios.h" +#include "../Cheats.h" +#include "../NLS.h" +#include "../elf.h" +#include "../Util.h" +#include "../Port.h" +#include "agbprint.h" +#ifdef PROFILING +#include "prof/prof.h" +#endif + +#ifdef _MSC_VER + // Disable "empty statement" warnings + #pragma warning(disable: 4390) + // Visual C's inline assembler treats "offset" as a reserved word, so we + // tell it otherwise. If you want to use it, write "OFFSET" in capitals. + #define offset offset_ +#endif + +/////////////////////////////////////////////////////////////////////////// + +static int clockTicks; + +static INSN_REGPARM void armUnknownInsn(u32 opcode) +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_UNDEFINED) { + log("Undefined ARM instruction %08x at %08x\n", opcode, + armNextPC-4); + } +#endif + CPUUndefinedException(); +} + +#ifdef BKPT_SUPPORT +static INSN_REGPARM void armBreakpoint(u32 opcode) +{ + extern void (*dbgSignal)(int,int); + reg[15].I -= 4; + armNextPC -= 4; + dbgSignal(5, (opcode & 0x0f) | ((opcode>>4) & 0xfff0)); + clockTicks = -1; +} +#endif + + +// Subroutine to count instructions (for debugging/optimizing) +//#define INSN_COUNTER // comment out if you don't want it +#ifdef INSN_COUNTER +static void count(u32 opcode, int cond_res) +{ + static int insncount = 0; // number of insns seen + static int executed = 0; // number of insns executed + static int mergewith[4096]; // map instructions to routines + static int count[4096]; // count of each 12-bit code + int index = ((opcode>>16)&0xFF0) | ((opcode>>4)&0x0F); + static FILE *outfile = NULL; + + if (!insncount) { + for (int i = 0; i < 4096; i++) { + for (int j = 0; j < i; j++) { + if (armInsnTable[i] == armInsnTable[j]) + break; + } + mergewith[i] = j; + } + outfile = fopen("VBA-armcount.txt", "w"); + } + if (cond_res) { + count[mergewith[index]]++; + executed++; + } + insncount++; + if (outfile && insncount%1000000 == 0) { + fprintf(outfile, "Total instructions: %d\n", insncount); + fprintf(outfile, "Instructions executed: %d\n", executed); + for (int i = 0; i < 4096; i++) { + if (count[i]) + fprintf(outfile, "arm%03X: %d\n", i, count[i]); + } + } +} +#endif + +// Common macros ////////////////////////////////////////////////////////// + +#ifdef BKPT_SUPPORT +#define CONSOLE_OUTPUT(a,b) do { \ + extern void (*dbgOutput)(char *, u32); \ + if ((opcode == 0xe0000000) && (reg[0].I == 0xC0DED00D)) { \ + dbgOutput((a), (b)); \ +} while (0) +#else +#define CONSOLE_OUTPUT(a,b) /* nothing */ +#endif + +#define NEG(i) ((i) >> 31) +#define POS(i) ((~(i)) >> 31) + +// The following macros are used for optimization; any not defined for a +// particular compiler/CPU combination default to the C core versions. +// +// ALU_INIT_C: Used at the beginning of ALU instructions (AND/EOR/...). +// (ALU_INIT_NC) Can consist of variable declarations, like the C core, +// or the start of a continued assembly block, like the +// x86-optimized version. The _C version is used when the +// carry flag from the shift operation is needed (logical +// operations that set condition codes, like ANDS); the +// _NC version is used when the carry result is ignored. +// VALUE_XXX: Retrieve the second operand's value for an ALU instruction. +// The _C and _NC versions are used the same way as ALU_INIT. +// OP_XXX: ALU operations. XXX is the instruction name. +// ALU_FINISH: Appended to all ALU instructions. Usually empty, but if +// ALU_INIT started a block ALU_FINISH can be used to end it +// (as with the asm(...) statement in the x86 core). +// SETCOND_NONE: Used in multiply instructions in place of SETCOND_MUL +// when the condition codes are not set. Usually empty. +// SETCOND_MUL: Used in multiply instructions to set the condition codes. +// ROR_IMM_MSR: Used to rotate the immediate operand for MSR. +// ROR_OFFSET: Used to rotate the `offset' parameter for LDR and STR +// instructions. +// RRX_OFFSET: Used to rotate (RRX) the `offset' parameter for LDR and +// STR instructions. + +#ifndef C_CORE + +#if 0 // definitions have changed +//#ifdef __POWERPC__ + #define OP_SUBS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define OP_RSBS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subfco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define OP_ADDS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("addco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define OP_ADCS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("mtspr xer, %4\n" \ + "addeo. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value), \ + "r" (C_FLAG << 29) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define OP_SBCS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("mtspr xer, %4\n" \ + "subfeo. %0, %3, %2\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value), \ + "r" (C_FLAG << 29) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define OP_RSCS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("mtspr xer, %4\n" \ + "subfeo. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value), \ + "r" (C_FLAG << 29) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define OP_CMP \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value) \ + ); \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define OP_CMN \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("addco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value) \ + ); \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + +#else // !__POWERPC__ + +// Macros to emit instructions in the format used by the particular compiler. +// We use GNU assembler syntax: "op src, dest" rather than "op dest, src" + +#ifdef __GNUC__ + #define ALU_HEADER asm("mov %%ecx, %%edi; " + #define ALU_TRAILER : "=D" (opcode) : "c" (opcode) : "eax", "ebx", "edx", "esi") + #define EMIT0(op) #op"; " + #define EMIT1(op,arg) #op" "arg"; " + #define EMIT2(op,src,dest) #op" "src", "dest"; " + #define CONST(val) "$"#val + #define VAR(var) "_"#var + #define VARL(var) "_"#var + #define REGREF1(index) "_reg("index")" + #define REGREF2(index,scale) "_reg(,"index","#scale")" + #define LABEL(n) #n": " + #define LABELREF(n,dir) #n#dir + #define al "%%al" + #define ah "%%ah" + #define eax "%%eax" + #define bl "%%bl" + #define bh "%%bh" + #define ebx "%%ebx" + #define cl "%%cl" + #define ch "%%ch" + #define ecx "%%ecx" + #define dl "%%dl" + #define dh "%%dh" + #define edx "%%edx" + #define esp "%%esp" + #define ebp "%%ebp" + #define esi "%%esi" + #define edi "%%edi" + #define movzx movzb +#else + #define ALU_HEADER __asm { __asm mov ecx, opcode + #define ALU_TRAILER } + #define EMIT0(op) __asm op + #define EMIT1(op,arg) __asm op arg + #define EMIT2(op,src,dest) __asm op dest, src + #define CONST(val) val + #define VAR(var) var + #define VARL(var) dword ptr var + #define REGREF1(index) reg[index] + #define REGREF2(index,scale) reg[index*scale] + #define LABEL(n) __asm l##n: + #define LABELREF(n,dir) l##n +#endif + +//X//#ifndef _MSC_VER +// ALU op register usage: +// EAX -> 2nd operand value, result (RSB/RSC) +// EBX -> C_OUT (carry flag from shift/rotate) +// ECX -> opcode (input), shift/rotate count +// EDX -> Rn (base) value, result (all except RSB/RSC) +// ESI -> Rd (destination) index * 4 + +// Helper macros for loading value / shift count +#define VALUE_LOAD_IMM \ + EMIT2(and, CONST(0x0F), eax) \ + EMIT2(mov, REGREF2(eax,4), eax) \ + EMIT2(shr, CONST(7), ecx) \ + EMIT2(and, CONST(0x1F), ecx) +#define VALUE_LOAD_REG \ + EMIT2(and, CONST(0x0F), eax) \ + EMIT2(mov, REGREF2(eax,4), eax) \ + EMIT2(movzx, ch, ecx) \ + EMIT2(and, CONST(0x0F), ecx) \ + EMIT2(mov, REGREF2(ecx,4), ecx) + +// Helper macros for setting flags +#define SETCOND_LOGICAL \ + EMIT1(sets, VAR(N_FLAG)) \ + EMIT1(setz, VAR(Z_FLAG)) \ + EMIT2(mov, bl, VAR(C_FLAG)) +#define SETCOND_ADD \ + EMIT1(sets, VAR(N_FLAG)) \ + EMIT1(setz, VAR(Z_FLAG)) \ + EMIT1(seto, VAR(V_FLAG)) \ + EMIT1(setc, VAR(C_FLAG)) +#define SETCOND_SUB \ + EMIT1(sets, VAR(N_FLAG)) \ + EMIT1(setz, VAR(Z_FLAG)) \ + EMIT1(seto, VAR(V_FLAG)) \ + EMIT1(setnc, VAR(C_FLAG)) + +// ALU initialization +#define ALU_INIT(LOAD_C_FLAG) \ + ALU_HEADER \ + LOAD_C_FLAG \ + EMIT2(mov, ecx, edx) \ + EMIT2(shr, CONST(14), edx) \ + EMIT2(mov, ecx, eax) \ + EMIT2(mov, ecx, esi) \ + EMIT2(shr, CONST(10), esi) \ + EMIT2(and, CONST(0x3C), edx) \ + EMIT2(mov, REGREF1(edx), edx) \ + EMIT2(and, CONST(0x3C), esi) + +#define LOAD_C_FLAG_YES EMIT2(mov, VAR(C_FLAG), bl) +#define LOAD_C_FLAG_NO /*nothing*/ +#define ALU_INIT_C ALU_INIT(LOAD_C_FLAG_YES) +#define ALU_INIT_NC ALU_INIT(LOAD_C_FLAG_NO) + +// Macros to load the value operand for an ALU op; these all set N/Z +// according to the value + +// OP Rd,Rb,Rm LSL # +#define VALUE_LSL_IMM_C \ + VALUE_LOAD_IMM \ + EMIT1(jnz, LABELREF(1,f)) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(shl, cl, eax) \ + EMIT1(setc, bl) \ + LABEL(0) +#define VALUE_LSL_IMM_NC \ + VALUE_LOAD_IMM \ + EMIT2(shl, cl, eax) + +// OP Rd,Rb,Rm LSL Rs +#define VALUE_LSL_REG_C \ + VALUE_LOAD_REG \ + EMIT2(test, cl, cl) \ + EMIT1(jz, LABELREF(0,f)) \ + EMIT2(cmp, CONST(0x20), cl) \ + EMIT1(je, LABELREF(1,f)) \ + EMIT1(ja, LABELREF(2,f)) \ + EMIT2(shl, cl, eax) \ + EMIT1(setc, bl) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(test, CONST(1), al) \ + EMIT1(setnz, bl) \ + EMIT2(xor, eax, eax) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(2) \ + EMIT2(xor, ebx, ebx) \ + EMIT2(xor, eax, eax) \ + LABEL(0) +#define VALUE_LSL_REG_NC \ + VALUE_LOAD_REG \ + EMIT2(cmp, CONST(0x20), cl) \ + EMIT1(jae, LABELREF(1,f)) \ + EMIT2(shl, cl, eax) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(xor, eax, eax) \ + LABEL(0) + +// OP Rd,Rb,Rm LSR # +#define VALUE_LSR_IMM_C \ + VALUE_LOAD_IMM \ + EMIT1(jz, LABELREF(1,f)) \ + EMIT2(shr, cl, eax) \ + EMIT1(setc, bl) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(test, eax, eax) \ + EMIT1(sets, bl) \ + EMIT2(xor, eax, eax) \ + LABEL(0) +#define VALUE_LSR_IMM_NC \ + VALUE_LOAD_IMM \ + EMIT1(jz, LABELREF(1,f)) \ + EMIT2(shr, cl, eax) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(xor, eax, eax) \ + LABEL(0) + +// OP Rd,Rb,Rm LSR Rs +#define VALUE_LSR_REG_C \ + VALUE_LOAD_REG \ + EMIT2(test, cl, cl) \ + EMIT1(jz, LABELREF(0,f)) \ + EMIT2(cmp, CONST(0x20), cl) \ + EMIT1(je, LABELREF(1,f)) \ + EMIT1(ja, LABELREF(2,f)) \ + EMIT2(shr, cl, eax) \ + EMIT1(setc, bl) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(test, eax, eax) \ + EMIT1(sets, bl) \ + EMIT2(xor, eax, eax) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(2) \ + EMIT2(xor, ebx, ebx) \ + EMIT2(xor, eax, eax) \ + LABEL(0) +#define VALUE_LSR_REG_NC \ + VALUE_LOAD_REG \ + EMIT2(cmp, CONST(0x20), cl) \ + EMIT1(jae, LABELREF(1,f)) \ + EMIT2(shr, cl, eax) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(xor, eax, eax) \ + LABEL(0) + +// OP Rd,Rb,Rm ASR # +#define VALUE_ASR_IMM_C \ + VALUE_LOAD_IMM \ + EMIT1(jz, LABELREF(1,f)) \ + EMIT2(sar, cl, eax) \ + EMIT1(setc, bl) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(sar, CONST(31), eax) \ + EMIT1(sets, bl) \ + LABEL(0) +#define VALUE_ASR_IMM_NC \ + VALUE_LOAD_IMM \ + EMIT1(jz, LABELREF(1,f)) \ + EMIT2(sar, cl, eax) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(sar, CONST(31), eax) \ + LABEL(0) + +// OP Rd,Rb,Rm ASR Rs +#define VALUE_ASR_REG_C \ + VALUE_LOAD_REG \ + EMIT2(test, cl, cl) \ + EMIT1(jz, LABELREF(0,f)) \ + EMIT2(cmp, CONST(0x20), cl) \ + EMIT1(jae, LABELREF(1,f)) \ + EMIT2(sar, cl, eax) \ + EMIT1(setc, bl) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(sar, CONST(31), eax) \ + EMIT1(sets, bl) \ + LABEL(0) +#define VALUE_ASR_REG_NC \ + VALUE_LOAD_REG \ + EMIT2(cmp, CONST(0x20), cl) \ + EMIT1(jae, LABELREF(1,f)) \ + EMIT2(sar, cl, eax) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(sar, CONST(31), eax) \ + LABEL(0) + +// OP Rd,Rb,Rm ROR # +#define VALUE_ROR_IMM_C \ + VALUE_LOAD_IMM \ + EMIT1(jz, LABELREF(1,f)) \ + EMIT2(ror, cl, eax) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(bt, CONST(0), ebx) \ + EMIT2(rcr, CONST(1), eax) \ + LABEL(0) \ + EMIT1(setc, bl) +#define VALUE_ROR_IMM_NC \ + VALUE_LOAD_IMM \ + EMIT1(jz, LABELREF(1,f)) \ + EMIT2(ror, cl, eax) \ + EMIT1(jmp, LABELREF(0,f)) \ + LABEL(1) \ + EMIT2(bt, CONST(0), VARL(C_FLAG)) \ + EMIT2(rcr, CONST(1), eax) \ + LABEL(0) + +// OP Rd,Rb,Rm ROR Rs +#define VALUE_ROR_REG_C \ + VALUE_LOAD_REG \ + EMIT2(bt, CONST(0), ebx) \ + EMIT2(ror, cl, eax) \ + EMIT1(setc, bl) +#define VALUE_ROR_REG_NC \ + VALUE_LOAD_REG \ + EMIT2(ror, cl, eax) + +// OP Rd,Rb,# ROR # +#define VALUE_IMM_C \ + EMIT2(movzx, ch, ecx) \ + EMIT2(add, ecx, ecx) \ + EMIT2(movzx, al, eax) \ + EMIT2(bt, CONST(0), ebx) \ + EMIT2(ror, cl, eax) \ + EMIT1(setc, bl) +#define VALUE_IMM_NC \ + EMIT2(movzx, ch, ecx) \ + EMIT2(add, ecx, ecx) \ + EMIT2(movzx, al, eax) \ + EMIT2(ror, cl, eax) + +// Macros to perform ALU ops + +// Set condition codes iff the destination register is not R15 (PC) +#define CHECK_PC(OP, SETCOND) \ + EMIT2(cmp, CONST(0x3C), esi) \ + EMIT1(je, LABELREF(8,f)) \ + OP SETCOND \ + EMIT1(jmp, LABELREF(9,f)) \ + LABEL(8) \ + OP \ + LABEL(9) + +#define OP_AND \ + EMIT2(and, eax, edx) \ + EMIT2(mov, edx, REGREF1(esi)) +#define OP_ANDS CHECK_PC(OP_AND, SETCOND_LOGICAL) +#define OP_EOR \ + EMIT2(xor, eax, edx) \ + EMIT2(mov, edx, REGREF1(esi)) +#define OP_EORS CHECK_PC(OP_EOR, SETCOND_LOGICAL) +#define OP_SUB \ + EMIT2(sub, eax, edx) \ + EMIT2(mov, edx, REGREF1(esi)) +#define OP_SUBS CHECK_PC(OP_SUB, SETCOND_SUB) +#define OP_RSB \ + EMIT2(sub, edx, eax) \ + EMIT2(mov, eax, REGREF1(esi)) +#define OP_RSBS CHECK_PC(OP_RSB, SETCOND_SUB) +#define OP_ADD \ + EMIT2(add, eax, edx) \ + EMIT2(mov, edx, REGREF1(esi)) +#define OP_ADDS CHECK_PC(OP_ADD, SETCOND_ADD) +#define OP_ADC \ + EMIT2(bt, CONST(0), VARL(C_FLAG)) \ + EMIT2(adc, eax, edx) \ + EMIT2(mov, edx, REGREF1(esi)) +#define OP_ADCS CHECK_PC(OP_ADC, SETCOND_ADD) +#define OP_SBC \ + EMIT2(bt, CONST(0), VARL(C_FLAG)) \ + EMIT0(cmc) \ + EMIT2(sbb, eax, edx) \ + EMIT2(mov, edx, REGREF1(esi)) +#define OP_SBCS CHECK_PC(OP_SBC, SETCOND_SUB) +#define OP_RSC \ + EMIT2(bt, CONST(0), VARL(C_FLAG)) \ + EMIT0(cmc) \ + EMIT2(sbb, edx, eax) \ + EMIT2(mov, eax, REGREF1(esi)) +#define OP_RSCS CHECK_PC(OP_RSC, SETCOND_SUB) +#define OP_TST \ + EMIT2(and, eax, edx) \ + SETCOND_LOGICAL +#define OP_TEQ \ + EMIT2(xor, eax, edx) \ + SETCOND_LOGICAL +#define OP_CMP \ + EMIT2(sub, eax, edx) \ + SETCOND_SUB +#define OP_CMN \ + EMIT2(add, eax, edx) \ + SETCOND_ADD +#define OP_ORR \ + EMIT2(or, eax, edx) \ + EMIT2(mov, edx, REGREF1(esi)) +#define OP_ORRS CHECK_PC(OP_ORR, SETCOND_LOGICAL) +#define OP_MOV \ + EMIT2(mov, eax, REGREF1(esi)) +#define OP_MOVS CHECK_PC(EMIT2(test,eax,eax) EMIT2(mov,eax,REGREF1(esi)), SETCOND_LOGICAL) +#define OP_BIC \ + EMIT1(not, eax) \ + EMIT2(and, eax, edx) \ + EMIT2(mov, edx, REGREF1(esi)) +#define OP_BICS CHECK_PC(OP_BIC, SETCOND_LOGICAL) +#define OP_MVN \ + EMIT1(not, eax) \ + EMIT2(mov, eax, REGREF1(esi)) +#define OP_MVNS CHECK_PC(OP_MVN, SETCOND_LOGICAL) + +// ALU cleanup macro +#define ALU_FINISH ALU_TRAILER + +// End of ALU macros +//X//#endif //_MSC_VER + +#ifdef __GNUC__ + +#define ROR_IMM_MSR \ + asm ("ror %%cl, %%eax;" \ + : "=a" (value) \ + : "a" (opcode & 0xFF), "c" (shift)); + +#define ROR_OFFSET \ + asm("ror %%cl, %0" \ + : "=r" (offset) \ + : "0" (offset), "c" (shift)); + +#define RRX_OFFSET \ + asm("btl $0, _C_FLAG;" \ + "rcr $1, %0" \ + : "=r" (offset) \ + : "0" (offset)); + +#else // !__GNUC__, i.e. Visual C++ + +#define ROR_IMM_MSR \ + __asm { \ + __asm mov ecx, shift \ + __asm ror value, cl \ + } + + +#define ROR_OFFSET \ + __asm { \ + __asm mov ecx, shift \ + __asm ror offset, cl \ + } + +#define RRX_OFFSET \ + __asm { \ + __asm bt dword ptr C_FLAG, 0 \ + __asm rcr offset, 1 \ + } + +#endif // !__GNUC__ + +#endif // !__POWERPC__ +#endif // !C_CORE + +// C core + +#define C_SETCOND_LOGICAL \ + N_FLAG = ((s32)res < 0) ? true : false; \ + Z_FLAG = (res == 0) ? true : false; \ + C_FLAG = C_OUT; +#define C_SETCOND_ADD \ + N_FLAG = ((s32)res < 0) ? true : false; \ + Z_FLAG = (res == 0) ? true : false; \ + V_FLAG = ((NEG(lhs) & NEG(rhs) & POS(res)) | \ + (POS(lhs) & POS(rhs) & NEG(res))) ? true : false;\ + C_FLAG = ((NEG(lhs) & NEG(rhs)) | \ + (NEG(lhs) & POS(res)) | \ + (NEG(rhs) & POS(res))) ? true : false; +#define C_SETCOND_SUB \ + N_FLAG = ((s32)res < 0) ? true : false; \ + Z_FLAG = (res == 0) ? true : false; \ + V_FLAG = ((NEG(lhs) & POS(rhs) & POS(res)) | \ + (POS(lhs) & NEG(rhs) & NEG(res))) ? true : false;\ + C_FLAG = ((NEG(lhs) & POS(rhs)) | \ + (NEG(lhs) & POS(res)) | \ + (POS(rhs) & POS(res))) ? true : false; + +#ifndef ALU_INIT_C + #define ALU_INIT_C \ + int dest = (opcode>>12) & 15; \ + bool C_OUT = C_FLAG; \ + u32 value; +#endif +// OP Rd,Rb,Rm LSL # +#ifndef VALUE_LSL_IMM_C + #define VALUE_LSL_IMM_C \ + unsigned int shift = (opcode >> 7) & 0x1F; \ + if (LIKELY(!shift)) { /* LSL #0 most common? */ \ + value = reg[opcode & 0x0F].I; \ + } else { \ + u32 v = reg[opcode & 0x0F].I; \ + C_OUT = (v >> (32 - shift)) & 1 ? true : false; \ + value = v << shift; \ + } +#endif +// OP Rd,Rb,Rm LSL Rs +#ifndef VALUE_LSL_REG_C + #define VALUE_LSL_REG_C \ + unsigned int shift = reg[(opcode >> 8)&15].B.B0; \ + if (LIKELY(shift)) { \ + if (shift == 32) { \ + value = 0; \ + C_OUT = (reg[opcode & 0x0F].I & 1 ? true : false);\ + } else if (LIKELY(shift < 32)) { \ + u32 v = reg[opcode & 0x0F].I; \ + C_OUT = (v >> (32 - shift)) & 1 ? true : false;\ + value = v << shift; \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } else { \ + value = reg[opcode & 0x0F].I; \ + } +#endif +// OP Rd,Rb,Rm LSR # +#ifndef VALUE_LSR_IMM_C + #define VALUE_LSR_IMM_C \ + unsigned int shift = (opcode >> 7) & 0x1F; \ + if (LIKELY(shift)) { \ + u32 v = reg[opcode & 0x0F].I; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ + value = v >> shift; \ + } else { \ + value = 0; \ + C_OUT = (reg[opcode & 0x0F].I & 0x80000000) ? true : false;\ + } +#endif +// OP Rd,Rb,Rm LSR Rs +#ifndef VALUE_LSR_REG_C + #define VALUE_LSR_REG_C \ + unsigned int shift = reg[(opcode >> 8)&15].B.B0; \ + if (LIKELY(shift)) { \ + if (shift == 32) { \ + value = 0; \ + C_OUT = (reg[opcode & 0x0F].I & 0x80000000 ? true : false);\ + } else if (LIKELY(shift < 32)) { \ + u32 v = reg[opcode & 0x0F].I; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false;\ + value = v >> shift; \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } else { \ + value = reg[opcode & 0x0F].I; \ + } +#endif +// OP Rd,Rb,Rm ASR # +#ifndef VALUE_ASR_IMM_C + #define VALUE_ASR_IMM_C \ + unsigned int shift = (opcode >> 7) & 0x1F; \ + if (LIKELY(shift)) { \ + /* VC++ BUG: u32 v; (s32)v>>n is optimized to shr! */ \ + s32 v = reg[opcode & 0x0F].I; \ + C_OUT = (v >> (int)(shift - 1)) & 1 ? true : false;\ + value = v >> (int)shift; \ + } else { \ + if (reg[opcode & 0x0F].I & 0x80000000) { \ + value = 0xFFFFFFFF; \ + C_OUT = true; \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } +#endif +// OP Rd,Rb,Rm ASR Rs +#ifndef VALUE_ASR_REG_C + #define VALUE_ASR_REG_C \ + unsigned int shift = reg[(opcode >> 8)&15].B.B0; \ + if (LIKELY(shift < 32)) { \ + if (LIKELY(shift)) { \ + s32 v = reg[opcode & 0x0F].I; \ + C_OUT = (v >> (int)(shift - 1)) & 1 ? true : false;\ + value = v >> (int)shift; \ + } else { \ + value = reg[opcode & 0x0F].I; \ + } \ + } else { \ + if (reg[opcode & 0x0F].I & 0x80000000) { \ + value = 0xFFFFFFFF; \ + C_OUT = true; \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } +#endif +// OP Rd,Rb,Rm ROR # +#ifndef VALUE_ROR_IMM_C + #define VALUE_ROR_IMM_C \ + unsigned int shift = (opcode >> 7) & 0x1F; \ + if (LIKELY(shift)) { \ + u32 v = reg[opcode & 0x0F].I; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ + value = ((v << (32 - shift)) | \ + (v >> shift)); \ + } else { \ + u32 v = reg[opcode & 0x0F].I; \ + C_OUT = (v & 1) ? true : false; \ + value = ((v >> 1) | \ + (C_FLAG << 31)); \ + } +#endif +// OP Rd,Rb,Rm ROR Rs +#ifndef VALUE_ROR_REG_C + #define VALUE_ROR_REG_C \ + unsigned int shift = reg[(opcode >> 8)&15].B.B0; \ + if (LIKELY(shift & 0x1F)) { \ + u32 v = reg[opcode & 0x0F].I; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ + value = ((v << (32 - shift)) | \ + (v >> shift)); \ + } else { \ + value = reg[opcode & 0x0F].I; \ + if (shift) \ + C_OUT = (value & 0x80000000 ? true : false);\ + } +#endif +// OP Rd,Rb,# ROR # +#ifndef VALUE_IMM_C + #define VALUE_IMM_C \ + int shift = (opcode & 0xF00) >> 7; \ + if (UNLIKELY(shift)) { \ + u32 v = opcode & 0xFF; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ + value = ((v << (32 - shift)) | \ + (v >> shift)); \ + } else { \ + value = opcode & 0xFF; \ + } +#endif + +// Make the non-carry versions default to the carry versions +// (this is fine for C--the compiler will optimize the dead code out) +#ifndef ALU_INIT_NC + #define ALU_INIT_NC ALU_INIT_C +#endif +#ifndef VALUE_LSL_IMM_NC + #define VALUE_LSL_IMM_NC VALUE_LSL_IMM_C +#endif +#ifndef VALUE_LSL_REG_NC + #define VALUE_LSL_REG_NC VALUE_LSL_REG_C +#endif +#ifndef VALUE_LSR_IMM_NC + #define VALUE_LSR_IMM_NC VALUE_LSR_IMM_C +#endif +#ifndef VALUE_LSR_REG_NC + #define VALUE_LSR_REG_NC VALUE_LSR_REG_C +#endif +#ifndef VALUE_ASR_IMM_NC + #define VALUE_ASR_IMM_NC VALUE_ASR_IMM_C +#endif +#ifndef VALUE_ASR_REG_NC + #define VALUE_ASR_REG_NC VALUE_ASR_REG_C +#endif +#ifndef VALUE_ROR_IMM_NC + #define VALUE_ROR_IMM_NC VALUE_ROR_IMM_C +#endif +#ifndef VALUE_ROR_REG_NC + #define VALUE_ROR_REG_NC VALUE_ROR_REG_C +#endif +#ifndef VALUE_IMM_NC + #define VALUE_IMM_NC VALUE_IMM_C +#endif + +#define C_CHECK_PC(SETCOND) if (LIKELY(dest != 15)) { SETCOND } +#ifndef OP_AND + #define OP_AND \ + u32 res = reg[(opcode>>16)&15].I & value; \ + reg[dest].I = res; +#endif +#ifndef OP_ANDS + #define OP_ANDS OP_AND C_CHECK_PC(C_SETCOND_LOGICAL) +#endif +#ifndef OP_EOR + #define OP_EOR \ + u32 res = reg[(opcode>>16)&15].I ^ value; \ + reg[dest].I = res; +#endif +#ifndef OP_EORS + #define OP_EORS OP_EOR C_CHECK_PC(C_SETCOND_LOGICAL) +#endif +#ifndef OP_SUB + #define OP_SUB \ + u32 lhs = reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs - rhs; \ + reg[dest].I = res; +#endif +#ifndef OP_SUBS + #define OP_SUBS OP_SUB C_CHECK_PC(C_SETCOND_SUB) +#endif +#ifndef OP_RSB + #define OP_RSB \ + u32 lhs = reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = rhs - lhs; \ + reg[dest].I = res; +#endif +#ifndef OP_RSBS + #define OP_RSBS OP_RSB C_CHECK_PC(C_SETCOND_SUB) +#endif +#ifndef OP_ADD + #define OP_ADD \ + u32 lhs = reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs + rhs; \ + reg[dest].I = res; +#endif +#ifndef OP_ADDS + #define OP_ADDS OP_ADD C_CHECK_PC(C_SETCOND_ADD) +#endif +#ifndef OP_ADC + #define OP_ADC \ + u32 lhs = reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs + rhs + (u32)C_FLAG; \ + reg[dest].I = res; +#endif +#ifndef OP_ADCS + #define OP_ADCS OP_ADC C_CHECK_PC(C_SETCOND_ADD) +#endif +#ifndef OP_SBC + #define OP_SBC \ + u32 lhs = reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs - rhs - !((u32)C_FLAG); \ + reg[dest].I = res; +#endif +#ifndef OP_SBCS + #define OP_SBCS OP_SBC C_CHECK_PC(C_SETCOND_SUB) +#endif +#ifndef OP_RSC + #define OP_RSC \ + u32 lhs = reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = rhs - lhs - !((u32)C_FLAG); \ + reg[dest].I = res; +#endif +#ifndef OP_RSCS + #define OP_RSCS OP_RSC C_CHECK_PC(C_SETCOND_SUB) +#endif +#ifndef OP_TST + #define OP_TST \ + u32 res = reg[(opcode >> 16) & 0x0F].I & value; \ + C_SETCOND_LOGICAL; +#endif +#ifndef OP_TEQ + #define OP_TEQ \ + u32 res = reg[(opcode >> 16) & 0x0F].I ^ value; \ + C_SETCOND_LOGICAL; +#endif +#ifndef OP_CMP + #define OP_CMP \ + u32 lhs = reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs - rhs; \ + C_SETCOND_SUB; +#endif +#ifndef OP_CMN + #define OP_CMN \ + u32 lhs = reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs + rhs; \ + C_SETCOND_ADD; +#endif +#ifndef OP_ORR + #define OP_ORR \ + u32 res = reg[(opcode >> 16) & 0x0F].I | value; \ + reg[dest].I = res; +#endif +#ifndef OP_ORRS + #define OP_ORRS OP_ORR C_CHECK_PC(C_SETCOND_LOGICAL) +#endif +#ifndef OP_MOV + #define OP_MOV \ + u32 res = value; \ + reg[dest].I = res; +#endif +#ifndef OP_MOVS + #define OP_MOVS OP_MOV C_CHECK_PC(C_SETCOND_LOGICAL) +#endif +#ifndef OP_BIC + #define OP_BIC \ + u32 res = reg[(opcode >> 16) & 0x0F].I & (~value); \ + reg[dest].I = res; +#endif +#ifndef OP_BICS + #define OP_BICS OP_BIC C_CHECK_PC(C_SETCOND_LOGICAL) +#endif +#ifndef OP_MVN + #define OP_MVN \ + u32 res = ~value; \ + reg[dest].I = res; +#endif +#ifndef OP_MVNS + #define OP_MVNS OP_MVN C_CHECK_PC(C_SETCOND_LOGICAL) +#endif + +#ifndef SETCOND_NONE + #define SETCOND_NONE /*nothing*/ +#endif +#ifndef SETCOND_MUL + #define SETCOND_MUL \ + N_FLAG = ((s32)reg[dest].I < 0) ? true : false; \ + Z_FLAG = reg[dest].I ? false : true; +#endif +#ifndef SETCOND_MULL + #define SETCOND_MULL \ + N_FLAG = (reg[dest].I & 0x80000000) ? true : false;\ + Z_FLAG = reg[dest].I || reg[acc].I ? false : true; +#endif + +#ifndef ALU_FINISH + #define ALU_FINISH /*nothing*/ +#endif + +#ifndef ROR_IMM_MSR + #define ROR_IMM_MSR \ + u32 v = opcode & 0xff; \ + value = ((v << (32 - shift)) | (v >> shift)); +#endif +#ifndef ROR_OFFSET + #define ROR_OFFSET \ + offset = ((offset << (32 - shift)) | (offset >> shift)); +#endif +#ifndef RRX_OFFSET + #define RRX_OFFSET \ + offset = ((offset >> 1) | ((int)C_FLAG << 31)); +#endif + +// ALU ops (except multiply) ////////////////////////////////////////////// + +// ALU_INIT: init code (ALU_INIT_C or ALU_INIT_NC) +// GETVALUE: load value and shift/rotate (VALUE_XXX) +// OP: ALU operation (OP_XXX) +// MODECHANGE: MODECHANGE_NO or MODECHANGE_YES +// ISREGSHIFT: 1 for insns of the form ...,Rn LSL/etc Rs; 0 otherwise +// ALU_INIT, GETVALUE, OP, and ALU_FINISH are concatenated in order. +#define ALU_INSN(ALU_INIT, GETVALUE, OP, MODECHANGE, ISREGSHIFT) \ + ALU_INIT GETVALUE OP ALU_FINISH; \ + if (LIKELY((opcode & 0x0000F000) != 0x0000F000)) { \ + clockTicks = 1 + ISREGSHIFT \ + + codeTicksAccessSeq32(armNextPC); \ + } else { \ + MODECHANGE; \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + ARM_PREFETCH; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + THUMB_PREFETCH; \ + } \ + clockTicks = 3 + ISREGSHIFT \ + + codeTicksAccess32(armNextPC) \ + + codeTicksAccessSeq32(armNextPC) \ + + codeTicksAccessSeq32(armNextPC); \ + } + +#define MODECHANGE_NO /*nothing*/ +#define MODECHANGE_YES CPUSwitchMode(reg[17].I & 0x1f, false); + +#define DEFINE_ALU_INSN_C(CODE1, CODE2, OP, MODECHANGE) \ + static INSN_REGPARM void arm##CODE1##0(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSL_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + static INSN_REGPARM void arm##CODE1##1(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSL_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + static INSN_REGPARM void arm##CODE1##2(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSR_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + static INSN_REGPARM void arm##CODE1##3(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSR_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + static INSN_REGPARM void arm##CODE1##4(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ASR_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + static INSN_REGPARM void arm##CODE1##5(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ASR_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + static INSN_REGPARM void arm##CODE1##6(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ROR_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + static INSN_REGPARM void arm##CODE1##7(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ROR_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + static INSN_REGPARM void arm##CODE2##0(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); } +#define DEFINE_ALU_INSN_NC(CODE1, CODE2, OP, MODECHANGE) \ + static INSN_REGPARM void arm##CODE1##0(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSL_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + static INSN_REGPARM void arm##CODE1##1(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSL_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + static INSN_REGPARM void arm##CODE1##2(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSR_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + static INSN_REGPARM void arm##CODE1##3(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSR_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + static INSN_REGPARM void arm##CODE1##4(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ASR_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + static INSN_REGPARM void arm##CODE1##5(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ASR_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + static INSN_REGPARM void arm##CODE1##6(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ROR_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + static INSN_REGPARM void arm##CODE1##7(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ROR_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + static INSN_REGPARM void arm##CODE2##0(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); } + +// AND +DEFINE_ALU_INSN_NC(00, 20, AND, NO) +// ANDS +DEFINE_ALU_INSN_C (01, 21, ANDS, YES) + +// EOR +DEFINE_ALU_INSN_NC(02, 22, EOR, NO) +// EORS +DEFINE_ALU_INSN_C (03, 23, EORS, YES) + +// SUB +DEFINE_ALU_INSN_NC(04, 24, SUB, NO) +// SUBS +DEFINE_ALU_INSN_NC(05, 25, SUBS, YES) + +// RSB +DEFINE_ALU_INSN_NC(06, 26, RSB, NO) +// RSBS +DEFINE_ALU_INSN_NC(07, 27, RSBS, YES) + +// ADD +DEFINE_ALU_INSN_NC(08, 28, ADD, NO) +// ADDS +DEFINE_ALU_INSN_NC(09, 29, ADDS, YES) + +// ADC +DEFINE_ALU_INSN_NC(0A, 2A, ADC, NO) +// ADCS +DEFINE_ALU_INSN_NC(0B, 2B, ADCS, YES) + +// SBC +DEFINE_ALU_INSN_NC(0C, 2C, SBC, NO) +// SBCS +DEFINE_ALU_INSN_NC(0D, 2D, SBCS, YES) + +// RSC +DEFINE_ALU_INSN_NC(0E, 2E, RSC, NO) +// RSCS +DEFINE_ALU_INSN_NC(0F, 2F, RSCS, YES) + +// TST +DEFINE_ALU_INSN_C (11, 31, TST, NO) + +// TEQ +DEFINE_ALU_INSN_C (13, 33, TEQ, NO) + +// CMP +DEFINE_ALU_INSN_NC(15, 35, CMP, NO) + +// CMN +DEFINE_ALU_INSN_NC(17, 37, CMN, NO) + +// ORR +DEFINE_ALU_INSN_NC(18, 38, ORR, NO) +// ORRS +DEFINE_ALU_INSN_C (19, 39, ORRS, YES) + +// MOV +DEFINE_ALU_INSN_NC(1A, 3A, MOV, NO) +// MOVS +DEFINE_ALU_INSN_C (1B, 3B, MOVS, YES) + +// BIC +DEFINE_ALU_INSN_NC(1C, 3C, BIC, NO) +// BICS +DEFINE_ALU_INSN_C (1D, 3D, BICS, YES) + +// MVN +DEFINE_ALU_INSN_NC(1E, 3E, MVN, NO) +// MVNS +DEFINE_ALU_INSN_C (1F, 3F, MVNS, YES) + +// Multiply instructions ////////////////////////////////////////////////// + +// OP: OP_MUL, OP_MLA etc. +// SETCOND: SETCOND_NONE, SETCOND_MUL, or SETCOND_MULL +// CYCLES: base cycle count (1, 2, or 3) +#define MUL_INSN(OP, SETCOND, CYCLES) \ + int mult = (opcode & 0x0F); \ + u32 rs = reg[(opcode >> 8) & 0x0F].I; \ + int acc = (opcode >> 12) & 0x0F; /* or destLo */ \ + int dest = (opcode >> 16) & 0x0F; /* or destHi */ \ + OP; \ + SETCOND; \ + if ((s32)rs < 0) \ + rs = ~rs; \ + if ((rs & 0xFFFFFF00) == 0) \ + clockTicks += 0; \ + else if ((rs & 0xFFFF0000) == 0) \ + clockTicks += 1; \ + else if ((rs & 0xFF000000) == 0) \ + clockTicks += 2; \ + else \ + clockTicks += 3; \ + if (busPrefetchCount == 0) \ + busPrefetchCount = ((busPrefetchCount+1)<> 32); +#define OP_MLAL(SIGN) \ + SIGN##64 res = ((SIGN##64)reg[dest].I<<32 | reg[acc].I)\ + + ((SIGN##64)(SIGN##32)reg[mult].I \ + * (SIGN##64)(SIGN##32)rs); \ + reg[acc].I = (u32)res; \ + reg[dest].I = (u32)(res >> 32); +#define OP_UMULL OP_MULL(u) +#define OP_UMLAL OP_MLAL(u) +#define OP_SMULL OP_MULL(s) +#define OP_SMLAL OP_MLAL(s) + +// MUL Rd, Rm, Rs +static INSN_REGPARM void arm009(u32 opcode) { MUL_INSN(OP_MUL, SETCOND_NONE, 1); } +// MULS Rd, Rm, Rs +static INSN_REGPARM void arm019(u32 opcode) { MUL_INSN(OP_MUL, SETCOND_MUL, 1); } + +// MLA Rd, Rm, Rs, Rn +static INSN_REGPARM void arm029(u32 opcode) { MUL_INSN(OP_MLA, SETCOND_NONE, 2); } +// MLAS Rd, Rm, Rs, Rn +static INSN_REGPARM void arm039(u32 opcode) { MUL_INSN(OP_MLA, SETCOND_MUL, 2); } + +// UMULL RdLo, RdHi, Rn, Rs +static INSN_REGPARM void arm089(u32 opcode) { MUL_INSN(OP_UMULL, SETCOND_NONE, 2); } +// UMULLS RdLo, RdHi, Rn, Rs +static INSN_REGPARM void arm099(u32 opcode) { MUL_INSN(OP_UMULL, SETCOND_MULL, 2); } + +// UMLAL RdLo, RdHi, Rn, Rs +static INSN_REGPARM void arm0A9(u32 opcode) { MUL_INSN(OP_UMLAL, SETCOND_NONE, 3); } +// UMLALS RdLo, RdHi, Rn, Rs +static INSN_REGPARM void arm0B9(u32 opcode) { MUL_INSN(OP_UMLAL, SETCOND_MULL, 3); } + +// SMULL RdLo, RdHi, Rm, Rs +static INSN_REGPARM void arm0C9(u32 opcode) { MUL_INSN(OP_SMULL, SETCOND_NONE, 2); } +// SMULLS RdLo, RdHi, Rm, Rs +static INSN_REGPARM void arm0D9(u32 opcode) { MUL_INSN(OP_SMULL, SETCOND_MULL, 2); } + +// SMLAL RdLo, RdHi, Rm, Rs +static INSN_REGPARM void arm0E9(u32 opcode) { MUL_INSN(OP_SMLAL, SETCOND_NONE, 3); } +// SMLALS RdLo, RdHi, Rm, Rs +static INSN_REGPARM void arm0F9(u32 opcode) { MUL_INSN(OP_SMLAL, SETCOND_MULL, 3); } + +// Misc instructions ////////////////////////////////////////////////////// + +// SWP Rd, Rm, [Rn] +static INSN_REGPARM void arm109(u32 opcode) +{ + u32 address = reg[(opcode >> 16) & 15].I; + u32 temp = CPUReadMemory(address); + CPUWriteMemory(address, reg[opcode&15].I); + reg[(opcode >> 12) & 15].I = temp; + clockTicks = 4 + dataTicksAccess32(address) + dataTicksAccess32(address) + + codeTicksAccess32(armNextPC); +} + +// SWPB Rd, Rm, [Rn] +static INSN_REGPARM void arm149(u32 opcode) +{ + u32 address = reg[(opcode >> 16) & 15].I; + u32 temp = CPUReadByte(address); + CPUWriteByte(address, reg[opcode&15].B.B0); + reg[(opcode>>12)&15].I = temp; + clockTicks = 4 + dataTicksAccess32(address) + dataTicksAccess32(address) + + codeTicksAccess32(armNextPC); +} + +// MRS Rd, CPSR +static INSN_REGPARM void arm100(u32 opcode) +{ + if (LIKELY((opcode & 0x0FFF0FFF) == 0x010F0000)) { + CPUUpdateCPSR(); + reg[(opcode >> 12) & 0x0F].I = reg[16].I; + } else { + armUnknownInsn(opcode); + } +} + +// MRS Rd, SPSR +static INSN_REGPARM void arm140(u32 opcode) +{ + if (LIKELY((opcode & 0x0FFF0FFF) == 0x014F0000)) { + reg[(opcode >> 12) & 0x0F].I = reg[17].I; + } else { + armUnknownInsn(opcode); + } +} + +// MSR CPSR_fields, Rm +static INSN_REGPARM void arm120(u32 opcode) +{ + if (LIKELY((opcode & 0x0FF0FFF0) == 0x0120F000)) { + CPUUpdateCPSR(); + u32 value = reg[opcode & 15].I; + u32 newValue = reg[16].I; + if (armMode > 0x10) { + if (opcode & 0x00010000) + newValue = (newValue & 0xFFFFFF00) | (value & 0x000000FF); + if (opcode & 0x00020000) + newValue = (newValue & 0xFFFF00FF) | (value & 0x0000FF00); + if (opcode & 0x00040000) + newValue = (newValue & 0xFF00FFFF) | (value & 0x00FF0000); + } + if (opcode & 0x00080000) + newValue = (newValue & 0x00FFFFFF) | (value & 0xFF000000); + newValue |= 0x10; + CPUSwitchMode(newValue & 0x1F, false); + reg[16].I = newValue; + CPUUpdateFlags(); + if (!armState) { // this should not be allowed, but it seems to work + THUMB_PREFETCH; + reg[15].I = armNextPC + 2; + } + } else { + armUnknownInsn(opcode); + } +} + +// MSR SPSR_fields, Rm +static INSN_REGPARM void arm160(u32 opcode) +{ + if (LIKELY((opcode & 0x0FF0FFF0) == 0x0160F000)) { + u32 value = reg[opcode & 15].I; + if (armMode > 0x10 && armMode < 0x1F) { + if (opcode & 0x00010000) + reg[17].I = (reg[17].I & 0xFFFFFF00) | (value & 0x000000FF); + if (opcode & 0x00020000) + reg[17].I = (reg[17].I & 0xFFFF00FF) | (value & 0x0000FF00); + if (opcode & 0x00040000) + reg[17].I = (reg[17].I & 0xFF00FFFF) | (value & 0x00FF0000); + if (opcode & 0x00080000) + reg[17].I = (reg[17].I & 0x00FFFFFF) | (value & 0xFF000000); + } + } else { + armUnknownInsn(opcode); + } +} + +// MSR CPSR_fields, # +static INSN_REGPARM void arm320(u32 opcode) +{ + if (LIKELY((opcode & 0x0FF0F000) == 0x0320F000)) { + CPUUpdateCPSR(); + u32 value = opcode & 0xFF; + int shift = (opcode & 0xF00) >> 7; + if (shift) { + ROR_IMM_MSR; + } + u32 newValue = reg[16].I; + if (armMode > 0x10) { + if (opcode & 0x00010000) + newValue = (newValue & 0xFFFFFF00) | (value & 0x000000FF); + if (opcode & 0x00020000) + newValue = (newValue & 0xFFFF00FF) | (value & 0x0000FF00); + if (opcode & 0x00040000) + newValue = (newValue & 0xFF00FFFF) | (value & 0x00FF0000); + } + if (opcode & 0x00080000) + newValue = (newValue & 0x00FFFFFF) | (value & 0xFF000000); + + newValue |= 0x10; + + CPUSwitchMode(newValue & 0x1F, false); + reg[16].I = newValue; + CPUUpdateFlags(); + if (!armState) { // this should not be allowed, but it seems to work + THUMB_PREFETCH; + reg[15].I = armNextPC + 2; + } + } else { + armUnknownInsn(opcode); + } +} + +// MSR SPSR_fields, # +static INSN_REGPARM void arm360(u32 opcode) +{ + if (LIKELY((opcode & 0x0FF0F000) == 0x0360F000)) { + if (armMode > 0x10 && armMode < 0x1F) { + u32 value = opcode & 0xFF; + int shift = (opcode & 0xF00) >> 7; + if (shift) { + ROR_IMM_MSR; + } + if (opcode & 0x00010000) + reg[17].I = (reg[17].I & 0xFFFFFF00) | (value & 0x000000FF); + if (opcode & 0x00020000) + reg[17].I = (reg[17].I & 0xFFFF00FF) | (value & 0x0000FF00); + if (opcode & 0x00040000) + reg[17].I = (reg[17].I & 0xFF00FFFF) | (value & 0x00FF0000); + if (opcode & 0x00080000) + reg[17].I = (reg[17].I & 0x00FFFFFF) | (value & 0xFF000000); + } + } else { + armUnknownInsn(opcode); + } +} + +// BX Rm +static INSN_REGPARM void arm121(u32 opcode) +{ + if (LIKELY((opcode & 0x0FFFFFF0) == 0x012FFF10)) { + int base = opcode & 0x0F; + busPrefetchCount = 0; + armState = reg[base].I & 1 ? false : true; + if (armState) { + reg[15].I = reg[base].I & 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + ARM_PREFETCH; + clockTicks = 3 + codeTicksAccessSeq32(armNextPC) + + codeTicksAccessSeq32(armNextPC) + + codeTicksAccess32(armNextPC); + } else { + reg[15].I = reg[base].I & 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = 3 + codeTicksAccessSeq16(armNextPC) + + codeTicksAccessSeq16(armNextPC) + + codeTicksAccess16(armNextPC); + } + } else { + armUnknownInsn(opcode); + } +} + +// Load/store ///////////////////////////////////////////////////////////// + +#define OFFSET_IMM \ + int offset = opcode & 0xFFF; +#define OFFSET_IMM8 \ + int offset = ((opcode & 0x0F) | ((opcode>>4) & 0xF0)); +#define OFFSET_REG \ + int offset = reg[opcode & 15].I; +#define OFFSET_LSL \ + int offset = reg[opcode & 15].I << ((opcode>>7) & 31); +#define OFFSET_LSR \ + int shift = (opcode >> 7) & 31; \ + int offset = shift ? reg[opcode & 15].I >> shift : 0; +#define OFFSET_ASR \ + int shift = (opcode >> 7) & 31; \ + int offset; \ + if (shift) \ + offset = (int)((s32)reg[opcode & 15].I >> shift);\ + else if (reg[opcode & 15].I & 0x80000000) \ + offset = 0xFFFFFFFF; \ + else \ + offset = 0; +#define OFFSET_ROR \ + int shift = (opcode >> 7) & 31; \ + u32 offset = reg[opcode & 15].I; \ + if (shift) { \ + ROR_OFFSET; \ + } else { \ + RRX_OFFSET; \ + } + +#define ADDRESS_POST (reg[base].I) +#define ADDRESS_PREDEC (reg[base].I - offset) +#define ADDRESS_PREINC (reg[base].I + offset) + +#define OP_STR CPUWriteMemory(address, reg[dest].I) +#define OP_STRH CPUWriteHalfWord(address, reg[dest].W.W0) +#define OP_STRB CPUWriteByte(address, reg[dest].B.B0) +#define OP_LDR reg[dest].I = CPUReadMemory(address) +#define OP_LDRH reg[dest].I = CPUReadHalfWord(address) +#define OP_LDRB reg[dest].I = CPUReadByte(address) +#define OP_LDRSH reg[dest].I = (s16)CPUReadHalfWordSigned(address) +#define OP_LDRSB reg[dest].I = (s8)CPUReadByte(address) + +#define WRITEBACK_NONE /*nothing*/ +#define WRITEBACK_PRE reg[base].I = address +#define WRITEBACK_POSTDEC reg[base].I = address - offset +#define WRITEBACK_POSTINC reg[base].I = address + offset + +#define LDRSTR_INIT(CALC_OFFSET, CALC_ADDRESS) \ + if (busPrefetchCount == 0) \ + busPrefetch = busPrefetchEnable; \ + int dest = (opcode >> 12) & 15; \ + int base = (opcode >> 16) & 15; \ + CALC_OFFSET; \ + u32 address = CALC_ADDRESS; + +#define STR(CALC_OFFSET, CALC_ADDRESS, STORE_DATA, WRITEBACK1, WRITEBACK2, SIZE) \ + LDRSTR_INIT(CALC_OFFSET, CALC_ADDRESS); \ + WRITEBACK1; \ + STORE_DATA; \ + WRITEBACK2; \ + clockTicks = 2 + dataTicksAccess##SIZE(address) \ + + codeTicksAccess32(armNextPC); +#define LDR(CALC_OFFSET, CALC_ADDRESS, LOAD_DATA, WRITEBACK, SIZE) \ + LDRSTR_INIT(CALC_OFFSET, CALC_ADDRESS); \ + LOAD_DATA; \ + if (dest != base) \ + { \ + WRITEBACK; \ + } \ + clockTicks = 0; \ + if (dest == 15) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + ARM_PREFETCH; \ + clockTicks += 2 + dataTicksAccessSeq32(address) \ + + dataTicksAccessSeq32(address);\ + } \ + clockTicks += 3 + dataTicksAccess##SIZE(address) \ + + codeTicksAccess32(armNextPC); +#define STR_POSTDEC(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_POST, STORE_DATA, WRITEBACK_NONE, WRITEBACK_POSTDEC, SIZE) +#define STR_POSTINC(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_POST, STORE_DATA, WRITEBACK_NONE, WRITEBACK_POSTINC, SIZE) +#define STR_PREDEC(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_PREDEC, STORE_DATA, WRITEBACK_NONE, WRITEBACK_NONE, SIZE) +#define STR_PREDEC_WB(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_PREDEC, STORE_DATA, WRITEBACK_PRE, WRITEBACK_NONE, SIZE) +#define STR_PREINC(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_PREINC, STORE_DATA, WRITEBACK_NONE, WRITEBACK_NONE, SIZE) +#define STR_PREINC_WB(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_PREINC, STORE_DATA, WRITEBACK_PRE, WRITEBACK_NONE, SIZE) +#define LDR_POSTDEC(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_POST, LOAD_DATA, WRITEBACK_POSTDEC, SIZE) +#define LDR_POSTINC(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_POST, LOAD_DATA, WRITEBACK_POSTINC, SIZE) +#define LDR_PREDEC(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_PREDEC, LOAD_DATA, WRITEBACK_NONE, SIZE) +#define LDR_PREDEC_WB(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_PREDEC, LOAD_DATA, WRITEBACK_PRE, SIZE) +#define LDR_PREINC(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_PREINC, LOAD_DATA, WRITEBACK_NONE, SIZE) +#define LDR_PREINC_WB(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_PREINC, LOAD_DATA, WRITEBACK_PRE, SIZE) + +// STRH Rd, [Rn], -Rm +static INSN_REGPARM void arm00B(u32 opcode) { STR_POSTDEC(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn], #-offset +static INSN_REGPARM void arm04B(u32 opcode) { STR_POSTDEC(OFFSET_IMM8, OP_STRH, 16); } +// STRH Rd, [Rn], Rm +static INSN_REGPARM void arm08B(u32 opcode) { STR_POSTINC(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn], #offset +static INSN_REGPARM void arm0CB(u32 opcode) { STR_POSTINC(OFFSET_IMM8, OP_STRH, 16); } +// STRH Rd, [Rn, -Rm] +static INSN_REGPARM void arm10B(u32 opcode) { STR_PREDEC(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn, -Rm]! +static INSN_REGPARM void arm12B(u32 opcode) { STR_PREDEC_WB(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn, -#offset] +static INSN_REGPARM void arm14B(u32 opcode) { STR_PREDEC(OFFSET_IMM8, OP_STRH, 16); } +// STRH Rd, [Rn, -#offset]! +static INSN_REGPARM void arm16B(u32 opcode) { STR_PREDEC_WB(OFFSET_IMM8, OP_STRH, 16); } +// STRH Rd, [Rn, Rm] +static INSN_REGPARM void arm18B(u32 opcode) { STR_PREINC(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn, Rm]! +static INSN_REGPARM void arm1AB(u32 opcode) { STR_PREINC_WB(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn, #offset] +static INSN_REGPARM void arm1CB(u32 opcode) { STR_PREINC(OFFSET_IMM8, OP_STRH, 16); } +// STRH Rd, [Rn, #offset]! +static INSN_REGPARM void arm1EB(u32 opcode) { STR_PREINC_WB(OFFSET_IMM8, OP_STRH, 16); } + +// LDRH Rd, [Rn], -Rm +static INSN_REGPARM void arm01B(u32 opcode) { LDR_POSTDEC(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn], #-offset +static INSN_REGPARM void arm05B(u32 opcode) { LDR_POSTDEC(OFFSET_IMM8, OP_LDRH, 16); } +// LDRH Rd, [Rn], Rm +static INSN_REGPARM void arm09B(u32 opcode) { LDR_POSTINC(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn], #offset +static INSN_REGPARM void arm0DB(u32 opcode) { LDR_POSTINC(OFFSET_IMM8, OP_LDRH, 16); } +// LDRH Rd, [Rn, -Rm] +static INSN_REGPARM void arm11B(u32 opcode) { LDR_PREDEC(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn, -Rm]! +static INSN_REGPARM void arm13B(u32 opcode) { LDR_PREDEC_WB(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn, -#offset] +static INSN_REGPARM void arm15B(u32 opcode) { LDR_PREDEC(OFFSET_IMM8, OP_LDRH, 16); } +// LDRH Rd, [Rn, -#offset]! +static INSN_REGPARM void arm17B(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM8, OP_LDRH, 16); } +// LDRH Rd, [Rn, Rm] +static INSN_REGPARM void arm19B(u32 opcode) { LDR_PREINC(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn, Rm]! +static INSN_REGPARM void arm1BB(u32 opcode) { LDR_PREINC_WB(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn, #offset] +static INSN_REGPARM void arm1DB(u32 opcode) { LDR_PREINC(OFFSET_IMM8, OP_LDRH, 16); } +// LDRH Rd, [Rn, #offset]! +static INSN_REGPARM void arm1FB(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM8, OP_LDRH, 16); } + +// LDRSB Rd, [Rn], -Rm +static INSN_REGPARM void arm01D(u32 opcode) { LDR_POSTDEC(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn], #-offset +static INSN_REGPARM void arm05D(u32 opcode) { LDR_POSTDEC(OFFSET_IMM8, OP_LDRSB, 16); } +// LDRSB Rd, [Rn], Rm +static INSN_REGPARM void arm09D(u32 opcode) { LDR_POSTINC(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn], #offset +static INSN_REGPARM void arm0DD(u32 opcode) { LDR_POSTINC(OFFSET_IMM8, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, -Rm] +static INSN_REGPARM void arm11D(u32 opcode) { LDR_PREDEC(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, -Rm]! +static INSN_REGPARM void arm13D(u32 opcode) { LDR_PREDEC_WB(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, -#offset] +static INSN_REGPARM void arm15D(u32 opcode) { LDR_PREDEC(OFFSET_IMM8, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, -#offset]! +static INSN_REGPARM void arm17D(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM8, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, Rm] +static INSN_REGPARM void arm19D(u32 opcode) { LDR_PREINC(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, Rm]! +static INSN_REGPARM void arm1BD(u32 opcode) { LDR_PREINC_WB(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, #offset] +static INSN_REGPARM void arm1DD(u32 opcode) { LDR_PREINC(OFFSET_IMM8, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, #offset]! +static INSN_REGPARM void arm1FD(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM8, OP_LDRSB, 16); } + +// LDRSH Rd, [Rn], -Rm +static INSN_REGPARM void arm01F(u32 opcode) { LDR_POSTDEC(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn], #-offset +static INSN_REGPARM void arm05F(u32 opcode) { LDR_POSTDEC(OFFSET_IMM8, OP_LDRSH, 16); } +// LDRSH Rd, [Rn], Rm +static INSN_REGPARM void arm09F(u32 opcode) { LDR_POSTINC(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn], #offset +static INSN_REGPARM void arm0DF(u32 opcode) { LDR_POSTINC(OFFSET_IMM8, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, -Rm] +static INSN_REGPARM void arm11F(u32 opcode) { LDR_PREDEC(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, -Rm]! +static INSN_REGPARM void arm13F(u32 opcode) { LDR_PREDEC_WB(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, -#offset] +static INSN_REGPARM void arm15F(u32 opcode) { LDR_PREDEC(OFFSET_IMM8, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, -#offset]! +static INSN_REGPARM void arm17F(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM8, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, Rm] +static INSN_REGPARM void arm19F(u32 opcode) { LDR_PREINC(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, Rm]! +static INSN_REGPARM void arm1BF(u32 opcode) { LDR_PREINC_WB(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, #offset] +static INSN_REGPARM void arm1DF(u32 opcode) { LDR_PREINC(OFFSET_IMM8, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, #offset]! +static INSN_REGPARM void arm1FF(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM8, OP_LDRSH, 16); } + +// STR[T] Rd, [Rn], -# +// Note: STR and STRT do the same thing on the GBA (likewise for LDR/LDRT etc) +static INSN_REGPARM void arm400(u32 opcode) { STR_POSTDEC(OFFSET_IMM, OP_STR, 32); } +// LDR[T] Rd, [Rn], -# +static INSN_REGPARM void arm410(u32 opcode) { LDR_POSTDEC(OFFSET_IMM, OP_LDR, 32); } +// STRB[T] Rd, [Rn], -# +static INSN_REGPARM void arm440(u32 opcode) { STR_POSTDEC(OFFSET_IMM, OP_STRB, 16); } +// LDRB[T] Rd, [Rn], -# +static INSN_REGPARM void arm450(u32 opcode) { LDR_POSTDEC(OFFSET_IMM, OP_LDRB, 16); } +// STR[T] Rd, [Rn], # +static INSN_REGPARM void arm480(u32 opcode) { STR_POSTINC(OFFSET_IMM, OP_STR, 32); } +// LDR Rd, [Rn], # +static INSN_REGPARM void arm490(u32 opcode) { LDR_POSTINC(OFFSET_IMM, OP_LDR, 32); } +// STRB[T] Rd, [Rn], # +static INSN_REGPARM void arm4C0(u32 opcode) { STR_POSTINC(OFFSET_IMM, OP_STRB, 16); } +// LDRB[T] Rd, [Rn], # +static INSN_REGPARM void arm4D0(u32 opcode) { LDR_POSTINC(OFFSET_IMM, OP_LDRB, 16); } +// STR Rd, [Rn, -#] +static INSN_REGPARM void arm500(u32 opcode) { STR_PREDEC(OFFSET_IMM, OP_STR, 32); } +// LDR Rd, [Rn, -#] +static INSN_REGPARM void arm510(u32 opcode) { LDR_PREDEC(OFFSET_IMM, OP_LDR, 32); } +// STR Rd, [Rn, -#]! +static INSN_REGPARM void arm520(u32 opcode) { STR_PREDEC_WB(OFFSET_IMM, OP_STR, 32); } +// LDR Rd, [Rn, -#]! +static INSN_REGPARM void arm530(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM, OP_LDR, 32); } +// STRB Rd, [Rn, -#] +static INSN_REGPARM void arm540(u32 opcode) { STR_PREDEC(OFFSET_IMM, OP_STRB, 16); } +// LDRB Rd, [Rn, -#] +static INSN_REGPARM void arm550(u32 opcode) { LDR_PREDEC(OFFSET_IMM, OP_LDRB, 16); } +// STRB Rd, [Rn, -#]! +static INSN_REGPARM void arm560(u32 opcode) { STR_PREDEC_WB(OFFSET_IMM, OP_STRB, 16); } +// LDRB Rd, [Rn, -#]! +static INSN_REGPARM void arm570(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM, OP_LDRB, 16); } +// STR Rd, [Rn, #] +static INSN_REGPARM void arm580(u32 opcode) { STR_PREINC(OFFSET_IMM, OP_STR, 32); } +// LDR Rd, [Rn, #] +static INSN_REGPARM void arm590(u32 opcode) { LDR_PREINC(OFFSET_IMM, OP_LDR, 32); } +// STR Rd, [Rn, #]! +static INSN_REGPARM void arm5A0(u32 opcode) { STR_PREINC_WB(OFFSET_IMM, OP_STR, 32); } +// LDR Rd, [Rn, #]! +static INSN_REGPARM void arm5B0(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM, OP_LDR, 32); } +// STRB Rd, [Rn, #] +static INSN_REGPARM void arm5C0(u32 opcode) { STR_PREINC(OFFSET_IMM, OP_STRB, 16); } +// LDRB Rd, [Rn, #] +static INSN_REGPARM void arm5D0(u32 opcode) { LDR_PREINC(OFFSET_IMM, OP_LDRB, 16); } +// STRB Rd, [Rn, #]! +static INSN_REGPARM void arm5E0(u32 opcode) { STR_PREINC_WB(OFFSET_IMM, OP_STRB, 16); } +// LDRB Rd, [Rn, #]! +static INSN_REGPARM void arm5F0(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM, OP_LDRB, 16); } + +// STR[T] Rd, [Rn], -Rm, LSL # +static INSN_REGPARM void arm600(u32 opcode) { STR_POSTDEC(OFFSET_LSL, OP_STR, 32); } +// STR[T] Rd, [Rn], -Rm, LSR # +static INSN_REGPARM void arm602(u32 opcode) { STR_POSTDEC(OFFSET_LSR, OP_STR, 32); } +// STR[T] Rd, [Rn], -Rm, ASR # +static INSN_REGPARM void arm604(u32 opcode) { STR_POSTDEC(OFFSET_ASR, OP_STR, 32); } +// STR[T] Rd, [Rn], -Rm, ROR # +static INSN_REGPARM void arm606(u32 opcode) { STR_POSTDEC(OFFSET_ROR, OP_STR, 32); } +// LDR[T] Rd, [Rn], -Rm, LSL # +static INSN_REGPARM void arm610(u32 opcode) { LDR_POSTDEC(OFFSET_LSL, OP_LDR, 32); } +// LDR[T] Rd, [Rn], -Rm, LSR # +static INSN_REGPARM void arm612(u32 opcode) { LDR_POSTDEC(OFFSET_LSR, OP_LDR, 32); } +// LDR[T] Rd, [Rn], -Rm, ASR # +static INSN_REGPARM void arm614(u32 opcode) { LDR_POSTDEC(OFFSET_ASR, OP_LDR, 32); } +// LDR[T] Rd, [Rn], -Rm, ROR # +static INSN_REGPARM void arm616(u32 opcode) { LDR_POSTDEC(OFFSET_ROR, OP_LDR, 32); } +// STRB[T] Rd, [Rn], -Rm, LSL # +static INSN_REGPARM void arm640(u32 opcode) { STR_POSTDEC(OFFSET_LSL, OP_STRB, 16); } +// STRB[T] Rd, [Rn], -Rm, LSR # +static INSN_REGPARM void arm642(u32 opcode) { STR_POSTDEC(OFFSET_LSR, OP_STRB, 16); } +// STRB[T] Rd, [Rn], -Rm, ASR # +static INSN_REGPARM void arm644(u32 opcode) { STR_POSTDEC(OFFSET_ASR, OP_STRB, 16); } +// STRB[T] Rd, [Rn], -Rm, ROR # +static INSN_REGPARM void arm646(u32 opcode) { STR_POSTDEC(OFFSET_ROR, OP_STRB, 16); } +// LDRB[T] Rd, [Rn], -Rm, LSL # +static INSN_REGPARM void arm650(u32 opcode) { LDR_POSTDEC(OFFSET_LSL, OP_LDRB, 16); } +// LDRB[T] Rd, [Rn], -Rm, LSR # +static INSN_REGPARM void arm652(u32 opcode) { LDR_POSTDEC(OFFSET_LSR, OP_LDRB, 16); } +// LDRB[T] Rd, [Rn], -Rm, ASR # +static INSN_REGPARM void arm654(u32 opcode) { LDR_POSTDEC(OFFSET_ASR, OP_LDRB, 16); } +// LDRB Rd, [Rn], -Rm, ROR # +static INSN_REGPARM void arm656(u32 opcode) { LDR_POSTDEC(OFFSET_ROR, OP_LDRB, 16); } +// STR[T] Rd, [Rn], Rm, LSL # +static INSN_REGPARM void arm680(u32 opcode) { STR_POSTINC(OFFSET_LSL, OP_STR, 32); } +// STR[T] Rd, [Rn], Rm, LSR # +static INSN_REGPARM void arm682(u32 opcode) { STR_POSTINC(OFFSET_LSR, OP_STR, 32); } +// STR[T] Rd, [Rn], Rm, ASR # +static INSN_REGPARM void arm684(u32 opcode) { STR_POSTINC(OFFSET_ASR, OP_STR, 32); } +// STR[T] Rd, [Rn], Rm, ROR # +static INSN_REGPARM void arm686(u32 opcode) { STR_POSTINC(OFFSET_ROR, OP_STR, 32); } +// LDR[T] Rd, [Rn], Rm, LSL # +static INSN_REGPARM void arm690(u32 opcode) { LDR_POSTINC(OFFSET_LSL, OP_LDR, 32); } +// LDR[T] Rd, [Rn], Rm, LSR # +static INSN_REGPARM void arm692(u32 opcode) { LDR_POSTINC(OFFSET_LSR, OP_LDR, 32); } +// LDR[T] Rd, [Rn], Rm, ASR # +static INSN_REGPARM void arm694(u32 opcode) { LDR_POSTINC(OFFSET_ASR, OP_LDR, 32); } +// LDR[T] Rd, [Rn], Rm, ROR # +static INSN_REGPARM void arm696(u32 opcode) { LDR_POSTINC(OFFSET_ROR, OP_LDR, 32); } +// STRB[T] Rd, [Rn], Rm, LSL # +static INSN_REGPARM void arm6C0(u32 opcode) { STR_POSTINC(OFFSET_LSL, OP_STRB, 16); } +// STRB[T] Rd, [Rn], Rm, LSR # +static INSN_REGPARM void arm6C2(u32 opcode) { STR_POSTINC(OFFSET_LSR, OP_STRB, 16); } +// STRB[T] Rd, [Rn], Rm, ASR # +static INSN_REGPARM void arm6C4(u32 opcode) { STR_POSTINC(OFFSET_ASR, OP_STRB, 16); } +// STRB[T] Rd, [Rn], Rm, ROR # +static INSN_REGPARM void arm6C6(u32 opcode) { STR_POSTINC(OFFSET_ROR, OP_STRB, 16); } +// LDRB[T] Rd, [Rn], Rm, LSL # +static INSN_REGPARM void arm6D0(u32 opcode) { LDR_POSTINC(OFFSET_LSL, OP_LDRB, 16); } +// LDRB[T] Rd, [Rn], Rm, LSR # +static INSN_REGPARM void arm6D2(u32 opcode) { LDR_POSTINC(OFFSET_LSR, OP_LDRB, 16); } +// LDRB[T] Rd, [Rn], Rm, ASR # +static INSN_REGPARM void arm6D4(u32 opcode) { LDR_POSTINC(OFFSET_ASR, OP_LDRB, 16); } +// LDRB[T] Rd, [Rn], Rm, ROR # +static INSN_REGPARM void arm6D6(u32 opcode) { LDR_POSTINC(OFFSET_ROR, OP_LDRB, 16); } +// STR Rd, [Rn, -Rm, LSL #] +static INSN_REGPARM void arm700(u32 opcode) { STR_PREDEC(OFFSET_LSL, OP_STR, 32); } +// STR Rd, [Rn, -Rm, LSR #] +static INSN_REGPARM void arm702(u32 opcode) { STR_PREDEC(OFFSET_LSR, OP_STR, 32); } +// STR Rd, [Rn, -Rm, ASR #] +static INSN_REGPARM void arm704(u32 opcode) { STR_PREDEC(OFFSET_ASR, OP_STR, 32); } +// STR Rd, [Rn, -Rm, ROR #] +static INSN_REGPARM void arm706(u32 opcode) { STR_PREDEC(OFFSET_ROR, OP_STR, 32); } +// LDR Rd, [Rn, -Rm, LSL #] +static INSN_REGPARM void arm710(u32 opcode) { LDR_PREDEC(OFFSET_LSL, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, LSR #] +static INSN_REGPARM void arm712(u32 opcode) { LDR_PREDEC(OFFSET_LSR, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, ASR #] +static INSN_REGPARM void arm714(u32 opcode) { LDR_PREDEC(OFFSET_ASR, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, ROR #] +static INSN_REGPARM void arm716(u32 opcode) { LDR_PREDEC(OFFSET_ROR, OP_LDR, 32); } +// STR Rd, [Rn, -Rm, LSL #]! +static INSN_REGPARM void arm720(u32 opcode) { STR_PREDEC_WB(OFFSET_LSL, OP_STR, 32); } +// STR Rd, [Rn, -Rm, LSR #]! +static INSN_REGPARM void arm722(u32 opcode) { STR_PREDEC_WB(OFFSET_LSR, OP_STR, 32); } +// STR Rd, [Rn, -Rm, ASR #]! +static INSN_REGPARM void arm724(u32 opcode) { STR_PREDEC_WB(OFFSET_ASR, OP_STR, 32); } +// STR Rd, [Rn, -Rm, ROR #]! +static INSN_REGPARM void arm726(u32 opcode) { STR_PREDEC_WB(OFFSET_ROR, OP_STR, 32); } +// LDR Rd, [Rn, -Rm, LSL #]! +static INSN_REGPARM void arm730(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSL, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, LSR #]! +static INSN_REGPARM void arm732(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSR, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, ASR #]! +static INSN_REGPARM void arm734(u32 opcode) { LDR_PREDEC_WB(OFFSET_ASR, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, ROR #]! +static INSN_REGPARM void arm736(u32 opcode) { LDR_PREDEC_WB(OFFSET_ROR, OP_LDR, 32); } +// STRB Rd, [Rn, -Rm, LSL #] +static INSN_REGPARM void arm740(u32 opcode) { STR_PREDEC(OFFSET_LSL, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, LSR #] +static INSN_REGPARM void arm742(u32 opcode) { STR_PREDEC(OFFSET_LSR, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, ASR #] +static INSN_REGPARM void arm744(u32 opcode) { STR_PREDEC(OFFSET_ASR, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, ROR #] +static INSN_REGPARM void arm746(u32 opcode) { STR_PREDEC(OFFSET_ROR, OP_STRB, 16); } +// LDRB Rd, [Rn, -Rm, LSL #] +static INSN_REGPARM void arm750(u32 opcode) { LDR_PREDEC(OFFSET_LSL, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, LSR #] +static INSN_REGPARM void arm752(u32 opcode) { LDR_PREDEC(OFFSET_LSR, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, ASR #] +static INSN_REGPARM void arm754(u32 opcode) { LDR_PREDEC(OFFSET_ASR, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, ROR #] +static INSN_REGPARM void arm756(u32 opcode) { LDR_PREDEC(OFFSET_ROR, OP_LDRB, 16); } +// STRB Rd, [Rn, -Rm, LSL #]! +static INSN_REGPARM void arm760(u32 opcode) { STR_PREDEC_WB(OFFSET_LSL, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, LSR #]! +static INSN_REGPARM void arm762(u32 opcode) { STR_PREDEC_WB(OFFSET_LSR, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, ASR #]! +static INSN_REGPARM void arm764(u32 opcode) { STR_PREDEC_WB(OFFSET_ASR, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, ROR #]! +static INSN_REGPARM void arm766(u32 opcode) { STR_PREDEC_WB(OFFSET_ROR, OP_STRB, 16); } +// LDRB Rd, [Rn, -Rm, LSL #]! +static INSN_REGPARM void arm770(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSL, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, LSR #]! +static INSN_REGPARM void arm772(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSR, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, ASR #]! +static INSN_REGPARM void arm774(u32 opcode) { LDR_PREDEC_WB(OFFSET_ASR, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, ROR #]! +static INSN_REGPARM void arm776(u32 opcode) { LDR_PREDEC_WB(OFFSET_ROR, OP_LDRB, 16); } +// STR Rd, [Rn, Rm, LSL #] +static INSN_REGPARM void arm780(u32 opcode) { STR_PREINC(OFFSET_LSL, OP_STR, 32); } +// STR Rd, [Rn, Rm, LSR #] +static INSN_REGPARM void arm782(u32 opcode) { STR_PREINC(OFFSET_LSR, OP_STR, 32); } +// STR Rd, [Rn, Rm, ASR #] +static INSN_REGPARM void arm784(u32 opcode) { STR_PREINC(OFFSET_ASR, OP_STR, 32); } +// STR Rd, [Rn, Rm, ROR #] +static INSN_REGPARM void arm786(u32 opcode) { STR_PREINC(OFFSET_ROR, OP_STR, 32); } +// LDR Rd, [Rn, Rm, LSL #] +static INSN_REGPARM void arm790(u32 opcode) { LDR_PREINC(OFFSET_LSL, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, LSR #] +static INSN_REGPARM void arm792(u32 opcode) { LDR_PREINC(OFFSET_LSR, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, ASR #] +static INSN_REGPARM void arm794(u32 opcode) { LDR_PREINC(OFFSET_ASR, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, ROR #] +static INSN_REGPARM void arm796(u32 opcode) { LDR_PREINC(OFFSET_ROR, OP_LDR, 32); } +// STR Rd, [Rn, Rm, LSL #]! +static INSN_REGPARM void arm7A0(u32 opcode) { STR_PREINC_WB(OFFSET_LSL, OP_STR, 32); } +// STR Rd, [Rn, Rm, LSR #]! +static INSN_REGPARM void arm7A2(u32 opcode) { STR_PREINC_WB(OFFSET_LSR, OP_STR, 32); } +// STR Rd, [Rn, Rm, ASR #]! +static INSN_REGPARM void arm7A4(u32 opcode) { STR_PREINC_WB(OFFSET_ASR, OP_STR, 32); } +// STR Rd, [Rn, Rm, ROR #]! +static INSN_REGPARM void arm7A6(u32 opcode) { STR_PREINC_WB(OFFSET_ROR, OP_STR, 32); } +// LDR Rd, [Rn, Rm, LSL #]! +static INSN_REGPARM void arm7B0(u32 opcode) { LDR_PREINC_WB(OFFSET_LSL, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, LSR #]! +static INSN_REGPARM void arm7B2(u32 opcode) { LDR_PREINC_WB(OFFSET_LSR, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, ASR #]! +static INSN_REGPARM void arm7B4(u32 opcode) { LDR_PREINC_WB(OFFSET_ASR, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, ROR #]! +static INSN_REGPARM void arm7B6(u32 opcode) { LDR_PREINC_WB(OFFSET_ROR, OP_LDR, 32); } +// STRB Rd, [Rn, Rm, LSL #] +static INSN_REGPARM void arm7C0(u32 opcode) { STR_PREINC(OFFSET_LSL, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, LSR #] +static INSN_REGPARM void arm7C2(u32 opcode) { STR_PREINC(OFFSET_LSR, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, ASR #] +static INSN_REGPARM void arm7C4(u32 opcode) { STR_PREINC(OFFSET_ASR, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, ROR #] +static INSN_REGPARM void arm7C6(u32 opcode) { STR_PREINC(OFFSET_ROR, OP_STRB, 16); } +// LDRB Rd, [Rn, Rm, LSL #] +static INSN_REGPARM void arm7D0(u32 opcode) { LDR_PREINC(OFFSET_LSL, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, LSR #] +static INSN_REGPARM void arm7D2(u32 opcode) { LDR_PREINC(OFFSET_LSR, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, ASR #] +static INSN_REGPARM void arm7D4(u32 opcode) { LDR_PREINC(OFFSET_ASR, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, ROR #] +static INSN_REGPARM void arm7D6(u32 opcode) { LDR_PREINC(OFFSET_ROR, OP_LDRB, 16); } +// STRB Rd, [Rn, Rm, LSL #]! +static INSN_REGPARM void arm7E0(u32 opcode) { STR_PREINC_WB(OFFSET_LSL, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, LSR #]! +static INSN_REGPARM void arm7E2(u32 opcode) { STR_PREINC_WB(OFFSET_LSR, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, ASR #]! +static INSN_REGPARM void arm7E4(u32 opcode) { STR_PREINC_WB(OFFSET_ASR, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, ROR #]! +static INSN_REGPARM void arm7E6(u32 opcode) { STR_PREINC_WB(OFFSET_ROR, OP_STRB, 16); } +// LDRB Rd, [Rn, Rm, LSL #]! +static INSN_REGPARM void arm7F0(u32 opcode) { LDR_PREINC_WB(OFFSET_LSL, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, LSR #]! +static INSN_REGPARM void arm7F2(u32 opcode) { LDR_PREINC_WB(OFFSET_LSR, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, ASR #]! +static INSN_REGPARM void arm7F4(u32 opcode) { LDR_PREINC_WB(OFFSET_ASR, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, ROR #]! +static INSN_REGPARM void arm7F6(u32 opcode) { LDR_PREINC_WB(OFFSET_ROR, OP_LDRB, 16); } + +// STM/LDM //////////////////////////////////////////////////////////////// + +#define STM_REG(bit,num) \ + if (opcode & (1U<<(bit))) { \ + CPUWriteMemory(address, reg[(num)].I); \ + if (!count) { \ + clockTicks += 1 + dataTicksAccess32(address);\ + } else { \ + clockTicks += 1 + dataTicksAccessSeq32(address);\ + } \ + count++; \ + address += 4; \ + } +#define STMW_REG(bit,num) \ + if (opcode & (1U<<(bit))) { \ + CPUWriteMemory(address, reg[(num)].I); \ + if (!count) { \ + clockTicks += 1 + dataTicksAccess32(address);\ + } else { \ + clockTicks += 1 + dataTicksAccessSeq32(address);\ + } \ + reg[base].I = temp; \ + count++; \ + address += 4; \ + } +#define LDM_REG(bit,num) \ + if (opcode & (1U<<(bit))) { \ + reg[(num)].I = CPUReadMemory(address); \ + if (!count) { \ + clockTicks += 1 + dataTicksAccess32(address);\ + } else { \ + clockTicks += 1 + dataTicksAccessSeq32(address);\ + } \ + count++; \ + address += 4; \ + } +#define STM_LOW(STORE_REG) \ + STORE_REG(0, 0); \ + STORE_REG(1, 1); \ + STORE_REG(2, 2); \ + STORE_REG(3, 3); \ + STORE_REG(4, 4); \ + STORE_REG(5, 5); \ + STORE_REG(6, 6); \ + STORE_REG(7, 7); +#define STM_HIGH(STORE_REG) \ + STORE_REG(8, 8); \ + STORE_REG(9, 9); \ + STORE_REG(10, 10); \ + STORE_REG(11, 11); \ + STORE_REG(12, 12); \ + STORE_REG(13, 13); \ + STORE_REG(14, 14); +#define STM_HIGH_2(STORE_REG) \ + if (armMode == 0x11) { \ + STORE_REG(8, R8_FIQ); \ + STORE_REG(9, R9_FIQ); \ + STORE_REG(10, R10_FIQ); \ + STORE_REG(11, R11_FIQ); \ + STORE_REG(12, R12_FIQ); \ + } else { \ + STORE_REG(8, 8); \ + STORE_REG(9, 9); \ + STORE_REG(10, 10); \ + STORE_REG(11, 11); \ + STORE_REG(12, 12); \ + } \ + if (armMode != 0x10 && armMode != 0x1F) { \ + STORE_REG(13, R13_USR); \ + STORE_REG(14, R14_USR); \ + } else { \ + STORE_REG(13, 13); \ + STORE_REG(14, 14); \ + } +#define STM_PC \ + if (opcode & (1U<<15)) { \ + CPUWriteMemory(address, reg[15].I+4); \ + if (!count) { \ + clockTicks += 1 + dataTicksAccess32(address);\ + } else { \ + clockTicks += 1 + dataTicksAccessSeq32(address);\ + } \ + count++; \ + } +#define STMW_PC \ + if (opcode & (1U<<15)) { \ + CPUWriteMemory(address, reg[15].I+4); \ + if (!count) { \ + clockTicks += 1 + dataTicksAccess32(address);\ + } else { \ + clockTicks += 1 + dataTicksAccessSeq32(address);\ + } \ + reg[base].I = temp; \ + count++; \ + } +#define LDM_LOW \ + LDM_REG(0, 0); \ + LDM_REG(1, 1); \ + LDM_REG(2, 2); \ + LDM_REG(3, 3); \ + LDM_REG(4, 4); \ + LDM_REG(5, 5); \ + LDM_REG(6, 6); \ + LDM_REG(7, 7); +#define LDM_HIGH \ + LDM_REG(8, 8); \ + LDM_REG(9, 9); \ + LDM_REG(10, 10); \ + LDM_REG(11, 11); \ + LDM_REG(12, 12); \ + LDM_REG(13, 13); \ + LDM_REG(14, 14); +#define LDM_HIGH_2 \ + if (armMode == 0x11) { \ + LDM_REG(8, R8_FIQ); \ + LDM_REG(9, R9_FIQ); \ + LDM_REG(10, R10_FIQ); \ + LDM_REG(11, R11_FIQ); \ + LDM_REG(12, R12_FIQ); \ + } else { \ + LDM_REG(8, 8); \ + LDM_REG(9, 9); \ + LDM_REG(10, 10); \ + LDM_REG(11, 11); \ + LDM_REG(12, 12); \ + } \ + if (armMode != 0x10 && armMode != 0x1F) { \ + LDM_REG(13, R13_USR); \ + LDM_REG(14, R14_USR); \ + } else { \ + LDM_REG(13, 13); \ + LDM_REG(14, 14); \ + } +#define STM_ALL \ + STM_LOW(STM_REG); \ + STM_HIGH(STM_REG); \ + STM_PC; +#define STMW_ALL \ + STM_LOW(STMW_REG); \ + STM_HIGH(STMW_REG); \ + STMW_PC; +#define LDM_ALL \ + LDM_LOW; \ + LDM_HIGH; \ + if (opcode & (1U<<15)) { \ + reg[15].I = CPUReadMemory(address); \ + if (!count) { \ + clockTicks += 1 + dataTicksAccess32(address);\ + } else { \ + clockTicks += 1 + dataTicksAccessSeq32(address);\ + } \ + count++; \ + } \ + if (opcode & (1U<<15)) { \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + ARM_PREFETCH; \ + clockTicks += 1 + codeTicksAccessSeq32(armNextPC);\ + } +#define STM_ALL_2 \ + STM_LOW(STM_REG); \ + STM_HIGH_2(STM_REG); \ + STM_PC; +#define STMW_ALL_2 \ + STM_LOW(STMW_REG); \ + STM_HIGH_2(STMW_REG); \ + STMW_PC; +#define LDM_ALL_2 \ + LDM_LOW; \ + if (opcode & (1U<<15)) { \ + LDM_HIGH; \ + reg[15].I = CPUReadMemory(address); \ + if (!count) { \ + clockTicks += 1 + dataTicksAccess32(address); \ + } else { \ + clockTicks += 1 + dataTicksAccessSeq32(address); \ + } \ + count++; \ + } else { \ + LDM_HIGH_2; \ + } +#define LDM_ALL_2B \ + if (opcode & (1U<<15)) { \ + CPUSwitchMode(reg[17].I & 0x1F, false); \ + if (armState) { \ + armNextPC = reg[15].I & 0xFFFFFFFC; \ + reg[15].I = armNextPC + 4; \ + ARM_PREFETCH; \ + } else { \ + armNextPC = reg[15].I & 0xFFFFFFFE; \ + reg[15].I = armNextPC + 2; \ + THUMB_PREFETCH; \ + } \ + clockTicks += 1 + codeTicksAccessSeq32(armNextPC);\ + } + + +// STMDA Rn, {Rlist} +static INSN_REGPARM void arm800(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + int count = 0; + STM_ALL; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMDA Rn, {Rlist} +static INSN_REGPARM void arm810(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// STMDA Rn!, {Rlist} +static INSN_REGPARM void arm820(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp+4) & 0xFFFFFFFC; + int count = 0; + STMW_ALL; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMDA Rn!, {Rlist} +static INSN_REGPARM void arm830(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess32(armNextPC); + if (!(opcode & (1U << base))) + reg[base].I = temp; +} + +// STMDA Rn, {Rlist}^ +static INSN_REGPARM void arm840(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp+4) & 0xFFFFFFFC; + int count = 0; + STM_ALL_2; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMDA Rn, {Rlist}^ +static INSN_REGPARM void arm850(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// STMDA Rn!, {Rlist}^ +static INSN_REGPARM void arm860(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp+4) & 0xFFFFFFFC; + int count = 0; + STMW_ALL_2; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMDA Rn!, {Rlist}^ +static INSN_REGPARM void arm870(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + if (!(opcode & (1U << base))) + reg[base].I = temp; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// STMIA Rn, {Rlist} +static INSN_REGPARM void arm880(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + int count = 0; + STM_ALL; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMIA Rn, {Rlist} +static INSN_REGPARM void arm890(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// STMIA Rn!, {Rlist} +static INSN_REGPARM void arm8A0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + int count = 0; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]); + STMW_ALL; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMIA Rn!, {Rlist} +static INSN_REGPARM void arm8B0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = reg[base].I & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess32(armNextPC); + if (!(opcode & (1U << base))) + reg[base].I = temp; +} + +// STMIA Rn, {Rlist}^ +static INSN_REGPARM void arm8C0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + int count = 0; + STM_ALL_2; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMIA Rn, {Rlist}^ +static INSN_REGPARM void arm8D0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// STMIA Rn!, {Rlist}^ +static INSN_REGPARM void arm8E0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + int count = 0; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]); + STMW_ALL_2; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMIA Rn!, {Rlist}^ +static INSN_REGPARM void arm8F0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = reg[base].I & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + if (!(opcode & (1U << base))) + reg[base].I = temp; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// STMDB Rn, {Rlist} +static INSN_REGPARM void arm900(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + STM_ALL; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMDB Rn, {Rlist} +static INSN_REGPARM void arm910(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// STMDB Rn!, {Rlist} +static INSN_REGPARM void arm920(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + STMW_ALL; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMDB Rn!, {Rlist} +static INSN_REGPARM void arm930(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess32(armNextPC); + if (!(opcode & (1U << base))) + reg[base].I = temp; +} + +// STMDB Rn, {Rlist}^ +static INSN_REGPARM void arm940(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + STM_ALL_2; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMDB Rn, {Rlist}^ +static INSN_REGPARM void arm950(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// STMDB Rn!, {Rlist}^ +static INSN_REGPARM void arm960(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + STMW_ALL_2; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMDB Rn!, {Rlist}^ +static INSN_REGPARM void arm970(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + if (!(opcode & (1U << base))) + reg[base].I = temp; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// STMIB Rn, {Rlist} +static INSN_REGPARM void arm980(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + STM_ALL; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMIB Rn, {Rlist} +static INSN_REGPARM void arm990(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// STMIB Rn!, {Rlist} +static INSN_REGPARM void arm9A0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]); + STMW_ALL; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMIB Rn!, {Rlist} +static INSN_REGPARM void arm9B0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess32(armNextPC); + if (!(opcode & (1U << base))) + reg[base].I = temp; +} + +// STMIB Rn, {Rlist}^ +static INSN_REGPARM void arm9C0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + STM_ALL_2; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMIB Rn, {Rlist}^ +static INSN_REGPARM void arm9D0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// STMIB Rn!, {Rlist}^ +static INSN_REGPARM void arm9E0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]); + STMW_ALL_2; + clockTicks += 1 + codeTicksAccess32(armNextPC); +} + +// LDMIB Rn!, {Rlist}^ +static INSN_REGPARM void arm9F0(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + if (!(opcode & (1U << base))) + reg[base].I = temp; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess32(armNextPC); +} + +// B/BL/SWI and (unimplemented) coproc support //////////////////////////// + +// B +static INSN_REGPARM void armA00(u32 opcode) +{ + int offset = opcode & 0x00FFFFFF; + if (offset & 0x00800000) + offset |= 0xFF000000; // negative offset + reg[15].I += offset<<2; + armNextPC = reg[15].I; + reg[15].I += 4; + ARM_PREFETCH; + clockTicks = codeTicksAccessSeq32(armNextPC) + 1; + clockTicks += 2 + codeTicksAccess32(armNextPC) + + codeTicksAccessSeq32(armNextPC); + busPrefetchCount = 0; +} + +// BL +static INSN_REGPARM void armB00(u32 opcode) +{ + int offset = opcode & 0x00FFFFFF; + if (offset & 0x00800000) + offset |= 0xFF000000; // negative offset + reg[14].I = reg[15].I - 4; + reg[15].I += offset<<2; + armNextPC = reg[15].I; + reg[15].I += 4; + ARM_PREFETCH; + clockTicks = codeTicksAccessSeq32(armNextPC) + 1; + clockTicks += 2 + codeTicksAccess32(armNextPC) + + codeTicksAccessSeq32(armNextPC); + busPrefetchCount = 0; +} + + +#ifdef GP_SUPPORT +// MRC +static INSN_REGPARM void armE01(u32 opcode) +{ +} +#else + #define armE01 armUnknownInsn +#endif + + +// SWI +static INSN_REGPARM void armF00(u32 opcode) +{ + clockTicks = codeTicksAccessSeq32(armNextPC) + 1; + clockTicks += 2 + codeTicksAccess32(armNextPC) + + codeTicksAccessSeq32(armNextPC); + busPrefetchCount = 0; + CPUSoftwareInterrupt(opcode & 0x00FFFFFF); +} + +// Instruction table ////////////////////////////////////////////////////// + +typedef INSN_REGPARM void (*insnfunc_t)(u32 opcode); +#define REP16(insn) \ + insn,insn,insn,insn,insn,insn,insn,insn,\ + insn,insn,insn,insn,insn,insn,insn,insn +#define REP256(insn) \ + REP16(insn),REP16(insn),REP16(insn),REP16(insn),\ + REP16(insn),REP16(insn),REP16(insn),REP16(insn),\ + REP16(insn),REP16(insn),REP16(insn),REP16(insn),\ + REP16(insn),REP16(insn),REP16(insn),REP16(insn) +#define arm_UI armUnknownInsn +#ifdef BKPT_SUPPORT + #define arm_BP armBreakpoint +#else + #define arm_BP armUnknownInsn +#endif +static insnfunc_t armInsnTable[4096] = { + arm000,arm001,arm002,arm003,arm004,arm005,arm006,arm007, // 000 + arm000,arm009,arm002,arm00B,arm004,arm_UI,arm006,arm_UI, // 008 + arm010,arm011,arm012,arm013,arm014,arm015,arm016,arm017, // 010 + arm010,arm019,arm012,arm01B,arm014,arm01D,arm016,arm01F, // 018 + arm020,arm021,arm022,arm023,arm024,arm025,arm026,arm027, // 020 + arm020,arm029,arm022,arm_UI,arm024,arm_UI,arm026,arm_UI, // 028 + arm030,arm031,arm032,arm033,arm034,arm035,arm036,arm037, // 030 + arm030,arm039,arm032,arm_UI,arm034,arm01D,arm036,arm01F, // 038 + arm040,arm041,arm042,arm043,arm044,arm045,arm046,arm047, // 040 + arm040,arm_UI,arm042,arm04B,arm044,arm_UI,arm046,arm_UI, // 048 + arm050,arm051,arm052,arm053,arm054,arm055,arm056,arm057, // 050 + arm050,arm_UI,arm052,arm05B,arm054,arm05D,arm056,arm05F, // 058 + arm060,arm061,arm062,arm063,arm064,arm065,arm066,arm067, // 060 + arm060,arm_UI,arm062,arm_UI,arm064,arm_UI,arm066,arm_UI, // 068 + arm070,arm071,arm072,arm073,arm074,arm075,arm076,arm077, // 070 + arm070,arm_UI,arm072,arm_UI,arm074,arm05D,arm076,arm05F, // 078 + arm080,arm081,arm082,arm083,arm084,arm085,arm086,arm087, // 080 + arm080,arm089,arm082,arm08B,arm084,arm_UI,arm086,arm_UI, // 088 + arm090,arm091,arm092,arm093,arm094,arm095,arm096,arm097, // 090 + arm090,arm099,arm092,arm09B,arm094,arm09D,arm096,arm09F, // 098 + arm0A0,arm0A1,arm0A2,arm0A3,arm0A4,arm0A5,arm0A6,arm0A7, // 0A0 + arm0A0,arm0A9,arm0A2,arm_UI,arm0A4,arm_UI,arm0A6,arm_UI, // 0A8 + arm0B0,arm0B1,arm0B2,arm0B3,arm0B4,arm0B5,arm0B6,arm0B7, // 0B0 + arm0B0,arm0B9,arm0B2,arm_UI,arm0B4,arm09D,arm0B6,arm09F, // 0B8 + arm0C0,arm0C1,arm0C2,arm0C3,arm0C4,arm0C5,arm0C6,arm0C7, // 0C0 + arm0C0,arm0C9,arm0C2,arm0CB,arm0C4,arm_UI,arm0C6,arm_UI, // 0C8 + arm0D0,arm0D1,arm0D2,arm0D3,arm0D4,arm0D5,arm0D6,arm0D7, // 0D0 + arm0D0,arm0D9,arm0D2,arm0DB,arm0D4,arm0DD,arm0D6,arm0DF, // 0D8 + arm0E0,arm0E1,arm0E2,arm0E3,arm0E4,arm0E5,arm0E6,arm0E7, // 0E0 + arm0E0,arm0E9,arm0E2,arm_UI,arm0E4,arm_UI,arm0E6,arm_UI, // 0E8 + arm0F0,arm0F1,arm0F2,arm0F3,arm0F4,arm0F5,arm0F6,arm0F7, // 0F0 + arm0F0,arm0F9,arm0F2,arm_UI,arm0F4,arm0DD,arm0F6,arm0DF, // 0F8 + + arm100,arm_UI,arm_UI,arm_UI,arm_UI,arm_UI,arm_UI,arm_UI, // 100 + arm_UI,arm109,arm_UI,arm10B,arm_UI,arm_UI,arm_UI,arm_UI, // 108 + arm110,arm111,arm112,arm113,arm114,arm115,arm116,arm117, // 110 + arm110,arm_UI,arm112,arm11B,arm114,arm11D,arm116,arm11F, // 118 + arm120,arm121,arm_UI,arm_UI,arm_UI,arm_UI,arm_UI,arm_BP, // 120 + arm_UI,arm_UI,arm_UI,arm12B,arm_UI,arm_UI,arm_UI,arm_UI, // 128 + arm130,arm131,arm132,arm133,arm134,arm135,arm136,arm137, // 130 + arm130,arm_UI,arm132,arm13B,arm134,arm13D,arm136,arm13F, // 138 + arm140,arm_UI,arm_UI,arm_UI,arm_UI,arm_UI,arm_UI,arm_UI, // 140 + arm_UI,arm149,arm_UI,arm14B,arm_UI,arm_UI,arm_UI,arm_UI, // 148 + arm150,arm151,arm152,arm153,arm154,arm155,arm156,arm157, // 150 + arm150,arm_UI,arm152,arm15B,arm154,arm15D,arm156,arm15F, // 158 + arm160,arm_UI,arm_UI,arm_UI,arm_UI,arm_UI,arm_UI,arm_UI, // 160 + arm_UI,arm_UI,arm_UI,arm16B,arm_UI,arm_UI,arm_UI,arm_UI, // 168 + arm170,arm171,arm172,arm173,arm174,arm175,arm176,arm177, // 170 + arm170,arm_UI,arm172,arm17B,arm174,arm17D,arm176,arm17F, // 178 + arm180,arm181,arm182,arm183,arm184,arm185,arm186,arm187, // 180 + arm180,arm_UI,arm182,arm18B,arm184,arm_UI,arm186,arm_UI, // 188 + arm190,arm191,arm192,arm193,arm194,arm195,arm196,arm197, // 190 + arm190,arm_UI,arm192,arm19B,arm194,arm19D,arm196,arm19F, // 198 + arm1A0,arm1A1,arm1A2,arm1A3,arm1A4,arm1A5,arm1A6,arm1A7, // 1A0 + arm1A0,arm_UI,arm1A2,arm1AB,arm1A4,arm_UI,arm1A6,arm_UI, // 1A8 + arm1B0,arm1B1,arm1B2,arm1B3,arm1B4,arm1B5,arm1B6,arm1B7, // 1B0 + arm1B0,arm_UI,arm1B2,arm1BB,arm1B4,arm1BD,arm1B6,arm1BF, // 1B8 + arm1C0,arm1C1,arm1C2,arm1C3,arm1C4,arm1C5,arm1C6,arm1C7, // 1C0 + arm1C0,arm_UI,arm1C2,arm1CB,arm1C4,arm_UI,arm1C6,arm_UI, // 1C8 + arm1D0,arm1D1,arm1D2,arm1D3,arm1D4,arm1D5,arm1D6,arm1D7, // 1D0 + arm1D0,arm_UI,arm1D2,arm1DB,arm1D4,arm1DD,arm1D6,arm1DF, // 1D8 + arm1E0,arm1E1,arm1E2,arm1E3,arm1E4,arm1E5,arm1E6,arm1E7, // 1E0 + arm1E0,arm_UI,arm1E2,arm1EB,arm1E4,arm_UI,arm1E6,arm_UI, // 1E8 + arm1F0,arm1F1,arm1F2,arm1F3,arm1F4,arm1F5,arm1F6,arm1F7, // 1F0 + arm1F0,arm_UI,arm1F2,arm1FB,arm1F4,arm1FD,arm1F6,arm1FF, // 1F8 + + REP16(arm200),REP16(arm210),REP16(arm220),REP16(arm230), // 200 + REP16(arm240),REP16(arm250),REP16(arm260),REP16(arm270), // 240 + REP16(arm280),REP16(arm290),REP16(arm2A0),REP16(arm2B0), // 280 + REP16(arm2C0),REP16(arm2D0),REP16(arm2E0),REP16(arm2F0), // 2C0 + REP16(arm_UI),REP16(arm310),REP16(arm320),REP16(arm330), // 300 + REP16(arm_UI),REP16(arm350),REP16(arm360),REP16(arm370), // 340 + REP16(arm380),REP16(arm390),REP16(arm3A0),REP16(arm3B0), // 380 + REP16(arm3C0),REP16(arm3D0),REP16(arm3E0),REP16(arm3F0), // 3C0 + + REP16(arm400),REP16(arm410),REP16(arm400),REP16(arm410), // 400 + REP16(arm440),REP16(arm450),REP16(arm440),REP16(arm450), // 440 + REP16(arm480),REP16(arm490),REP16(arm480),REP16(arm490), // 480 + REP16(arm4C0),REP16(arm4D0),REP16(arm4C0),REP16(arm4D0), // 4C0 + REP16(arm500),REP16(arm510),REP16(arm520),REP16(arm530), // 500 + REP16(arm540),REP16(arm550),REP16(arm560),REP16(arm570), // 540 + REP16(arm580),REP16(arm590),REP16(arm5A0),REP16(arm5B0), // 580 + REP16(arm5C0),REP16(arm5D0),REP16(arm5E0),REP16(arm5F0), // 5C0 + + arm600,arm_UI,arm602,arm_UI,arm604,arm_UI,arm606,arm_UI, // 600 + arm600,arm_UI,arm602,arm_UI,arm604,arm_UI,arm606,arm_UI, // 608 + arm610,arm_UI,arm612,arm_UI,arm614,arm_UI,arm616,arm_UI, // 610 + arm610,arm_UI,arm612,arm_UI,arm614,arm_UI,arm616,arm_UI, // 618 + arm600,arm_UI,arm602,arm_UI,arm604,arm_UI,arm606,arm_UI, // 620 + arm600,arm_UI,arm602,arm_UI,arm604,arm_UI,arm606,arm_UI, // 628 + arm610,arm_UI,arm612,arm_UI,arm614,arm_UI,arm616,arm_UI, // 630 + arm610,arm_UI,arm612,arm_UI,arm614,arm_UI,arm616,arm_UI, // 638 + arm640,arm_UI,arm642,arm_UI,arm644,arm_UI,arm646,arm_UI, // 640 + arm640,arm_UI,arm642,arm_UI,arm644,arm_UI,arm646,arm_UI, // 648 + arm650,arm_UI,arm652,arm_UI,arm654,arm_UI,arm656,arm_UI, // 650 + arm650,arm_UI,arm652,arm_UI,arm654,arm_UI,arm656,arm_UI, // 658 + arm640,arm_UI,arm642,arm_UI,arm644,arm_UI,arm646,arm_UI, // 660 + arm640,arm_UI,arm642,arm_UI,arm644,arm_UI,arm646,arm_UI, // 668 + arm650,arm_UI,arm652,arm_UI,arm654,arm_UI,arm656,arm_UI, // 670 + arm650,arm_UI,arm652,arm_UI,arm654,arm_UI,arm656,arm_UI, // 678 + arm680,arm_UI,arm682,arm_UI,arm684,arm_UI,arm686,arm_UI, // 680 + arm680,arm_UI,arm682,arm_UI,arm684,arm_UI,arm686,arm_UI, // 688 + arm690,arm_UI,arm692,arm_UI,arm694,arm_UI,arm696,arm_UI, // 690 + arm690,arm_UI,arm692,arm_UI,arm694,arm_UI,arm696,arm_UI, // 698 + arm680,arm_UI,arm682,arm_UI,arm684,arm_UI,arm686,arm_UI, // 6A0 + arm680,arm_UI,arm682,arm_UI,arm684,arm_UI,arm686,arm_UI, // 6A8 + arm690,arm_UI,arm692,arm_UI,arm694,arm_UI,arm696,arm_UI, // 6B0 + arm690,arm_UI,arm692,arm_UI,arm694,arm_UI,arm696,arm_UI, // 6B8 + arm6C0,arm_UI,arm6C2,arm_UI,arm6C4,arm_UI,arm6C6,arm_UI, // 6C0 + arm6C0,arm_UI,arm6C2,arm_UI,arm6C4,arm_UI,arm6C6,arm_UI, // 6C8 + arm6D0,arm_UI,arm6D2,arm_UI,arm6D4,arm_UI,arm6D6,arm_UI, // 6D0 + arm6D0,arm_UI,arm6D2,arm_UI,arm6D4,arm_UI,arm6D6,arm_UI, // 6D8 + arm6C0,arm_UI,arm6C2,arm_UI,arm6C4,arm_UI,arm6C6,arm_UI, // 6E0 + arm6C0,arm_UI,arm6C2,arm_UI,arm6C4,arm_UI,arm6C6,arm_UI, // 6E8 + arm6D0,arm_UI,arm6D2,arm_UI,arm6D4,arm_UI,arm6D6,arm_UI, // 6F0 + arm6D0,arm_UI,arm6D2,arm_UI,arm6D4,arm_UI,arm6D6,arm_UI, // 6F8 + + arm700,arm_UI,arm702,arm_UI,arm704,arm_UI,arm706,arm_UI, // 700 + arm700,arm_UI,arm702,arm_UI,arm704,arm_UI,arm706,arm_UI, // 708 + arm710,arm_UI,arm712,arm_UI,arm714,arm_UI,arm716,arm_UI, // 710 + arm710,arm_UI,arm712,arm_UI,arm714,arm_UI,arm716,arm_UI, // 718 + arm720,arm_UI,arm722,arm_UI,arm724,arm_UI,arm726,arm_UI, // 720 + arm720,arm_UI,arm722,arm_UI,arm724,arm_UI,arm726,arm_UI, // 728 + arm730,arm_UI,arm732,arm_UI,arm734,arm_UI,arm736,arm_UI, // 730 + arm730,arm_UI,arm732,arm_UI,arm734,arm_UI,arm736,arm_UI, // 738 + arm740,arm_UI,arm742,arm_UI,arm744,arm_UI,arm746,arm_UI, // 740 + arm740,arm_UI,arm742,arm_UI,arm744,arm_UI,arm746,arm_UI, // 748 + arm750,arm_UI,arm752,arm_UI,arm754,arm_UI,arm756,arm_UI, // 750 + arm750,arm_UI,arm752,arm_UI,arm754,arm_UI,arm756,arm_UI, // 758 + arm760,arm_UI,arm762,arm_UI,arm764,arm_UI,arm766,arm_UI, // 760 + arm760,arm_UI,arm762,arm_UI,arm764,arm_UI,arm766,arm_UI, // 768 + arm770,arm_UI,arm772,arm_UI,arm774,arm_UI,arm776,arm_UI, // 770 + arm770,arm_UI,arm772,arm_UI,arm774,arm_UI,arm776,arm_UI, // 778 + arm780,arm_UI,arm782,arm_UI,arm784,arm_UI,arm786,arm_UI, // 780 + arm780,arm_UI,arm782,arm_UI,arm784,arm_UI,arm786,arm_UI, // 788 + arm790,arm_UI,arm792,arm_UI,arm794,arm_UI,arm796,arm_UI, // 790 + arm790,arm_UI,arm792,arm_UI,arm794,arm_UI,arm796,arm_UI, // 798 + arm7A0,arm_UI,arm7A2,arm_UI,arm7A4,arm_UI,arm7A6,arm_UI, // 7A0 + arm7A0,arm_UI,arm7A2,arm_UI,arm7A4,arm_UI,arm7A6,arm_UI, // 7A8 + arm7B0,arm_UI,arm7B2,arm_UI,arm7B4,arm_UI,arm7B6,arm_UI, // 7B0 + arm7B0,arm_UI,arm7B2,arm_UI,arm7B4,arm_UI,arm7B6,arm_UI, // 7B8 + arm7C0,arm_UI,arm7C2,arm_UI,arm7C4,arm_UI,arm7C6,arm_UI, // 7C0 + arm7C0,arm_UI,arm7C2,arm_UI,arm7C4,arm_UI,arm7C6,arm_UI, // 7C8 + arm7D0,arm_UI,arm7D2,arm_UI,arm7D4,arm_UI,arm7D6,arm_UI, // 7D0 + arm7D0,arm_UI,arm7D2,arm_UI,arm7D4,arm_UI,arm7D6,arm_UI, // 7D8 + arm7E0,arm_UI,arm7E2,arm_UI,arm7E4,arm_UI,arm7E6,arm_UI, // 7E0 + arm7E0,arm_UI,arm7E2,arm_UI,arm7E4,arm_UI,arm7E6,arm_UI, // 7E8 + arm7F0,arm_UI,arm7F2,arm_UI,arm7F4,arm_UI,arm7F6,arm_UI, // 7F0 + arm7F0,arm_UI,arm7F2,arm_UI,arm7F4,arm_UI,arm7F6,arm_BP, // 7F8 + + REP16(arm800),REP16(arm810),REP16(arm820),REP16(arm830), // 800 + REP16(arm840),REP16(arm850),REP16(arm860),REP16(arm870), // 840 + REP16(arm880),REP16(arm890),REP16(arm8A0),REP16(arm8B0), // 880 + REP16(arm8C0),REP16(arm8D0),REP16(arm8E0),REP16(arm8F0), // 8C0 + REP16(arm900),REP16(arm910),REP16(arm920),REP16(arm930), // 900 + REP16(arm940),REP16(arm950),REP16(arm960),REP16(arm970), // 940 + REP16(arm980),REP16(arm990),REP16(arm9A0),REP16(arm9B0), // 980 + REP16(arm9C0),REP16(arm9D0),REP16(arm9E0),REP16(arm9F0), // 9C0 + + REP256(armA00), // A00 + REP256(armB00), // B00 + REP256(arm_UI), // C00 + REP256(arm_UI), // D00 + + arm_UI,armE01,arm_UI,armE01,arm_UI,armE01,arm_UI,armE01, // E00 + arm_UI,armE01,arm_UI,armE01,arm_UI,armE01,arm_UI,armE01, // E08 + arm_UI,armE01,arm_UI,armE01,arm_UI,armE01,arm_UI,armE01, // E10 + arm_UI,armE01,arm_UI,armE01,arm_UI,armE01,arm_UI,armE01, // E18 + REP16(arm_UI), // E20 + REP16(arm_UI), // E30 + REP16(arm_UI),REP16(arm_UI),REP16(arm_UI),REP16(arm_UI), // E40 + REP16(arm_UI),REP16(arm_UI),REP16(arm_UI),REP16(arm_UI), // E80 + REP16(arm_UI),REP16(arm_UI),REP16(arm_UI),REP16(arm_UI), // EC0 + + REP256(armF00), // F00 +}; + +// Wrapper routine (execution loop) /////////////////////////////////////// + +#include +static void tester(void) { + static int ran=0;if(ran)return;ran=1; + FILE*f=fopen("p:\\timing.txt","w");if(!f)return; + for (int op=/*0*/9; op> 28; + bool cond_res = true; + if (UNLIKELY(cond != 0x0E)) { // most opcodes are AL (always) + switch(cond) { + case 0x00: // EQ + cond_res = Z_FLAG; + break; + case 0x01: // NE + cond_res = !Z_FLAG; + break; + case 0x02: // CS + cond_res = C_FLAG; + break; + case 0x03: // CC + cond_res = !C_FLAG; + break; + case 0x04: // MI + cond_res = N_FLAG; + break; + case 0x05: // PL + cond_res = !N_FLAG; + break; + case 0x06: // VS + cond_res = V_FLAG; + break; + case 0x07: // VC + cond_res = !V_FLAG; + break; + case 0x08: // HI + cond_res = C_FLAG && !Z_FLAG; + break; + case 0x09: // LS + cond_res = !C_FLAG || Z_FLAG; + break; + case 0x0A: // GE + cond_res = N_FLAG == V_FLAG; + break; + case 0x0B: // LT + cond_res = N_FLAG != V_FLAG; + break; + case 0x0C: // GT + cond_res = !Z_FLAG &&(N_FLAG == V_FLAG); + break; + case 0x0D: // LE + cond_res = Z_FLAG || (N_FLAG != V_FLAG); + break; + case 0x0E: // AL (impossible, checked above) + cond_res = true; + break; + case 0x0F: + default: + // ??? + cond_res = false; + break; + } + } + + if (cond_res) + (*armInsnTable[((opcode>>16)&0xFF0) | ((opcode>>4)&0x0F)])(opcode); +#ifdef INSN_COUNTER + count(opcode, cond_res); +#endif + if (clockTicks < 0) + return 0; + if (clockTicks == 0) + clockTicks = 1 + codeTicksAccessSeq32(oldArmNextPC); + cpuTotalTicks += clockTicks; + + } while (cpuTotalTicks +#include +#include +#include +#include + +#include "GBA.h" +#include "GBAcpu.h" +#include "GBAinline.h" +#include "../Globals.h" +#include "../EEprom.h" +#include "../Flash.h" +#include "../Sound.h" +#include "../Sram.h" +#include "../bios.h" +#include "../Cheats.h" +#include "../NLS.h" +#include "../elf.h" +#include "../Util.h" +#include "../Port.h" +#include "agbprint.h" +#ifdef PROFILING +#include "prof/prof.h" +#endif + +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + +/////////////////////////////////////////////////////////////////////////// + +static int clockTicks; + +static INSN_REGPARM void thumbUnknownInsn(u32 opcode) +{ +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_UNDEFINED) + log("Undefined THUMB instruction %04x at %08x\n", opcode, armNextPC-2); +#endif + CPUUndefinedException(); +} + +#ifdef BKPT_SUPPORT +static INSN_REGPARM void thumbBreakpoint(u32 opcode) +{ + extern void (*dbgSignal)(int,int); + reg[15].I -= 2; + armNextPC -= 2; + dbgSignal(5, opcode & 255); + clockTicks = -1; +} +#endif + +// Common macros ////////////////////////////////////////////////////////// + +#ifdef BKPT_SUPPORT +# define THUMB_CONSOLE_OUTPUT(a,b) do { \ + if ((opcode == 0x4000) && (reg[0].I == 0xC0DED00D)) { \ + extern void (*dbgOutput)(const char *, u32); \ + dbgOutput((a), (b)); \ + } \ +} while (0) +# define UPDATE_OLDREG do { \ + if (debugger_last) { \ + snprintf(oldbuffer, sizeof(oldbuffer), "%08X", \ + armState ? reg[15].I - 4 : reg[15].I - 2); \ + int i; \ + for (i = 0; i < 18; i++) { \ + oldreg[i] = reg[i].I; \ + } \ + } \ +} while (0) +#else +# define THUMB_CONSOLE_OUTPUT(a,b) +# define UPDATE_OLDREG +#endif + +#define NEG(i) ((i) >> 31) +#define POS(i) ((~(i)) >> 31) + +#ifndef C_CORE +#ifdef __GNUC__ +#ifdef __POWERPC__ + #define ADD_RD_RS_RN(N) \ + { \ + register int Flags; \ + register int Result; \ + asm volatile("addco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[source].I), \ + "r" (reg[N].I) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define ADD_RD_RS_O3(N) \ + { \ + register int Flags; \ + register int Result; \ + asm volatile("addco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[source].I), \ + "r" (N) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define ADD_RD_RS_O3_0 ADD_RD_RS_O3 + #define ADD_RN_O8(d) \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("addco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[(d)].I), \ + "r" (opcode & 255) \ + ); \ + reg[(d)].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define CMN_RD_RS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("addco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[dest].I), \ + "r" (value) \ + ); \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define ADC_RD_RS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("mtspr xer, %4\n" \ + "addeo. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[dest].I), \ + "r" (value), \ + "r" (C_FLAG << 29) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define SUB_RD_RS_RN(N) \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[source].I), \ + "r" (reg[N].I) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define SUB_RD_RS_O3(N) \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[source].I), \ + "r" (N) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define SUB_RD_RS_O3_0 SUB_RD_RS_O3 + #define SUB_RN_O8(d) \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[(d)].I), \ + "r" (opcode & 255) \ + ); \ + reg[(d)].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define CMP_RN_O8(d) \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[(d)].I), \ + "r" (opcode & 255) \ + ); \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define SBC_RD_RS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("mtspr xer, %4\n" \ + "subfeo. %0, %3, %2\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[dest].I), \ + "r" (value), \ + "r" (C_FLAG << 29) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define NEG_RD_RS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subfco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[source].I), \ + "r" (0) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define CMP_RD_RS \ + {\ + register int Flags; \ + register int Result; \ + asm volatile("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[dest].I), \ + "r" (value) \ + ); \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } +#else + #define ADD_RN_O8(d) \ + asm ("andl $0xFF, %%eax;"\ + "addl %%eax, %0;"\ + "setsb _N_FLAG;"\ + "setzb _Z_FLAG;"\ + "setcb _C_FLAG;"\ + "setob _V_FLAG;"\ + : "=m" (reg[(d)].I)); + #define CMN_RD_RS \ + asm ("add %0, %1;"\ + "setsb _N_FLAG;"\ + "setzb _Z_FLAG;"\ + "setcb _C_FLAG;"\ + "setob _V_FLAG;"\ + : \ + : "r" (value), "r" (reg[dest].I):"1"); + #define ADC_RD_RS \ + asm ("bt $0, _C_FLAG;"\ + "adc %1, %%ebx;"\ + "setsb _N_FLAG;"\ + "setzb _Z_FLAG;"\ + "setcb _C_FLAG;"\ + "setob _V_FLAG;"\ + : "=b" (reg[dest].I)\ + : "r" (value), "b" (reg[dest].I)); + #define SUB_RN_O8(d) \ + asm ("andl $0xFF, %%eax;"\ + "subl %%eax, %0;"\ + "setsb _N_FLAG;"\ + "setzb _Z_FLAG;"\ + "setncb _C_FLAG;"\ + "setob _V_FLAG;"\ + : "=m" (reg[(d)].I)); + #define MOV_RN_O8(d) \ + asm ("andl $0xFF, %%eax;"\ + "movb $0, _N_FLAG;"\ + "movl %%eax, %0;"\ + "setzb _Z_FLAG;"\ + : "=m" (reg[(d)].I)); + #define CMP_RN_O8(d) \ + asm ("andl $0xFF, %%eax;"\ + "cmpl %%eax, %0;"\ + "setsb _N_FLAG;"\ + "setzb _Z_FLAG;"\ + "setncb _C_FLAG;"\ + "setob _V_FLAG;"\ + : \ + : "m" (reg[(d)].I)); + #define SBC_RD_RS \ + asm volatile ("bt $0, _C_FLAG;"\ + "cmc;"\ + "sbb %1, %%ebx;"\ + "setsb _N_FLAG;"\ + "setzb _Z_FLAG;"\ + "setncb _C_FLAG;"\ + "setob _V_FLAG;"\ + : "=b" (reg[dest].I)\ + : "r" (value), "b" (reg[dest].I) : "cc", "memory"); + #define LSL_RD_RS \ + asm ("shl %%cl, %%eax;"\ + "setcb _C_FLAG;"\ + : "=a" (value)\ + : "a" (reg[dest].I), "c" (value)); + #define LSR_RD_RS \ + asm ("shr %%cl, %%eax;"\ + "setcb _C_FLAG;"\ + : "=a" (value)\ + : "a" (reg[dest].I), "c" (value)); + #define ASR_RD_RS \ + asm ("sar %%cl, %%eax;"\ + "setcb _C_FLAG;"\ + : "=a" (value)\ + : "a" (reg[dest].I), "c" (value)); + #define ROR_RD_RS \ + asm ("ror %%cl, %%eax;"\ + "setcb _C_FLAG;"\ + : "=a" (value)\ + : "a" (reg[dest].I), "c" (value)); + #define NEG_RD_RS \ + asm ("neg %%ebx;"\ + "setsb _N_FLAG;"\ + "setzb _Z_FLAG;"\ + "setncb _C_FLAG;"\ + "setob _V_FLAG;"\ + : "=b" (reg[dest].I)\ + : "b" (reg[source].I)); + #define CMP_RD_RS \ + asm ("sub %0, %1;"\ + "setsb _N_FLAG;"\ + "setzb _Z_FLAG;"\ + "setncb _C_FLAG;"\ + "setob _V_FLAG;"\ + : \ + : "r" (value), "r" (reg[dest].I):"1"); + #define IMM5_INSN(OP,N) \ + asm("movl %%eax,%%ecx;" \ + "shrl $1,%%eax;" \ + "andl $7,%%ecx;" \ + "andl $0x1C,%%eax;" \ + "movl _reg(%%eax),%%edx;" \ + OP \ + "setsb _N_FLAG;" \ + "setzb _Z_FLAG;" \ + "movl %%edx,_reg(,%%ecx,4);" \ + : : "i" (N)) + #define IMM5_INSN_0(OP) \ + asm("movl %%eax,%%ecx;" \ + "shrl $1,%%eax;" \ + "andl $7,%%ecx;" \ + "andl $0x1C,%%eax;" \ + "movl _reg(%%eax),%%edx;" \ + OP \ + "setsb _N_FLAG;" \ + "setzb _Z_FLAG;" \ + "movl %%edx,_reg(,%%ecx,4);" \ + : : ) + #define IMM5_LSL \ + "shll %0,%%edx;"\ + "setcb _C_FLAG;" + #define IMM5_LSL_0 \ + "testl %%edx,%%edx;" + #define IMM5_LSR \ + "shrl %0,%%edx;"\ + "setcb _C_FLAG;" + #define IMM5_LSR_0 \ + "testl %%edx,%%edx;"\ + "setsb _C_FLAG;"\ + "xorl %%edx,%%edx;" + #define IMM5_ASR \ + "sarl %0,%%edx;"\ + "setcb _C_FLAG;" + #define IMM5_ASR_0 \ + "sarl $31,%%edx;"\ + "setsb _C_FLAG;" + #define THREEARG_INSN(OP,N) \ + asm("movl %%eax,%%edx;" \ + "shrl $1,%%edx;" \ + "andl $0x1C,%%edx;" \ + "andl $7,%%eax;" \ + "movl _reg(%%edx),%%ecx;" \ + OP(N) \ + "setsb _N_FLAG;" \ + "setzb _Z_FLAG;" \ + "movl %%ecx,_reg(,%%eax,4)"::) + #define ADD_RD_RS_RN(N) \ + "add (_reg+"#N"*4),%%ecx;" \ + "setcb _C_FLAG;" \ + "setob _V_FLAG;" + #define ADD_RD_RS_O3(N) \ + "add $"#N",%%ecx;" \ + "setcb _C_FLAG;" \ + "setob _V_FLAG;" + #define ADD_RD_RS_O3_0(N) \ + "movb $0,_C_FLAG;" \ + "add $0,%%ecx;" \ + "movb $0,_V_FLAG;" + #define SUB_RD_RS_RN(N) \ + "sub (_reg+"#N"*4),%%ecx;" \ + "setncb _C_FLAG;" \ + "setob _V_FLAG;" + #define SUB_RD_RS_O3(N) \ + "sub $"#N",%%ecx;" \ + "setncb _C_FLAG;" \ + "setob _V_FLAG;" + #define SUB_RD_RS_O3_0(N) \ + "movb $1,_C_FLAG;" \ + "sub $0,%%ecx;" \ + "movb $0,_V_FLAG;" +#endif +#else // !__GNUC__ + #define ADD_RD_RS_RN(N) \ + {\ + __asm mov eax, source\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm add ebx, dword ptr [OFFSET reg+4*N]\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + #define ADD_RD_RS_O3(N) \ + {\ + __asm mov eax, source\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm add ebx, N\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + #define ADD_RD_RS_O3_0 \ + {\ + __asm mov eax, source\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm add ebx, 0\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm mov byte ptr C_FLAG, 0\ + __asm mov byte ptr V_FLAG, 0\ + } + #define ADD_RN_O8(d) \ + {\ + __asm mov ebx, opcode\ + __asm and ebx, 255\ + __asm add dword ptr [OFFSET reg+4*(d)], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + #define CMN_RD_RS \ + {\ + __asm mov eax, dest\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm add ebx, value\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + #define ADC_RD_RS \ + {\ + __asm mov ebx, dest\ + __asm mov ebx, dword ptr [OFFSET reg+4*ebx]\ + __asm bt word ptr C_FLAG, 0\ + __asm adc ebx, value\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + #define SUB_RD_RS_RN(N) \ + {\ + __asm mov eax, source\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm sub ebx, dword ptr [OFFSET reg+4*N]\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + #define SUB_RD_RS_O3(N) \ + {\ + __asm mov eax, source\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm sub ebx, N\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + #define SUB_RD_RS_O3_0 \ + {\ + __asm mov eax, source\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm sub ebx, 0\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm mov byte ptr C_FLAG, 1\ + __asm mov byte ptr V_FLAG, 0\ + } + #define SUB_RN_O8(d) \ + {\ + __asm mov ebx, opcode\ + __asm and ebx, 255\ + __asm sub dword ptr [OFFSET reg + 4*(d)], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + #define MOV_RN_O8(d) \ + {\ + __asm mov eax, opcode\ + __asm and eax, 255\ + __asm mov dword ptr [OFFSET reg+4*(d)], eax\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + } + #define CMP_RN_O8(d) \ + {\ + __asm mov eax, dword ptr [OFFSET reg+4*(d)]\ + __asm mov ebx, opcode\ + __asm and ebx, 255\ + __asm sub eax, ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + #define SBC_RD_RS \ + {\ + __asm mov ebx, dest\ + __asm mov ebx, dword ptr [OFFSET reg + 4*ebx]\ + __asm mov eax, value\ + __asm bt word ptr C_FLAG, 0\ + __asm cmc\ + __asm sbb ebx, eax\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg + 4*eax], ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + #define LSL_RD_RM_I5 \ + {\ + __asm mov eax, source\ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\ + __asm mov cl, byte ptr shift\ + __asm shl eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_FLAG\ + } + #define LSL_RD_RS \ + {\ + __asm mov eax, dest\ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\ + __asm mov cl, byte ptr value\ + __asm shl eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_FLAG\ + } + #define LSR_RD_RM_I5 \ + {\ + __asm mov eax, source\ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\ + __asm mov cl, byte ptr shift\ + __asm shr eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_FLAG\ + } + #define LSR_RD_RS \ + {\ + __asm mov eax, dest\ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\ + __asm mov cl, byte ptr value\ + __asm shr eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_FLAG\ + } + #define ASR_RD_RM_I5 \ + {\ + __asm mov eax, source\ + __asm mov eax, dword ptr [OFFSET reg + 4*eax]\ + __asm mov cl, byte ptr shift\ + __asm sar eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_FLAG\ + } + #define ASR_RD_RS \ + {\ + __asm mov eax, dest\ + __asm mov eax, dword ptr [OFFSET reg + 4*eax]\ + __asm mov cl, byte ptr value\ + __asm sar eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_FLAG\ + } + #define ROR_RD_RS \ + {\ + __asm mov eax, dest\ + __asm mov eax, dword ptr [OFFSET reg + 4*eax]\ + __asm mov cl, byte ptr value\ + __asm ror eax, cl\ + __asm mov value, eax\ + __asm setc byte ptr C_FLAG\ + } + #define NEG_RD_RS \ + {\ + __asm mov ebx, source\ + __asm mov ebx, dword ptr [OFFSET reg+4*ebx]\ + __asm neg ebx\ + __asm mov eax, dest\ + __asm mov dword ptr [OFFSET reg+4*eax],ebx\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } + #define CMP_RD_RS \ + {\ + __asm mov eax, dest\ + __asm mov ebx, dword ptr [OFFSET reg+4*eax]\ + __asm sub ebx, value\ + __asm sets byte ptr N_FLAG\ + __asm setz byte ptr Z_FLAG\ + __asm setnc byte ptr C_FLAG\ + __asm seto byte ptr V_FLAG\ + } +#endif +#endif + +// C core +#ifndef ADDCARRY + #define ADDCARRY(a, b, c) \ + C_FLAG = ((NEG(a) & NEG(b)) |\ + (NEG(a) & POS(c)) |\ + (NEG(b) & POS(c))) ? true : false; +#endif +#ifndef ADDOVERFLOW + #define ADDOVERFLOW(a, b, c) \ + V_FLAG = ((NEG(a) & NEG(b) & POS(c)) |\ + (POS(a) & POS(b) & NEG(c))) ? true : false; +#endif +#ifndef SUBCARRY + #define SUBCARRY(a, b, c) \ + C_FLAG = ((NEG(a) & POS(b)) |\ + (NEG(a) & POS(c)) |\ + (POS(b) & POS(c))) ? true : false; +#endif +#ifndef SUBOVERFLOW + #define SUBOVERFLOW(a, b, c)\ + V_FLAG = ((NEG(a) & POS(b) & POS(c)) |\ + (POS(a) & NEG(b) & NEG(c))) ? true : false; +#endif +#ifndef ADD_RD_RS_RN + #define ADD_RD_RS_RN(N) \ + {\ + u32 lhs = reg[source].I;\ + u32 rhs = reg[N].I;\ + u32 res = lhs + rhs;\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef ADD_RD_RS_O3 + #define ADD_RD_RS_O3(N) \ + {\ + u32 lhs = reg[source].I;\ + u32 rhs = N;\ + u32 res = lhs + rhs;\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef ADD_RD_RS_O3_0 +# define ADD_RD_RS_O3_0 ADD_RD_RS_O3 +#endif +#ifndef ADD_RN_O8 + #define ADD_RN_O8(d) \ + {\ + u32 lhs = reg[(d)].I;\ + u32 rhs = (opcode & 255);\ + u32 res = lhs + rhs;\ + reg[(d)].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef CMN_RD_RS + #define CMN_RD_RS \ + {\ + u32 lhs = reg[dest].I;\ + u32 rhs = value;\ + u32 res = lhs + rhs;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef ADC_RD_RS + #define ADC_RD_RS \ + {\ + u32 lhs = reg[dest].I;\ + u32 rhs = value;\ + u32 res = lhs + rhs + (u32)C_FLAG;\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef SUB_RD_RS_RN + #define SUB_RD_RS_RN(N) \ + {\ + u32 lhs = reg[source].I;\ + u32 rhs = reg[N].I;\ + u32 res = lhs - rhs;\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef SUB_RD_RS_O3 + #define SUB_RD_RS_O3(N) \ + {\ + u32 lhs = reg[source].I;\ + u32 rhs = N;\ + u32 res = lhs - rhs;\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef SUB_RD_RS_O3_0 +# define SUB_RD_RS_O3_0 SUB_RD_RS_O3 +#endif +#ifndef SUB_RN_O8 + #define SUB_RN_O8(d) \ + {\ + u32 lhs = reg[(d)].I;\ + u32 rhs = (opcode & 255);\ + u32 res = lhs - rhs;\ + reg[(d)].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef MOV_RN_O8 + #define MOV_RN_O8(d) \ + {\ + reg[d].I = opcode & 255;\ + N_FLAG = false;\ + Z_FLAG = (reg[d].I ? false : true);\ + } +#endif +#ifndef CMP_RN_O8 + #define CMP_RN_O8(d) \ + {\ + u32 lhs = reg[(d)].I;\ + u32 rhs = (opcode & 255);\ + u32 res = lhs - rhs;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef SBC_RD_RS + #define SBC_RD_RS \ + {\ + u32 lhs = reg[dest].I;\ + u32 rhs = value;\ + u32 res = lhs - rhs - !((u32)C_FLAG);\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef LSL_RD_RM_I5 + #define LSL_RD_RM_I5 \ + {\ + C_FLAG = (reg[source].I >> (32 - shift)) & 1 ? true : false;\ + value = reg[source].I << shift;\ + } +#endif +#ifndef LSL_RD_RS + #define LSL_RD_RS \ + {\ + C_FLAG = (reg[dest].I >> (32 - value)) & 1 ? true : false;\ + value = reg[dest].I << value;\ + } +#endif +#ifndef LSR_RD_RM_I5 + #define LSR_RD_RM_I5 \ + {\ + C_FLAG = (reg[source].I >> (shift - 1)) & 1 ? true : false;\ + value = reg[source].I >> shift;\ + } +#endif +#ifndef LSR_RD_RS + #define LSR_RD_RS \ + {\ + C_FLAG = (reg[dest].I >> (value - 1)) & 1 ? true : false;\ + value = reg[dest].I >> value;\ + } +#endif +#ifndef ASR_RD_RM_I5 + #define ASR_RD_RM_I5 \ + {\ + C_FLAG = ((s32)reg[source].I >> (int)(shift - 1)) & 1 ? true : false;\ + value = (s32)reg[source].I >> (int)shift;\ + } +#endif +#ifndef ASR_RD_RS + #define ASR_RD_RS \ + {\ + C_FLAG = ((s32)reg[dest].I >> (int)(value - 1)) & 1 ? true : false;\ + value = (s32)reg[dest].I >> (int)value;\ + } +#endif +#ifndef ROR_RD_RS + #define ROR_RD_RS \ + {\ + C_FLAG = (reg[dest].I >> (value - 1)) & 1 ? true : false;\ + value = ((reg[dest].I << (32 - value)) |\ + (reg[dest].I >> value));\ + } +#endif +#ifndef NEG_RD_RS + #define NEG_RD_RS \ + {\ + u32 lhs = reg[source].I;\ + u32 rhs = 0;\ + u32 res = rhs - lhs;\ + reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(rhs, lhs, res);\ + SUBOVERFLOW(rhs, lhs, res);\ + } +#endif +#ifndef CMP_RD_RS + #define CMP_RD_RS \ + {\ + u32 lhs = reg[dest].I;\ + u32 rhs = value;\ + u32 res = lhs - rhs;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef IMM5_INSN + #define IMM5_INSN(OP,N) \ + int dest = opcode & 0x07;\ + int source = (opcode >> 3) & 0x07;\ + u32 value;\ + OP(N);\ + reg[dest].I = value;\ + N_FLAG = (value & 0x80000000 ? true : false);\ + Z_FLAG = (value ? false : true); + #define IMM5_INSN_0(OP) \ + int dest = opcode & 0x07;\ + int source = (opcode >> 3) & 0x07;\ + u32 value;\ + OP;\ + reg[dest].I = value;\ + N_FLAG = (value & 0x80000000 ? true : false);\ + Z_FLAG = (value ? false : true); + #define IMM5_LSL(N) \ + int shift = N;\ + LSL_RD_RM_I5; + #define IMM5_LSL_0 \ + value = reg[source].I; + #define IMM5_LSR(N) \ + int shift = N;\ + LSR_RD_RM_I5; + #define IMM5_LSR_0 \ + C_FLAG = reg[source].I & 0x80000000 ? true : false;\ + value = 0; + #define IMM5_ASR(N) \ + int shift = N;\ + ASR_RD_RM_I5; + #define IMM5_ASR_0 \ + if(reg[source].I & 0x80000000) {\ + value = 0xFFFFFFFF;\ + C_FLAG = true;\ + } else {\ + value = 0;\ + C_FLAG = false;\ + } +#endif +#ifndef THREEARG_INSN + #define THREEARG_INSN(OP,N) \ + int dest = opcode & 0x07; \ + int source = (opcode >> 3) & 0x07; \ + OP(N); +#endif + +// Shift instructions ///////////////////////////////////////////////////// + +#define DEFINE_IMM5_INSN(OP,BASE) \ + static INSN_REGPARM void thumb##BASE##_00(u32 opcode) { IMM5_INSN_0(OP##_0); } \ + static INSN_REGPARM void thumb##BASE##_01(u32 opcode) { IMM5_INSN(OP, 1); } \ + static INSN_REGPARM void thumb##BASE##_02(u32 opcode) { IMM5_INSN(OP, 2); } \ + static INSN_REGPARM void thumb##BASE##_03(u32 opcode) { IMM5_INSN(OP, 3); } \ + static INSN_REGPARM void thumb##BASE##_04(u32 opcode) { IMM5_INSN(OP, 4); } \ + static INSN_REGPARM void thumb##BASE##_05(u32 opcode) { IMM5_INSN(OP, 5); } \ + static INSN_REGPARM void thumb##BASE##_06(u32 opcode) { IMM5_INSN(OP, 6); } \ + static INSN_REGPARM void thumb##BASE##_07(u32 opcode) { IMM5_INSN(OP, 7); } \ + static INSN_REGPARM void thumb##BASE##_08(u32 opcode) { IMM5_INSN(OP, 8); } \ + static INSN_REGPARM void thumb##BASE##_09(u32 opcode) { IMM5_INSN(OP, 9); } \ + static INSN_REGPARM void thumb##BASE##_0A(u32 opcode) { IMM5_INSN(OP,10); } \ + static INSN_REGPARM void thumb##BASE##_0B(u32 opcode) { IMM5_INSN(OP,11); } \ + static INSN_REGPARM void thumb##BASE##_0C(u32 opcode) { IMM5_INSN(OP,12); } \ + static INSN_REGPARM void thumb##BASE##_0D(u32 opcode) { IMM5_INSN(OP,13); } \ + static INSN_REGPARM void thumb##BASE##_0E(u32 opcode) { IMM5_INSN(OP,14); } \ + static INSN_REGPARM void thumb##BASE##_0F(u32 opcode) { IMM5_INSN(OP,15); } \ + static INSN_REGPARM void thumb##BASE##_10(u32 opcode) { IMM5_INSN(OP,16); } \ + static INSN_REGPARM void thumb##BASE##_11(u32 opcode) { IMM5_INSN(OP,17); } \ + static INSN_REGPARM void thumb##BASE##_12(u32 opcode) { IMM5_INSN(OP,18); } \ + static INSN_REGPARM void thumb##BASE##_13(u32 opcode) { IMM5_INSN(OP,19); } \ + static INSN_REGPARM void thumb##BASE##_14(u32 opcode) { IMM5_INSN(OP,20); } \ + static INSN_REGPARM void thumb##BASE##_15(u32 opcode) { IMM5_INSN(OP,21); } \ + static INSN_REGPARM void thumb##BASE##_16(u32 opcode) { IMM5_INSN(OP,22); } \ + static INSN_REGPARM void thumb##BASE##_17(u32 opcode) { IMM5_INSN(OP,23); } \ + static INSN_REGPARM void thumb##BASE##_18(u32 opcode) { IMM5_INSN(OP,24); } \ + static INSN_REGPARM void thumb##BASE##_19(u32 opcode) { IMM5_INSN(OP,25); } \ + static INSN_REGPARM void thumb##BASE##_1A(u32 opcode) { IMM5_INSN(OP,26); } \ + static INSN_REGPARM void thumb##BASE##_1B(u32 opcode) { IMM5_INSN(OP,27); } \ + static INSN_REGPARM void thumb##BASE##_1C(u32 opcode) { IMM5_INSN(OP,28); } \ + static INSN_REGPARM void thumb##BASE##_1D(u32 opcode) { IMM5_INSN(OP,29); } \ + static INSN_REGPARM void thumb##BASE##_1E(u32 opcode) { IMM5_INSN(OP,30); } \ + static INSN_REGPARM void thumb##BASE##_1F(u32 opcode) { IMM5_INSN(OP,31); } + +// LSL Rd, Rm, #Imm 5 +DEFINE_IMM5_INSN(IMM5_LSL,00) +// LSR Rd, Rm, #Imm 5 +DEFINE_IMM5_INSN(IMM5_LSR,08) +// ASR Rd, Rm, #Imm 5 +DEFINE_IMM5_INSN(IMM5_ASR,10) + +// 3-argument ADD/SUB ///////////////////////////////////////////////////// + +#define DEFINE_REG3_INSN(OP,BASE) \ + static INSN_REGPARM void thumb##BASE##_0(u32 opcode) { THREEARG_INSN(OP,0); } \ + static INSN_REGPARM void thumb##BASE##_1(u32 opcode) { THREEARG_INSN(OP,1); } \ + static INSN_REGPARM void thumb##BASE##_2(u32 opcode) { THREEARG_INSN(OP,2); } \ + static INSN_REGPARM void thumb##BASE##_3(u32 opcode) { THREEARG_INSN(OP,3); } \ + static INSN_REGPARM void thumb##BASE##_4(u32 opcode) { THREEARG_INSN(OP,4); } \ + static INSN_REGPARM void thumb##BASE##_5(u32 opcode) { THREEARG_INSN(OP,5); } \ + static INSN_REGPARM void thumb##BASE##_6(u32 opcode) { THREEARG_INSN(OP,6); } \ + static INSN_REGPARM void thumb##BASE##_7(u32 opcode) { THREEARG_INSN(OP,7); } + +#define DEFINE_IMM3_INSN(OP,BASE) \ + static INSN_REGPARM void thumb##BASE##_0(u32 opcode) { THREEARG_INSN(OP##_0,0); } \ + static INSN_REGPARM void thumb##BASE##_1(u32 opcode) { THREEARG_INSN(OP,1); } \ + static INSN_REGPARM void thumb##BASE##_2(u32 opcode) { THREEARG_INSN(OP,2); } \ + static INSN_REGPARM void thumb##BASE##_3(u32 opcode) { THREEARG_INSN(OP,3); } \ + static INSN_REGPARM void thumb##BASE##_4(u32 opcode) { THREEARG_INSN(OP,4); } \ + static INSN_REGPARM void thumb##BASE##_5(u32 opcode) { THREEARG_INSN(OP,5); } \ + static INSN_REGPARM void thumb##BASE##_6(u32 opcode) { THREEARG_INSN(OP,6); } \ + static INSN_REGPARM void thumb##BASE##_7(u32 opcode) { THREEARG_INSN(OP,7); } + +// ADD Rd, Rs, Rn +DEFINE_REG3_INSN(ADD_RD_RS_RN,18) +// SUB Rd, Rs, Rn +DEFINE_REG3_INSN(SUB_RD_RS_RN,1A) +// ADD Rd, Rs, #Offset3 +DEFINE_IMM3_INSN(ADD_RD_RS_O3,1C) +// SUB Rd, Rs, #Offset3 +DEFINE_IMM3_INSN(SUB_RD_RS_O3,1E) + +// MOV/CMP/ADD/SUB immediate ////////////////////////////////////////////// + +// MOV R0, #Offset8 +static INSN_REGPARM void thumb20(u32 opcode) { MOV_RN_O8(0); } +// MOV R1, #Offset8 +static INSN_REGPARM void thumb21(u32 opcode) { MOV_RN_O8(1); } +// MOV R2, #Offset8 +static INSN_REGPARM void thumb22(u32 opcode) { MOV_RN_O8(2); } +// MOV R3, #Offset8 +static INSN_REGPARM void thumb23(u32 opcode) { MOV_RN_O8(3); } +// MOV R4, #Offset8 +static INSN_REGPARM void thumb24(u32 opcode) { MOV_RN_O8(4); } +// MOV R5, #Offset8 +static INSN_REGPARM void thumb25(u32 opcode) { MOV_RN_O8(5); } +// MOV R6, #Offset8 +static INSN_REGPARM void thumb26(u32 opcode) { MOV_RN_O8(6); } +// MOV R7, #Offset8 +static INSN_REGPARM void thumb27(u32 opcode) { MOV_RN_O8(7); } + +// CMP R0, #Offset8 +static INSN_REGPARM void thumb28(u32 opcode) { CMP_RN_O8(0); } +// CMP R1, #Offset8 +static INSN_REGPARM void thumb29(u32 opcode) { CMP_RN_O8(1); } +// CMP R2, #Offset8 +static INSN_REGPARM void thumb2A(u32 opcode) { CMP_RN_O8(2); } +// CMP R3, #Offset8 +static INSN_REGPARM void thumb2B(u32 opcode) { CMP_RN_O8(3); } +// CMP R4, #Offset8 +static INSN_REGPARM void thumb2C(u32 opcode) { CMP_RN_O8(4); } +// CMP R5, #Offset8 +static INSN_REGPARM void thumb2D(u32 opcode) { CMP_RN_O8(5); } +// CMP R6, #Offset8 +static INSN_REGPARM void thumb2E(u32 opcode) { CMP_RN_O8(6); } +// CMP R7, #Offset8 +static INSN_REGPARM void thumb2F(u32 opcode) { CMP_RN_O8(7); } + +// ADD R0,#Offset8 +static INSN_REGPARM void thumb30(u32 opcode) { ADD_RN_O8(0); } +// ADD R1,#Offset8 +static INSN_REGPARM void thumb31(u32 opcode) { ADD_RN_O8(1); } +// ADD R2,#Offset8 +static INSN_REGPARM void thumb32(u32 opcode) { ADD_RN_O8(2); } +// ADD R3,#Offset8 +static INSN_REGPARM void thumb33(u32 opcode) { ADD_RN_O8(3); } +// ADD R4,#Offset8 +static INSN_REGPARM void thumb34(u32 opcode) { ADD_RN_O8(4); } +// ADD R5,#Offset8 +static INSN_REGPARM void thumb35(u32 opcode) { ADD_RN_O8(5); } +// ADD R6,#Offset8 +static INSN_REGPARM void thumb36(u32 opcode) { ADD_RN_O8(6); } +// ADD R7,#Offset8 +static INSN_REGPARM void thumb37(u32 opcode) { ADD_RN_O8(7); } + +// SUB R0,#Offset8 +static INSN_REGPARM void thumb38(u32 opcode) { SUB_RN_O8(0); } +// SUB R1,#Offset8 +static INSN_REGPARM void thumb39(u32 opcode) { SUB_RN_O8(1); } +// SUB R2,#Offset8 +static INSN_REGPARM void thumb3A(u32 opcode) { SUB_RN_O8(2); } +// SUB R3,#Offset8 +static INSN_REGPARM void thumb3B(u32 opcode) { SUB_RN_O8(3); } +// SUB R4,#Offset8 +static INSN_REGPARM void thumb3C(u32 opcode) { SUB_RN_O8(4); } +// SUB R5,#Offset8 +static INSN_REGPARM void thumb3D(u32 opcode) { SUB_RN_O8(5); } +// SUB R6,#Offset8 +static INSN_REGPARM void thumb3E(u32 opcode) { SUB_RN_O8(6); } +// SUB R7,#Offset8 +static INSN_REGPARM void thumb3F(u32 opcode) { SUB_RN_O8(7); } + +// ALU operations ///////////////////////////////////////////////////////// + +// AND Rd, Rs +static INSN_REGPARM void thumb40_0(u32 opcode) +{ + int dest = opcode & 7; + reg[dest].I &= reg[(opcode >> 3)&7].I; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; + THUMB_CONSOLE_OUTPUT(NULL, reg[2].I); +} + +// EOR Rd, Rs +static INSN_REGPARM void thumb40_1(u32 opcode) +{ + int dest = opcode & 7; + reg[dest].I ^= reg[(opcode >> 3)&7].I; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; +} + +// LSL Rd, Rs +static INSN_REGPARM void thumb40_2(u32 opcode) +{ + int dest = opcode & 7; + u32 value = reg[(opcode >> 3)&7].B.B0; + if(value) { + if(value == 32) { + value = 0; + C_FLAG = (reg[dest].I & 1 ? true : false); + } else if(value < 32) { + LSL_RD_RS; + } else { + value = 0; + C_FLAG = false; + } + reg[dest].I = value; + } + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; + clockTicks = codeTicksAccess16(armNextPC)+2; +} + +// LSR Rd, Rs +static INSN_REGPARM void thumb40_3(u32 opcode) +{ + int dest = opcode & 7; + u32 value = reg[(opcode >> 3)&7].B.B0; + if(value) { + if(value == 32) { + value = 0; + C_FLAG = (reg[dest].I & 0x80000000 ? true : false); + } else if(value < 32) { + LSR_RD_RS; + } else { + value = 0; + C_FLAG = false; + } + reg[dest].I = value; + } + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; + clockTicks = codeTicksAccess16(armNextPC)+2; +} + +// ASR Rd, Rs +static INSN_REGPARM void thumb41_0(u32 opcode) +{ + int dest = opcode & 7; + u32 value = reg[(opcode >> 3)&7].B.B0; + if(value) { + if(value < 32) { + ASR_RD_RS; + reg[dest].I = value; + } else { + if(reg[dest].I & 0x80000000){ + reg[dest].I = 0xFFFFFFFF; + C_FLAG = true; + } else { + reg[dest].I = 0x00000000; + C_FLAG = false; + } + } + } + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; + clockTicks = codeTicksAccess16(armNextPC)+2; +} + +// ADC Rd, Rs +static INSN_REGPARM void thumb41_1(u32 opcode) +{ + int dest = opcode & 0x07; + u32 value = reg[(opcode >> 3)&7].I; + ADC_RD_RS; +} + +// SBC Rd, Rs +static INSN_REGPARM void thumb41_2(u32 opcode) +{ + int dest = opcode & 0x07; + u32 value = reg[(opcode >> 3)&7].I; + SBC_RD_RS; +} + +// ROR Rd, Rs +static INSN_REGPARM void thumb41_3(u32 opcode) +{ + int dest = opcode & 7; + u32 value = reg[(opcode >> 3)&7].B.B0; + + if(value) { + value = value & 0x1f; + if(value == 0) { + C_FLAG = (reg[dest].I & 0x80000000 ? true : false); + } else { + ROR_RD_RS; + reg[dest].I = value; + } + } + clockTicks = codeTicksAccess16(armNextPC)+2; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; +} + +// TST Rd, Rs +static INSN_REGPARM void thumb42_0(u32 opcode) +{ + u32 value = reg[opcode & 7].I & reg[(opcode >> 3) & 7].I; + N_FLAG = value & 0x80000000 ? true : false; + Z_FLAG = value ? false : true; +} + +// NEG Rd, Rs +static INSN_REGPARM void thumb42_1(u32 opcode) +{ + int dest = opcode & 7; + int source = (opcode >> 3) & 7; + NEG_RD_RS; +} + +// CMP Rd, Rs +static INSN_REGPARM void thumb42_2(u32 opcode) +{ + int dest = opcode & 7; + u32 value = reg[(opcode >> 3)&7].I; + CMP_RD_RS; +} + +// CMN Rd, Rs +static INSN_REGPARM void thumb42_3(u32 opcode) +{ + int dest = opcode & 7; + u32 value = reg[(opcode >> 3)&7].I; + CMN_RD_RS; +} + +// ORR Rd, Rs +static INSN_REGPARM void thumb43_0(u32 opcode) +{ + int dest = opcode & 7; + reg[dest].I |= reg[(opcode >> 3) & 7].I; + Z_FLAG = reg[dest].I ? false : true; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; +} + +// MUL Rd, Rs +static INSN_REGPARM void thumb43_1(u32 opcode) +{ + clockTicks = 1; + int dest = opcode & 7; + u32 rm = reg[dest].I; + reg[dest].I = reg[(opcode >> 3) & 7].I * rm; + if (((s32)rm) < 0) + rm = ~rm; + if ((rm & 0xFFFFFF00) == 0) + clockTicks += 0; + else if ((rm & 0xFFFF0000) == 0) + clockTicks += 1; + else if ((rm & 0xFF000000) == 0) + clockTicks += 2; + else + clockTicks += 3; + busPrefetchCount = (busPrefetchCount<>(8-clockTicks)); + clockTicks += codeTicksAccess16(armNextPC) + 1; + Z_FLAG = reg[dest].I ? false : true; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; +} + +// BIC Rd, Rs +static INSN_REGPARM void thumb43_2(u32 opcode) +{ + int dest = opcode & 7; + reg[dest].I &= (~reg[(opcode >> 3) & 7].I); + Z_FLAG = reg[dest].I ? false : true; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; +} + +// MVN Rd, Rs +static INSN_REGPARM void thumb43_3(u32 opcode) +{ + int dest = opcode & 7; + reg[dest].I = ~reg[(opcode >> 3) & 7].I; + Z_FLAG = reg[dest].I ? false : true; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; +} + +// High-register instructions and BX ////////////////////////////////////// + +// ADD Rd, Hs +static INSN_REGPARM void thumb44_1(u32 opcode) +{ + reg[opcode&7].I += reg[((opcode>>3)&7)+8].I; +} + +// ADD Hd, Rs +static INSN_REGPARM void thumb44_2(u32 opcode) +{ + reg[(opcode&7)+8].I += reg[(opcode>>3)&7].I; + if((opcode&7) == 7) { + reg[15].I &= 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = codeTicksAccessSeq16(armNextPC)*2 + + codeTicksAccess16(armNextPC) + 3; + } +} + +// ADD Hd, Hs +static INSN_REGPARM void thumb44_3(u32 opcode) +{ + reg[(opcode&7)+8].I += reg[((opcode>>3)&7)+8].I; + if((opcode&7) == 7) { + reg[15].I &= 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = codeTicksAccessSeq16(armNextPC)*2 + + codeTicksAccess16(armNextPC) + 3; + } +} + +// CMP Rd, Hs +static INSN_REGPARM void thumb45_1(u32 opcode) +{ + int dest = opcode & 7; + u32 value = reg[((opcode>>3)&7)+8].I; + CMP_RD_RS; +} + +// CMP Hd, Rs +static INSN_REGPARM void thumb45_2(u32 opcode) +{ + int dest = (opcode & 7) + 8; + u32 value = reg[(opcode>>3)&7].I; + CMP_RD_RS; +} + +// CMP Hd, Hs +static INSN_REGPARM void thumb45_3(u32 opcode) +{ + int dest = (opcode & 7) + 8; + u32 value = reg[((opcode>>3)&7)+8].I; + CMP_RD_RS; +} + +// MOV Rd, Hs +static INSN_REGPARM void thumb46_1(u32 opcode) +{ + reg[opcode&7].I = reg[((opcode>>3)&7)+8].I; +} + +// MOV Hd, Rs +static INSN_REGPARM void thumb46_2(u32 opcode) +{ + reg[(opcode&7)+8].I = reg[(opcode>>3)&7].I; + if((opcode&7) == 7) { + UPDATE_OLDREG; + reg[15].I &= 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = codeTicksAccessSeq16(armNextPC)*2 + + codeTicksAccess16(armNextPC) + 3; + } +} + +// MOV Hd, Hs +static INSN_REGPARM void thumb46_3(u32 opcode) +{ + reg[(opcode&7)+8].I = reg[((opcode>>3)&7)+8].I; + if((opcode&7) == 7) { + UPDATE_OLDREG; + reg[15].I &= 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = codeTicksAccessSeq16(armNextPC)*2 + + codeTicksAccess16(armNextPC) + 3; + } +} + + +// BX Rs +static INSN_REGPARM void thumb47(u32 opcode) +{ + int base = (opcode >> 3) & 15; + busPrefetchCount=0; + UPDATE_OLDREG; + reg[15].I = reg[base].I; + if(reg[base].I & 1) { + armState = false; + reg[15].I &= 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = codeTicksAccessSeq16(armNextPC) + + codeTicksAccessSeq16(armNextPC) + codeTicksAccess16(armNextPC) + 3; + } else { + armState = true; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + ARM_PREFETCH; + clockTicks = codeTicksAccessSeq32(armNextPC) + + codeTicksAccessSeq32(armNextPC) + codeTicksAccess32(armNextPC) + 3; + } +} + +// Load/store instructions //////////////////////////////////////////////// + +// LDR R0~R7,[PC, #Imm] +static INSN_REGPARM void thumb48(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = (reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2); + reg[regist].I = CPUReadMemoryQuick(address); + busPrefetchCount=0; + clockTicks = 3 + dataTicksAccess32(address) + codeTicksAccess16(armNextPC); +} + +// STR Rd, [Rs, Rn] +static INSN_REGPARM void thumb50(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + reg[(opcode>>6)&7].I; + CPUWriteMemory(address, reg[opcode & 7].I); + clockTicks = dataTicksAccess32(address) + codeTicksAccess16(armNextPC) + 2; +} + +// STRH Rd, [Rs, Rn] +static INSN_REGPARM void thumb52(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + reg[(opcode>>6)&7].I; + CPUWriteHalfWord(address, reg[opcode&7].W.W0); + clockTicks = dataTicksAccess16(address) + codeTicksAccess16(armNextPC) + 2; +} + +// STRB Rd, [Rs, Rn] +static INSN_REGPARM void thumb54(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + reg[(opcode >>6)&7].I; + CPUWriteByte(address, reg[opcode & 7].B.B0); + clockTicks = dataTicksAccess16(address) + codeTicksAccess16(armNextPC) + 2; +} + +// LDSB Rd, [Rs, Rn] +static INSN_REGPARM void thumb56(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + reg[(opcode>>6)&7].I; + reg[opcode&7].I = (s8)CPUReadByte(address); + clockTicks = 3 + dataTicksAccess16(address) + codeTicksAccess16(armNextPC); +} + +// LDR Rd, [Rs, Rn] +static INSN_REGPARM void thumb58(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + reg[(opcode>>6)&7].I; + reg[opcode&7].I = CPUReadMemory(address); + clockTicks = 3 + dataTicksAccess32(address) + codeTicksAccess16(armNextPC); +} + +// LDRH Rd, [Rs, Rn] +static INSN_REGPARM void thumb5A(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + reg[(opcode>>6)&7].I; + reg[opcode&7].I = CPUReadHalfWord(address); + clockTicks = 3 + dataTicksAccess32(address) + codeTicksAccess16(armNextPC); +} + +// LDRB Rd, [Rs, Rn] +static INSN_REGPARM void thumb5C(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + reg[(opcode>>6)&7].I; + reg[opcode&7].I = CPUReadByte(address); + clockTicks = 3 + dataTicksAccess16(address) + codeTicksAccess16(armNextPC); +} + +// LDSH Rd, [Rs, Rn] +static INSN_REGPARM void thumb5E(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + reg[(opcode>>6)&7].I; + reg[opcode&7].I = (s16)CPUReadHalfWordSigned(address); + clockTicks = 3 + dataTicksAccess16(address) + codeTicksAccess16(armNextPC); +} + +// STR Rd, [Rs, #Imm] +static INSN_REGPARM void thumb60(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<2); + CPUWriteMemory(address, reg[opcode&7].I); + clockTicks = dataTicksAccess32(address) + codeTicksAccess16(armNextPC) + 2; +} + +// LDR Rd, [Rs, #Imm] +static INSN_REGPARM void thumb68(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<2); + reg[opcode&7].I = CPUReadMemory(address); + clockTicks = 3 + dataTicksAccess32(address) + codeTicksAccess16(armNextPC); +} + +// STRB Rd, [Rs, #Imm] +static INSN_REGPARM void thumb70(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + (((opcode>>6)&31)); + CPUWriteByte(address, reg[opcode&7].B.B0); + clockTicks = dataTicksAccess16(address) + codeTicksAccess16(armNextPC) + 2; +} + +// LDRB Rd, [Rs, #Imm] +static INSN_REGPARM void thumb78(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + (((opcode>>6)&31)); + reg[opcode&7].I = CPUReadByte(address); + clockTicks = 3 + dataTicksAccess16(address) + codeTicksAccess16(armNextPC); +} + +// STRH Rd, [Rs, #Imm] +static INSN_REGPARM void thumb80(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<1); + CPUWriteHalfWord(address, reg[opcode&7].W.W0); + clockTicks = dataTicksAccess16(address) + codeTicksAccess16(armNextPC) + 2; +} + +// LDRH Rd, [Rs, #Imm] +static INSN_REGPARM void thumb88(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<1); + reg[opcode&7].I = CPUReadHalfWord(address); + clockTicks = 3 + dataTicksAccess16(address) + codeTicksAccess16(armNextPC); +} + +// STR R0~R7, [SP, #Imm] +static INSN_REGPARM void thumb90(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[13].I + ((opcode&255)<<2); + CPUWriteMemory(address, reg[regist].I); + clockTicks = dataTicksAccess32(address) + codeTicksAccess16(armNextPC) + 2; +} + +// LDR R0~R7, [SP, #Imm] +static INSN_REGPARM void thumb98(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[13].I + ((opcode&255)<<2); + reg[regist].I = CPUReadMemoryQuick(address); + clockTicks = 3 + dataTicksAccess32(address) + codeTicksAccess16(armNextPC); +} + +// PC/stack-related /////////////////////////////////////////////////////// + +// ADD R0~R7, PC, Imm +static INSN_REGPARM void thumbA0(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + reg[regist].I = (reg[15].I & 0xFFFFFFFC) + ((opcode&255)<<2); +} + +// ADD R0~R7, SP, Imm +static INSN_REGPARM void thumbA8(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + reg[regist].I = reg[13].I + ((opcode&255)<<2); +} + +// ADD SP, Imm +static INSN_REGPARM void thumbB0(u32 opcode) +{ + int offset = (opcode & 127) << 2; + if(opcode & 0x80) + offset = -offset; + reg[13].I += offset; +} + +// Push and pop /////////////////////////////////////////////////////////// + +#define PUSH_REG(val, r) \ + if (opcode & (val)) { \ + CPUWriteMemory(address, reg[(r)].I); \ + if (!count) { \ + clockTicks += 1 + dataTicksAccess32(address); \ + } else { \ + clockTicks += 1 + dataTicksAccessSeq32(address); \ + } \ + count++; \ + address += 4; \ + } + +#define POP_REG(val, r) \ + if (opcode & (val)) { \ + reg[(r)].I = CPUReadMemory(address); \ + if (!count) { \ + clockTicks += 1 + dataTicksAccess32(address); \ + } else { \ + clockTicks += 1 + dataTicksAccessSeq32(address); \ + } \ + count++; \ + address += 4; \ + } + +// PUSH {Rlist} +static INSN_REGPARM void thumbB4(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int count = 0; + u32 temp = reg[13].I - 4 * cpuBitsSet[opcode & 0xff]; + u32 address = temp & 0xFFFFFFFC; + PUSH_REG(1, 0); + PUSH_REG(2, 1); + PUSH_REG(4, 2); + PUSH_REG(8, 3); + PUSH_REG(16, 4); + PUSH_REG(32, 5); + PUSH_REG(64, 6); + PUSH_REG(128, 7); + clockTicks += 1 + codeTicksAccess16(armNextPC); + reg[13].I = temp; +} + +// PUSH {Rlist, LR} +static INSN_REGPARM void thumbB5(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int count = 0; + u32 temp = reg[13].I - 4 - 4 * cpuBitsSet[opcode & 0xff]; + u32 address = temp & 0xFFFFFFFC; + PUSH_REG(1, 0); + PUSH_REG(2, 1); + PUSH_REG(4, 2); + PUSH_REG(8, 3); + PUSH_REG(16, 4); + PUSH_REG(32, 5); + PUSH_REG(64, 6); + PUSH_REG(128, 7); + PUSH_REG(256, 14); + clockTicks += 1 + codeTicksAccess16(armNextPC); + reg[13].I = temp; +} + +// POP {Rlist} +static INSN_REGPARM void thumbBC(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int count = 0; + u32 address = reg[13].I & 0xFFFFFFFC; + u32 temp = reg[13].I + 4*cpuBitsSet[opcode & 0xFF]; + POP_REG(1, 0); + POP_REG(2, 1); + POP_REG(4, 2); + POP_REG(8, 3); + POP_REG(16, 4); + POP_REG(32, 5); + POP_REG(64, 6); + POP_REG(128, 7); + reg[13].I = temp; + clockTicks = 2 + codeTicksAccess16(armNextPC); +} + +// POP {Rlist, PC} +static INSN_REGPARM void thumbBD(u32 opcode) +{ + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + int count = 0; + u32 address = reg[13].I & 0xFFFFFFFC; + u32 temp = reg[13].I + 4 + 4*cpuBitsSet[opcode & 0xFF]; + POP_REG(1, 0); + POP_REG(2, 1); + POP_REG(4, 2); + POP_REG(8, 3); + POP_REG(16, 4); + POP_REG(32, 5); + POP_REG(64, 6); + POP_REG(128, 7); + reg[15].I = (CPUReadMemory(address) & 0xFFFFFFFE); + if (!count) { + clockTicks += 1 + dataTicksAccess32(address); + } else { + clockTicks += 1 + dataTicksAccessSeq32(address); + } + count++; + armNextPC = reg[15].I; + reg[15].I += 2; + reg[13].I = temp; + THUMB_PREFETCH; + busPrefetchCount = 0; + clockTicks += 3 + codeTicksAccess16(armNextPC) + codeTicksAccess16(armNextPC); +} + +// Load/store multiple //////////////////////////////////////////////////// + +#define THUMB_STM_REG(val,r,b) \ + if(opcode & (val)) { \ + CPUWriteMemory(address, reg[(r)].I); \ + reg[(b)].I = temp; \ + if (!count) { \ + clockTicks += 1 + dataTicksAccess32(address); \ + } else { \ + clockTicks += 1 + dataTicksAccessSeq32(address); \ + } \ + count++; \ + address += 4; \ + } + +#define THUMB_LDM_REG(val,r) \ + if(opcode & (val)) { \ + reg[(r)].I = CPUReadMemory(address); \ + if (!count) { \ + clockTicks += 1 + dataTicksAccess32(address); \ + } else { \ + clockTicks += 1 + dataTicksAccessSeq32(address); \ + } \ + count++; \ + address += 4; \ + } + +// STM R0~7!, {Rlist} +static INSN_REGPARM void thumbC0(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[regist].I & 0xFFFFFFFC; + u32 temp = reg[regist].I + 4*cpuBitsSet[opcode & 0xff]; + int count = 0; + // store + THUMB_STM_REG(1, 0, regist); + THUMB_STM_REG(2, 1, regist); + THUMB_STM_REG(4, 2, regist); + THUMB_STM_REG(8, 3, regist); + THUMB_STM_REG(16, 4, regist); + THUMB_STM_REG(32, 5, regist); + THUMB_STM_REG(64, 6, regist); + THUMB_STM_REG(128, 7, regist); + clockTicks = 1 + codeTicksAccess16(armNextPC); +} + +// LDM R0~R7!, {Rlist} +static INSN_REGPARM void thumbC8(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + if (busPrefetchCount == 0) + busPrefetch = busPrefetchEnable; + u32 address = reg[regist].I & 0xFFFFFFFC; + u32 temp = reg[regist].I + 4*cpuBitsSet[opcode & 0xFF]; + int count = 0; + // load + THUMB_LDM_REG(1, 0); + THUMB_LDM_REG(2, 1); + THUMB_LDM_REG(4, 2); + THUMB_LDM_REG(8, 3); + THUMB_LDM_REG(16, 4); + THUMB_LDM_REG(32, 5); + THUMB_LDM_REG(64, 6); + THUMB_LDM_REG(128, 7); + clockTicks = 2 + codeTicksAccess16(armNextPC); + if(!(opcode & (1<>6])(opcode); + + if (clockTicks < 0) + return 0; + if (clockTicks==0) + clockTicks = codeTicksAccessSeq16(oldArmNextPC) + 1; + cpuTotalTicks += clockTicks; + + } while (cpuTotalTicks < cpuNextEvent && !armState && !holdState && !SWITicks); + return 1; +} diff --git a/src/agb/GBA.cpp b/src/agb/GBA.cpp new file mode 100644 index 00000000..20fb31a4 --- /dev/null +++ b/src/agb/GBA.cpp @@ -0,0 +1,3983 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005-2006 Forgotten and the VBA development team +// Copyright (C) VBA-M development team +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include +#include +#include +#include +#include "GBA.h" +#include "GBAcpu.h" +#include "GBAinline.h" +#include "../Globals.h" +#include "GBAGFX.h" +#include "../EEprom.h" +#include "../Flash.h" +#include "../Sound.h" +#include "../Sram.h" +#include "../bios.h" +#include "../Cheats.h" +#include "../NLS.h" +#include "../elf.h" +#include "../Util.h" +#include "../Port.h" +#include "agbprint.h" +#ifdef PROFILING +#include "prof/prof.h" +#endif + +#ifdef __GNUC__ +#define _stricmp strcasecmp +#endif + + +extern int emulating; +#ifdef LINK_EMULATION +extern int linktime; +extern void StartLink(u16); +extern void StartJOYLink(u16); +extern void StartGPLink(u16); +extern void LinkSSend(u16); +extern void LinkUpdate(int); +extern int linktime2; +#endif +int SWITicks = 0; +int IRQTicks = 0; + +u32 mastercode = 0; +int layerEnableDelay = 0; +bool busPrefetch = false; +bool busPrefetchEnable = false; +u32 busPrefetchCount = 0; +int cpuDmaTicksToUpdate = 0; +int cpuDmaCount = 0; +bool cpuDmaHack = false; +u32 cpuDmaLast = 0; +int dummyAddress = 0; + +bool cpuBreakLoop = false; +int cpuNextEvent = 0; + +int gbaSaveType = 0; // used to remember the save type on reset +bool intState = false; +bool stopState = false; +bool holdState = false; +int holdType = 0; +bool cpuSramEnabled = true; +bool cpuFlashEnabled = true; +bool cpuEEPROMEnabled = true; +bool cpuEEPROMSensorEnabled = false; + +u32 cpuPrefetch[2]; + +int cpuTotalTicks = 0; +#ifdef PROFILING +int profilingTicks = 0; +int profilingTicksReload = 0; +static profile_segment *profilSegment = NULL; +#endif + +#ifdef BKPT_SUPPORT +u8 freezeWorkRAM[0x40000]; +u8 freezeInternalRAM[0x8000]; +u8 freezeVRAM[0x18000]; +u8 freezePRAM[0x400]; +u8 freezeOAM[0x400]; +bool debugger_last; +#endif + +int lcdTicks = (useBios && !skipBios) ? 1008 : 208; +u8 timerOnOffDelay = 0; +u16 timer0Value = 0; +bool timer0On = false; +int timer0Ticks = 0; +int timer0Reload = 0; +int timer0ClockReload = 0; +u16 timer1Value = 0; +bool timer1On = false; +int timer1Ticks = 0; +int timer1Reload = 0; +int timer1ClockReload = 0; +u16 timer2Value = 0; +bool timer2On = false; +int timer2Ticks = 0; +int timer2Reload = 0; +int timer2ClockReload = 0; +u16 timer3Value = 0; +bool timer3On = false; +int timer3Ticks = 0; +int timer3Reload = 0; +int timer3ClockReload = 0; +u32 dma0Source = 0; +u32 dma0Dest = 0; +u32 dma1Source = 0; +u32 dma1Dest = 0; +u32 dma2Source = 0; +u32 dma2Dest = 0; +u32 dma3Source = 0; +u32 dma3Dest = 0; +void (*cpuSaveGameFunc)(u32,u8) = flashSaveDecide; +void (*renderLine)() = mode0RenderLine; +bool fxOn = false; +bool windowOn = false; +int frameCount = 0; +char buffer[1024]; +FILE *out = NULL; +u32 lastTime = 0; +int count = 0; + +int capture = 0; +int capturePrevious = 0; +int captureNumber = 0; + +const int TIMER_TICKS[4] = { + 0, + 6, + 8, + 10 +}; + +const u32 objTilesAddress [3] = {0x010000, 0x014000, 0x014000}; +const u8 gamepakRamWaitState[4] = { 4, 3, 2, 8 }; +const u8 gamepakWaitState[4] = { 4, 3, 2, 8 }; +const u8 gamepakWaitState0[2] = { 2, 1 }; +const u8 gamepakWaitState1[2] = { 4, 1 }; +const u8 gamepakWaitState2[2] = { 8, 1 }; +const bool isInRom [16]= + { false, false, false, false, false, false, false, false, + true, true, true, true, true, true, false, false }; + +u8 memoryWait[16] = + { 0, 0, 2, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 0 }; +u8 memoryWait32[16] = + { 0, 0, 5, 0, 0, 1, 1, 0, 7, 7, 9, 9, 13, 13, 4, 0 }; +u8 memoryWaitSeq[16] = + { 0, 0, 2, 0, 0, 0, 0, 0, 2, 2, 4, 4, 8, 8, 4, 0 }; +u8 memoryWaitSeq32[16] = + { 0, 0, 5, 0, 0, 1, 1, 0, 5, 5, 9, 9, 17, 17, 4, 0 }; + +// The videoMemoryWait constants are used to add some waitstates +// if the opcode access video memory data outside of vblank/hblank +// It seems to happen on only one ticks for each pixel. +// Not used for now (too problematic with current code). +//const u8 videoMemoryWait[16] = +// {0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + +u8 biosProtected[4]; + +#ifdef WORDS_BIGENDIAN +bool cpuBiosSwapped = false; +#endif + +u32 myROM[] = { +0xEA000006, +0xEA000093, +0xEA000006, +0x00000000, +0x00000000, +0x00000000, +0xEA000088, +0x00000000, +0xE3A00302, +0xE1A0F000, +0xE92D5800, +0xE55EC002, +0xE28FB03C, +0xE79BC10C, +0xE14FB000, +0xE92D0800, +0xE20BB080, +0xE38BB01F, +0xE129F00B, +0xE92D4004, +0xE1A0E00F, +0xE12FFF1C, +0xE8BD4004, +0xE3A0C0D3, +0xE129F00C, +0xE8BD0800, +0xE169F00B, +0xE8BD5800, +0xE1B0F00E, +0x0000009C, +0x0000009C, +0x0000009C, +0x0000009C, +0x000001F8, +0x000001F0, +0x000000AC, +0x000000A0, +0x000000FC, +0x00000168, +0xE12FFF1E, +0xE1A03000, +0xE1A00001, +0xE1A01003, +0xE2113102, +0x42611000, +0xE033C040, +0x22600000, +0xE1B02001, +0xE15200A0, +0x91A02082, +0x3AFFFFFC, +0xE1500002, +0xE0A33003, +0x20400002, +0xE1320001, +0x11A020A2, +0x1AFFFFF9, +0xE1A01000, +0xE1A00003, +0xE1B0C08C, +0x22600000, +0x42611000, +0xE12FFF1E, +0xE92D0010, +0xE1A0C000, +0xE3A01001, +0xE1500001, +0x81A000A0, +0x81A01081, +0x8AFFFFFB, +0xE1A0000C, +0xE1A04001, +0xE3A03000, +0xE1A02001, +0xE15200A0, +0x91A02082, +0x3AFFFFFC, +0xE1500002, +0xE0A33003, +0x20400002, +0xE1320001, +0x11A020A2, +0x1AFFFFF9, +0xE0811003, +0xE1B010A1, +0xE1510004, +0x3AFFFFEE, +0xE1A00004, +0xE8BD0010, +0xE12FFF1E, +0xE0010090, +0xE1A01741, +0xE2611000, +0xE3A030A9, +0xE0030391, +0xE1A03743, +0xE2833E39, +0xE0030391, +0xE1A03743, +0xE2833C09, +0xE283301C, +0xE0030391, +0xE1A03743, +0xE2833C0F, +0xE28330B6, +0xE0030391, +0xE1A03743, +0xE2833C16, +0xE28330AA, +0xE0030391, +0xE1A03743, +0xE2833A02, +0xE2833081, +0xE0030391, +0xE1A03743, +0xE2833C36, +0xE2833051, +0xE0030391, +0xE1A03743, +0xE2833CA2, +0xE28330F9, +0xE0000093, +0xE1A00840, +0xE12FFF1E, +0xE3A00001, +0xE3A01001, +0xE92D4010, +0xE3A03000, +0xE3A04001, +0xE3500000, +0x1B000004, +0xE5CC3301, +0xEB000002, +0x0AFFFFFC, +0xE8BD4010, +0xE12FFF1E, +0xE3A0C301, +0xE5CC3208, +0xE15C20B8, +0xE0110002, +0x10222000, +0x114C20B8, +0xE5CC4208, +0xE12FFF1E, +0xE92D500F, +0xE3A00301, +0xE1A0E00F, +0xE510F004, +0xE8BD500F, +0xE25EF004, +0xE59FD044, +0xE92D5000, +0xE14FC000, +0xE10FE000, +0xE92D5000, +0xE3A0C302, +0xE5DCE09C, +0xE35E00A5, +0x1A000004, +0x05DCE0B4, +0x021EE080, +0xE28FE004, +0x159FF018, +0x059FF018, +0xE59FD018, +0xE8BD5000, +0xE169F00C, +0xE8BD5000, +0xE25EF004, +0x03007FF0, +0x09FE2000, +0x09FFC000, +0x03007FE0 +}; + +variable_desc saveGameStruct[] = { + { &DISPCNT , sizeof(u16) }, + { &DISPSTAT , sizeof(u16) }, + { &VCOUNT , sizeof(u16) }, + { &BG0CNT , sizeof(u16) }, + { &BG1CNT , sizeof(u16) }, + { &BG2CNT , sizeof(u16) }, + { &BG3CNT , sizeof(u16) }, + { &BG0HOFS , sizeof(u16) }, + { &BG0VOFS , sizeof(u16) }, + { &BG1HOFS , sizeof(u16) }, + { &BG1VOFS , sizeof(u16) }, + { &BG2HOFS , sizeof(u16) }, + { &BG2VOFS , sizeof(u16) }, + { &BG3HOFS , sizeof(u16) }, + { &BG3VOFS , sizeof(u16) }, + { &BG2PA , sizeof(u16) }, + { &BG2PB , sizeof(u16) }, + { &BG2PC , sizeof(u16) }, + { &BG2PD , sizeof(u16) }, + { &BG2X_L , sizeof(u16) }, + { &BG2X_H , sizeof(u16) }, + { &BG2Y_L , sizeof(u16) }, + { &BG2Y_H , sizeof(u16) }, + { &BG3PA , sizeof(u16) }, + { &BG3PB , sizeof(u16) }, + { &BG3PC , sizeof(u16) }, + { &BG3PD , sizeof(u16) }, + { &BG3X_L , sizeof(u16) }, + { &BG3X_H , sizeof(u16) }, + { &BG3Y_L , sizeof(u16) }, + { &BG3Y_H , sizeof(u16) }, + { &WIN0H , sizeof(u16) }, + { &WIN1H , sizeof(u16) }, + { &WIN0V , sizeof(u16) }, + { &WIN1V , sizeof(u16) }, + { &WININ , sizeof(u16) }, + { &WINOUT , sizeof(u16) }, + { &MOSAIC , sizeof(u16) }, + { &BLDMOD , sizeof(u16) }, + { &COLEV , sizeof(u16) }, + { &COLY , sizeof(u16) }, + { &DM0SAD_L , sizeof(u16) }, + { &DM0SAD_H , sizeof(u16) }, + { &DM0DAD_L , sizeof(u16) }, + { &DM0DAD_H , sizeof(u16) }, + { &DM0CNT_L , sizeof(u16) }, + { &DM0CNT_H , sizeof(u16) }, + { &DM1SAD_L , sizeof(u16) }, + { &DM1SAD_H , sizeof(u16) }, + { &DM1DAD_L , sizeof(u16) }, + { &DM1DAD_H , sizeof(u16) }, + { &DM1CNT_L , sizeof(u16) }, + { &DM1CNT_H , sizeof(u16) }, + { &DM2SAD_L , sizeof(u16) }, + { &DM2SAD_H , sizeof(u16) }, + { &DM2DAD_L , sizeof(u16) }, + { &DM2DAD_H , sizeof(u16) }, + { &DM2CNT_L , sizeof(u16) }, + { &DM2CNT_H , sizeof(u16) }, + { &DM3SAD_L , sizeof(u16) }, + { &DM3SAD_H , sizeof(u16) }, + { &DM3DAD_L , sizeof(u16) }, + { &DM3DAD_H , sizeof(u16) }, + { &DM3CNT_L , sizeof(u16) }, + { &DM3CNT_H , sizeof(u16) }, + { &TM0D , sizeof(u16) }, + { &TM0CNT , sizeof(u16) }, + { &TM1D , sizeof(u16) }, + { &TM1CNT , sizeof(u16) }, + { &TM2D , sizeof(u16) }, + { &TM2CNT , sizeof(u16) }, + { &TM3D , sizeof(u16) }, + { &TM3CNT , sizeof(u16) }, + { &P1 , sizeof(u16) }, + { &IE , sizeof(u16) }, + { &IF , sizeof(u16) }, + { &IME , sizeof(u16) }, + { &holdState, sizeof(bool) }, + { &holdType, sizeof(int) }, + { &lcdTicks, sizeof(int) }, + { &timer0On , sizeof(bool) }, + { &timer0Ticks , sizeof(int) }, + { &timer0Reload , sizeof(int) }, + { &timer0ClockReload , sizeof(int) }, + { &timer1On , sizeof(bool) }, + { &timer1Ticks , sizeof(int) }, + { &timer1Reload , sizeof(int) }, + { &timer1ClockReload , sizeof(int) }, + { &timer2On , sizeof(bool) }, + { &timer2Ticks , sizeof(int) }, + { &timer2Reload , sizeof(int) }, + { &timer2ClockReload , sizeof(int) }, + { &timer3On , sizeof(bool) }, + { &timer3Ticks , sizeof(int) }, + { &timer3Reload , sizeof(int) }, + { &timer3ClockReload , sizeof(int) }, + { &dma0Source , sizeof(u32) }, + { &dma0Dest , sizeof(u32) }, + { &dma1Source , sizeof(u32) }, + { &dma1Dest , sizeof(u32) }, + { &dma2Source , sizeof(u32) }, + { &dma2Dest , sizeof(u32) }, + { &dma3Source , sizeof(u32) }, + { &dma3Dest , sizeof(u32) }, + { &fxOn, sizeof(bool) }, + { &windowOn, sizeof(bool) }, + { &N_FLAG , sizeof(bool) }, + { &C_FLAG , sizeof(bool) }, + { &Z_FLAG , sizeof(bool) }, + { &V_FLAG , sizeof(bool) }, + { &armState , sizeof(bool) }, + { &armIrqEnable , sizeof(bool) }, + { &armNextPC , sizeof(u32) }, + { &armMode , sizeof(int) }, + { &saveType , sizeof(int) }, + { NULL, 0 } +}; + +static int romSize = 0x2000000; + +#ifdef PROFILING +void cpuProfil(profile_segment *seg) +{ + profilSegment = seg; +} + +void cpuEnableProfiling(int hz) +{ + if(hz == 0) + hz = 100; + profilingTicks = profilingTicksReload = 16777216 / hz; + profSetHertz(hz); +} +#endif + + +inline int CPUUpdateTicks() +{ + int cpuLoopTicks = lcdTicks; + + if(soundTicks < cpuLoopTicks) + cpuLoopTicks = soundTicks; + + if(timer0On && (timer0Ticks < cpuLoopTicks)) { + cpuLoopTicks = timer0Ticks; + } + if(timer1On && !(TM1CNT & 4) && (timer1Ticks < cpuLoopTicks)) { + cpuLoopTicks = timer1Ticks; + } + if(timer2On && !(TM2CNT & 4) && (timer2Ticks < cpuLoopTicks)) { + cpuLoopTicks = timer2Ticks; + } + if(timer3On && !(TM3CNT & 4) && (timer3Ticks < cpuLoopTicks)) { + cpuLoopTicks = timer3Ticks; + } +#ifdef PROFILING + if(profilingTicksReload != 0) { + if(profilingTicks < cpuLoopTicks) { + cpuLoopTicks = profilingTicks; + } + } +#endif + + if (SWITicks) { + if (SWITicks < cpuLoopTicks) + cpuLoopTicks = SWITicks; + } + + if (IRQTicks) { + if (IRQTicks < cpuLoopTicks) + cpuLoopTicks = IRQTicks; + } + + return cpuLoopTicks; +} + +void CPUUpdateWindow0() +{ + int x00 = WIN0H>>8; + int x01 = WIN0H & 255; + + if(x00 <= x01) { + for(int i = 0; i < 240; i++) { + gfxInWin0[i] = (i >= x00 && i < x01); + } + } else { + for(int i = 0; i < 240; i++) { + gfxInWin0[i] = (i >= x00 || i < x01); + } + } +} + +void CPUUpdateWindow1() +{ + int x00 = WIN1H>>8; + int x01 = WIN1H & 255; + + if(x00 <= x01) { + for(int i = 0; i < 240; i++) { + gfxInWin1[i] = (i >= x00 && i < x01); + } + } else { + for(int i = 0; i < 240; i++) { + gfxInWin1[i] = (i >= x00 || i < x01); + } + } +} + +extern u32 line0[240]; +extern u32 line1[240]; +extern u32 line2[240]; +extern u32 line3[240]; + +#define CLEAR_ARRAY(a) \ + {\ + u32 *array = (a);\ + for(int i = 0; i < 240; i++) {\ + *array++ = 0x80000000;\ + }\ + }\ + +void CPUUpdateRenderBuffers(bool force) +{ + if(!(layerEnable & 0x0100) || force) { + CLEAR_ARRAY(line0); + } + if(!(layerEnable & 0x0200) || force) { + CLEAR_ARRAY(line1); + } + if(!(layerEnable & 0x0400) || force) { + CLEAR_ARRAY(line2); + } + if(!(layerEnable & 0x0800) || force) { + CLEAR_ARRAY(line3); + } +} + +static bool CPUWriteState(gzFile gzFile) +{ + utilWriteInt(gzFile, SAVE_GAME_VERSION); + + utilGzWrite(gzFile, &rom[0xa0], 16); + + utilWriteInt(gzFile, useBios); + + utilGzWrite(gzFile, ®[0], sizeof(reg)); + + utilWriteData(gzFile, saveGameStruct); + + // new to version 0.7.1 + utilWriteInt(gzFile, stopState); + // new to version 0.8 + utilWriteInt(gzFile, IRQTicks); + + utilGzWrite(gzFile, internalRAM, 0x8000); + utilGzWrite(gzFile, paletteRAM, 0x400); + utilGzWrite(gzFile, workRAM, 0x40000); + utilGzWrite(gzFile, vram, 0x20000); + utilGzWrite(gzFile, oam, 0x400); + utilGzWrite(gzFile, pix, 4*241*162); + utilGzWrite(gzFile, ioMem, 0x400); + + eepromSaveGame(gzFile); + flashSaveGame(gzFile); + soundSaveGame(gzFile); + + cheatsSaveGame(gzFile); + + // version 1.5 + rtcSaveGame(gzFile); + + return true; +} + +bool CPUWriteState(const char *file) +{ + gzFile gzFile = utilGzOpen(file, "wb"); + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), file); + return false; + } + + bool res = CPUWriteState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool CPUWriteMemState(char *memory, int available) +{ + gzFile gzFile = utilMemGzOpen(memory, available, "w"); + + if(gzFile == NULL) { + return false; + } + + bool res = CPUWriteState(gzFile); + + long pos = utilGzMemTell(gzFile)+8; + + if(pos >= (available)) + res = false; + + utilGzClose(gzFile); + + return res; +} + +static bool CPUReadState(gzFile gzFile) +{ + int version = utilReadInt(gzFile); + + if(version > SAVE_GAME_VERSION || version < SAVE_GAME_VERSION_1) { + systemMessage(MSG_UNSUPPORTED_VBA_SGM, + N_("Unsupported VisualBoyAdvance save game version %d"), + version); + return false; + } + + u8 romname[17]; + + utilGzRead(gzFile, romname, 16); + + if(memcmp(&rom[0xa0], romname, 16) != 0) { + romname[16]=0; + for(int i = 0; i < 16; i++) + if(romname[i] < 32) + romname[i] = 32; + systemMessage(MSG_CANNOT_LOAD_SGM, N_("Cannot load save game for %s"), romname); + return false; + } + + bool ub = utilReadInt(gzFile) ? true : false; + + if(ub != useBios) { + if(useBios) + systemMessage(MSG_SAVE_GAME_NOT_USING_BIOS, + N_("Save game is not using the BIOS files")); + else + systemMessage(MSG_SAVE_GAME_USING_BIOS, + N_("Save game is using the BIOS file")); + return false; + } + + utilGzRead(gzFile, ®[0], sizeof(reg)); + + utilReadData(gzFile, saveGameStruct); + + if(version < SAVE_GAME_VERSION_3) + stopState = false; + else + stopState = utilReadInt(gzFile) ? true : false; + + if(version < SAVE_GAME_VERSION_4) + { + IRQTicks = 0; + intState = false; + } + else + { + IRQTicks = utilReadInt(gzFile); + if (IRQTicks>0) + intState = true; + else + { + intState = false; + IRQTicks = 0; + } + } + + utilGzRead(gzFile, internalRAM, 0x8000); + utilGzRead(gzFile, paletteRAM, 0x400); + utilGzRead(gzFile, workRAM, 0x40000); + utilGzRead(gzFile, vram, 0x20000); + utilGzRead(gzFile, oam, 0x400); + if(version < SAVE_GAME_VERSION_6) + utilGzRead(gzFile, pix, 4*240*160); + else + utilGzRead(gzFile, pix, 4*241*162); + utilGzRead(gzFile, ioMem, 0x400); + + eepromReadGame(gzFile, version); + flashReadGame(gzFile, version); + soundReadGame(gzFile, version); + + if(version > SAVE_GAME_VERSION_1) { + cheatsReadGame(gzFile, version); + } + if(version > SAVE_GAME_VERSION_6) { + rtcReadGame(gzFile); + } + + if(version <= SAVE_GAME_VERSION_7) { + u32 temp; +#define SWAP(a,b,c) \ + temp = (a);\ + (a) = (b)<<16|(c);\ + (b) = (temp) >> 16;\ + (c) = (temp) & 0xFFFF; + + SWAP(dma0Source, DM0SAD_H, DM0SAD_L); + SWAP(dma0Dest, DM0DAD_H, DM0DAD_L); + SWAP(dma1Source, DM1SAD_H, DM1SAD_L); + SWAP(dma1Dest, DM1DAD_H, DM1DAD_L); + SWAP(dma2Source, DM2SAD_H, DM2SAD_L); + SWAP(dma2Dest, DM2DAD_H, DM2DAD_L); + SWAP(dma3Source, DM3SAD_H, DM3SAD_L); + SWAP(dma3Dest, DM3DAD_H, DM3DAD_L); + } + + if(version <= SAVE_GAME_VERSION_8) { + timer0ClockReload = TIMER_TICKS[TM0CNT & 3]; + timer1ClockReload = TIMER_TICKS[TM1CNT & 3]; + timer2ClockReload = TIMER_TICKS[TM2CNT & 3]; + timer3ClockReload = TIMER_TICKS[TM3CNT & 3]; + + timer0Ticks = ((0x10000 - TM0D) << timer0ClockReload) - timer0Ticks; + timer1Ticks = ((0x10000 - TM1D) << timer1ClockReload) - timer1Ticks; + timer2Ticks = ((0x10000 - TM2D) << timer2ClockReload) - timer2Ticks; + timer3Ticks = ((0x10000 - TM3D) << timer3ClockReload) - timer3Ticks; + interp_rate(); + } + + // set pointers! + layerEnable = layerSettings & DISPCNT; + + CPUUpdateRender(); + CPUUpdateRenderBuffers(true); + CPUUpdateWindow0(); + CPUUpdateWindow1(); + gbaSaveType = 0; + switch(saveType) { + case 0: + cpuSaveGameFunc = flashSaveDecide; + break; + case 1: + cpuSaveGameFunc = sramWrite; + gbaSaveType = 1; + break; + case 2: + cpuSaveGameFunc = flashWrite; + gbaSaveType = 2; + break; + case 3: + break; + case 5: + gbaSaveType = 5; + break; + default: + systemMessage(MSG_UNSUPPORTED_SAVE_TYPE, + N_("Unsupported save type %d"), saveType); + break; + } + if(eepromInUse) + gbaSaveType = 3; + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + if(armState) { + ARM_PREFETCH; + } else { + THUMB_PREFETCH; + } + + CPUUpdateRegister(0x204, CPUReadHalfWordQuick(0x4000204)); + + return true; +} + +bool CPUReadMemState(char *memory, int available) +{ + gzFile gzFile = utilMemGzOpen(memory, available, "r"); + + bool res = CPUReadState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool CPUReadState(const char * file) +{ + gzFile gzFile = utilGzOpen(file, "rb"); + + if(gzFile == NULL) + return false; + + bool res = CPUReadState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool CPUExportEepromFile(const char *fileName) +{ + if(eepromInUse) { + FILE *file = fopen(fileName, "wb"); + + if(!file) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), + fileName); + return false; + } + + for(int i = 0; i < eepromSize;) { + for(int j = 0; j < 8; j++) { + if(fwrite(&eepromData[i+7-j], 1, 1, file) != 1) { + fclose(file); + return false; + } + } + i += 8; + } + fclose(file); + } + return true; +} + +bool CPUWriteBatteryFile(const char *fileName) +{ + if(gbaSaveType == 0) { + if(eepromInUse) + gbaSaveType = 3; + else switch(saveType) { + case 1: + gbaSaveType = 1; + break; + case 2: + gbaSaveType = 2; + break; + } + } + + if((gbaSaveType) && (gbaSaveType!=5)) { + FILE *file = fopen(fileName, "wb"); + + if(!file) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), + fileName); + return false; + } + + // only save if Flash/Sram in use or EEprom in use + if(gbaSaveType != 3) { + if(gbaSaveType == 2) { + if(fwrite(flashSaveMemory, 1, flashSize, file) != (size_t)flashSize) { + fclose(file); + return false; + } + } else { + if(fwrite(flashSaveMemory, 1, 0x10000, file) != 0x10000) { + fclose(file); + return false; + } + } + } else { + if(fwrite(eepromData, 1, eepromSize, file) != (size_t)eepromSize) { + fclose(file); + return false; + } + } + fclose(file); + } + return true; +} + +bool CPUReadGSASnapshot(const char *fileName) +{ + int i; + FILE *file = fopen(fileName, "rb"); + + if(!file) { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); + return false; + } + + // check file size to know what we should read + fseek(file, 0, SEEK_END); + + // long size = ftell(file); + fseek(file, 0x0, SEEK_SET); + fread(&i, 1, 4, file); + fseek(file, i, SEEK_CUR); // Skip SharkPortSave + fseek(file, 4, SEEK_CUR); // skip some sort of flag + fread(&i, 1, 4, file); // name length + fseek(file, i, SEEK_CUR); // skip name + fread(&i, 1, 4, file); // desc length + fseek(file, i, SEEK_CUR); // skip desc + fread(&i, 1, 4, file); // notes length + fseek(file, i, SEEK_CUR); // skip notes + int saveSize; + fread(&saveSize, 1, 4, file); // read length + saveSize -= 0x1c; // remove header size + char buffer[17]; + char buffer2[17]; + fread(buffer, 1, 16, file); + buffer[16] = 0; + for(i = 0; i < 16; i++) + if(buffer[i] < 32) + buffer[i] = 32; + memcpy(buffer2, &rom[0xa0], 16); + buffer2[16] = 0; + for(i = 0; i < 16; i++) + if(buffer2[i] < 32) + buffer2[i] = 32; + if(memcmp(buffer, buffer2, 16)) { + systemMessage(MSG_CANNOT_IMPORT_SNAPSHOT_FOR, + N_("Cannot import snapshot for %s. Current game is %s"), + buffer, + buffer2); + fclose(file); + return false; + } + fseek(file, 12, SEEK_CUR); // skip some flags + if(saveSize >= 65536) { + if(fread(flashSaveMemory, 1, saveSize, file) != (size_t)saveSize) { + fclose(file); + return false; + } + } else { + systemMessage(MSG_UNSUPPORTED_SNAPSHOT_FILE, + N_("Unsupported snapshot file %s"), + fileName); + fclose(file); + return false; + } + fclose(file); + CPUReset(); + return true; +} + +bool CPUWriteGSASnapshot(const char *fileName, + const char *title, + const char *desc, + const char *notes) +{ + FILE *file = fopen(fileName, "wb"); + + if(!file) { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); + return false; + } + + u8 buffer[17]; + + utilPutDword(buffer, 0x0d); // SharkPortSave length + fwrite(buffer, 1, 4, file); + fwrite("SharkPortSave", 1, 0x0d, file); + utilPutDword(buffer, 0x000f0000); + fwrite(buffer, 1, 4, file); // save type 0x000f0000 = GBA save + utilPutDword(buffer, (u32)strlen(title)); + fwrite(buffer, 1, 4, file); // title length + fwrite(title, 1, strlen(title), file); + utilPutDword(buffer, (u32)strlen(desc)); + fwrite(buffer, 1, 4, file); // desc length + fwrite(desc, 1, strlen(desc), file); + utilPutDword(buffer, (u32)strlen(notes)); + fwrite(buffer, 1, 4, file); // notes length + fwrite(notes, 1, strlen(notes), file); + int saveSize = 0x10000; + if(gbaSaveType == 2) + saveSize = flashSize; + int totalSize = saveSize + 0x1c; + + utilPutDword(buffer, totalSize); // length of remainder of save - CRC + fwrite(buffer, 1, 4, file); + + char temp[0x2001c]; + memset(temp, 0, 28); + memcpy(temp, &rom[0xa0], 16); // copy internal name + temp[0x10] = rom[0xbe]; // reserved area (old checksum) + temp[0x11] = rom[0xbf]; // reserved area (old checksum) + temp[0x12] = rom[0xbd]; // complement check + temp[0x13] = rom[0xb0]; // maker + temp[0x14] = 1; // 1 save ? + memcpy(&temp[0x1c], flashSaveMemory, saveSize); // copy save + fwrite(temp, 1, totalSize, file); // write save + header + u32 crc = 0; + + for(int i = 0; i < totalSize; i++) { + crc += ((u32)temp[i] << (crc % 0x18)); + } + + utilPutDword(buffer, crc); + fwrite(buffer, 1, 4, file); // CRC? + + fclose(file); + return true; +} + +bool CPUImportEepromFile(const char *fileName) +{ + FILE *file = fopen(fileName, "rb"); + + if(!file) + return false; + + // check file size to know what we should read + fseek(file, 0, SEEK_END); + + long size = ftell(file); + fseek(file, 0, SEEK_SET); + if(size == 512 || size == 0x2000) { + if(fread(eepromData, 1, size, file) != (size_t)size) { + fclose(file); + return false; + } + for(int i = 0; i < size;) { + u8 tmp = eepromData[i]; + eepromData[i] = eepromData[7-i]; + eepromData[7-i] = tmp; + i++; + tmp = eepromData[i]; + eepromData[i] = eepromData[7-i]; + eepromData[7-i] = tmp; + i++; + tmp = eepromData[i]; + eepromData[i] = eepromData[7-i]; + eepromData[7-i] = tmp; + i++; + tmp = eepromData[i]; + eepromData[i] = eepromData[7-i]; + eepromData[7-i] = tmp; + i++; + i += 4; + } + } else + return false; + fclose(file); + return true; +} + +bool CPUReadBatteryFile(const char *fileName) +{ + FILE *file = fopen(fileName, "rb"); + + if(!file) + return false; + + // check file size to know what we should read + fseek(file, 0, SEEK_END); + + long size = ftell(file); + fseek(file, 0, SEEK_SET); + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + if(size == 512 || size == 0x2000) { + if(fread(eepromData, 1, size, file) != (size_t)size) { + fclose(file); + return false; + } + } else { + if(size == 0x20000) { + if(fread(flashSaveMemory, 1, 0x20000, file) != 0x20000) { + fclose(file); + return false; + } + flashSetSize(0x20000); + } else { + if(fread(flashSaveMemory, 1, 0x10000, file) != 0x10000) { + fclose(file); + return false; + } + flashSetSize(0x10000); + } + } + fclose(file); + return true; +} + +bool CPUWritePNGFile(const char *fileName) +{ + return utilWritePNGFile(fileName, 240, 160, pix); +} + +bool CPUWriteBMPFile(const char *fileName) +{ + return utilWriteBMPFile(fileName, 240, 160, pix); +} + +bool CPUIsZipFile(const char * file) +{ + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".zip") == 0) + return true; + } + } + + return false; +} + +bool CPUIsGBAImage(const char * file) +{ + cpuIsMultiBoot = false; + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".gba") == 0) + return true; + if(_stricmp(p, ".agb") == 0) + return true; + if(_stricmp(p, ".bin") == 0) + return true; + if(_stricmp(p, ".elf") == 0) + return true; + if(_stricmp(p, ".mb") == 0) { + cpuIsMultiBoot = true; + return true; + } + } + } + + return false; +} + +bool CPUIsGBABios(const char * file) +{ + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".gba") == 0) + return true; + if(_stricmp(p, ".agb") == 0) + return true; + if(_stricmp(p, ".bin") == 0) + return true; + if(_stricmp(p, ".bios") == 0) + return true; + if(_stricmp(p, ".rom") == 0) + return true; + } + } + + return false; +} + +bool CPUIsELF(const char *file) +{ + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".elf") == 0) + return true; + } + } + return false; +} + +void CPUCleanUp() +{ +#ifdef PROFILING + if(profilingTicksReload) { + profCleanup(); + } +#endif + + if(rom != NULL) { + free(rom); + rom = NULL; + } + + if(vram != NULL) { + free(vram); + vram = NULL; + } + + if(paletteRAM != NULL) { + free(paletteRAM); + paletteRAM = NULL; + } + + if(internalRAM != NULL) { + free(internalRAM); + internalRAM = NULL; + } + + if(workRAM != NULL) { + free(workRAM); + workRAM = NULL; + } + + if(bios != NULL) { + free(bios); + bios = NULL; + } + + if(pix != NULL) { + free(pix); + pix = NULL; + } + + if(oam != NULL) { + free(oam); + oam = NULL; + } + + if(ioMem != NULL) { + free(ioMem); + ioMem = NULL; + } + + elfCleanUp(); + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + emulating = 0; +} + +int CPULoadRom(const char *szFile) +{ + romSize = 0x2000000; + if(rom != NULL) { + CPUCleanUp(); + } + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + rom = (u8 *)malloc(0x2000000); + if(rom == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "ROM"); + return 0; + } + workRAM = (u8 *)calloc(1, 0x40000); + if(workRAM == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "WRAM"); + return 0; + } + + u8 *whereToLoad = cpuIsMultiBoot ? workRAM : rom; + + if(CPUIsELF(szFile)) { + FILE *f = fopen(szFile, "rb"); + if(!f) { + systemMessage(MSG_ERROR_OPENING_IMAGE, N_("Error opening image %s"), + szFile); + free(rom); + rom = NULL; + free(workRAM); + workRAM = NULL; + return 0; + } + bool res = elfRead(szFile, romSize, f); + if(!res || romSize == 0) { + free(rom); + rom = NULL; + free(workRAM); + workRAM = NULL; + elfCleanUp(); + return 0; + } + } else if(!utilLoad(szFile, + utilIsGBAImage, + whereToLoad, + romSize)) { + free(rom); + rom = NULL; + free(workRAM); + workRAM = NULL; + return 0; + } + + u16 *temp = (u16 *)(rom+((romSize+1)&~1)); + int i; + for(i = (romSize+1)&~1; i < 0x2000000; i+=2) { + WRITE16LE(temp, (i >> 1) & 0xFFFF); + temp++; + } + + bios = (u8 *)calloc(1,0x4000); + if(bios == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "BIOS"); + CPUCleanUp(); + return 0; + } + internalRAM = (u8 *)calloc(1,0x8000); + if(internalRAM == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "IRAM"); + CPUCleanUp(); + return 0; + } + paletteRAM = (u8 *)calloc(1,0x400); + if(paletteRAM == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "PRAM"); + CPUCleanUp(); + return 0; + } + vram = (u8 *)calloc(1, 0x20000); + if(vram == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "VRAM"); + CPUCleanUp(); + return 0; + } + oam = (u8 *)calloc(1, 0x400); + if(oam == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "OAM"); + CPUCleanUp(); + return 0; + } + pix = (u8 *)calloc(1, 4 * 241 * 162); + if(pix == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "PIX"); + CPUCleanUp(); + return 0; + } + ioMem = (u8 *)calloc(1, 0x400); + if(ioMem == NULL) { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "IO"); + CPUCleanUp(); + return 0; + } + + flashInit(); + eepromInit(); + + CPUUpdateRenderBuffers(true); + + return romSize; +} + +void doMirroring (bool b) +{ + u32 mirroredRomSize = (((romSize)>>20) & 0x3F)<<20; + u32 mirroredRomAddress = romSize; + if ((mirroredRomSize <=0x800000) && (b)) + { + mirroredRomAddress = mirroredRomSize; + if (mirroredRomSize==0) + mirroredRomSize=0x100000; + while (mirroredRomAddress<0x01000000) + { + memcpy ((u16 *)(rom+mirroredRomAddress), (u16 *)(rom), mirroredRomSize); + mirroredRomAddress+=mirroredRomSize; + } + } +} + +void CPUUpdateRender() +{ + switch(DISPCNT & 7) { + case 0: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode0RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode0RenderLineNoWindow; + else + renderLine = mode0RenderLineAll; + break; + case 1: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode1RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode1RenderLineNoWindow; + else + renderLine = mode1RenderLineAll; + break; + case 2: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode2RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode2RenderLineNoWindow; + else + renderLine = mode2RenderLineAll; + break; + case 3: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode3RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode3RenderLineNoWindow; + else + renderLine = mode3RenderLineAll; + break; + case 4: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode4RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode4RenderLineNoWindow; + else + renderLine = mode4RenderLineAll; + break; + case 5: + if((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode5RenderLine; + else if(fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode5RenderLineNoWindow; + else + renderLine = mode5RenderLineAll; + default: + break; + } +} + +void CPUUpdateCPSR() +{ + u32 CPSR = reg[16].I & 0x40; + if(N_FLAG) + CPSR |= 0x80000000; + if(Z_FLAG) + CPSR |= 0x40000000; + if(C_FLAG) + CPSR |= 0x20000000; + if(V_FLAG) + CPSR |= 0x10000000; + if(!armState) + CPSR |= 0x00000020; + if(!armIrqEnable) + CPSR |= 0x80; + CPSR |= (armMode & 0x1F); + reg[16].I = CPSR; +} + +void CPUUpdateFlags(bool breakLoop) +{ + u32 CPSR = reg[16].I; + + N_FLAG = (CPSR & 0x80000000) ? true: false; + Z_FLAG = (CPSR & 0x40000000) ? true: false; + C_FLAG = (CPSR & 0x20000000) ? true: false; + V_FLAG = (CPSR & 0x10000000) ? true: false; + armState = (CPSR & 0x20) ? false : true; + armIrqEnable = (CPSR & 0x80) ? false : true; + if(breakLoop) { + if (armIrqEnable && (IF & IE) && (IME & 1)) + cpuNextEvent = cpuTotalTicks; + } +} + +void CPUUpdateFlags() +{ + CPUUpdateFlags(true); +} + +#ifdef WORDS_BIGENDIAN +static void CPUSwap(volatile u32 *a, volatile u32 *b) +{ + volatile u32 c = *b; + *b = *a; + *a = c; +} +#else +static void CPUSwap(u32 *a, u32 *b) +{ + u32 c = *b; + *b = *a; + *a = c; +} +#endif + +void CPUSwitchMode(int mode, bool saveState, bool breakLoop) +{ + // if(armMode == mode) + // return; + + CPUUpdateCPSR(); + + switch(armMode) { + case 0x10: + case 0x1F: + reg[R13_USR].I = reg[13].I; + reg[R14_USR].I = reg[14].I; + reg[17].I = reg[16].I; + break; + case 0x11: + CPUSwap(®[R8_FIQ].I, ®[8].I); + CPUSwap(®[R9_FIQ].I, ®[9].I); + CPUSwap(®[R10_FIQ].I, ®[10].I); + CPUSwap(®[R11_FIQ].I, ®[11].I); + CPUSwap(®[R12_FIQ].I, ®[12].I); + reg[R13_FIQ].I = reg[13].I; + reg[R14_FIQ].I = reg[14].I; + reg[SPSR_FIQ].I = reg[17].I; + break; + case 0x12: + reg[R13_IRQ].I = reg[13].I; + reg[R14_IRQ].I = reg[14].I; + reg[SPSR_IRQ].I = reg[17].I; + break; + case 0x13: + reg[R13_SVC].I = reg[13].I; + reg[R14_SVC].I = reg[14].I; + reg[SPSR_SVC].I = reg[17].I; + break; + case 0x17: + reg[R13_ABT].I = reg[13].I; + reg[R14_ABT].I = reg[14].I; + reg[SPSR_ABT].I = reg[17].I; + break; + case 0x1b: + reg[R13_UND].I = reg[13].I; + reg[R14_UND].I = reg[14].I; + reg[SPSR_UND].I = reg[17].I; + break; + } + + u32 CPSR = reg[16].I; + u32 SPSR = reg[17].I; + + switch(mode) { + case 0x10: + case 0x1F: + reg[13].I = reg[R13_USR].I; + reg[14].I = reg[R14_USR].I; + reg[16].I = SPSR; + break; + case 0x11: + CPUSwap(®[8].I, ®[R8_FIQ].I); + CPUSwap(®[9].I, ®[R9_FIQ].I); + CPUSwap(®[10].I, ®[R10_FIQ].I); + CPUSwap(®[11].I, ®[R11_FIQ].I); + CPUSwap(®[12].I, ®[R12_FIQ].I); + reg[13].I = reg[R13_FIQ].I; + reg[14].I = reg[R14_FIQ].I; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_FIQ].I; + break; + case 0x12: + reg[13].I = reg[R13_IRQ].I; + reg[14].I = reg[R14_IRQ].I; + reg[16].I = SPSR; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_IRQ].I; + break; + case 0x13: + reg[13].I = reg[R13_SVC].I; + reg[14].I = reg[R14_SVC].I; + reg[16].I = SPSR; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_SVC].I; + break; + case 0x17: + reg[13].I = reg[R13_ABT].I; + reg[14].I = reg[R14_ABT].I; + reg[16].I = SPSR; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_ABT].I; + break; + case 0x1b: + reg[13].I = reg[R13_UND].I; + reg[14].I = reg[R14_UND].I; + reg[16].I = SPSR; + if(saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_UND].I; + break; + default: + systemMessage(MSG_UNSUPPORTED_ARM_MODE, N_("Unsupported ARM mode %02x"), mode); + break; + } + armMode = mode; + CPUUpdateFlags(breakLoop); + CPUUpdateCPSR(); +} + +void CPUSwitchMode(int mode, bool saveState) +{ + CPUSwitchMode(mode, saveState, true); +} + +void CPUUndefinedException() +{ + u32 PC = reg[15].I; + bool savedArmState = armState; + CPUSwitchMode(0x1b, true, false); + reg[14].I = PC - (savedArmState ? 4 : 2); + reg[15].I = 0x04; + armState = true; + armIrqEnable = false; + armNextPC = 0x04; + ARM_PREFETCH; + reg[15].I += 4; +} + +void CPUSoftwareInterrupt() +{ + u32 PC = reg[15].I; + bool savedArmState = armState; + CPUSwitchMode(0x13, true, false); + reg[14].I = PC - (savedArmState ? 4 : 2); + reg[15].I = 0x08; + armState = true; + armIrqEnable = false; + armNextPC = 0x08; + ARM_PREFETCH; + reg[15].I += 4; +} + +void CPUSoftwareInterrupt(int comment) +{ + static bool disableMessage = false; + if(armState) comment >>= 16; +#ifdef BKPT_SUPPORT + if(comment == 0xff) { + extern void (*dbgOutput)(const char *, u32); + dbgOutput(NULL, reg[0].I); + return; + } +#endif +#ifdef PROFILING + if(comment == 0xfe) { + profStartup(reg[0].I, reg[1].I); + return; + } + if(comment == 0xfd) { + profControl(reg[0].I); + return; + } + if(comment == 0xfc) { + profCleanup(); + return; + } + if(comment == 0xfb) { + profCount(); + return; + } +#endif + if(comment == 0xfa) { + agbPrintFlush(); + return; + } +#ifdef SDL + if(comment == 0xf9) { + emulating = 0; + cpuNextEvent = cpuTotalTicks; + cpuBreakLoop = true; + return; + } +#endif + if(useBios) { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("SWI: %08x at %08x (0x%08x,0x%08x,0x%08x,VCOUNT = %2d)\n", comment, + armState ? armNextPC - 4: armNextPC -2, + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + CPUSoftwareInterrupt(); + return; + } + // This would be correct, but it causes problems if uncommented + // else { + // biosProtected = 0xe3a02004; + // } + + switch(comment) { + case 0x00: + BIOS_SoftReset(); + ARM_PREFETCH; + break; + case 0x01: + BIOS_RegisterRamReset(); + break; + case 0x02: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("Halt: (VCOUNT = %2d)\n", + VCOUNT); + } +#endif + holdState = true; + holdType = -1; + cpuNextEvent = cpuTotalTicks; + break; + case 0x03: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("Stop: (VCOUNT = %2d)\n", + VCOUNT); + } +#endif + holdState = true; + holdType = -1; + stopState = true; + cpuNextEvent = cpuTotalTicks; + break; + case 0x04: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("IntrWait: 0x%08x,0x%08x (VCOUNT = %2d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + CPUSoftwareInterrupt(); + break; + case 0x05: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("VBlankIntrWait: (VCOUNT = %2d)\n", + VCOUNT); + } +#endif + CPUSoftwareInterrupt(); + break; + case 0x06: + CPUSoftwareInterrupt(); + break; + case 0x07: + CPUSoftwareInterrupt(); + break; + case 0x08: + BIOS_Sqrt(); + break; + case 0x09: + BIOS_ArcTan(); + break; + case 0x0A: + BIOS_ArcTan2(); + break; + case 0x0B: + { + int len = (reg[2].I & 0x1FFFFF) >>1; + if (!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + len) & 0xe000000) == 0)) + { + if ((reg[2].I >> 24) & 1) + { + if ((reg[2].I >> 26) & 1) + SWITicks = (7 + memoryWait32[(reg[1].I>>24) & 0xF]) * (len>>1); + else + SWITicks = (8 + memoryWait[(reg[1].I>>24) & 0xF]) * (len); + } + else + { + if ((reg[2].I >> 26) & 1) + SWITicks = (10 + memoryWait32[(reg[0].I>>24) & 0xF] + + memoryWait32[(reg[1].I>>24) & 0xF]) * (len>>1); + else + SWITicks = (11 + memoryWait[(reg[0].I>>24) & 0xF] + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + } + } + BIOS_CpuSet(); + break; + case 0x0C: + { + int len = (reg[2].I & 0x1FFFFF) >>5; + if (!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + len) & 0xe000000) == 0)) + { + if ((reg[2].I >> 24) & 1) + SWITicks = (6 + memoryWait32[(reg[1].I>>24) & 0xF] + + 7 * (memoryWaitSeq32[(reg[1].I>>24) & 0xF] + 1)) * len; + else + SWITicks = (9 + memoryWait32[(reg[0].I>>24) & 0xF] + + memoryWait32[(reg[1].I>>24) & 0xF] + + 7 * (memoryWaitSeq32[(reg[0].I>>24) & 0xF] + + memoryWaitSeq32[(reg[1].I>>24) & 0xF] + 2)) * len; + } + } + BIOS_CpuFastSet(); + break; + case 0x0D: + BIOS_GetBiosChecksum(); + break; + case 0x0E: + BIOS_BgAffineSet(); + break; + case 0x0F: + BIOS_ObjAffineSet(); + break; + case 0x10: + { + int len = CPUReadHalfWord(reg[2].I); + if (!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + len) & 0xe000000) == 0)) + SWITicks = (32 + memoryWait[(reg[0].I>>24) & 0xF]) * len; + } + BIOS_BitUnPack(); + break; + case 0x11: + { + u32 len = CPUReadMemory(reg[0].I) >> 8; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (9 + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_LZ77UnCompWram(); + break; + case 0x12: + { + u32 len = CPUReadMemory(reg[0].I) >> 8; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (19 + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_LZ77UnCompVram(); + break; + case 0x13: + { + u32 len = CPUReadMemory(reg[0].I) >> 8; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (29 + (memoryWait[(reg[0].I>>24) & 0xF]<<1)) * len; + } + BIOS_HuffUnComp(); + break; + case 0x14: + { + u32 len = CPUReadMemory(reg[0].I) >> 8; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (11 + memoryWait[(reg[0].I>>24) & 0xF] + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_RLUnCompWram(); + break; + case 0x15: + { + u32 len = CPUReadMemory(reg[0].I) >> 9; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (34 + (memoryWait[(reg[0].I>>24) & 0xF] << 1) + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_RLUnCompVram(); + break; + case 0x16: + { + u32 len = CPUReadMemory(reg[0].I) >> 8; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (13 + memoryWait[(reg[0].I>>24) & 0xF] + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_Diff8bitUnFilterWram(); + break; + case 0x17: + { + u32 len = CPUReadMemory(reg[0].I) >> 9; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (39 + (memoryWait[(reg[0].I>>24) & 0xF]<<1) + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_Diff8bitUnFilterVram(); + break; + case 0x18: + { + u32 len = CPUReadMemory(reg[0].I) >> 9; + if(!(((reg[0].I & 0xe000000) == 0) || + ((reg[0].I + (len & 0x1fffff)) & 0xe000000) == 0)) + SWITicks = (13 + memoryWait[(reg[0].I>>24) & 0xF] + + memoryWait[(reg[1].I>>24) & 0xF]) * len; + } + BIOS_Diff16bitUnFilter(); + break; + case 0x19: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("SoundBiasSet: 0x%08x (VCOUNT = %2d)\n", + reg[0].I, + VCOUNT); + } +#endif + if(reg[0].I) + systemSoundPause(); + else + systemSoundResume(); + break; + case 0x1F: + BIOS_MidiKey2Freq(); + break; + case 0x2A: + BIOS_SndDriverJmpTableCopy(); + // let it go, because we don't really emulate this function + default: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("SWI: %08x at %08x (0x%08x,0x%08x,0x%08x,VCOUNT = %2d)\n", comment, + armState ? armNextPC - 4: armNextPC -2, + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + + if(!disableMessage) { + systemMessage(MSG_UNSUPPORTED_BIOS_FUNCTION, + N_("Unsupported BIOS function %02x called from %08x. A BIOS file is needed in order to get correct behaviour."), + comment, + armMode ? armNextPC - 4: armNextPC - 2); + disableMessage = true; + } + break; + } +} + +void CPUCompareVCOUNT() +{ + if(VCOUNT == (DISPSTAT >> 8)) { + DISPSTAT |= 4; + UPDATE_REG(0x04, DISPSTAT); + + if(DISPSTAT & 0x20) { + IF |= 4; + UPDATE_REG(0x202, IF); + } + } else { + DISPSTAT &= 0xFFFB; + UPDATE_REG(0x4, DISPSTAT); + } + if (layerEnableDelay>0) + { + layerEnableDelay--; + if (layerEnableDelay==1) + layerEnable = layerSettings & DISPCNT; + } + +} + +void doDMA(u32 &s, u32 &d, u32 si, u32 di, u32 c, int transfer32) +{ + int sm = s >> 24; + int dm = d >> 24; + int sw = 0; + int dw = 0; + int sc = c; + + cpuDmaCount = c; + // This is done to get the correct waitstates. + if (sm>15) + sm=15; + if (dm>15) + dm=15; + + //if ((sm>=0x05) && (sm<=0x07) || (dm>=0x05) && (dm <=0x07)) + // blank = (((DISPSTAT | ((DISPSTAT>>1)&1))==1) ? true : false); + + if(transfer32) { + s &= 0xFFFFFFFC; + if(s < 0x02000000 && (reg[15].I >> 24)) { + while(c != 0) { + CPUWriteMemory(d, 0); + d += di; + c--; + } + } else { + while(c != 0) { + cpuDmaLast = CPUReadMemory(s); + CPUWriteMemory(d, cpuDmaLast); + d += di; + s += si; + c--; + } + } + } else { + s &= 0xFFFFFFFE; + si = (int)si >> 1; + di = (int)di >> 1; + if(s < 0x02000000 && (reg[15].I >> 24)) { + while(c != 0) { + CPUWriteHalfWord(d, 0); + d += di; + c--; + } + } else { + while(c != 0) { + cpuDmaLast = CPUReadHalfWord(s); + CPUWriteHalfWord(d, cpuDmaLast); + cpuDmaLast |= (cpuDmaLast<<16); + d += di; + s += si; + c--; + } + } + } + + cpuDmaCount = 0; + + int totalTicks = 0; + + if(transfer32) { + sw =1+memoryWaitSeq32[sm & 15]; + dw =1+memoryWaitSeq32[dm & 15]; + totalTicks = (sw+dw)*(sc-1) + 6 + memoryWait32[sm & 15] + + memoryWaitSeq32[dm & 15]; + } + else + { + sw = 1+memoryWaitSeq[sm & 15]; + dw = 1+memoryWaitSeq[dm & 15]; + totalTicks = (sw+dw)*(sc-1) + 6 + memoryWait[sm & 15] + + memoryWaitSeq[dm & 15]; + } + + cpuDmaTicksToUpdate += totalTicks; + +} + +void CPUCheckDMA(int reason, int dmamask) +{ + // DMA 0 + if((DM0CNT_H & 0x8000) && (dmamask & 1)) { + if(((DM0CNT_H >> 12) & 3) == reason) { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch((DM0CNT_H >> 7) & 3) { + case 0: + break; + case 1: + sourceIncrement = (u32)-4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch((DM0CNT_H >> 5) & 3) { + case 0: + break; + case 1: + destIncrement = (u32)-4; + break; + case 2: + destIncrement = 0; + break; + } +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA0) { + int count = (DM0CNT_L ? DM0CNT_L : 0x4000) << 1; + if(DM0CNT_H & 0x0400) + count <<= 1; + log("DMA0: s=%08x d=%08x c=%04x count=%08x\n", dma0Source, dma0Dest, + DM0CNT_H, + count); + } +#endif + doDMA(dma0Source, dma0Dest, sourceIncrement, destIncrement, + DM0CNT_L ? DM0CNT_L : 0x4000, + DM0CNT_H & 0x0400); + cpuDmaHack = true; + + if(DM0CNT_H & 0x4000) { + IF |= 0x0100; + UPDATE_REG(0x202, IF); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM0CNT_H >> 5) & 3) == 3) { + dma0Dest = DM0DAD_L | (DM0DAD_H << 16); + } + + if(!(DM0CNT_H & 0x0200) || (reason == 0)) { + DM0CNT_H &= 0x7FFF; + UPDATE_REG(0xBA, DM0CNT_H); + } + } + } + + // DMA 1 + if((DM1CNT_H & 0x8000) && (dmamask & 2)) { + if(((DM1CNT_H >> 12) & 3) == reason) { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch((DM1CNT_H >> 7) & 3) { + case 0: + break; + case 1: + sourceIncrement = (u32)-4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch((DM1CNT_H >> 5) & 3) { + case 0: + break; + case 1: + destIncrement = (u32)-4; + break; + case 2: + destIncrement = 0; + break; + } + if(reason == 3) { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA1) { + log("DMA1: s=%08x d=%08x c=%04x count=%08x\n", dma1Source, dma1Dest, + DM1CNT_H, + 16); + } +#endif + doDMA(dma1Source, dma1Dest, sourceIncrement, 0, 4, + 0x0400); + } else { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA1) { + int count = (DM1CNT_L ? DM1CNT_L : 0x4000) << 1; + if(DM1CNT_H & 0x0400) + count <<= 1; + log("DMA1: s=%08x d=%08x c=%04x count=%08x\n", dma1Source, dma1Dest, + DM1CNT_H, + count); + } +#endif + doDMA(dma1Source, dma1Dest, sourceIncrement, destIncrement, + DM1CNT_L ? DM1CNT_L : 0x4000, + DM1CNT_H & 0x0400); + } + cpuDmaHack = true; + + if(DM1CNT_H & 0x4000) { + IF |= 0x0200; + UPDATE_REG(0x202, IF); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM1CNT_H >> 5) & 3) == 3) { + dma1Dest = DM1DAD_L | (DM1DAD_H << 16); + } + + if(!(DM1CNT_H & 0x0200) || (reason == 0)) { + DM1CNT_H &= 0x7FFF; + UPDATE_REG(0xC6, DM1CNT_H); + } + } + } + + // DMA 2 + if((DM2CNT_H & 0x8000) && (dmamask & 4)) { + if(((DM2CNT_H >> 12) & 3) == reason) { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch((DM2CNT_H >> 7) & 3) { + case 0: + break; + case 1: + sourceIncrement = (u32)-4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch((DM2CNT_H >> 5) & 3) { + case 0: + break; + case 1: + destIncrement = (u32)-4; + break; + case 2: + destIncrement = 0; + break; + } + if(reason == 3) { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA2) { + int count = (4) << 2; + log("DMA2: s=%08x d=%08x c=%04x count=%08x\n", dma2Source, dma2Dest, + DM2CNT_H, + count); + } +#endif + doDMA(dma2Source, dma2Dest, sourceIncrement, 0, 4, + 0x0400); + } else { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA2) { + int count = (DM2CNT_L ? DM2CNT_L : 0x4000) << 1; + if(DM2CNT_H & 0x0400) + count <<= 1; + log("DMA2: s=%08x d=%08x c=%04x count=%08x\n", dma2Source, dma2Dest, + DM2CNT_H, + count); + } +#endif + doDMA(dma2Source, dma2Dest, sourceIncrement, destIncrement, + DM2CNT_L ? DM2CNT_L : 0x4000, + DM2CNT_H & 0x0400); + } + cpuDmaHack = true; + + if(DM2CNT_H & 0x4000) { + IF |= 0x0400; + UPDATE_REG(0x202, IF); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM2CNT_H >> 5) & 3) == 3) { + dma2Dest = DM2DAD_L | (DM2DAD_H << 16); + } + + if(!(DM2CNT_H & 0x0200) || (reason == 0)) { + DM2CNT_H &= 0x7FFF; + UPDATE_REG(0xD2, DM2CNT_H); + } + } + } + + // DMA 3 + if((DM3CNT_H & 0x8000) && (dmamask & 8)) { + if(((DM3CNT_H >> 12) & 3) == reason) { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch((DM3CNT_H >> 7) & 3) { + case 0: + break; + case 1: + sourceIncrement = (u32)-4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch((DM3CNT_H >> 5) & 3) { + case 0: + break; + case 1: + destIncrement = (u32)-4; + break; + case 2: + destIncrement = 0; + break; + } +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_DMA3) { + int count = (DM3CNT_L ? DM3CNT_L : 0x10000) << 1; + if(DM3CNT_H & 0x0400) + count <<= 1; + log("DMA3: s=%08x d=%08x c=%04x count=%08x\n", dma3Source, dma3Dest, + DM3CNT_H, + count); + } +#endif + doDMA(dma3Source, dma3Dest, sourceIncrement, destIncrement, + DM3CNT_L ? DM3CNT_L : 0x10000, + DM3CNT_H & 0x0400); + if(DM3CNT_H & 0x4000) { + IF |= 0x0800; + UPDATE_REG(0x202, IF); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM3CNT_H >> 5) & 3) == 3) { + dma3Dest = DM3DAD_L | (DM3DAD_H << 16); + } + + if(!(DM3CNT_H & 0x0200) || (reason == 0)) { + DM3CNT_H &= 0x7FFF; + UPDATE_REG(0xDE, DM3CNT_H); + } + } + } +} + +void CPUUpdateRegister(u32 address, u16 value) +{ + switch(address) { + case 0x00: + { + if ((value & 7) >5) + DISPCNT = (value &7); + bool change = ((DISPCNT ^ value) & 0x80) ? true : false; + bool changeBG = ((DISPCNT ^ value) & 0x0F00) ? true : false; + u16 changeBGon = (((~DISPCNT) & value) & 0x0F00); + DISPCNT = (value & 0xFFF7); + UPDATE_REG(0x00, DISPCNT); + + if (changeBGon) + { + layerEnableDelay=4; + layerEnable = layerSettings & value & (~changeBGon); + } + else + layerEnable = layerSettings & value; + // CPUUpdateTicks(); + + windowOn = (layerEnable & 0x6000) ? true : false; + if(change && !((value & 0x80))) { + if(!(DISPSTAT & 1)) { + lcdTicks = 1008; + // VCOUNT = 0; + // UPDATE_REG(0x06, VCOUNT); + DISPSTAT &= 0xFFFC; + UPDATE_REG(0x04, DISPSTAT); + CPUCompareVCOUNT(); + } + // (*renderLine)(); + } + CPUUpdateRender(); + // we only care about changes in BG0-BG3 + if(changeBG) + CPUUpdateRenderBuffers(false); + } + break; + case 0x04: + DISPSTAT = (value & 0xFF38) | (DISPSTAT & 7); + UPDATE_REG(0x04, DISPSTAT); + break; + case 0x06: + // not writable + break; + case 0x08: + BG0CNT = (value & 0xDFCF); + UPDATE_REG(0x08, BG0CNT); + break; + case 0x0A: + BG1CNT = (value & 0xDFCF); + UPDATE_REG(0x0A, BG1CNT); + break; + case 0x0C: + BG2CNT = (value & 0xFFCF); + UPDATE_REG(0x0C, BG2CNT); + break; + case 0x0E: + BG3CNT = (value & 0xFFCF); + UPDATE_REG(0x0E, BG3CNT); + break; + case 0x10: + BG0HOFS = value & 511; + UPDATE_REG(0x10, BG0HOFS); + break; + case 0x12: + BG0VOFS = value & 511; + UPDATE_REG(0x12, BG0VOFS); + break; + case 0x14: + BG1HOFS = value & 511; + UPDATE_REG(0x14, BG1HOFS); + break; + case 0x16: + BG1VOFS = value & 511; + UPDATE_REG(0x16, BG1VOFS); + break; + case 0x18: + BG2HOFS = value & 511; + UPDATE_REG(0x18, BG2HOFS); + break; + case 0x1A: + BG2VOFS = value & 511; + UPDATE_REG(0x1A, BG2VOFS); + break; + case 0x1C: + BG3HOFS = value & 511; + UPDATE_REG(0x1C, BG3HOFS); + break; + case 0x1E: + BG3VOFS = value & 511; + UPDATE_REG(0x1E, BG3VOFS); + break; + case 0x20: + BG2PA = value; + UPDATE_REG(0x20, BG2PA); + break; + case 0x22: + BG2PB = value; + UPDATE_REG(0x22, BG2PB); + break; + case 0x24: + BG2PC = value; + UPDATE_REG(0x24, BG2PC); + break; + case 0x26: + BG2PD = value; + UPDATE_REG(0x26, BG2PD); + break; + case 0x28: + BG2X_L = value; + UPDATE_REG(0x28, BG2X_L); + gfxBG2Changed |= 1; + break; + case 0x2A: + BG2X_H = (value & 0xFFF); + UPDATE_REG(0x2A, BG2X_H); + gfxBG2Changed |= 1; + break; + case 0x2C: + BG2Y_L = value; + UPDATE_REG(0x2C, BG2Y_L); + gfxBG2Changed |= 2; + break; + case 0x2E: + BG2Y_H = value & 0xFFF; + UPDATE_REG(0x2E, BG2Y_H); + gfxBG2Changed |= 2; + break; + case 0x30: + BG3PA = value; + UPDATE_REG(0x30, BG3PA); + break; + case 0x32: + BG3PB = value; + UPDATE_REG(0x32, BG3PB); + break; + case 0x34: + BG3PC = value; + UPDATE_REG(0x34, BG3PC); + break; + case 0x36: + BG3PD = value; + UPDATE_REG(0x36, BG3PD); + break; + case 0x38: + BG3X_L = value; + UPDATE_REG(0x38, BG3X_L); + gfxBG3Changed |= 1; + break; + case 0x3A: + BG3X_H = value & 0xFFF; + UPDATE_REG(0x3A, BG3X_H); + gfxBG3Changed |= 1; + break; + case 0x3C: + BG3Y_L = value; + UPDATE_REG(0x3C, BG3Y_L); + gfxBG3Changed |= 2; + break; + case 0x3E: + BG3Y_H = value & 0xFFF; + UPDATE_REG(0x3E, BG3Y_H); + gfxBG3Changed |= 2; + break; + case 0x40: + WIN0H = value; + UPDATE_REG(0x40, WIN0H); + CPUUpdateWindow0(); + break; + case 0x42: + WIN1H = value; + UPDATE_REG(0x42, WIN1H); + CPUUpdateWindow1(); + break; + case 0x44: + WIN0V = value; + UPDATE_REG(0x44, WIN0V); + break; + case 0x46: + WIN1V = value; + UPDATE_REG(0x46, WIN1V); + break; + case 0x48: + WININ = value & 0x3F3F; + UPDATE_REG(0x48, WININ); + break; + case 0x4A: + WINOUT = value & 0x3F3F; + UPDATE_REG(0x4A, WINOUT); + break; + case 0x4C: + MOSAIC = value; + UPDATE_REG(0x4C, MOSAIC); + break; + case 0x50: + BLDMOD = value & 0x3FFF; + UPDATE_REG(0x50, BLDMOD); + fxOn = ((BLDMOD>>6)&3) != 0; + CPUUpdateRender(); + break; + case 0x52: + COLEV = value & 0x1F1F; + UPDATE_REG(0x52, COLEV); + break; + case 0x54: + COLY = value & 0x1F; + UPDATE_REG(0x54, COLY); + break; + case 0x60: + case 0x62: + case 0x64: + case 0x68: + case 0x6c: + case 0x70: + case 0x72: + case 0x74: + case 0x78: + case 0x7c: + case 0x80: + case 0x84: + soundEvent(address&0xFF, (u8)(value & 0xFF)); + soundEvent((address&0xFF)+1, (u8)(value>>8)); + break; + case 0x82: + case 0x88: + case 0xa0: + case 0xa2: + case 0xa4: + case 0xa6: + case 0x90: + case 0x92: + case 0x94: + case 0x96: + case 0x98: + case 0x9a: + case 0x9c: + case 0x9e: + soundEvent(address&0xFF, value); + break; + case 0xB0: + DM0SAD_L = value; + UPDATE_REG(0xB0, DM0SAD_L); + break; + case 0xB2: + DM0SAD_H = value & 0x07FF; + UPDATE_REG(0xB2, DM0SAD_H); + break; + case 0xB4: + DM0DAD_L = value; + UPDATE_REG(0xB4, DM0DAD_L); + break; + case 0xB6: + DM0DAD_H = value & 0x07FF; + UPDATE_REG(0xB6, DM0DAD_H); + break; + case 0xB8: + DM0CNT_L = value & 0x3FFF; + UPDATE_REG(0xB8, 0); + break; + case 0xBA: + { + bool start = ((DM0CNT_H ^ value) & 0x8000) ? true : false; + value &= 0xF7E0; + + DM0CNT_H = value; + UPDATE_REG(0xBA, DM0CNT_H); + + if(start && (value & 0x8000)) { + dma0Source = DM0SAD_L | (DM0SAD_H << 16); + dma0Dest = DM0DAD_L | (DM0DAD_H << 16); + CPUCheckDMA(0, 1); + } + } + break; + case 0xBC: + DM1SAD_L = value; + UPDATE_REG(0xBC, DM1SAD_L); + break; + case 0xBE: + DM1SAD_H = value & 0x0FFF; + UPDATE_REG(0xBE, DM1SAD_H); + break; + case 0xC0: + DM1DAD_L = value; + UPDATE_REG(0xC0, DM1DAD_L); + break; + case 0xC2: + DM1DAD_H = value & 0x07FF; + UPDATE_REG(0xC2, DM1DAD_H); + break; + case 0xC4: + DM1CNT_L = value & 0x3FFF; + UPDATE_REG(0xC4, 0); + break; + case 0xC6: + { + bool start = ((DM1CNT_H ^ value) & 0x8000) ? true : false; + value &= 0xF7E0; + + DM1CNT_H = value; + UPDATE_REG(0xC6, DM1CNT_H); + + if(start && (value & 0x8000)) { + dma1Source = DM1SAD_L | (DM1SAD_H << 16); + dma1Dest = DM1DAD_L | (DM1DAD_H << 16); + CPUCheckDMA(0, 2); + } + } + break; + case 0xC8: + DM2SAD_L = value; + UPDATE_REG(0xC8, DM2SAD_L); + break; + case 0xCA: + DM2SAD_H = value & 0x0FFF; + UPDATE_REG(0xCA, DM2SAD_H); + break; + case 0xCC: + DM2DAD_L = value; + UPDATE_REG(0xCC, DM2DAD_L); + break; + case 0xCE: + DM2DAD_H = value & 0x07FF; + UPDATE_REG(0xCE, DM2DAD_H); + break; + case 0xD0: + DM2CNT_L = value & 0x3FFF; + UPDATE_REG(0xD0, 0); + break; + case 0xD2: + { + bool start = ((DM2CNT_H ^ value) & 0x8000) ? true : false; + + value &= 0xF7E0; + + DM2CNT_H = value; + UPDATE_REG(0xD2, DM2CNT_H); + + if(start && (value & 0x8000)) { + dma2Source = DM2SAD_L | (DM2SAD_H << 16); + dma2Dest = DM2DAD_L | (DM2DAD_H << 16); + + CPUCheckDMA(0, 4); + } + } + break; + case 0xD4: + DM3SAD_L = value; + UPDATE_REG(0xD4, DM3SAD_L); + break; + case 0xD6: + DM3SAD_H = value & 0x0FFF; + UPDATE_REG(0xD6, DM3SAD_H); + break; + case 0xD8: + DM3DAD_L = value; + UPDATE_REG(0xD8, DM3DAD_L); + break; + case 0xDA: + DM3DAD_H = value & 0x0FFF; + UPDATE_REG(0xDA, DM3DAD_H); + break; + case 0xDC: + DM3CNT_L = value; + UPDATE_REG(0xDC, 0); + break; + case 0xDE: + { + bool start = ((DM3CNT_H ^ value) & 0x8000) ? true : false; + + value &= 0xFFE0; + + DM3CNT_H = value; + UPDATE_REG(0xDE, DM3CNT_H); + + if(start && (value & 0x8000)) { + dma3Source = DM3SAD_L | (DM3SAD_H << 16); + dma3Dest = DM3DAD_L | (DM3DAD_H << 16); + CPUCheckDMA(0,8); + } + } + break; + case 0x100: + timer0Reload = value; + interp_rate(); + break; + case 0x102: + timer0Value = value; + timerOnOffDelay|=1; + cpuNextEvent = cpuTotalTicks; + break; + case 0x104: + timer1Reload = value; + interp_rate(); + break; + case 0x106: + timer1Value = value; + timerOnOffDelay|=2; + cpuNextEvent = cpuTotalTicks; + break; + case 0x108: + timer2Reload = value; + break; + case 0x10A: + timer2Value = value; + timerOnOffDelay|=4; + cpuNextEvent = cpuTotalTicks; + break; + case 0x10C: + timer3Reload = value; + break; + case 0x10E: + timer3Value = value; + timerOnOffDelay|=8; + cpuNextEvent = cpuTotalTicks; + break; + case 0x128: + #ifdef LINK_EMULATION + if (linkenable) + { + StartLink(value); + } + else +#endif + { + if(value & 0x80) { + value &= 0xff7f; + if(value & 1 && (value & 0x4000)) { + UPDATE_REG(0x12a, 0xFF); + IF |= 0x80; + UPDATE_REG(0x202, IF); + value &= 0x7f7f; + } + } + UPDATE_REG(0x128, value); + } + break; + case 0x12a: + #ifdef LINK_EMULATION + if(linkenable && lspeed) + LinkSSend(value); + #endif + { + UPDATE_REG(0x134, value); + } + break; + case 0x130: + P1 |= (value & 0x3FF); + UPDATE_REG(0x130, P1); + break; + case 0x132: + UPDATE_REG(0x132, value & 0xC3FF); + break; + case 0x134: +#ifdef LINK_EMULATION + if (linkenable) + StartGPLink(value); + else +#endif + UPDATE_REG(0x134, value); + + break; + case 0x140: +#ifdef LINK_EMULATION + if (linkenable) + StartJOYLink(value); + else +#endif + UPDATE_REG(0x140, value); + + break; + case 0x200: + IE = value & 0x3FFF; + UPDATE_REG(0x200, IE); + if ((IME & 1) && (IF & IE) && armIrqEnable) + cpuNextEvent = cpuTotalTicks; + break; + case 0x202: + IF ^= (value & IF); + UPDATE_REG(0x202, IF); + break; + case 0x204: + { + memoryWait[0x0e] = memoryWaitSeq[0x0e] = gamepakRamWaitState[value & 3]; + + if(!speedHack) { + memoryWait[0x08] = memoryWait[0x09] = gamepakWaitState[(value >> 2) & 3]; + memoryWaitSeq[0x08] = memoryWaitSeq[0x09] = + gamepakWaitState0[(value >> 4) & 1]; + + memoryWait[0x0a] = memoryWait[0x0b] = gamepakWaitState[(value >> 5) & 3]; + memoryWaitSeq[0x0a] = memoryWaitSeq[0x0b] = + gamepakWaitState1[(value >> 7) & 1]; + + memoryWait[0x0c] = memoryWait[0x0d] = gamepakWaitState[(value >> 8) & 3]; + memoryWaitSeq[0x0c] = memoryWaitSeq[0x0d] = + gamepakWaitState2[(value >> 10) & 1]; + } else { + memoryWait[0x08] = memoryWait[0x09] = 3; + memoryWaitSeq[0x08] = memoryWaitSeq[0x09] = 1; + + memoryWait[0x0a] = memoryWait[0x0b] = 3; + memoryWaitSeq[0x0a] = memoryWaitSeq[0x0b] = 1; + + memoryWait[0x0c] = memoryWait[0x0d] = 3; + memoryWaitSeq[0x0c] = memoryWaitSeq[0x0d] = 1; + } + + for(int i = 8; i < 15; i++) { + memoryWait32[i] = memoryWait[i] + memoryWaitSeq[i] + 1; + memoryWaitSeq32[i] = memoryWaitSeq[i]*2 + 1; + } + + if((value & 0x4000) == 0x4000) { + busPrefetchEnable = true; + busPrefetch = false; + busPrefetchCount = 0; + } else { + busPrefetchEnable = false; + busPrefetch = false; + busPrefetchCount = 0; + } + UPDATE_REG(0x204, value & 0x7FFF); + + } + break; + case 0x208: + IME = value & 1; + UPDATE_REG(0x208, IME); + if ((IME & 1) && (IF & IE) && armIrqEnable) + cpuNextEvent = cpuTotalTicks; + break; + case 0x300: + if(value != 0) + value &= 0xFFFE; + UPDATE_REG(0x300, value); + break; + default: + UPDATE_REG(address&0x3FE, value); + break; + } +} + +void applyTimer () +{ + if (timerOnOffDelay & 1) + { + timer0ClockReload = TIMER_TICKS[timer0Value & 3]; + if(!timer0On && (timer0Value & 0x80)) { + // reload the counter + TM0D = timer0Reload; + timer0Ticks = (0x10000 - TM0D) << timer0ClockReload; + UPDATE_REG(0x100, TM0D); + } + timer0On = timer0Value & 0x80 ? true : false; + TM0CNT = timer0Value & 0xC7; + interp_rate(); + UPDATE_REG(0x102, TM0CNT); + // CPUUpdateTicks(); + } + if (timerOnOffDelay & 2) + { + timer1ClockReload = TIMER_TICKS[timer1Value & 3]; + if(!timer1On && (timer1Value & 0x80)) { + // reload the counter + TM1D = timer1Reload; + timer1Ticks = (0x10000 - TM1D) << timer1ClockReload; + UPDATE_REG(0x104, TM1D); + } + timer1On = timer1Value & 0x80 ? true : false; + TM1CNT = timer1Value & 0xC7; + interp_rate(); + UPDATE_REG(0x106, TM1CNT); + } + if (timerOnOffDelay & 4) + { + timer2ClockReload = TIMER_TICKS[timer2Value & 3]; + if(!timer2On && (timer2Value & 0x80)) { + // reload the counter + TM2D = timer2Reload; + timer2Ticks = (0x10000 - TM2D) << timer2ClockReload; + UPDATE_REG(0x108, TM2D); + } + timer2On = timer2Value & 0x80 ? true : false; + TM2CNT = timer2Value & 0xC7; + UPDATE_REG(0x10A, TM2CNT); + } + if (timerOnOffDelay & 8) + { + timer3ClockReload = TIMER_TICKS[timer3Value & 3]; + if(!timer3On && (timer3Value & 0x80)) { + // reload the counter + TM3D = timer3Reload; + timer3Ticks = (0x10000 - TM3D) << timer3ClockReload; + UPDATE_REG(0x10C, TM3D); + } + timer3On = timer3Value & 0x80 ? true : false; + TM3CNT = timer3Value & 0xC7; + UPDATE_REG(0x10E, TM3CNT); + } + cpuNextEvent = CPUUpdateTicks(); + timerOnOffDelay = 0; +} + +u8 cpuBitsSet[256]; +u8 cpuLowestBitSet[256]; + +void CPUInit(const char *biosFileName, bool useBiosFile) +{ +#ifdef WORDS_BIGENDIAN + if(!cpuBiosSwapped) { + for(unsigned int i = 0; i < sizeof(myROM)/4; i++) { + WRITE32LE(&myROM[i], myROM[i]); + } + cpuBiosSwapped = true; + } +#endif + gbaSaveType = 0; + eepromInUse = 0; + saveType = 0; + useBios = false; + + if(useBiosFile) { + int size = 0x4000; + if(utilLoad(biosFileName, + CPUIsGBABios, + bios, + size)) { + if(size == 0x4000) + useBios = true; + else + systemMessage(MSG_INVALID_BIOS_FILE_SIZE, N_("Invalid BIOS file size")); + } + } + + if(!useBios) { + memcpy(bios, myROM, sizeof(myROM)); + } + + int i = 0; + + biosProtected[0] = 0x00; + biosProtected[1] = 0xf0; + biosProtected[2] = 0x29; + biosProtected[3] = 0xe1; + + for(i = 0; i < 256; i++) { + int count = 0; + int j; + for(j = 0; j < 8; j++) + if(i & (1 << j)) + count++; + cpuBitsSet[i] = count; + + for(j = 0; j < 8; j++) + if(i & (1 << j)) + break; + cpuLowestBitSet[i] = j; + } + + for(i = 0; i < 0x400; i++) + ioReadable[i] = true; + for(i = 0x10; i < 0x48; i++) + ioReadable[i] = false; + for(i = 0x4c; i < 0x50; i++) + ioReadable[i] = false; + for(i = 0x54; i < 0x60; i++) + ioReadable[i] = false; + for(i = 0x8c; i < 0x90; i++) + ioReadable[i] = false; + for(i = 0xa0; i < 0xb8; i++) + ioReadable[i] = false; + for(i = 0xbc; i < 0xc4; i++) + ioReadable[i] = false; + for(i = 0xc8; i < 0xd0; i++) + ioReadable[i] = false; + for(i = 0xd4; i < 0xdc; i++) + ioReadable[i] = false; + for(i = 0xe0; i < 0x100; i++) + ioReadable[i] = false; + for(i = 0x110; i < 0x120; i++) + ioReadable[i] = false; + for(i = 0x12c; i < 0x130; i++) + ioReadable[i] = false; + for(i = 0x138; i < 0x140; i++) + ioReadable[i] = false; + for(i = 0x144; i < 0x150; i++) + ioReadable[i] = false; + for(i = 0x15c; i < 0x200; i++) + ioReadable[i] = false; + for(i = 0x20c; i < 0x300; i++) + ioReadable[i] = false; + for(i = 0x304; i < 0x400; i++) + ioReadable[i] = false; + + if(romSize < 0x1fe2000) { + *((u16 *)&rom[0x1fe209c]) = 0xdffa; // SWI 0xFA + *((u16 *)&rom[0x1fe209e]) = 0x4770; // BX LR + } else { + agbPrintEnable(false); + } +} + +void CPUReset() +{ + if(gbaSaveType == 0) { + if(eepromInUse) + gbaSaveType = 3; + else + switch(saveType) { + case 1: + gbaSaveType = 1; + break; + case 2: + gbaSaveType = 2; + break; + } + } + rtcReset(); + // clean registers + memset(®[0], 0, sizeof(reg)); + // clean OAM + memset(oam, 0, 0x400); + // clean palette + memset(paletteRAM, 0, 0x400); + // clean picture + memset(pix, 0, 4*160*240); + // clean vram + memset(vram, 0, 0x20000); + // clean io memory + memset(ioMem, 0, 0x400); + + DISPCNT = 0x0080; + DISPSTAT = 0x0000; + VCOUNT = (useBios && !skipBios) ? 0 :0x007E; + BG0CNT = 0x0000; + BG1CNT = 0x0000; + BG2CNT = 0x0000; + BG3CNT = 0x0000; + BG0HOFS = 0x0000; + BG0VOFS = 0x0000; + BG1HOFS = 0x0000; + BG1VOFS = 0x0000; + BG2HOFS = 0x0000; + BG2VOFS = 0x0000; + BG3HOFS = 0x0000; + BG3VOFS = 0x0000; + BG2PA = 0x0100; + BG2PB = 0x0000; + BG2PC = 0x0000; + BG2PD = 0x0100; + BG2X_L = 0x0000; + BG2X_H = 0x0000; + BG2Y_L = 0x0000; + BG2Y_H = 0x0000; + BG3PA = 0x0100; + BG3PB = 0x0000; + BG3PC = 0x0000; + BG3PD = 0x0100; + BG3X_L = 0x0000; + BG3X_H = 0x0000; + BG3Y_L = 0x0000; + BG3Y_H = 0x0000; + WIN0H = 0x0000; + WIN1H = 0x0000; + WIN0V = 0x0000; + WIN1V = 0x0000; + WININ = 0x0000; + WINOUT = 0x0000; + MOSAIC = 0x0000; + BLDMOD = 0x0000; + COLEV = 0x0000; + COLY = 0x0000; + DM0SAD_L = 0x0000; + DM0SAD_H = 0x0000; + DM0DAD_L = 0x0000; + DM0DAD_H = 0x0000; + DM0CNT_L = 0x0000; + DM0CNT_H = 0x0000; + DM1SAD_L = 0x0000; + DM1SAD_H = 0x0000; + DM1DAD_L = 0x0000; + DM1DAD_H = 0x0000; + DM1CNT_L = 0x0000; + DM1CNT_H = 0x0000; + DM2SAD_L = 0x0000; + DM2SAD_H = 0x0000; + DM2DAD_L = 0x0000; + DM2DAD_H = 0x0000; + DM2CNT_L = 0x0000; + DM2CNT_H = 0x0000; + DM3SAD_L = 0x0000; + DM3SAD_H = 0x0000; + DM3DAD_L = 0x0000; + DM3DAD_H = 0x0000; + DM3CNT_L = 0x0000; + DM3CNT_H = 0x0000; + TM0D = 0x0000; + TM0CNT = 0x0000; + TM1D = 0x0000; + TM1CNT = 0x0000; + TM2D = 0x0000; + TM2CNT = 0x0000; + TM3D = 0x0000; + TM3CNT = 0x0000; + P1 = 0x03FF; + IE = 0x0000; + IF = 0x0000; + IME = 0x0000; + + armMode = 0x1F; + + if(cpuIsMultiBoot) { + reg[13].I = 0x03007F00; + reg[15].I = 0x02000000; + reg[16].I = 0x00000000; + reg[R13_IRQ].I = 0x03007FA0; + reg[R13_SVC].I = 0x03007FE0; + armIrqEnable = true; + } else { + if(useBios && !skipBios) { + reg[15].I = 0x00000000; + armMode = 0x13; + armIrqEnable = false; + } else { + reg[13].I = 0x03007F00; + reg[15].I = 0x08000000; + reg[16].I = 0x00000000; + reg[R13_IRQ].I = 0x03007FA0; + reg[R13_SVC].I = 0x03007FE0; + armIrqEnable = true; + } + } + armState = true; + C_FLAG = V_FLAG = N_FLAG = Z_FLAG = false; + UPDATE_REG(0x00, DISPCNT); + UPDATE_REG(0x06, VCOUNT); + UPDATE_REG(0x20, BG2PA); + UPDATE_REG(0x26, BG2PD); + UPDATE_REG(0x30, BG3PA); + UPDATE_REG(0x36, BG3PD); + UPDATE_REG(0x130, P1); + UPDATE_REG(0x88, 0x200); + + // disable FIQ + reg[16].I |= 0x40; + + CPUUpdateCPSR(); + + armNextPC = reg[15].I; + reg[15].I += 4; + + // reset internal state + holdState = false; + holdType = 0; + + biosProtected[0] = 0x00; + biosProtected[1] = 0xf0; + biosProtected[2] = 0x29; + biosProtected[3] = 0xe1; + + lcdTicks = (useBios && !skipBios) ? 1008 : 208; + timer0On = false; + timer0Ticks = 0; + timer0Reload = 0; + timer0ClockReload = 0; + timer1On = false; + timer1Ticks = 0; + timer1Reload = 0; + timer1ClockReload = 0; + timer2On = false; + timer2Ticks = 0; + timer2Reload = 0; + timer2ClockReload = 0; + timer3On = false; + timer3Ticks = 0; + timer3Reload = 0; + timer3ClockReload = 0; + dma0Source = 0; + dma0Dest = 0; + dma1Source = 0; + dma1Dest = 0; + dma2Source = 0; + dma2Dest = 0; + dma3Source = 0; + dma3Dest = 0; + cpuSaveGameFunc = flashSaveDecide; + renderLine = mode0RenderLine; + fxOn = false; + windowOn = false; + frameCount = 0; + saveType = 0; + layerEnable = DISPCNT & layerSettings; + + CPUUpdateRenderBuffers(true); + + for(int i = 0; i < 256; i++) { + map[i].address = (u8 *)&dummyAddress; + map[i].mask = 0; + } + + map[0].address = bios; + map[0].mask = 0x3FFF; + map[2].address = workRAM; + map[2].mask = 0x3FFFF; + map[3].address = internalRAM; + map[3].mask = 0x7FFF; + map[4].address = ioMem; + map[4].mask = 0x3FF; + map[5].address = paletteRAM; + map[5].mask = 0x3FF; + map[6].address = vram; + map[6].mask = 0x1FFFF; + map[7].address = oam; + map[7].mask = 0x3FF; + map[8].address = rom; + map[8].mask = 0x1FFFFFF; + map[9].address = rom; + map[9].mask = 0x1FFFFFF; + map[10].address = rom; + map[10].mask = 0x1FFFFFF; + map[12].address = rom; + map[12].mask = 0x1FFFFFF; + map[14].address = flashSaveMemory; + map[14].mask = 0xFFFF; + + eepromReset(); + flashReset(); + + soundReset(); + + CPUUpdateWindow0(); + CPUUpdateWindow1(); + + // make sure registers are correctly initialized if not using BIOS + if(!useBios) { + if(cpuIsMultiBoot) + BIOS_RegisterRamReset(0xfe); + else + BIOS_RegisterRamReset(0xff); + } else { + if(cpuIsMultiBoot) + BIOS_RegisterRamReset(0xfe); + } + + switch(cpuSaveType) { + case 0: // automatic + cpuSramEnabled = true; + cpuFlashEnabled = true; + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = false; + saveType = gbaSaveType = 0; + break; + case 1: // EEPROM + cpuSramEnabled = false; + cpuFlashEnabled = false; + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = false; + saveType = gbaSaveType = 3; + // EEPROM usage is automatically detected + break; + case 2: // SRAM + cpuSramEnabled = true; + cpuFlashEnabled = false; + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = sramDelayedWrite; // to insure we detect the write + saveType = gbaSaveType = 1; + break; + case 3: // FLASH + cpuSramEnabled = false; + cpuFlashEnabled = true; + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = flashDelayedWrite; // to insure we detect the write + saveType = gbaSaveType = 2; + break; + case 4: // EEPROM+Sensor + cpuSramEnabled = false; + cpuFlashEnabled = false; + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = true; + // EEPROM usage is automatically detected + saveType = gbaSaveType = 3; + break; + case 5: // NONE + cpuSramEnabled = false; + cpuFlashEnabled = false; + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + // no save at all + saveType = gbaSaveType = 5; + break; + } + + ARM_PREFETCH; + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + cpuDmaHack = false; + + lastTime = systemGetClock(); + + SWITicks = 0; +} + +void CPUInterrupt() +{ + u32 PC = reg[15].I; + bool savedState = armState; + CPUSwitchMode(0x12, true, false); + reg[14].I = PC; + if(!savedState) + reg[14].I += 2; + reg[15].I = 0x18; + armState = true; + armIrqEnable = false; + + armNextPC = reg[15].I; + reg[15].I += 4; + ARM_PREFETCH; + + // if(!holdState) + biosProtected[0] = 0x02; + biosProtected[1] = 0xc0; + biosProtected[2] = 0x5e; + biosProtected[3] = 0xe5; +} + +#ifdef SDL +void log(const char *defaultMsg, ...) +{ + char buffer[2048]; + va_list valist; + + va_start(valist, defaultMsg); + vsprintf(buffer, defaultMsg, valist); + + if(out == NULL) { + out = fopen("trace.log","w"); + } + + fputs(buffer, out); + + va_end(valist); +} +#else +extern void winlog(const char *, ...); +#endif + +void CPULoop(int ticks) +{ + int clockTicks; + int timerOverflow = 0; + // variable used by the CPU core + cpuTotalTicks = 0; +#ifdef LINK_EMULATION + if(linkenable) + cpuNextEvent = 1; +#endif + cpuBreakLoop = false; + cpuNextEvent = CPUUpdateTicks(); + if(cpuNextEvent > ticks) + cpuNextEvent = ticks; + + + for(;;) { +#ifndef FINAL_VERSION + if(systemDebug) { + if(systemDebug >= 10 && !holdState) { + CPUUpdateCPSR(); +#ifdef BKPT_SUPPORT + if (debugger_last) + { + sprintf(buffer, "R00=%08x R01=%08x R02=%08x R03=%08x R04=%08x R05=%08x R06=%08x R07=%08x R08=%08x R09=%08x R10=%08x R11=%08x R12=%08x R13=%08x R14=%08x R15=%08x R16=%08x R17=%08x\n", + oldreg[0], oldreg[1], oldreg[2], oldreg[3], oldreg[4], oldreg[5], + oldreg[6], oldreg[7], oldreg[8], oldreg[9], oldreg[10], oldreg[11], + oldreg[12], oldreg[13], oldreg[14], oldreg[15], oldreg[16], + oldreg[17]); + } +#endif + sprintf(buffer, "R00=%08x R01=%08x R02=%08x R03=%08x R04=%08x R05=%08x R06=%08x R07=%08x R08=%08x R09=%08x R10=%08x R11=%08x R12=%08x R13=%08x R14=%08x R15=%08x R16=%08x R17=%08x\n", + reg[0].I, reg[1].I, reg[2].I, reg[3].I, reg[4].I, reg[5].I, + reg[6].I, reg[7].I, reg[8].I, reg[9].I, reg[10].I, reg[11].I, + reg[12].I, reg[13].I, reg[14].I, reg[15].I, reg[16].I, + reg[17].I); +#ifdef SDL + log(buffer); +#else + winlog(buffer); +#endif + } else if(!holdState) { + sprintf(buffer, "PC=%08x\n", armNextPC); +#ifdef SDL + log(buffer); +#else + winlog(buffer); +#endif + } + } +#endif /* FINAL_VERSION */ + + if(!holdState && !SWITicks) { + if(armState) { + if (!armExecute()) + return; + } else { + if (!thumbExecute()) + return; + } + clockTicks = 0; + } else + clockTicks = CPUUpdateTicks(); + + cpuTotalTicks += clockTicks; + + + if(cpuTotalTicks >= cpuNextEvent) { + int remainingTicks = cpuTotalTicks - cpuNextEvent; + + if (SWITicks) + { + SWITicks-=clockTicks; + if (SWITicks<0) + SWITicks = 0; + } + + clockTicks = cpuNextEvent; + cpuTotalTicks = 0; + cpuDmaHack = false; + + updateLoop: + + if (IRQTicks) + { + IRQTicks -= clockTicks; + if (IRQTicks<0) + IRQTicks = 0; + } + + lcdTicks -= clockTicks; + + + if(lcdTicks <= 0) { + if(DISPSTAT & 1) { // V-BLANK + // if in V-Blank mode, keep computing... + if(DISPSTAT & 2) { + lcdTicks += 1008; + VCOUNT++; + UPDATE_REG(0x06, VCOUNT); + DISPSTAT &= 0xFFFD; + UPDATE_REG(0x04, DISPSTAT); + CPUCompareVCOUNT(); + } else { + lcdTicks += 224; + DISPSTAT |= 2; + UPDATE_REG(0x04, DISPSTAT); + if(DISPSTAT & 16) { + IF |= 2; + UPDATE_REG(0x202, IF); + } + } + + if(VCOUNT >= 228) { //Reaching last line + DISPSTAT &= 0xFFFC; + UPDATE_REG(0x04, DISPSTAT); + VCOUNT = 0; + UPDATE_REG(0x06, VCOUNT); + CPUCompareVCOUNT(); + } + } else { + int framesToSkip = systemFrameSkip; + if(speedup) + framesToSkip = 9; // try 6 FPS during speedup + + if(DISPSTAT & 2) { + // if in H-Blank, leave it and move to drawing mode + VCOUNT++; + UPDATE_REG(0x06, VCOUNT); + + lcdTicks += 1008; + DISPSTAT &= 0xFFFD; + if(VCOUNT == 160) { + count++; + systemFrame(); + + if((count % 10) == 0) { + system10Frames(60); + } + if(count == 60) { + u32 time = systemGetClock(); + if(time != lastTime) { + u32 t = 100000/(time - lastTime); + systemShowSpeed(t); + } else + systemShowSpeed(0); + lastTime = time; + count = 0; + } + u32 joy = 0; + // update joystick information + if(systemReadJoypads()) + // read default joystick + joy = systemReadJoypad(-1); + P1 = 0x03FF ^ (joy & 0x3FF); + if(cpuEEPROMSensorEnabled) + systemUpdateMotionSensor(); + UPDATE_REG(0x130, P1); + u16 P1CNT = READ16LE(((u16 *)&ioMem[0x132])); + // this seems wrong, but there are cases where the game + // can enter the stop state without requesting an IRQ from + // the joypad. + if((P1CNT & 0x4000) || stopState) { + u16 p1 = (0x3FF ^ P1) & 0x3FF; + if(P1CNT & 0x8000) { + if(p1 == (P1CNT & 0x3FF)) { + IF |= 0x1000; + UPDATE_REG(0x202, IF); + } + } else { + if(p1 & P1CNT) { + IF |= 0x1000; + UPDATE_REG(0x202, IF); + } + } + } + + u32 ext = (joy >> 10); + // If no (m) code is enabled, apply the cheats at each LCDline + if((cheatsEnabled) && (mastercode==0)) + remainingTicks += cheatsCheckKeys(P1^0x3FF, ext); + speedup = (ext & 1) ? true : false; + capture = (ext & 2) ? true : false; + + if(capture && !capturePrevious) { + captureNumber++; + systemScreenCapture(captureNumber); + } + capturePrevious = capture; + + DISPSTAT |= 1; + DISPSTAT &= 0xFFFD; + UPDATE_REG(0x04, DISPSTAT); + if(DISPSTAT & 0x0008) { + IF |= 1; + UPDATE_REG(0x202, IF); + } + CPUCheckDMA(1, 0x0f); + if(frameCount >= framesToSkip) { + systemDrawScreen(); + frameCount = 0; + } else + frameCount++; + if(systemPauseOnFrame()) + ticks = 0; + } + + UPDATE_REG(0x04, DISPSTAT); + CPUCompareVCOUNT(); + + } else { + if(frameCount >= framesToSkip) + { + (*renderLine)(); + switch(systemColorDepth) { + case 16: + { + u16 *dest = (u16 *)pix + 242 * (VCOUNT+1); + for(int x = 0; x < 240;) { + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++]&0xFFFF]; + } + // for filters that read past the screen + *dest++ = 0; + } + break; + case 24: + { + u8 *dest = (u8 *)pix + 240 * VCOUNT * 3; + for(int x = 0; x < 240;) { + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + } + } + break; + case 32: + { + u32 *dest = (u32 *)pix + 241 * (VCOUNT+1); + for(int x = 0; x < 240; ) { + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + } + } + break; + } + } + // entering H-Blank + DISPSTAT |= 2; + UPDATE_REG(0x04, DISPSTAT); + lcdTicks += 224; + CPUCheckDMA(2, 0x0f); + if(DISPSTAT & 16) { + IF |= 2; + UPDATE_REG(0x202, IF); + } + } + } + } + + // we shouldn't be doing sound in stop state, but we loose synchronization + // if sound is disabled, so in stop state, soundTick will just produce + // mute sound + soundTicks -= clockTicks; + if(soundTicks <= 0) { + psoundTickfn(); + soundTicks += SOUND_CLOCK_TICKS; + } + + if(!stopState) { + if(timer0On) { + timer0Ticks -= clockTicks; + if(timer0Ticks <= 0) { + timer0Ticks += (0x10000 - timer0Reload) << timer0ClockReload; + timerOverflow |= 1; + soundTimerOverflow(0); + if(TM0CNT & 0x40) { + IF |= 0x08; + UPDATE_REG(0x202, IF); + } + } + TM0D = 0xFFFF - (timer0Ticks >> timer0ClockReload); + UPDATE_REG(0x100, TM0D); + } + + if(timer1On) { + if(TM1CNT & 4) { + if(timerOverflow & 1) { + TM1D++; + if(TM1D == 0) { + TM1D += timer1Reload; + timerOverflow |= 2; + soundTimerOverflow(1); + if(TM1CNT & 0x40) { + IF |= 0x10; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x104, TM1D); + } + } else { + timer1Ticks -= clockTicks; + if(timer1Ticks <= 0) { + timer1Ticks += (0x10000 - timer1Reload) << timer1ClockReload; + timerOverflow |= 2; + soundTimerOverflow(1); + if(TM1CNT & 0x40) { + IF |= 0x10; + UPDATE_REG(0x202, IF); + } + } + TM1D = 0xFFFF - (timer1Ticks >> timer1ClockReload); + UPDATE_REG(0x104, TM1D); + } + } + + if(timer2On) { + if(TM2CNT & 4) { + if(timerOverflow & 2) { + TM2D++; + if(TM2D == 0) { + TM2D += timer2Reload; + timerOverflow |= 4; + if(TM2CNT & 0x40) { + IF |= 0x20; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x108, TM2D); + } + } else { + timer2Ticks -= clockTicks; + if(timer2Ticks <= 0) { + timer2Ticks += (0x10000 - timer2Reload) << timer2ClockReload; + timerOverflow |= 4; + if(TM2CNT & 0x40) { + IF |= 0x20; + UPDATE_REG(0x202, IF); + } + } + TM2D = 0xFFFF - (timer2Ticks >> timer2ClockReload); + UPDATE_REG(0x108, TM2D); + } + } + + if(timer3On) { + if(TM3CNT & 4) { + if(timerOverflow & 4) { + TM3D++; + if(TM3D == 0) { + TM3D += timer3Reload; + if(TM3CNT & 0x40) { + IF |= 0x40; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x10C, TM3D); + } + } else { + timer3Ticks -= clockTicks; + if(timer3Ticks <= 0) { + timer3Ticks += (0x10000 - timer3Reload) << timer3ClockReload; + if(TM3CNT & 0x40) { + IF |= 0x40; + UPDATE_REG(0x202, IF); + } + } + TM3D = 0xFFFF - (timer3Ticks >> timer3ClockReload); + UPDATE_REG(0x10C, TM3D); + } + } + } + + timerOverflow = 0; + + + +#ifdef PROFILING + profilingTicks -= clockTicks; + if(profilingTicks <= 0) { + profilingTicks += profilingTicksReload; + if(profilSegment) { + profile_segment *seg = profilSegment; + do { + u16 *b = (u16 *)seg->sbuf; + int pc = ((reg[15].I - seg->s_lowpc) * seg->s_scale)/0x10000; + if(pc >= 0 && pc < seg->ssiz) { + b[pc]++; + break; + } + + seg = seg->next; + } while(seg); + } + } +#endif + + ticks -= clockTicks; +#ifdef LINK_EMULATION + if (linkenable) + LinkUpdate(clockTicks); +#endif + cpuNextEvent = CPUUpdateTicks(); + + if(cpuDmaTicksToUpdate > 0) { + if(cpuDmaTicksToUpdate > cpuNextEvent) + clockTicks = cpuNextEvent; + else + clockTicks = cpuDmaTicksToUpdate; + cpuDmaTicksToUpdate -= clockTicks; + if(cpuDmaTicksToUpdate < 0) + cpuDmaTicksToUpdate = 0; + cpuDmaHack = true; + goto updateLoop; + } +#ifdef LINK_EMULATION + if(linkenable) + cpuNextEvent = 1; +#endif + if(IF && (IME & 1) && armIrqEnable) { + int res = IF & IE; + if(stopState) + res &= 0x3080; + if(res) { + if (intState) + { + if (!IRQTicks) + { + CPUInterrupt(); + intState = false; + holdState = false; + stopState = false; + holdType = 0; + } + } + else + { + if (!holdState) + { + intState = true; + IRQTicks=7; + if (cpuNextEvent> IRQTicks) + cpuNextEvent = IRQTicks; + } + else + { + CPUInterrupt(); + holdState = false; + stopState = false; + holdType = 0; + } + } + + // Stops the SWI Ticks emulation if an IRQ is executed + //(to avoid problems with nested IRQ/SWI) + if (SWITicks) + SWITicks = 0; + } + } + + if(remainingTicks > 0) { + if(remainingTicks > cpuNextEvent) + clockTicks = cpuNextEvent; + else + clockTicks = remainingTicks; + remainingTicks -= clockTicks; + if(remainingTicks < 0) + remainingTicks = 0; + goto updateLoop; + } + + if (timerOnOffDelay) + applyTimer(); + + if(cpuNextEvent > ticks) + cpuNextEvent = ticks; + + if(ticks <= 0 || cpuBreakLoop) + break; + + } + } +} + + + +struct EmulatedSystem GBASystem = { + // emuMain + CPULoop, + // emuReset + CPUReset, + // emuCleanUp + CPUCleanUp, + // emuReadBattery + CPUReadBatteryFile, + // emuWriteBattery + CPUWriteBatteryFile, + // emuReadState + CPUReadState, + // emuWriteState + CPUWriteState, + // emuReadMemState + CPUReadMemState, + // emuWriteMemState + CPUWriteMemState, + // emuWritePNG + CPUWritePNGFile, + // emuWriteBMP + CPUWriteBMPFile, + // emuUpdateCPSR + CPUUpdateCPSR, + // emuHasDebugger + true, + // emuCount +#ifdef FINAL_VERSION + 250000 +#else + 5000 +#endif +}; diff --git a/src/agb/GBA.h b/src/agb/GBA.h new file mode 100644 index 00000000..a1c04a00 --- /dev/null +++ b/src/agb/GBA.h @@ -0,0 +1,160 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_GBA_H +#define VBA_GBA_H + +#include "../System.h" + +#define SAVE_GAME_VERSION_1 1 +#define SAVE_GAME_VERSION_2 2 +#define SAVE_GAME_VERSION_3 3 +#define SAVE_GAME_VERSION_4 4 +#define SAVE_GAME_VERSION_5 5 +#define SAVE_GAME_VERSION_6 6 +#define SAVE_GAME_VERSION_7 7 +#define SAVE_GAME_VERSION_8 8 +#define SAVE_GAME_VERSION_9 9 +#define SAVE_GAME_VERSION_10 10 +#define SAVE_GAME_VERSION SAVE_GAME_VERSION_10 + +typedef struct { + u8 *address; + u32 mask; +} memoryMap; + +typedef union { + struct { +#ifdef WORDS_BIGENDIAN + u8 B3; + u8 B2; + u8 B1; + u8 B0; +#else + u8 B0; + u8 B1; + u8 B2; + u8 B3; +#endif + } B; + struct { +#ifdef WORDS_BIGENDIAN + u16 W1; + u16 W0; +#else + u16 W0; + u16 W1; +#endif + } W; +#ifdef WORDS_BIGENDIAN + volatile u32 I; +#else + u32 I; +#endif +} reg_pair; + +#ifndef NO_GBA_MAP +extern memoryMap map[256]; +#endif + +extern reg_pair reg[45]; +extern u8 biosProtected[4]; + +extern bool N_FLAG; +extern bool Z_FLAG; +extern bool C_FLAG; +extern bool V_FLAG; +extern bool armIrqEnable; +extern bool armState; +extern int armMode; +extern void (*cpuSaveGameFunc)(u32,u8); + +#ifdef BKPT_SUPPORT +extern u8 freezeWorkRAM[0x40000]; +extern u8 freezeInternalRAM[0x8000]; +extern u8 freezeVRAM[0x18000]; +extern u8 freezeOAM[0x400]; +extern u8 freezePRAM[0x400]; +extern bool debugger_last; +extern int oldreg[17]; +extern char oldbuffer[10]; +#endif + +extern bool CPUReadGSASnapshot(const char *); +extern bool CPUWriteGSASnapshot(const char *, const char *, const char *, const char *); +extern bool CPUWriteBatteryFile(const char *); +extern bool CPUReadBatteryFile(const char *); +extern bool CPUExportEepromFile(const char *); +extern bool CPUImportEepromFile(const char *); +extern bool CPUWritePNGFile(const char *); +extern bool CPUWriteBMPFile(const char *); +extern void CPUCleanUp(); +extern void CPUUpdateRender(); +extern void CPUUpdateRenderBuffers(bool); +extern bool CPUReadMemState(char *, int); +extern bool CPUReadState(const char *); +extern bool CPUWriteMemState(char *, int); +extern bool CPUWriteState(const char *); +extern int CPULoadRom(const char *); +extern void doMirroring(bool); +extern void CPUUpdateRegister(u32, u16); +extern void applyTimer (); +extern void CPUInit(const char *,bool); +extern void CPUReset(); +extern void CPULoop(int); +extern void CPUCheckDMA(int,int); +extern bool CPUIsGBAImage(const char *); +extern bool CPUIsZipFile(const char *); +#ifdef PROFILING +#include "prof/prof.h" +extern void cpuProfil(profile_segment *seg); +extern void cpuEnableProfiling(int hz); +#endif + +extern struct EmulatedSystem GBASystem; + +#define R13_IRQ 18 +#define R14_IRQ 19 +#define SPSR_IRQ 20 +#define R13_USR 26 +#define R14_USR 27 +#define R13_SVC 28 +#define R14_SVC 29 +#define SPSR_SVC 30 +#define R13_ABT 31 +#define R14_ABT 32 +#define SPSR_ABT 33 +#define R13_UND 34 +#define R14_UND 35 +#define SPSR_UND 36 +#define R8_FIQ 37 +#define R9_FIQ 38 +#define R10_FIQ 39 +#define R11_FIQ 40 +#define R12_FIQ 41 +#define R13_FIQ 42 +#define R14_FIQ 43 +#define SPSR_FIQ 44 + +#include "../Cheats.h" +#include "../Globals.h" +#include "../EEprom.h" +#include "../Flash.h" + +#endif //VBA_GBA_H diff --git a/src/agb/GBAGfx.cpp b/src/agb/GBAGfx.cpp new file mode 100644 index 00000000..24c73f68 --- /dev/null +++ b/src/agb/GBAGfx.cpp @@ -0,0 +1,47 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "../System.h" + +int coeff[32] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}; + +u32 line0[240]; +u32 line1[240]; +u32 line2[240]; +u32 line3[240]; +u32 lineOBJ[240]; +u32 lineOBJWin[240]; +u32 lineMix[240]; +bool gfxInWin0[240]; +bool gfxInWin1[240]; +int lineOBJpixleft[128]; + +int gfxBG2Changed = 0; +int gfxBG3Changed = 0; + +int gfxBG2X = 0; +int gfxBG2Y = 0; +int gfxBG2LastX = 0; +int gfxBG2LastY = 0; +int gfxBG3X = 0; +int gfxBG3Y = 0; +int gfxBG3LastX = 0; +int gfxBG3LastY = 0; +int gfxLastVCOUNT = 0; diff --git a/src/agb/GBAGfx.h b/src/agb/GBAGfx.h new file mode 100644 index 00000000..aa5cf6e6 --- /dev/null +++ b/src/agb/GBAGfx.h @@ -0,0 +1,1602 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005 Forgotten and the VBA development team +// Copyright (C) 2008 VBA-M development team +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_GFX_H +#define VBA_GFX_H + +#include "GBA.h" +#include "gbaGfx.h" +#include "../Globals.h" + +#include "../Port.h" + +//#define SPRITE_DEBUG + +static void gfxDrawTextScreen(u16, u16, u16, u32 *); +static void gfxDrawRotScreen(u16, + u16, u16, + u16, u16, + u16, u16, + u16, u16, + int&, int&, + int, + u32*); +static void gfxDrawRotScreen16Bit(u16, + u16, u16, + u16, u16, + u16, u16, + u16, u16, + int&, int&, + int, + u32*); +static void gfxDrawRotScreen256(u16, + u16, u16, + u16, u16, + u16, u16, + u16, u16, + int&, int&, + int, + u32*); +static void gfxDrawRotScreen16Bit160(u16, + u16, u16, + u16, u16, + u16, u16, + u16, u16, + int&, int&, + int, + u32*); +static void gfxDrawSprites(u32 *); +static void gfxIncreaseBrightness(u32 *line, int coeff); +static void gfxDecreaseBrightness(u32 *line, int coeff); +static void gfxAlphaBlend(u32 *ta, u32 *tb, int ca, int cb); + +void mode0RenderLine(); +void mode0RenderLineNoWindow(); +void mode0RenderLineAll(); + +void mode1RenderLine(); +void mode1RenderLineNoWindow(); +void mode1RenderLineAll(); + +void mode2RenderLine(); +void mode2RenderLineNoWindow(); +void mode2RenderLineAll(); + +void mode3RenderLine(); +void mode3RenderLineNoWindow(); +void mode3RenderLineAll(); + +void mode4RenderLine(); +void mode4RenderLineNoWindow(); +void mode4RenderLineAll(); + +void mode5RenderLine(); +void mode5RenderLineNoWindow(); +void mode5RenderLineAll(); + +extern int coeff[32]; +extern u32 line0[240]; +extern u32 line1[240]; +extern u32 line2[240]; +extern u32 line3[240]; +extern u32 lineOBJ[240]; +extern u32 lineOBJWin[240]; +extern u32 lineMix[240]; +extern bool gfxInWin0[240]; +extern bool gfxInWin1[240]; +extern int lineOBJpixleft[128]; + +extern int gfxBG2Changed; +extern int gfxBG3Changed; + +extern int gfxBG2X; +extern int gfxBG2Y; +extern int gfxBG2LastX; +extern int gfxBG2LastY; +extern int gfxBG3X; +extern int gfxBG3Y; +extern int gfxBG3LastX; +extern int gfxBG3LastY; +extern int gfxLastVCOUNT; + +static inline void gfxClearArray(u32 *array) +{ + for(int i = 0; i < 240; i++) { + *array++ = 0x80000000; + } +} + +static inline void gfxDrawTextScreen(u16 control, u16 hofs, u16 vofs, + u32 *line) +{ + u16 *palette = (u16 *)paletteRAM; + u8 *charBase = &vram[((control >> 2) & 0x03) * 0x4000]; + u16 *screenBase = (u16 *)&vram[((control >> 8) & 0x1f) * 0x800]; + u32 prio = ((control & 3)<<25) + 0x1000000; + int sizeX = 256; + int sizeY = 256; + switch((control >> 14) & 3) { + case 0: + break; + case 1: + sizeX = 512; + break; + case 2: + sizeY = 512; + break; + case 3: + sizeX = 512; + sizeY = 512; + break; + } + + int maskX = sizeX-1; + int maskY = sizeY-1; + + bool mosaicOn = (control & 0x40) ? true : false; + + int xxx = hofs & maskX; + int yyy = (vofs + VCOUNT) & maskY; + int mosaicX = (MOSAIC & 0x000F)+1; + int mosaicY = ((MOSAIC & 0x00F0)>>4)+1; + + if(mosaicOn) { + if((VCOUNT % mosaicY) != 0) { + mosaicY = VCOUNT - (VCOUNT % mosaicY); + yyy = (vofs + mosaicY) & maskY; + } + } + + if(yyy > 255 && sizeY > 256) { + yyy &= 255; + screenBase += 0x400; + if(sizeX > 256) + screenBase += 0x400; + } + + int yshift = ((yyy>>3)<<5); + if((control) & 0x80) { + u16 *screenSource = screenBase + 0x400 * (xxx>>8) + ((xxx & 255)>>3) + yshift; + for(int x = 0; x < 240; x++) { + u16 data = READ16LE(screenSource); + + int tile = data & 0x3FF; + int tileX = (xxx & 7); + int tileY = yyy & 7; + + if(tileX == 7) + screenSource++; + + if(data & 0x0400) + tileX = 7 - tileX; + if(data & 0x0800) + tileY = 7 - tileY; + + u8 color = charBase[tile * 64 + tileY * 8 + tileX]; + + line[x] = color ? (READ16LE(&palette[color]) | prio): 0x80000000; + + xxx++; + if(xxx == 256) { + if(sizeX > 256) + screenSource = screenBase + 0x400 + yshift; + else { + screenSource = screenBase + yshift; + xxx = 0; + } + } else if(xxx >= sizeX) { + xxx = 0; + screenSource = screenBase + yshift; + } + } + } else { + u16 *screenSource = screenBase + 0x400*(xxx>>8)+((xxx&255)>>3) + + yshift; + for(int x = 0; x < 240; x++) { + u16 data = READ16LE(screenSource); + + int tile = data & 0x3FF; + int tileX = (xxx & 7); + int tileY = yyy & 7; + + if(tileX == 7) + screenSource++; + + if(data & 0x0400) + tileX = 7 - tileX; + if(data & 0x0800) + tileY = 7 - tileY; + + u8 color = charBase[(tile<<5) + (tileY<<2) + (tileX>>1)]; + + if(tileX & 1) { + color = (color >> 4); + } else { + color &= 0x0F; + } + + int pal = (data>>8) & 0xF0; + line[x] = color ? (READ16LE(&palette[pal + color])|prio): 0x80000000; + + xxx++; + if(xxx == 256) { + if(sizeX > 256) + screenSource = screenBase + 0x400 + yshift; + else { + screenSource = screenBase + yshift; + xxx = 0; + } + } else if(xxx >= sizeX) { + xxx = 0; + screenSource = screenBase + yshift; + } + } + } + if(mosaicOn) { + if(mosaicX > 1) { + int m = 1; + for(int i = 0; i < 239; i++) { + line[i+1] = line[i]; + m++; + if(m == mosaicX) { + m = 1; + i++; + } + } + } + } +} + +static inline void gfxDrawRotScreen(u16 control, + u16 x_l, u16 x_h, + u16 y_l, u16 y_h, + u16 pa, u16 pb, + u16 pc, u16 pd, + int& currentX, int& currentY, + int changed, + u32 *line) +{ + u16 *palette = (u16 *)paletteRAM; + u8 *charBase = &vram[((control >> 2) & 0x03) * 0x4000]; + u8 *screenBase = (u8 *)&vram[((control >> 8) & 0x1f) * 0x800]; + int prio = ((control & 3) << 25) + 0x1000000; + + int sizeX = 128; + int sizeY = 128; + switch((control >> 14) & 3) { + case 0: + break; + case 1: + sizeX = sizeY = 256; + break; + case 2: + sizeX = sizeY = 512; + break; + case 3: + sizeX = sizeY = 1024; + break; + } + + int maskX = sizeX-1; + int maskY = sizeY-1; + + int yshift = ((control >> 14) & 3)+4; + + int dx = pa & 0x7FFF; + if(pa & 0x8000) + dx |= 0xFFFF8000; + int dmx = pb & 0x7FFF; + if(pb & 0x8000) + dmx |= 0xFFFF8000; + int dy = pc & 0x7FFF; + if(pc & 0x8000) + dy |= 0xFFFF8000; + int dmy = pd & 0x7FFF; + if(pd & 0x8000) + dmy |= 0xFFFF8000; + + if(VCOUNT == 0) + changed = 3; + + if(changed & 1) { + currentX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + currentX |= 0xF8000000; + } else { + currentX += dmx; + } + + if(changed & 2) { + currentY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + currentY |= 0xF8000000; + } else { + currentY += dmy; + } + + int realX = currentX; + int realY = currentY; + + if(control & 0x40) { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = (VCOUNT % mosaicY); + realX -= y*dmx; + realY -= y*dmy; + } + + if(control & 0x2000) { + for(int x = 0; x < 240; x++) { + int xxx = (realX >> 8) & maskX; + int yyy = (realY >> 8) & maskY; + + int tile = screenBase[(xxx>>3) + ((yyy>>3)<> 8); + int yyy = (realY >> 8); + + if(xxx < 0 || + yyy < 0 || + xxx >= sizeX || + yyy >= sizeY) { + line[x] = 0x80000000; + } else { + int tile = screenBase[(xxx>>3) + ((yyy>>3)< 1) { + int m = 1; + for(int i = 0; i < 239; i++) { + line[i+1] = line[i]; + m++; + if(m == mosaicX) { + m = 1; + i++; + } + } + } + } +} + +static inline void gfxDrawRotScreen16Bit(u16 control, + u16 x_l, u16 x_h, + u16 y_l, u16 y_h, + u16 pa, u16 pb, + u16 pc, u16 pd, + int& currentX, int& currentY, + int changed, + u32 *line) +{ + u16 *screenBase = (u16 *)&vram[0]; + int prio = ((control & 3) << 25) + 0x1000000; + int sizeX = 240; + int sizeY = 160; + + int startX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + startX |= 0xF8000000; + int startY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + startY |= 0xF8000000; + + int dx = pa & 0x7FFF; + if(pa & 0x8000) + dx |= 0xFFFF8000; + int dmx = pb & 0x7FFF; + if(pb & 0x8000) + dmx |= 0xFFFF8000; + int dy = pc & 0x7FFF; + if(pc & 0x8000) + dy |= 0xFFFF8000; + int dmy = pd & 0x7FFF; + if(pd & 0x8000) + dmy |= 0xFFFF8000; + + if(VCOUNT == 0) + changed = 3; + + if(changed & 1) { + currentX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + currentX |= 0xF8000000; + } else + currentX += dmx; + + if(changed & 2) { + currentY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + currentY |= 0xF8000000; + } else { + currentY += dmy; + } + + int realX = currentX; + int realY = currentY; + + if(control & 0x40) { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = (VCOUNT % mosaicY); + realX -= y*dmx; + realY -= y*dmy; + } + + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + for(int x = 0; x < 240; x++) { + if(xxx < 0 || + yyy < 0 || + xxx >= sizeX || + yyy >= sizeY) { + line[x] = 0x80000000; + } else { + line[x] = (READ16LE(&screenBase[yyy * sizeX + xxx]) | prio); + } + realX += dx; + realY += dy; + + xxx = (realX >> 8); + yyy = (realY >> 8); + } + + if(control & 0x40) { + int mosaicX = (MOSAIC & 0xF) + 1; + if(mosaicX > 1) { + int m = 1; + for(int i = 0; i < 239; i++) { + line[i+1] = line[i]; + m++; + if(m == mosaicX) { + m = 1; + i++; + } + } + } + } +} + +static inline void gfxDrawRotScreen256(u16 control, + u16 x_l, u16 x_h, + u16 y_l, u16 y_h, + u16 pa, u16 pb, + u16 pc, u16 pd, + int ¤tX, int& currentY, + int changed, + u32 *line) +{ + u16 *palette = (u16 *)paletteRAM; + u8 *screenBase = (DISPCNT & 0x0010) ? &vram[0xA000] : &vram[0x0000]; + int prio = ((control & 3) << 25) + 0x1000000; + int sizeX = 240; + int sizeY = 160; + + int startX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + startX |= 0xF8000000; + int startY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + startY |= 0xF8000000; + + int dx = pa & 0x7FFF; + if(pa & 0x8000) + dx |= 0xFFFF8000; + int dmx = pb & 0x7FFF; + if(pb & 0x8000) + dmx |= 0xFFFF8000; + int dy = pc & 0x7FFF; + if(pc & 0x8000) + dy |= 0xFFFF8000; + int dmy = pd & 0x7FFF; + if(pd & 0x8000) + dmy |= 0xFFFF8000; + + if(VCOUNT == 0) + changed = 3; + + if(changed & 1) { + currentX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + currentX |= 0xF8000000; + } else { + currentX += dmx; + } + + if(changed & 2) { + currentY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + currentY |= 0xF8000000; + } else { + currentY += dmy; + } + + int realX = currentX; + int realY = currentY; + + if(control & 0x40) { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = VCOUNT - (VCOUNT % mosaicY); + realX = startX + y*dmx; + realY = startY + y*dmy; + } + + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + for(int x = 0; x < 240; x++) { + if(xxx < 0 || + yyy < 0 || + xxx >= sizeX || + yyy >= sizeY) { + line[x] = 0x80000000; + } else { + u8 color = screenBase[yyy * 240 + xxx]; + + line[x] = color ? (READ16LE(&palette[color])|prio): 0x80000000; + } + realX += dx; + realY += dy; + + xxx = (realX >> 8); + yyy = (realY >> 8); + } + + if(control & 0x40) { + int mosaicX = (MOSAIC & 0xF) + 1; + if(mosaicX > 1) { + int m = 1; + for(int i = 0; i < 239; i++) { + line[i+1] = line[i]; + m++; + if(m == mosaicX) { + m = 1; + i++; + } + } + } + } +} + +static inline void gfxDrawRotScreen16Bit160(u16 control, + u16 x_l, u16 x_h, + u16 y_l, u16 y_h, + u16 pa, u16 pb, + u16 pc, u16 pd, + int& currentX, int& currentY, + int changed, + u32 *line) +{ + u16 *screenBase = (DISPCNT & 0x0010) ? (u16 *)&vram[0xa000] : + (u16 *)&vram[0]; + int prio = ((control & 3) << 25) + 0x1000000; + int sizeX = 160; + int sizeY = 128; + + int startX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + startX |= 0xF8000000; + int startY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + startY |= 0xF8000000; + + int dx = pa & 0x7FFF; + if(pa & 0x8000) + dx |= 0xFFFF8000; + int dmx = pb & 0x7FFF; + if(pb & 0x8000) + dmx |= 0xFFFF8000; + int dy = pc & 0x7FFF; + if(pc & 0x8000) + dy |= 0xFFFF8000; + int dmy = pd & 0x7FFF; + if(pd & 0x8000) + dmy |= 0xFFFF8000; + + if(VCOUNT == 0) + changed = 3; + + if(changed & 1) { + currentX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + currentX |= 0xF8000000; + } else { + currentX += dmx; + } + + if(changed & 2) { + currentY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + currentY |= 0xF8000000; + } else { + currentY += dmy; + } + + int realX = currentX; + int realY = currentY; + + if(control & 0x40) { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = VCOUNT - (VCOUNT % mosaicY); + realX = startX + y*dmx; + realY = startY + y*dmy; + } + + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + for(int x = 0; x < 240; x++) { + if(xxx < 0 || + yyy < 0 || + xxx >= sizeX || + yyy >= sizeY) { + line[x] = 0x80000000; + } else { + line[x] = (READ16LE(&screenBase[yyy * sizeX + xxx]) | prio); + } + realX += dx; + realY += dy; + + xxx = (realX >> 8); + yyy = (realY >> 8); + } + + if(control & 0x40) { + int mosaicX = (MOSAIC & 0xF) + 1; + if(mosaicX > 1) { + int m = 1; + for(int i = 0; i < 239; i++) { + line[i+1] = line[i]; + m++; + if(m == mosaicX) { + m = 1; + i++; + } + } + } + } +} + +static inline void gfxDrawSprites(u32 *lineOBJ) +{ + // lineOBJpix is used to keep track of the drawn OBJs + // and to stop drawing them if the 'maximum number of OBJ per line' + // has been reached. + int lineOBJpix = (DISPCNT & 0x20) ? 954 : 1226; + int m=0; + gfxClearArray(lineOBJ); + if(layerEnable & 0x1000) { + u16 *sprites = (u16 *)oam; + u16 *spritePalette = &((u16 *)paletteRAM)[256]; + int mosaicY = ((MOSAIC & 0xF000)>>12) + 1; + int mosaicX = ((MOSAIC & 0xF00)>>8) + 1; + for(int x = 0; x < 128 ; x++) { + u16 a0 = READ16LE(sprites++); + u16 a1 = READ16LE(sprites++); + u16 a2 = READ16LE(sprites++); + sprites++; + + lineOBJpixleft[x]=lineOBJpix; + + lineOBJpix-=2; + if (lineOBJpix<=0) + continue; + + if ((a0 & 0x0c00) == 0x0c00) + a0 &=0xF3FF; + + if ((a0>>14) == 3) + { + a0 &= 0x3FFF; + a1 &= 0x3FFF; + } + + int sizeX = 8<<(a1>>14); + int sizeY = sizeX; + + if ((a0>>14) & 1) + { + if (sizeX<32) + sizeX<<=1; + if (sizeY>8) + sizeY>>=1; + } + else if ((a0>>14) & 2) + { + if (sizeX>8) + sizeX>>=1; + if (sizeY<32) + sizeY<<=1; + } + +#ifdef SPRITE_DEBUG + int maskX = sizeX-1; + int maskY = sizeY-1; +#endif + + int sy = (a0 & 255); + int sx = (a1 & 0x1FF); + + // computes ticks used by OBJ-WIN if OBJWIN is enabled + if (((a0 & 0x0c00) == 0x0800) && (layerEnable & 0x8000)) + { + if ((a0 & 0x0300) == 0x0300) + { + sizeX<<=1; + sizeY<<=1; + } + if((sy+sizeY) > 256) + sy -= 256; + if ((sx+sizeX)> 512) + sx-=512; + if (sx<0) + { + sizeX+=sx; + sx = 0; + } + else if ((sx+sizeX)>240) + sizeX=240-sx; + if ((VCOUNT>=sy) && (VCOUNT 256) + sy -= 256; + int t = VCOUNT - sy; + if((t >= 0) && (t < fieldY)) { + int startpix = 0; + if ((sx+fieldX)> 512) + { + startpix=512-sx; + } + if (lineOBJpix>0) + if((sx < 240) || startpix) { + lineOBJpix-=8; + // int t2 = t - (fieldY >> 1); + int rot = (a1 >> 9) & 0x1F; + u16 *OAM = (u16 *)oam; + int dx = READ16LE(&OAM[3 + (rot << 4)]); + if(dx & 0x8000) + dx |= 0xFFFF8000; + int dmx = READ16LE(&OAM[7 + (rot << 4)]); + if(dmx & 0x8000) + dmx |= 0xFFFF8000; + int dy = READ16LE(&OAM[11 + (rot << 4)]); + if(dy & 0x8000) + dy |= 0xFFFF8000; + int dmy = READ16LE(&OAM[15 + (rot << 4)]); + if(dmy & 0x8000) + dmy |= 0xFFFF8000; + + if(a0 & 0x1000) { + t -= (t % mosaicY); + } + + int realX = ((sizeX) << 7) - (fieldX >> 1)*dx - (fieldY>>1)*dmx + + t * dmx; + int realY = ((sizeY) << 7) - (fieldX >> 1)*dy - (fieldY>>1)*dmy + + t * dmy; + + u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + + if(a0 & 0x2000) { + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + int inc = 32; + if(DISPCNT & 0x40) + inc = sizeX >> 2; + else + c &= 0x3FE; + for(int x = 0; x < fieldX; x++) { + if (x >= startpix) + lineOBJpix-=2; + if (lineOBJpix<0) + continue; + int xxx = realX >> 8; + int yyy = realY >> 8; + + if(xxx < 0 || xxx >= sizeX || + yyy < 0 || yyy >= sizeY || + sx >= 240); + else { + u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<3) + ((xxx >> 3)<<6) + + (xxx & 7))&0x7FFF)]; + if ((color==0) && (((prio >> 25)&3) < + ((lineOBJ[sx]>>25)&3))) { + lineOBJ[sx] = (lineOBJ[sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } else if((color) && (prio < (lineOBJ[sx]&0xFF000000))) { + lineOBJ[sx] = READ16LE(&spritePalette[color]) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + + if (a0 & 0x1000) { + m++; + if (m==mosaicX) + m=0; + } +#ifdef SPRITE_DEBUG + if(t == 0 || t == maskY || x == 0 || x == maskX) + lineOBJ[sx] = 0x001F; +#endif + } + sx = (sx+1)&511; + realX += dx; + realY += dy; + } + } else { + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if(DISPCNT & 0x40) + inc = sizeX >> 3; + int palette = (a2 >> 8) & 0xF0; + for(int x = 0; x < fieldX; x++) { + if (x >= startpix) + lineOBJpix-=2; + if (lineOBJpix<0) + continue; + int xxx = realX >> 8; + int yyy = realY >> 8; + if(xxx < 0 || xxx >= sizeX || + yyy < 0 || yyy >= sizeY || + sx >= 240); + else { + u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<2) + ((xxx >> 3)<<5) + + ((xxx & 7)>>1))&0x7FFF)]; + if(xxx & 1) + color >>= 4; + else + color &= 0x0F; + + if ((color==0) && (((prio >> 25)&3) < + ((lineOBJ[sx]>>25)&3))) { + lineOBJ[sx] = (lineOBJ[sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } else if((color) && (prio < (lineOBJ[sx]&0xFF000000))) { + lineOBJ[sx] = READ16LE(&spritePalette[palette+color]) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + } + if((a0 & 0x1000) && m) { + m++; + if (m==mosaicX) + m=0; + } + +#ifdef SPRITE_DEBUG + if(t == 0 || t == maskY || x == 0 || x == maskX) + lineOBJ[sx] = 0x001F; +#endif + sx = (sx+1)&511; + realX += dx; + realY += dy; + + } + } + } + } + } else { + if(sy+sizeY > 256) + sy -= 256; + int t = VCOUNT - sy; + if((t >= 0) && (t < sizeY)) { + int startpix = 0; + if ((sx+sizeX)> 512) + { + startpix=512-sx; + } + if((sx < 240) || startpix) { + lineOBJpix+=2; + if(a0 & 0x2000) { + if(a1 & 0x2000) + t = sizeY - t - 1; + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if(DISPCNT & 0x40) { + inc = sizeX >> 2; + } else { + c &= 0x3FE; + } + int xxx = 0; + if(a1 & 0x1000) + xxx = sizeX-1; + + if(a0 & 0x1000) { + t -= (t % mosaicY); + } + + int address = 0x10000 + ((((c+ (t>>3) * inc) << 5) + + ((t & 7) << 3) + ((xxx>>3)<<6) + (xxx & 7)) & 0x7FFF); + + if(a1 & 0x1000) + xxx = 7; + u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + + for(int xx = 0; xx < sizeX; xx++) { + if (xx >= startpix) + lineOBJpix--; + if (lineOBJpix<0) + continue; + if(sx < 240) { + u8 color = vram[address]; + if ((color==0) && (((prio >> 25)&3) < + ((lineOBJ[sx]>>25)&3))) { + lineOBJ[sx] = (lineOBJ[sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } else if((color) && (prio < (lineOBJ[sx]&0xFF000000))) { + lineOBJ[sx] = READ16LE(&spritePalette[color]) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + + if (a0 & 0x1000) { + m++; + if (m==mosaicX) + m=0; + } + +#ifdef SPRITE_DEBUG + if(t == 0 || t == maskY || xx == 0 || xx == maskX) + lineOBJ[sx] = 0x001F; +#endif + } + + sx = (sx+1) & 511; + if(a1 & 0x1000) { + xxx--; + address--; + if(xxx == -1) { + address -= 56; + xxx = 7; + } + if(address < 0x10000) + address += 0x8000; + } else { + xxx++; + address++; + if(xxx == 8) { + address += 56; + xxx = 0; + } + if(address > 0x17fff) + address -= 0x8000; + } + } + } else { + if(a1 & 0x2000) + t = sizeY - t - 1; + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if(DISPCNT & 0x40) { + inc = sizeX >> 3; + } + int xxx = 0; + if(a1 & 0x1000) + xxx = sizeX - 1; + + if(a0 & 0x1000) { + t -= (t % mosaicY); + } + + int address = 0x10000 + ((((c + (t>>3) * inc)<<5) + + ((t & 7)<<2) + ((xxx>>3)<<5) + ((xxx & 7) >> 1))&0x7FFF); + u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + int palette = (a2 >> 8) & 0xF0; + if(a1 & 0x1000) { + xxx = 7; + for(int xx = sizeX - 1; xx >= 0; xx--) { + if (xx >= startpix) + lineOBJpix--; + if (lineOBJpix<0) + continue; + if(sx < 240) { + u8 color = vram[address]; + if(xx & 1) { + color = (color >> 4); + } else + color &= 0x0F; + + if ((color==0) && (((prio >> 25)&3) < + ((lineOBJ[sx]>>25)&3))) { + lineOBJ[sx] = (lineOBJ[sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } else if((color) && (prio < (lineOBJ[sx]&0xFF000000))) { + lineOBJ[sx] = READ16LE(&spritePalette[palette + color]) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + } + if (a0 & 0x1000) { + m++; + if (m==mosaicX) + m=0; + } +#ifdef SPRITE_DEBUG + if(t == 0 || t == maskY || xx == 0 || xx == maskX) + lineOBJ[sx] = 0x001F; +#endif + sx = (sx+1) & 511; + xxx--; + if(!(xx & 1)) + address--; + if(xxx == -1) { + xxx = 7; + address -= 28; + } + if(address < 0x10000) + address += 0x8000; + } + } else { + for(int xx = 0; xx < sizeX; xx++) { + if (xx >= startpix) + lineOBJpix--; + if (lineOBJpix<0) + continue; + if(sx < 240) { + u8 color = vram[address]; + if(xx & 1) { + color = (color >> 4); + } else + color &= 0x0F; + + if ((color==0) && (((prio >> 25)&3) < + ((lineOBJ[sx]>>25)&3))) { + lineOBJ[sx] = (lineOBJ[sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } else if((color) && (prio < (lineOBJ[sx]&0xFF000000))) { + lineOBJ[sx] = READ16LE(&spritePalette[palette + color]) | prio; + if((a0 & 0x1000) && m) + lineOBJ[sx]=(lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + + } + } + if (a0 & 0x1000) { + m++; + if (m==mosaicX) + m=0; + } +#ifdef SPRITE_DEBUG + if(t == 0 || t == maskY || xx == 0 || xx == maskX) + lineOBJ[sx] = 0x001F; +#endif + sx = (sx+1) & 511; + xxx++; + if(xx & 1) + address++; + if(xxx == 8) { + address += 28; + xxx = 0; + } + if(address > 0x17fff) + address -= 0x8000; + } + } + } + } + } + } + } + } +} + +static inline void gfxDrawOBJWin(u32 *lineOBJWin) +{ + gfxClearArray(lineOBJWin); + if((layerEnable & 0x9000) == 0x9000) { + u16 *sprites = (u16 *)oam; + // u16 *spritePalette = &((u16 *)paletteRAM)[256]; + for(int x = 0; x < 128 ; x++) { + int lineOBJpix = lineOBJpixleft[x]; + u16 a0 = READ16LE(sprites++); + u16 a1 = READ16LE(sprites++); + u16 a2 = READ16LE(sprites++); + sprites++; + + if (lineOBJpix<=0) + continue; + + // ignores non OBJ-WIN and disabled OBJ-WIN + if(((a0 & 0x0c00) != 0x0800) || ((a0 & 0x0300) == 0x0200)) + continue; + + if ((a0 & 0x0c00) == 0x0c00) + a0 &=0xF3FF; + + if ((a0>>14) == 3) + { + a0 &= 0x3FFF; + a1 &= 0x3FFF; + } + + int sizeX = 8<<(a1>>14); + int sizeY = sizeX; + + if ((a0>>14) & 1) + { + if (sizeX<32) + sizeX<<=1; + if (sizeY>8) + sizeY>>=1; + } + else if ((a0>>14) & 2) + { + if (sizeX>8) + sizeX>>=1; + if (sizeY<32) + sizeY<<=1; + } + + int sy = (a0 & 255); + + if(a0 & 0x0100) { + int fieldX = sizeX; + int fieldY = sizeY; + if(a0 & 0x0200) { + fieldX <<= 1; + fieldY <<= 1; + } + if((sy+fieldY) > 256) + sy -= 256; + int t = VCOUNT - sy; + if((t >= 0) && (t < fieldY)) { + int sx = (a1 & 0x1FF); + int startpix = 0; + if ((sx+fieldX)> 512) + { + startpix=512-sx; + } + if((sx < 240) || startpix) { + lineOBJpix-=8; + // int t2 = t - (fieldY >> 1); + int rot = (a1 >> 9) & 0x1F; + u16 *OAM = (u16 *)oam; + int dx = READ16LE(&OAM[3 + (rot << 4)]); + if(dx & 0x8000) + dx |= 0xFFFF8000; + int dmx = READ16LE(&OAM[7 + (rot << 4)]); + if(dmx & 0x8000) + dmx |= 0xFFFF8000; + int dy = READ16LE(&OAM[11 + (rot << 4)]); + if(dy & 0x8000) + dy |= 0xFFFF8000; + int dmy = READ16LE(&OAM[15 + (rot << 4)]); + if(dmy & 0x8000) + dmy |= 0xFFFF8000; + + int realX = ((sizeX) << 7) - (fieldX >> 1)*dx - (fieldY>>1)*dmx + + t * dmx; + int realY = ((sizeY) << 7) - (fieldX >> 1)*dy - (fieldY>>1)*dmy + + t * dmy; + + // u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + + if(a0 & 0x2000) { + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + int inc = 32; + if(DISPCNT & 0x40) + inc = sizeX >> 2; + else + c &= 0x3FE; + for(int x = 0; x < fieldX; x++) { + if (x >= startpix) + lineOBJpix-=2; + if (lineOBJpix<0) + continue; + int xxx = realX >> 8; + int yyy = realY >> 8; + + if(xxx < 0 || xxx >= sizeX || + yyy < 0 || yyy >= sizeY || + sx >= 240) { + } else { + u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<3) + ((xxx >> 3)<<6) + + (xxx & 7))&0x7fff)]; + if(color) { + lineOBJWin[sx] = 1; + } + } + sx = (sx+1)&511; + realX += dx; + realY += dy; + } + } else { + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if(DISPCNT & 0x40) + inc = sizeX >> 3; + // int palette = (a2 >> 8) & 0xF0; + for(int x = 0; x < fieldX; x++) { + if (x >= startpix) + lineOBJpix-=2; + if (lineOBJpix<0) + continue; + int xxx = realX >> 8; + int yyy = realY >> 8; + + // if(x == 0 || x == (sizeX-1) || + // t == 0 || t == (sizeY-1)) { + // lineOBJ[sx] = 0x001F | prio; + // } else { + if(xxx < 0 || xxx >= sizeX || + yyy < 0 || yyy >= sizeY || + sx >= 240) { + } else { + u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<2) + ((xxx >> 3)<<5) + + ((xxx & 7)>>1))&0x7fff)]; + if(xxx & 1) + color >>= 4; + else + color &= 0x0F; + + if(color) { + lineOBJWin[sx] = 1; + } + } + // } + sx = (sx+1)&511; + realX += dx; + realY += dy; + } + } + } + } + } else { + if((sy+sizeY) > 256) + sy -= 256; + int t = VCOUNT - sy; + if((t >= 0) && (t < sizeY)) { + int sx = (a1 & 0x1FF); + int startpix = 0; + if ((sx+sizeX)> 512) + { + startpix=512-sx; + } + if((sx < 240) || startpix) { + lineOBJpix+=2; + if(a0 & 0x2000) { + if(a1 & 0x2000) + t = sizeY - t - 1; + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if(DISPCNT & 0x40) { + inc = sizeX >> 2; + } else { + c &= 0x3FE; + } + int xxx = 0; + if(a1 & 0x1000) + xxx = sizeX-1; + int address = 0x10000 + ((((c+ (t>>3) * inc) << 5) + + ((t & 7) << 3) + ((xxx>>3)<<6) + (xxx & 7))&0x7fff); + if(a1 & 0x1000) + xxx = 7; + // u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + for(int xx = 0; xx < sizeX; xx++) { + if (xx >= startpix) + lineOBJpix--; + if (lineOBJpix<0) + continue; + if(sx < 240) { + u8 color = vram[address]; + if(color) { + lineOBJWin[sx] = 1; + } + } + + sx = (sx+1) & 511; + if(a1 & 0x1000) { + xxx--; + address--; + if(xxx == -1) { + address -= 56; + xxx = 7; + } + if(address < 0x10000) + address += 0x8000; + } else { + xxx++; + address++; + if(xxx == 8) { + address += 56; + xxx = 0; + } + if(address > 0x17fff) + address -= 0x8000; + } + } + } else { + if(a1 & 0x2000) + t = sizeY - t - 1; + int c = (a2 & 0x3FF); + if((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if(DISPCNT & 0x40) { + inc = sizeX >> 3; + } + int xxx = 0; + if(a1 & 0x1000) + xxx = sizeX - 1; + int address = 0x10000 + ((((c + (t>>3) * inc)<<5) + + ((t & 7)<<2) + ((xxx>>3)<<5) + ((xxx & 7) >> 1))&0x7fff); + // u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + // int palette = (a2 >> 8) & 0xF0; + if(a1 & 0x1000) { + xxx = 7; + for(int xx = sizeX - 1; xx >= 0; xx--) { + if (xx >= startpix) + lineOBJpix--; + if (lineOBJpix<0) + continue; + if(sx < 240) { + u8 color = vram[address]; + if(xx & 1) { + color = (color >> 4); + } else + color &= 0x0F; + + if(color) { + lineOBJWin[sx] = 1; + } + } + sx = (sx+1) & 511; + xxx--; + if(!(xx & 1)) + address--; + if(xxx == -1) { + xxx = 7; + address -= 28; + } + if(address < 0x10000) + address += 0x8000; + } + } else { + for(int xx = 0; xx < sizeX; xx++) { + if (xx >= startpix) + lineOBJpix--; + if (lineOBJpix<0) + continue; + if(sx < 240) { + u8 color = vram[address]; + if(xx & 1) { + color = (color >> 4); + } else + color &= 0x0F; + + if(color) { + lineOBJWin[sx] = 1; + } + } + sx = (sx+1) & 511; + xxx++; + if(xx & 1) + address++; + if(xxx == 8) { + address += 28; + xxx = 0; + } + if(address > 0x17fff) + address -= 0x8000; + } + } + } + } + } + } + } + } +} + +static inline u32 gfxIncreaseBrightness(u32 color, int coeff) +{ + color &= 0xffff; + color = ((color << 16) | color) & 0x3E07C1F; + + color = color + (((0x3E07C1F - color) * coeff) >> 4); + color &= 0x3E07C1F; + + return (color >> 16) | color; +} + +static inline void gfxIncreaseBrightness(u32 *line, int coeff) +{ + for(int x = 0; x < 240; x++) { + u32 color = *line; + int r = (color & 0x1F); + int g = ((color >> 5) & 0x1F); + int b = ((color >> 10) & 0x1F); + + r = r + (((31 - r) * coeff) >> 4); + g = g + (((31 - g) * coeff) >> 4); + b = b + (((31 - b) * coeff) >> 4); + if(r > 31) + r = 31; + if(g > 31) + g = 31; + if(b > 31) + b = 31; + *line++ = (color & 0xFFFF0000) | (b << 10) | (g << 5) | r; + } +} + +static inline u32 gfxDecreaseBrightness(u32 color, int coeff) +{ + color &= 0xffff; + color = ((color << 16) | color) & 0x3E07C1F; + + color = color - (((color * coeff) >> 4) & 0x3E07C1F); + + return (color >> 16) | color; +} + +static inline void gfxDecreaseBrightness(u32 *line, int coeff) +{ + for(int x = 0; x < 240; x++) { + u32 color = *line; + int r = (color & 0x1F); + int g = ((color >> 5) & 0x1F); + int b = ((color >> 10) & 0x1F); + + r = r - ((r * coeff) >> 4); + g = g - ((g * coeff) >> 4); + b = b - ((b * coeff) >> 4); + if(r < 0) + r = 0; + if(g < 0) + g = 0; + if(b < 0) + b = 0; + *line++ = (color & 0xFFFF0000) | (b << 10) | (g << 5) | r; + } +} + +static inline u32 gfxAlphaBlend(u32 color, u32 color2, int ca, int cb) +{ + if(color < 0x80000000) { + color&=0xffff; + color2&=0xffff; + + color = ((color << 16) | color) & 0x03E07C1F; + color2 = ((color2 << 16) | color2) & 0x03E07C1F; + color = ((color * ca) + (color2 * cb)) >> 4; + + if ((ca + cb)>16) + { + if (color & 0x20) + color |= 0x1f; + if (color & 0x8000) + color |= 0x7C00; + if (color & 0x4000000) + color |= 0x03E00000; + } + + color &= 0x03E07C1F; + color = (color >> 16) | color; + } + return color; +} + +static inline void gfxAlphaBlend(u32 *ta, u32 *tb, int ca, int cb) +{ + for(int x = 0; x < 240; x++) { + u32 color = *ta; + if(color < 0x80000000) { + int r = (color & 0x1F); + int g = ((color >> 5) & 0x1F); + int b = ((color >> 10) & 0x1F); + u32 color2 = (*tb++); + int r0 = (color2 & 0x1F); + int g0 = ((color2 >> 5) & 0x1F); + int b0 = ((color2 >> 10) & 0x1F); + + r = ((r * ca) + (r0 * cb)) >> 4; + g = ((g * ca) + (g0 * cb)) >> 4; + b = ((b * ca) + (b0 * cb)) >> 4; + + if(r > 31) + r = 31; + if(g > 31) + g = 31; + if(b > 31) + b = 31; + + *ta++ = (color & 0xFFFF0000) | (b << 10) | (g << 5) | r; + } else { + ta++; + tb++; + } + } +} + +#endif // VBA_GFX_H diff --git a/src/agb/GBALink.cpp b/src/agb/GBALink.cpp new file mode 100644 index 00000000..78317bbc --- /dev/null +++ b/src/agb/GBALink.cpp @@ -0,0 +1,1083 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team +// This file was written by denopqrihg + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +// Link.cpp : Emulation of GBA link accessories +// + +#include "GBA.h" +#include +#include "../win32/stdafx.h" +#include "../port.h" +#include "GBALink.h" +#include "../win32/vba.h" +#include "../win32/MainWnd.h" +#include "../win32/LinkOptions.h" +#include "../win32/Reg.h" + +#define UPDATE_REG(address, value) WRITE16LE(((u16 *)&ioMem[address]),value) +#define GBLINK_READY 8 + +int linktime = 0; +u8 tspeed=3; +u8 transfer=0; +LINKDATA *linkmem=NULL; +int linkid = 0, vbaid = 0; +HANDLE linksync[4]; +int savedlinktime=0; +HANDLE mmf=NULL; +char linkevent[] = "VBA link event "; +static int i, j; +int linktimeout = 1000; +int linklog = 0; +FILE *linklogfile = NULL; +LANLINKDATA lanlink; +u16 linkdata[4]; +int lspeed = 0; +lserver ls; +lclient lc; +bool oncewait = false, after = false; +bool adapter = false; +bool linkenable = false; +u8 rfu_cmd, rfu_qsend, rfu_qrecv; +int rfu_state, rfu_polarity, linktime2, counter, rfu_masterq; +int transferend, numtransfers = 0; +u32 rfu_masterdata[32]; + +extern unsigned char *gbMemory; +extern int gbInterrupt; + +int trtimedata[4][4] = {{34080, 8520, 5680, 2840}, {65536, 16384, 10923, 5461}, {99609, 24903, 16602, 8301}, {133692, 33423, 22282, 11141}}; +int trtimeend[3][4] = {{72527, 18132, 12088, 6044}, {106608, 26652, 17768, 8884}, {133692, 33423, 22282, 11141}}; +int gbtime = 1024; + +DWORD WINAPI LinkClientThread(void *); +DWORD WINAPI LinkServerThread(void *); +int StartServer(void); +int GetSioMode(u16, u16); +u16 StartRFU(u16); + +char *MakeInstanceFilename(const char *Input) +{ + if (vbaid == 0) + return (char *)Input; + + static char *result=NULL; + if (result!=NULL) + free(result); + + result = (char *)malloc(strlen(Input)+3); + char *p = strrchr((char *)Input, '.'); + sprintf(result, "%.*s-%d.%s", (int)(p-Input), Input, vbaid+1, p+1); + return result; +} + + +void StartLink(WORD value){ + if(ioMem==NULL) return; + if(adapter){ + UPDATE_REG(0x128, StartRFU(value)); + return; + } + switch(GetSioMode(value, READ16LE(&ioMem[0x134]))){ + case MULTIPLAYER: + if(value & 0x80){ + if(!linkid){ + if(!transfer){ + if(lanlink.active){ + if(lanlink.connected){ + linkdata[0] = READ16LE(&ioMem[0x12a]); + savedlinktime = linktime; + tspeed = value & 3; + ls.Send(); + transfer = 1; + linktime = 0; + UPDATE_REG(0x120, linkdata[0]); + UPDATE_REG(0x122, 0xffff); + WRITE32LE(&ioMem[0x124], 0xffffffff); + if(lanlink.speed&&oncewait==false) ls.howmanytimes++; + after = false; + } + } else if(linkmem->numgbas>1){ + ResetEvent(linksync[0]); + linkmem->linkcmd[0] = ('M'<<8)+(value&3); + linkmem->linkdata[0] = READ16LE(&ioMem[0x12a]); + if(linkmem->numtransfers!=0) linkmem->lastlinktime = linktime; + else linkmem->lastlinktime = 0; + if((++linkmem->numtransfers)==0) linkmem->numtransfers=2; + transfer = 1; + linktime = 0; + tspeed = value & 3; + WRITE32LE(&ioMem[0x120], 0xffffffff); + WRITE32LE(&ioMem[0x124], 0xffffffff); + } + } + } + value &= 0xff7f; + value |= (transfer!=0)<<7; + } + value &= 0xff8b; + value |= (linkid ? 0xc : 8); + value |= linkid<<4; + UPDATE_REG(0x128, value); + if(linkid) UPDATE_REG(0x134, 7); + else UPDATE_REG(0x134, 3); + break; + case NORMAL8: + if(linklog) fprintf(linklogfile, "Attempt to use 8-bit Normal mode %04x\n", value); + UPDATE_REG(0x128, value); + break; + case NORMAL32: + if(linklog) fprintf(linklogfile, "Attempt to use 32-bit Normal mode %04x %x%x\n", value, READ16LE(&ioMem[0x122]), READ16LE(&ioMem[0x120])); + UPDATE_REG(0x128, value); + break; + case UART: + if(linklog) fprintf(linklogfile, "Attempt to use UART mode %04x\n", value); + UPDATE_REG(0x128, value); + break; + default: + UPDATE_REG(0x128, value); + break; + } +} + +void StartGPLink(u16 value){ + if(!value){ + UPDATE_REG(0x134, 0); + return; + } + switch(GetSioMode(READ16LE(&ioMem[0x128]), value)){ + case MULTIPLAYER: + value &= 0xc0f0; + value |= 3; + if(linkid) value |= 4; + UPDATE_REG(0x134, value); + UPDATE_REG(0x128, ((READ16LE(&ioMem[0x128])&0xff8b)|(linkid ? 0xc : 8)|(linkid<<4))); + return; + break; + case GP: + if(linklog){ + if(value==0x8000) fprintf(linklogfile, "Circuit reset\n"); + else if(!adapter) fprintf(linklogfile, "Attempt to use General-purpose mode %04x\n", value); + } + if(adapter) rfu_state = RFU_INIT; + // This was not there, but sonic games won't start if it's not here. + UPDATE_REG(0x134, value); + break; + case JOYBUS: + UPDATE_REG(0x134, value); + break; + default: + UPDATE_REG(0x134, value); + break; + } + return; +} + +void StartJOYLink(u16 value){ + if(!value){ + UPDATE_REG(0x140, 0); + return; + } + if(GetSioMode(READ16LE(&ioMem[0x128]), READ16LE(&ioMem[0x134]))==JOYBUS&&linklog) fprintf(linklogfile, "Attempt to use JOY-BUS mode %04x\n", value); + return; +} + +void LinkUpdate(int ticks){ + linktime += ticks; + if(adapter){ + linktime2 += ticks; + transferend -= ticks; + if(transfer&&transferend<=0){ + transfer = 0; + if(READ16LE(&ioMem[0x128])&0x4000){ + IF |= 0x80; + UPDATE_REG(0x202, IF); + } + UPDATE_REG(0x128, READ16LE(&ioMem[0x128]) & 0xff7f); + } + return; + } + + if(lanlink.active){ + if(lanlink.connected){ + if(after){ + if(linkid&&linktime>6044){ + lc.Recv(); + oncewait = true; + } else return; + } + if(linkid&&!transfer&&lc.numtransfers>0&&linktime>=savedlinktime){ + linkdata[linkid] = READ16LE(&ioMem[0x12a]); + if(!lc.oncesend) lc.Send(); + lc.oncesend = false; + UPDATE_REG(0x120, linkdata[0]); + UPDATE_REG(0x128, READ16LE(&ioMem[0x128]) | 0x80); + transfer = 1; + if(lc.numtransfers==1) linktime = 0; + else linktime -= savedlinktime; + } + if(transfer&&linktime>=trtimeend[lanlink.numgbas-1][tspeed]){ + if(READ16LE(&ioMem[0x128]) & 0x4000){ + IF |= 0x80; + UPDATE_REG(0x202, IF); + } + UPDATE_REG(0x128, (READ16LE(&ioMem[0x128]) & 0xff0f) | (linkid << 4)); + transfer = 0; + linktime -= trtimeend[lanlink.numgbas-1][tspeed]; + oncewait = false; + if(!lanlink.speed){ + if(linkid) lc.Recv(); + else ls.Recv(); + UPDATE_REG(0x122, linkdata[1]); + UPDATE_REG(0x124, linkdata[2]); + UPDATE_REG(0x126, linkdata[3]); + if(linklog) fprintf(linklogfile, "%04x %04x %04x %04x %10u\n", linkdata[0], linkdata[1], linkdata[2], linkdata[3], savedlinktime); + oncewait = true; + } else { + after = true; + if(lanlink.numgbas==1){ + UPDATE_REG(0x122, linkdata[1]); + UPDATE_REG(0x124, linkdata[2]); + UPDATE_REG(0x126, linkdata[3]); + if(linklog) fprintf(linklogfile, "%04x %04x %04x %04x %10u\n", linkdata[0], linkdata[1], linkdata[2], linkdata[3], savedlinktime); + } + + } + } + } + return; + } + // ** CRASH ** linkmem is NULL, todo investigate why, added null check + if(linkid&&!transfer&&linkmem&&linktime>=linkmem->lastlinktime&&linkmem->numtransfers){ + linkmem->linkdata[linkid] = READ16LE(&ioMem[0x12a]); + + if(linkmem->numtransfers==1){ + linktime = 0; + if(WaitForSingleObject(linksync[linkid], linktimeout)==WAIT_TIMEOUT) linkmem->numtransfers=0; + } else linktime -= linkmem->lastlinktime; + + switch((linkmem->linkcmd[0])>>8){ + case 'M': + tspeed = (linkmem->linkcmd[0]) & 3; + transfer = 1; + WRITE32LE(&ioMem[0x120], 0xffffffff); + WRITE32LE(&ioMem[0x124], 0xffffffff); + UPDATE_REG(0x128, READ16LE(&ioMem[0x128]) | 0x80); + break; + } + } + + if(!transfer) return; + + if(transfer&&linktime>=trtimedata[transfer-1][tspeed]&&transfer<=linkmem->numgbas){ + if(transfer-linkid==2){ + SetEvent(linksync[linkid+1]); + if(WaitForSingleObject(linksync[linkid], linktimeout)==WAIT_TIMEOUT) + linkmem->numtransfers=0; + ResetEvent(linksync[linkid]); + if(linklog) fprintf(linklogfile, "%04x %04x %04x %04x %10u\n", + linkmem->linkdata[0], linkmem->linkdata[1], linkmem->linkdata[2], linkmem->linkdata[3], linkmem->lastlinktime); + } + + + UPDATE_REG(0x11e + (transfer<<1), linkmem->linkdata[transfer-1]); + transfer++; + } + + if(transfer&&linktime>=trtimeend[linkmem->numgbas-2][tspeed]){ + if(linkid==linkmem->numgbas-1){ + SetEvent(linksync[0]); + if(WaitForSingleObject(linksync[linkid], linktimeout)==WAIT_TIMEOUT) + linkmem->numtransfers=0; + ResetEvent(linksync[linkid]); + if(linklog) fprintf(linklogfile, "%04x %04x %04x %04x %10u\n", + linkmem->linkdata[0], linkmem->linkdata[1], linkmem->linkdata[2], linkmem->linkdata[3], linkmem->lastlinktime); + } + transfer = 0; + linktime -= trtimeend[0][tspeed]; + if(READ16LE(&ioMem[0x128]) & 0x4000){ + IF |= 0x80; + UPDATE_REG(0x202, IF); + } + UPDATE_REG(0x128, (READ16LE(&ioMem[0x128]) & 0xff0f) | (linkid << 4)); + linkmem->linkdata[linkid] = 0xffff; + } + + return; +} + +inline int GetSioMode(u16 reg1, u16 reg2){ + if(!(reg2&0x8000)){ + switch(reg1&0x3000){ + case 0x0000: + return NORMAL8; + case 0x1000: + return NORMAL32; + case 0x2000: + return MULTIPLAYER; + case 0x3000: + return UART; + } + } + if(reg2&0x4000) return JOYBUS; + return GP; +} + +u16 StartRFU(u16 value){ + switch(GetSioMode(value, READ16LE(&ioMem[0x134]))){ + case NORMAL8: + rfu_polarity = 0; + return value; + break; + case NORMAL32: + if(value&8) value &= 0xfffb; // A kind of acknowledge procedure + else value |= 4; + if(value&0x80){ + if((value&3)==1) transferend = 2048; + else transferend = 256; + u16 a = READ16LE(&ioMem[0x122]); + switch(rfu_state){ + case RFU_INIT: + if(READ32LE(&ioMem[0x120])==0xb0bb8001){ + rfu_state = RFU_COMM; // end of startup + } + UPDATE_REG(0x122, READ16LE(&ioMem[0x120])); + UPDATE_REG(0x120, a); + break; + case RFU_COMM: + if(a==0x9966){ + rfu_cmd = ioMem[0x120]; + if((rfu_qsend=ioMem[0x121])!=0){ + rfu_state = RFU_SEND; + counter = 0; + } + if(rfu_cmd==0x25||rfu_cmd==0x24){ + linkmem->rfu_q[vbaid] = rfu_qsend; + } + UPDATE_REG(0x120, 0); + UPDATE_REG(0x122, 0x8000); + } else if(a==0x8000){ + switch(rfu_cmd){ + case 0x1a: // check if someone joined + if(linkmem->rfu_request[vbaid]!=0){ + rfu_state = RFU_RECV; + rfu_qrecv = 1; + } + linkid = -1; + rfu_cmd |= 0x80; + break; + case 0x1e: // receive broadcast data + case 0x1d: // no visible difference + rfu_polarity = 0; + rfu_state = RFU_RECV; + rfu_qrecv = 7; + counter = 0; + rfu_cmd |= 0x80; + break; + case 0x30: + linkmem->rfu_request[vbaid] = 0; + linkmem->rfu_q[vbaid] = 0; + linkid = 0; + numtransfers = 0; + rfu_cmd |= 0x80; + if(linkmem->numgbas==2) SetEvent(linksync[1-vbaid]); + break; + case 0x11: // ? always receives 0xff - I suspect it's something for 3+ players + case 0x13: // unknown + case 0x20: // this has something to do with 0x1f + case 0x21: // this too + rfu_cmd |= 0x80; + rfu_polarity = 0; + rfu_state = 3; + rfu_qrecv = 1; + break; + case 0x26: + if(linkid>0){ + rfu_qrecv = rfu_masterq; + } + if((rfu_qrecv=linkmem->rfu_q[1-vbaid])!=0){ + rfu_state = RFU_RECV; + counter = 0; + } + rfu_cmd |= 0x80; + break; + case 0x24: // send data + if((numtransfers++)==0) linktime = 1; + linkmem->rfu_linktime[vbaid] = linktime; + if(linkmem->numgbas==2){ + SetEvent(linksync[1-vbaid]); + WaitForSingleObject(linksync[vbaid], linktimeout); + ResetEvent(linksync[vbaid]); + } + rfu_cmd |= 0x80; + linktime = 0; + linkid = -1; + break; + case 0x25: // send & wait for data + case 0x1f: // pick a server + case 0x10: // init + case 0x16: // send broadcast data + case 0x17: // setup or something ? + case 0x27: // wait for data ? + case 0x3d: // init + default: + rfu_cmd |= 0x80; + break; + case 0xa5: // 2nd part of send&wait function 0x25 + case 0xa7: // 2nd part of wait function 0x27 + if(linkid==-1){ + linkid++; + linkmem->rfu_linktime[vbaid] = 0; + } + if(linkid&&linkmem->rfu_request[1-vbaid]==0){ + linkmem->rfu_q[1-vbaid] = 0; + transferend = 256; + rfu_polarity = 1; + rfu_cmd = 0x29; + linktime = 0; + break; + } + if((numtransfers++)==0) linktime = 0; + linkmem->rfu_linktime[vbaid] = linktime; + if(linkmem->numgbas==2){ + if(!linkid||(linkid&&numtransfers)) SetEvent(linksync[1-vbaid]); + WaitForSingleObject(linksync[vbaid], linktimeout); + ResetEvent(linksync[vbaid]); + } + if(linkid>0){ + memcpy(rfu_masterdata, linkmem->rfu_data[1-vbaid], 128); + rfu_masterq = linkmem->rfu_q[1-vbaid]; + } + transferend = linkmem->rfu_linktime[1-vbaid] - linktime + 256; + if(transferend<256) transferend = 256; + linktime = -transferend; + rfu_polarity = 1; + rfu_cmd = 0x28; + break; + } + UPDATE_REG(0x122, 0x9966); + UPDATE_REG(0x120, (rfu_qrecv<<8) | rfu_cmd); + } else { + UPDATE_REG(0x120, 0); + UPDATE_REG(0x122, 0x8000); + } + break; + case RFU_SEND: + if(--rfu_qsend==0) rfu_state = RFU_COMM; + switch(rfu_cmd){ + case 0x16: + linkmem->rfu_bdata[vbaid][counter++] = READ32LE(&ioMem[0x120]); + break; + case 0x17: + linkid = 1; + break; + case 0x1f: + linkmem->rfu_request[1-vbaid] = 1; + break; + case 0x24: + case 0x25: + linkmem->rfu_data[vbaid][counter++] = READ32LE(&ioMem[0x120]); + break; + } + UPDATE_REG(0x120, 0); + UPDATE_REG(0x122, 0x8000); + break; + case RFU_RECV: + if(--rfu_qrecv==0) rfu_state = RFU_COMM; + switch(rfu_cmd){ + case 0x9d: + case 0x9e: + if(counter==0){ + UPDATE_REG(0x120, 0x61f1); + UPDATE_REG(0x122, 0); + counter++; + break; + } + UPDATE_REG(0x120, linkmem->rfu_bdata[1-vbaid][counter-1]&0xffff); + UPDATE_REG(0x122, linkmem->rfu_bdata[1-vbaid][counter-1]>>16); + counter++; + break; + case 0xa6: + if(linkid>0){ + UPDATE_REG(0x120, rfu_masterdata[counter]&0xffff); + UPDATE_REG(0x122, rfu_masterdata[counter++]>>16); + } else { + UPDATE_REG(0x120, linkmem->rfu_data[1-vbaid][counter]&0xffff); + UPDATE_REG(0x122, linkmem->rfu_data[1-vbaid][counter++]>>16); + } + break; + case 0x93: // it seems like the game doesn't care about this value + UPDATE_REG(0x120, 0x1234); // put anything in here + UPDATE_REG(0x122, 0x0200); // also here, but it should be 0200 + break; + case 0xa0: + case 0xa1: + UPDATE_REG(0x120, 0x641b); + UPDATE_REG(0x122, 0x0000); + break; + case 0x9a: + UPDATE_REG(0x120, 0x61f9); + UPDATE_REG(0x122, 0); + break; + case 0x91: + UPDATE_REG(0x120, 0x00ff); + UPDATE_REG(0x122, 0x0000); + break; + default: + UPDATE_REG(0x120, 0x0173); + UPDATE_REG(0x122, 0x0000); + break; + } + break; + } + transfer = 1; + } + if(rfu_polarity) value ^= 4; // sometimes it's the other way around + default: + return value; + } +} + +void gbLinkStart(u8 value){ +// Not in this version :-) +} + + +void gbLinkUpdate(void){ +} + +int InitLink(void){ + WSADATA wsadata; + BOOL disable = true; + + linkid = 0; + + if(WSAStartup(MAKEWORD(1,1), &wsadata)!=0){ + WSACleanup(); + return 0; + } + + if((lanlink.tcpsocket=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))==INVALID_SOCKET){ + MessageBox(NULL, "Couldn't create socket.", "Error!", MB_OK); + WSACleanup(); + return 0; + } + + setsockopt(lanlink.tcpsocket, IPPROTO_TCP, TCP_NODELAY, (char*)&disable, sizeof(BOOL)); + + if((mmf=CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(LINKDATA), "VBA link memory"))==NULL){ + closesocket(lanlink.tcpsocket); + WSACleanup(); + MessageBox(NULL, "Error creating file mapping", "Error", MB_OK|MB_ICONEXCLAMATION); + return 0; + } + + if(GetLastError() == ERROR_ALREADY_EXISTS) + vbaid = 1; + else + vbaid = 0; + + if((linkmem=(LINKDATA *)MapViewOfFile(mmf, FILE_MAP_WRITE, 0, 0, sizeof(LINKDATA)))==NULL){ + closesocket(lanlink.tcpsocket); + WSACleanup(); + CloseHandle(mmf); + MessageBox(NULL, "Error mapping file", "Error", MB_OK|MB_ICONEXCLAMATION); + return 0; + } + + if(linkmem->linkflags&LINK_PARENTLOST) + vbaid = 0; + + if(vbaid==0){ + linkid = 0; + if(linkmem->linkflags&LINK_PARENTLOST){ + linkmem->numgbas++; + linkmem->linkflags &= ~LINK_PARENTLOST; + } + else + linkmem->numgbas=1; + + for(i=0;i<4;i++){ + linkevent[15]=(char)i+'1'; + if((linksync[i]=CreateEvent(NULL, true, false, linkevent))==NULL){ + closesocket(lanlink.tcpsocket); + WSACleanup(); + UnmapViewOfFile(linkmem); + CloseHandle(mmf); + for(j=0;jnumgbas; + linkid = vbaid; + linkmem->numgbas++; + + linklog = 0; + if(linkmem->numgbas>4){ + linkmem->numgbas=4; + closesocket(lanlink.tcpsocket); + WSACleanup(); + MessageBox(NULL, "5 or more GBAs not supported.", "Error!", MB_OK|MB_ICONEXCLAMATION); + UnmapViewOfFile(linkmem); + CloseHandle(mmf); + return 0; + } + for(i=0;i<4;i++){ + linkevent[15]=(char)i+'1'; + if((linksync[i]=OpenEvent(EVENT_ALL_ACCESS, false, linkevent))==NULL){ + closesocket(lanlink.tcpsocket); + WSACleanup(); + CloseHandle(mmf); + UnmapViewOfFile(linkmem); + for(j=0;jlastlinktime=0xffffffff; + linkmem->numtransfers=0; + linkmem->linkflags=0; + lanlink.connected = false; + lanlink.thread = NULL; + lanlink.speed = false; + for(i=0;i<4;i++){ + linkmem->linkdata[i] = 0xffff; + linkdata[i] = 0xffff; + } +return 1; +} + +int openLinkLog(void){ + char filename[20]; + if(linklog){ + sprintf(filename, "vbalog%1d.txt", vbaid+1); + if((linklogfile=fopen(filename, "at"))==NULL){ + linklog=false; + return 0; + } + fprintf(linklogfile, "----- Log opened -----\n"); + } + return 1; +} + +void closeLinkLog() +{ + if(linklogfile) + { + fclose(linklogfile); + linklogfile=NULL; + } +} + +void CloseLink(void){ + if(lanlink.connected){ + if(linkid){ + char outbuffer[4]; + outbuffer[0] = 4; + outbuffer[1] = -32; + if(lanlink.type==0) send(lanlink.tcpsocket, outbuffer, 4, 0); + } else { + char outbuffer[12]; + int i; + outbuffer[0] = 12; + outbuffer[1] = -32; + for(i=1;i<=lanlink.numgbas;i++){ + if(lanlink.type==0){ + send(ls.tcpsocket[i], outbuffer, 12, 0); + } + closesocket(ls.tcpsocket[i]); + } + } + } + linkmem->numgbas--; + if(!linkid&&linkmem->numgbas!=0) + linkmem->linkflags|=LINK_PARENTLOST; + CloseHandle(mmf); + UnmapViewOfFile(linkmem); + + for(i=0;i<4;i++){ + if(linksync[i]!=NULL){ + PulseEvent(linksync[i]); + CloseHandle(linksync[i]); + } + } + regSetDwordValue("LAN", lanlink.active); + if(linklog) closeLinkLog(); + closesocket(lanlink.tcpsocket); + WSACleanup(); +return; +} + +lserver::lserver(void){ + intinbuffer = (int*)inbuffer; + u16inbuffer = (u16*)inbuffer; + intoutbuffer = (int*)outbuffer; + u16outbuffer = (u16*)outbuffer; + oncewait = false; +} + +int lserver::Init(void *serverdlg){ + SOCKADDR_IN info; + DWORD nothing; + char str[100]; + + info.sin_family = AF_INET; + info.sin_addr.S_un.S_addr = INADDR_ANY; + info.sin_port = htons(5738); + + if(bind(lanlink.tcpsocket, (LPSOCKADDR)&info, sizeof(SOCKADDR_IN))==SOCKET_ERROR){ + closesocket(lanlink.tcpsocket); + if((lanlink.tcpsocket=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))==INVALID_SOCKET) + return WSAGetLastError(); + if(bind(lanlink.tcpsocket, (LPSOCKADDR)&info, sizeof(SOCKADDR_IN))==SOCKET_ERROR) + return WSAGetLastError(); + } + + if(listen(lanlink.tcpsocket, lanlink.numgbas)==SOCKET_ERROR) + return WSAGetLastError(); + + if(lanlink.thread!=NULL){ + lanlink.terminate = true; + WaitForSingleObject(linksync[vbaid], 500); + lanlink.thread = NULL; + } + lanlink.terminate = false; + linkid = 0; + + gethostname(str, 100); + ((ServerWait*)serverdlg)->m_serveraddress.Format("Server IP address is: %s", inet_ntoa(*(LPIN_ADDR)(gethostbyname(str)->h_addr_list[0]))); + + lanlink.thread = CreateThread(NULL, 0, LinkServerThread, serverdlg, 0, ¬hing); + + return 0; + +} + +DWORD WINAPI LinkServerThread(void *serverdlg){ + fd_set fdset; + timeval wsocktimeout; + char inbuffer[256], outbuffer[256]; + int *intinbuffer = (int*)inbuffer; + u16 *u16inbuffer = (u16*)inbuffer; + int *intoutbuffer = (int*)outbuffer; + u16 *u16outbuffer = (u16*)outbuffer; + BOOL disable = true; + + wsocktimeout.tv_sec = 1; + wsocktimeout.tv_usec = 0; + i = 0; + + while(im_plconn[i].Format("Player %d connected", i+1); + ((ServerWait*)serverdlg)->UpdateData(false); + i++; + } + } + ((ServerWait*)serverdlg)->m_prgctrl.StepIt(); + } + MessageBox(NULL, "All players connected", "Link", MB_OK); + ((ServerWait*)serverdlg)->SendMessage(WM_CLOSE, 0, 0); + + for(i=1;i<=lanlink.numgbas;i++){ + outbuffer[0] = 4; + send(ls.tcpsocket[i], outbuffer, 4, 0); + } + + lanlink.connected = true; + + return 0; +} + +void lserver::Send(void){ + if(lanlink.type==0){ // TCP + if(savedlinktime==-1){ + outbuffer[0] = 4; + outbuffer[1] = -32; //0xe0 + for(i=1;i<=lanlink.numgbas;i++){ + send(tcpsocket[i], outbuffer, 4, 0); + recv(tcpsocket[i], inbuffer, 4, 0); + } + } + outbuffer[1] = tspeed; + u16outbuffer[1] = linkdata[0]; + intoutbuffer[1] = savedlinktime; + if(lanlink.numgbas==1){ + if(lanlink.type==0){ + outbuffer[0] = 8; + send(tcpsocket[1], outbuffer, 8, 0); + } + } + else if(lanlink.numgbas==2){ + u16outbuffer[4] = linkdata[2]; + if(lanlink.type==0){ + outbuffer[0] = 10; + send(tcpsocket[1], outbuffer, 10, 0); + u16outbuffer[4] = linkdata[1]; + send(tcpsocket[2], outbuffer, 10, 0); + } + } else { + if(lanlink.type==0){ + outbuffer[0] = 12; + u16outbuffer[4] = linkdata[2]; + u16outbuffer[5] = linkdata[3]; + send(tcpsocket[1], outbuffer, 12, 0); + u16outbuffer[4] = linkdata[1]; + send(tcpsocket[2], outbuffer, 12, 0); + u16outbuffer[5] = linkdata[2]; + send(tcpsocket[3], outbuffer, 12, 0); + } + } + } + return; +} + +void lserver::Recv(void){ + int numbytes; + if(lanlink.type==0){ // TCP + wsocktimeout.tv_usec = 0; + wsocktimeout.tv_sec = linktimeout / 1000; + fdset.fd_count = lanlink.numgbas; + for(i=0;i1) memcpy(inbuffer, inbuffer+inbuffer[0]*(howmanytimes-1), inbuffer[0]); + if(inbuffer[1]==-32){ + char message[30]; + lanlink.connected = false; + sprintf(message, "Player %d disconnected.", i+2); + MessageBox(NULL, message, "Link", MB_OK); + outbuffer[0] = 4; + outbuffer[1] = -32; + for(i=1;ih_addr_list); + + if(ioctlsocket(lanlink.tcpsocket, FIONBIO, ¬block)==SOCKET_ERROR) + return WSAGetLastError(); + + if(lanlink.thread!=NULL){ + lanlink.terminate = true; + WaitForSingleObject(linksync[vbaid], 500); + lanlink.thread = NULL; + } + + ((ServerWait*)waitdlg)->SetWindowText("Connecting..."); + lanlink.terminate = false; + lanlink.thread = CreateThread(NULL, 0, LinkClientThread, waitdlg, 0, ¬hing); + return 0; +} + +DWORD WINAPI LinkClientThread(void *waitdlg){ + fd_set fdset; + timeval wsocktimeout; + int numbytes; + char inbuffer[16]; + u16 *u16inbuffer = (u16*)inbuffer; + unsigned long block = 0; + + if(connect(lanlink.tcpsocket, (LPSOCKADDR)&lc.serverinfo, sizeof(SOCKADDR_IN))==SOCKET_ERROR){ + if(WSAGetLastError()!=WSAEWOULDBLOCK){ + MessageBox(NULL, "Couldn't connect to server.", "Link", MB_OK); + return 1; + } + wsocktimeout.tv_sec = 1; + wsocktimeout.tv_usec = 0; + do{ + if(lanlink.terminate) return 0; + fdset.fd_count = 1; + fdset.fd_array[0] = lanlink.tcpsocket; + ((ServerWait*)waitdlg)->m_prgctrl.StepIt(); + } while(select(0, NULL, &fdset, NULL, &wsocktimeout)!=1&&connect(lanlink.tcpsocket, (LPSOCKADDR)&lc.serverinfo, sizeof(SOCKADDR_IN))!=0); + } + + ioctlsocket(lanlink.tcpsocket, FIONBIO, &block); + + numbytes = 0; + while(numbytes<4) + numbytes += recv(lanlink.tcpsocket, inbuffer+numbytes, 16, 0); + linkid = (int)u16inbuffer[0]; + lanlink.numgbas = (int)u16inbuffer[1]; + + ((ServerWait*)waitdlg)->m_serveraddress.Format("Connected as #%d", linkid+1); + if(lanlink.numgbas!=linkid) ((ServerWait*)waitdlg)->m_plconn[0].Format("Waiting for %d players to join", lanlink.numgbas-linkid); + else ((ServerWait*)waitdlg)->m_plconn[0].Format("All players joined."); + + numbytes = 0; + inbuffer[0] = 1; + while(numbytesSendMessage(WM_CLOSE, 0, 0); + + block = 1; + + ioctlsocket(lanlink.tcpsocket, FIONBIO, &block); + + lanlink.connected = true; + return 0; +} + +void lclient::CheckConn(void){ + if((numbytes=recv(lanlink.tcpsocket, inbuffer, 256, 0))>0){ + while(numbytes + +#ifndef LINKH +#define LINKH +#define LINK_PARENTLOST 0x80 +#define UNSUPPORTED -1 +#define MULTIPLAYER 0 +#define NORMAL8 1 +#define NORMAL32 2 +#define UART 3 +#define JOYBUS 4 +#define GP 5 +#define RFU_INIT 0 +#define RFU_COMM 1 +#define RFU_SEND 2 +#define RFU_RECV 3 + +typedef struct { + WORD linkdata[4]; + WORD linkcmd[4]; + WORD numtransfers; + int lastlinktime; + unsigned char numgbas; + unsigned char linkflags; + int rfu_q[4]; + u8 rfu_request[4]; + int rfu_linktime[4]; + u32 rfu_bdata[4][7]; + u32 rfu_data[4][32]; +} LINKDATA; + +class lserver{ + int numbytes; + fd_set fdset; + timeval wsocktimeout; + //timeval udptimeout; + char inbuffer[256], outbuffer[256]; + int *intinbuffer; + u16 *u16inbuffer; + int *intoutbuffer; + u16 *u16outbuffer; + int counter; + int done; +public: + int howmanytimes; + SOCKET tcpsocket[4]; + SOCKADDR_IN udpaddr[4]; + lserver(void); + int Init(void*); + void Send(void); + void Recv(void); +}; + +class lclient{ + fd_set fdset; + timeval wsocktimeout; + char inbuffer[256], outbuffer[256]; + int *intinbuffer; + u16 *u16inbuffer; + int *intoutbuffer; + u16 *u16outbuffer; + int numbytes; +public: + bool oncesend; + SOCKADDR_IN serverinfo; + SOCKET noblock; + int numtransfers; + lclient(void); + int Init(LPHOSTENT, void*); + void Send(void); + void Recv(void); + void CheckConn(void); +}; + +typedef struct { + SOCKET tcpsocket; + //SOCKET udpsocket; + int numgbas; + HANDLE thread; + u8 type; + u8 server; + bool terminate; + bool connected; + bool speed; + bool active; +} LANLINKDATA; + +extern void LinkUpdate(void); +extern void LinkChildStop(void); +extern void LinkChildSend(u16); +extern int openLinkLog(void); +extern void closeLinkLog(); +extern void CloseLanLink(void); +extern char *MakeInstanceFilename(const char *Input); + +extern LANLINKDATA lanlink; +extern FILE *linklogfile; +extern int vbaid; +extern int linklog; +extern bool adapter; +extern bool linkenable; +extern int linktimeout; +extern lclient lc; +extern int linkid; + +#endif diff --git a/src/agb/GBAcpu.h b/src/agb/GBAcpu.h new file mode 100644 index 00000000..c617b140 --- /dev/null +++ b/src/agb/GBAcpu.h @@ -0,0 +1,302 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_GBAcpu_H +#define VBA_GBAcpu_H + +extern int armExecute(); +extern int thumbExecute(); + +#ifdef __GNUC__ +# define INSN_REGPARM __attribute__((regparm(1))) +# define LIKELY(x) __builtin_expect(!!(x),1) +# define UNLIKELY(x) __builtin_expect(!!(x),0) +#else +# define INSN_REGPARM /*nothing*/ +# define LIKELY(x) (x) +# define UNLIKELY(x) (x) +#endif + +#define UPDATE_REG(address, value)\ + {\ + WRITE16LE(((u16 *)&ioMem[address]),value);\ + }\ + +#define ARM_PREFETCH \ + {\ + cpuPrefetch[0] = CPUReadMemoryQuick(armNextPC);\ + cpuPrefetch[1] = CPUReadMemoryQuick(armNextPC+4);\ + } + +#define THUMB_PREFETCH \ + {\ + cpuPrefetch[0] = CPUReadHalfWordQuick(armNextPC);\ + cpuPrefetch[1] = CPUReadHalfWordQuick(armNextPC+2);\ + } + +#define ARM_PREFETCH_NEXT \ + cpuPrefetch[1] = CPUReadMemoryQuick(armNextPC+4); + +#define THUMB_PREFETCH_NEXT\ + cpuPrefetch[1] = CPUReadHalfWordQuick(armNextPC+2); + + +extern int SWITicks; +extern u32 mastercode; +extern bool busPrefetch; +extern bool busPrefetchEnable; +extern u32 busPrefetchCount; +extern int cpuNextEvent; +extern bool holdState; +extern u32 cpuPrefetch[2]; +extern int cpuTotalTicks; +extern u8 memoryWait[16]; +extern u8 memoryWait32[16]; +extern u8 memoryWaitSeq[16]; +extern u8 memoryWaitSeq32[16]; +extern u8 cpuBitsSet[256]; +extern u8 cpuLowestBitSet[256]; +extern void CPUSwitchMode(int mode, bool saveState, bool breakLoop); +extern void CPUSwitchMode(int mode, bool saveState); +extern void CPUUpdateCPSR(); +extern void CPUUpdateFlags(bool breakLoop); +extern void CPUUpdateFlags(); +extern void CPUUndefinedException(); +extern void CPUSoftwareInterrupt(); +extern void CPUSoftwareInterrupt(int comment); + + +// Waitstates when accessing data +inline int dataTicksAccess16(u32 address) // DATA 8/16bits NON SEQ +{ + int addr = (address>>24)&15; + int value = memoryWait[addr]; + + if ((addr>=0x08) || (addr < 0x02)) + { + busPrefetchCount=0; + busPrefetch=false; + } + else if (busPrefetch) + { + int waitState = value; + if (!waitState) + waitState = 1; + busPrefetchCount = ((busPrefetchCount+1)<>24)&15; + int value = memoryWait32[addr]; + + if ((addr>=0x08) || (addr < 0x02)) + { + busPrefetchCount=0; + busPrefetch=false; + } + else if (busPrefetch) + { + int waitState = value; + if (!waitState) + waitState = 1; + busPrefetchCount = ((busPrefetchCount+1)<>24)&15; + int value = memoryWaitSeq[addr]; + + if ((addr>=0x08) || (addr < 0x02)) + { + busPrefetchCount=0; + busPrefetch=false; + } + else if (busPrefetch) + { + int waitState = value; + if (!waitState) + waitState = 1; + busPrefetchCount = ((busPrefetchCount+1)<>24)&15; + int value = memoryWaitSeq32[addr]; + + if ((addr>=0x08) || (addr < 0x02)) + { + busPrefetchCount=0; + busPrefetch=false; + } + else if (busPrefetch) + { + int waitState = value; + if (!waitState) + waitState = 1; + busPrefetchCount = ((busPrefetchCount+1)<>24)&15; + + if ((addr>=0x08) && (addr<=0x0D)) + { + if (busPrefetchCount&0x1) + { + if (busPrefetchCount&0x2) + { + busPrefetchCount = ((busPrefetchCount&0xFF)>>2) | (busPrefetchCount&0xFFFFFF00); + return 0; + } + busPrefetchCount = ((busPrefetchCount&0xFF)>>1) | (busPrefetchCount&0xFFFFFF00); + return memoryWaitSeq[addr]-1; + } + else + { + busPrefetchCount=0; + return memoryWait[addr]; + } + } + else + { + busPrefetchCount = 0; + return memoryWait[addr]; + } +} + +inline int codeTicksAccess32(u32 address) // ARM NON SEQ +{ + int addr = (address>>24)&15; + + if ((addr>=0x08) && (addr<=0x0D)) + { + if (busPrefetchCount&0x1) + { + if (busPrefetchCount&0x2) + { + busPrefetchCount = ((busPrefetchCount&0xFF)>>2) | (busPrefetchCount&0xFFFFFF00); + return 0; + } + busPrefetchCount = ((busPrefetchCount&0xFF)>>1) | (busPrefetchCount&0xFFFFFF00); + return memoryWaitSeq[addr] - 1; + } + else + { + busPrefetchCount = 0; + return memoryWait32[addr]; + } + } + else + { + busPrefetchCount = 0; + return memoryWait32[addr]; + } +} + +inline int codeTicksAccessSeq16(u32 address) // THUMB SEQ +{ + int addr = (address>>24)&15; + + if ((addr>=0x08) && (addr<=0x0D)) + { + if (busPrefetchCount&0x1) + { + busPrefetchCount = ((busPrefetchCount&0xFF)>>1) | (busPrefetchCount&0xFFFFFF00); + return 0; + } + else + if (busPrefetchCount>0xFF) + { + busPrefetchCount=0; + return memoryWait[addr]; + } + else + return memoryWaitSeq[addr]; + } + else + { + busPrefetchCount = 0; + return memoryWaitSeq[addr]; + } +} + +inline int codeTicksAccessSeq32(u32 address) // ARM SEQ +{ + int addr = (address>>24)&15; + + if ((addr>=0x08) && (addr<=0x0D)) + { + if (busPrefetchCount&0x1) + { + if (busPrefetchCount&0x2) + { + busPrefetchCount = ((busPrefetchCount&0xFF)>>2) | (busPrefetchCount&0xFFFFFF00); + return 0; + } + busPrefetchCount = ((busPrefetchCount&0xFF)>>1) | (busPrefetchCount&0xFFFFFF00); + return memoryWaitSeq[addr]; + } + else + if (busPrefetchCount>0xFF) + { + busPrefetchCount=0; + return memoryWait32[addr]; + } + else + return memoryWaitSeq32[addr]; + } + else + { + return memoryWaitSeq32[addr]; + } +} + + +// Emulates the Cheat System (m) code +inline void cpuMasterCodeCheck() +{ + if((mastercode) && (mastercode == armNextPC)) + { + u32 joy = 0; + if(systemReadJoypads()) + joy = systemReadJoypad(-1); + u32 ext = (joy >> 10); + cpuTotalTicks += cheatsCheckKeys(P1^0x3FF, ext); + } +} + +#endif //VBA_GBAcpu_H diff --git a/src/agb/GBAinline.h b/src/agb/GBAinline.h new file mode 100644 index 00000000..1b48a63f --- /dev/null +++ b/src/agb/GBAinline.h @@ -0,0 +1,739 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_GBAinline_H +#define VBA_GBAinline_H + +#include "../System.h" +#include "../Port.h" +#include "../RTC.h" +#include "../Sound.h" +#include "agbprint.h" + +extern const u32 objTilesAddress[3]; + +extern bool stopState; +extern bool holdState; +extern int holdType; +extern int cpuNextEvent; +extern bool cpuSramEnabled; +extern bool cpuFlashEnabled; +extern bool cpuEEPROMEnabled; +extern bool cpuEEPROMSensorEnabled; +extern bool cpuDmaHack; +extern u32 cpuDmaLast; +extern bool timer0On; +extern int timer0Ticks; +extern int timer0ClockReload; +extern bool timer1On; +extern int timer1Ticks; +extern int timer1ClockReload; +extern bool timer2On; +extern int timer2Ticks; +extern int timer2ClockReload; +extern bool timer3On; +extern int timer3Ticks; +extern int timer3ClockReload; +extern int cpuTotalTicks; + +#define CPUReadByteQuick(addr) \ + map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] + +#define CPUReadHalfWordQuick(addr) \ + READ16LE(((u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +#define CPUReadMemoryQuick(addr) \ + READ32LE(((u32*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +static inline u32 CPUReadMemory(u32 address) +{ + +#ifdef GBA_LOGGING + if(address & 3) { + if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) { + log("Unaligned word read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } + } +#endif + + u32 value; + switch(address >> 24) { + case 0: + if(reg[15].I >> 24) { + if(address < 0x4000) { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_READ) { + log("Illegal word read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + + value = READ32LE(((u32 *)&biosProtected)); + } + else goto unreadable; + } else + value = READ32LE(((u32 *)&bios[address & 0x3FFC])); + break; + case 2: + value = READ32LE(((u32 *)&workRAM[address & 0x3FFFC])); + break; + case 3: + value = READ32LE(((u32 *)&internalRAM[address & 0x7ffC])); + break; + case 4: + if((address < 0x4000400) && ioReadable[address & 0x3fc]) { + if(ioReadable[(address & 0x3fc) + 2]) + value = READ32LE(((u32 *)&ioMem[address & 0x3fC])); + else + value = READ16LE(((u16 *)&ioMem[address & 0x3fc])); + } else goto unreadable; + break; + case 5: + value = READ32LE(((u32 *)&paletteRAM[address & 0x3fC])); + break; + case 6: + address = (address & 0x1fffc); + if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) + { + value = 0; + break; + } + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + value = READ32LE(((u32 *)&vram[address])); + break; + case 7: + value = READ32LE(((u32 *)&oam[address & 0x3FC])); + break; + case 8: + case 9: + case 10: + case 11: + case 12: + value = READ32LE(((u32 *)&rom[address&0x1FFFFFC])); + break; + case 13: + if(cpuEEPROMEnabled) + // no need to swap this + return eepromRead(address); + goto unreadable; + case 14: + if(cpuFlashEnabled | cpuSramEnabled) + // no need to swap this + return flashRead(address); + // default + default: + unreadable: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_READ) { + log("Illegal word read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + + if(cpuDmaHack) { + value = cpuDmaLast; + } else { + if(armState) { + value = CPUReadMemoryQuick(reg[15].I); + } else { + value = CPUReadHalfWordQuick(reg[15].I) | + CPUReadHalfWordQuick(reg[15].I) << 16; + } + } + } + + if(address & 3) { +#ifdef C_CORE + int shift = (address & 3) << 3; + value = (value >> shift) | (value << (32 - shift)); +#else +#ifdef __GNUC__ + asm("and $3, %%ecx;" + "shl $3 ,%%ecx;" + "ror %%cl, %0" + : "=r" (value) + : "r" (value), "c" (address)); +#else + __asm { + mov ecx, address; + and ecx, 3; + shl ecx, 3; + ror [dword ptr value], cl; + } +#endif +#endif + } + return value; +} + +extern u32 myROM[]; + +static inline u32 CPUReadHalfWord(u32 address) +{ +#ifdef GBA_LOGGING + if(address & 1) { + if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) { + log("Unaligned halfword read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } + } +#endif + + u32 value; + + switch(address >> 24) { + case 0: + if (reg[15].I >> 24) { + if(address < 0x4000) { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_READ) { + log("Illegal halfword read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + value = READ16LE(((u16 *)&biosProtected[address&2])); + } else goto unreadable; + } else + value = READ16LE(((u16 *)&bios[address & 0x3FFE])); + break; + case 2: + value = READ16LE(((u16 *)&workRAM[address & 0x3FFFE])); + break; + case 3: + value = READ16LE(((u16 *)&internalRAM[address & 0x7ffe])); + break; + case 4: + if((address < 0x4000400) && ioReadable[address & 0x3fe]) + { + value = READ16LE(((u16 *)&ioMem[address & 0x3fe])); + if (((address & 0x3fe)>0xFF) && ((address & 0x3fe)<0x10E)) + { + if (((address & 0x3fe) == 0x100) && timer0On) + value = 0xFFFF - ((timer0Ticks-cpuTotalTicks) >> timer0ClockReload); + else + if (((address & 0x3fe) == 0x104) && timer1On && !(TM1CNT & 4)) + value = 0xFFFF - ((timer1Ticks-cpuTotalTicks) >> timer1ClockReload); + else + if (((address & 0x3fe) == 0x108) && timer2On && !(TM2CNT & 4)) + value = 0xFFFF - ((timer2Ticks-cpuTotalTicks) >> timer2ClockReload); + else + if (((address & 0x3fe) == 0x10C) && timer3On && !(TM3CNT & 4)) + value = 0xFFFF - ((timer3Ticks-cpuTotalTicks) >> timer3ClockReload); + } + } + else goto unreadable; + break; + case 5: + value = READ16LE(((u16 *)&paletteRAM[address & 0x3fe])); + break; + case 6: + address = (address & 0x1fffe); + if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) + { + value = 0; + break; + } + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + value = READ16LE(((u16 *)&vram[address])); + break; + case 7: + value = READ16LE(((u16 *)&oam[address & 0x3fe])); + break; + case 8: + case 9: + case 10: + case 11: + case 12: + if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8) + value = rtcRead(address); + else + value = READ16LE(((u16 *)&rom[address & 0x1FFFFFE])); + break; + case 13: + if(cpuEEPROMEnabled) + // no need to swap this + return eepromRead(address); + goto unreadable; + case 14: + if(cpuFlashEnabled | cpuSramEnabled) + // no need to swap this + return flashRead(address); + // default + default: + unreadable: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_READ) { + log("Illegal halfword read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + if(cpuDmaHack) { + value = cpuDmaLast & 0xFFFF; + } else { + if(armState) { + value = CPUReadHalfWordQuick(reg[15].I + (address & 2)); + } else { + value = CPUReadHalfWordQuick(reg[15].I); + } + } + break; + } + + if(address & 1) { + value = (value >> 8) | (value << 24); + } + + return value; +} + +static inline u16 CPUReadHalfWordSigned(u32 address) +{ + u16 value = CPUReadHalfWord(address); + if((address & 1)) + value = (s8)value; + return value; +} + +static inline u8 CPUReadByte(u32 address) +{ + switch(address >> 24) { + case 0: + if (reg[15].I >> 24) { + if(address < 0x4000) { +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_READ) { + log("Illegal byte read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + return biosProtected[address & 3]; + } else goto unreadable; + } + return bios[address & 0x3FFF]; + case 2: + return workRAM[address & 0x3FFFF]; + case 3: + return internalRAM[address & 0x7fff]; + case 4: + if((address < 0x4000400) && ioReadable[address & 0x3ff]) + return ioMem[address & 0x3ff]; + else goto unreadable; + case 5: + return paletteRAM[address & 0x3ff]; + case 6: + address = (address & 0x1ffff); + if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) + return 0; + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + return vram[address]; + case 7: + return oam[address & 0x3ff]; + case 8: + case 9: + case 10: + case 11: + case 12: + return rom[address & 0x1FFFFFF]; + case 13: + if(cpuEEPROMEnabled) + return eepromRead(address); + goto unreadable; + case 14: + if(cpuSramEnabled | cpuFlashEnabled) + return flashRead(address); + if(cpuEEPROMSensorEnabled) { + switch(address & 0x00008f00) { + case 0x8200: + return systemGetSensorX() & 255; + case 0x8300: + return (systemGetSensorX() >> 8)|0x80; + case 0x8400: + return systemGetSensorY() & 255; + case 0x8500: + return systemGetSensorY() >> 8; + } + } + // default + default: + unreadable: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_READ) { + log("Illegal byte read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + if(cpuDmaHack) { + return cpuDmaLast & 0xFF; + } else { + if(armState) { + return CPUReadByteQuick(reg[15].I+(address & 3)); + } else { + return CPUReadByteQuick(reg[15].I+(address & 1)); + } + } + break; + } +} + +static inline void CPUWriteMemory(u32 address, u32 value) +{ + +#ifdef GBA_LOGGING + if(address & 3) { + if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) { + log("Unaligned word write: %08x to %08x from %08x\n", + value, + address, + armMode ? armNextPC - 4 : armNextPC - 2); + } + } +#endif + + switch(address >> 24) { + case 0x02: +#ifdef BKPT_SUPPORT + if(*((u32 *)&freezeWorkRAM[address & 0x3FFFC])) + cheatsWriteMemory(address & 0x203FFFC, + value); + else +#endif + WRITE32LE(((u32 *)&workRAM[address & 0x3FFFC]), value); + break; + case 0x03: +#ifdef BKPT_SUPPORT + if(*((u32 *)&freezeInternalRAM[address & 0x7ffc])) + cheatsWriteMemory(address & 0x3007FFC, + value); + else +#endif + WRITE32LE(((u32 *)&internalRAM[address & 0x7ffC]), value); + break; + case 0x04: + if(address < 0x4000400) { + CPUUpdateRegister((address & 0x3FC), value & 0xFFFF); + CPUUpdateRegister((address & 0x3FC) + 2, (value >> 16)); + } else goto unwritable; + break; + case 0x05: +#ifdef BKPT_SUPPORT + if(*((u32 *)&freezePRAM[address & 0x3fc])) + cheatsWriteMemory(address & 0x70003FC, + value); + else +#endif + WRITE32LE(((u32 *)&paletteRAM[address & 0x3FC]), value); + break; + case 0x06: + address = (address & 0x1fffc); + if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) + return; + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + +#ifdef BKPT_SUPPORT + if(*((u32 *)&freezeVRAM[address])) + cheatsWriteMemory(address + 0x06000000, value); + else +#endif + + WRITE32LE(((u32 *)&vram[address]), value); + break; + case 0x07: +#ifdef BKPT_SUPPORT + if(*((u32 *)&freezeOAM[address & 0x3fc])) + cheatsWriteMemory(address & 0x70003FC, + value); + else +#endif + WRITE32LE(((u32 *)&oam[address & 0x3fc]), value); + break; + case 0x0D: + if(cpuEEPROMEnabled) { + eepromWrite(address, value); + break; + } + goto unwritable; + case 0x0E: + if(!eepromInUse | cpuSramEnabled | cpuFlashEnabled) { + (*cpuSaveGameFunc)(address, (u8)value); + break; + } + // default + default: + unwritable: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_WRITE) { + log("Illegal word write: %08x to %08x from %08x\n", + value, + address, + armMode ? armNextPC - 4 : armNextPC - 2); + } +#endif + break; + } +} + +static inline void CPUWriteHalfWord(u32 address, u16 value) +{ +#ifdef GBA_LOGGING + if(address & 1) { + if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) { + log("Unaligned halfword write: %04x to %08x from %08x\n", + value, + address, + armMode ? armNextPC - 4 : armNextPC - 2); + } + } +#endif + + switch(address >> 24) { + case 2: +#ifdef BKPT_SUPPORT + if(*((u16 *)&freezeWorkRAM[address & 0x3FFFE])) + cheatsWriteHalfWord(address & 0x203FFFE, + value); + else +#endif + WRITE16LE(((u16 *)&workRAM[address & 0x3FFFE]),value); + break; + case 3: +#ifdef BKPT_SUPPORT + if(*((u16 *)&freezeInternalRAM[address & 0x7ffe])) + cheatsWriteHalfWord(address & 0x3007ffe, + value); + else +#endif + WRITE16LE(((u16 *)&internalRAM[address & 0x7ffe]), value); + break; + case 4: + if(address < 0x4000400) + CPUUpdateRegister(address & 0x3fe, value); + else goto unwritable; + break; + case 5: +#ifdef BKPT_SUPPORT + if(*((u16 *)&freezePRAM[address & 0x03fe])) + cheatsWriteHalfWord(address & 0x70003fe, + value); + else +#endif + WRITE16LE(((u16 *)&paletteRAM[address & 0x3fe]), value); + break; + case 6: + address = (address & 0x1fffe); + if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) + return; + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; +#ifdef BKPT_SUPPORT + if(*((u16 *)&freezeVRAM[address])) + cheatsWriteHalfWord(address + 0x06000000, + value); + else +#endif + WRITE16LE(((u16 *)&vram[address]), value); + break; + case 7: +#ifdef BKPT_SUPPORT + if(*((u16 *)&freezeOAM[address & 0x03fe])) + cheatsWriteHalfWord(address & 0x70003fe, + value); + else +#endif + WRITE16LE(((u16 *)&oam[address & 0x3fe]), value); + break; + case 8: + case 9: + if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8) { + if(!rtcWrite(address, value)) + goto unwritable; + } else if(!agbPrintWrite(address, value)) goto unwritable; + break; + case 13: + if(cpuEEPROMEnabled) { + eepromWrite(address, (u8)value); + break; + } + goto unwritable; + case 14: + if(!eepromInUse | cpuSramEnabled | cpuFlashEnabled) { + (*cpuSaveGameFunc)(address, (u8)value); + break; + } + goto unwritable; + default: + unwritable: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_WRITE) { + log("Illegal halfword write: %04x to %08x from %08x\n", + value, + address, + armMode ? armNextPC - 4 : armNextPC - 2); + } +#endif + break; + } +} + +static inline void CPUWriteByte(u32 address, u8 b) +{ + switch(address >> 24) { + case 2: +#ifdef BKPT_SUPPORT + if(freezeWorkRAM[address & 0x3FFFF]) + cheatsWriteByte(address & 0x203FFFF, b); + else +#endif + workRAM[address & 0x3FFFF] = b; + break; + case 3: +#ifdef BKPT_SUPPORT + if(freezeInternalRAM[address & 0x7fff]) + cheatsWriteByte(address & 0x3007fff, b); + else +#endif + internalRAM[address & 0x7fff] = b; + break; + case 4: + if(address < 0x4000400) { + switch(address & 0x3FF) { + case 0x301: + if(b == 0x80) + stopState = true; + holdState = 1; + holdType = -1; + cpuNextEvent = cpuTotalTicks; + break; + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x68: + case 0x69: + case 0x6c: + case 0x6d: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x78: + case 0x79: + case 0x7c: + case 0x7d: + case 0x80: + case 0x81: + case 0x84: + case 0x85: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9a: + case 0x9b: + case 0x9c: + case 0x9d: + case 0x9e: + case 0x9f: + soundEvent(address&0xFF, b); + break; + default: + if(address & 1) + CPUUpdateRegister(address & 0x3fe, + ((READ16LE(((u16 *)&ioMem[address & 0x3fe]))) + & 0x00FF) | + b<<8); + else + CPUUpdateRegister(address & 0x3fe, + ((READ16LE(((u16 *)&ioMem[address & 0x3fe])) & 0xFF00) | b)); + } + break; + } else goto unwritable; + break; + case 5: + // no need to switch + *((u16 *)&paletteRAM[address & 0x3FE]) = (b << 8) | b; + break; + case 6: + address = (address & 0x1fffe); + if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000)) + return; + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + + // no need to switch + // byte writes to OBJ VRAM are ignored + if ((address) < objTilesAddress[((DISPCNT&7)+1)>>2]) + { +#ifdef BKPT_SUPPORT + if(freezeVRAM[address]) + cheatsWriteByte(address + 0x06000000, b); + else +#endif + *((u16 *)&vram[address]) = (b << 8) | b; + } + break; + case 7: + // no need to switch + // byte writes to OAM are ignored + // *((u16 *)&oam[address & 0x3FE]) = (b << 8) | b; + break; + case 13: + if(cpuEEPROMEnabled) { + eepromWrite(address, b); + break; + } + goto unwritable; + case 14: + if (!(saveType == 5) && (!eepromInUse | cpuSramEnabled | cpuFlashEnabled)) { + + //if(!cpuEEPROMEnabled && (cpuSramEnabled | cpuFlashEnabled)) { + + (*cpuSaveGameFunc)(address, b); + break; + } + // default + default: + unwritable: +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_ILLEGAL_WRITE) { + log("Illegal byte write: %02x to %08x from %08x\n", + b, + address, + armMode ? armNextPC - 4 : armNextPC -2 ); + } +#endif + break; + } +} + +#endif //VBA_GBAinline_H diff --git a/src/agb/agbprint.cpp b/src/agb/agbprint.cpp new file mode 100644 index 00000000..8b2db30e --- /dev/null +++ b/src/agb/agbprint.cpp @@ -0,0 +1,99 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include + +#include "GBA.h" +#include "../Globals.h" +#include "../Port.h" + +#define debuggerWriteHalfWord(addr, value) \ + WRITE16LE((u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask], (value)) + +#define debuggerReadHalfWord(addr) \ + READ16LE(((u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +static bool agbPrintEnabled = false; +static bool agbPrintProtect = false; + +bool agbPrintWrite(u32 address, u16 value) +{ + if(agbPrintEnabled) { + if(address == 0x9fe2ffe) { // protect + agbPrintProtect = (value != 0); + debuggerWriteHalfWord(address, value); + return true; + } else { + if(agbPrintProtect && + ((address >= 0x9fe20f8 && address <= 0x9fe20ff) // control structure + || (address >= 0x8fd0000 && address <= 0x8fdffff) + || (address >= 0x9fd0000 && address <= 0x9fdffff))) { + debuggerWriteHalfWord(address, value); + return true; + } + } + } + return false; +} + +void agbPrintReset() +{ + agbPrintProtect = false; +} + +void agbPrintEnable(bool enable) +{ + agbPrintEnabled = enable; +} + +bool agbPrintIsEnabled() +{ + return agbPrintEnabled; +} + +extern void (*dbgOutput)(const char *, u32); + +void agbPrintFlush() +{ + u16 get = debuggerReadHalfWord(0x9fe20fc); + u16 put = debuggerReadHalfWord(0x9fe20fe); + + u32 address = (debuggerReadHalfWord(0x9fe20fa) << 16); + if(address != 0xfd0000 && address != 0x1fd0000) { + dbgOutput("Did you forget to call AGBPrintInit?\n", 0); + // get rid of the text otherwise we will continue to be called + debuggerWriteHalfWord(0x9fe20fc, put); + return; + } + + u8 *data = &rom[address]; + + while(get != put) { + char c = data[get++]; + char s[2]; + s[0] = c; + s[1] = 0; + + if(systemVerbose & VERBOSE_AGBPRINT) + dbgOutput(s, 0); + if(c == '\n') + break; + } + debuggerWriteHalfWord(0x9fe20fc, get); +} diff --git a/src/agb/agbprint.h b/src/agb/agbprint.h new file mode 100644 index 00000000..7f63fa6b --- /dev/null +++ b/src/agb/agbprint.h @@ -0,0 +1,27 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_AGBPRINT_H +#define VBA_AGBPRINT_H +extern void agbPrintEnable(bool); +extern bool agbPrintIsEnabled(); +extern void agbPrintReset(); +extern bool agbPrintWrite(u32, u16); +extern void agbPrintFlush(); +#endif diff --git a/src/agb/gbafilter.cpp b/src/agb/gbafilter.cpp new file mode 100644 index 00000000..7aef6f51 --- /dev/null +++ b/src/agb/gbafilter.cpp @@ -0,0 +1,227 @@ +#include "gbafilter.h" + +#include + +extern int systemColorDepth; +extern int systemRedShift; +extern int systemGreenShift; +extern int systemBlueShift; + +extern u16 systemColorMap16[0x10000]; +extern u32 systemColorMap32[0x10000]; + +static const unsigned char curve[32] = { 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0e, 0x10, 0x12, + 0x14, 0x16, 0x18, 0x1c, 0x20, 0x28, 0x30, 0x38, + 0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x80, + 0x88, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0}; + +// output R G B +static const unsigned char influence[3 * 3] = { 16, 4, 4, // red + 8, 16, 8, // green + 0, 8, 16};// blue + +inline void swap(short & a, short & b) +{ + short temp = a; + a = b; + b = temp; +} + +void gbafilter_pal(u16 * buf, int count) +{ + short temp[3 * 3], s; + unsigned pix; + u8 red, green, blue; + + while (count--) + { + pix = *buf; + + s = curve[(pix >> systemGreenShift) & 0x1f]; + temp[3] = s * influence[3]; + temp[4] = s * influence[4]; + temp[5] = s * influence[5]; + + s = curve[(pix >> systemRedShift) & 0x1f]; + temp[0] = s * influence[0]; + temp[1] = s * influence[1]; + temp[2] = s * influence[2]; + + s = curve[(pix >> systemBlueShift) & 0x1f]; + temp[6] = s * influence[6]; + temp[7] = s * influence[7]; + temp[8] = s * influence[8]; + + if (temp[0] < temp[3]) swap(temp[0], temp[3]); + if (temp[0] < temp[6]) swap(temp[0], temp[6]); + if (temp[3] < temp[6]) swap(temp[3], temp[6]); + temp[3] <<= 1; + temp[0] <<= 2; + temp[0] += temp[3] + temp[6]; + + red = ((int(temp[0]) * 160) >> 17) + 4; + if (red > 31) red = 31; + + if (temp[2] < temp[5]) swap(temp[2], temp[5]); + if (temp[2] < temp[8]) swap(temp[2], temp[8]); + if (temp[5] < temp[8]) swap(temp[5], temp[8]); + temp[5] <<= 1; + temp[2] <<= 2; + temp[2] += temp[5] + temp[8]; + + blue = ((int(temp[2]) * 160) >> 17) + 4; + if (blue > 31) blue = 31; + + if (temp[1] < temp[4]) swap(temp[1], temp[4]); + if (temp[1] < temp[7]) swap(temp[1], temp[7]); + if (temp[4] < temp[7]) swap(temp[4], temp[7]); + temp[4] <<= 1; + temp[1] <<= 2; + temp[1] += temp[4] + temp[7]; + + green = ((int(temp[1]) * 160) >> 17) + 4; + if (green > 31) green = 31; + + pix = red << systemRedShift; + pix += green << systemGreenShift; + pix += blue << systemBlueShift; + + *buf++ = pix; + } +} + +void gbafilter_pal32(u32 * buf, int count) +{ + short temp[3 * 3], s; + unsigned pix; + u8 red, green, blue; + + while (count--) + { + pix = *buf; + + s = curve[(pix >> systemGreenShift) & 0x1f]; + temp[3] = s * influence[3]; + temp[4] = s * influence[4]; + temp[5] = s * influence[5]; + + s = curve[(pix >> systemRedShift) & 0x1f]; + temp[0] = s * influence[0]; + temp[1] = s * influence[1]; + temp[2] = s * influence[2]; + + s = curve[(pix >> systemBlueShift) & 0x1f]; + temp[6] = s * influence[6]; + temp[7] = s * influence[7]; + temp[8] = s * influence[8]; + + if (temp[0] < temp[3]) swap(temp[0], temp[3]); + if (temp[0] < temp[6]) swap(temp[0], temp[6]); + if (temp[3] < temp[6]) swap(temp[3], temp[6]); + temp[3] <<= 1; + temp[0] <<= 2; + temp[0] += temp[3] + temp[6]; + + //red = ((int(temp[0]) * 160) >> 17) + 4; + red = ((int(temp[0]) * 160) >> 14) + 32; + + if (temp[2] < temp[5]) swap(temp[2], temp[5]); + if (temp[2] < temp[8]) swap(temp[2], temp[8]); + if (temp[5] < temp[8]) swap(temp[5], temp[8]); + temp[5] <<= 1; + temp[2] <<= 2; + temp[2] += temp[5] + temp[8]; + + //blue = ((int(temp[2]) * 160) >> 17) + 4; + blue = ((int(temp[2]) * 160) >> 14) + 32; + + if (temp[1] < temp[4]) swap(temp[1], temp[4]); + if (temp[1] < temp[7]) swap(temp[1], temp[7]); + if (temp[4] < temp[7]) swap(temp[4], temp[7]); + temp[4] <<= 1; + temp[1] <<= 2; + temp[1] += temp[4] + temp[7]; + + //green = ((int(temp[1]) * 160) >> 17) + 4; + green = ((int(temp[1]) * 160) >> 14) + 32; + + //pix = red << redshift; + //pix += green << greenshift; + //pix += blue << blueshift; + + pix = red << (systemRedShift - 3); + pix += green << (systemGreenShift - 3); + pix += blue << (systemBlueShift - 3); + + *buf++ = pix; + } +} + +// for palette mode to work with the three spoony filters in 32bpp depth + +void gbafilter_pad(u8 * buf, int count) +{ + union + { + struct + { + u8 r; + u8 g; + u8 b; + u8 a; + } part; + unsigned whole; + } + mask; + + mask.whole = 0x1f << systemRedShift; + mask.whole += 0x1f << systemGreenShift; + mask.whole += 0x1f << systemBlueShift; + + switch (systemColorDepth) + { + case 24: + while (count--) + { + *buf++ &= mask.part.r; + *buf++ &= mask.part.g; + *buf++ &= mask.part.b; + } + break; + case 32: + while (count--) + { + *((u32*)buf) &= mask.whole; + buf += 4; + } + } +} + +/* +void UpdateSystemColorMaps(int lcd) +{ + switch(systemColorDepth) { + case 16: + { + for(int i = 0; i < 0x10000; i++) { + systemColorMap16[i] = ((i & 0x1f) << systemRedShift) | + (((i & 0x3e0) >> 5) << systemGreenShift) | + (((i & 0x7c00) >> 10) << systemBlueShift); + } + if (lcd == 1) gbafilter_pal(systemColorMap16, 0x10000); + } + break; + case 24: + case 32: + { + for(int i = 0; i < 0x10000; i++) { + systemColorMap32[i] = ((i & 0x1f) << systemRedShift) | + (((i & 0x3e0) >> 5) << systemGreenShift) | + (((i & 0x7c00) >> 10) << systemBlueShift); + } + if (lcd == 1) gbafilter_pal32(systemColorMap32, 0x10000); + } + break; + } +} +*/ diff --git a/src/agb/gbafilter.h b/src/agb/gbafilter.h new file mode 100644 index 00000000..17dfa3cb --- /dev/null +++ b/src/agb/gbafilter.h @@ -0,0 +1,5 @@ +#include "../System.h" + +void gbafilter_pal(u16 * buf, int count); +void gbafilter_pal32(u32 * buf, int count); +void gbafilter_pad(u8 * buf, int count); diff --git a/src/dmg/GB.cpp b/src/dmg/GB.cpp new file mode 100644 index 00000000..44584892 --- /dev/null +++ b/src/dmg/GB.cpp @@ -0,0 +1,5449 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005-2006 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include +#include +#include + +#include "../System.h" +#include "../NLS.h" +#include "gb.h" +#include "gbCheats.h" +#include "gbGlobals.h" +#include "gbMemory.h" +#include "gbSGB.h" +#include "gbSound.h" +#include "../Util.h" + +#ifdef __GNUC__ +#define _stricmp strcasecmp +#endif + +extern u8 *pix; +extern bool speedup; + +bool gbUpdateSizes(); +bool inBios = false; + +// debugging +bool memorydebug = false; +char gbBuffer[2048]; + +extern u16 gbLineMix[160]; + +// mappers +void (*mapper)(u16,u8) = NULL; +void (*mapperRAM)(u16,u8) = NULL; +u8 (*mapperReadRAM)(u16) = NULL; +void (*mapperUpdateClock)() = NULL; + +// registers +gbRegister PC; +gbRegister SP; +gbRegister AF; +gbRegister BC; +gbRegister DE; +gbRegister HL; +u16 IFF = 0; +// 0xff04 +u8 register_DIV = 0; +// 0xff05 +u8 register_TIMA = 0; +// 0xff06 +u8 register_TMA = 0; +// 0xff07 +u8 register_TAC = 0; +// 0xff0f +u8 register_IF = 0; +// 0xff40 +u8 register_LCDC = 0; +// 0xff41 +u8 register_STAT = 0; +// 0xff42 +u8 register_SCY = 0; +// 0xff43 +u8 register_SCX = 0; +// 0xff44 +u8 register_LY = 0; +// 0xff45 +u8 register_LYC = 0; +// 0xff46 +u8 register_DMA = 0; +// 0xff4a +u8 register_WY = 0; +// 0xff4b +u8 register_WX = 0; +// 0xff4f +u8 register_VBK = 0; +// 0xff51 +u8 register_HDMA1 = 0; +// 0xff52 +u8 register_HDMA2 = 0; +// 0xff53 +u8 register_HDMA3 = 0; +// 0xff54 +u8 register_HDMA4 = 0; +// 0xff55 +u8 register_HDMA5 = 0; +// 0xff70 +u8 register_SVBK = 0; +// 0xffff +u8 register_IE = 0; + +// ticks definition +int GBDIV_CLOCK_TICKS = 64; +int GBLCD_MODE_0_CLOCK_TICKS = 51; +int GBLCD_MODE_1_CLOCK_TICKS = 1140; +int GBLCD_MODE_2_CLOCK_TICKS = 20; +int GBLCD_MODE_3_CLOCK_TICKS = 43; +int GBLY_INCREMENT_CLOCK_TICKS = 114; +int GBTIMER_MODE_0_CLOCK_TICKS = 256; +int GBTIMER_MODE_1_CLOCK_TICKS = 4; +int GBTIMER_MODE_2_CLOCK_TICKS = 16; +int GBTIMER_MODE_3_CLOCK_TICKS = 64; +int GBSERIAL_CLOCK_TICKS = 128; +int GBSYNCHRONIZE_CLOCK_TICKS = 52920; + +// state variables + +// general +int clockTicks = 0; +bool gbSystemMessage = false; +int gbGBCColorType = 0; +int gbHardware = 0; +int gbRomType = 0; +int gbRemainingClockTicks = 0; +int gbOldClockTicks = 0; +int gbIntBreak = 0; +int gbInterruptLaunched = 0; +u8 gbCheatingDevice = 0; // 1 = GS, 2 = GG +// breakpoint +bool breakpoint = false; +// interrupt +int gbInt48Signal = 0; +int gbInterruptWait = 0; +// serial +int gbSerialOn = 0; +int gbSerialTicks = 0; +int gbSerialBits = 0; +// timer +bool gbTimerOn = false; +int gbTimerTicks = GBTIMER_MODE_0_CLOCK_TICKS; +int gbTimerClockTicks = GBTIMER_MODE_0_CLOCK_TICKS; +int gbTimerMode = 0; +bool gbIncreased = false; +// The internal timer is always active, and it is +// not reset by writing to register_TIMA/TMA, but by +// writing to register_DIV... +int gbInternalTimer = 0x55; +const u8 gbTimerMask [4] = {0xff, 0x3, 0xf, 0x3f}; +const u8 gbTimerBug [8] = {0x80, 0x80, 0x02, 0x02, 0x0, 0xff, 0x0, 0xff}; +bool gbTimerModeChange = false; +bool gbTimerOnChange = false; +// lcd +bool gbScreenOn = true; +int gbLcdMode = 2; +int gbLcdModeDelayed = 2; +int gbLcdTicks = GBLCD_MODE_2_CLOCK_TICKS-1; +int gbLcdTicksDelayed = GBLCD_MODE_2_CLOCK_TICKS; +int gbLcdLYIncrementTicks = 114; +int gbLcdLYIncrementTicksDelayed = 115; +int gbScreenTicks = 0; +u8 gbSCYLine[300]; +u8 gbSCXLine[300]; +u8 gbBgpLine[300]; +u8 gbObp0Line [300]; +u8 gbObp1Line [300]; +u8 gbSpritesTicks [300]; +u8 oldRegister_WY; +bool gbLYChangeHappened = false; +bool gbLCDChangeHappened = false; +int gbLine99Ticks = 1; +int gbRegisterLYLCDCOffOn = 0; +int inUseRegister_WY = 0; + +// Used to keep track of the line that ellapse +// when screen is off +int gbWhiteScreen = 0; +bool gbBlackScreen = false; +int register_LCDCBusy = 0; + +// div +int gbDivTicks = GBDIV_CLOCK_TICKS; +// cgb +int gbVramBank = 0; +int gbWramBank = 1; +//sgb +bool gbSgbResetFlag = false; +// gbHdmaDestination is 0x99d0 on startup (tested on HW) +// but I'm not sure what gbHdmaSource is... +int gbHdmaSource = 0x99d0; +int gbHdmaDestination = 0x99d0; +int gbHdmaBytes = 0x0000; +int gbHdmaOn = 0; +int gbSpeed = 0; +// frame counting +int gbFrameCount = 0; +int gbFrameSkip = 0; +int gbFrameSkipCount = 0; +// timing +u32 gbLastTime = 0; +u32 gbElapsedTime = 0; +u32 gbTimeNow = 0; +int gbSynchronizeTicks = GBSYNCHRONIZE_CLOCK_TICKS; +// emulator features +int gbBattery = 0; +bool gbBatteryError = false; +int gbCaptureNumber = 0; +bool gbCapture = false; +bool gbCapturePrevious = false; +int gbJoymask[4] = { 0, 0, 0, 0 }; + +int gbRomSizes[] = { 0x00008000, // 32K + 0x00010000, // 64K + 0x00020000, // 128K + 0x00040000, // 256K + 0x00080000, // 512K + 0x00100000, // 1024K + 0x00200000, // 2048K + 0x00400000, // 4096K + 0x00800000 // 8192K +}; +int gbRomSizesMasks[] = { 0x00007fff, + 0x0000ffff, + 0x0001ffff, + 0x0003ffff, + 0x0007ffff, + 0x000fffff, + 0x001fffff, + 0x003fffff, + 0x007fffff +}; + +int gbRamSizes[6] = { 0x00000000, // 0K + 0x00002000, // 2K // Changed to 2000 to avoid problems with gbMemoryMap... + 0x00002000, // 8K + 0x00008000, // 32K + 0x00020000, // 128K + 0x00010000 // 64K +}; + +int gbRamSizesMasks[6] = { 0x00000000, + 0x000007ff, + 0x00001fff, + 0x00007fff, + 0x0001ffff, + 0x0000ffff +}; + +int gbCycles[] = { +// 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 3, 2, 2, 1, 1, 2, 1, 5, 2, 2, 2, 1, 1, 2, 1, // 0 + 1, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1, // 1 + 2, 3, 2, 2, 1, 1, 2, 1, 2, 2, 2, 2, 1, 1, 2, 1, // 2 + 2, 3, 2, 2, 3, 3, 3, 1, 2, 2, 2, 2, 1, 1, 2, 1, // 3 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 4 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 5 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 6 + 2, 2, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, // 7 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 8 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 9 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // a + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // b + 2, 3, 3, 4, 3, 4, 2, 4, 2, 4, 3, 2, 3, 6, 2, 4, // c + 2, 3, 3, 1, 3, 4, 2, 4, 2, 4, 3, 1, 3, 1, 2, 4, // d + 3, 3, 2, 1, 1, 4, 2, 4, 4, 1, 4, 1, 1, 1, 2, 4, // e + 3, 3, 2, 1, 1, 4, 2, 4, 3, 2, 4, 1, 0, 1, 2, 4 // f +}; + +int gbCyclesCB[] = { +// 0 1 2 3 4 5 6 7 8 9 a b c d e f + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 0 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 1 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 2 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 3 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 4 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 5 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 6 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 7 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 8 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 9 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // a + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // b + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // c + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // d + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // e + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2 // f +}; + +u16 DAATable[] = { + 0x0080,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, + 0x0800,0x0900,0x1020,0x1120,0x1220,0x1320,0x1420,0x1520, + 0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700, + 0x1800,0x1900,0x2020,0x2120,0x2220,0x2320,0x2420,0x2520, + 0x2000,0x2100,0x2200,0x2300,0x2400,0x2500,0x2600,0x2700, + 0x2800,0x2900,0x3020,0x3120,0x3220,0x3320,0x3420,0x3520, + 0x3000,0x3100,0x3200,0x3300,0x3400,0x3500,0x3600,0x3700, + 0x3800,0x3900,0x4020,0x4120,0x4220,0x4320,0x4420,0x4520, + 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, + 0x4800,0x4900,0x5020,0x5120,0x5220,0x5320,0x5420,0x5520, + 0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x5600,0x5700, + 0x5800,0x5900,0x6020,0x6120,0x6220,0x6320,0x6420,0x6520, + 0x6000,0x6100,0x6200,0x6300,0x6400,0x6500,0x6600,0x6700, + 0x6800,0x6900,0x7020,0x7120,0x7220,0x7320,0x7420,0x7520, + 0x7000,0x7100,0x7200,0x7300,0x7400,0x7500,0x7600,0x7700, + 0x7800,0x7900,0x8020,0x8120,0x8220,0x8320,0x8420,0x8520, + 0x8000,0x8100,0x8200,0x8300,0x8400,0x8500,0x8600,0x8700, + 0x8800,0x8900,0x9020,0x9120,0x9220,0x9320,0x9420,0x9520, + 0x9000,0x9100,0x9200,0x9300,0x9400,0x9500,0x9600,0x9700, + 0x9800,0x9900,0x00B0,0x0130,0x0230,0x0330,0x0430,0x0530, + 0x0090,0x0110,0x0210,0x0310,0x0410,0x0510,0x0610,0x0710, + 0x0810,0x0910,0x1030,0x1130,0x1230,0x1330,0x1430,0x1530, + 0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710, + 0x1810,0x1910,0x2030,0x2130,0x2230,0x2330,0x2430,0x2530, + 0x2010,0x2110,0x2210,0x2310,0x2410,0x2510,0x2610,0x2710, + 0x2810,0x2910,0x3030,0x3130,0x3230,0x3330,0x3430,0x3530, + 0x3010,0x3110,0x3210,0x3310,0x3410,0x3510,0x3610,0x3710, + 0x3810,0x3910,0x4030,0x4130,0x4230,0x4330,0x4430,0x4530, + 0x4010,0x4110,0x4210,0x4310,0x4410,0x4510,0x4610,0x4710, + 0x4810,0x4910,0x5030,0x5130,0x5230,0x5330,0x5430,0x5530, + 0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710, + 0x5810,0x5910,0x6030,0x6130,0x6230,0x6330,0x6430,0x6530, + 0x6010,0x6110,0x6210,0x6310,0x6410,0x6510,0x6610,0x6710, + 0x6810,0x6910,0x7030,0x7130,0x7230,0x7330,0x7430,0x7530, + 0x7010,0x7110,0x7210,0x7310,0x7410,0x7510,0x7610,0x7710, + 0x7810,0x7910,0x8030,0x8130,0x8230,0x8330,0x8430,0x8530, + 0x8010,0x8110,0x8210,0x8310,0x8410,0x8510,0x8610,0x8710, + 0x8810,0x8910,0x9030,0x9130,0x9230,0x9330,0x9430,0x9530, + 0x9010,0x9110,0x9210,0x9310,0x9410,0x9510,0x9610,0x9710, + 0x9810,0x9910,0xA030,0xA130,0xA230,0xA330,0xA430,0xA530, + 0xA010,0xA110,0xA210,0xA310,0xA410,0xA510,0xA610,0xA710, + 0xA810,0xA910,0xB030,0xB130,0xB230,0xB330,0xB430,0xB530, + 0xB010,0xB110,0xB210,0xB310,0xB410,0xB510,0xB610,0xB710, + 0xB810,0xB910,0xC030,0xC130,0xC230,0xC330,0xC430,0xC530, + 0xC010,0xC110,0xC210,0xC310,0xC410,0xC510,0xC610,0xC710, + 0xC810,0xC910,0xD030,0xD130,0xD230,0xD330,0xD430,0xD530, + 0xD010,0xD110,0xD210,0xD310,0xD410,0xD510,0xD610,0xD710, + 0xD810,0xD910,0xE030,0xE130,0xE230,0xE330,0xE430,0xE530, + 0xE010,0xE110,0xE210,0xE310,0xE410,0xE510,0xE610,0xE710, + 0xE810,0xE910,0xF030,0xF130,0xF230,0xF330,0xF430,0xF530, + 0xF010,0xF110,0xF210,0xF310,0xF410,0xF510,0xF610,0xF710, + 0xF810,0xF910,0x00B0,0x0130,0x0230,0x0330,0x0430,0x0530, + 0x0090,0x0110,0x0210,0x0310,0x0410,0x0510,0x0610,0x0710, + 0x0810,0x0910,0x1030,0x1130,0x1230,0x1330,0x1430,0x1530, + 0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710, + 0x1810,0x1910,0x2030,0x2130,0x2230,0x2330,0x2430,0x2530, + 0x2010,0x2110,0x2210,0x2310,0x2410,0x2510,0x2610,0x2710, + 0x2810,0x2910,0x3030,0x3130,0x3230,0x3330,0x3430,0x3530, + 0x3010,0x3110,0x3210,0x3310,0x3410,0x3510,0x3610,0x3710, + 0x3810,0x3910,0x4030,0x4130,0x4230,0x4330,0x4430,0x4530, + 0x4010,0x4110,0x4210,0x4310,0x4410,0x4510,0x4610,0x4710, + 0x4810,0x4910,0x5030,0x5130,0x5230,0x5330,0x5430,0x5530, + 0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710, + 0x5810,0x5910,0x6030,0x6130,0x6230,0x6330,0x6430,0x6530, + 0x0600,0x0700,0x0800,0x0900,0x0A00,0x0B00,0x0C00,0x0D00, + 0x0E00,0x0F00,0x1020,0x1120,0x1220,0x1320,0x1420,0x1520, + 0x1600,0x1700,0x1800,0x1900,0x1A00,0x1B00,0x1C00,0x1D00, + 0x1E00,0x1F00,0x2020,0x2120,0x2220,0x2320,0x2420,0x2520, + 0x2600,0x2700,0x2800,0x2900,0x2A00,0x2B00,0x2C00,0x2D00, + 0x2E00,0x2F00,0x3020,0x3120,0x3220,0x3320,0x3420,0x3520, + 0x3600,0x3700,0x3800,0x3900,0x3A00,0x3B00,0x3C00,0x3D00, + 0x3E00,0x3F00,0x4020,0x4120,0x4220,0x4320,0x4420,0x4520, + 0x4600,0x4700,0x4800,0x4900,0x4A00,0x4B00,0x4C00,0x4D00, + 0x4E00,0x4F00,0x5020,0x5120,0x5220,0x5320,0x5420,0x5520, + 0x5600,0x5700,0x5800,0x5900,0x5A00,0x5B00,0x5C00,0x5D00, + 0x5E00,0x5F00,0x6020,0x6120,0x6220,0x6320,0x6420,0x6520, + 0x6600,0x6700,0x6800,0x6900,0x6A00,0x6B00,0x6C00,0x6D00, + 0x6E00,0x6F00,0x7020,0x7120,0x7220,0x7320,0x7420,0x7520, + 0x7600,0x7700,0x7800,0x7900,0x7A00,0x7B00,0x7C00,0x7D00, + 0x7E00,0x7F00,0x8020,0x8120,0x8220,0x8320,0x8420,0x8520, + 0x8600,0x8700,0x8800,0x8900,0x8A00,0x8B00,0x8C00,0x8D00, + 0x8E00,0x8F00,0x9020,0x9120,0x9220,0x9320,0x9420,0x9520, + 0x9600,0x9700,0x9800,0x9900,0x9A00,0x9B00,0x9C00,0x9D00, + 0x9E00,0x9F00,0x00B0,0x0130,0x0230,0x0330,0x0430,0x0530, + 0x0610,0x0710,0x0810,0x0910,0x0A10,0x0B10,0x0C10,0x0D10, + 0x0E10,0x0F10,0x1030,0x1130,0x1230,0x1330,0x1430,0x1530, + 0x1610,0x1710,0x1810,0x1910,0x1A10,0x1B10,0x1C10,0x1D10, + 0x1E10,0x1F10,0x2030,0x2130,0x2230,0x2330,0x2430,0x2530, + 0x2610,0x2710,0x2810,0x2910,0x2A10,0x2B10,0x2C10,0x2D10, + 0x2E10,0x2F10,0x3030,0x3130,0x3230,0x3330,0x3430,0x3530, + 0x3610,0x3710,0x3810,0x3910,0x3A10,0x3B10,0x3C10,0x3D10, + 0x3E10,0x3F10,0x4030,0x4130,0x4230,0x4330,0x4430,0x4530, + 0x4610,0x4710,0x4810,0x4910,0x4A10,0x4B10,0x4C10,0x4D10, + 0x4E10,0x4F10,0x5030,0x5130,0x5230,0x5330,0x5430,0x5530, + 0x5610,0x5710,0x5810,0x5910,0x5A10,0x5B10,0x5C10,0x5D10, + 0x5E10,0x5F10,0x6030,0x6130,0x6230,0x6330,0x6430,0x6530, + 0x6610,0x6710,0x6810,0x6910,0x6A10,0x6B10,0x6C10,0x6D10, + 0x6E10,0x6F10,0x7030,0x7130,0x7230,0x7330,0x7430,0x7530, + 0x7610,0x7710,0x7810,0x7910,0x7A10,0x7B10,0x7C10,0x7D10, + 0x7E10,0x7F10,0x8030,0x8130,0x8230,0x8330,0x8430,0x8530, + 0x8610,0x8710,0x8810,0x8910,0x8A10,0x8B10,0x8C10,0x8D10, + 0x8E10,0x8F10,0x9030,0x9130,0x9230,0x9330,0x9430,0x9530, + 0x9610,0x9710,0x9810,0x9910,0x9A10,0x9B10,0x9C10,0x9D10, + 0x9E10,0x9F10,0xA030,0xA130,0xA230,0xA330,0xA430,0xA530, + 0xA610,0xA710,0xA810,0xA910,0xAA10,0xAB10,0xAC10,0xAD10, + 0xAE10,0xAF10,0xB030,0xB130,0xB230,0xB330,0xB430,0xB530, + 0xB610,0xB710,0xB810,0xB910,0xBA10,0xBB10,0xBC10,0xBD10, + 0xBE10,0xBF10,0xC030,0xC130,0xC230,0xC330,0xC430,0xC530, + 0xC610,0xC710,0xC810,0xC910,0xCA10,0xCB10,0xCC10,0xCD10, + 0xCE10,0xCF10,0xD030,0xD130,0xD230,0xD330,0xD430,0xD530, + 0xD610,0xD710,0xD810,0xD910,0xDA10,0xDB10,0xDC10,0xDD10, + 0xDE10,0xDF10,0xE030,0xE130,0xE230,0xE330,0xE430,0xE530, + 0xE610,0xE710,0xE810,0xE910,0xEA10,0xEB10,0xEC10,0xED10, + 0xEE10,0xEF10,0xF030,0xF130,0xF230,0xF330,0xF430,0xF530, + 0xF610,0xF710,0xF810,0xF910,0xFA10,0xFB10,0xFC10,0xFD10, + 0xFE10,0xFF10,0x00B0,0x0130,0x0230,0x0330,0x0430,0x0530, + 0x0610,0x0710,0x0810,0x0910,0x0A10,0x0B10,0x0C10,0x0D10, + 0x0E10,0x0F10,0x1030,0x1130,0x1230,0x1330,0x1430,0x1530, + 0x1610,0x1710,0x1810,0x1910,0x1A10,0x1B10,0x1C10,0x1D10, + 0x1E10,0x1F10,0x2030,0x2130,0x2230,0x2330,0x2430,0x2530, + 0x2610,0x2710,0x2810,0x2910,0x2A10,0x2B10,0x2C10,0x2D10, + 0x2E10,0x2F10,0x3030,0x3130,0x3230,0x3330,0x3430,0x3530, + 0x3610,0x3710,0x3810,0x3910,0x3A10,0x3B10,0x3C10,0x3D10, + 0x3E10,0x3F10,0x4030,0x4130,0x4230,0x4330,0x4430,0x4530, + 0x4610,0x4710,0x4810,0x4910,0x4A10,0x4B10,0x4C10,0x4D10, + 0x4E10,0x4F10,0x5030,0x5130,0x5230,0x5330,0x5430,0x5530, + 0x5610,0x5710,0x5810,0x5910,0x5A10,0x5B10,0x5C10,0x5D10, + 0x5E10,0x5F10,0x6030,0x6130,0x6230,0x6330,0x6430,0x6530, + 0x00C0,0x0140,0x0240,0x0340,0x0440,0x0540,0x0640,0x0740, + 0x0840,0x0940,0x0440,0x0540,0x0640,0x0740,0x0840,0x0940, + 0x1040,0x1140,0x1240,0x1340,0x1440,0x1540,0x1640,0x1740, + 0x1840,0x1940,0x1440,0x1540,0x1640,0x1740,0x1840,0x1940, + 0x2040,0x2140,0x2240,0x2340,0x2440,0x2540,0x2640,0x2740, + 0x2840,0x2940,0x2440,0x2540,0x2640,0x2740,0x2840,0x2940, + 0x3040,0x3140,0x3240,0x3340,0x3440,0x3540,0x3640,0x3740, + 0x3840,0x3940,0x3440,0x3540,0x3640,0x3740,0x3840,0x3940, + 0x4040,0x4140,0x4240,0x4340,0x4440,0x4540,0x4640,0x4740, + 0x4840,0x4940,0x4440,0x4540,0x4640,0x4740,0x4840,0x4940, + 0x5040,0x5140,0x5240,0x5340,0x5440,0x5540,0x5640,0x5740, + 0x5840,0x5940,0x5440,0x5540,0x5640,0x5740,0x5840,0x5940, + 0x6040,0x6140,0x6240,0x6340,0x6440,0x6540,0x6640,0x6740, + 0x6840,0x6940,0x6440,0x6540,0x6640,0x6740,0x6840,0x6940, + 0x7040,0x7140,0x7240,0x7340,0x7440,0x7540,0x7640,0x7740, + 0x7840,0x7940,0x7440,0x7540,0x7640,0x7740,0x7840,0x7940, + 0x8040,0x8140,0x8240,0x8340,0x8440,0x8540,0x8640,0x8740, + 0x8840,0x8940,0x8440,0x8540,0x8640,0x8740,0x8840,0x8940, + 0x9040,0x9140,0x9240,0x9340,0x9440,0x9540,0x9640,0x9740, + 0x9840,0x9940,0x3450,0x3550,0x3650,0x3750,0x3850,0x3950, + 0x4050,0x4150,0x4250,0x4350,0x4450,0x4550,0x4650,0x4750, + 0x4850,0x4950,0x4450,0x4550,0x4650,0x4750,0x4850,0x4950, + 0x5050,0x5150,0x5250,0x5350,0x5450,0x5550,0x5650,0x5750, + 0x5850,0x5950,0x5450,0x5550,0x5650,0x5750,0x5850,0x5950, + 0x6050,0x6150,0x6250,0x6350,0x6450,0x6550,0x6650,0x6750, + 0x6850,0x6950,0x6450,0x6550,0x6650,0x6750,0x6850,0x6950, + 0x7050,0x7150,0x7250,0x7350,0x7450,0x7550,0x7650,0x7750, + 0x7850,0x7950,0x7450,0x7550,0x7650,0x7750,0x7850,0x7950, + 0x8050,0x8150,0x8250,0x8350,0x8450,0x8550,0x8650,0x8750, + 0x8850,0x8950,0x8450,0x8550,0x8650,0x8750,0x8850,0x8950, + 0x9050,0x9150,0x9250,0x9350,0x9450,0x9550,0x9650,0x9750, + 0x9850,0x9950,0x9450,0x9550,0x9650,0x9750,0x9850,0x9950, + 0xA050,0xA150,0xA250,0xA350,0xA450,0xA550,0xA650,0xA750, + 0xA850,0xA950,0xA450,0xA550,0xA650,0xA750,0xA850,0xA950, + 0xB050,0xB150,0xB250,0xB350,0xB450,0xB550,0xB650,0xB750, + 0xB850,0xB950,0xB450,0xB550,0xB650,0xB750,0xB850,0xB950, + 0xC050,0xC150,0xC250,0xC350,0xC450,0xC550,0xC650,0xC750, + 0xC850,0xC950,0xC450,0xC550,0xC650,0xC750,0xC850,0xC950, + 0xD050,0xD150,0xD250,0xD350,0xD450,0xD550,0xD650,0xD750, + 0xD850,0xD950,0xD450,0xD550,0xD650,0xD750,0xD850,0xD950, + 0xE050,0xE150,0xE250,0xE350,0xE450,0xE550,0xE650,0xE750, + 0xE850,0xE950,0xE450,0xE550,0xE650,0xE750,0xE850,0xE950, + 0xF050,0xF150,0xF250,0xF350,0xF450,0xF550,0xF650,0xF750, + 0xF850,0xF950,0xF450,0xF550,0xF650,0xF750,0xF850,0xF950, + 0x00D0,0x0150,0x0250,0x0350,0x0450,0x0550,0x0650,0x0750, + 0x0850,0x0950,0x0450,0x0550,0x0650,0x0750,0x0850,0x0950, + 0x1050,0x1150,0x1250,0x1350,0x1450,0x1550,0x1650,0x1750, + 0x1850,0x1950,0x1450,0x1550,0x1650,0x1750,0x1850,0x1950, + 0x2050,0x2150,0x2250,0x2350,0x2450,0x2550,0x2650,0x2750, + 0x2850,0x2950,0x2450,0x2550,0x2650,0x2750,0x2850,0x2950, + 0x3050,0x3150,0x3250,0x3350,0x3450,0x3550,0x3650,0x3750, + 0x3850,0x3950,0x3450,0x3550,0x3650,0x3750,0x3850,0x3950, + 0x4050,0x4150,0x4250,0x4350,0x4450,0x4550,0x4650,0x4750, + 0x4850,0x4950,0x4450,0x4550,0x4650,0x4750,0x4850,0x4950, + 0x5050,0x5150,0x5250,0x5350,0x5450,0x5550,0x5650,0x5750, + 0x5850,0x5950,0x5450,0x5550,0x5650,0x5750,0x5850,0x5950, + 0x6050,0x6150,0x6250,0x6350,0x6450,0x6550,0x6650,0x6750, + 0x6850,0x6950,0x6450,0x6550,0x6650,0x6750,0x6850,0x6950, + 0x7050,0x7150,0x7250,0x7350,0x7450,0x7550,0x7650,0x7750, + 0x7850,0x7950,0x7450,0x7550,0x7650,0x7750,0x7850,0x7950, + 0x8050,0x8150,0x8250,0x8350,0x8450,0x8550,0x8650,0x8750, + 0x8850,0x8950,0x8450,0x8550,0x8650,0x8750,0x8850,0x8950, + 0x9050,0x9150,0x9250,0x9350,0x9450,0x9550,0x9650,0x9750, + 0x9850,0x9950,0x9450,0x9550,0x9650,0x9750,0x9850,0x9950, + 0xFA60,0xFB60,0xFC60,0xFD60,0xFE60,0xFF60,0x00C0,0x0140, + 0x0240,0x0340,0x0440,0x0540,0x0640,0x0740,0x0840,0x0940, + 0x0A60,0x0B60,0x0C60,0x0D60,0x0E60,0x0F60,0x1040,0x1140, + 0x1240,0x1340,0x1440,0x1540,0x1640,0x1740,0x1840,0x1940, + 0x1A60,0x1B60,0x1C60,0x1D60,0x1E60,0x1F60,0x2040,0x2140, + 0x2240,0x2340,0x2440,0x2540,0x2640,0x2740,0x2840,0x2940, + 0x2A60,0x2B60,0x2C60,0x2D60,0x2E60,0x2F60,0x3040,0x3140, + 0x3240,0x3340,0x3440,0x3540,0x3640,0x3740,0x3840,0x3940, + 0x3A60,0x3B60,0x3C60,0x3D60,0x3E60,0x3F60,0x4040,0x4140, + 0x4240,0x4340,0x4440,0x4540,0x4640,0x4740,0x4840,0x4940, + 0x4A60,0x4B60,0x4C60,0x4D60,0x4E60,0x4F60,0x5040,0x5140, + 0x5240,0x5340,0x5440,0x5540,0x5640,0x5740,0x5840,0x5940, + 0x5A60,0x5B60,0x5C60,0x5D60,0x5E60,0x5F60,0x6040,0x6140, + 0x6240,0x6340,0x6440,0x6540,0x6640,0x6740,0x6840,0x6940, + 0x6A60,0x6B60,0x6C60,0x6D60,0x6E60,0x6F60,0x7040,0x7140, + 0x7240,0x7340,0x7440,0x7540,0x7640,0x7740,0x7840,0x7940, + 0x7A60,0x7B60,0x7C60,0x7D60,0x7E60,0x7F60,0x8040,0x8140, + 0x8240,0x8340,0x8440,0x8540,0x8640,0x8740,0x8840,0x8940, + 0x8A60,0x8B60,0x8C60,0x8D60,0x8E60,0x8F60,0x9040,0x9140, + 0x9240,0x9340,0x3450,0x3550,0x3650,0x3750,0x3850,0x3950, + 0x3A70,0x3B70,0x3C70,0x3D70,0x3E70,0x3F70,0x4050,0x4150, + 0x4250,0x4350,0x4450,0x4550,0x4650,0x4750,0x4850,0x4950, + 0x4A70,0x4B70,0x4C70,0x4D70,0x4E70,0x4F70,0x5050,0x5150, + 0x5250,0x5350,0x5450,0x5550,0x5650,0x5750,0x5850,0x5950, + 0x5A70,0x5B70,0x5C70,0x5D70,0x5E70,0x5F70,0x6050,0x6150, + 0x6250,0x6350,0x6450,0x6550,0x6650,0x6750,0x6850,0x6950, + 0x6A70,0x6B70,0x6C70,0x6D70,0x6E70,0x6F70,0x7050,0x7150, + 0x7250,0x7350,0x7450,0x7550,0x7650,0x7750,0x7850,0x7950, + 0x7A70,0x7B70,0x7C70,0x7D70,0x7E70,0x7F70,0x8050,0x8150, + 0x8250,0x8350,0x8450,0x8550,0x8650,0x8750,0x8850,0x8950, + 0x8A70,0x8B70,0x8C70,0x8D70,0x8E70,0x8F70,0x9050,0x9150, + 0x9250,0x9350,0x9450,0x9550,0x9650,0x9750,0x9850,0x9950, + 0x9A70,0x9B70,0x9C70,0x9D70,0x9E70,0x9F70,0xA050,0xA150, + 0xA250,0xA350,0xA450,0xA550,0xA650,0xA750,0xA850,0xA950, + 0xAA70,0xAB70,0xAC70,0xAD70,0xAE70,0xAF70,0xB050,0xB150, + 0xB250,0xB350,0xB450,0xB550,0xB650,0xB750,0xB850,0xB950, + 0xBA70,0xBB70,0xBC70,0xBD70,0xBE70,0xBF70,0xC050,0xC150, + 0xC250,0xC350,0xC450,0xC550,0xC650,0xC750,0xC850,0xC950, + 0xCA70,0xCB70,0xCC70,0xCD70,0xCE70,0xCF70,0xD050,0xD150, + 0xD250,0xD350,0xD450,0xD550,0xD650,0xD750,0xD850,0xD950, + 0xDA70,0xDB70,0xDC70,0xDD70,0xDE70,0xDF70,0xE050,0xE150, + 0xE250,0xE350,0xE450,0xE550,0xE650,0xE750,0xE850,0xE950, + 0xEA70,0xEB70,0xEC70,0xED70,0xEE70,0xEF70,0xF050,0xF150, + 0xF250,0xF350,0xF450,0xF550,0xF650,0xF750,0xF850,0xF950, + 0xFA70,0xFB70,0xFC70,0xFD70,0xFE70,0xFF70,0x00D0,0x0150, + 0x0250,0x0350,0x0450,0x0550,0x0650,0x0750,0x0850,0x0950, + 0x0A70,0x0B70,0x0C70,0x0D70,0x0E70,0x0F70,0x1050,0x1150, + 0x1250,0x1350,0x1450,0x1550,0x1650,0x1750,0x1850,0x1950, + 0x1A70,0x1B70,0x1C70,0x1D70,0x1E70,0x1F70,0x2050,0x2150, + 0x2250,0x2350,0x2450,0x2550,0x2650,0x2750,0x2850,0x2950, + 0x2A70,0x2B70,0x2C70,0x2D70,0x2E70,0x2F70,0x3050,0x3150, + 0x3250,0x3350,0x3450,0x3550,0x3650,0x3750,0x3850,0x3950, + 0x3A70,0x3B70,0x3C70,0x3D70,0x3E70,0x3F70,0x4050,0x4150, + 0x4250,0x4350,0x4450,0x4550,0x4650,0x4750,0x4850,0x4950, + 0x4A70,0x4B70,0x4C70,0x4D70,0x4E70,0x4F70,0x5050,0x5150, + 0x5250,0x5350,0x5450,0x5550,0x5650,0x5750,0x5850,0x5950, + 0x5A70,0x5B70,0x5C70,0x5D70,0x5E70,0x5F70,0x6050,0x6150, + 0x6250,0x6350,0x6450,0x6550,0x6650,0x6750,0x6850,0x6950, + 0x6A70,0x6B70,0x6C70,0x6D70,0x6E70,0x6F70,0x7050,0x7150, + 0x7250,0x7350,0x7450,0x7550,0x7650,0x7750,0x7850,0x7950, + 0x7A70,0x7B70,0x7C70,0x7D70,0x7E70,0x7F70,0x8050,0x8150, + 0x8250,0x8350,0x8450,0x8550,0x8650,0x8750,0x8850,0x8950, + 0x8A70,0x8B70,0x8C70,0x8D70,0x8E70,0x8F70,0x9050,0x9150, + 0x9250,0x9350,0x9450,0x9550,0x9650,0x9750,0x9850,0x9950, +}; + +u8 ZeroTable[256] = { + 0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0 +}; + +#define GBSAVE_GAME_VERSION_1 1 +#define GBSAVE_GAME_VERSION_2 2 +#define GBSAVE_GAME_VERSION_3 3 +#define GBSAVE_GAME_VERSION_4 4 +#define GBSAVE_GAME_VERSION_5 5 +#define GBSAVE_GAME_VERSION_6 6 +#define GBSAVE_GAME_VERSION_7 7 +#define GBSAVE_GAME_VERSION_8 8 +#define GBSAVE_GAME_VERSION_9 9 +#define GBSAVE_GAME_VERSION_10 10 +#define GBSAVE_GAME_VERSION_11 11 +#define GBSAVE_GAME_VERSION GBSAVE_GAME_VERSION_11 + +int inline gbGetValue(int min,int max,int v) +{ + return (int)(min+(float)(max-min)*(2.0*(v/31.0)-(v/31.0)*(v/31.0))); +} + +void gbGenFilter() +{ + for (int r=0;r<32;r++) { + for (int g=0;g<32;g++) { + for (int b=0;b<32;b++) { + int nr=gbGetValue(gbGetValue(4,14,g), + gbGetValue(24,29,g),r)-4; + int ng=gbGetValue(gbGetValue(4+gbGetValue(0,5,r), + 14+gbGetValue(0,3,r),b), + gbGetValue(24+gbGetValue(0,3,r), + 29+gbGetValue(0,1,r),b),g)-4; + int nb=gbGetValue(gbGetValue(4+gbGetValue(0,5,r), + 14+gbGetValue(0,3,r),g), + gbGetValue(24+gbGetValue(0,3,r), + 29+gbGetValue(0,1,r),g),b)-4; + gbColorFilter[(b<<10)|(g<<5)|r]=(nb<<10)|(ng<<5)|nr; + } + } + } +} + +bool gbIsGameboyRom(char * file) +{ + if(strlen(file) > 4) { + char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".gb") == 0) + return true; + if(_stricmp(p, ".gbc") == 0) + return true; + if(_stricmp(p, ".cgb") == 0) + return true; + if(_stricmp(p, ".sgb") == 0) + return true; + } + } + + return false; +} + +void gbCopyMemory(u16 d, u16 s, int count) +{ + while(count) { + gbMemoryMap[d>>12][d & 0x0fff] = gbMemoryMap[s>>12][s & 0x0fff]; + s++; + d++; + count--; + } +} + +void gbDoHdma() +{ + + gbCopyMemory((gbHdmaDestination & 0x1ff0) | 0x8000, + gbHdmaSource & 0xfff0, + 0x10); + + gbHdmaDestination += 0x10; + if (gbHdmaDestination == 0xa000) + gbHdmaDestination = 0x8000; + + gbHdmaSource += 0x10; + if (gbHdmaSource == 0x8000) + gbHdmaSource = 0xa000; + + register_HDMA2 = gbHdmaSource & 0xff; + register_HDMA1 = gbHdmaSource>>8; + + register_HDMA4 = gbHdmaDestination & 0xff; + register_HDMA3 = gbHdmaDestination>>8; + + gbHdmaBytes -= 0x10; + gbMemory[0xff55] = --register_HDMA5; + if(register_HDMA5 == 0xff) + gbHdmaOn = 0; + +// We need to add the dmaClockticks for HDMA ! + if(gbSpeed) + gbDmaTicks = 17; + else + gbDmaTicks = 9; + + if (IFF & 0x80) + gbDmaTicks++; + +} + +// fix for Harley and Lego Racers +void gbCompareLYToLYC() +{ + if(register_LCDC & 0x80) { + if(register_LY == register_LYC) { + + // mark that we have a match + register_STAT |= 4; + + // check if we need an interrupt + if (register_STAT & 0x40) + { + // send LCD interrupt only if no interrupt 48h signal... + if (!gbInt48Signal) + { + register_IF |=2; + } + gbInt48Signal |= 8; + } + } + else // no match + { + register_STAT &= 0xfb; + gbInt48Signal &=~8; + } + } +} + +void gbWriteMemory(register u16 address, register u8 value) +{ + + if(address < 0x8000) { +#ifndef FINAL_VERSION + if(memorydebug && (address>0x3fff || address < 0x2000)) { + log("Memory register write %04x=%02x PC=%04x\n", + address, + value, + PC.W); + } + +#endif + if(mapper) + (*mapper)(address, value); + return; + + } + + if(address < 0xa000) { + // No access to Vram during mode 3 + // (used to emulate the gfx differences between GB & GBC-GBA/SP in Stunt Racer) + if ((gbLcdModeDelayed !=3) || + // This part is used to emulate a small difference between hardwares + // (check 8-in-1's arrow on GBA/GBC to verify it) + ((register_LY == 0) && ((gbHardware & 0xa) && (gbScreenOn==false) && + (register_LCDC & 0x80)) && + (gbLcdLYIncrementTicksDelayed ==(GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS)))) + gbMemoryMap[address>>12][address&0x0fff] = value; + return; + } + + // Used for the mirroring of 0xC000 in 0xE000 + if ((address >= 0xe000) && (address < 0xfe00)) + address &= ~0x2000; + + if(address < 0xc000) { +#ifndef FINAL_VERSION + if(memorydebug) { + log("Memory register write %04x=%02x PC=%04x\n", + address, + value, + PC.W); + } +#endif + + // Is that a correct fix ??? (it used to be 'if (mapper)')... + if(mapperRAM) + (*mapperRAM)(address, value); + return; + } + + + if(address < 0xfe00) { + gbMemoryMap[address>>12][address & 0x0fff] = value; + return; + } + + // OAM not accessible during mode 2 & 3. + if(address < 0xfea0) + { + if (((gbHardware & 0xa) && ((gbLcdMode | gbLcdModeDelayed) &2)) || + ((gbHardware & 5) && (((gbLcdModeDelayed == 2) && + (gbLcdTicksDelayed<=GBLCD_MODE_2_CLOCK_TICKS)) || + (gbLcdModeDelayed == 3)))) + return; + else + { + gbMemory[address] = value; + return; + } + } + + + + if((address > 0xfea0) && (address < 0xff00)){ // GBC allows reading/writing to that area + gbMemory[address] = value; + return; + } + + switch(address & 0x00ff) { + + case 0x00: { + gbMemory[0xff00] = ((gbMemory[0xff00] & 0xcf) | + (value & 0x30) | 0xc0); + if(gbSgbMode) { + gbSgbDoBitTransfer(value); + } + return; + } + + case 0x01: { + gbMemory[0xff01] = value; + return; + } + + // serial control + case 0x02: { + gbSerialOn = (value & 0x80); + gbMemory[0xff02] = value; + if(gbSerialOn) { + gbSerialTicks = GBSERIAL_CLOCK_TICKS; +#ifdef LINK_EMULATION + if(linkConnected) { + if(value & 1) { + linkSendByte(0x100|gbMemory[0xFF01]); + Sleep(5); + } + } +#endif + } + + gbSerialBits = 0; + return; + } + + case 0x04: { + // DIV register resets on any write + // (not totally perfect, but better than nothing) + gbMemory[0xff04] = register_DIV = 0; + gbDivTicks = GBDIV_CLOCK_TICKS; + // Another weird timer 'bug' : + // Writing to DIV register resets the internal timer, + // and can also increase TIMA/trigger an interrupt + // in some cases... + if (gbTimerOn && !(gbInternalTimer & (gbTimerClockTicks>>1))) + { + gbMemory[0xff05] = ++register_TIMA; + if(register_TIMA == 0) { + // timer overflow! + + // reload timer modulo + gbMemory[0xff05] = register_TIMA = register_TMA; + + // flag interrupt + gbMemory[0xff0f] = register_IF |= 4; + } + } + gbInternalTimer = 0xff; + return; + } + case 0x05: + gbMemory[0xff05] = register_TIMA = value; + return; + + case 0x06: + gbMemory[0xff06] = register_TMA = value; + return; + + // TIMER control + case 0x07: { + + gbTimerModeChange = (((value & 3) != (register_TAC&3)) && (value & register_TAC & 4)) ? true : false; + gbTimerOnChange = (((value ^ register_TAC) & 4) == 4) ? true : false; + + gbTimerOn = (value & 4) ? true : false; + + if (gbTimerOnChange || gbTimerModeChange) + { + gbTimerMode = value & 3; + + switch(gbTimerMode) { + case 0: + gbTimerClockTicks = GBTIMER_MODE_0_CLOCK_TICKS; + break; + case 1: + gbTimerClockTicks = GBTIMER_MODE_1_CLOCK_TICKS; + break; + case 2: + gbTimerClockTicks = GBTIMER_MODE_2_CLOCK_TICKS; + break; + case 3: + gbTimerClockTicks = GBTIMER_MODE_3_CLOCK_TICKS; + break; + } + } + + + // This looks weird, but this emulates a bug in which register_TIMA + // is increased when writing to register_TAC + // (This fixes Korodice's long-delay between menus bug). + + if (gbTimerOnChange || gbTimerModeChange) + { + bool temp = false; + + if ((gbTimerOn && !gbTimerModeChange) && (gbTimerMode & 2) && + !(gbInternalTimer & 0x80) && (gbInternalTimer & (gbTimerClockTicks>>1)) && + !(gbInternalTimer & (gbTimerClockTicks>>5))) + temp = true; + else if ((!gbTimerOn && !gbTimerModeChange && gbTimerOnChange ) && ((gbTimerTicks-1) < (gbTimerClockTicks>>1))) + temp = true; + else if (gbTimerOn && gbTimerModeChange && !gbTimerOnChange) + { + switch(gbTimerMode & 3) + { + case 0x00: + temp = false; + break; + case 0x01: + if (((gbInternalTimer & 0x82) == 2) && (gbTimerTicks>(clockTicks+1))) + temp = true; + break; + case 0x02: + if (((gbInternalTimer & 0x88) == 0x8) && (gbTimerTicks>(clockTicks+1))) + temp = true; + break; + case 0x03: + if (((gbInternalTimer & 0xA0) == 0x20) && (gbTimerTicks>(clockTicks+1))) + temp = true; + break; + } + } + + if (temp) + { + gbMemory[0xff05] = ++register_TIMA; + if((register_TIMA & 0xff) == 0) { + // timer overflow! + + // reload timer modulo + gbMemory[0xff05] = register_TIMA = register_TMA; + + // flag interrupt + gbMemory[0xff0f] = register_IF |= 4; + } + } + } + gbMemory[0xff07] = register_TAC = value; + return; + } + + case 0x0f: { + gbMemory[0xff0f] = register_IF = value; + //gbMemory[0xff0f] = register_IE |= value; + return; + } + + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1a: + case 0x1b: + case 0x1c: + case 0x1d: + case 0x1e: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: { + if (gbMemory[NR52] & 0x80) { + SOUND_EVENT(address,value); + return; + } + } + + case 0x26: { + SOUND_EVENT(address,value); + return; + } + + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3a: + case 0x3b: + case 0x3c: + case 0x3d: + case 0x3e: + case 0x3f: { + SOUND_EVENT(address,value); + //gbMemory[address] = value; + return; + } + + case 0x40: { + int lcdChange = (register_LCDC & 0x80) ^ (value & 0x80); + + // don't draw the window if it was not enabled and not being drawn before + if(!(register_LCDC & 0x20) && (value & 0x20) && gbWindowLine == -1 && + register_LY > register_WY) + gbWindowLine = 146; + // 007 fix : don't draw the first window's 1st line if it's enable 'too late' + // (ie. if register_LY == register_WY when enabling it) + // and move it to the next line + else if (!(register_LCDC & 0x20) && (value & 0x20) && (register_LY == register_WY)) + gbWindowLine = -2; + + + gbMemory[0xff40] = register_LCDC = value; + + + if(lcdChange) { + if((value & 0x80) && (!register_LCDCBusy)) { + + // if (!gbWhiteScreen && !gbSgbMask) + + // systemDrawScreen(); + + + + gbRegisterLYLCDCOffOn = (register_LY + 144) % 154; + + gbLcdTicks = GBLCD_MODE_2_CLOCK_TICKS - (gbSpeed ? 2 : 1); + gbLcdTicksDelayed = GBLCD_MODE_2_CLOCK_TICKS - (gbSpeed ? 1 : 0); + gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS - (gbSpeed ? 2 : 1); + gbLcdLYIncrementTicksDelayed = GBLY_INCREMENT_CLOCK_TICKS - (gbSpeed ? 1 : 0); + gbLcdMode = 2; + gbLcdModeDelayed = 2; + gbMemory[0xff41] = register_STAT = (register_STAT & 0xfc) | 2; + gbMemory[0xff44] = register_LY = 0x00; + gbInt48Signal = 0; + gbLYChangeHappened = false; + gbLCDChangeHappened = false; + gbWindowLine = 146; + oldRegister_WY = 146; + + // Fix for Namco Gallery Vol.2 + // (along with updating register_LCDC at the start of 'case 0x40') : + if(register_STAT & 0x20) + { + // send LCD interrupt only if no interrupt 48h signal... + if (!gbInt48Signal) + { + gbMemory[0xff0f] = register_IF |= 2; + } + gbInt48Signal |= 4; + } + gbCompareLYToLYC(); + + } else { + + register_LCDCBusy = clockTicks+6; + + //used to update the screen with white lines when it's off. + //(it looks strange, but it's pretty accurate) + + gbWhiteScreen = 0; + + gbScreenTicks = ((150-register_LY)*GBLY_INCREMENT_CLOCK_TICKS + + (49<<(gbSpeed ? 1 : 0))); + + // disable the screen rendering + gbScreenOn = false; + gbLcdTicks = 0; + gbLcdMode = 0; + gbLcdModeDelayed = 0; + gbMemory[0xff41] = register_STAT &= 0xfc; + gbInt48Signal = 0; + } + } + return; + } + + // STAT + case 0x41: { + //register_STAT = (register_STAT & 0x87) | + // (value & 0x7c); + gbMemory[0xff41] = register_STAT = (value & 0xf8) | (register_STAT & 0x07); // fix ? + // GB bug from Devrs FAQ + // Corrected : it happens if Lcd Mode<2, but also if LY == LYC whatever + // Lcd Mode is, and if !gbInt48Signal in all cases. The screen being off + // doesn't matter (the bug will still happen). + // That fixes 'Satoru Nakajima - F-1 Hero' crash bug. + + if((gbHardware & 5) && (((!gbInt48Signal) && (gbLcdMode<2) && (register_LCDC & 0x80)) || + (register_LY == register_LYC))) + { + gbMemory[0xff0f] = register_IF |=2; + } + + gbInt48Signal &= ((register_STAT>>3) & 0xF); + + if((register_LCDC & 0x80)) { + if ((register_STAT & 0x08) && (gbLcdMode == 0)) + { + if (!gbInt48Signal) + { + gbMemory[0xff0f] = register_IF |=2; + } + gbInt48Signal |= 1; + } + if ((register_STAT & 0x10) && (gbLcdMode == 1)) + { + if (!gbInt48Signal) + { + gbMemory[0xff0f] = register_IF |=2; + } + gbInt48Signal |= 2; + } + if ((register_STAT & 0x20) && (gbLcdMode == 2)) + { + if (!gbInt48Signal) + { + gbMemory[0xff0f] = register_IF |=2; + } + gbInt48Signal |= 4; + } + gbCompareLYToLYC(); + + gbMemory[0xff0f] = register_IF; + gbMemory[0xff41] = register_STAT; + + } + return; + } + + // SCY + case 0x42: { + int temp = -1; + + if ((gbLcdMode == 3) || (gbLcdModeDelayed == 3)) + temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - + gbLcdLYIncrementTicks); + + if (temp >=0) + { + for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) + if (temp < 300) + gbSCYLine[i] = value; + } + + else + memset(gbSCYLine, value, sizeof(gbSCYLine)); + + gbMemory[0xff42] = register_SCY = value; + return; + } + + // SCX + case 0x43: { + int temp = -1; + + if (gbLcdModeDelayed == 3) + temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - + gbLcdLYIncrementTicksDelayed); + + if (temp >=0) + { + for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) + if (temp < 300) + gbSCXLine[i] = value; + } + + else + memset(gbSCXLine, value, sizeof(gbSCXLine)); + + gbMemory[0xff43] = register_SCX = value; + return; + } + + // LY + case 0x44: { + // read only + return; + } + + // LYC + case 0x45: { + if (register_LYC != value) + { + gbMemory[0xff45] = register_LYC = value; + if(register_LCDC & 0x80) { + gbCompareLYToLYC(); + } + } + return; + } + + // DMA! + case 0x46: { + int source = value * 0x0100; + + gbCopyMemory(0xfe00, + source, + 0xa0); + gbMemory[0xff46] = register_DMA = value; + return; + } + + // BGP + case 0x47: { + + int temp = -1; + + gbMemory[0xff47] = value; + + if (gbLcdModeDelayed == 3) + temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - + gbLcdLYIncrementTicksDelayed); + + if (temp >=0) + { + for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) + if (temp < 300) + gbBgpLine[i] = value; + } + else + memset(gbBgpLine,value,sizeof(gbBgpLine)); + + gbBgp[0] = value & 0x03; + gbBgp[1] = (value & 0x0c)>>2; + gbBgp[2] = (value & 0x30)>>4; + gbBgp[3] = (value & 0xc0)>>6; + break; + } + + // OBP0 + case 0x48: { + int temp = -1; + + gbMemory[0xff48] = value; + + if (gbLcdModeDelayed == 3) + temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - + gbLcdLYIncrementTicksDelayed); + + if (temp >=0) + { + for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) + if (temp < 300) + gbObp0Line[i] = value; + } + else + memset(gbObp0Line,value,sizeof(gbObp0Line)); + + gbObp0[0] = value & 0x03; + gbObp0[1] = (value & 0x0c)>>2; + gbObp0[2] = (value & 0x30)>>4; + gbObp0[3] = (value & 0xc0)>>6; + break; + } + + // OBP1 + case 0x49: { + int temp = -1; + + gbMemory[0xff49] = value; + + if (gbLcdModeDelayed == 3) + temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - + gbLcdLYIncrementTicksDelayed); + + if (temp >=0) + { + for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) + if (temp < 300) + gbObp1Line[i] = value; + } + else + memset(gbObp1Line,value,sizeof(gbObp1Line)); + + gbObp1[0] = value & 0x03; + gbObp1[1] = (value & 0x0c)>>2; + gbObp1[2] = (value & 0x30)>>4; + gbObp1[3] = (value & 0xc0)>>6; + break; + } + + // WY + case 0x4a: + gbMemory[0xff4a] = register_WY = value; + if ((register_LY <= register_WY) && ((gbWindowLine < 0) || (gbWindowLine>=144))) + { + gbWindowLine = -1; + oldRegister_WY = register_WY; + } + return; + + // WX + case 0x4b: + gbMemory[0xff4b] = register_WX = value; + return; + + // KEY1 + case 0x4d: { + if(gbCgbMode) { + gbMemory[0xff4d] = (gbMemory[0xff4d] & 0x80) | (value & 1) | 0x7e; + return; + } + } + break; + + // VBK + case 0x4f: { + if(gbCgbMode) { + value = value & 1; + if(value == gbVramBank) + return; + + int vramAddress = value * 0x2000; + gbMemoryMap[0x08] = &gbVram[vramAddress]; + gbMemoryMap[0x09] = &gbVram[vramAddress + 0x1000]; + + gbVramBank = value; + register_VBK = value; + } + return; + } + break; + + // BOOTROM disable register (also gbCgbMode enabler if value & 0x10 ?) + case 0x50 : + { + if (useBios && inBios && !skipBios && (value & 1)) + { + gbMemoryMap[0x00] = &gbRom[0x0000]; + memcpy ((u8 *)(gbRom+0x100), (u8 *)(gbMemory + 0x100), 0xF00); + inBios = false; + } + } + + // HDMA1 + case 0x51: { + if(gbCgbMode) { + if(value > 0x7f && value < 0xa0) + value = 0; + + gbHdmaSource = (value << 8) | (gbHdmaSource & 0xf0); + + register_HDMA1 = value; + return; + } + } + break; + + // HDMA2 + case 0x52: { + if(gbCgbMode) { + value = value & 0xf0; + + gbHdmaSource = (gbHdmaSource & 0xff00) | (value); + + register_HDMA2 = value; + return; + } + } + break; + + // HDMA3 + case 0x53: { + if(gbCgbMode) { + value = value & 0x1f; + gbHdmaDestination = (value << 8) | (gbHdmaDestination & 0xf0); + gbHdmaDestination |= 0x8000; + register_HDMA3 = value; + return; + } + } + break; + + // HDMA4 + case 0x54: { + if(gbCgbMode) { + value = value & 0xf0; + gbHdmaDestination = (gbHdmaDestination & 0x1f00) | value; + gbHdmaDestination |= 0x8000; + register_HDMA4 = value; + return; + } + } + break; + + // HDMA5 + case 0x55: { + + if(gbCgbMode) { + gbHdmaBytes = 16 + (value & 0x7f) * 16; + if(gbHdmaOn) { + if(value & 0x80) { + gbMemory[0xff55] = register_HDMA5 = (value & 0x7f); + } else { + register_HDMA5 = 0xff; + gbHdmaOn = 0; + } + } else { + if(value & 0x80) { + gbHdmaOn = 1; + gbMemory[0xff55] = register_HDMA5 = value & 0x7f; + if(gbLcdModeDelayed == 0) + gbDoHdma(); + } else { + // we need to take the time it takes to complete the transfer into + // account... according to GB DEV FAQs, the setup time is the same + // for single and double speed, but the actual transfer takes the + // same time + if(gbSpeed) + gbDmaTicks = 2+16 * ((value & 0x7f) +1); + else + gbDmaTicks = 1+8 * ((value & 0x7f)+1); + + gbCopyMemory((gbHdmaDestination & 0x1ff0) | 0x8000, + gbHdmaSource & 0xfff0, + gbHdmaBytes); + gbHdmaDestination += gbHdmaBytes; + gbHdmaSource += gbHdmaBytes; + + gbMemory[0xff51] = register_HDMA1 = 0xff;// = (gbHdmaSource >> 8) & 0xff; + gbMemory[0xff52] = register_HDMA2 = 0xff;// = gbHdmaSource & 0xf0; + gbMemory[0xff53] = register_HDMA3 = 0xff;// = ((gbHdmaDestination - 0x8000) >> 8) & 0x1f; + gbMemory[0xff54] = register_HDMA4 = 0xff;// = gbHdmaDestination & 0xf0; + gbMemory[0xff55] = register_HDMA5 = 0xff; + } + } + return; + } + } + break; + + // BCPS + case 0x68: { + if(gbCgbMode) { + int paletteIndex = (value & 0x3f) >> 1; + int paletteHiLo = (value & 0x01); + + gbMemory[0xff68] = value; + + gbMemory[0xff69] = (paletteHiLo ? + (gbPalette[paletteIndex] >> 8) : + (gbPalette[paletteIndex] & 0x00ff)); + return; + } + } + break; + + // BCPD + case 0x69: { + if(gbCgbMode) { + int v = gbMemory[0xff68]; + int paletteIndex = (v & 0x3f) >> 1; + int paletteHiLo = (v & 0x01); + + // No access to gbPalette during mode 3 (Color Panel Demo) + if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || + (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || + ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || + ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) + { + gbMemory[0xff69] = value; + gbPalette[paletteIndex] = (paletteHiLo ? + ((value << 8) | (gbPalette[paletteIndex] & 0xff)) : + ((gbPalette[paletteIndex] & 0xff00) | (value))) & 0x7fff; + } + + + if(gbMemory[0xff68] & 0x80) { + int index = ((gbMemory[0xff68] & 0x3f) + 1) & 0x3f; + + gbMemory[0xff68] = (gbMemory[0xff68] & 0x80) | index; + gbMemory[0xff69] = (index & 1 ? + (gbPalette[index>>1] >> 8) : + (gbPalette[index>>1] & 0x00ff)); + } + return; + } + } + break; + + // OCPS + case 0x6a: { + if(gbCgbMode) { + int paletteIndex = (value & 0x3f) >> 1; + int paletteHiLo = (value & 0x01); + + paletteIndex += 32; + + gbMemory[0xff6a] = value; + + gbMemory[0xff6b] = (paletteHiLo ? + (gbPalette[paletteIndex] >> 8) : + (gbPalette[paletteIndex] & 0x00ff)); + return; + } + } + break; + + // OCPD + case 0x6b: { + + if(gbCgbMode) { + int v = gbMemory[0xff6a]; + int paletteIndex = (v & 0x3f) >> 1; + int paletteHiLo = (v & 0x01); + + paletteIndex += 32; + + // No access to gbPalette during mode 3 (Color Panel Demo) + if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || + (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || + ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || + ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) + { + gbMemory[0xff6b] = value; + gbPalette[paletteIndex] = (paletteHiLo ? + ((value << 8) | (gbPalette[paletteIndex] & 0xff)) : + ((gbPalette[paletteIndex] & 0xff00) | (value))) & 0x7fff; + } + + if(gbMemory[0xff6a] & 0x80) { + int index = ((gbMemory[0xff6a] & 0x3f) + 1) & 0x3f; + + gbMemory[0xff6a] = (gbMemory[0xff6a] & 0x80) | index; + + gbMemory[0xff6b] = (index & 1 ? + (gbPalette[(index>>1) + 32] >> 8) : + (gbPalette[(index>>1) + 32] & 0x00ff)); + } + return; + } + } + break; + + case 0x6c: { + gbMemory[0xff6c] = 0xfe | value; + return; + } + + + // SVBK + case 0x70: { + if(gbCgbMode) { + value = value & 7; + + int bank = value; + if(value == 0) + bank = 1; + + if(bank == gbWramBank) + return; + + int wramAddress = bank * 0x1000; + gbMemoryMap[0x0d] = &gbWram[wramAddress]; + + gbWramBank = bank; + gbMemory[0xff70] = register_SVBK = value; + return; + } + } + + case 0x75:{ + gbMemory[0xff75] = 0x8f | value; + return; + } + + case 0xff: { + gbMemory[0xffff] = register_IE = value; + return; + } + } + + if(address < 0xff80) + { + gbMemory[address] = value; + return; + } + + gbMemory[address] = value; +} + +u8 gbReadOpcode(register u16 address) +{ + if(gbCheatMap[address]) + return gbCheatRead(address); + + if(address < 0x8000) + return gbMemoryMap[address>>12][address&0x0fff]; + + if(address < 0xa000) + { + // A lot of 'ugly' checks... But only way to emulate this particular behaviour... + if (((gbHardware & 0xa) && ((gbLcdModeDelayed !=3) || ((register_LY == 0) && + (gbScreenOn==false) && (register_LCDC & 0x80)) && + (gbLcdLYIncrementTicksDelayed ==(GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS)))) || + ((gbHardware & 0x5) && (gbLcdModeDelayed !=3) && + ((gbLcdMode !=3) || ((register_LY == 0) && ((gbScreenOn==false) && + (register_LCDC & 0x80)) && + (gbLcdLYIncrementTicks ==(GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS)))))) + return gbMemoryMap[address>>12][address&0x0fff]; + return 0xff; + } + + // Used for the mirroring of 0xC000 in 0xE000 + if ((address >= 0xe000) && (address < 0xfe00)) + address &= ~0x2000; + + switch(address & 0xf000) { + case 0x0a: + case 0x0b: + if(mapperReadRAM) + return mapperReadRAM(address); + break; + case 0x0f: + if(address > 0xff00) { + switch(address & 0x00ff) { + case 0x02: + return (gbMemory[0xff02]); + case 0x03: + return (0xff); + case 0x04: + return register_DIV; + case 0x05: + return register_TIMA; + case 0x06: + return register_TMA; + case 0x07: + return (0xf8 | register_TAC); + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + return (0xff); + case 0x0f: + return (0xe0 | gbMemory[0xff0f]); + case 0x40: + return register_LCDC; + case 0x41: + // This is a GB/C only bug (ie. not GBA/SP). + if ((gbHardware & 7) && (gbLcdMode == 2) && (gbLcdModeDelayed == 1) && (!gbSpeed)) + return (0x80 | gbMemory[0xff41] & 0xFC); + else + return (0x80 | gbMemory[0xff41]); + case 0x42: + return register_SCY; + case 0x43: + return register_SCX; + case 0x44: + if (((gbHardware & 7) && ((gbLcdMode == 1) && (gbLcdTicks == 0x71))) || + (!(register_LCDC && 0x80))) + return 0; + else + return register_LY; + case 0x45: + return register_LYC; + case 0x46: + return register_DMA; + case 0x4a: + return register_WY; + case 0x4b: + return register_WX; + case 0x4c: + return 0xff; + case 0x4f: + return (0xfe | register_VBK); + case 0x51: + return register_HDMA1; + case 0x52: + return register_HDMA2; + case 0x53: + return register_HDMA3; + case 0x54: + return register_HDMA4; + case 0x55: + return register_HDMA5; + case 0x68: + case 0x6a: + if (gbCgbMode) + return (0x40 | gbMemory[address]); + else + return 0xc0; + case 0x69: + case 0x6b: + if (gbCgbMode) + { + // No access to gbPalette during mode 3 (Color Panel Demo) + if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || + (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || + ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || + ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) + return (gbMemory[address]); + else + return 0xff; + } + else + return 0xff; + case 0x70: + if (gbCgbMode) + return (0xf8 | register_SVBK); + else + return 0xff; + case 0xff: + return register_IE; + } + } + // OAM not accessible during mode 2 & 3. + if(((address >= 0xfe00) && (address<0xfea0)) && + ((gbLcdMode | gbLcdModeDelayed) &2)) + return 0xff; + break; + } + + if ((address >= 0xfea0) && (address < 0xff00)) + { + if (gbHardware & 1) + return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0x00 : 0xff ); + else if (gbHardware & 2) + return gbMemoryMap[address>>12][address & 0x0fff]; + else if (gbHardware & 4) + return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0xff : 0x00 ); + else if (gbHardware & 8) + return ((address & 0xf0) |((address & 0xf0)>>4)); + } + + return gbMemoryMap[address>>12][address & 0x0fff]; +} + +u8 gbReadMemory(register u16 address) +{ + if(gbCheatMap[address]) + return gbCheatRead(address); + + + if(address < 0x8000) + return gbMemoryMap[address>>12][address&0x0fff]; + + if(address < 0xa000) + { + // A lot of 'ugly' checks... But only way to emulate this particular behaviour... + if (((gbHardware & 0xa) && ((gbLcdModeDelayed !=3) || ((register_LY == 0) && + (gbScreenOn==false) && (register_LCDC & 0x80)) && + (gbLcdLYIncrementTicksDelayed ==(GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS)))) || + ((gbHardware & 0x5) && (gbLcdModeDelayed !=3) && + ((gbLcdMode !=3) || ((register_LY == 0) && ((gbScreenOn==false) && + (register_LCDC & 0x80)) && + (gbLcdLYIncrementTicks ==(GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS)))))) + return gbMemoryMap[address>>12][address&0x0fff]; + return 0xff; + } + + if ((address >= 0xe000) && (address < 0xfe00)) + address &= ~0x2000; + + if(address < 0xc000) { +#ifndef FINAL_VERSION + if(memorydebug) { + log("Memory register read %04x PC=%04x\n", + address, + PC.W); + } +#endif + + // for the 2kb ram limit (fixes crash in shawu's story + // but now its sram test fails, as the it expects 8kb and not 2kb... + // So use the 'genericflashcard' option to fix it). + if (address<=(0xa000+gbRamSizeMask)) + { + if(mapperReadRAM) + return mapperReadRAM(address); + return gbMemoryMap[address>>12][address & 0x0fff]; + } + return 0xff; + } + + if(address >= 0xff00) { + if ( address >= 0xFF10 && address <= 0xFF3F ) + return gbSoundRead( address ); + + switch(address & 0x00ff) { + case 0x00: + { + if(gbSgbMode) { + gbSgbReadingController |= 4; + gbSgbResetPacketState(); + } + + int b = gbMemory[0xff00]; + + if((b & 0x30) == 0x20) { + b &= 0xf0; + + int joy = 0; + if(gbSgbMode && gbSgbMultiplayer) { + switch(gbSgbNextController) { + case 0x0f: + joy = 0; + break; + case 0x0e: + joy = 1; + break; + case 0x0d: + joy = 2; + break; + case 0x0c: + joy = 3; + break; + default: + joy = 0; + } + } + int joystate = gbJoymask[joy]; + if(!(joystate & 128)) + b |= 0x08; + if(!(joystate & 64)) + b |= 0x04; + if(!(joystate & 32)) + b |= 0x02; + if(!(joystate & 16)) + b |= 0x01; + + gbMemory[0xff00] = b; + } else if((b & 0x30) == 0x10) { + b &= 0xf0; + + int joy = 0; + if(gbSgbMode && gbSgbMultiplayer) { + switch(gbSgbNextController) { + case 0x0f: + joy = 0; + break; + case 0x0e: + joy = 1; + break; + case 0x0d: + joy = 2; + break; + case 0x0c: + joy = 3; + break; + default: + joy = 0; + } + } + int joystate = gbJoymask[joy]; + if(!(joystate & 8)) + b |= 0x08; + if(!(joystate & 4)) + b |= 0x04; + if(!(joystate & 2)) + b |= 0x02; + if(!(joystate & 1)) + b |= 0x01; + + gbMemory[0xff00] = b; + } else { + if(gbSgbMode && gbSgbMultiplayer) { + gbMemory[0xff00] = 0xf0 | gbSgbNextController; + } else { + gbMemory[0xff00] = 0xff; + } + } + } + return gbMemory[0xff00]; + break; + case 0x01: + return gbMemory[0xff01]; + case 0x02: + return (gbMemory[0xff02]); + case 0x04: + return register_DIV; + case 0x05: + return register_TIMA; + case 0x06: + return register_TMA; + case 0x07: + return (0xf8 | register_TAC); + case 0x0f: + return (0xe0 | gbMemory[0xff0f]); + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + if ((gbMemory[NR30] & 0x80) && (gbMemory[NR34] & 0x80)) + return 0xFF; + else + return gbMemoryMap[address>>12][address & 0x0fff]; + case 0x40: + return register_LCDC; + case 0x41: + // This is a GB/C only bug (ie. not GBA/SP). + if ((gbHardware & 7) && (gbLcdMode == 2) && (gbLcdModeDelayed == 1) && (!gbSpeed)) + return (0x80 | gbMemory[0xff41] & 0xFC); + else + return (0x80 | gbMemory[0xff41]); + case 0x42: + return register_SCY; + case 0x43: + return register_SCX; + case 0x44: + if (((gbHardware & 7) && ((gbLcdMode == 1) && (gbLcdTicks == 0x71))) || + (!(register_LCDC && 0x80))) + return (0); + else + return register_LY; + case 0x45: + return register_LYC; + case 0x46: + return register_DMA; + case 0x4a: + return register_WY; + case 0x4b: + return register_WX; + case 0x4f: + return (0xfe | register_VBK); + case 0x51: + return register_HDMA1; + case 0x52: + return register_HDMA2; + case 0x53: + return register_HDMA3; + case 0x54: + return register_HDMA4; + case 0x55: + return register_HDMA5; + case 0x68: + case 0x6a: + if (gbCgbMode) + return (0x40 | gbMemory[address]); + else + return 0xc0; + case 0x69: + case 0x6b: + if (gbCgbMode) + { + // No access to gbPalette during mode 3 (Color Panel Demo) + if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || + (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || + ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || + ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) + return (gbMemory[address]); + else + return 0xff; + } + else + return 0xff; + case 0x70: + if (gbCgbMode) + return (0xf8 | register_SVBK); + else + return 0xff; + case 0xff: + return register_IE; + } + } + // OAM not accessible during mode 2 & 3. + if(((address >= 0xfe00) && (address<0xfea0)) && + (((gbLcdMode | gbLcdModeDelayed) &2) && + (!(gbSpeed && (gbHardware & 0x2) && !(gbLcdModeDelayed & 2) && (gbLcdMode == 2))) || + (gbSpeed && (gbHardware & 0x2) && (gbLcdModeDelayed == 0) && (gbLcdTicksDelayed == (GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]))))) + return 0xff; + + if ((address >= 0xfea0) && (address < 0xff00)) + { + if (gbHardware & 1) + return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0x00 : 0xff ); + else if (gbHardware & 2) + return gbMemoryMap[address>>12][address & 0x0fff]; + else if (gbHardware & 4) + return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0xff : 0x00 ); + else if (gbHardware & 8) + return ((address & 0xf0) |((address & 0xf0)>>4)); + } + + return gbMemoryMap[address>>12][address & 0x0fff]; +} + +void gbVblank_interrupt() +{ + gbCheatWrite(false); // Emulates GS codes. + gbMemory[0xff0f] = register_IF &= 0xfe; + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + PC.W = 0x40; +} + +void gbLcd_interrupt() +{ + gbMemory[0xff0f] = register_IF &= 0xfd; + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + PC.W = 0x48; +} + +void gbTimer_interrupt() +{ + gbMemory[0xff0f] = register_IF &= 0xfb; + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + PC.W = 0x50; +} + +void gbSerial_interrupt() +{ + gbMemory[0xff0f] = register_IF &= 0xf7; + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + PC.W = 0x58; +} + +void gbJoypad_interrupt() +{ + gbMemory[0xff0f] = register_IF &= 0xef; + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + PC.W = 0x60; +} + +void gbSpeedSwitch() +{ + gbBlackScreen = true; + if(gbSpeed == 0) { + gbSpeed = 1; + GBLCD_MODE_0_CLOCK_TICKS = 51 * 2; + GBLCD_MODE_1_CLOCK_TICKS = 1140 * 2; + GBLCD_MODE_2_CLOCK_TICKS = 20 * 2; + GBLCD_MODE_3_CLOCK_TICKS = 43 * 2; + GBLY_INCREMENT_CLOCK_TICKS = 114 * 2; + GBDIV_CLOCK_TICKS = 64; + GBTIMER_MODE_0_CLOCK_TICKS = 256; + GBTIMER_MODE_1_CLOCK_TICKS = 4; + GBTIMER_MODE_2_CLOCK_TICKS = 16; + GBTIMER_MODE_3_CLOCK_TICKS = 64; + GBSERIAL_CLOCK_TICKS = 128 * 2; + gbLcdTicks *= 2; + gbLcdTicksDelayed *=2; + gbLcdTicksDelayed--; + gbLcdLYIncrementTicks *= 2; + gbLcdLYIncrementTicksDelayed *= 2; + gbLcdLYIncrementTicksDelayed--; + gbSerialTicks *= 2; + //SOUND_CLOCK_TICKS = soundQuality * 24 * 2; + //soundTicks *= 2; + gbLine99Ticks = 3; + } else { + gbSpeed = 0; + GBLCD_MODE_0_CLOCK_TICKS = 51; + GBLCD_MODE_1_CLOCK_TICKS = 1140; + GBLCD_MODE_2_CLOCK_TICKS = 20; + GBLCD_MODE_3_CLOCK_TICKS = 43; + GBLY_INCREMENT_CLOCK_TICKS = 114; + GBDIV_CLOCK_TICKS = 64; + GBTIMER_MODE_0_CLOCK_TICKS = 256; + GBTIMER_MODE_1_CLOCK_TICKS = 4; + GBTIMER_MODE_2_CLOCK_TICKS = 16; + GBTIMER_MODE_3_CLOCK_TICKS = 64; + GBSERIAL_CLOCK_TICKS = 128; + gbLcdTicks >>= 1; + gbLcdTicksDelayed++; + gbLcdTicksDelayed >>=1; + gbLcdLYIncrementTicks >>= 1; + gbLcdLYIncrementTicksDelayed++; + gbLcdLYIncrementTicksDelayed >>= 1; + gbSerialTicks /= 2; + //SOUND_CLOCK_TICKS = soundQuality * 24; + //soundTicks /= 2; + gbLine99Ticks = 1; + if (gbHardware & 8) + gbLine99Ticks++; + } + gbDmaTicks += (134)*GBLY_INCREMENT_CLOCK_TICKS + (37<<(gbSpeed ? 1 : 0)); +} + +bool CPUIsGBBios(const char * file) +{ + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".gb") == 0) + return true; + if(_stricmp(p, ".bin") == 0) + return true; + if(_stricmp(p, ".bios") == 0) + return true; + if(_stricmp(p, ".rom") == 0) + return true; + } + } + + return false; +} + +void gbCPUInit(const char *biosFileName, bool useBiosFile) +{ + useBios = false; + if (useBiosFile) + { + int size = 0x100; + if(utilLoad(biosFileName, + CPUIsGBBios, + bios, + size)) { + if(size == 0x100) + useBios = true; + else + systemMessage(MSG_INVALID_BIOS_FILE_SIZE, N_("Invalid BOOTROM file size")); + } + } +} + +void gbGetHardwareType() +{ + gbCgbMode = 0; + gbSgbMode = 0; + if(gbRom[0x143] & 0x80) { + if((gbEmulatorType == 0) || + gbEmulatorType == 1 || + gbEmulatorType == 4) { + gbCgbMode = 1; + } + } + + if((gbCgbMode == 0 ) && (gbRom[0x146] == 0x03)) { + if(gbEmulatorType == 0 || + gbEmulatorType == 2 || + gbEmulatorType == 5) + gbSgbMode = 1; + } + + gbHardware = 1; // GB + if (((gbCgbMode == 1) && (gbEmulatorType == 0)) || (gbEmulatorType == 1)) + gbHardware = 2; // GBC + else if (((gbSgbMode == 1) && (gbEmulatorType == 0)) || (gbEmulatorType == 2) || (gbEmulatorType == 5)) + gbHardware = 4; // SGB(2) + else if (gbEmulatorType == 4) + gbHardware = 8; // GBA + + gbGBCColorType = 0; + if (gbHardware & 8) // If GBA is selected, choose the GBA default settings. + gbGBCColorType = 2; // (0 = GBC, 1 = GBA, 2 = GBASP) +} + +void gbReset() +{ + gbGetHardwareType(); + + oldRegister_WY = 146; + gbInterruptLaunched = 0; + + if(gbCgbMode == 1) { + if (gbVram == NULL) + gbVram = (u8 *)malloc(0x4000); + if (gbWram == NULL) + gbWram = (u8 *)malloc(0x8000); + memset(gbVram,0,0x4000); + memset(gbPalette,0, 2*128); + } + else + { + if(gbVram != NULL) { + free(gbVram); + gbVram = NULL; + } + if(gbWram != NULL) { + free(gbWram); + gbWram = NULL; + } + } + + gbLYChangeHappened = false; + gbLCDChangeHappened = false; + gbBlackScreen = false; + gbInterruptWait = 0; + gbDmaTicks = 0; + clockTicks = 0; + + if(gbSpeed) { + gbSpeedSwitch(); + gbMemory[0xff4d] = 0; + } + + // clean Wram + // This kinda emulates the startup state of Wram on GB/C (not very accurate, + // but way closer to the reality than filling it with 00es or FFes). + // On GBA/GBASP, it's kinda filled with random data. + // In all cases, most of the 2nd bank is filled with 00s. + // The starting data are important for some 'buggy' games, like Buster Brothers or + // Karamuchou ha Oosawagi!. + if (gbMemory != NULL) + { + memset(gbMemory,0xff, 65536); + for (int temp = 0xC000; temp < 0xE000; temp++) + if ((temp & 0x8) ^((temp & 0x800)>>8)) + { + if ((gbHardware & 0x02) && (gbGBCColorType == 0)) + gbMemory[temp] = 0x0; + else + gbMemory[temp] = 0x0f; + } + + else + gbMemory[temp] = 0xff; + } + + + + // clean LineBuffer + if (gbLineBuffer != NULL) + memset(gbLineBuffer, 0, sizeof(gbLineBuffer)); + // clean Pix + if (pix != NULL) + memset(pix, 0, sizeof(pix)); + // clean Vram + if (gbVram != NULL) + memset(gbVram, 0, 0x4000); + // clean Wram 2 + // This kinda emulates the startup state of Wram on GBC (not very accurate, + // but way closer to the reality than filling it with 00es or FFes). + // On GBA/GBASP, it's kinda filled with random data. + // In all cases, most of the 2nd bank is filled with 00s. + // The starting data are important for some 'buggy' games, like Buster Brothers or + // Karamuchou ha Oosawagi! + if (gbWram != NULL) + { + for (int i = 0; i<8; i++) + if (i != 2) + memcpy ((u16 *)(gbWram+i*0x1000), (u16 *)(gbMemory+0xC000), 0x1000); + } + + memset(gbSCYLine,0,sizeof(gbSCYLine)); + memset(gbSCXLine,0,sizeof(gbSCXLine)); + memset(gbBgpLine,0xfc,sizeof(gbBgpLine)); + if (gbHardware & 5) + { + memset(gbObp0Line,0xff,sizeof(gbObp0Line)); + memset(gbObp1Line,0xff,sizeof(gbObp1Line)); + } + else + { + memset(gbObp0Line,0x0,sizeof(gbObp0Line)); + memset(gbObp1Line,0x0,sizeof(gbObp1Line)); + } + memset(gbSpritesTicks,0x0,sizeof(gbSpritesTicks)); + + SP.W = 0xfffe; + AF.W = 0x01b0; + BC.W = 0x0013; + DE.W = 0x00d8; + HL.W = 0x014d; + PC.W = 0x0100; + IFF = 0; + gbInt48Signal = 0; + + register_TIMA = 0; + register_TMA = 0; + register_TAC = 0; + gbMemory[0xff0f] = register_IF = 1; + gbMemory[0xff40] = register_LCDC = 0x91; + gbMemory[0xff47] = 0xfc; + + if (gbCgbMode) + gbMemory[0xff4d] = 0x7e; + else + gbMemory[0xff4d] = 0xff; + + if (!gbCgbMode) + gbMemory[0xff70] = gbMemory[0xff74] = 0xff; + + if (gbCgbMode) + gbMemory[0xff56] = 0x3e; + else + gbMemory[0xff56] = 0xff; + + register_SCY = 0; + register_SCX = 0; + register_LYC = 0; + register_DMA = 0xff; + register_WY = 0; + register_WX = 0; + register_VBK = 0; + register_HDMA1 = 0xff; + register_HDMA2 = 0xff; + register_HDMA3 = 0xff; + register_HDMA4 = 0xff; + register_HDMA5 = 0xff; + register_SVBK = 0; + register_IE = 0; + + if (gbCgbMode) + gbMemory[0xff02] = 0x7c; + else + gbMemory[0xff02] = 0x7e; + + gbMemory[0xff03] = 0xff; + int i; + for (i = 0x8; i<0xf; i++) + gbMemory[0xff00+i] = 0xff; + + gbMemory[0xff13] = 0xff; + gbMemory[0xff15] = 0xff; + gbMemory[0xff18] = 0xff; + gbMemory[0xff1d] = 0xff; + gbMemory[0xff1f] = 0xff; + + for (i = 0x27; i<0x30; i++) + gbMemory[0xff00+i] = 0xff; + + gbMemory[0xff4c] = 0xff; + gbMemory[0xff4e] = 0xff; + gbMemory[0xff50] = 0xff; + + for (i = 0x57; i<0x68; i++) + gbMemory[0xff00+i] = 0xff; + + for (i = 0x5d; i<0x70; i++) + gbMemory[0xff00+i] = 0xff; + + gbMemory[0xff71] = 0xff; + + for (i = 0x78; i<0x80; i++) + gbMemory[0xff00+i] = 0xff; + + if (gbHardware & 0xa) + { + + if (gbHardware & 2) + { + AF.W = 0x1180; + BC.W = 0x0000; + } + else + { + AF.W = 0x1100; + BC.W = 0x0100; // GBA/SP have B = 0x01 (which means GBC & GBA/SP bootrom are different !) + } + + gbMemory[0xff26] = 0xf1; + if (gbCgbMode) + { + + gbMemory[0xff31] = 0xff; + gbMemory[0xff33] = 0xff; + gbMemory[0xff35] = 0xff; + gbMemory[0xff37] = 0xff; + gbMemory[0xff39] = 0xff; + gbMemory[0xff3b] = 0xff; + gbMemory[0xff3d] = 0xff; + + gbMemory[0xff44] = register_LY = 0x90; + gbDivTicks = 0x19 + ((gbHardware & 2) >> 1); + gbInternalTimer = 0x58 + ((gbHardware & 2) >> 1); + gbLcdTicks = GBLCD_MODE_1_CLOCK_TICKS - + (register_LY-0x8F)*GBLY_INCREMENT_CLOCK_TICKS + 72 + ((gbHardware & 2) >> 1); + gbLcdLYIncrementTicks = 72 + ((gbHardware & 2) >> 1); + gbMemory[0xff04] = register_DIV = 0x1E; + } + else + { + gbMemory[0xff44] = register_LY = 0x94; + gbDivTicks = 0x22 + ((gbHardware & 2) >> 1); + gbInternalTimer = 0x61 + ((gbHardware & 2) >> 1); + gbLcdTicks = GBLCD_MODE_1_CLOCK_TICKS - + (register_LY-0x8F)*GBLY_INCREMENT_CLOCK_TICKS + 25 + ((gbHardware & 2) >> 1); + gbLcdLYIncrementTicks = 25 + ((gbHardware & 2) >> 1); + gbMemory[0xff04] = register_DIV = 0x26; + } + + + DE.W = 0xff56; + HL.W = 0x000d; + + register_HDMA5 = 0xff; + gbMemory[0xff68] = 0xc0; + gbMemory[0xff6a] = 0xc0; + + + gbMemory[0xff41] = register_STAT = 0x81; + gbLcdMode = 1; + } + else + { + if (gbHardware & 4) + { + if(gbEmulatorType == 5) + AF.W = 0xffb0; + else + AF.W = 0x01b0; + BC.W = 0x0013; + DE.W = 0x00d8; + HL.W = 0x014d; + } + gbDivTicks = 14; + gbInternalTimer = gbDivTicks--; + gbMemory[0xff04] = register_DIV = 0xAB; + gbMemory[0xff41] = register_STAT = 0x85; + gbMemory[0xff44] = register_LY = 0x00; + gbLcdTicks = 15; + gbLcdLYIncrementTicks = 114+gbLcdTicks; + gbLcdMode = 1; + + // used for the handling of the gb Boot Rom + if ((gbHardware & 5) && (bios != NULL) && useBios && !skipBios) + { + memcpy ((u8 *)(gbMemory), (u8 *)(gbRom), 0x1000); + memcpy ((u8 *)(gbMemory), (u8 *)(bios), 0x100); + gbWhiteScreen = 0; + + gbInternalTimer = 0x3e; + gbDivTicks = 0x3f; + gbMemory[0xff04] = register_DIV = 0x00; + PC.W = 0x0000; + register_LCDC = 0x11; + gbScreenOn = false; + gbLcdTicks = 0; + gbLcdMode = 0; + gbLcdModeDelayed = 0; + gbMemory[0xff41] = register_STAT &= 0xfc; + gbInt48Signal = 0; + gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS; + } + } + + gbLine99Ticks = 1; + if (gbHardware & 8) + gbLine99Ticks++; + + gbLcdModeDelayed = gbLcdMode; + gbLcdTicksDelayed = gbLcdTicks+1; + gbLcdLYIncrementTicksDelayed = gbLcdLYIncrementTicks+1; + + + gbTimerModeChange = false; + gbTimerOnChange = false; + gbTimerOn = false; + + if(gbCgbMode) { + for (int i = 0; i<0x20; i++) + gbPalette[i] = 0x7fff; + + // This is just to show that the starting values of the OBJ palettes are different + // between the 3 consoles, and that they 'kinda' stay the same at each reset + // (they can slightly change, somehow (randomly?)). + // You can check the effects of gbGBCColorType on the "Vila Caldan Color" gbc demo. + // Note that you could also check the Div register to check on which system the game + // is running (GB,GBC and GBA(SP) have different startup values). + // Unfortunatly, I don't have any SGB system, so I can't get their starting values. + + if (gbGBCColorType == 0) // GBC Hardware + { + gbPalette[0x20] = 0x0600; + gbPalette[0x21] = 0xfdf3; + gbPalette[0x22] = 0x041c; + gbPalette[0x23] = 0xf5db; + gbPalette[0x24] = 0x4419; + gbPalette[0x25] = 0x57ea; + gbPalette[0x26] = 0x2808; + gbPalette[0x27] = 0x9b75; + gbPalette[0x28] = 0x129b; + gbPalette[0x29] = 0xfce0; + gbPalette[0x2a] = 0x22da; + gbPalette[0x2b] = 0x4ac5; + gbPalette[0x2c] = 0x2d71; + gbPalette[0x2d] = 0xf0c2; + gbPalette[0x2e] = 0x5137; + gbPalette[0x2f] = 0x2d41; + gbPalette[0x30] = 0x6b2d; + gbPalette[0x31] = 0x2215; + gbPalette[0x32] = 0xbe0a; + gbPalette[0x33] = 0xc053; + gbPalette[0x34] = 0xfe5f; + gbPalette[0x35] = 0xe000; + gbPalette[0x36] = 0xbe10; + gbPalette[0x37] = 0x914d; + gbPalette[0x38] = 0x7f91; + gbPalette[0x39] = 0x02b5; + gbPalette[0x3a] = 0x77ac; + gbPalette[0x3b] = 0x14e5; + gbPalette[0x3c] = 0xcf89; + gbPalette[0x3d] = 0xa03d; + gbPalette[0x3e] = 0xfd50; + gbPalette[0x3f] = 0x91ff; + } + else if (gbGBCColorType == 1) // GBA Hardware + { + gbPalette[0x20] = 0xbe00; + gbPalette[0x21] = 0xfdfd; + gbPalette[0x22] = 0xbd69; + gbPalette[0x23] = 0x7baf; + gbPalette[0x24] = 0xf5ff; + gbPalette[0x25] = 0x3f8f; + gbPalette[0x26] = 0xcee5; + gbPalette[0x27] = 0x5bf7; + gbPalette[0x28] = 0xb35b; + gbPalette[0x29] = 0xef97; + gbPalette[0x2a] = 0xef9f; + gbPalette[0x2b] = 0x97f7; + gbPalette[0x2c] = 0x82bf; + gbPalette[0x2d] = 0x9f3d; + gbPalette[0x2e] = 0xddde; + gbPalette[0x2f] = 0xbad5; + gbPalette[0x30] = 0x3cba; + gbPalette[0x31] = 0xdfd7; + gbPalette[0x32] = 0xedea; + gbPalette[0x33] = 0xfeda; + gbPalette[0x34] = 0xf7f9; + gbPalette[0x35] = 0xfdee; + gbPalette[0x36] = 0x6d2f; + gbPalette[0x37] = 0xf0e6; + gbPalette[0x38] = 0xf7f0; + gbPalette[0x39] = 0xf296; + gbPalette[0x3a] = 0x3bf1; + gbPalette[0x3b] = 0xe211; + gbPalette[0x3c] = 0x69ba; + gbPalette[0x3d] = 0x3d0d; + gbPalette[0x3e] = 0xdfd3; + gbPalette[0x3f] = 0xa6ba; + } + else if (gbGBCColorType == 2) // GBASP Hardware + { + gbPalette[0x20] = 0x9c00; + gbPalette[0x21] = 0x6340; + gbPalette[0x22] = 0x10c6; + gbPalette[0x23] = 0xdb97; + gbPalette[0x24] = 0x7622; + gbPalette[0x25] = 0x3e57; + gbPalette[0x26] = 0x2e12; + gbPalette[0x27] = 0x95c3; + gbPalette[0x28] = 0x1095; + gbPalette[0x29] = 0x488c; + gbPalette[0x2a] = 0x8241; + gbPalette[0x2b] = 0xde8c; + gbPalette[0x2c] = 0xfabc; + gbPalette[0x2d] = 0x0e81; + gbPalette[0x2e] = 0x7675; + gbPalette[0x2f] = 0xfdec; + gbPalette[0x30] = 0xddfd; + gbPalette[0x31] = 0x5995; + gbPalette[0x32] = 0x066a; + gbPalette[0x33] = 0xed1e; + gbPalette[0x34] = 0x1e84; + gbPalette[0x35] = 0x1d14; + gbPalette[0x36] = 0x11c3; + gbPalette[0x37] = 0x2749; + gbPalette[0x38] = 0xa727; + gbPalette[0x39] = 0x6266; + gbPalette[0x3a] = 0xe27b; + gbPalette[0x3b] = 0xe3fc; + gbPalette[0x3c] = 0x1f76; + gbPalette[0x3d] = 0xf158; + gbPalette[0x3e] = 0x468e; + gbPalette[0x3f] = 0xa540; + } + } else { + if(gbSgbMode) { + for(int i = 0; i < 8; i++) + gbPalette[i] = systemGbPalette[gbPaletteOption*8+i]; + + } + for(int i = 0; i < 8; i++) + gbPalette[i] = systemGbPalette[gbPaletteOption*8+i]; + } + + GBTIMER_MODE_0_CLOCK_TICKS = 256; + GBTIMER_MODE_1_CLOCK_TICKS = 4; + GBTIMER_MODE_2_CLOCK_TICKS = 16; + GBTIMER_MODE_3_CLOCK_TICKS = 64; + + GBLY_INCREMENT_CLOCK_TICKS = 114; + gbTimerTicks = GBTIMER_MODE_0_CLOCK_TICKS; + gbTimerClockTicks = GBTIMER_MODE_0_CLOCK_TICKS; + gbSerialTicks = 0; + gbSerialBits = 0; + gbSerialOn = 0; + gbWindowLine = -1; + gbTimerOn = false; + gbTimerMode = 0; + gbSpeed = 0; + gbJoymask[0] = gbJoymask[1] = gbJoymask[2] = gbJoymask[3] = 0; + + if(gbCgbMode) { + gbSpeed = 0; + gbHdmaOn = 0; + gbHdmaSource = 0x99d0; + gbHdmaDestination = 0x99d0; + gbVramBank = 0; + gbWramBank = 1; + + } + + // used to clean the borders + if (gbSgbMode) + { + gbSgbResetFlag = true; + gbSgbReset(); + if (gbBorderOn) + gbSgbRenderBorder(); + gbSgbResetFlag = false; + } + + for(i = 0; i < 4; i++) + gbBgp[i] = gbObp0[i] = gbObp1[i] = i; + + memset(&gbDataMBC1,0, sizeof(gbDataMBC1)); + gbDataMBC1.mapperROMBank = 1; + + gbDataMBC2.mapperRAMEnable = 0; + gbDataMBC2.mapperROMBank = 1; + + memset(&gbDataMBC3,0, 6 * sizeof(int)); + gbDataMBC3.mapperROMBank = 1; + + memset(&gbDataMBC5, 0, sizeof(gbDataMBC5)); + gbDataMBC5.mapperROMBank = 1; + + memset(&gbDataHuC1, 0, sizeof(gbDataHuC1)); + gbDataHuC1.mapperROMBank = 1; + + memset(&gbDataHuC3, 0, sizeof(gbDataHuC3)); + gbDataHuC3.mapperROMBank = 1; + + memset(&gbDataTAMA5,0, 26*sizeof(int)); + gbDataTAMA5.mapperROMBank = 1; + + memset(&gbDataMMM01,0, sizeof(gbDataMMM01)); + gbDataMMM01.mapperROMBank = 1; + + if (useBios && !skipBios && (gbHardware & 5)) + { + gbMemoryMap[0x00] = &gbMemory[0x0000]; + inBios = true; + } + else + { + gbMemoryMap[0x00] = &gbRom[0x0000]; + inBios = false; + } + + gbMemoryMap[0x01] = &gbRom[0x1000]; + gbMemoryMap[0x02] = &gbRom[0x2000]; + gbMemoryMap[0x03] = &gbRom[0x3000]; + gbMemoryMap[0x04] = &gbRom[0x4000]; + gbMemoryMap[0x05] = &gbRom[0x5000]; + gbMemoryMap[0x06] = &gbRom[0x6000]; + gbMemoryMap[0x07] = &gbRom[0x7000]; + if(gbCgbMode) { + gbMemoryMap[0x08] = &gbVram[0x0000]; + gbMemoryMap[0x09] = &gbVram[0x1000]; + gbMemoryMap[0x0a] = &gbMemory[0xa000]; + gbMemoryMap[0x0b] = &gbMemory[0xb000]; + gbMemoryMap[0x0c] = &gbMemory[0xc000]; + gbMemoryMap[0x0d] = &gbWram[0x1000]; + gbMemoryMap[0x0e] = &gbMemory[0xe000]; + gbMemoryMap[0x0f] = &gbMemory[0xf000]; + } else { + gbMemoryMap[0x08] = &gbMemory[0x8000]; + gbMemoryMap[0x09] = &gbMemory[0x9000]; + gbMemoryMap[0x0a] = &gbMemory[0xa000]; + gbMemoryMap[0x0b] = &gbMemory[0xb000]; + gbMemoryMap[0x0c] = &gbMemory[0xc000]; + gbMemoryMap[0x0d] = &gbMemory[0xd000]; + gbMemoryMap[0x0e] = &gbMemory[0xe000]; + gbMemoryMap[0x0f] = &gbMemory[0xf000]; + } + + if(gbRam) { + gbMemoryMap[0x0a] = &gbRam[0x0000]; + gbMemoryMap[0x0b] = &gbRam[0x1000]; + } + + gbSoundReset(); + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + gbLastTime = systemGetClock(); + gbFrameCount = 0; + + gbScreenOn = true; + gbSystemMessage = false; + + gbCheatWrite(true); // Emulates GS codes. + +} + +void gbWriteSaveMBC1(const char * name) +{ + if (gbRam) + { + FILE *gzFile = fopen(name,"wb"); + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(gbRam, + 1, + (gbRamSizeMask+1), + gzFile); + + fclose(gzFile); + } +} + +void gbWriteSaveMBC2(const char * name) +{ + if (gbRam) + { + FILE *file = fopen(name, "wb"); + + if(file == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(&gbMemory[0xa000], + 1, + 256, + file); + + fclose(file); + } +} + +void gbWriteSaveMBC3(const char * name, bool extendedSave) +{ + if (gbRam || extendedSave) + { + FILE *gzFile = fopen(name,"wb"); + if (gbRam) + { + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(gbRam, + 1, + (gbRamSizeMask+1), + gzFile); + } + + if(extendedSave) + fwrite(&gbDataMBC3.mapperSeconds, + 1, + 10*sizeof(int) + sizeof(time_t), + gzFile); + + fclose(gzFile); + } +} + +void gbWriteSaveMBC5(const char * name) +{ + if (gbRam) + { + FILE *gzFile = fopen(name,"wb"); + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(gbRam, + 1, + (gbRamSizeMask+1), + gzFile); + + fclose(gzFile); + } +} + +void gbWriteSaveMBC7(const char * name) +{ + if (gbRam) + { + FILE *file = fopen(name, "wb"); + + if(file == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(&gbMemory[0xa000], + 1, + 256, + file); + + fclose(file); + } +} + +void gbWriteSaveTAMA5(const char * name, bool extendedSave) +{ + FILE *gzFile = fopen(name,"wb"); + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + if (gbRam) + fwrite(gbRam, + 1, + (gbRamSizeMask+1), + gzFile); + + fwrite(gbTAMA5ram, + 1, + (gbTAMA5ramSize), + gzFile); + + if(extendedSave) + fwrite(&gbDataTAMA5.mapperSeconds, + 1, + 14*sizeof(int) + sizeof(time_t), + gzFile); + + fclose(gzFile); +} + +void gbWriteSaveMMM01(const char * name) +{ + if (gbRam) + { + FILE *gzFile = fopen(name,"wb"); + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(gbRam, + 1, + (gbRamSizeMask+1), + gzFile); + + fclose(gzFile); + } +} + + +bool gbReadSaveMBC1(const char * name) +{ + if (gbRam) + { + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) { + return false; + } + + int read = gzread(gzFile, + gbRam, + (gbRamSizeMask+1)); + + if(read != (gbRamSizeMask+1)) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + // Also checks if the battery file it bigger than gbRamSizeMask+1 ! + u8 data[1]; + data[0] = 0; + + read = gzread(gzFile, + data, + 1); + if(read >0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + gzclose(gzFile); + return true; + } + else + return false; +} + + +bool gbReadSaveMBC2(const char * name) +{ + if (gbRam) + { + FILE *file = fopen(name, "rb"); + + if(file == NULL) { + return false; + } + + size_t read = fread(&gbMemory[0xa000], + 1, + 256, + file); + + if(read != 256) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + fclose(file); + gbBatteryError = true; + return false; + } + + // Also checks if the battery file it bigger than gbRamSizeMask+1 ! + u8 data[1]; + data[0] = 0; + + read = fread(&data[0], + 1, + 1, + file); + if(read > 0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + fclose(file); + gbBatteryError = true; + return false; + } + + fclose(file); + return true; + } + else + return false; +} + +bool gbReadSaveMBC3(const char * name) +{ + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) { + return false; + } + + int read = 0; + + if (gbRam) + read = gzread(gzFile, + gbRam, + (gbRamSizeMask+1)); + else + read = (gbRamSizeMask+1); + + + bool res = true; + + if(read != (gbRamSizeMask+1)) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gbBatteryError = true; + res = false; + } else if ((gbRomType == 0xf) || (gbRomType == 0x10)){ + read = gzread(gzFile, + &gbDataMBC3.mapperSeconds, + sizeof(int)*10 + sizeof(time_t)); + + if(read != (sizeof(int)*10 + sizeof(time_t)) && read != 0) { + systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), + name); + res = false; + } + else if (read == 0) + { + systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), + name); + res = false; + } + else + { + // Also checks if the battery file it bigger than gbRamSizeMask+1+RTC ! + u8 data[1]; + data[0] = 0; + + read = gzread(gzFile, + data, + 1); + if(read >0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gbBatteryError = true; + res = false; + } + } + } + + gzclose(gzFile); + return res; +} + +bool gbReadSaveMBC5(const char * name) +{ + if (gbRam) + { + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) { + return false; + } + + int read = gzread(gzFile, + gbRam, + (gbRamSizeMask+1)); + + if(read != (gbRamSizeMask+1)) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + + // Also checks if the battery file it bigger than gbRamSizeMask+1 ! + u8 data[1]; + data[0] = 0; + + read = gzread(gzFile, + data, + 1); + if(read >0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + gzclose(gzFile); + return true; + } + else + return false; +} + +bool gbReadSaveMBC7(const char * name) +{ + if (gbRam) + { + FILE *file = fopen(name, "rb"); + + if(file == NULL) { + return false; + } + + size_t read = fread(&gbMemory[0xa000], + 1, + 256, + file); + + if(read != 256) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + fclose(file); + gbBatteryError = true; + return false; + } + + // Also checks if the battery file it bigger than gbRamSizeMask+1 ! + u8 data[1]; + data[0] = 0; + + read = fread(&data[0], + 1, + 1, + file); + if(read > 0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + fclose(file); + gbBatteryError = true; + return false; + } + + fclose(file); + return true; + } + else + return false; +} + +bool gbReadSaveTAMA5(const char * name) +{ + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) { + return false; + } + + int read = 0; + + if (gbRam) + read = gzread(gzFile, + gbRam, + (gbRamSizeMask+1)); + else + read = gbRamSizeMask; + + read += gzread(gzFile, + gbTAMA5ram, + gbTAMA5ramSize); + + bool res = true; + + if(read != (gbRamSizeMask+gbTAMA5ramSize+1)) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gbBatteryError = true; + res = false; + } else { + read = gzread(gzFile, + &gbDataTAMA5.mapperSeconds, + sizeof(int)*14 + sizeof(time_t)); + + if(read != (sizeof(int)*14 + sizeof(time_t)) && read != 0) { + systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), + name); + res = false; + } + else if (read == 0) + { + systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), + name); + res = false; + } + else + { + // Also checks if the battery file it bigger than gbRamSizeMask+1+RTC ! + u8 data[1]; + data[0] = 0; + + read = gzread(gzFile, + data, + 1); + if(read >0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gbBatteryError = true; + res = false; + } + } + } + + gzclose(gzFile); + return res; +} + + +bool gbReadSaveMMM01(const char * name) +{ + if (gbRam) + { + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) { + return false; + } + + int read = gzread(gzFile, + gbRam, + (gbRamSizeMask+1)); + + if(read != (gbRamSizeMask+1)) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + // Also checks if the battery file it bigger than gbRamSizeMask+1 ! + u8 data[1]; + data[0] = 0; + + read = gzread(gzFile, + data, + 1); + if(read >0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + gzclose(gzFile); + return true; + } + else + return false; +} + +void gbInit() +{ + gbGenFilter(); + gbSgbInit(); + + gbMemory = (u8 *)malloc(65536); + + pix = (u8 *)calloc(1,4*257*226); + + gbLineBuffer = (u16 *)malloc(160 * sizeof(u16)); +} + +bool gbWriteBatteryFile(const char *file, bool extendedSave) +{ + if(gbBattery) { + switch(gbRomType) { + case 0x03: + gbWriteSaveMBC1(file); + break; + case 0x06: + gbWriteSaveMBC2(file); + break; + case 0x0d: + gbWriteSaveMMM01(file); + break; + case 0x0f: + case 0x10: + gbWriteSaveMBC3(file, extendedSave); + break; + case 0x13: + case 0xfc: + gbWriteSaveMBC3(file, false); + case 0x1b: + case 0x1e: + gbWriteSaveMBC5(file); + break; + case 0x22: + gbWriteSaveMBC7(file); + break; + case 0xfd: + gbWriteSaveTAMA5(file, extendedSave); + break; + case 0xff: + gbWriteSaveMBC1(file); + break; + } + } + return true; +} + +bool gbWriteBatteryFile(const char *file) +{ + if (!gbBatteryError) + { + gbWriteBatteryFile(file, true); + return true; + } + else return false; +} + +bool gbReadBatteryFile(const char *file) +{ + bool res = false; + if(gbBattery) { + switch(gbRomType) { + case 0x03: + res = gbReadSaveMBC1(file); + break; + case 0x06: + res = gbReadSaveMBC2(file); + break; + case 0x0d: + res = gbReadSaveMMM01(file); + break; + case 0x0f: + case 0x10: + if(!gbReadSaveMBC3(file)) { + time(&gbDataMBC3.mapperLastTime); + struct tm *lt; + lt = localtime(&gbDataMBC3.mapperLastTime); + gbDataMBC3.mapperSeconds = lt->tm_sec; + gbDataMBC3.mapperMinutes = lt->tm_min; + gbDataMBC3.mapperHours = lt->tm_hour; + gbDataMBC3.mapperDays = lt->tm_yday & 255; + gbDataMBC3.mapperControl = (gbDataMBC3.mapperControl & 0xfe) | + (lt->tm_yday > 255 ? 1: 0); + res = false; + break; + } + res = true; + break; + case 0x13: + case 0xfc: + res = gbReadSaveMBC3(file); + case 0x1b: + case 0x1e: + res = gbReadSaveMBC5(file); + break; + case 0x22: + res = gbReadSaveMBC7(file); + case 0xfd: + if(!gbReadSaveTAMA5(file)) { + u8 gbDaysinMonth [12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + time(&gbDataTAMA5.mapperLastTime); + struct tm *lt; + lt = localtime(&gbDataTAMA5.mapperLastTime); + gbDataTAMA5.mapperSeconds = lt->tm_sec; + gbDataTAMA5.mapperMinutes = lt->tm_min; + gbDataTAMA5.mapperHours = lt->tm_hour; + gbDataTAMA5.mapperDays = 1; + gbDataTAMA5.mapperMonths = 1; + gbDataTAMA5.mapperYears = 1970; + int days = lt->tm_yday+365*3; + while (days) + { + gbDataTAMA5.mapperDays++; + days--; + if (gbDataTAMA5.mapperDays>gbDaysinMonth[gbDataTAMA5.mapperMonths-1]) + { + gbDataTAMA5.mapperDays = 1; + gbDataTAMA5.mapperMonths++; + if (gbDataTAMA5.mapperMonths>12) + { + gbDataTAMA5.mapperMonths = 1; + gbDataTAMA5.mapperYears++; + if ((gbDataTAMA5.mapperYears & 3) == 0) + gbDaysinMonth[1] = 29; + else + gbDaysinMonth[1] = 28; + } + } + } + gbDataTAMA5.mapperControl = (gbDataTAMA5.mapperControl & 0xfe) | + (lt->tm_yday > 255 ? 1: 0); + res = false; + break; + } + res = true; + break; + case 0xff: + res = gbReadSaveMBC1(file); + break; + } + } + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + return res; +} + +bool gbReadGSASnapshot(const char *fileName) +{ + FILE *file = fopen(fileName, "rb"); + + if(!file) { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); + return false; + } + + fseek(file, 0x4, SEEK_SET); + char buffer[16]; + char buffer2[16]; + fread(buffer, 1, 15, file); + buffer[15] = 0; + memcpy(buffer2, &gbRom[0x134], 15); + buffer2[15] = 0; + if(memcmp(buffer, buffer2, 15)) { + systemMessage(MSG_CANNOT_IMPORT_SNAPSHOT_FOR, + N_("Cannot import snapshot for %s. Current game is %s"), + buffer, + buffer2); + fclose(file); + return false; + } + fseek(file, 0x13, SEEK_SET); + size_t read = 0; + int toRead = 0; + switch(gbRomType) { + case 0x03: + case 0x0f: + case 0x10: + case 0x13: + case 0x1b: + case 0x1e: + case 0xff: + read = fread(gbRam, 1, (gbRamSizeMask+1), file); + toRead = (gbRamSizeMask+1); + break; + case 0x06: + case 0x22: + read = fread(&gbMemory[0xa000],1,256,file); + toRead = 256; + break; + default: + systemMessage(MSG_UNSUPPORTED_SNAPSHOT_FILE, + N_("Unsupported snapshot file %s"), + fileName); + fclose(file); + return false; + } + fclose(file); + gbReset(); + return true; +} + +variable_desc gbSaveGameStruct[] = { + { &PC.W, sizeof(u16) }, + { &SP.W, sizeof(u16) }, + { &AF.W, sizeof(u16) }, + { &BC.W, sizeof(u16) }, + { &DE.W, sizeof(u16) }, + { &HL.W, sizeof(u16) }, + { &IFF, sizeof(u8) }, + { &GBLCD_MODE_0_CLOCK_TICKS, sizeof(int) }, + { &GBLCD_MODE_1_CLOCK_TICKS, sizeof(int) }, + { &GBLCD_MODE_2_CLOCK_TICKS, sizeof(int) }, + { &GBLCD_MODE_3_CLOCK_TICKS, sizeof(int) }, + { &GBDIV_CLOCK_TICKS, sizeof(int) }, + { &GBLY_INCREMENT_CLOCK_TICKS, sizeof(int) }, + { &GBTIMER_MODE_0_CLOCK_TICKS, sizeof(int) }, + { &GBTIMER_MODE_1_CLOCK_TICKS, sizeof(int) }, + { &GBTIMER_MODE_2_CLOCK_TICKS, sizeof(int) }, + { &GBTIMER_MODE_3_CLOCK_TICKS, sizeof(int) }, + { &GBSERIAL_CLOCK_TICKS, sizeof(int) }, + { &GBSYNCHRONIZE_CLOCK_TICKS, sizeof(int) }, + { &gbDivTicks, sizeof(int) }, + { &gbLcdMode, sizeof(int) }, + { &gbLcdTicks, sizeof(int) }, + { &gbLcdLYIncrementTicks, sizeof(int) }, + { &gbTimerTicks, sizeof(int) }, + { &gbTimerClockTicks, sizeof(int) }, + { &gbSerialTicks, sizeof(int) }, + { &gbSerialBits, sizeof(int) }, + { &gbInt48Signal, sizeof(int) }, + { &gbInterruptWait, sizeof(int) }, + { &gbSynchronizeTicks, sizeof(int) }, + { &gbTimerOn, sizeof(int) }, + { &gbTimerMode, sizeof(int) }, + { &gbSerialOn, sizeof(int) }, + { &gbWindowLine, sizeof(int) }, + { &gbCgbMode, sizeof(int) }, + { &gbVramBank, sizeof(int) }, + { &gbWramBank, sizeof(int) }, + { &gbHdmaSource, sizeof(int) }, + { &gbHdmaDestination, sizeof(int) }, + { &gbHdmaBytes, sizeof(int) }, + { &gbHdmaOn, sizeof(int) }, + { &gbSpeed, sizeof(int) }, + { &gbSgbMode, sizeof(int) }, + { ®ister_DIV, sizeof(u8) }, + { ®ister_TIMA, sizeof(u8) }, + { ®ister_TMA, sizeof(u8) }, + { ®ister_TAC, sizeof(u8) }, + { ®ister_IF, sizeof(u8) }, + { ®ister_LCDC, sizeof(u8) }, + { ®ister_STAT, sizeof(u8) }, + { ®ister_SCY, sizeof(u8) }, + { ®ister_SCX, sizeof(u8) }, + { ®ister_LY, sizeof(u8) }, + { ®ister_LYC, sizeof(u8) }, + { ®ister_DMA, sizeof(u8) }, + { ®ister_WY, sizeof(u8) }, + { ®ister_WX, sizeof(u8) }, + { ®ister_VBK, sizeof(u8) }, + { ®ister_HDMA1, sizeof(u8) }, + { ®ister_HDMA2, sizeof(u8) }, + { ®ister_HDMA3, sizeof(u8) }, + { ®ister_HDMA4, sizeof(u8) }, + { ®ister_HDMA5, sizeof(u8) }, + { ®ister_SVBK, sizeof(u8) }, + { ®ister_IE , sizeof(u8) }, + { &gbBgp[0], sizeof(u8) }, + { &gbBgp[1], sizeof(u8) }, + { &gbBgp[2], sizeof(u8) }, + { &gbBgp[3], sizeof(u8) }, + { &gbObp0[0], sizeof(u8) }, + { &gbObp0[1], sizeof(u8) }, + { &gbObp0[2], sizeof(u8) }, + { &gbObp0[3], sizeof(u8) }, + { &gbObp1[0], sizeof(u8) }, + { &gbObp1[1], sizeof(u8) }, + { &gbObp1[2], sizeof(u8) }, + { &gbObp1[3], sizeof(u8) }, + { NULL, 0 } +}; + + +static bool gbWriteSaveState(gzFile gzFile) +{ + + utilWriteInt(gzFile, GBSAVE_GAME_VERSION); + + utilGzWrite(gzFile, &gbRom[0x134], 15); + + utilWriteInt(gzFile, useBios); + utilWriteInt(gzFile, inBios); + + utilWriteData(gzFile, gbSaveGameStruct); + + utilGzWrite(gzFile, &IFF, 2); + + if(gbSgbMode) { + gbSgbSaveGame(gzFile); + } + + utilGzWrite(gzFile, &gbDataMBC1, sizeof(gbDataMBC1)); + utilGzWrite(gzFile, &gbDataMBC2, sizeof(gbDataMBC2)); + utilGzWrite(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)); + utilGzWrite(gzFile, &gbDataMBC5, sizeof(gbDataMBC5)); + utilGzWrite(gzFile, &gbDataHuC1, sizeof(gbDataHuC1)); + utilGzWrite(gzFile, &gbDataHuC3, sizeof(gbDataHuC3)); + utilGzWrite(gzFile, &gbDataTAMA5, sizeof(gbDataTAMA5)); + if (gbTAMA5ram != NULL) + utilGzWrite(gzFile, gbTAMA5ram, gbTAMA5ramSize); + utilGzWrite(gzFile, &gbDataMMM01, sizeof(gbDataMMM01)); + + utilGzWrite(gzFile, gbPalette, 128 * sizeof(u16)); + + utilGzWrite(gzFile, &gbMemory[0x8000], 0x8000); + + if(gbRamSize && gbRam) { + utilWriteInt(gzFile, gbRamSize); + utilGzWrite(gzFile, gbRam, gbRamSize); + } + + if(gbCgbMode) { + utilGzWrite(gzFile, gbVram, 0x4000); + utilGzWrite(gzFile, gbWram, 0x8000); + } + + gbSoundSaveGame(gzFile); + + gbCheatsSaveGame(gzFile); + + utilWriteInt(gzFile, gbLcdModeDelayed); + utilWriteInt(gzFile, gbLcdTicksDelayed); + utilWriteInt(gzFile, gbLcdLYIncrementTicksDelayed); + utilWriteInt(gzFile, gbSpritesTicks[299]); + utilWriteInt(gzFile, gbTimerModeChange); + utilWriteInt(gzFile, gbTimerOnChange); + utilWriteInt(gzFile, gbHardware); + utilWriteInt(gzFile, gbBlackScreen); + utilWriteInt(gzFile, oldRegister_WY); + utilWriteInt(gzFile, gbWindowLine); + utilWriteInt(gzFile, inUseRegister_WY); + utilWriteInt(gzFile, gbScreenOn); + return true; +} + +bool gbWriteMemSaveState(char *memory, int available) +{ + gzFile gzFile = utilMemGzOpen(memory, available, "w"); + + if(gzFile == NULL) { + return false; + } + + bool res = gbWriteSaveState(gzFile); + + long pos = utilGzMemTell(gzFile)+8; + + if(pos >= (available)) + res = false; + + utilGzClose(gzFile); + + return res; +} + +bool gbWriteSaveState(const char *name) +{ + gzFile gzFile = utilGzOpen(name,"wb"); + + if(gzFile == NULL) + return false; + + bool res = gbWriteSaveState(gzFile); + + utilGzClose(gzFile); + return res; +} + +static bool gbReadSaveState(gzFile gzFile) +{ + int version = utilReadInt(gzFile); + + if(version > GBSAVE_GAME_VERSION || version < 0) { + systemMessage(MSG_UNSUPPORTED_VB_SGM, + N_("Unsupported VisualBoy save game version %d"), version); + return false; + } + + u8 romname[20]; + + utilGzRead(gzFile, romname, 15); + + if(memcmp(&gbRom[0x134], romname, 15) != 0) { + systemMessage(MSG_CANNOT_LOAD_SGM_FOR, + N_("Cannot load save game for %s. Playing %s"), + romname, &gbRom[0x134]); + return false; + } + + + bool ub = false; + bool ib = false; + + if (version >= 11) + { + ub = utilReadInt(gzFile) ? true : false; + ib = utilReadInt(gzFile) ? true : false; + + if((ub != useBios) && (ib)) { + if(useBios) + systemMessage(MSG_SAVE_GAME_NOT_USING_BIOS, + N_("Save game is not using the BIOS files")); + else + systemMessage(MSG_SAVE_GAME_USING_BIOS, + N_("Save game is using the BIOS file")); + return false; + } + } + + gbReset(); + + inBios = ib; + + utilReadData(gzFile, gbSaveGameStruct); + + + // Correct crash when loading color gameboy save in regular gameboy type. + if (!gbCgbMode) + { + if(gbVram != NULL) { + free(gbVram); + gbVram = NULL; + } + if(gbWram != NULL) { + free(gbWram); + gbWram = NULL; + } + } + else + { + if(gbVram == NULL) + gbVram = (u8 *)malloc(0x4000); + if(gbWram == NULL) + gbWram = (u8 *)malloc(0x8000); + memset(gbVram,0,0x4000); + memset(gbPalette,0, 2*128); + } + + + + if(version >= GBSAVE_GAME_VERSION_7) { + utilGzRead(gzFile, &IFF, 2); + } + + if(gbSgbMode) { + gbSgbReadGame(gzFile, version); + } else { + gbSgbMask = 0; // loading a game at the wrong time causes no display + } + if (version<11) + utilGzRead(gzFile, &gbDataMBC1, sizeof(gbDataMBC1) - sizeof(int)); + else + utilGzRead(gzFile, &gbDataMBC1, sizeof(gbDataMBC1)); + utilGzRead(gzFile, &gbDataMBC2, sizeof(gbDataMBC2)); + if(version < GBSAVE_GAME_VERSION_4) + // prior to version 4, there was no adjustment for the time the game + // was last played, so we have less to read. This needs update if the + // structure changes again. + utilGzRead(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)-sizeof(time_t)); + else + utilGzRead(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)); + utilGzRead(gzFile, &gbDataMBC5, sizeof(gbDataMBC5)); + utilGzRead(gzFile, &gbDataHuC1, sizeof(gbDataHuC1)); + utilGzRead(gzFile, &gbDataHuC3, sizeof(gbDataHuC3)); + if (version>=11) + { + utilGzRead(gzFile, &gbDataTAMA5, sizeof(gbDataTAMA5)); + if (gbTAMA5ram != NULL) + utilGzRead(gzFile, gbTAMA5ram, gbTAMA5ramSize); + utilGzRead(gzFile, &gbDataMMM01, sizeof(gbDataMMM01)); + } + + if(version < GBSAVE_GAME_VERSION_5) { + utilGzRead(gzFile, pix, 256*224*sizeof(u16)); + } + memset(pix, 0, 257*226*sizeof(u32)); + + if(version < GBSAVE_GAME_VERSION_6) { + utilGzRead(gzFile, gbPalette, 64 * sizeof(u16)); + } else + utilGzRead(gzFile, gbPalette, 128 * sizeof(u16)); + + if (version < 11) + utilGzRead(gzFile, gbPalette, 128 * sizeof(u16)); + + if(version < GBSAVE_GAME_VERSION_10) { + if(!gbCgbMode && !gbSgbMode) { + for(int i = 0; i < 8; i++) + gbPalette[i] = systemGbPalette[gbPaletteOption*8+i]; + } + } + + utilGzRead(gzFile, &gbMemory[0x8000], 0x8000); + + if(gbRamSize && gbRam) { + if (version < 11) + utilGzRead(gzFile, gbRam, gbRamSize); + else + { + int ramSize = utilReadInt(gzFile); + utilGzRead(gzFile, gbRam, (gbRamSize>ramSize) ? ramSize : gbRamSize); + if (ramSize>gbRamSize) + gzseek(gzFile,ramSize-gbRamSize,SEEK_CUR); + } + } + + memset(gbSCYLine, register_SCY, sizeof(gbSCYLine)); + memset(gbSCXLine, register_SCX, sizeof(gbSCXLine)); + memset(gbBgpLine, (gbBgp[0] | (gbBgp[1]<<2) | (gbBgp[2]<<4) | + (gbBgp[3]<<6)), sizeof(gbBgpLine)); + memset(gbObp0Line, (gbObp0[0] | (gbObp0[1]<<2) | (gbObp0[2]<<4) | + (gbObp0[3]<<6)), sizeof(gbObp0Line)); + memset(gbObp1Line, (gbObp1[0] | (gbObp1[1]<<2) | (gbObp1[2]<<4) | + (gbObp1[3]<<6)), sizeof(gbObp1Line)); + memset(gbSpritesTicks, 0x0, sizeof(gbSpritesTicks)); + + if (inBios) + { + gbMemoryMap[0x00] = &gbMemory[0x0000]; + memcpy ((u8 *)(gbMemory), (u8 *)(gbRom), 0x1000); + memcpy ((u8 *)(gbMemory), (u8 *)(bios), 0x100); + } + else + gbMemoryMap[0x00] = &gbRom[0x0000]; + gbMemoryMap[0x01] = &gbRom[0x1000]; + gbMemoryMap[0x02] = &gbRom[0x2000]; + gbMemoryMap[0x03] = &gbRom[0x3000]; + gbMemoryMap[0x04] = &gbRom[0x4000]; + gbMemoryMap[0x05] = &gbRom[0x5000]; + gbMemoryMap[0x06] = &gbRom[0x6000]; + gbMemoryMap[0x07] = &gbRom[0x7000]; + gbMemoryMap[0x08] = &gbMemory[0x8000]; + gbMemoryMap[0x09] = &gbMemory[0x9000]; + gbMemoryMap[0x0a] = &gbMemory[0xa000]; + gbMemoryMap[0x0b] = &gbMemory[0xb000]; + gbMemoryMap[0x0c] = &gbMemory[0xc000]; + gbMemoryMap[0x0d] = &gbMemory[0xd000]; + gbMemoryMap[0x0e] = &gbMemory[0xe000]; + gbMemoryMap[0x0f] = &gbMemory[0xf000]; + + switch(gbRomType) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + // MBC 1 + memoryUpdateMapMBC1(); + break; + case 0x05: + case 0x06: + // MBC2 + memoryUpdateMapMBC2(); + break; + case 0x0b: + case 0x0c: + case 0x0d: + // MMM01 + memoryUpdateMapMMM01(); + break; + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + // MBC 3 + memoryUpdateMapMBC3(); + break; + case 0x19: + case 0x1a: + case 0x1b: + // MBC5 + memoryUpdateMapMBC5(); + break; + case 0x1c: + case 0x1d: + case 0x1e: + // MBC 5 Rumble + memoryUpdateMapMBC5(); + break; + case 0x22: + // MBC 7 + memoryUpdateMapMBC7(); + break; + case 0x56: + // GS3 + memoryUpdateMapGS3(); + break; + case 0xfd: + // TAMA5 + memoryUpdateMapTAMA5(); + break; + case 0xfe: + // HuC3 + memoryUpdateMapHuC3(); + break; + case 0xff: + // HuC1 + memoryUpdateMapHuC1(); + break; + } + + if(gbCgbMode) { + utilGzRead(gzFile, gbVram, 0x4000); + utilGzRead(gzFile, gbWram, 0x8000); + + int value = register_SVBK; + if(value == 0) + value = 1; + + gbMemoryMap[0x08] = &gbVram[register_VBK * 0x2000]; + gbMemoryMap[0x09] = &gbVram[register_VBK * 0x2000 + 0x1000]; + gbMemoryMap[0x0d] = &gbWram[value * 0x1000]; + } + + gbSoundReadGame(version, gzFile); + + if (gbCgbMode && gbSgbMode) { + gbSgbMode = 0; + } + + if(gbBorderOn && !gbSgbMask) { + gbSgbRenderBorder(); + } + + systemDrawScreen(); + + if(version > GBSAVE_GAME_VERSION_1) + gbCheatsReadGame(gzFile, version); + + if (version<11) + { + gbWriteMemory(0xff00, 0); + gbMemory[0xff04] = register_DIV; + gbMemory[0xff05] = register_TIMA; + gbMemory[0xff06] = register_TMA; + gbMemory[0xff07] = register_TAC; + gbMemory[0xff40] = register_LCDC; + gbMemory[0xff42] = register_SCY; + gbMemory[0xff43] = register_SCX; + gbMemory[0xff44] = register_LY; + gbMemory[0xff45] = register_LYC; + gbMemory[0xff46] = register_DMA; + gbMemory[0xff4a] = register_WY; + gbMemory[0xff4b] = register_WX; + gbMemory[0xff4f] = register_VBK; + gbMemory[0xff51] = register_HDMA1; + gbMemory[0xff52] = register_HDMA2; + gbMemory[0xff53] = register_HDMA3; + gbMemory[0xff54] = register_HDMA4; + gbMemory[0xff55] = register_HDMA5; + gbMemory[0xff70] = register_SVBK; + gbMemory[0xffff] = register_IE; + GBDIV_CLOCK_TICKS = 64; + + if (gbSpeed) + gbDivTicks /=2; + + if ((gbLcdMode == 0) && (register_STAT & 8)) + gbInt48Signal |= 1; + if ((gbLcdMode == 1) && (register_STAT & 0x10)) + gbInt48Signal |= 2; + if ((gbLcdMode == 2) && (register_STAT & 0x20)) + gbInt48Signal |= 4; + if ((register_LY==register_LYC) && (register_STAT & 0x40)) + gbInt48Signal |= 8; + + gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS; + + if (gbLcdMode == 2) + gbLcdLYIncrementTicks-=GBLCD_MODE_2_CLOCK_TICKS-gbLcdTicks; + else if (gbLcdMode == 3) + gbLcdLYIncrementTicks -=GBLCD_MODE_2_CLOCK_TICKS+GBLCD_MODE_3_CLOCK_TICKS-gbLcdTicks; + else if (gbLcdMode == 0) + gbLcdLYIncrementTicks =gbLcdTicks; + else if (gbLcdMode == 1) + { + gbLcdLYIncrementTicks = gbLcdTicks % GBLY_INCREMENT_CLOCK_TICKS; + if (register_LY == 0x99) + gbLcdLYIncrementTicks =gbLine99Ticks; + else if (register_LY == 0) + gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS; + } + + gbLcdModeDelayed = gbLcdMode; + gbLcdTicksDelayed = gbLcdTicks--; + gbLcdLYIncrementTicksDelayed = gbLcdLYIncrementTicks--; + gbInterruptWait = 0; + memset(gbSpritesTicks,0,sizeof(gbSpritesTicks)); + } + else + { + gbLcdModeDelayed = utilReadInt(gzFile); + gbLcdTicksDelayed = utilReadInt(gzFile); + gbLcdLYIncrementTicksDelayed = utilReadInt(gzFile); + gbSpritesTicks[299] = utilReadInt(gzFile) & 0xff; + gbTimerModeChange = (utilReadInt(gzFile) ? true : false); + gbTimerOnChange = (utilReadInt(gzFile) ? true : false); + gbHardware = utilReadInt(gzFile); + gbBlackScreen = (utilReadInt(gzFile) ? true : false); + oldRegister_WY = utilReadInt(gzFile); + gbWindowLine = utilReadInt(gzFile); + inUseRegister_WY = utilReadInt(gzFile); + gbScreenOn = (utilReadInt(gzFile) ? true : false); + } + + if (gbSpeed) + gbLine99Ticks *= 2; + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + return true; +} + +bool gbReadMemSaveState(char *memory, int available) +{ + gzFile gzFile = utilMemGzOpen(memory, available, "r"); + + bool res = gbReadSaveState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool gbReadSaveState(const char *name) +{ + gzFile gzFile = utilGzOpen(name,"rb"); + + if(gzFile == NULL) { + return false; + } + + bool res = gbReadSaveState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool gbWritePNGFile(const char *fileName) +{ + if(gbBorderOn) + return utilWritePNGFile(fileName, 256, 224, pix); + return utilWritePNGFile(fileName, 160, 144, pix); +} + +bool gbWriteBMPFile(const char *fileName) +{ + if(gbBorderOn) + return utilWriteBMPFile(fileName, 256, 224, pix); + return utilWriteBMPFile(fileName, 160, 144, pix); +} + +void gbCleanUp() +{ + if(gbRam != NULL) { + free(gbRam); + gbRam = NULL; + } + + if(gbRom != NULL) { + free(gbRom); + gbRom = NULL; + } + + if(gbMemory != NULL) { + free(gbMemory); + gbMemory = NULL; + } + + if(gbLineBuffer != NULL) { + free(gbLineBuffer); + gbLineBuffer = NULL; + } + + if(pix != NULL) { + free(pix); + pix = NULL; + } + + gbSgbShutdown(); + + if(gbVram != NULL) { + free(gbVram); + gbVram = NULL; + } + + if(gbWram != NULL) { + free(gbWram); + gbWram = NULL; + } + + if(gbTAMA5ram != NULL) { + free(gbTAMA5ram); + gbTAMA5ram = NULL; + } + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; +} + +bool gbLoadRom(const char *szFile) +{ + int size = 0; + + if(gbRom != NULL) { + gbCleanUp(); + } + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + gbRom = utilLoad(szFile, + utilIsGBImage, + NULL, + size); + if(!gbRom) + return false; + + gbRomSize = size; + + gbBatteryError = false; + + if(bios != NULL) { + free(bios); + bios = NULL; + } + bios = (u8 *)calloc(1,0x100); + + return gbUpdateSizes(); +} + +bool gbUpdateSizes() +{ + if(gbRom[0x148] > 8) { + systemMessage(MSG_UNSUPPORTED_ROM_SIZE, + N_("Unsupported rom size %02x"), gbRom[0x148]); + return false; + } + + if(gbRomSize < gbRomSizes[gbRom[0x148]]) { + gbRom = (u8 *)realloc(gbRom, gbRomSizes[gbRom[0x148]]); + for (int i = gbRomSize; igbRomSizes[gbRom[0x148]]) && (genericflashcardEnable)) + { + gbRomSize = gbRomSize>>16; + gbRom[0x148] = 0; + if (gbRomSize) + { + while (!((gbRomSize & 1) || (gbRom[0x148] == 7))) + { + gbRom[0x148]++; + gbRomSize>>=1; + } + gbRom[0x148]++; + } + gbRom = (u8 *)realloc(gbRom, gbRomSizes[gbRom[0x148]]); + } + gbRomSize = gbRomSizes[gbRom[0x148]]; + gbRomSizeMask = gbRomSizesMasks[gbRom[0x148]]; + + + // The 'genericflashcard' option allows some PD to work. + // However, the setting is dangerous (if you let in enabled + // and play a normal game, it might just break everything). + // That's why it is not saved in the emulator options. + // Also I added some checks in VBA to make sure your saves will not be + // overwritten if you wrongly enable this option for a game + // you already played (and vice-versa, ie. if you forgot to + // enable the option for a game you played with it enabled, like Shawu Story). + u8 ramsize = genericflashcardEnable ? 5 : gbRom[0x149]; + gbRom[0x149] = ramsize; + + if ((gbRom[2] == 0x6D) && (gbRom[5] == 0x47) && (gbRom[6] == 0x65) && (gbRom[7] == 0x6E) && + (gbRom[8] == 0x69) && (gbRom[9] == 0x65) && (gbRom[0xA] == 0x28) && (gbRom[0xB] == 0x54)) + { + gbCheatingDevice = 1; // GameGenie + for (int i = 0; i < 0x20; i++) // Cleans GG hardware registers + gbRom[0x4000+i] = 0; + } + else if (((gbRom[0x104] == 0x44) && (gbRom[0x156] == 0xEA) && (gbRom[0x158] == 0x7F) && + (gbRom[0x159] == 0xEA) && (gbRom[0x15B] == 0x7F)) || ((gbRom[0x165] == 0x3E) && + (gbRom[0x166] == 0xD9) && (gbRom[0x16D] == 0xE1) && (gbRom[0x16E] == 0x7F))) + gbCheatingDevice = 2; // GameShark + else gbCheatingDevice = 0; + + if(ramsize > 5) { + systemMessage(MSG_UNSUPPORTED_RAM_SIZE, + N_("Unsupported ram size %02x"), gbRom[0x149]); + return false; + } + + gbRamSize = gbRamSizes[ramsize]; + gbRamSizeMask = gbRamSizesMasks[ramsize]; + + if(gbRamSize) { + gbRam = (u8 *)malloc(gbRamSize); + memset(gbRam, 0xff, gbRamSize); + } + + + gbRomType = gbRom[0x147]; + if (genericflashcardEnable) + { + /*if (gbRomType<2) + gbRomType =3; + else if ((gbRomType == 0xc) || (gbRomType == 0xf) || (gbRomType == 0x12) || + (gbRomType == 0x16) || (gbRomType == 0x1a) || (gbRomType == 0x1d)) + gbRomType++; + else if ((gbRomType == 0xb) || (gbRomType == 0x11) || (gbRomType == 0x15) || + (gbRomType == 0x19) || (gbRomType == 0x1c)) + gbRomType+=2; + else if ((gbRomType == 0x5) || (gbRomType == 0x6)) + gbRomType = 0x1a;*/ + gbRomType = 0x1b; + } + else if (gbCheatingDevice == 1) + gbRomType = 0x55; + else if (gbCheatingDevice == 2) + gbRomType = 0x56; + + gbRom[0x147] = gbRomType; + + mapperReadRAM = NULL; + + switch(gbRomType) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x08: + case 0x09: + // MBC 1 + mapper = mapperMBC1ROM; + mapperRAM = mapperMBC1RAM; + mapperReadRAM = mapperMBC1ReadRAM; + break; + case 0x05: + case 0x06: + // MBC2 + mapper = mapperMBC2ROM; + mapperRAM = mapperMBC2RAM; + gbRamSize = 0x200; + gbRamSizeMask = 0x1ff; + break; + case 0x0b: + case 0x0c: + case 0x0d: + // MMM01 + mapper = mapperMMM01ROM; + mapperRAM = mapperMMM01RAM; + break; + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0xfc: + // MBC 3 + mapper = mapperMBC3ROM; + mapperRAM = mapperMBC3RAM; + mapperReadRAM = mapperMBC3ReadRAM; + break; + case 0x19: + case 0x1a: + case 0x1b: + // MBC5 + mapper = mapperMBC5ROM; + mapperRAM = mapperMBC5RAM; + mapperReadRAM = mapperMBC5ReadRAM; + break; + case 0x1c: + case 0x1d: + case 0x1e: + // MBC 5 Rumble + mapper = mapperMBC5ROM; + mapperRAM = mapperMBC5RAM; + mapperReadRAM = mapperMBC5ReadRAM; + break; + case 0x22: + // MBC 7 + mapper = mapperMBC7ROM; + mapperRAM = mapperMBC7RAM; + mapperReadRAM = mapperMBC7ReadRAM; + break; + // GG (GameGenie) + case 0x55: + mapper = mapperGGROM; + break; + case 0x56: + // GS (GameShark) + mapper = mapperGS3ROM; + break; + case 0xfd: + // TAMA5 + if (gbRam!= NULL) + { + free(gbRam); + gbRam = NULL; + } + + ramsize = 3; + gbRamSize = gbRamSizes[3]; + gbRamSizeMask = gbRamSizesMasks[3]; + gbRam = (u8 *)malloc(gbRamSize); + memset(gbRam, 0x0, gbRamSize); + + gbTAMA5ramSize = 0x100; + + if (gbTAMA5ram == NULL) + gbTAMA5ram = (u8 *)malloc(gbTAMA5ramSize); + memset(gbTAMA5ram, 0x0, gbTAMA5ramSize); + + mapperRAM = mapperTAMA5RAM; + mapperReadRAM = mapperTAMA5ReadRAM; + mapperUpdateClock = memoryUpdateTAMA5Clock; + break; + case 0xfe: + // HuC3 + mapper = mapperHuC3ROM; + mapperRAM = mapperHuC3RAM; + mapperReadRAM = mapperHuC3ReadRAM; + break; + case 0xff: + // HuC1 + mapper = mapperHuC1ROM; + mapperRAM = mapperHuC1RAM; + break; + default: + systemMessage(MSG_UNKNOWN_CARTRIDGE_TYPE, + N_("Unknown cartridge type %02x"), gbRomType); + return false; + } + + switch(gbRomType) { + case 0x03: + case 0x06: + case 0x0f: + case 0x10: + case 0x13: + case 0x1b: + case 0x1d: + case 0x1e: + case 0x22: + case 0xfd: + case 0xff: + gbBattery = 1; + break; + } + + gbInit(); + + //gbReset(); + + switch(gbRomType) { + case 0x1c: + case 0x1d: + case 0x1e: + gbDataMBC5.isRumbleCartridge = 1; + } + + return true; +} + +int gbGetNextEvent (int clockTicks) +{ + if (register_LCDC & 0x80) + { + if(gbLcdTicks < clockTicks) + clockTicks = gbLcdTicks; + + if(gbLcdTicksDelayed < clockTicks) + clockTicks = gbLcdTicksDelayed; + + if(gbLcdLYIncrementTicksDelayed < clockTicks) + clockTicks = gbLcdLYIncrementTicksDelayed; + } + + if(gbLcdLYIncrementTicks < clockTicks) + clockTicks = gbLcdLYIncrementTicks; + + if(gbSerialOn && (gbSerialTicks < clockTicks)) + clockTicks = gbSerialTicks; + + if(gbTimerOn && (((gbInternalTimer) & gbTimerMask[gbTimerMode])+1 < clockTicks)) + clockTicks = ((gbInternalTimer) & gbTimerMask[gbTimerMode])+1; + + //if(soundTicks && (soundTicks < clockTicks)) + // clockTicks = soundTicks; + + if ((clockTicks<=0) || (gbInterruptWait)) + clockTicks = 1; + + return clockTicks; +} + +void gbDrawLine() +{ + switch(systemColorDepth) { + case 16: + { + u16 * dest = (u16 *)pix + + (gbBorderLineSkip+2) * (register_LY + gbBorderRowSkip+1) + + gbBorderColumnSkip; + for(int x = 0; x < 160; ) { + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + } + if(gbBorderOn) + dest += gbBorderColumnSkip; + *dest++ = 0; // for filters that read one pixel more + } + break; + + case 24: + { + u8 *dest = (u8 *)pix + + 3*(gbBorderLineSkip * (register_LY + gbBorderRowSkip) + + gbBorderColumnSkip); + for(int x = 0; x < 160;) { + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + } + } + break; + + case 32: + { + u32 * dest = (u32 *)pix + + (gbBorderLineSkip+1) * (register_LY + gbBorderRowSkip+1) + + gbBorderColumnSkip; + for(int x = 0; x < 160;) { + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + } + } + break; + } +} + +void gbEmulate(int ticksToStop) +{ + gbRegister tempRegister; + u8 tempValue; + s8 offset; + + clockTicks = 0; + gbDmaTicks = 0; + + register int opcode = 0; + + int opcode1 = 0; + int opcode2 = 0; + bool execute = false; + + while(1) { +#ifndef FINAL_VERSION + if(systemDebug) { + if(!(IFF & 0x80)) { + if(systemDebug > 1) { + sprintf(gbBuffer,"PC=%04x AF=%04x BC=%04x DE=%04x HL=%04x SP=%04x I=%04x\n", + PC.W, AF.W, BC.W, DE.W,HL.W,SP.W,IFF); + } else { + sprintf(gbBuffer,"PC=%04x I=%02x\n", PC.W, IFF); + } + log(gbBuffer); + } + } +#endif + + u16 oldPCW = PC.W; + + if(IFF & 0x80) { + if(register_LCDC & 0x80) { + clockTicks = gbLcdTicks; + } else + clockTicks = 1000; + + clockTicks = gbGetNextEvent(clockTicks); + + /*if(gbLcdTicksDelayed < clockTicks) + clockTicks = gbLcdTicksDelayed; + + if(gbLcdLYIncrementTicksDelayed < clockTicks) + clockTicks = gbLcdLYIncrementTicksDelayed; + + if(gbLcdLYIncrementTicks < clockTicks) + clockTicks = gbLcdLYIncrementTicks; + + if(gbSerialOn && (gbSerialTicks < clockTicks)) + clockTicks = gbSerialTicks; + + if(gbTimerOn && (((gbInternalTimer) & gbTimerMask[gbTimerMode])+1 < clockTicks)) + clockTicks = ((gbInternalTimer) & gbTimerMask[gbTimerMode])+1; + + if(soundTicks && (soundTicks < clockTicks)) + clockTicks = soundTicks; + + if ((clockTicks<=0) || (gbInterruptWait)) + clockTicks = 1;*/ + + } else { + + // First we apply the clockTicks, then we execute the opcodes. + opcode1 = 0; + opcode2 = 0; + execute = true; + + opcode2 = opcode1 = opcode = gbReadOpcode(PC.W++); + + // If HALT state was launched while IME = 0 and (register_IF & register_IE & 0x1F), + // PC.W is not incremented for the first byte of the next instruction. + if (IFF & 2) + { + PC.W--; + IFF &= ~2; + } + + clockTicks = gbCycles[opcode]; + + switch(opcode) { + case 0xCB: + // extended opcode + opcode2 = opcode = gbReadOpcode(PC.W++); + clockTicks = gbCyclesCB[opcode]; + break; + } + gbOldClockTicks = clockTicks-1; + gbIntBreak = 1; + } + + + if(!emulating) + return; + + // For 'breakpoint' support (opcode 0xFC is considered as a breakpoint) + if ((clockTicks==0) && execute) + { + PC.W = oldPCW; + return; + } + + + if (!(IFF & 0x80)) + clockTicks = 1; + + gbRedoLoop: + + + + if (gbInterruptWait) + gbInterruptWait = 0; + else + gbInterruptLaunched = 0; + + + // Used for the EI/DI instruction's delay. + if (IFF & 0x38) + { + int tempIFF = (IFF >> 4) & 3; + + if (tempIFF <=clockTicks) + { + tempIFF = 0; + IFF |=1; + } + else + tempIFF -= clockTicks; + IFF = (IFF & 0xCF) | (tempIFF <<4); + + if (IFF & 0x08) + IFF &= 0x82; + } + + + if (register_LCDCBusy) + { + register_LCDCBusy-=clockTicks; + if (register_LCDCBusy<0) + register_LCDCBusy = 0; + } + + + if(gbSgbMode) { + if(gbSgbPacketTimeout) { + gbSgbPacketTimeout -= clockTicks; + + if(gbSgbPacketTimeout <= 0) + gbSgbResetPacketState(); + } + } + + ticksToStop -= clockTicks; + + // DIV register emulation + gbDivTicks -= clockTicks; + while(gbDivTicks <= 0) { + gbMemory[0xff04] = ++register_DIV; + gbDivTicks += GBDIV_CLOCK_TICKS; + } + + if(register_LCDC & 0x80) { + // LCD stuff + + gbLcdTicks -= clockTicks; + gbLcdTicksDelayed -= clockTicks; + gbLcdLYIncrementTicks -= clockTicks; + gbLcdLYIncrementTicksDelayed -= clockTicks; + + + // our counters are off, see what we need to do + + // This looks (and kinda is) complicated, however this + // is the only way I found to emulate properly the way + // the real hardware operates... + while(((gbLcdTicks <= 0) && (gbLCDChangeHappened == false)) || + ((gbLcdTicksDelayed <= 0) && (gbLCDChangeHappened == true)) || + ((gbLcdLYIncrementTicks <= 0) && (gbLYChangeHappened == false)) || + ((gbLcdLYIncrementTicksDelayed<=0) && (gbLYChangeHappened == true))) + { + + if ((gbLcdLYIncrementTicks <= 0) && (!gbLYChangeHappened)) + { + gbLYChangeHappened = true; + gbMemory[0xff44] = register_LY = (register_LY + 1) % 154; + + if (register_LY == 0x91) + { + /* if (IFF & 0x80) + gbScreenOn = !gbScreenOn; + else*/ if (register_LCDC & 0x80) + gbScreenOn = true; + } + + gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS; + + if (gbLcdMode == 1) + { + + if(register_LY == 153) + gbLcdLYIncrementTicks -= GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; + else if(register_LY == 0) + gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; + } + + // GB only 'bug' : Halt state is broken one tick before LY==LYC interrupt + // is reflected in the registers. + if ((gbHardware & 5) && (IFF & 0x80) && (register_LY == register_LYC) + && (register_STAT & 0x40) && (register_LY != 0)) + { + if (!((gbLcdModeDelayed != 1) && (register_LY==0))) + { + gbInt48Signal &= ~9; + gbCompareLYToLYC(); + gbLYChangeHappened = false; + gbMemory[0xff41] = register_STAT; + gbMemory[0xff0f] = register_IF; + } + + gbLcdLYIncrementTicksDelayed += GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks+1; + } + } + + + if ((gbLcdTicks <= 0) && (!gbLCDChangeHappened)) + { + gbLCDChangeHappened = true; + + switch(gbLcdMode) + { + case 0: + { + // H-Blank + // check if we reached the V-Blank period + if(register_LY == 144) { + // Yes, V-Blank + // set the LY increment counter + if (gbHardware & 0x5) + { + register_IF |= 1; // V-Blank interrupt + } + + gbInt48Signal &= ~6; + if(register_STAT & 0x10) + { + // send LCD interrupt only if no interrupt 48h signal... + if ((!(gbInt48Signal & 1)) && ((!(gbInt48Signal & 8)) || (gbHardware & 0x0a))) + { + register_IF |=2; + gbInterruptLaunched |= 2; + if (gbHardware & 0xa) + gbInterruptWait = 1; + } + gbInt48Signal |= 2; + } + gbInt48Signal &= ~1; + + gbLcdTicks += GBLCD_MODE_1_CLOCK_TICKS; + gbLcdMode = 1; + + } else { + // go the the OAM being accessed mode + gbLcdTicks += GBLCD_MODE_2_CLOCK_TICKS; + gbLcdMode = 2; + + gbInt48Signal &= ~6; + if(register_STAT & 0x20) + { + // send LCD interrupt only if no interrupt 48h signal... + if (!gbInt48Signal) + { + register_IF |= 2; + gbInterruptLaunched |= 2; + } + gbInt48Signal |= 4; + } + gbInt48Signal &= ~1; + } + } + break; + case 1: + { + // V-Blank + // next mode is OAM being accessed mode + gbInt48Signal &= ~5; + if(register_STAT & 0x20) + { + // send LCD interrupt only if no interrupt 48h signal... + if (!gbInt48Signal) + { + register_IF |= 2; + gbInterruptLaunched |= 2; + if ((gbHardware & 0xa) && (IFF & 0x80)) + gbInterruptWait = 1; + } + gbInt48Signal |= 4; + } + gbInt48Signal &= ~2; + + gbLcdTicks += GBLCD_MODE_2_CLOCK_TICKS; + + gbLcdMode = 2; + register_LY = 0x00; + + } + break; + case 2: + { + + // OAM being accessed mode + // next mode is OAM and VRAM in use + if ((gbScreenOn) && (register_LCDC & 0x80)) + { + gbDrawSprites(false); + // Used to add a one tick delay when a window line is drawn. + //(fixes a part of Carmaggedon problem) + if((register_LCDC & 0x01 || gbCgbMode) && (register_LCDC & 0x20) && + (gbWindowLine != -2)) { + + int inUseRegister_WY = 0; + int tempgbWindowLine = gbWindowLine; + + if ((tempgbWindowLine == -1) || (tempgbWindowLine>144)) + { + inUseRegister_WY = oldRegister_WY; + if (register_LY>oldRegister_WY) + tempgbWindowLine = 146; + } + + int wy = inUseRegister_WY; + + if(register_LY >= inUseRegister_WY) { + + if (tempgbWindowLine == -1) + tempgbWindowLine = 0; + + int wx = register_WX; + wx -= 7; + if (wx<0) + wx = 0; + + if((wx <= 159) && (tempgbWindowLine <= 143)) + for (int i = wx; i<300; i++) + if (gbSpeed) + gbSpritesTicks[i]+=3; + else + gbSpritesTicks[i]+=1; + } + } + } + + gbInt48Signal &= ~7; + + gbLcdTicks += GBLCD_MODE_3_CLOCK_TICKS+gbSpritesTicks[299]; + gbLcdMode = 3; + } + break; + case 3: + { + // OAM and VRAM in use + // next mode is H-Blank + + + gbInt48Signal &= ~7; + if(register_STAT & 0x08) + { + // send LCD interrupt only if no interrupt 48h signal... + if (!(gbInt48Signal & 8)) + { + register_IF |= 2; + if ((gbHardware & 0xa) && (IFF & 0x80)) + gbInterruptWait = 1; + } + gbInt48Signal |= 1; + } + + gbLcdTicks += GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]; + + gbLcdMode = 0; + + // No HDMA during HALT ! + if(gbHdmaOn && (!(IFF & 0x80) || (register_IE & register_IF & 0x1f))) { + gbDoHdma(); + } + + } + break; + } + } + + + if ((gbLcdTicksDelayed <= 0) && (gbLCDChangeHappened)) { + int framesToSkip = systemFrameSkip; + if(speedup) + framesToSkip = 9; // try 6 FPS during speedup + //gbLcdTicksDelayed = gbLcdTicks+1; + gbLCDChangeHappened = false; + switch(gbLcdModeDelayed) { + case 0: + { + // H-Blank + + memset(gbSCYLine,gbSCYLine[299],sizeof(gbSCYLine)); + memset(gbSCXLine,gbSCXLine[299],sizeof(gbSCXLine)); + memset(gbBgpLine,gbBgpLine[299],sizeof(gbBgpLine)); + memset(gbObp0Line,gbObp0Line[299],sizeof(gbObp0Line)); + memset(gbObp1Line,gbObp1Line[299],sizeof(gbObp1Line)); + memset(gbSpritesTicks,gbSpritesTicks[299],sizeof(gbSpritesTicks)); + + if (gbWindowLine <0) + oldRegister_WY = register_WY; + // check if we reached the V-Blank period + if(register_LY == 144) { + // Yes, V-Blank + // set the LY increment counter + + if(register_LCDC & 0x80) { + if (gbHardware & 0xa) + { + + register_IF |= 1; // V-Blank interrupt + gbInterruptLaunched |=1; + } + + + } + + gbLcdTicksDelayed += GBLCD_MODE_1_CLOCK_TICKS; + gbLcdModeDelayed = 1; + + gbFrameCount++; + systemFrame(); + + if((gbFrameCount % 10) == 0) + system10Frames(60); + + if(gbFrameCount >= 60) { + u32 currentTime = systemGetClock(); + if(currentTime != gbLastTime) + systemShowSpeed(100000/(currentTime - gbLastTime)); + else + systemShowSpeed(0); + gbLastTime = currentTime; + gbFrameCount = 0; + } + + if(systemReadJoypads()) { + // read joystick + if(gbSgbMode && gbSgbMultiplayer) { + if(gbSgbFourPlayers) { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + gbJoymask[2] = systemReadJoypad(2); + gbJoymask[3] = systemReadJoypad(3); + } else { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + } + } else { + gbJoymask[0] = systemReadJoypad(-1); + } + } + int newmask = gbJoymask[0] & 255; + + if(gbRomType == 0x22) { + systemUpdateMotionSensor(); + } + + if(newmask) + { + gbMemory[0xff0f] |= 16; + } + + + newmask = (gbJoymask[0] >> 10); + + speedup = (newmask & 1) ? true : false; + gbCapture = (newmask & 2) ? true : false; + + if(gbCapture && !gbCapturePrevious) { + gbCaptureNumber++; + systemScreenCapture(gbCaptureNumber); + } + gbCapturePrevious = gbCapture; + + if(gbFrameSkipCount >= framesToSkip) { + + if(!gbSgbMask) + { + if (gbBorderOn) + gbSgbRenderBorder(); + //if (gbScreenOn) + systemDrawScreen(); + } + gbFrameSkipCount = 0; + } else + gbFrameSkipCount++; + + } else { + // go the the OAM being accessed mode + gbLcdTicksDelayed += GBLCD_MODE_2_CLOCK_TICKS; + gbLcdModeDelayed = 2; + gbInt48Signal &= ~3; + } + } + break; + case 1: + { + // V-Blank + // next mode is OAM being accessed mode + + // gbScreenOn = true; + + oldRegister_WY = register_WY; + + gbLcdTicksDelayed += GBLCD_MODE_2_CLOCK_TICKS; + gbLcdModeDelayed = 2; + + // reset the window line + gbWindowLine = -1; + } + break; + case 2: + { + // OAM being accessed mode + // next mode is OAM and VRAM in use + gbLcdTicksDelayed += GBLCD_MODE_3_CLOCK_TICKS+gbSpritesTicks[299]; + gbLcdModeDelayed = 3; + } + break; + case 3: + { + + // OAM and VRAM in use + // next mode is H-Blank + if((register_LY < 144) && (register_LCDC & 0x80) && gbScreenOn) { + if(!gbSgbMask) { + if(gbFrameSkipCount >= framesToSkip) { + if (!gbBlackScreen) + { + gbRenderLine(); + gbDrawSprites(true); + } + else if (gbBlackScreen) + { + u16 color = gbColorOption ? gbColorFilter[0] : 0; + if (!gbCgbMode) + color = gbColorOption ? gbColorFilter[gbPalette[3] & 0x7FFF] : + gbPalette[3] & 0x7FFF; + for(int i = 0; i < 160; i++) + { + gbLineMix[i] = color; + gbLineBuffer[i] = 0; + } + } + gbDrawLine(); + } + } + } + gbLcdTicksDelayed += GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]; + gbLcdModeDelayed = 0; + } + break; + } + } + + if ((gbLcdLYIncrementTicksDelayed <= 0) && (gbLYChangeHappened == true)) + { + + gbLYChangeHappened = false; + + if (!((gbLcdMode != 1) && (register_LY==0))) + { + { + gbInt48Signal &= ~8; + gbCompareLYToLYC(); + if ((gbInt48Signal == 8) && (!((register_LY == 0) && (gbHardware & 1)))) + gbInterruptLaunched |= 2; + if ((gbHardware & (gbSpeed ? 8 : 2)) && (register_LY==0) && ((register_STAT & 0x44) == 0x44) && (gbLcdLYIncrementTicksDelayed==0)) + { + gbInterruptWait = 1; + + } + } + } + gbLcdLYIncrementTicksDelayed += GBLY_INCREMENT_CLOCK_TICKS; + + if (gbLcdModeDelayed == 1) + { + + if(register_LY == 153) + gbLcdLYIncrementTicksDelayed -= GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; + else if(register_LY == 0) + gbLcdLYIncrementTicksDelayed += GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; + } + gbMemory[0xff0f] = register_IF; + gbMemory[0xff41] = register_STAT; + } + } + gbMemory[0xff0f] = register_IF; + gbMemory[0xff41] = register_STAT = (register_STAT & 0xfc) | gbLcdModeDelayed; + } + else + { + + // Used to update the screen with white lines when it's off. + // (it looks strange, but it's kinda accurate :p) + // You can try the Mario Demo Vx.x for exemple + // (check the bottom 2 lines while moving) + if (!gbWhiteScreen) + { + gbScreenTicks -= clockTicks; + gbLcdLYIncrementTicks -= clockTicks; + while (gbLcdLYIncrementTicks <=0) + { + register_LY = ((register_LY+1)%154); + gbLcdLYIncrementTicks+=GBLY_INCREMENT_CLOCK_TICKS; + } + if (gbScreenTicks <= 0) + { + gbWhiteScreen = 1; + u8 register_LYLcdOff = ((register_LY+154)%154); + for (register_LY=0;register_LY <= 0x90;register_LY++) + { + u16 color = gbColorOption ? gbColorFilter[0x7FFF] : + 0x7FFF; + if (!gbCgbMode) + color = gbColorOption ? gbColorFilter[gbPalette[0] & 0x7FFF] : + gbPalette[0] & 0x7FFF; + for(int i = 0; i < 160; i++) + { + gbLineMix[i] = color; + gbLineBuffer[i] = 0; + } + gbDrawLine(); + } + register_LY = register_LYLcdOff; + } + } + + if (gbWhiteScreen) + { + gbLcdLYIncrementTicks -= clockTicks; + + while (gbLcdLYIncrementTicks <=0) + { + register_LY = ((register_LY+1)%154); + gbLcdLYIncrementTicks+=GBLY_INCREMENT_CLOCK_TICKS; + if (register_LY<144) + { + + u16 color = gbColorOption ? gbColorFilter[0x7FFF] : + 0x7FFF; + if (!gbCgbMode) + color = gbColorOption ? gbColorFilter[gbPalette[0] & 0x7FFF] : + gbPalette[0] & 0x7FFF; + for(int i = 0; i < 160; i++) + { + gbLineMix[i] = color; + gbLineBuffer[i] = 0; + } + gbDrawLine(); + } + else if ((register_LY==144) && (!systemFrameSkip)) + { + int framesToSkip = systemFrameSkip; + if(speedup) + framesToSkip = 9; // try 6 FPS during speedup + if((gbFrameSkipCount >= framesToSkip) || (gbWhiteScreen == 1)) { + gbWhiteScreen = 2; + + if(!gbSgbMask) + { + if (gbBorderOn) + gbSgbRenderBorder(); + //if (gbScreenOn) + systemDrawScreen(); + } + } + if(systemReadJoypads()) { + // read joystick + if(gbSgbMode && gbSgbMultiplayer) { + if(gbSgbFourPlayers) { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + gbJoymask[2] = systemReadJoypad(2); + gbJoymask[3] = systemReadJoypad(3); + } else { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + } + } else { + gbJoymask[0] = systemReadJoypad(-1); + } + } + gbFrameCount++; + + systemFrame(); + + if((gbFrameCount % 10) == 0) + system10Frames(60); + + if(gbFrameCount >= 60) { + u32 currentTime = systemGetClock(); + if(currentTime != gbLastTime) + systemShowSpeed(100000/(currentTime - gbLastTime)); + else + systemShowSpeed(0); + gbLastTime = currentTime; + gbFrameCount = 0; + } + } + } + } + } + + gbMemory[0xff41] = register_STAT; + + // serial emulation + if(gbSerialOn) { +#ifdef LINK_EMULATION + if(linkConnected) { + gbSerialTicks -= clockTicks; + + while(gbSerialTicks <= 0) { + // increment number of shifted bits + gbSerialBits++; + linkProc(); + if(gbSerialOn && (gbMemory[0xff02] & 1)) { + if(gbSerialBits == 8) { + gbSerialBits = 0; + gbMemory[0xff01] = 0xff; + gbMemory[0xff02] &= 0x7f; + gbSerialOn = 0; + gbMemory[0xff0f] = register_IF |= 8; + gbSerialTicks = 0; + } + } + gbSerialTicks += GBSERIAL_CLOCK_TICKS; + } + } else { +#endif + if(gbMemory[0xff02] & 1) { + gbSerialTicks -= clockTicks; + + // overflow + while(gbSerialTicks <= 0) { + // shift serial byte to right and put a 1 bit in its place + // gbMemory[0xff01] = 0x80 | (gbMemory[0xff01]>>1); + // increment number of shifted bits + gbSerialBits++; + if(gbSerialBits == 8) { + // end of transmission + if(gbSerialFunction) // external device + gbMemory[0xff01] = gbSerialFunction(gbMemory[0xff01]); + else + gbMemory[0xff01] = 0xff; + gbSerialTicks = 0; + gbMemory[0xff02] &= 0x7f; + gbSerialOn = 0; + gbMemory[0xff0f] = register_IF |= 8; + gbSerialBits = 0; + } else + gbSerialTicks += GBSERIAL_CLOCK_TICKS; + } + } +#ifdef LINK_EMULATION + } +#endif + } + + + soundTicks -= clockTicks; + if ( !gbSpeed ) + soundTicks -= clockTicks; + + while(soundTicks < 0) { + soundTicks += SOUND_CLOCK_TICKS; + + gbSoundTick(); + } + + + // timer emulation + + if(gbTimerOn) { + gbTimerTicks= ((gbInternalTimer) & gbTimerMask[gbTimerMode])+1-clockTicks; + + while(gbTimerTicks <= 0) { + register_TIMA++; + // timer overflow! + if((register_TIMA & 0xff) == 0) { + // reload timer modulo + register_TIMA = register_TMA; + // flag interrupt + gbMemory[0xff0f] = register_IF |= 4; + } + gbTimerTicks += gbTimerClockTicks; + } + gbTimerOnChange = false; + gbTimerModeChange = false; + + gbMemory[0xff05] = register_TIMA; + + } + + gbInternalTimer -= clockTicks; + while (gbInternalTimer<0) + gbInternalTimer+=0x100; + + /* + if(soundOffFlag) { + if(synchronize && !speedup) { + synchronizeTicks -= clockTicks; + + while(synchronizeTicks < 0) { + synchronizeTicks += SYNCHRONIZE_CLOCK_TICKS; + + DWORD now = timeGetTime(); + gbElapsedTime += (now - timeNow); + + if(gbElapsedTime < 50) { + DWORD diff = 50 - gbElapsedTime; + Sleep(diff); + timeNow = timeGetTime(); + elapsedTime = timeNow - now - diff; + if((int)elapsedTime < 0) + elapsedTime = 0; + } else { + timeNow = timeGetTime(); + elapsedTime = 0; + } + } + } + } + */ + + clockTicks = 0; + + if (gbIntBreak == 1) + { + gbIntBreak = 0; + if ((register_IE & register_IF & gbInterruptLaunched & 0x3) && + ((IFF & 0x81) == 1) && (!gbInterruptWait) && (execute)) + { + gbIntBreak = 2; + PC.W = oldPCW; + execute = false; + gbOldClockTicks = 0; + } + if (gbOldClockTicks) + { + clockTicks = gbOldClockTicks; + gbOldClockTicks = 0; + goto gbRedoLoop; + } + } + + // Executes the opcode(s), and apply the instruction's remaining clockTicks (if any). + if (execute) + { + switch(opcode1) { + case 0xCB: + // extended opcode + switch(opcode2) { +#include "gbCodesCB.h" + } + break; +#include "gbCodes.h" + } + execute = false; + + if (clockTicks) + { + gbDmaTicks += clockTicks; + clockTicks = 0; + } + } + + if (gbDmaTicks) + { + clockTicks = gbGetNextEvent(gbDmaTicks); + + if (clockTicks<=gbDmaTicks) + gbDmaTicks -= clockTicks; + else + { + clockTicks = gbDmaTicks; + gbDmaTicks = 0; + } + + goto gbRedoLoop; + } + + + // Remove the 'if an IE is pending' flag if IE has finished being executed. + if ((IFF & 0x40) && !(IFF & 0x30)) + IFF &= 0x81; + + + + if ((register_IE & register_IF & 0x1f) && (IFF & 0x81) && (!gbInterruptWait)) + { + + if (IFF & 1) + { + // Add 5 ticks for the interrupt execution time + gbDmaTicks += 5; + + if (gbIntBreak == 2) + { + gbDmaTicks--; + gbIntBreak = 0; + } + + + if(register_IF & register_IE & 1) + gbVblank_interrupt(); + else if(register_IF & register_IE & 2) + gbLcd_interrupt(); + else if(register_IF & register_IE & 4) + gbTimer_interrupt(); + else if(register_IF & register_IE & 8) + gbSerial_interrupt(); + else if(register_IF & register_IE & 16) + gbJoypad_interrupt(); + } + + IFF &= ~0x81; + } + + if (IFF & 0x08) + IFF &=~0x79; + + // Used to apply the interrupt's execution time. + if (gbDmaTicks) + { + clockTicks = gbGetNextEvent(gbDmaTicks); + + if (clockTicks<=gbDmaTicks) + gbDmaTicks -= clockTicks; + else + { + clockTicks = gbDmaTicks; + gbDmaTicks = 0; + } + goto gbRedoLoop; + } + + + gbBlackScreen = false; + + if((ticksToStop <= 0)) { + if(!(register_LCDC & 0x80)) { + if(systemReadJoypads()) { + // read joystick + if(gbSgbMode && gbSgbMultiplayer) { + if(gbSgbFourPlayers) { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + gbJoymask[2] = systemReadJoypad(2); + gbJoymask[3] = systemReadJoypad(3); + } else { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + } + } else { + gbJoymask[0] = systemReadJoypad(-1); + } + } + } + return; + } + } +} + +struct EmulatedSystem GBSystem = { + // emuMain + gbEmulate, + // emuReset + gbReset, + // emuCleanUp + gbCleanUp, + // emuReadBattery + gbReadBatteryFile, + // emuWriteBattery + gbWriteBatteryFile, + // emuReadState + gbReadSaveState, + // emuWriteState + gbWriteSaveState, + // emuReadMemState + gbReadMemSaveState, + // emuWriteMemState + gbWriteMemSaveState, + // emuWritePNG + gbWritePNGFile, + // emuWriteBMP + gbWriteBMPFile, + // emuUpdateCPSR + NULL, + // emuHasDebugger + false, + // emuCount +#ifdef FINAL_VERSION + 70000/4, +#else + 1000, +#endif +}; diff --git a/src/dmg/GB.cpp.bak b/src/dmg/GB.cpp.bak new file mode 100644 index 00000000..44584892 --- /dev/null +++ b/src/dmg/GB.cpp.bak @@ -0,0 +1,5449 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005-2006 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include +#include +#include + +#include "../System.h" +#include "../NLS.h" +#include "gb.h" +#include "gbCheats.h" +#include "gbGlobals.h" +#include "gbMemory.h" +#include "gbSGB.h" +#include "gbSound.h" +#include "../Util.h" + +#ifdef __GNUC__ +#define _stricmp strcasecmp +#endif + +extern u8 *pix; +extern bool speedup; + +bool gbUpdateSizes(); +bool inBios = false; + +// debugging +bool memorydebug = false; +char gbBuffer[2048]; + +extern u16 gbLineMix[160]; + +// mappers +void (*mapper)(u16,u8) = NULL; +void (*mapperRAM)(u16,u8) = NULL; +u8 (*mapperReadRAM)(u16) = NULL; +void (*mapperUpdateClock)() = NULL; + +// registers +gbRegister PC; +gbRegister SP; +gbRegister AF; +gbRegister BC; +gbRegister DE; +gbRegister HL; +u16 IFF = 0; +// 0xff04 +u8 register_DIV = 0; +// 0xff05 +u8 register_TIMA = 0; +// 0xff06 +u8 register_TMA = 0; +// 0xff07 +u8 register_TAC = 0; +// 0xff0f +u8 register_IF = 0; +// 0xff40 +u8 register_LCDC = 0; +// 0xff41 +u8 register_STAT = 0; +// 0xff42 +u8 register_SCY = 0; +// 0xff43 +u8 register_SCX = 0; +// 0xff44 +u8 register_LY = 0; +// 0xff45 +u8 register_LYC = 0; +// 0xff46 +u8 register_DMA = 0; +// 0xff4a +u8 register_WY = 0; +// 0xff4b +u8 register_WX = 0; +// 0xff4f +u8 register_VBK = 0; +// 0xff51 +u8 register_HDMA1 = 0; +// 0xff52 +u8 register_HDMA2 = 0; +// 0xff53 +u8 register_HDMA3 = 0; +// 0xff54 +u8 register_HDMA4 = 0; +// 0xff55 +u8 register_HDMA5 = 0; +// 0xff70 +u8 register_SVBK = 0; +// 0xffff +u8 register_IE = 0; + +// ticks definition +int GBDIV_CLOCK_TICKS = 64; +int GBLCD_MODE_0_CLOCK_TICKS = 51; +int GBLCD_MODE_1_CLOCK_TICKS = 1140; +int GBLCD_MODE_2_CLOCK_TICKS = 20; +int GBLCD_MODE_3_CLOCK_TICKS = 43; +int GBLY_INCREMENT_CLOCK_TICKS = 114; +int GBTIMER_MODE_0_CLOCK_TICKS = 256; +int GBTIMER_MODE_1_CLOCK_TICKS = 4; +int GBTIMER_MODE_2_CLOCK_TICKS = 16; +int GBTIMER_MODE_3_CLOCK_TICKS = 64; +int GBSERIAL_CLOCK_TICKS = 128; +int GBSYNCHRONIZE_CLOCK_TICKS = 52920; + +// state variables + +// general +int clockTicks = 0; +bool gbSystemMessage = false; +int gbGBCColorType = 0; +int gbHardware = 0; +int gbRomType = 0; +int gbRemainingClockTicks = 0; +int gbOldClockTicks = 0; +int gbIntBreak = 0; +int gbInterruptLaunched = 0; +u8 gbCheatingDevice = 0; // 1 = GS, 2 = GG +// breakpoint +bool breakpoint = false; +// interrupt +int gbInt48Signal = 0; +int gbInterruptWait = 0; +// serial +int gbSerialOn = 0; +int gbSerialTicks = 0; +int gbSerialBits = 0; +// timer +bool gbTimerOn = false; +int gbTimerTicks = GBTIMER_MODE_0_CLOCK_TICKS; +int gbTimerClockTicks = GBTIMER_MODE_0_CLOCK_TICKS; +int gbTimerMode = 0; +bool gbIncreased = false; +// The internal timer is always active, and it is +// not reset by writing to register_TIMA/TMA, but by +// writing to register_DIV... +int gbInternalTimer = 0x55; +const u8 gbTimerMask [4] = {0xff, 0x3, 0xf, 0x3f}; +const u8 gbTimerBug [8] = {0x80, 0x80, 0x02, 0x02, 0x0, 0xff, 0x0, 0xff}; +bool gbTimerModeChange = false; +bool gbTimerOnChange = false; +// lcd +bool gbScreenOn = true; +int gbLcdMode = 2; +int gbLcdModeDelayed = 2; +int gbLcdTicks = GBLCD_MODE_2_CLOCK_TICKS-1; +int gbLcdTicksDelayed = GBLCD_MODE_2_CLOCK_TICKS; +int gbLcdLYIncrementTicks = 114; +int gbLcdLYIncrementTicksDelayed = 115; +int gbScreenTicks = 0; +u8 gbSCYLine[300]; +u8 gbSCXLine[300]; +u8 gbBgpLine[300]; +u8 gbObp0Line [300]; +u8 gbObp1Line [300]; +u8 gbSpritesTicks [300]; +u8 oldRegister_WY; +bool gbLYChangeHappened = false; +bool gbLCDChangeHappened = false; +int gbLine99Ticks = 1; +int gbRegisterLYLCDCOffOn = 0; +int inUseRegister_WY = 0; + +// Used to keep track of the line that ellapse +// when screen is off +int gbWhiteScreen = 0; +bool gbBlackScreen = false; +int register_LCDCBusy = 0; + +// div +int gbDivTicks = GBDIV_CLOCK_TICKS; +// cgb +int gbVramBank = 0; +int gbWramBank = 1; +//sgb +bool gbSgbResetFlag = false; +// gbHdmaDestination is 0x99d0 on startup (tested on HW) +// but I'm not sure what gbHdmaSource is... +int gbHdmaSource = 0x99d0; +int gbHdmaDestination = 0x99d0; +int gbHdmaBytes = 0x0000; +int gbHdmaOn = 0; +int gbSpeed = 0; +// frame counting +int gbFrameCount = 0; +int gbFrameSkip = 0; +int gbFrameSkipCount = 0; +// timing +u32 gbLastTime = 0; +u32 gbElapsedTime = 0; +u32 gbTimeNow = 0; +int gbSynchronizeTicks = GBSYNCHRONIZE_CLOCK_TICKS; +// emulator features +int gbBattery = 0; +bool gbBatteryError = false; +int gbCaptureNumber = 0; +bool gbCapture = false; +bool gbCapturePrevious = false; +int gbJoymask[4] = { 0, 0, 0, 0 }; + +int gbRomSizes[] = { 0x00008000, // 32K + 0x00010000, // 64K + 0x00020000, // 128K + 0x00040000, // 256K + 0x00080000, // 512K + 0x00100000, // 1024K + 0x00200000, // 2048K + 0x00400000, // 4096K + 0x00800000 // 8192K +}; +int gbRomSizesMasks[] = { 0x00007fff, + 0x0000ffff, + 0x0001ffff, + 0x0003ffff, + 0x0007ffff, + 0x000fffff, + 0x001fffff, + 0x003fffff, + 0x007fffff +}; + +int gbRamSizes[6] = { 0x00000000, // 0K + 0x00002000, // 2K // Changed to 2000 to avoid problems with gbMemoryMap... + 0x00002000, // 8K + 0x00008000, // 32K + 0x00020000, // 128K + 0x00010000 // 64K +}; + +int gbRamSizesMasks[6] = { 0x00000000, + 0x000007ff, + 0x00001fff, + 0x00007fff, + 0x0001ffff, + 0x0000ffff +}; + +int gbCycles[] = { +// 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 3, 2, 2, 1, 1, 2, 1, 5, 2, 2, 2, 1, 1, 2, 1, // 0 + 1, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1, // 1 + 2, 3, 2, 2, 1, 1, 2, 1, 2, 2, 2, 2, 1, 1, 2, 1, // 2 + 2, 3, 2, 2, 3, 3, 3, 1, 2, 2, 2, 2, 1, 1, 2, 1, // 3 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 4 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 5 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 6 + 2, 2, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, // 7 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 8 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 9 + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // a + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // b + 2, 3, 3, 4, 3, 4, 2, 4, 2, 4, 3, 2, 3, 6, 2, 4, // c + 2, 3, 3, 1, 3, 4, 2, 4, 2, 4, 3, 1, 3, 1, 2, 4, // d + 3, 3, 2, 1, 1, 4, 2, 4, 4, 1, 4, 1, 1, 1, 2, 4, // e + 3, 3, 2, 1, 1, 4, 2, 4, 3, 2, 4, 1, 0, 1, 2, 4 // f +}; + +int gbCyclesCB[] = { +// 0 1 2 3 4 5 6 7 8 9 a b c d e f + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 0 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 1 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 2 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 3 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 4 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 5 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 6 + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 7 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 8 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // 9 + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // a + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // b + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // c + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // d + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, // e + 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2 // f +}; + +u16 DAATable[] = { + 0x0080,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, + 0x0800,0x0900,0x1020,0x1120,0x1220,0x1320,0x1420,0x1520, + 0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700, + 0x1800,0x1900,0x2020,0x2120,0x2220,0x2320,0x2420,0x2520, + 0x2000,0x2100,0x2200,0x2300,0x2400,0x2500,0x2600,0x2700, + 0x2800,0x2900,0x3020,0x3120,0x3220,0x3320,0x3420,0x3520, + 0x3000,0x3100,0x3200,0x3300,0x3400,0x3500,0x3600,0x3700, + 0x3800,0x3900,0x4020,0x4120,0x4220,0x4320,0x4420,0x4520, + 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, + 0x4800,0x4900,0x5020,0x5120,0x5220,0x5320,0x5420,0x5520, + 0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x5600,0x5700, + 0x5800,0x5900,0x6020,0x6120,0x6220,0x6320,0x6420,0x6520, + 0x6000,0x6100,0x6200,0x6300,0x6400,0x6500,0x6600,0x6700, + 0x6800,0x6900,0x7020,0x7120,0x7220,0x7320,0x7420,0x7520, + 0x7000,0x7100,0x7200,0x7300,0x7400,0x7500,0x7600,0x7700, + 0x7800,0x7900,0x8020,0x8120,0x8220,0x8320,0x8420,0x8520, + 0x8000,0x8100,0x8200,0x8300,0x8400,0x8500,0x8600,0x8700, + 0x8800,0x8900,0x9020,0x9120,0x9220,0x9320,0x9420,0x9520, + 0x9000,0x9100,0x9200,0x9300,0x9400,0x9500,0x9600,0x9700, + 0x9800,0x9900,0x00B0,0x0130,0x0230,0x0330,0x0430,0x0530, + 0x0090,0x0110,0x0210,0x0310,0x0410,0x0510,0x0610,0x0710, + 0x0810,0x0910,0x1030,0x1130,0x1230,0x1330,0x1430,0x1530, + 0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710, + 0x1810,0x1910,0x2030,0x2130,0x2230,0x2330,0x2430,0x2530, + 0x2010,0x2110,0x2210,0x2310,0x2410,0x2510,0x2610,0x2710, + 0x2810,0x2910,0x3030,0x3130,0x3230,0x3330,0x3430,0x3530, + 0x3010,0x3110,0x3210,0x3310,0x3410,0x3510,0x3610,0x3710, + 0x3810,0x3910,0x4030,0x4130,0x4230,0x4330,0x4430,0x4530, + 0x4010,0x4110,0x4210,0x4310,0x4410,0x4510,0x4610,0x4710, + 0x4810,0x4910,0x5030,0x5130,0x5230,0x5330,0x5430,0x5530, + 0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710, + 0x5810,0x5910,0x6030,0x6130,0x6230,0x6330,0x6430,0x6530, + 0x6010,0x6110,0x6210,0x6310,0x6410,0x6510,0x6610,0x6710, + 0x6810,0x6910,0x7030,0x7130,0x7230,0x7330,0x7430,0x7530, + 0x7010,0x7110,0x7210,0x7310,0x7410,0x7510,0x7610,0x7710, + 0x7810,0x7910,0x8030,0x8130,0x8230,0x8330,0x8430,0x8530, + 0x8010,0x8110,0x8210,0x8310,0x8410,0x8510,0x8610,0x8710, + 0x8810,0x8910,0x9030,0x9130,0x9230,0x9330,0x9430,0x9530, + 0x9010,0x9110,0x9210,0x9310,0x9410,0x9510,0x9610,0x9710, + 0x9810,0x9910,0xA030,0xA130,0xA230,0xA330,0xA430,0xA530, + 0xA010,0xA110,0xA210,0xA310,0xA410,0xA510,0xA610,0xA710, + 0xA810,0xA910,0xB030,0xB130,0xB230,0xB330,0xB430,0xB530, + 0xB010,0xB110,0xB210,0xB310,0xB410,0xB510,0xB610,0xB710, + 0xB810,0xB910,0xC030,0xC130,0xC230,0xC330,0xC430,0xC530, + 0xC010,0xC110,0xC210,0xC310,0xC410,0xC510,0xC610,0xC710, + 0xC810,0xC910,0xD030,0xD130,0xD230,0xD330,0xD430,0xD530, + 0xD010,0xD110,0xD210,0xD310,0xD410,0xD510,0xD610,0xD710, + 0xD810,0xD910,0xE030,0xE130,0xE230,0xE330,0xE430,0xE530, + 0xE010,0xE110,0xE210,0xE310,0xE410,0xE510,0xE610,0xE710, + 0xE810,0xE910,0xF030,0xF130,0xF230,0xF330,0xF430,0xF530, + 0xF010,0xF110,0xF210,0xF310,0xF410,0xF510,0xF610,0xF710, + 0xF810,0xF910,0x00B0,0x0130,0x0230,0x0330,0x0430,0x0530, + 0x0090,0x0110,0x0210,0x0310,0x0410,0x0510,0x0610,0x0710, + 0x0810,0x0910,0x1030,0x1130,0x1230,0x1330,0x1430,0x1530, + 0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710, + 0x1810,0x1910,0x2030,0x2130,0x2230,0x2330,0x2430,0x2530, + 0x2010,0x2110,0x2210,0x2310,0x2410,0x2510,0x2610,0x2710, + 0x2810,0x2910,0x3030,0x3130,0x3230,0x3330,0x3430,0x3530, + 0x3010,0x3110,0x3210,0x3310,0x3410,0x3510,0x3610,0x3710, + 0x3810,0x3910,0x4030,0x4130,0x4230,0x4330,0x4430,0x4530, + 0x4010,0x4110,0x4210,0x4310,0x4410,0x4510,0x4610,0x4710, + 0x4810,0x4910,0x5030,0x5130,0x5230,0x5330,0x5430,0x5530, + 0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710, + 0x5810,0x5910,0x6030,0x6130,0x6230,0x6330,0x6430,0x6530, + 0x0600,0x0700,0x0800,0x0900,0x0A00,0x0B00,0x0C00,0x0D00, + 0x0E00,0x0F00,0x1020,0x1120,0x1220,0x1320,0x1420,0x1520, + 0x1600,0x1700,0x1800,0x1900,0x1A00,0x1B00,0x1C00,0x1D00, + 0x1E00,0x1F00,0x2020,0x2120,0x2220,0x2320,0x2420,0x2520, + 0x2600,0x2700,0x2800,0x2900,0x2A00,0x2B00,0x2C00,0x2D00, + 0x2E00,0x2F00,0x3020,0x3120,0x3220,0x3320,0x3420,0x3520, + 0x3600,0x3700,0x3800,0x3900,0x3A00,0x3B00,0x3C00,0x3D00, + 0x3E00,0x3F00,0x4020,0x4120,0x4220,0x4320,0x4420,0x4520, + 0x4600,0x4700,0x4800,0x4900,0x4A00,0x4B00,0x4C00,0x4D00, + 0x4E00,0x4F00,0x5020,0x5120,0x5220,0x5320,0x5420,0x5520, + 0x5600,0x5700,0x5800,0x5900,0x5A00,0x5B00,0x5C00,0x5D00, + 0x5E00,0x5F00,0x6020,0x6120,0x6220,0x6320,0x6420,0x6520, + 0x6600,0x6700,0x6800,0x6900,0x6A00,0x6B00,0x6C00,0x6D00, + 0x6E00,0x6F00,0x7020,0x7120,0x7220,0x7320,0x7420,0x7520, + 0x7600,0x7700,0x7800,0x7900,0x7A00,0x7B00,0x7C00,0x7D00, + 0x7E00,0x7F00,0x8020,0x8120,0x8220,0x8320,0x8420,0x8520, + 0x8600,0x8700,0x8800,0x8900,0x8A00,0x8B00,0x8C00,0x8D00, + 0x8E00,0x8F00,0x9020,0x9120,0x9220,0x9320,0x9420,0x9520, + 0x9600,0x9700,0x9800,0x9900,0x9A00,0x9B00,0x9C00,0x9D00, + 0x9E00,0x9F00,0x00B0,0x0130,0x0230,0x0330,0x0430,0x0530, + 0x0610,0x0710,0x0810,0x0910,0x0A10,0x0B10,0x0C10,0x0D10, + 0x0E10,0x0F10,0x1030,0x1130,0x1230,0x1330,0x1430,0x1530, + 0x1610,0x1710,0x1810,0x1910,0x1A10,0x1B10,0x1C10,0x1D10, + 0x1E10,0x1F10,0x2030,0x2130,0x2230,0x2330,0x2430,0x2530, + 0x2610,0x2710,0x2810,0x2910,0x2A10,0x2B10,0x2C10,0x2D10, + 0x2E10,0x2F10,0x3030,0x3130,0x3230,0x3330,0x3430,0x3530, + 0x3610,0x3710,0x3810,0x3910,0x3A10,0x3B10,0x3C10,0x3D10, + 0x3E10,0x3F10,0x4030,0x4130,0x4230,0x4330,0x4430,0x4530, + 0x4610,0x4710,0x4810,0x4910,0x4A10,0x4B10,0x4C10,0x4D10, + 0x4E10,0x4F10,0x5030,0x5130,0x5230,0x5330,0x5430,0x5530, + 0x5610,0x5710,0x5810,0x5910,0x5A10,0x5B10,0x5C10,0x5D10, + 0x5E10,0x5F10,0x6030,0x6130,0x6230,0x6330,0x6430,0x6530, + 0x6610,0x6710,0x6810,0x6910,0x6A10,0x6B10,0x6C10,0x6D10, + 0x6E10,0x6F10,0x7030,0x7130,0x7230,0x7330,0x7430,0x7530, + 0x7610,0x7710,0x7810,0x7910,0x7A10,0x7B10,0x7C10,0x7D10, + 0x7E10,0x7F10,0x8030,0x8130,0x8230,0x8330,0x8430,0x8530, + 0x8610,0x8710,0x8810,0x8910,0x8A10,0x8B10,0x8C10,0x8D10, + 0x8E10,0x8F10,0x9030,0x9130,0x9230,0x9330,0x9430,0x9530, + 0x9610,0x9710,0x9810,0x9910,0x9A10,0x9B10,0x9C10,0x9D10, + 0x9E10,0x9F10,0xA030,0xA130,0xA230,0xA330,0xA430,0xA530, + 0xA610,0xA710,0xA810,0xA910,0xAA10,0xAB10,0xAC10,0xAD10, + 0xAE10,0xAF10,0xB030,0xB130,0xB230,0xB330,0xB430,0xB530, + 0xB610,0xB710,0xB810,0xB910,0xBA10,0xBB10,0xBC10,0xBD10, + 0xBE10,0xBF10,0xC030,0xC130,0xC230,0xC330,0xC430,0xC530, + 0xC610,0xC710,0xC810,0xC910,0xCA10,0xCB10,0xCC10,0xCD10, + 0xCE10,0xCF10,0xD030,0xD130,0xD230,0xD330,0xD430,0xD530, + 0xD610,0xD710,0xD810,0xD910,0xDA10,0xDB10,0xDC10,0xDD10, + 0xDE10,0xDF10,0xE030,0xE130,0xE230,0xE330,0xE430,0xE530, + 0xE610,0xE710,0xE810,0xE910,0xEA10,0xEB10,0xEC10,0xED10, + 0xEE10,0xEF10,0xF030,0xF130,0xF230,0xF330,0xF430,0xF530, + 0xF610,0xF710,0xF810,0xF910,0xFA10,0xFB10,0xFC10,0xFD10, + 0xFE10,0xFF10,0x00B0,0x0130,0x0230,0x0330,0x0430,0x0530, + 0x0610,0x0710,0x0810,0x0910,0x0A10,0x0B10,0x0C10,0x0D10, + 0x0E10,0x0F10,0x1030,0x1130,0x1230,0x1330,0x1430,0x1530, + 0x1610,0x1710,0x1810,0x1910,0x1A10,0x1B10,0x1C10,0x1D10, + 0x1E10,0x1F10,0x2030,0x2130,0x2230,0x2330,0x2430,0x2530, + 0x2610,0x2710,0x2810,0x2910,0x2A10,0x2B10,0x2C10,0x2D10, + 0x2E10,0x2F10,0x3030,0x3130,0x3230,0x3330,0x3430,0x3530, + 0x3610,0x3710,0x3810,0x3910,0x3A10,0x3B10,0x3C10,0x3D10, + 0x3E10,0x3F10,0x4030,0x4130,0x4230,0x4330,0x4430,0x4530, + 0x4610,0x4710,0x4810,0x4910,0x4A10,0x4B10,0x4C10,0x4D10, + 0x4E10,0x4F10,0x5030,0x5130,0x5230,0x5330,0x5430,0x5530, + 0x5610,0x5710,0x5810,0x5910,0x5A10,0x5B10,0x5C10,0x5D10, + 0x5E10,0x5F10,0x6030,0x6130,0x6230,0x6330,0x6430,0x6530, + 0x00C0,0x0140,0x0240,0x0340,0x0440,0x0540,0x0640,0x0740, + 0x0840,0x0940,0x0440,0x0540,0x0640,0x0740,0x0840,0x0940, + 0x1040,0x1140,0x1240,0x1340,0x1440,0x1540,0x1640,0x1740, + 0x1840,0x1940,0x1440,0x1540,0x1640,0x1740,0x1840,0x1940, + 0x2040,0x2140,0x2240,0x2340,0x2440,0x2540,0x2640,0x2740, + 0x2840,0x2940,0x2440,0x2540,0x2640,0x2740,0x2840,0x2940, + 0x3040,0x3140,0x3240,0x3340,0x3440,0x3540,0x3640,0x3740, + 0x3840,0x3940,0x3440,0x3540,0x3640,0x3740,0x3840,0x3940, + 0x4040,0x4140,0x4240,0x4340,0x4440,0x4540,0x4640,0x4740, + 0x4840,0x4940,0x4440,0x4540,0x4640,0x4740,0x4840,0x4940, + 0x5040,0x5140,0x5240,0x5340,0x5440,0x5540,0x5640,0x5740, + 0x5840,0x5940,0x5440,0x5540,0x5640,0x5740,0x5840,0x5940, + 0x6040,0x6140,0x6240,0x6340,0x6440,0x6540,0x6640,0x6740, + 0x6840,0x6940,0x6440,0x6540,0x6640,0x6740,0x6840,0x6940, + 0x7040,0x7140,0x7240,0x7340,0x7440,0x7540,0x7640,0x7740, + 0x7840,0x7940,0x7440,0x7540,0x7640,0x7740,0x7840,0x7940, + 0x8040,0x8140,0x8240,0x8340,0x8440,0x8540,0x8640,0x8740, + 0x8840,0x8940,0x8440,0x8540,0x8640,0x8740,0x8840,0x8940, + 0x9040,0x9140,0x9240,0x9340,0x9440,0x9540,0x9640,0x9740, + 0x9840,0x9940,0x3450,0x3550,0x3650,0x3750,0x3850,0x3950, + 0x4050,0x4150,0x4250,0x4350,0x4450,0x4550,0x4650,0x4750, + 0x4850,0x4950,0x4450,0x4550,0x4650,0x4750,0x4850,0x4950, + 0x5050,0x5150,0x5250,0x5350,0x5450,0x5550,0x5650,0x5750, + 0x5850,0x5950,0x5450,0x5550,0x5650,0x5750,0x5850,0x5950, + 0x6050,0x6150,0x6250,0x6350,0x6450,0x6550,0x6650,0x6750, + 0x6850,0x6950,0x6450,0x6550,0x6650,0x6750,0x6850,0x6950, + 0x7050,0x7150,0x7250,0x7350,0x7450,0x7550,0x7650,0x7750, + 0x7850,0x7950,0x7450,0x7550,0x7650,0x7750,0x7850,0x7950, + 0x8050,0x8150,0x8250,0x8350,0x8450,0x8550,0x8650,0x8750, + 0x8850,0x8950,0x8450,0x8550,0x8650,0x8750,0x8850,0x8950, + 0x9050,0x9150,0x9250,0x9350,0x9450,0x9550,0x9650,0x9750, + 0x9850,0x9950,0x9450,0x9550,0x9650,0x9750,0x9850,0x9950, + 0xA050,0xA150,0xA250,0xA350,0xA450,0xA550,0xA650,0xA750, + 0xA850,0xA950,0xA450,0xA550,0xA650,0xA750,0xA850,0xA950, + 0xB050,0xB150,0xB250,0xB350,0xB450,0xB550,0xB650,0xB750, + 0xB850,0xB950,0xB450,0xB550,0xB650,0xB750,0xB850,0xB950, + 0xC050,0xC150,0xC250,0xC350,0xC450,0xC550,0xC650,0xC750, + 0xC850,0xC950,0xC450,0xC550,0xC650,0xC750,0xC850,0xC950, + 0xD050,0xD150,0xD250,0xD350,0xD450,0xD550,0xD650,0xD750, + 0xD850,0xD950,0xD450,0xD550,0xD650,0xD750,0xD850,0xD950, + 0xE050,0xE150,0xE250,0xE350,0xE450,0xE550,0xE650,0xE750, + 0xE850,0xE950,0xE450,0xE550,0xE650,0xE750,0xE850,0xE950, + 0xF050,0xF150,0xF250,0xF350,0xF450,0xF550,0xF650,0xF750, + 0xF850,0xF950,0xF450,0xF550,0xF650,0xF750,0xF850,0xF950, + 0x00D0,0x0150,0x0250,0x0350,0x0450,0x0550,0x0650,0x0750, + 0x0850,0x0950,0x0450,0x0550,0x0650,0x0750,0x0850,0x0950, + 0x1050,0x1150,0x1250,0x1350,0x1450,0x1550,0x1650,0x1750, + 0x1850,0x1950,0x1450,0x1550,0x1650,0x1750,0x1850,0x1950, + 0x2050,0x2150,0x2250,0x2350,0x2450,0x2550,0x2650,0x2750, + 0x2850,0x2950,0x2450,0x2550,0x2650,0x2750,0x2850,0x2950, + 0x3050,0x3150,0x3250,0x3350,0x3450,0x3550,0x3650,0x3750, + 0x3850,0x3950,0x3450,0x3550,0x3650,0x3750,0x3850,0x3950, + 0x4050,0x4150,0x4250,0x4350,0x4450,0x4550,0x4650,0x4750, + 0x4850,0x4950,0x4450,0x4550,0x4650,0x4750,0x4850,0x4950, + 0x5050,0x5150,0x5250,0x5350,0x5450,0x5550,0x5650,0x5750, + 0x5850,0x5950,0x5450,0x5550,0x5650,0x5750,0x5850,0x5950, + 0x6050,0x6150,0x6250,0x6350,0x6450,0x6550,0x6650,0x6750, + 0x6850,0x6950,0x6450,0x6550,0x6650,0x6750,0x6850,0x6950, + 0x7050,0x7150,0x7250,0x7350,0x7450,0x7550,0x7650,0x7750, + 0x7850,0x7950,0x7450,0x7550,0x7650,0x7750,0x7850,0x7950, + 0x8050,0x8150,0x8250,0x8350,0x8450,0x8550,0x8650,0x8750, + 0x8850,0x8950,0x8450,0x8550,0x8650,0x8750,0x8850,0x8950, + 0x9050,0x9150,0x9250,0x9350,0x9450,0x9550,0x9650,0x9750, + 0x9850,0x9950,0x9450,0x9550,0x9650,0x9750,0x9850,0x9950, + 0xFA60,0xFB60,0xFC60,0xFD60,0xFE60,0xFF60,0x00C0,0x0140, + 0x0240,0x0340,0x0440,0x0540,0x0640,0x0740,0x0840,0x0940, + 0x0A60,0x0B60,0x0C60,0x0D60,0x0E60,0x0F60,0x1040,0x1140, + 0x1240,0x1340,0x1440,0x1540,0x1640,0x1740,0x1840,0x1940, + 0x1A60,0x1B60,0x1C60,0x1D60,0x1E60,0x1F60,0x2040,0x2140, + 0x2240,0x2340,0x2440,0x2540,0x2640,0x2740,0x2840,0x2940, + 0x2A60,0x2B60,0x2C60,0x2D60,0x2E60,0x2F60,0x3040,0x3140, + 0x3240,0x3340,0x3440,0x3540,0x3640,0x3740,0x3840,0x3940, + 0x3A60,0x3B60,0x3C60,0x3D60,0x3E60,0x3F60,0x4040,0x4140, + 0x4240,0x4340,0x4440,0x4540,0x4640,0x4740,0x4840,0x4940, + 0x4A60,0x4B60,0x4C60,0x4D60,0x4E60,0x4F60,0x5040,0x5140, + 0x5240,0x5340,0x5440,0x5540,0x5640,0x5740,0x5840,0x5940, + 0x5A60,0x5B60,0x5C60,0x5D60,0x5E60,0x5F60,0x6040,0x6140, + 0x6240,0x6340,0x6440,0x6540,0x6640,0x6740,0x6840,0x6940, + 0x6A60,0x6B60,0x6C60,0x6D60,0x6E60,0x6F60,0x7040,0x7140, + 0x7240,0x7340,0x7440,0x7540,0x7640,0x7740,0x7840,0x7940, + 0x7A60,0x7B60,0x7C60,0x7D60,0x7E60,0x7F60,0x8040,0x8140, + 0x8240,0x8340,0x8440,0x8540,0x8640,0x8740,0x8840,0x8940, + 0x8A60,0x8B60,0x8C60,0x8D60,0x8E60,0x8F60,0x9040,0x9140, + 0x9240,0x9340,0x3450,0x3550,0x3650,0x3750,0x3850,0x3950, + 0x3A70,0x3B70,0x3C70,0x3D70,0x3E70,0x3F70,0x4050,0x4150, + 0x4250,0x4350,0x4450,0x4550,0x4650,0x4750,0x4850,0x4950, + 0x4A70,0x4B70,0x4C70,0x4D70,0x4E70,0x4F70,0x5050,0x5150, + 0x5250,0x5350,0x5450,0x5550,0x5650,0x5750,0x5850,0x5950, + 0x5A70,0x5B70,0x5C70,0x5D70,0x5E70,0x5F70,0x6050,0x6150, + 0x6250,0x6350,0x6450,0x6550,0x6650,0x6750,0x6850,0x6950, + 0x6A70,0x6B70,0x6C70,0x6D70,0x6E70,0x6F70,0x7050,0x7150, + 0x7250,0x7350,0x7450,0x7550,0x7650,0x7750,0x7850,0x7950, + 0x7A70,0x7B70,0x7C70,0x7D70,0x7E70,0x7F70,0x8050,0x8150, + 0x8250,0x8350,0x8450,0x8550,0x8650,0x8750,0x8850,0x8950, + 0x8A70,0x8B70,0x8C70,0x8D70,0x8E70,0x8F70,0x9050,0x9150, + 0x9250,0x9350,0x9450,0x9550,0x9650,0x9750,0x9850,0x9950, + 0x9A70,0x9B70,0x9C70,0x9D70,0x9E70,0x9F70,0xA050,0xA150, + 0xA250,0xA350,0xA450,0xA550,0xA650,0xA750,0xA850,0xA950, + 0xAA70,0xAB70,0xAC70,0xAD70,0xAE70,0xAF70,0xB050,0xB150, + 0xB250,0xB350,0xB450,0xB550,0xB650,0xB750,0xB850,0xB950, + 0xBA70,0xBB70,0xBC70,0xBD70,0xBE70,0xBF70,0xC050,0xC150, + 0xC250,0xC350,0xC450,0xC550,0xC650,0xC750,0xC850,0xC950, + 0xCA70,0xCB70,0xCC70,0xCD70,0xCE70,0xCF70,0xD050,0xD150, + 0xD250,0xD350,0xD450,0xD550,0xD650,0xD750,0xD850,0xD950, + 0xDA70,0xDB70,0xDC70,0xDD70,0xDE70,0xDF70,0xE050,0xE150, + 0xE250,0xE350,0xE450,0xE550,0xE650,0xE750,0xE850,0xE950, + 0xEA70,0xEB70,0xEC70,0xED70,0xEE70,0xEF70,0xF050,0xF150, + 0xF250,0xF350,0xF450,0xF550,0xF650,0xF750,0xF850,0xF950, + 0xFA70,0xFB70,0xFC70,0xFD70,0xFE70,0xFF70,0x00D0,0x0150, + 0x0250,0x0350,0x0450,0x0550,0x0650,0x0750,0x0850,0x0950, + 0x0A70,0x0B70,0x0C70,0x0D70,0x0E70,0x0F70,0x1050,0x1150, + 0x1250,0x1350,0x1450,0x1550,0x1650,0x1750,0x1850,0x1950, + 0x1A70,0x1B70,0x1C70,0x1D70,0x1E70,0x1F70,0x2050,0x2150, + 0x2250,0x2350,0x2450,0x2550,0x2650,0x2750,0x2850,0x2950, + 0x2A70,0x2B70,0x2C70,0x2D70,0x2E70,0x2F70,0x3050,0x3150, + 0x3250,0x3350,0x3450,0x3550,0x3650,0x3750,0x3850,0x3950, + 0x3A70,0x3B70,0x3C70,0x3D70,0x3E70,0x3F70,0x4050,0x4150, + 0x4250,0x4350,0x4450,0x4550,0x4650,0x4750,0x4850,0x4950, + 0x4A70,0x4B70,0x4C70,0x4D70,0x4E70,0x4F70,0x5050,0x5150, + 0x5250,0x5350,0x5450,0x5550,0x5650,0x5750,0x5850,0x5950, + 0x5A70,0x5B70,0x5C70,0x5D70,0x5E70,0x5F70,0x6050,0x6150, + 0x6250,0x6350,0x6450,0x6550,0x6650,0x6750,0x6850,0x6950, + 0x6A70,0x6B70,0x6C70,0x6D70,0x6E70,0x6F70,0x7050,0x7150, + 0x7250,0x7350,0x7450,0x7550,0x7650,0x7750,0x7850,0x7950, + 0x7A70,0x7B70,0x7C70,0x7D70,0x7E70,0x7F70,0x8050,0x8150, + 0x8250,0x8350,0x8450,0x8550,0x8650,0x8750,0x8850,0x8950, + 0x8A70,0x8B70,0x8C70,0x8D70,0x8E70,0x8F70,0x9050,0x9150, + 0x9250,0x9350,0x9450,0x9550,0x9650,0x9750,0x9850,0x9950, +}; + +u8 ZeroTable[256] = { + 0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0 +}; + +#define GBSAVE_GAME_VERSION_1 1 +#define GBSAVE_GAME_VERSION_2 2 +#define GBSAVE_GAME_VERSION_3 3 +#define GBSAVE_GAME_VERSION_4 4 +#define GBSAVE_GAME_VERSION_5 5 +#define GBSAVE_GAME_VERSION_6 6 +#define GBSAVE_GAME_VERSION_7 7 +#define GBSAVE_GAME_VERSION_8 8 +#define GBSAVE_GAME_VERSION_9 9 +#define GBSAVE_GAME_VERSION_10 10 +#define GBSAVE_GAME_VERSION_11 11 +#define GBSAVE_GAME_VERSION GBSAVE_GAME_VERSION_11 + +int inline gbGetValue(int min,int max,int v) +{ + return (int)(min+(float)(max-min)*(2.0*(v/31.0)-(v/31.0)*(v/31.0))); +} + +void gbGenFilter() +{ + for (int r=0;r<32;r++) { + for (int g=0;g<32;g++) { + for (int b=0;b<32;b++) { + int nr=gbGetValue(gbGetValue(4,14,g), + gbGetValue(24,29,g),r)-4; + int ng=gbGetValue(gbGetValue(4+gbGetValue(0,5,r), + 14+gbGetValue(0,3,r),b), + gbGetValue(24+gbGetValue(0,3,r), + 29+gbGetValue(0,1,r),b),g)-4; + int nb=gbGetValue(gbGetValue(4+gbGetValue(0,5,r), + 14+gbGetValue(0,3,r),g), + gbGetValue(24+gbGetValue(0,3,r), + 29+gbGetValue(0,1,r),g),b)-4; + gbColorFilter[(b<<10)|(g<<5)|r]=(nb<<10)|(ng<<5)|nr; + } + } + } +} + +bool gbIsGameboyRom(char * file) +{ + if(strlen(file) > 4) { + char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".gb") == 0) + return true; + if(_stricmp(p, ".gbc") == 0) + return true; + if(_stricmp(p, ".cgb") == 0) + return true; + if(_stricmp(p, ".sgb") == 0) + return true; + } + } + + return false; +} + +void gbCopyMemory(u16 d, u16 s, int count) +{ + while(count) { + gbMemoryMap[d>>12][d & 0x0fff] = gbMemoryMap[s>>12][s & 0x0fff]; + s++; + d++; + count--; + } +} + +void gbDoHdma() +{ + + gbCopyMemory((gbHdmaDestination & 0x1ff0) | 0x8000, + gbHdmaSource & 0xfff0, + 0x10); + + gbHdmaDestination += 0x10; + if (gbHdmaDestination == 0xa000) + gbHdmaDestination = 0x8000; + + gbHdmaSource += 0x10; + if (gbHdmaSource == 0x8000) + gbHdmaSource = 0xa000; + + register_HDMA2 = gbHdmaSource & 0xff; + register_HDMA1 = gbHdmaSource>>8; + + register_HDMA4 = gbHdmaDestination & 0xff; + register_HDMA3 = gbHdmaDestination>>8; + + gbHdmaBytes -= 0x10; + gbMemory[0xff55] = --register_HDMA5; + if(register_HDMA5 == 0xff) + gbHdmaOn = 0; + +// We need to add the dmaClockticks for HDMA ! + if(gbSpeed) + gbDmaTicks = 17; + else + gbDmaTicks = 9; + + if (IFF & 0x80) + gbDmaTicks++; + +} + +// fix for Harley and Lego Racers +void gbCompareLYToLYC() +{ + if(register_LCDC & 0x80) { + if(register_LY == register_LYC) { + + // mark that we have a match + register_STAT |= 4; + + // check if we need an interrupt + if (register_STAT & 0x40) + { + // send LCD interrupt only if no interrupt 48h signal... + if (!gbInt48Signal) + { + register_IF |=2; + } + gbInt48Signal |= 8; + } + } + else // no match + { + register_STAT &= 0xfb; + gbInt48Signal &=~8; + } + } +} + +void gbWriteMemory(register u16 address, register u8 value) +{ + + if(address < 0x8000) { +#ifndef FINAL_VERSION + if(memorydebug && (address>0x3fff || address < 0x2000)) { + log("Memory register write %04x=%02x PC=%04x\n", + address, + value, + PC.W); + } + +#endif + if(mapper) + (*mapper)(address, value); + return; + + } + + if(address < 0xa000) { + // No access to Vram during mode 3 + // (used to emulate the gfx differences between GB & GBC-GBA/SP in Stunt Racer) + if ((gbLcdModeDelayed !=3) || + // This part is used to emulate a small difference between hardwares + // (check 8-in-1's arrow on GBA/GBC to verify it) + ((register_LY == 0) && ((gbHardware & 0xa) && (gbScreenOn==false) && + (register_LCDC & 0x80)) && + (gbLcdLYIncrementTicksDelayed ==(GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS)))) + gbMemoryMap[address>>12][address&0x0fff] = value; + return; + } + + // Used for the mirroring of 0xC000 in 0xE000 + if ((address >= 0xe000) && (address < 0xfe00)) + address &= ~0x2000; + + if(address < 0xc000) { +#ifndef FINAL_VERSION + if(memorydebug) { + log("Memory register write %04x=%02x PC=%04x\n", + address, + value, + PC.W); + } +#endif + + // Is that a correct fix ??? (it used to be 'if (mapper)')... + if(mapperRAM) + (*mapperRAM)(address, value); + return; + } + + + if(address < 0xfe00) { + gbMemoryMap[address>>12][address & 0x0fff] = value; + return; + } + + // OAM not accessible during mode 2 & 3. + if(address < 0xfea0) + { + if (((gbHardware & 0xa) && ((gbLcdMode | gbLcdModeDelayed) &2)) || + ((gbHardware & 5) && (((gbLcdModeDelayed == 2) && + (gbLcdTicksDelayed<=GBLCD_MODE_2_CLOCK_TICKS)) || + (gbLcdModeDelayed == 3)))) + return; + else + { + gbMemory[address] = value; + return; + } + } + + + + if((address > 0xfea0) && (address < 0xff00)){ // GBC allows reading/writing to that area + gbMemory[address] = value; + return; + } + + switch(address & 0x00ff) { + + case 0x00: { + gbMemory[0xff00] = ((gbMemory[0xff00] & 0xcf) | + (value & 0x30) | 0xc0); + if(gbSgbMode) { + gbSgbDoBitTransfer(value); + } + return; + } + + case 0x01: { + gbMemory[0xff01] = value; + return; + } + + // serial control + case 0x02: { + gbSerialOn = (value & 0x80); + gbMemory[0xff02] = value; + if(gbSerialOn) { + gbSerialTicks = GBSERIAL_CLOCK_TICKS; +#ifdef LINK_EMULATION + if(linkConnected) { + if(value & 1) { + linkSendByte(0x100|gbMemory[0xFF01]); + Sleep(5); + } + } +#endif + } + + gbSerialBits = 0; + return; + } + + case 0x04: { + // DIV register resets on any write + // (not totally perfect, but better than nothing) + gbMemory[0xff04] = register_DIV = 0; + gbDivTicks = GBDIV_CLOCK_TICKS; + // Another weird timer 'bug' : + // Writing to DIV register resets the internal timer, + // and can also increase TIMA/trigger an interrupt + // in some cases... + if (gbTimerOn && !(gbInternalTimer & (gbTimerClockTicks>>1))) + { + gbMemory[0xff05] = ++register_TIMA; + if(register_TIMA == 0) { + // timer overflow! + + // reload timer modulo + gbMemory[0xff05] = register_TIMA = register_TMA; + + // flag interrupt + gbMemory[0xff0f] = register_IF |= 4; + } + } + gbInternalTimer = 0xff; + return; + } + case 0x05: + gbMemory[0xff05] = register_TIMA = value; + return; + + case 0x06: + gbMemory[0xff06] = register_TMA = value; + return; + + // TIMER control + case 0x07: { + + gbTimerModeChange = (((value & 3) != (register_TAC&3)) && (value & register_TAC & 4)) ? true : false; + gbTimerOnChange = (((value ^ register_TAC) & 4) == 4) ? true : false; + + gbTimerOn = (value & 4) ? true : false; + + if (gbTimerOnChange || gbTimerModeChange) + { + gbTimerMode = value & 3; + + switch(gbTimerMode) { + case 0: + gbTimerClockTicks = GBTIMER_MODE_0_CLOCK_TICKS; + break; + case 1: + gbTimerClockTicks = GBTIMER_MODE_1_CLOCK_TICKS; + break; + case 2: + gbTimerClockTicks = GBTIMER_MODE_2_CLOCK_TICKS; + break; + case 3: + gbTimerClockTicks = GBTIMER_MODE_3_CLOCK_TICKS; + break; + } + } + + + // This looks weird, but this emulates a bug in which register_TIMA + // is increased when writing to register_TAC + // (This fixes Korodice's long-delay between menus bug). + + if (gbTimerOnChange || gbTimerModeChange) + { + bool temp = false; + + if ((gbTimerOn && !gbTimerModeChange) && (gbTimerMode & 2) && + !(gbInternalTimer & 0x80) && (gbInternalTimer & (gbTimerClockTicks>>1)) && + !(gbInternalTimer & (gbTimerClockTicks>>5))) + temp = true; + else if ((!gbTimerOn && !gbTimerModeChange && gbTimerOnChange ) && ((gbTimerTicks-1) < (gbTimerClockTicks>>1))) + temp = true; + else if (gbTimerOn && gbTimerModeChange && !gbTimerOnChange) + { + switch(gbTimerMode & 3) + { + case 0x00: + temp = false; + break; + case 0x01: + if (((gbInternalTimer & 0x82) == 2) && (gbTimerTicks>(clockTicks+1))) + temp = true; + break; + case 0x02: + if (((gbInternalTimer & 0x88) == 0x8) && (gbTimerTicks>(clockTicks+1))) + temp = true; + break; + case 0x03: + if (((gbInternalTimer & 0xA0) == 0x20) && (gbTimerTicks>(clockTicks+1))) + temp = true; + break; + } + } + + if (temp) + { + gbMemory[0xff05] = ++register_TIMA; + if((register_TIMA & 0xff) == 0) { + // timer overflow! + + // reload timer modulo + gbMemory[0xff05] = register_TIMA = register_TMA; + + // flag interrupt + gbMemory[0xff0f] = register_IF |= 4; + } + } + } + gbMemory[0xff07] = register_TAC = value; + return; + } + + case 0x0f: { + gbMemory[0xff0f] = register_IF = value; + //gbMemory[0xff0f] = register_IE |= value; + return; + } + + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1a: + case 0x1b: + case 0x1c: + case 0x1d: + case 0x1e: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: { + if (gbMemory[NR52] & 0x80) { + SOUND_EVENT(address,value); + return; + } + } + + case 0x26: { + SOUND_EVENT(address,value); + return; + } + + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3a: + case 0x3b: + case 0x3c: + case 0x3d: + case 0x3e: + case 0x3f: { + SOUND_EVENT(address,value); + //gbMemory[address] = value; + return; + } + + case 0x40: { + int lcdChange = (register_LCDC & 0x80) ^ (value & 0x80); + + // don't draw the window if it was not enabled and not being drawn before + if(!(register_LCDC & 0x20) && (value & 0x20) && gbWindowLine == -1 && + register_LY > register_WY) + gbWindowLine = 146; + // 007 fix : don't draw the first window's 1st line if it's enable 'too late' + // (ie. if register_LY == register_WY when enabling it) + // and move it to the next line + else if (!(register_LCDC & 0x20) && (value & 0x20) && (register_LY == register_WY)) + gbWindowLine = -2; + + + gbMemory[0xff40] = register_LCDC = value; + + + if(lcdChange) { + if((value & 0x80) && (!register_LCDCBusy)) { + + // if (!gbWhiteScreen && !gbSgbMask) + + // systemDrawScreen(); + + + + gbRegisterLYLCDCOffOn = (register_LY + 144) % 154; + + gbLcdTicks = GBLCD_MODE_2_CLOCK_TICKS - (gbSpeed ? 2 : 1); + gbLcdTicksDelayed = GBLCD_MODE_2_CLOCK_TICKS - (gbSpeed ? 1 : 0); + gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS - (gbSpeed ? 2 : 1); + gbLcdLYIncrementTicksDelayed = GBLY_INCREMENT_CLOCK_TICKS - (gbSpeed ? 1 : 0); + gbLcdMode = 2; + gbLcdModeDelayed = 2; + gbMemory[0xff41] = register_STAT = (register_STAT & 0xfc) | 2; + gbMemory[0xff44] = register_LY = 0x00; + gbInt48Signal = 0; + gbLYChangeHappened = false; + gbLCDChangeHappened = false; + gbWindowLine = 146; + oldRegister_WY = 146; + + // Fix for Namco Gallery Vol.2 + // (along with updating register_LCDC at the start of 'case 0x40') : + if(register_STAT & 0x20) + { + // send LCD interrupt only if no interrupt 48h signal... + if (!gbInt48Signal) + { + gbMemory[0xff0f] = register_IF |= 2; + } + gbInt48Signal |= 4; + } + gbCompareLYToLYC(); + + } else { + + register_LCDCBusy = clockTicks+6; + + //used to update the screen with white lines when it's off. + //(it looks strange, but it's pretty accurate) + + gbWhiteScreen = 0; + + gbScreenTicks = ((150-register_LY)*GBLY_INCREMENT_CLOCK_TICKS + + (49<<(gbSpeed ? 1 : 0))); + + // disable the screen rendering + gbScreenOn = false; + gbLcdTicks = 0; + gbLcdMode = 0; + gbLcdModeDelayed = 0; + gbMemory[0xff41] = register_STAT &= 0xfc; + gbInt48Signal = 0; + } + } + return; + } + + // STAT + case 0x41: { + //register_STAT = (register_STAT & 0x87) | + // (value & 0x7c); + gbMemory[0xff41] = register_STAT = (value & 0xf8) | (register_STAT & 0x07); // fix ? + // GB bug from Devrs FAQ + // Corrected : it happens if Lcd Mode<2, but also if LY == LYC whatever + // Lcd Mode is, and if !gbInt48Signal in all cases. The screen being off + // doesn't matter (the bug will still happen). + // That fixes 'Satoru Nakajima - F-1 Hero' crash bug. + + if((gbHardware & 5) && (((!gbInt48Signal) && (gbLcdMode<2) && (register_LCDC & 0x80)) || + (register_LY == register_LYC))) + { + gbMemory[0xff0f] = register_IF |=2; + } + + gbInt48Signal &= ((register_STAT>>3) & 0xF); + + if((register_LCDC & 0x80)) { + if ((register_STAT & 0x08) && (gbLcdMode == 0)) + { + if (!gbInt48Signal) + { + gbMemory[0xff0f] = register_IF |=2; + } + gbInt48Signal |= 1; + } + if ((register_STAT & 0x10) && (gbLcdMode == 1)) + { + if (!gbInt48Signal) + { + gbMemory[0xff0f] = register_IF |=2; + } + gbInt48Signal |= 2; + } + if ((register_STAT & 0x20) && (gbLcdMode == 2)) + { + if (!gbInt48Signal) + { + gbMemory[0xff0f] = register_IF |=2; + } + gbInt48Signal |= 4; + } + gbCompareLYToLYC(); + + gbMemory[0xff0f] = register_IF; + gbMemory[0xff41] = register_STAT; + + } + return; + } + + // SCY + case 0x42: { + int temp = -1; + + if ((gbLcdMode == 3) || (gbLcdModeDelayed == 3)) + temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - + gbLcdLYIncrementTicks); + + if (temp >=0) + { + for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) + if (temp < 300) + gbSCYLine[i] = value; + } + + else + memset(gbSCYLine, value, sizeof(gbSCYLine)); + + gbMemory[0xff42] = register_SCY = value; + return; + } + + // SCX + case 0x43: { + int temp = -1; + + if (gbLcdModeDelayed == 3) + temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - + gbLcdLYIncrementTicksDelayed); + + if (temp >=0) + { + for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) + if (temp < 300) + gbSCXLine[i] = value; + } + + else + memset(gbSCXLine, value, sizeof(gbSCXLine)); + + gbMemory[0xff43] = register_SCX = value; + return; + } + + // LY + case 0x44: { + // read only + return; + } + + // LYC + case 0x45: { + if (register_LYC != value) + { + gbMemory[0xff45] = register_LYC = value; + if(register_LCDC & 0x80) { + gbCompareLYToLYC(); + } + } + return; + } + + // DMA! + case 0x46: { + int source = value * 0x0100; + + gbCopyMemory(0xfe00, + source, + 0xa0); + gbMemory[0xff46] = register_DMA = value; + return; + } + + // BGP + case 0x47: { + + int temp = -1; + + gbMemory[0xff47] = value; + + if (gbLcdModeDelayed == 3) + temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - + gbLcdLYIncrementTicksDelayed); + + if (temp >=0) + { + for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) + if (temp < 300) + gbBgpLine[i] = value; + } + else + memset(gbBgpLine,value,sizeof(gbBgpLine)); + + gbBgp[0] = value & 0x03; + gbBgp[1] = (value & 0x0c)>>2; + gbBgp[2] = (value & 0x30)>>4; + gbBgp[3] = (value & 0xc0)>>6; + break; + } + + // OBP0 + case 0x48: { + int temp = -1; + + gbMemory[0xff48] = value; + + if (gbLcdModeDelayed == 3) + temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - + gbLcdLYIncrementTicksDelayed); + + if (temp >=0) + { + for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) + if (temp < 300) + gbObp0Line[i] = value; + } + else + memset(gbObp0Line,value,sizeof(gbObp0Line)); + + gbObp0[0] = value & 0x03; + gbObp0[1] = (value & 0x0c)>>2; + gbObp0[2] = (value & 0x30)>>4; + gbObp0[3] = (value & 0xc0)>>6; + break; + } + + // OBP1 + case 0x49: { + int temp = -1; + + gbMemory[0xff49] = value; + + if (gbLcdModeDelayed == 3) + temp = ((GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS) - + gbLcdLYIncrementTicksDelayed); + + if (temp >=0) + { + for (int i=temp<<(gbSpeed ? 1 : 2);i<300;i++) + if (temp < 300) + gbObp1Line[i] = value; + } + else + memset(gbObp1Line,value,sizeof(gbObp1Line)); + + gbObp1[0] = value & 0x03; + gbObp1[1] = (value & 0x0c)>>2; + gbObp1[2] = (value & 0x30)>>4; + gbObp1[3] = (value & 0xc0)>>6; + break; + } + + // WY + case 0x4a: + gbMemory[0xff4a] = register_WY = value; + if ((register_LY <= register_WY) && ((gbWindowLine < 0) || (gbWindowLine>=144))) + { + gbWindowLine = -1; + oldRegister_WY = register_WY; + } + return; + + // WX + case 0x4b: + gbMemory[0xff4b] = register_WX = value; + return; + + // KEY1 + case 0x4d: { + if(gbCgbMode) { + gbMemory[0xff4d] = (gbMemory[0xff4d] & 0x80) | (value & 1) | 0x7e; + return; + } + } + break; + + // VBK + case 0x4f: { + if(gbCgbMode) { + value = value & 1; + if(value == gbVramBank) + return; + + int vramAddress = value * 0x2000; + gbMemoryMap[0x08] = &gbVram[vramAddress]; + gbMemoryMap[0x09] = &gbVram[vramAddress + 0x1000]; + + gbVramBank = value; + register_VBK = value; + } + return; + } + break; + + // BOOTROM disable register (also gbCgbMode enabler if value & 0x10 ?) + case 0x50 : + { + if (useBios && inBios && !skipBios && (value & 1)) + { + gbMemoryMap[0x00] = &gbRom[0x0000]; + memcpy ((u8 *)(gbRom+0x100), (u8 *)(gbMemory + 0x100), 0xF00); + inBios = false; + } + } + + // HDMA1 + case 0x51: { + if(gbCgbMode) { + if(value > 0x7f && value < 0xa0) + value = 0; + + gbHdmaSource = (value << 8) | (gbHdmaSource & 0xf0); + + register_HDMA1 = value; + return; + } + } + break; + + // HDMA2 + case 0x52: { + if(gbCgbMode) { + value = value & 0xf0; + + gbHdmaSource = (gbHdmaSource & 0xff00) | (value); + + register_HDMA2 = value; + return; + } + } + break; + + // HDMA3 + case 0x53: { + if(gbCgbMode) { + value = value & 0x1f; + gbHdmaDestination = (value << 8) | (gbHdmaDestination & 0xf0); + gbHdmaDestination |= 0x8000; + register_HDMA3 = value; + return; + } + } + break; + + // HDMA4 + case 0x54: { + if(gbCgbMode) { + value = value & 0xf0; + gbHdmaDestination = (gbHdmaDestination & 0x1f00) | value; + gbHdmaDestination |= 0x8000; + register_HDMA4 = value; + return; + } + } + break; + + // HDMA5 + case 0x55: { + + if(gbCgbMode) { + gbHdmaBytes = 16 + (value & 0x7f) * 16; + if(gbHdmaOn) { + if(value & 0x80) { + gbMemory[0xff55] = register_HDMA5 = (value & 0x7f); + } else { + register_HDMA5 = 0xff; + gbHdmaOn = 0; + } + } else { + if(value & 0x80) { + gbHdmaOn = 1; + gbMemory[0xff55] = register_HDMA5 = value & 0x7f; + if(gbLcdModeDelayed == 0) + gbDoHdma(); + } else { + // we need to take the time it takes to complete the transfer into + // account... according to GB DEV FAQs, the setup time is the same + // for single and double speed, but the actual transfer takes the + // same time + if(gbSpeed) + gbDmaTicks = 2+16 * ((value & 0x7f) +1); + else + gbDmaTicks = 1+8 * ((value & 0x7f)+1); + + gbCopyMemory((gbHdmaDestination & 0x1ff0) | 0x8000, + gbHdmaSource & 0xfff0, + gbHdmaBytes); + gbHdmaDestination += gbHdmaBytes; + gbHdmaSource += gbHdmaBytes; + + gbMemory[0xff51] = register_HDMA1 = 0xff;// = (gbHdmaSource >> 8) & 0xff; + gbMemory[0xff52] = register_HDMA2 = 0xff;// = gbHdmaSource & 0xf0; + gbMemory[0xff53] = register_HDMA3 = 0xff;// = ((gbHdmaDestination - 0x8000) >> 8) & 0x1f; + gbMemory[0xff54] = register_HDMA4 = 0xff;// = gbHdmaDestination & 0xf0; + gbMemory[0xff55] = register_HDMA5 = 0xff; + } + } + return; + } + } + break; + + // BCPS + case 0x68: { + if(gbCgbMode) { + int paletteIndex = (value & 0x3f) >> 1; + int paletteHiLo = (value & 0x01); + + gbMemory[0xff68] = value; + + gbMemory[0xff69] = (paletteHiLo ? + (gbPalette[paletteIndex] >> 8) : + (gbPalette[paletteIndex] & 0x00ff)); + return; + } + } + break; + + // BCPD + case 0x69: { + if(gbCgbMode) { + int v = gbMemory[0xff68]; + int paletteIndex = (v & 0x3f) >> 1; + int paletteHiLo = (v & 0x01); + + // No access to gbPalette during mode 3 (Color Panel Demo) + if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || + (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || + ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || + ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) + { + gbMemory[0xff69] = value; + gbPalette[paletteIndex] = (paletteHiLo ? + ((value << 8) | (gbPalette[paletteIndex] & 0xff)) : + ((gbPalette[paletteIndex] & 0xff00) | (value))) & 0x7fff; + } + + + if(gbMemory[0xff68] & 0x80) { + int index = ((gbMemory[0xff68] & 0x3f) + 1) & 0x3f; + + gbMemory[0xff68] = (gbMemory[0xff68] & 0x80) | index; + gbMemory[0xff69] = (index & 1 ? + (gbPalette[index>>1] >> 8) : + (gbPalette[index>>1] & 0x00ff)); + } + return; + } + } + break; + + // OCPS + case 0x6a: { + if(gbCgbMode) { + int paletteIndex = (value & 0x3f) >> 1; + int paletteHiLo = (value & 0x01); + + paletteIndex += 32; + + gbMemory[0xff6a] = value; + + gbMemory[0xff6b] = (paletteHiLo ? + (gbPalette[paletteIndex] >> 8) : + (gbPalette[paletteIndex] & 0x00ff)); + return; + } + } + break; + + // OCPD + case 0x6b: { + + if(gbCgbMode) { + int v = gbMemory[0xff6a]; + int paletteIndex = (v & 0x3f) >> 1; + int paletteHiLo = (v & 0x01); + + paletteIndex += 32; + + // No access to gbPalette during mode 3 (Color Panel Demo) + if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || + (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || + ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || + ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) + { + gbMemory[0xff6b] = value; + gbPalette[paletteIndex] = (paletteHiLo ? + ((value << 8) | (gbPalette[paletteIndex] & 0xff)) : + ((gbPalette[paletteIndex] & 0xff00) | (value))) & 0x7fff; + } + + if(gbMemory[0xff6a] & 0x80) { + int index = ((gbMemory[0xff6a] & 0x3f) + 1) & 0x3f; + + gbMemory[0xff6a] = (gbMemory[0xff6a] & 0x80) | index; + + gbMemory[0xff6b] = (index & 1 ? + (gbPalette[(index>>1) + 32] >> 8) : + (gbPalette[(index>>1) + 32] & 0x00ff)); + } + return; + } + } + break; + + case 0x6c: { + gbMemory[0xff6c] = 0xfe | value; + return; + } + + + // SVBK + case 0x70: { + if(gbCgbMode) { + value = value & 7; + + int bank = value; + if(value == 0) + bank = 1; + + if(bank == gbWramBank) + return; + + int wramAddress = bank * 0x1000; + gbMemoryMap[0x0d] = &gbWram[wramAddress]; + + gbWramBank = bank; + gbMemory[0xff70] = register_SVBK = value; + return; + } + } + + case 0x75:{ + gbMemory[0xff75] = 0x8f | value; + return; + } + + case 0xff: { + gbMemory[0xffff] = register_IE = value; + return; + } + } + + if(address < 0xff80) + { + gbMemory[address] = value; + return; + } + + gbMemory[address] = value; +} + +u8 gbReadOpcode(register u16 address) +{ + if(gbCheatMap[address]) + return gbCheatRead(address); + + if(address < 0x8000) + return gbMemoryMap[address>>12][address&0x0fff]; + + if(address < 0xa000) + { + // A lot of 'ugly' checks... But only way to emulate this particular behaviour... + if (((gbHardware & 0xa) && ((gbLcdModeDelayed !=3) || ((register_LY == 0) && + (gbScreenOn==false) && (register_LCDC & 0x80)) && + (gbLcdLYIncrementTicksDelayed ==(GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS)))) || + ((gbHardware & 0x5) && (gbLcdModeDelayed !=3) && + ((gbLcdMode !=3) || ((register_LY == 0) && ((gbScreenOn==false) && + (register_LCDC & 0x80)) && + (gbLcdLYIncrementTicks ==(GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS)))))) + return gbMemoryMap[address>>12][address&0x0fff]; + return 0xff; + } + + // Used for the mirroring of 0xC000 in 0xE000 + if ((address >= 0xe000) && (address < 0xfe00)) + address &= ~0x2000; + + switch(address & 0xf000) { + case 0x0a: + case 0x0b: + if(mapperReadRAM) + return mapperReadRAM(address); + break; + case 0x0f: + if(address > 0xff00) { + switch(address & 0x00ff) { + case 0x02: + return (gbMemory[0xff02]); + case 0x03: + return (0xff); + case 0x04: + return register_DIV; + case 0x05: + return register_TIMA; + case 0x06: + return register_TMA; + case 0x07: + return (0xf8 | register_TAC); + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + return (0xff); + case 0x0f: + return (0xe0 | gbMemory[0xff0f]); + case 0x40: + return register_LCDC; + case 0x41: + // This is a GB/C only bug (ie. not GBA/SP). + if ((gbHardware & 7) && (gbLcdMode == 2) && (gbLcdModeDelayed == 1) && (!gbSpeed)) + return (0x80 | gbMemory[0xff41] & 0xFC); + else + return (0x80 | gbMemory[0xff41]); + case 0x42: + return register_SCY; + case 0x43: + return register_SCX; + case 0x44: + if (((gbHardware & 7) && ((gbLcdMode == 1) && (gbLcdTicks == 0x71))) || + (!(register_LCDC && 0x80))) + return 0; + else + return register_LY; + case 0x45: + return register_LYC; + case 0x46: + return register_DMA; + case 0x4a: + return register_WY; + case 0x4b: + return register_WX; + case 0x4c: + return 0xff; + case 0x4f: + return (0xfe | register_VBK); + case 0x51: + return register_HDMA1; + case 0x52: + return register_HDMA2; + case 0x53: + return register_HDMA3; + case 0x54: + return register_HDMA4; + case 0x55: + return register_HDMA5; + case 0x68: + case 0x6a: + if (gbCgbMode) + return (0x40 | gbMemory[address]); + else + return 0xc0; + case 0x69: + case 0x6b: + if (gbCgbMode) + { + // No access to gbPalette during mode 3 (Color Panel Demo) + if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || + (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || + ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || + ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) + return (gbMemory[address]); + else + return 0xff; + } + else + return 0xff; + case 0x70: + if (gbCgbMode) + return (0xf8 | register_SVBK); + else + return 0xff; + case 0xff: + return register_IE; + } + } + // OAM not accessible during mode 2 & 3. + if(((address >= 0xfe00) && (address<0xfea0)) && + ((gbLcdMode | gbLcdModeDelayed) &2)) + return 0xff; + break; + } + + if ((address >= 0xfea0) && (address < 0xff00)) + { + if (gbHardware & 1) + return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0x00 : 0xff ); + else if (gbHardware & 2) + return gbMemoryMap[address>>12][address & 0x0fff]; + else if (gbHardware & 4) + return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0xff : 0x00 ); + else if (gbHardware & 8) + return ((address & 0xf0) |((address & 0xf0)>>4)); + } + + return gbMemoryMap[address>>12][address & 0x0fff]; +} + +u8 gbReadMemory(register u16 address) +{ + if(gbCheatMap[address]) + return gbCheatRead(address); + + + if(address < 0x8000) + return gbMemoryMap[address>>12][address&0x0fff]; + + if(address < 0xa000) + { + // A lot of 'ugly' checks... But only way to emulate this particular behaviour... + if (((gbHardware & 0xa) && ((gbLcdModeDelayed !=3) || ((register_LY == 0) && + (gbScreenOn==false) && (register_LCDC & 0x80)) && + (gbLcdLYIncrementTicksDelayed ==(GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS)))) || + ((gbHardware & 0x5) && (gbLcdModeDelayed !=3) && + ((gbLcdMode !=3) || ((register_LY == 0) && ((gbScreenOn==false) && + (register_LCDC & 0x80)) && + (gbLcdLYIncrementTicks ==(GBLY_INCREMENT_CLOCK_TICKS-GBLCD_MODE_2_CLOCK_TICKS)))))) + return gbMemoryMap[address>>12][address&0x0fff]; + return 0xff; + } + + if ((address >= 0xe000) && (address < 0xfe00)) + address &= ~0x2000; + + if(address < 0xc000) { +#ifndef FINAL_VERSION + if(memorydebug) { + log("Memory register read %04x PC=%04x\n", + address, + PC.W); + } +#endif + + // for the 2kb ram limit (fixes crash in shawu's story + // but now its sram test fails, as the it expects 8kb and not 2kb... + // So use the 'genericflashcard' option to fix it). + if (address<=(0xa000+gbRamSizeMask)) + { + if(mapperReadRAM) + return mapperReadRAM(address); + return gbMemoryMap[address>>12][address & 0x0fff]; + } + return 0xff; + } + + if(address >= 0xff00) { + if ( address >= 0xFF10 && address <= 0xFF3F ) + return gbSoundRead( address ); + + switch(address & 0x00ff) { + case 0x00: + { + if(gbSgbMode) { + gbSgbReadingController |= 4; + gbSgbResetPacketState(); + } + + int b = gbMemory[0xff00]; + + if((b & 0x30) == 0x20) { + b &= 0xf0; + + int joy = 0; + if(gbSgbMode && gbSgbMultiplayer) { + switch(gbSgbNextController) { + case 0x0f: + joy = 0; + break; + case 0x0e: + joy = 1; + break; + case 0x0d: + joy = 2; + break; + case 0x0c: + joy = 3; + break; + default: + joy = 0; + } + } + int joystate = gbJoymask[joy]; + if(!(joystate & 128)) + b |= 0x08; + if(!(joystate & 64)) + b |= 0x04; + if(!(joystate & 32)) + b |= 0x02; + if(!(joystate & 16)) + b |= 0x01; + + gbMemory[0xff00] = b; + } else if((b & 0x30) == 0x10) { + b &= 0xf0; + + int joy = 0; + if(gbSgbMode && gbSgbMultiplayer) { + switch(gbSgbNextController) { + case 0x0f: + joy = 0; + break; + case 0x0e: + joy = 1; + break; + case 0x0d: + joy = 2; + break; + case 0x0c: + joy = 3; + break; + default: + joy = 0; + } + } + int joystate = gbJoymask[joy]; + if(!(joystate & 8)) + b |= 0x08; + if(!(joystate & 4)) + b |= 0x04; + if(!(joystate & 2)) + b |= 0x02; + if(!(joystate & 1)) + b |= 0x01; + + gbMemory[0xff00] = b; + } else { + if(gbSgbMode && gbSgbMultiplayer) { + gbMemory[0xff00] = 0xf0 | gbSgbNextController; + } else { + gbMemory[0xff00] = 0xff; + } + } + } + return gbMemory[0xff00]; + break; + case 0x01: + return gbMemory[0xff01]; + case 0x02: + return (gbMemory[0xff02]); + case 0x04: + return register_DIV; + case 0x05: + return register_TIMA; + case 0x06: + return register_TMA; + case 0x07: + return (0xf8 | register_TAC); + case 0x0f: + return (0xe0 | gbMemory[0xff0f]); + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + if ((gbMemory[NR30] & 0x80) && (gbMemory[NR34] & 0x80)) + return 0xFF; + else + return gbMemoryMap[address>>12][address & 0x0fff]; + case 0x40: + return register_LCDC; + case 0x41: + // This is a GB/C only bug (ie. not GBA/SP). + if ((gbHardware & 7) && (gbLcdMode == 2) && (gbLcdModeDelayed == 1) && (!gbSpeed)) + return (0x80 | gbMemory[0xff41] & 0xFC); + else + return (0x80 | gbMemory[0xff41]); + case 0x42: + return register_SCY; + case 0x43: + return register_SCX; + case 0x44: + if (((gbHardware & 7) && ((gbLcdMode == 1) && (gbLcdTicks == 0x71))) || + (!(register_LCDC && 0x80))) + return (0); + else + return register_LY; + case 0x45: + return register_LYC; + case 0x46: + return register_DMA; + case 0x4a: + return register_WY; + case 0x4b: + return register_WX; + case 0x4f: + return (0xfe | register_VBK); + case 0x51: + return register_HDMA1; + case 0x52: + return register_HDMA2; + case 0x53: + return register_HDMA3; + case 0x54: + return register_HDMA4; + case 0x55: + return register_HDMA5; + case 0x68: + case 0x6a: + if (gbCgbMode) + return (0x40 | gbMemory[address]); + else + return 0xc0; + case 0x69: + case 0x6b: + if (gbCgbMode) + { + // No access to gbPalette during mode 3 (Color Panel Demo) + if (((gbLcdModeDelayed != 3) && (!((gbLcdMode == 0) && (gbLcdTicks>=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-1)))) && (!gbSpeed)) || + (gbSpeed && ((gbLcdMode == 1) || (gbLcdMode == 2) || + ((gbLcdMode == 3) && (gbLcdTicks>(GBLCD_MODE_3_CLOCK_TICKS-2))) || + ((gbLcdMode == 0) && (gbLcdTicks<=(GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]-2)))))) + return (gbMemory[address]); + else + return 0xff; + } + else + return 0xff; + case 0x70: + if (gbCgbMode) + return (0xf8 | register_SVBK); + else + return 0xff; + case 0xff: + return register_IE; + } + } + // OAM not accessible during mode 2 & 3. + if(((address >= 0xfe00) && (address<0xfea0)) && + (((gbLcdMode | gbLcdModeDelayed) &2) && + (!(gbSpeed && (gbHardware & 0x2) && !(gbLcdModeDelayed & 2) && (gbLcdMode == 2))) || + (gbSpeed && (gbHardware & 0x2) && (gbLcdModeDelayed == 0) && (gbLcdTicksDelayed == (GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]))))) + return 0xff; + + if ((address >= 0xfea0) && (address < 0xff00)) + { + if (gbHardware & 1) + return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0x00 : 0xff ); + else if (gbHardware & 2) + return gbMemoryMap[address>>12][address & 0x0fff]; + else if (gbHardware & 4) + return ((((address + ((address >> 4) - 0xfea)) >> 2) & 1) ? 0xff : 0x00 ); + else if (gbHardware & 8) + return ((address & 0xf0) |((address & 0xf0)>>4)); + } + + return gbMemoryMap[address>>12][address & 0x0fff]; +} + +void gbVblank_interrupt() +{ + gbCheatWrite(false); // Emulates GS codes. + gbMemory[0xff0f] = register_IF &= 0xfe; + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + PC.W = 0x40; +} + +void gbLcd_interrupt() +{ + gbMemory[0xff0f] = register_IF &= 0xfd; + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + PC.W = 0x48; +} + +void gbTimer_interrupt() +{ + gbMemory[0xff0f] = register_IF &= 0xfb; + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + PC.W = 0x50; +} + +void gbSerial_interrupt() +{ + gbMemory[0xff0f] = register_IF &= 0xf7; + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + PC.W = 0x58; +} + +void gbJoypad_interrupt() +{ + gbMemory[0xff0f] = register_IF &= 0xef; + gbWriteMemory(--SP.W, PC.B.B1); + gbWriteMemory(--SP.W, PC.B.B0); + PC.W = 0x60; +} + +void gbSpeedSwitch() +{ + gbBlackScreen = true; + if(gbSpeed == 0) { + gbSpeed = 1; + GBLCD_MODE_0_CLOCK_TICKS = 51 * 2; + GBLCD_MODE_1_CLOCK_TICKS = 1140 * 2; + GBLCD_MODE_2_CLOCK_TICKS = 20 * 2; + GBLCD_MODE_3_CLOCK_TICKS = 43 * 2; + GBLY_INCREMENT_CLOCK_TICKS = 114 * 2; + GBDIV_CLOCK_TICKS = 64; + GBTIMER_MODE_0_CLOCK_TICKS = 256; + GBTIMER_MODE_1_CLOCK_TICKS = 4; + GBTIMER_MODE_2_CLOCK_TICKS = 16; + GBTIMER_MODE_3_CLOCK_TICKS = 64; + GBSERIAL_CLOCK_TICKS = 128 * 2; + gbLcdTicks *= 2; + gbLcdTicksDelayed *=2; + gbLcdTicksDelayed--; + gbLcdLYIncrementTicks *= 2; + gbLcdLYIncrementTicksDelayed *= 2; + gbLcdLYIncrementTicksDelayed--; + gbSerialTicks *= 2; + //SOUND_CLOCK_TICKS = soundQuality * 24 * 2; + //soundTicks *= 2; + gbLine99Ticks = 3; + } else { + gbSpeed = 0; + GBLCD_MODE_0_CLOCK_TICKS = 51; + GBLCD_MODE_1_CLOCK_TICKS = 1140; + GBLCD_MODE_2_CLOCK_TICKS = 20; + GBLCD_MODE_3_CLOCK_TICKS = 43; + GBLY_INCREMENT_CLOCK_TICKS = 114; + GBDIV_CLOCK_TICKS = 64; + GBTIMER_MODE_0_CLOCK_TICKS = 256; + GBTIMER_MODE_1_CLOCK_TICKS = 4; + GBTIMER_MODE_2_CLOCK_TICKS = 16; + GBTIMER_MODE_3_CLOCK_TICKS = 64; + GBSERIAL_CLOCK_TICKS = 128; + gbLcdTicks >>= 1; + gbLcdTicksDelayed++; + gbLcdTicksDelayed >>=1; + gbLcdLYIncrementTicks >>= 1; + gbLcdLYIncrementTicksDelayed++; + gbLcdLYIncrementTicksDelayed >>= 1; + gbSerialTicks /= 2; + //SOUND_CLOCK_TICKS = soundQuality * 24; + //soundTicks /= 2; + gbLine99Ticks = 1; + if (gbHardware & 8) + gbLine99Ticks++; + } + gbDmaTicks += (134)*GBLY_INCREMENT_CLOCK_TICKS + (37<<(gbSpeed ? 1 : 0)); +} + +bool CPUIsGBBios(const char * file) +{ + if(strlen(file) > 4) { + const char * p = strrchr(file,'.'); + + if(p != NULL) { + if(_stricmp(p, ".gb") == 0) + return true; + if(_stricmp(p, ".bin") == 0) + return true; + if(_stricmp(p, ".bios") == 0) + return true; + if(_stricmp(p, ".rom") == 0) + return true; + } + } + + return false; +} + +void gbCPUInit(const char *biosFileName, bool useBiosFile) +{ + useBios = false; + if (useBiosFile) + { + int size = 0x100; + if(utilLoad(biosFileName, + CPUIsGBBios, + bios, + size)) { + if(size == 0x100) + useBios = true; + else + systemMessage(MSG_INVALID_BIOS_FILE_SIZE, N_("Invalid BOOTROM file size")); + } + } +} + +void gbGetHardwareType() +{ + gbCgbMode = 0; + gbSgbMode = 0; + if(gbRom[0x143] & 0x80) { + if((gbEmulatorType == 0) || + gbEmulatorType == 1 || + gbEmulatorType == 4) { + gbCgbMode = 1; + } + } + + if((gbCgbMode == 0 ) && (gbRom[0x146] == 0x03)) { + if(gbEmulatorType == 0 || + gbEmulatorType == 2 || + gbEmulatorType == 5) + gbSgbMode = 1; + } + + gbHardware = 1; // GB + if (((gbCgbMode == 1) && (gbEmulatorType == 0)) || (gbEmulatorType == 1)) + gbHardware = 2; // GBC + else if (((gbSgbMode == 1) && (gbEmulatorType == 0)) || (gbEmulatorType == 2) || (gbEmulatorType == 5)) + gbHardware = 4; // SGB(2) + else if (gbEmulatorType == 4) + gbHardware = 8; // GBA + + gbGBCColorType = 0; + if (gbHardware & 8) // If GBA is selected, choose the GBA default settings. + gbGBCColorType = 2; // (0 = GBC, 1 = GBA, 2 = GBASP) +} + +void gbReset() +{ + gbGetHardwareType(); + + oldRegister_WY = 146; + gbInterruptLaunched = 0; + + if(gbCgbMode == 1) { + if (gbVram == NULL) + gbVram = (u8 *)malloc(0x4000); + if (gbWram == NULL) + gbWram = (u8 *)malloc(0x8000); + memset(gbVram,0,0x4000); + memset(gbPalette,0, 2*128); + } + else + { + if(gbVram != NULL) { + free(gbVram); + gbVram = NULL; + } + if(gbWram != NULL) { + free(gbWram); + gbWram = NULL; + } + } + + gbLYChangeHappened = false; + gbLCDChangeHappened = false; + gbBlackScreen = false; + gbInterruptWait = 0; + gbDmaTicks = 0; + clockTicks = 0; + + if(gbSpeed) { + gbSpeedSwitch(); + gbMemory[0xff4d] = 0; + } + + // clean Wram + // This kinda emulates the startup state of Wram on GB/C (not very accurate, + // but way closer to the reality than filling it with 00es or FFes). + // On GBA/GBASP, it's kinda filled with random data. + // In all cases, most of the 2nd bank is filled with 00s. + // The starting data are important for some 'buggy' games, like Buster Brothers or + // Karamuchou ha Oosawagi!. + if (gbMemory != NULL) + { + memset(gbMemory,0xff, 65536); + for (int temp = 0xC000; temp < 0xE000; temp++) + if ((temp & 0x8) ^((temp & 0x800)>>8)) + { + if ((gbHardware & 0x02) && (gbGBCColorType == 0)) + gbMemory[temp] = 0x0; + else + gbMemory[temp] = 0x0f; + } + + else + gbMemory[temp] = 0xff; + } + + + + // clean LineBuffer + if (gbLineBuffer != NULL) + memset(gbLineBuffer, 0, sizeof(gbLineBuffer)); + // clean Pix + if (pix != NULL) + memset(pix, 0, sizeof(pix)); + // clean Vram + if (gbVram != NULL) + memset(gbVram, 0, 0x4000); + // clean Wram 2 + // This kinda emulates the startup state of Wram on GBC (not very accurate, + // but way closer to the reality than filling it with 00es or FFes). + // On GBA/GBASP, it's kinda filled with random data. + // In all cases, most of the 2nd bank is filled with 00s. + // The starting data are important for some 'buggy' games, like Buster Brothers or + // Karamuchou ha Oosawagi! + if (gbWram != NULL) + { + for (int i = 0; i<8; i++) + if (i != 2) + memcpy ((u16 *)(gbWram+i*0x1000), (u16 *)(gbMemory+0xC000), 0x1000); + } + + memset(gbSCYLine,0,sizeof(gbSCYLine)); + memset(gbSCXLine,0,sizeof(gbSCXLine)); + memset(gbBgpLine,0xfc,sizeof(gbBgpLine)); + if (gbHardware & 5) + { + memset(gbObp0Line,0xff,sizeof(gbObp0Line)); + memset(gbObp1Line,0xff,sizeof(gbObp1Line)); + } + else + { + memset(gbObp0Line,0x0,sizeof(gbObp0Line)); + memset(gbObp1Line,0x0,sizeof(gbObp1Line)); + } + memset(gbSpritesTicks,0x0,sizeof(gbSpritesTicks)); + + SP.W = 0xfffe; + AF.W = 0x01b0; + BC.W = 0x0013; + DE.W = 0x00d8; + HL.W = 0x014d; + PC.W = 0x0100; + IFF = 0; + gbInt48Signal = 0; + + register_TIMA = 0; + register_TMA = 0; + register_TAC = 0; + gbMemory[0xff0f] = register_IF = 1; + gbMemory[0xff40] = register_LCDC = 0x91; + gbMemory[0xff47] = 0xfc; + + if (gbCgbMode) + gbMemory[0xff4d] = 0x7e; + else + gbMemory[0xff4d] = 0xff; + + if (!gbCgbMode) + gbMemory[0xff70] = gbMemory[0xff74] = 0xff; + + if (gbCgbMode) + gbMemory[0xff56] = 0x3e; + else + gbMemory[0xff56] = 0xff; + + register_SCY = 0; + register_SCX = 0; + register_LYC = 0; + register_DMA = 0xff; + register_WY = 0; + register_WX = 0; + register_VBK = 0; + register_HDMA1 = 0xff; + register_HDMA2 = 0xff; + register_HDMA3 = 0xff; + register_HDMA4 = 0xff; + register_HDMA5 = 0xff; + register_SVBK = 0; + register_IE = 0; + + if (gbCgbMode) + gbMemory[0xff02] = 0x7c; + else + gbMemory[0xff02] = 0x7e; + + gbMemory[0xff03] = 0xff; + int i; + for (i = 0x8; i<0xf; i++) + gbMemory[0xff00+i] = 0xff; + + gbMemory[0xff13] = 0xff; + gbMemory[0xff15] = 0xff; + gbMemory[0xff18] = 0xff; + gbMemory[0xff1d] = 0xff; + gbMemory[0xff1f] = 0xff; + + for (i = 0x27; i<0x30; i++) + gbMemory[0xff00+i] = 0xff; + + gbMemory[0xff4c] = 0xff; + gbMemory[0xff4e] = 0xff; + gbMemory[0xff50] = 0xff; + + for (i = 0x57; i<0x68; i++) + gbMemory[0xff00+i] = 0xff; + + for (i = 0x5d; i<0x70; i++) + gbMemory[0xff00+i] = 0xff; + + gbMemory[0xff71] = 0xff; + + for (i = 0x78; i<0x80; i++) + gbMemory[0xff00+i] = 0xff; + + if (gbHardware & 0xa) + { + + if (gbHardware & 2) + { + AF.W = 0x1180; + BC.W = 0x0000; + } + else + { + AF.W = 0x1100; + BC.W = 0x0100; // GBA/SP have B = 0x01 (which means GBC & GBA/SP bootrom are different !) + } + + gbMemory[0xff26] = 0xf1; + if (gbCgbMode) + { + + gbMemory[0xff31] = 0xff; + gbMemory[0xff33] = 0xff; + gbMemory[0xff35] = 0xff; + gbMemory[0xff37] = 0xff; + gbMemory[0xff39] = 0xff; + gbMemory[0xff3b] = 0xff; + gbMemory[0xff3d] = 0xff; + + gbMemory[0xff44] = register_LY = 0x90; + gbDivTicks = 0x19 + ((gbHardware & 2) >> 1); + gbInternalTimer = 0x58 + ((gbHardware & 2) >> 1); + gbLcdTicks = GBLCD_MODE_1_CLOCK_TICKS - + (register_LY-0x8F)*GBLY_INCREMENT_CLOCK_TICKS + 72 + ((gbHardware & 2) >> 1); + gbLcdLYIncrementTicks = 72 + ((gbHardware & 2) >> 1); + gbMemory[0xff04] = register_DIV = 0x1E; + } + else + { + gbMemory[0xff44] = register_LY = 0x94; + gbDivTicks = 0x22 + ((gbHardware & 2) >> 1); + gbInternalTimer = 0x61 + ((gbHardware & 2) >> 1); + gbLcdTicks = GBLCD_MODE_1_CLOCK_TICKS - + (register_LY-0x8F)*GBLY_INCREMENT_CLOCK_TICKS + 25 + ((gbHardware & 2) >> 1); + gbLcdLYIncrementTicks = 25 + ((gbHardware & 2) >> 1); + gbMemory[0xff04] = register_DIV = 0x26; + } + + + DE.W = 0xff56; + HL.W = 0x000d; + + register_HDMA5 = 0xff; + gbMemory[0xff68] = 0xc0; + gbMemory[0xff6a] = 0xc0; + + + gbMemory[0xff41] = register_STAT = 0x81; + gbLcdMode = 1; + } + else + { + if (gbHardware & 4) + { + if(gbEmulatorType == 5) + AF.W = 0xffb0; + else + AF.W = 0x01b0; + BC.W = 0x0013; + DE.W = 0x00d8; + HL.W = 0x014d; + } + gbDivTicks = 14; + gbInternalTimer = gbDivTicks--; + gbMemory[0xff04] = register_DIV = 0xAB; + gbMemory[0xff41] = register_STAT = 0x85; + gbMemory[0xff44] = register_LY = 0x00; + gbLcdTicks = 15; + gbLcdLYIncrementTicks = 114+gbLcdTicks; + gbLcdMode = 1; + + // used for the handling of the gb Boot Rom + if ((gbHardware & 5) && (bios != NULL) && useBios && !skipBios) + { + memcpy ((u8 *)(gbMemory), (u8 *)(gbRom), 0x1000); + memcpy ((u8 *)(gbMemory), (u8 *)(bios), 0x100); + gbWhiteScreen = 0; + + gbInternalTimer = 0x3e; + gbDivTicks = 0x3f; + gbMemory[0xff04] = register_DIV = 0x00; + PC.W = 0x0000; + register_LCDC = 0x11; + gbScreenOn = false; + gbLcdTicks = 0; + gbLcdMode = 0; + gbLcdModeDelayed = 0; + gbMemory[0xff41] = register_STAT &= 0xfc; + gbInt48Signal = 0; + gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS; + } + } + + gbLine99Ticks = 1; + if (gbHardware & 8) + gbLine99Ticks++; + + gbLcdModeDelayed = gbLcdMode; + gbLcdTicksDelayed = gbLcdTicks+1; + gbLcdLYIncrementTicksDelayed = gbLcdLYIncrementTicks+1; + + + gbTimerModeChange = false; + gbTimerOnChange = false; + gbTimerOn = false; + + if(gbCgbMode) { + for (int i = 0; i<0x20; i++) + gbPalette[i] = 0x7fff; + + // This is just to show that the starting values of the OBJ palettes are different + // between the 3 consoles, and that they 'kinda' stay the same at each reset + // (they can slightly change, somehow (randomly?)). + // You can check the effects of gbGBCColorType on the "Vila Caldan Color" gbc demo. + // Note that you could also check the Div register to check on which system the game + // is running (GB,GBC and GBA(SP) have different startup values). + // Unfortunatly, I don't have any SGB system, so I can't get their starting values. + + if (gbGBCColorType == 0) // GBC Hardware + { + gbPalette[0x20] = 0x0600; + gbPalette[0x21] = 0xfdf3; + gbPalette[0x22] = 0x041c; + gbPalette[0x23] = 0xf5db; + gbPalette[0x24] = 0x4419; + gbPalette[0x25] = 0x57ea; + gbPalette[0x26] = 0x2808; + gbPalette[0x27] = 0x9b75; + gbPalette[0x28] = 0x129b; + gbPalette[0x29] = 0xfce0; + gbPalette[0x2a] = 0x22da; + gbPalette[0x2b] = 0x4ac5; + gbPalette[0x2c] = 0x2d71; + gbPalette[0x2d] = 0xf0c2; + gbPalette[0x2e] = 0x5137; + gbPalette[0x2f] = 0x2d41; + gbPalette[0x30] = 0x6b2d; + gbPalette[0x31] = 0x2215; + gbPalette[0x32] = 0xbe0a; + gbPalette[0x33] = 0xc053; + gbPalette[0x34] = 0xfe5f; + gbPalette[0x35] = 0xe000; + gbPalette[0x36] = 0xbe10; + gbPalette[0x37] = 0x914d; + gbPalette[0x38] = 0x7f91; + gbPalette[0x39] = 0x02b5; + gbPalette[0x3a] = 0x77ac; + gbPalette[0x3b] = 0x14e5; + gbPalette[0x3c] = 0xcf89; + gbPalette[0x3d] = 0xa03d; + gbPalette[0x3e] = 0xfd50; + gbPalette[0x3f] = 0x91ff; + } + else if (gbGBCColorType == 1) // GBA Hardware + { + gbPalette[0x20] = 0xbe00; + gbPalette[0x21] = 0xfdfd; + gbPalette[0x22] = 0xbd69; + gbPalette[0x23] = 0x7baf; + gbPalette[0x24] = 0xf5ff; + gbPalette[0x25] = 0x3f8f; + gbPalette[0x26] = 0xcee5; + gbPalette[0x27] = 0x5bf7; + gbPalette[0x28] = 0xb35b; + gbPalette[0x29] = 0xef97; + gbPalette[0x2a] = 0xef9f; + gbPalette[0x2b] = 0x97f7; + gbPalette[0x2c] = 0x82bf; + gbPalette[0x2d] = 0x9f3d; + gbPalette[0x2e] = 0xddde; + gbPalette[0x2f] = 0xbad5; + gbPalette[0x30] = 0x3cba; + gbPalette[0x31] = 0xdfd7; + gbPalette[0x32] = 0xedea; + gbPalette[0x33] = 0xfeda; + gbPalette[0x34] = 0xf7f9; + gbPalette[0x35] = 0xfdee; + gbPalette[0x36] = 0x6d2f; + gbPalette[0x37] = 0xf0e6; + gbPalette[0x38] = 0xf7f0; + gbPalette[0x39] = 0xf296; + gbPalette[0x3a] = 0x3bf1; + gbPalette[0x3b] = 0xe211; + gbPalette[0x3c] = 0x69ba; + gbPalette[0x3d] = 0x3d0d; + gbPalette[0x3e] = 0xdfd3; + gbPalette[0x3f] = 0xa6ba; + } + else if (gbGBCColorType == 2) // GBASP Hardware + { + gbPalette[0x20] = 0x9c00; + gbPalette[0x21] = 0x6340; + gbPalette[0x22] = 0x10c6; + gbPalette[0x23] = 0xdb97; + gbPalette[0x24] = 0x7622; + gbPalette[0x25] = 0x3e57; + gbPalette[0x26] = 0x2e12; + gbPalette[0x27] = 0x95c3; + gbPalette[0x28] = 0x1095; + gbPalette[0x29] = 0x488c; + gbPalette[0x2a] = 0x8241; + gbPalette[0x2b] = 0xde8c; + gbPalette[0x2c] = 0xfabc; + gbPalette[0x2d] = 0x0e81; + gbPalette[0x2e] = 0x7675; + gbPalette[0x2f] = 0xfdec; + gbPalette[0x30] = 0xddfd; + gbPalette[0x31] = 0x5995; + gbPalette[0x32] = 0x066a; + gbPalette[0x33] = 0xed1e; + gbPalette[0x34] = 0x1e84; + gbPalette[0x35] = 0x1d14; + gbPalette[0x36] = 0x11c3; + gbPalette[0x37] = 0x2749; + gbPalette[0x38] = 0xa727; + gbPalette[0x39] = 0x6266; + gbPalette[0x3a] = 0xe27b; + gbPalette[0x3b] = 0xe3fc; + gbPalette[0x3c] = 0x1f76; + gbPalette[0x3d] = 0xf158; + gbPalette[0x3e] = 0x468e; + gbPalette[0x3f] = 0xa540; + } + } else { + if(gbSgbMode) { + for(int i = 0; i < 8; i++) + gbPalette[i] = systemGbPalette[gbPaletteOption*8+i]; + + } + for(int i = 0; i < 8; i++) + gbPalette[i] = systemGbPalette[gbPaletteOption*8+i]; + } + + GBTIMER_MODE_0_CLOCK_TICKS = 256; + GBTIMER_MODE_1_CLOCK_TICKS = 4; + GBTIMER_MODE_2_CLOCK_TICKS = 16; + GBTIMER_MODE_3_CLOCK_TICKS = 64; + + GBLY_INCREMENT_CLOCK_TICKS = 114; + gbTimerTicks = GBTIMER_MODE_0_CLOCK_TICKS; + gbTimerClockTicks = GBTIMER_MODE_0_CLOCK_TICKS; + gbSerialTicks = 0; + gbSerialBits = 0; + gbSerialOn = 0; + gbWindowLine = -1; + gbTimerOn = false; + gbTimerMode = 0; + gbSpeed = 0; + gbJoymask[0] = gbJoymask[1] = gbJoymask[2] = gbJoymask[3] = 0; + + if(gbCgbMode) { + gbSpeed = 0; + gbHdmaOn = 0; + gbHdmaSource = 0x99d0; + gbHdmaDestination = 0x99d0; + gbVramBank = 0; + gbWramBank = 1; + + } + + // used to clean the borders + if (gbSgbMode) + { + gbSgbResetFlag = true; + gbSgbReset(); + if (gbBorderOn) + gbSgbRenderBorder(); + gbSgbResetFlag = false; + } + + for(i = 0; i < 4; i++) + gbBgp[i] = gbObp0[i] = gbObp1[i] = i; + + memset(&gbDataMBC1,0, sizeof(gbDataMBC1)); + gbDataMBC1.mapperROMBank = 1; + + gbDataMBC2.mapperRAMEnable = 0; + gbDataMBC2.mapperROMBank = 1; + + memset(&gbDataMBC3,0, 6 * sizeof(int)); + gbDataMBC3.mapperROMBank = 1; + + memset(&gbDataMBC5, 0, sizeof(gbDataMBC5)); + gbDataMBC5.mapperROMBank = 1; + + memset(&gbDataHuC1, 0, sizeof(gbDataHuC1)); + gbDataHuC1.mapperROMBank = 1; + + memset(&gbDataHuC3, 0, sizeof(gbDataHuC3)); + gbDataHuC3.mapperROMBank = 1; + + memset(&gbDataTAMA5,0, 26*sizeof(int)); + gbDataTAMA5.mapperROMBank = 1; + + memset(&gbDataMMM01,0, sizeof(gbDataMMM01)); + gbDataMMM01.mapperROMBank = 1; + + if (useBios && !skipBios && (gbHardware & 5)) + { + gbMemoryMap[0x00] = &gbMemory[0x0000]; + inBios = true; + } + else + { + gbMemoryMap[0x00] = &gbRom[0x0000]; + inBios = false; + } + + gbMemoryMap[0x01] = &gbRom[0x1000]; + gbMemoryMap[0x02] = &gbRom[0x2000]; + gbMemoryMap[0x03] = &gbRom[0x3000]; + gbMemoryMap[0x04] = &gbRom[0x4000]; + gbMemoryMap[0x05] = &gbRom[0x5000]; + gbMemoryMap[0x06] = &gbRom[0x6000]; + gbMemoryMap[0x07] = &gbRom[0x7000]; + if(gbCgbMode) { + gbMemoryMap[0x08] = &gbVram[0x0000]; + gbMemoryMap[0x09] = &gbVram[0x1000]; + gbMemoryMap[0x0a] = &gbMemory[0xa000]; + gbMemoryMap[0x0b] = &gbMemory[0xb000]; + gbMemoryMap[0x0c] = &gbMemory[0xc000]; + gbMemoryMap[0x0d] = &gbWram[0x1000]; + gbMemoryMap[0x0e] = &gbMemory[0xe000]; + gbMemoryMap[0x0f] = &gbMemory[0xf000]; + } else { + gbMemoryMap[0x08] = &gbMemory[0x8000]; + gbMemoryMap[0x09] = &gbMemory[0x9000]; + gbMemoryMap[0x0a] = &gbMemory[0xa000]; + gbMemoryMap[0x0b] = &gbMemory[0xb000]; + gbMemoryMap[0x0c] = &gbMemory[0xc000]; + gbMemoryMap[0x0d] = &gbMemory[0xd000]; + gbMemoryMap[0x0e] = &gbMemory[0xe000]; + gbMemoryMap[0x0f] = &gbMemory[0xf000]; + } + + if(gbRam) { + gbMemoryMap[0x0a] = &gbRam[0x0000]; + gbMemoryMap[0x0b] = &gbRam[0x1000]; + } + + gbSoundReset(); + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + gbLastTime = systemGetClock(); + gbFrameCount = 0; + + gbScreenOn = true; + gbSystemMessage = false; + + gbCheatWrite(true); // Emulates GS codes. + +} + +void gbWriteSaveMBC1(const char * name) +{ + if (gbRam) + { + FILE *gzFile = fopen(name,"wb"); + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(gbRam, + 1, + (gbRamSizeMask+1), + gzFile); + + fclose(gzFile); + } +} + +void gbWriteSaveMBC2(const char * name) +{ + if (gbRam) + { + FILE *file = fopen(name, "wb"); + + if(file == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(&gbMemory[0xa000], + 1, + 256, + file); + + fclose(file); + } +} + +void gbWriteSaveMBC3(const char * name, bool extendedSave) +{ + if (gbRam || extendedSave) + { + FILE *gzFile = fopen(name,"wb"); + if (gbRam) + { + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(gbRam, + 1, + (gbRamSizeMask+1), + gzFile); + } + + if(extendedSave) + fwrite(&gbDataMBC3.mapperSeconds, + 1, + 10*sizeof(int) + sizeof(time_t), + gzFile); + + fclose(gzFile); + } +} + +void gbWriteSaveMBC5(const char * name) +{ + if (gbRam) + { + FILE *gzFile = fopen(name,"wb"); + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(gbRam, + 1, + (gbRamSizeMask+1), + gzFile); + + fclose(gzFile); + } +} + +void gbWriteSaveMBC7(const char * name) +{ + if (gbRam) + { + FILE *file = fopen(name, "wb"); + + if(file == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(&gbMemory[0xa000], + 1, + 256, + file); + + fclose(file); + } +} + +void gbWriteSaveTAMA5(const char * name, bool extendedSave) +{ + FILE *gzFile = fopen(name,"wb"); + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + if (gbRam) + fwrite(gbRam, + 1, + (gbRamSizeMask+1), + gzFile); + + fwrite(gbTAMA5ram, + 1, + (gbTAMA5ramSize), + gzFile); + + if(extendedSave) + fwrite(&gbDataTAMA5.mapperSeconds, + 1, + 14*sizeof(int) + sizeof(time_t), + gzFile); + + fclose(gzFile); +} + +void gbWriteSaveMMM01(const char * name) +{ + if (gbRam) + { + FILE *gzFile = fopen(name,"wb"); + + if(gzFile == NULL) { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), name); + return; + } + + fwrite(gbRam, + 1, + (gbRamSizeMask+1), + gzFile); + + fclose(gzFile); + } +} + + +bool gbReadSaveMBC1(const char * name) +{ + if (gbRam) + { + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) { + return false; + } + + int read = gzread(gzFile, + gbRam, + (gbRamSizeMask+1)); + + if(read != (gbRamSizeMask+1)) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + // Also checks if the battery file it bigger than gbRamSizeMask+1 ! + u8 data[1]; + data[0] = 0; + + read = gzread(gzFile, + data, + 1); + if(read >0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + gzclose(gzFile); + return true; + } + else + return false; +} + + +bool gbReadSaveMBC2(const char * name) +{ + if (gbRam) + { + FILE *file = fopen(name, "rb"); + + if(file == NULL) { + return false; + } + + size_t read = fread(&gbMemory[0xa000], + 1, + 256, + file); + + if(read != 256) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + fclose(file); + gbBatteryError = true; + return false; + } + + // Also checks if the battery file it bigger than gbRamSizeMask+1 ! + u8 data[1]; + data[0] = 0; + + read = fread(&data[0], + 1, + 1, + file); + if(read > 0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + fclose(file); + gbBatteryError = true; + return false; + } + + fclose(file); + return true; + } + else + return false; +} + +bool gbReadSaveMBC3(const char * name) +{ + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) { + return false; + } + + int read = 0; + + if (gbRam) + read = gzread(gzFile, + gbRam, + (gbRamSizeMask+1)); + else + read = (gbRamSizeMask+1); + + + bool res = true; + + if(read != (gbRamSizeMask+1)) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gbBatteryError = true; + res = false; + } else if ((gbRomType == 0xf) || (gbRomType == 0x10)){ + read = gzread(gzFile, + &gbDataMBC3.mapperSeconds, + sizeof(int)*10 + sizeof(time_t)); + + if(read != (sizeof(int)*10 + sizeof(time_t)) && read != 0) { + systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), + name); + res = false; + } + else if (read == 0) + { + systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), + name); + res = false; + } + else + { + // Also checks if the battery file it bigger than gbRamSizeMask+1+RTC ! + u8 data[1]; + data[0] = 0; + + read = gzread(gzFile, + data, + 1); + if(read >0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gbBatteryError = true; + res = false; + } + } + } + + gzclose(gzFile); + return res; +} + +bool gbReadSaveMBC5(const char * name) +{ + if (gbRam) + { + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) { + return false; + } + + int read = gzread(gzFile, + gbRam, + (gbRamSizeMask+1)); + + if(read != (gbRamSizeMask+1)) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + + // Also checks if the battery file it bigger than gbRamSizeMask+1 ! + u8 data[1]; + data[0] = 0; + + read = gzread(gzFile, + data, + 1); + if(read >0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + gzclose(gzFile); + return true; + } + else + return false; +} + +bool gbReadSaveMBC7(const char * name) +{ + if (gbRam) + { + FILE *file = fopen(name, "rb"); + + if(file == NULL) { + return false; + } + + size_t read = fread(&gbMemory[0xa000], + 1, + 256, + file); + + if(read != 256) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + fclose(file); + gbBatteryError = true; + return false; + } + + // Also checks if the battery file it bigger than gbRamSizeMask+1 ! + u8 data[1]; + data[0] = 0; + + read = fread(&data[0], + 1, + 1, + file); + if(read > 0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + fclose(file); + gbBatteryError = true; + return false; + } + + fclose(file); + return true; + } + else + return false; +} + +bool gbReadSaveTAMA5(const char * name) +{ + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) { + return false; + } + + int read = 0; + + if (gbRam) + read = gzread(gzFile, + gbRam, + (gbRamSizeMask+1)); + else + read = gbRamSizeMask; + + read += gzread(gzFile, + gbTAMA5ram, + gbTAMA5ramSize); + + bool res = true; + + if(read != (gbRamSizeMask+gbTAMA5ramSize+1)) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gbBatteryError = true; + res = false; + } else { + read = gzread(gzFile, + &gbDataTAMA5.mapperSeconds, + sizeof(int)*14 + sizeof(time_t)); + + if(read != (sizeof(int)*14 + sizeof(time_t)) && read != 0) { + systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), + name); + res = false; + } + else if (read == 0) + { + systemMessage(MSG_FAILED_TO_READ_RTC,N_("Failed to read RTC from save game %s (continuing)"), + name); + res = false; + } + else + { + // Also checks if the battery file it bigger than gbRamSizeMask+1+RTC ! + u8 data[1]; + data[0] = 0; + + read = gzread(gzFile, + data, + 1); + if(read >0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gbBatteryError = true; + res = false; + } + } + } + + gzclose(gzFile); + return res; +} + + +bool gbReadSaveMMM01(const char * name) +{ + if (gbRam) + { + gzFile gzFile = gzopen(name, "rb"); + + if(gzFile == NULL) { + return false; + } + + int read = gzread(gzFile, + gbRam, + (gbRamSizeMask+1)); + + if(read != (gbRamSizeMask+1)) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + // Also checks if the battery file it bigger than gbRamSizeMask+1 ! + u8 data[1]; + data[0] = 0; + + read = gzread(gzFile, + data, + 1); + if(read >0) { + systemMessage(MSG_FAILED_TO_READ_SGM, + N_("Battery file's size incompatible with the rom settings %s (%d).\nWarning : save of the battery file is now disabled !"), name, read); + gzclose(gzFile); + gbBatteryError = true; + return false; + } + + gzclose(gzFile); + return true; + } + else + return false; +} + +void gbInit() +{ + gbGenFilter(); + gbSgbInit(); + + gbMemory = (u8 *)malloc(65536); + + pix = (u8 *)calloc(1,4*257*226); + + gbLineBuffer = (u16 *)malloc(160 * sizeof(u16)); +} + +bool gbWriteBatteryFile(const char *file, bool extendedSave) +{ + if(gbBattery) { + switch(gbRomType) { + case 0x03: + gbWriteSaveMBC1(file); + break; + case 0x06: + gbWriteSaveMBC2(file); + break; + case 0x0d: + gbWriteSaveMMM01(file); + break; + case 0x0f: + case 0x10: + gbWriteSaveMBC3(file, extendedSave); + break; + case 0x13: + case 0xfc: + gbWriteSaveMBC3(file, false); + case 0x1b: + case 0x1e: + gbWriteSaveMBC5(file); + break; + case 0x22: + gbWriteSaveMBC7(file); + break; + case 0xfd: + gbWriteSaveTAMA5(file, extendedSave); + break; + case 0xff: + gbWriteSaveMBC1(file); + break; + } + } + return true; +} + +bool gbWriteBatteryFile(const char *file) +{ + if (!gbBatteryError) + { + gbWriteBatteryFile(file, true); + return true; + } + else return false; +} + +bool gbReadBatteryFile(const char *file) +{ + bool res = false; + if(gbBattery) { + switch(gbRomType) { + case 0x03: + res = gbReadSaveMBC1(file); + break; + case 0x06: + res = gbReadSaveMBC2(file); + break; + case 0x0d: + res = gbReadSaveMMM01(file); + break; + case 0x0f: + case 0x10: + if(!gbReadSaveMBC3(file)) { + time(&gbDataMBC3.mapperLastTime); + struct tm *lt; + lt = localtime(&gbDataMBC3.mapperLastTime); + gbDataMBC3.mapperSeconds = lt->tm_sec; + gbDataMBC3.mapperMinutes = lt->tm_min; + gbDataMBC3.mapperHours = lt->tm_hour; + gbDataMBC3.mapperDays = lt->tm_yday & 255; + gbDataMBC3.mapperControl = (gbDataMBC3.mapperControl & 0xfe) | + (lt->tm_yday > 255 ? 1: 0); + res = false; + break; + } + res = true; + break; + case 0x13: + case 0xfc: + res = gbReadSaveMBC3(file); + case 0x1b: + case 0x1e: + res = gbReadSaveMBC5(file); + break; + case 0x22: + res = gbReadSaveMBC7(file); + case 0xfd: + if(!gbReadSaveTAMA5(file)) { + u8 gbDaysinMonth [12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + time(&gbDataTAMA5.mapperLastTime); + struct tm *lt; + lt = localtime(&gbDataTAMA5.mapperLastTime); + gbDataTAMA5.mapperSeconds = lt->tm_sec; + gbDataTAMA5.mapperMinutes = lt->tm_min; + gbDataTAMA5.mapperHours = lt->tm_hour; + gbDataTAMA5.mapperDays = 1; + gbDataTAMA5.mapperMonths = 1; + gbDataTAMA5.mapperYears = 1970; + int days = lt->tm_yday+365*3; + while (days) + { + gbDataTAMA5.mapperDays++; + days--; + if (gbDataTAMA5.mapperDays>gbDaysinMonth[gbDataTAMA5.mapperMonths-1]) + { + gbDataTAMA5.mapperDays = 1; + gbDataTAMA5.mapperMonths++; + if (gbDataTAMA5.mapperMonths>12) + { + gbDataTAMA5.mapperMonths = 1; + gbDataTAMA5.mapperYears++; + if ((gbDataTAMA5.mapperYears & 3) == 0) + gbDaysinMonth[1] = 29; + else + gbDaysinMonth[1] = 28; + } + } + } + gbDataTAMA5.mapperControl = (gbDataTAMA5.mapperControl & 0xfe) | + (lt->tm_yday > 255 ? 1: 0); + res = false; + break; + } + res = true; + break; + case 0xff: + res = gbReadSaveMBC1(file); + break; + } + } + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + return res; +} + +bool gbReadGSASnapshot(const char *fileName) +{ + FILE *file = fopen(fileName, "rb"); + + if(!file) { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); + return false; + } + + fseek(file, 0x4, SEEK_SET); + char buffer[16]; + char buffer2[16]; + fread(buffer, 1, 15, file); + buffer[15] = 0; + memcpy(buffer2, &gbRom[0x134], 15); + buffer2[15] = 0; + if(memcmp(buffer, buffer2, 15)) { + systemMessage(MSG_CANNOT_IMPORT_SNAPSHOT_FOR, + N_("Cannot import snapshot for %s. Current game is %s"), + buffer, + buffer2); + fclose(file); + return false; + } + fseek(file, 0x13, SEEK_SET); + size_t read = 0; + int toRead = 0; + switch(gbRomType) { + case 0x03: + case 0x0f: + case 0x10: + case 0x13: + case 0x1b: + case 0x1e: + case 0xff: + read = fread(gbRam, 1, (gbRamSizeMask+1), file); + toRead = (gbRamSizeMask+1); + break; + case 0x06: + case 0x22: + read = fread(&gbMemory[0xa000],1,256,file); + toRead = 256; + break; + default: + systemMessage(MSG_UNSUPPORTED_SNAPSHOT_FILE, + N_("Unsupported snapshot file %s"), + fileName); + fclose(file); + return false; + } + fclose(file); + gbReset(); + return true; +} + +variable_desc gbSaveGameStruct[] = { + { &PC.W, sizeof(u16) }, + { &SP.W, sizeof(u16) }, + { &AF.W, sizeof(u16) }, + { &BC.W, sizeof(u16) }, + { &DE.W, sizeof(u16) }, + { &HL.W, sizeof(u16) }, + { &IFF, sizeof(u8) }, + { &GBLCD_MODE_0_CLOCK_TICKS, sizeof(int) }, + { &GBLCD_MODE_1_CLOCK_TICKS, sizeof(int) }, + { &GBLCD_MODE_2_CLOCK_TICKS, sizeof(int) }, + { &GBLCD_MODE_3_CLOCK_TICKS, sizeof(int) }, + { &GBDIV_CLOCK_TICKS, sizeof(int) }, + { &GBLY_INCREMENT_CLOCK_TICKS, sizeof(int) }, + { &GBTIMER_MODE_0_CLOCK_TICKS, sizeof(int) }, + { &GBTIMER_MODE_1_CLOCK_TICKS, sizeof(int) }, + { &GBTIMER_MODE_2_CLOCK_TICKS, sizeof(int) }, + { &GBTIMER_MODE_3_CLOCK_TICKS, sizeof(int) }, + { &GBSERIAL_CLOCK_TICKS, sizeof(int) }, + { &GBSYNCHRONIZE_CLOCK_TICKS, sizeof(int) }, + { &gbDivTicks, sizeof(int) }, + { &gbLcdMode, sizeof(int) }, + { &gbLcdTicks, sizeof(int) }, + { &gbLcdLYIncrementTicks, sizeof(int) }, + { &gbTimerTicks, sizeof(int) }, + { &gbTimerClockTicks, sizeof(int) }, + { &gbSerialTicks, sizeof(int) }, + { &gbSerialBits, sizeof(int) }, + { &gbInt48Signal, sizeof(int) }, + { &gbInterruptWait, sizeof(int) }, + { &gbSynchronizeTicks, sizeof(int) }, + { &gbTimerOn, sizeof(int) }, + { &gbTimerMode, sizeof(int) }, + { &gbSerialOn, sizeof(int) }, + { &gbWindowLine, sizeof(int) }, + { &gbCgbMode, sizeof(int) }, + { &gbVramBank, sizeof(int) }, + { &gbWramBank, sizeof(int) }, + { &gbHdmaSource, sizeof(int) }, + { &gbHdmaDestination, sizeof(int) }, + { &gbHdmaBytes, sizeof(int) }, + { &gbHdmaOn, sizeof(int) }, + { &gbSpeed, sizeof(int) }, + { &gbSgbMode, sizeof(int) }, + { ®ister_DIV, sizeof(u8) }, + { ®ister_TIMA, sizeof(u8) }, + { ®ister_TMA, sizeof(u8) }, + { ®ister_TAC, sizeof(u8) }, + { ®ister_IF, sizeof(u8) }, + { ®ister_LCDC, sizeof(u8) }, + { ®ister_STAT, sizeof(u8) }, + { ®ister_SCY, sizeof(u8) }, + { ®ister_SCX, sizeof(u8) }, + { ®ister_LY, sizeof(u8) }, + { ®ister_LYC, sizeof(u8) }, + { ®ister_DMA, sizeof(u8) }, + { ®ister_WY, sizeof(u8) }, + { ®ister_WX, sizeof(u8) }, + { ®ister_VBK, sizeof(u8) }, + { ®ister_HDMA1, sizeof(u8) }, + { ®ister_HDMA2, sizeof(u8) }, + { ®ister_HDMA3, sizeof(u8) }, + { ®ister_HDMA4, sizeof(u8) }, + { ®ister_HDMA5, sizeof(u8) }, + { ®ister_SVBK, sizeof(u8) }, + { ®ister_IE , sizeof(u8) }, + { &gbBgp[0], sizeof(u8) }, + { &gbBgp[1], sizeof(u8) }, + { &gbBgp[2], sizeof(u8) }, + { &gbBgp[3], sizeof(u8) }, + { &gbObp0[0], sizeof(u8) }, + { &gbObp0[1], sizeof(u8) }, + { &gbObp0[2], sizeof(u8) }, + { &gbObp0[3], sizeof(u8) }, + { &gbObp1[0], sizeof(u8) }, + { &gbObp1[1], sizeof(u8) }, + { &gbObp1[2], sizeof(u8) }, + { &gbObp1[3], sizeof(u8) }, + { NULL, 0 } +}; + + +static bool gbWriteSaveState(gzFile gzFile) +{ + + utilWriteInt(gzFile, GBSAVE_GAME_VERSION); + + utilGzWrite(gzFile, &gbRom[0x134], 15); + + utilWriteInt(gzFile, useBios); + utilWriteInt(gzFile, inBios); + + utilWriteData(gzFile, gbSaveGameStruct); + + utilGzWrite(gzFile, &IFF, 2); + + if(gbSgbMode) { + gbSgbSaveGame(gzFile); + } + + utilGzWrite(gzFile, &gbDataMBC1, sizeof(gbDataMBC1)); + utilGzWrite(gzFile, &gbDataMBC2, sizeof(gbDataMBC2)); + utilGzWrite(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)); + utilGzWrite(gzFile, &gbDataMBC5, sizeof(gbDataMBC5)); + utilGzWrite(gzFile, &gbDataHuC1, sizeof(gbDataHuC1)); + utilGzWrite(gzFile, &gbDataHuC3, sizeof(gbDataHuC3)); + utilGzWrite(gzFile, &gbDataTAMA5, sizeof(gbDataTAMA5)); + if (gbTAMA5ram != NULL) + utilGzWrite(gzFile, gbTAMA5ram, gbTAMA5ramSize); + utilGzWrite(gzFile, &gbDataMMM01, sizeof(gbDataMMM01)); + + utilGzWrite(gzFile, gbPalette, 128 * sizeof(u16)); + + utilGzWrite(gzFile, &gbMemory[0x8000], 0x8000); + + if(gbRamSize && gbRam) { + utilWriteInt(gzFile, gbRamSize); + utilGzWrite(gzFile, gbRam, gbRamSize); + } + + if(gbCgbMode) { + utilGzWrite(gzFile, gbVram, 0x4000); + utilGzWrite(gzFile, gbWram, 0x8000); + } + + gbSoundSaveGame(gzFile); + + gbCheatsSaveGame(gzFile); + + utilWriteInt(gzFile, gbLcdModeDelayed); + utilWriteInt(gzFile, gbLcdTicksDelayed); + utilWriteInt(gzFile, gbLcdLYIncrementTicksDelayed); + utilWriteInt(gzFile, gbSpritesTicks[299]); + utilWriteInt(gzFile, gbTimerModeChange); + utilWriteInt(gzFile, gbTimerOnChange); + utilWriteInt(gzFile, gbHardware); + utilWriteInt(gzFile, gbBlackScreen); + utilWriteInt(gzFile, oldRegister_WY); + utilWriteInt(gzFile, gbWindowLine); + utilWriteInt(gzFile, inUseRegister_WY); + utilWriteInt(gzFile, gbScreenOn); + return true; +} + +bool gbWriteMemSaveState(char *memory, int available) +{ + gzFile gzFile = utilMemGzOpen(memory, available, "w"); + + if(gzFile == NULL) { + return false; + } + + bool res = gbWriteSaveState(gzFile); + + long pos = utilGzMemTell(gzFile)+8; + + if(pos >= (available)) + res = false; + + utilGzClose(gzFile); + + return res; +} + +bool gbWriteSaveState(const char *name) +{ + gzFile gzFile = utilGzOpen(name,"wb"); + + if(gzFile == NULL) + return false; + + bool res = gbWriteSaveState(gzFile); + + utilGzClose(gzFile); + return res; +} + +static bool gbReadSaveState(gzFile gzFile) +{ + int version = utilReadInt(gzFile); + + if(version > GBSAVE_GAME_VERSION || version < 0) { + systemMessage(MSG_UNSUPPORTED_VB_SGM, + N_("Unsupported VisualBoy save game version %d"), version); + return false; + } + + u8 romname[20]; + + utilGzRead(gzFile, romname, 15); + + if(memcmp(&gbRom[0x134], romname, 15) != 0) { + systemMessage(MSG_CANNOT_LOAD_SGM_FOR, + N_("Cannot load save game for %s. Playing %s"), + romname, &gbRom[0x134]); + return false; + } + + + bool ub = false; + bool ib = false; + + if (version >= 11) + { + ub = utilReadInt(gzFile) ? true : false; + ib = utilReadInt(gzFile) ? true : false; + + if((ub != useBios) && (ib)) { + if(useBios) + systemMessage(MSG_SAVE_GAME_NOT_USING_BIOS, + N_("Save game is not using the BIOS files")); + else + systemMessage(MSG_SAVE_GAME_USING_BIOS, + N_("Save game is using the BIOS file")); + return false; + } + } + + gbReset(); + + inBios = ib; + + utilReadData(gzFile, gbSaveGameStruct); + + + // Correct crash when loading color gameboy save in regular gameboy type. + if (!gbCgbMode) + { + if(gbVram != NULL) { + free(gbVram); + gbVram = NULL; + } + if(gbWram != NULL) { + free(gbWram); + gbWram = NULL; + } + } + else + { + if(gbVram == NULL) + gbVram = (u8 *)malloc(0x4000); + if(gbWram == NULL) + gbWram = (u8 *)malloc(0x8000); + memset(gbVram,0,0x4000); + memset(gbPalette,0, 2*128); + } + + + + if(version >= GBSAVE_GAME_VERSION_7) { + utilGzRead(gzFile, &IFF, 2); + } + + if(gbSgbMode) { + gbSgbReadGame(gzFile, version); + } else { + gbSgbMask = 0; // loading a game at the wrong time causes no display + } + if (version<11) + utilGzRead(gzFile, &gbDataMBC1, sizeof(gbDataMBC1) - sizeof(int)); + else + utilGzRead(gzFile, &gbDataMBC1, sizeof(gbDataMBC1)); + utilGzRead(gzFile, &gbDataMBC2, sizeof(gbDataMBC2)); + if(version < GBSAVE_GAME_VERSION_4) + // prior to version 4, there was no adjustment for the time the game + // was last played, so we have less to read. This needs update if the + // structure changes again. + utilGzRead(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)-sizeof(time_t)); + else + utilGzRead(gzFile, &gbDataMBC3, sizeof(gbDataMBC3)); + utilGzRead(gzFile, &gbDataMBC5, sizeof(gbDataMBC5)); + utilGzRead(gzFile, &gbDataHuC1, sizeof(gbDataHuC1)); + utilGzRead(gzFile, &gbDataHuC3, sizeof(gbDataHuC3)); + if (version>=11) + { + utilGzRead(gzFile, &gbDataTAMA5, sizeof(gbDataTAMA5)); + if (gbTAMA5ram != NULL) + utilGzRead(gzFile, gbTAMA5ram, gbTAMA5ramSize); + utilGzRead(gzFile, &gbDataMMM01, sizeof(gbDataMMM01)); + } + + if(version < GBSAVE_GAME_VERSION_5) { + utilGzRead(gzFile, pix, 256*224*sizeof(u16)); + } + memset(pix, 0, 257*226*sizeof(u32)); + + if(version < GBSAVE_GAME_VERSION_6) { + utilGzRead(gzFile, gbPalette, 64 * sizeof(u16)); + } else + utilGzRead(gzFile, gbPalette, 128 * sizeof(u16)); + + if (version < 11) + utilGzRead(gzFile, gbPalette, 128 * sizeof(u16)); + + if(version < GBSAVE_GAME_VERSION_10) { + if(!gbCgbMode && !gbSgbMode) { + for(int i = 0; i < 8; i++) + gbPalette[i] = systemGbPalette[gbPaletteOption*8+i]; + } + } + + utilGzRead(gzFile, &gbMemory[0x8000], 0x8000); + + if(gbRamSize && gbRam) { + if (version < 11) + utilGzRead(gzFile, gbRam, gbRamSize); + else + { + int ramSize = utilReadInt(gzFile); + utilGzRead(gzFile, gbRam, (gbRamSize>ramSize) ? ramSize : gbRamSize); + if (ramSize>gbRamSize) + gzseek(gzFile,ramSize-gbRamSize,SEEK_CUR); + } + } + + memset(gbSCYLine, register_SCY, sizeof(gbSCYLine)); + memset(gbSCXLine, register_SCX, sizeof(gbSCXLine)); + memset(gbBgpLine, (gbBgp[0] | (gbBgp[1]<<2) | (gbBgp[2]<<4) | + (gbBgp[3]<<6)), sizeof(gbBgpLine)); + memset(gbObp0Line, (gbObp0[0] | (gbObp0[1]<<2) | (gbObp0[2]<<4) | + (gbObp0[3]<<6)), sizeof(gbObp0Line)); + memset(gbObp1Line, (gbObp1[0] | (gbObp1[1]<<2) | (gbObp1[2]<<4) | + (gbObp1[3]<<6)), sizeof(gbObp1Line)); + memset(gbSpritesTicks, 0x0, sizeof(gbSpritesTicks)); + + if (inBios) + { + gbMemoryMap[0x00] = &gbMemory[0x0000]; + memcpy ((u8 *)(gbMemory), (u8 *)(gbRom), 0x1000); + memcpy ((u8 *)(gbMemory), (u8 *)(bios), 0x100); + } + else + gbMemoryMap[0x00] = &gbRom[0x0000]; + gbMemoryMap[0x01] = &gbRom[0x1000]; + gbMemoryMap[0x02] = &gbRom[0x2000]; + gbMemoryMap[0x03] = &gbRom[0x3000]; + gbMemoryMap[0x04] = &gbRom[0x4000]; + gbMemoryMap[0x05] = &gbRom[0x5000]; + gbMemoryMap[0x06] = &gbRom[0x6000]; + gbMemoryMap[0x07] = &gbRom[0x7000]; + gbMemoryMap[0x08] = &gbMemory[0x8000]; + gbMemoryMap[0x09] = &gbMemory[0x9000]; + gbMemoryMap[0x0a] = &gbMemory[0xa000]; + gbMemoryMap[0x0b] = &gbMemory[0xb000]; + gbMemoryMap[0x0c] = &gbMemory[0xc000]; + gbMemoryMap[0x0d] = &gbMemory[0xd000]; + gbMemoryMap[0x0e] = &gbMemory[0xe000]; + gbMemoryMap[0x0f] = &gbMemory[0xf000]; + + switch(gbRomType) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + // MBC 1 + memoryUpdateMapMBC1(); + break; + case 0x05: + case 0x06: + // MBC2 + memoryUpdateMapMBC2(); + break; + case 0x0b: + case 0x0c: + case 0x0d: + // MMM01 + memoryUpdateMapMMM01(); + break; + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + // MBC 3 + memoryUpdateMapMBC3(); + break; + case 0x19: + case 0x1a: + case 0x1b: + // MBC5 + memoryUpdateMapMBC5(); + break; + case 0x1c: + case 0x1d: + case 0x1e: + // MBC 5 Rumble + memoryUpdateMapMBC5(); + break; + case 0x22: + // MBC 7 + memoryUpdateMapMBC7(); + break; + case 0x56: + // GS3 + memoryUpdateMapGS3(); + break; + case 0xfd: + // TAMA5 + memoryUpdateMapTAMA5(); + break; + case 0xfe: + // HuC3 + memoryUpdateMapHuC3(); + break; + case 0xff: + // HuC1 + memoryUpdateMapHuC1(); + break; + } + + if(gbCgbMode) { + utilGzRead(gzFile, gbVram, 0x4000); + utilGzRead(gzFile, gbWram, 0x8000); + + int value = register_SVBK; + if(value == 0) + value = 1; + + gbMemoryMap[0x08] = &gbVram[register_VBK * 0x2000]; + gbMemoryMap[0x09] = &gbVram[register_VBK * 0x2000 + 0x1000]; + gbMemoryMap[0x0d] = &gbWram[value * 0x1000]; + } + + gbSoundReadGame(version, gzFile); + + if (gbCgbMode && gbSgbMode) { + gbSgbMode = 0; + } + + if(gbBorderOn && !gbSgbMask) { + gbSgbRenderBorder(); + } + + systemDrawScreen(); + + if(version > GBSAVE_GAME_VERSION_1) + gbCheatsReadGame(gzFile, version); + + if (version<11) + { + gbWriteMemory(0xff00, 0); + gbMemory[0xff04] = register_DIV; + gbMemory[0xff05] = register_TIMA; + gbMemory[0xff06] = register_TMA; + gbMemory[0xff07] = register_TAC; + gbMemory[0xff40] = register_LCDC; + gbMemory[0xff42] = register_SCY; + gbMemory[0xff43] = register_SCX; + gbMemory[0xff44] = register_LY; + gbMemory[0xff45] = register_LYC; + gbMemory[0xff46] = register_DMA; + gbMemory[0xff4a] = register_WY; + gbMemory[0xff4b] = register_WX; + gbMemory[0xff4f] = register_VBK; + gbMemory[0xff51] = register_HDMA1; + gbMemory[0xff52] = register_HDMA2; + gbMemory[0xff53] = register_HDMA3; + gbMemory[0xff54] = register_HDMA4; + gbMemory[0xff55] = register_HDMA5; + gbMemory[0xff70] = register_SVBK; + gbMemory[0xffff] = register_IE; + GBDIV_CLOCK_TICKS = 64; + + if (gbSpeed) + gbDivTicks /=2; + + if ((gbLcdMode == 0) && (register_STAT & 8)) + gbInt48Signal |= 1; + if ((gbLcdMode == 1) && (register_STAT & 0x10)) + gbInt48Signal |= 2; + if ((gbLcdMode == 2) && (register_STAT & 0x20)) + gbInt48Signal |= 4; + if ((register_LY==register_LYC) && (register_STAT & 0x40)) + gbInt48Signal |= 8; + + gbLcdLYIncrementTicks = GBLY_INCREMENT_CLOCK_TICKS; + + if (gbLcdMode == 2) + gbLcdLYIncrementTicks-=GBLCD_MODE_2_CLOCK_TICKS-gbLcdTicks; + else if (gbLcdMode == 3) + gbLcdLYIncrementTicks -=GBLCD_MODE_2_CLOCK_TICKS+GBLCD_MODE_3_CLOCK_TICKS-gbLcdTicks; + else if (gbLcdMode == 0) + gbLcdLYIncrementTicks =gbLcdTicks; + else if (gbLcdMode == 1) + { + gbLcdLYIncrementTicks = gbLcdTicks % GBLY_INCREMENT_CLOCK_TICKS; + if (register_LY == 0x99) + gbLcdLYIncrementTicks =gbLine99Ticks; + else if (register_LY == 0) + gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS; + } + + gbLcdModeDelayed = gbLcdMode; + gbLcdTicksDelayed = gbLcdTicks--; + gbLcdLYIncrementTicksDelayed = gbLcdLYIncrementTicks--; + gbInterruptWait = 0; + memset(gbSpritesTicks,0,sizeof(gbSpritesTicks)); + } + else + { + gbLcdModeDelayed = utilReadInt(gzFile); + gbLcdTicksDelayed = utilReadInt(gzFile); + gbLcdLYIncrementTicksDelayed = utilReadInt(gzFile); + gbSpritesTicks[299] = utilReadInt(gzFile) & 0xff; + gbTimerModeChange = (utilReadInt(gzFile) ? true : false); + gbTimerOnChange = (utilReadInt(gzFile) ? true : false); + gbHardware = utilReadInt(gzFile); + gbBlackScreen = (utilReadInt(gzFile) ? true : false); + oldRegister_WY = utilReadInt(gzFile); + gbWindowLine = utilReadInt(gzFile); + inUseRegister_WY = utilReadInt(gzFile); + gbScreenOn = (utilReadInt(gzFile) ? true : false); + } + + if (gbSpeed) + gbLine99Ticks *= 2; + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + return true; +} + +bool gbReadMemSaveState(char *memory, int available) +{ + gzFile gzFile = utilMemGzOpen(memory, available, "r"); + + bool res = gbReadSaveState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool gbReadSaveState(const char *name) +{ + gzFile gzFile = utilGzOpen(name,"rb"); + + if(gzFile == NULL) { + return false; + } + + bool res = gbReadSaveState(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool gbWritePNGFile(const char *fileName) +{ + if(gbBorderOn) + return utilWritePNGFile(fileName, 256, 224, pix); + return utilWritePNGFile(fileName, 160, 144, pix); +} + +bool gbWriteBMPFile(const char *fileName) +{ + if(gbBorderOn) + return utilWriteBMPFile(fileName, 256, 224, pix); + return utilWriteBMPFile(fileName, 160, 144, pix); +} + +void gbCleanUp() +{ + if(gbRam != NULL) { + free(gbRam); + gbRam = NULL; + } + + if(gbRom != NULL) { + free(gbRom); + gbRom = NULL; + } + + if(gbMemory != NULL) { + free(gbMemory); + gbMemory = NULL; + } + + if(gbLineBuffer != NULL) { + free(gbLineBuffer); + gbLineBuffer = NULL; + } + + if(pix != NULL) { + free(pix); + pix = NULL; + } + + gbSgbShutdown(); + + if(gbVram != NULL) { + free(gbVram); + gbVram = NULL; + } + + if(gbWram != NULL) { + free(gbWram); + gbWram = NULL; + } + + if(gbTAMA5ram != NULL) { + free(gbTAMA5ram); + gbTAMA5ram = NULL; + } + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; +} + +bool gbLoadRom(const char *szFile) +{ + int size = 0; + + if(gbRom != NULL) { + gbCleanUp(); + } + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + gbRom = utilLoad(szFile, + utilIsGBImage, + NULL, + size); + if(!gbRom) + return false; + + gbRomSize = size; + + gbBatteryError = false; + + if(bios != NULL) { + free(bios); + bios = NULL; + } + bios = (u8 *)calloc(1,0x100); + + return gbUpdateSizes(); +} + +bool gbUpdateSizes() +{ + if(gbRom[0x148] > 8) { + systemMessage(MSG_UNSUPPORTED_ROM_SIZE, + N_("Unsupported rom size %02x"), gbRom[0x148]); + return false; + } + + if(gbRomSize < gbRomSizes[gbRom[0x148]]) { + gbRom = (u8 *)realloc(gbRom, gbRomSizes[gbRom[0x148]]); + for (int i = gbRomSize; igbRomSizes[gbRom[0x148]]) && (genericflashcardEnable)) + { + gbRomSize = gbRomSize>>16; + gbRom[0x148] = 0; + if (gbRomSize) + { + while (!((gbRomSize & 1) || (gbRom[0x148] == 7))) + { + gbRom[0x148]++; + gbRomSize>>=1; + } + gbRom[0x148]++; + } + gbRom = (u8 *)realloc(gbRom, gbRomSizes[gbRom[0x148]]); + } + gbRomSize = gbRomSizes[gbRom[0x148]]; + gbRomSizeMask = gbRomSizesMasks[gbRom[0x148]]; + + + // The 'genericflashcard' option allows some PD to work. + // However, the setting is dangerous (if you let in enabled + // and play a normal game, it might just break everything). + // That's why it is not saved in the emulator options. + // Also I added some checks in VBA to make sure your saves will not be + // overwritten if you wrongly enable this option for a game + // you already played (and vice-versa, ie. if you forgot to + // enable the option for a game you played with it enabled, like Shawu Story). + u8 ramsize = genericflashcardEnable ? 5 : gbRom[0x149]; + gbRom[0x149] = ramsize; + + if ((gbRom[2] == 0x6D) && (gbRom[5] == 0x47) && (gbRom[6] == 0x65) && (gbRom[7] == 0x6E) && + (gbRom[8] == 0x69) && (gbRom[9] == 0x65) && (gbRom[0xA] == 0x28) && (gbRom[0xB] == 0x54)) + { + gbCheatingDevice = 1; // GameGenie + for (int i = 0; i < 0x20; i++) // Cleans GG hardware registers + gbRom[0x4000+i] = 0; + } + else if (((gbRom[0x104] == 0x44) && (gbRom[0x156] == 0xEA) && (gbRom[0x158] == 0x7F) && + (gbRom[0x159] == 0xEA) && (gbRom[0x15B] == 0x7F)) || ((gbRom[0x165] == 0x3E) && + (gbRom[0x166] == 0xD9) && (gbRom[0x16D] == 0xE1) && (gbRom[0x16E] == 0x7F))) + gbCheatingDevice = 2; // GameShark + else gbCheatingDevice = 0; + + if(ramsize > 5) { + systemMessage(MSG_UNSUPPORTED_RAM_SIZE, + N_("Unsupported ram size %02x"), gbRom[0x149]); + return false; + } + + gbRamSize = gbRamSizes[ramsize]; + gbRamSizeMask = gbRamSizesMasks[ramsize]; + + if(gbRamSize) { + gbRam = (u8 *)malloc(gbRamSize); + memset(gbRam, 0xff, gbRamSize); + } + + + gbRomType = gbRom[0x147]; + if (genericflashcardEnable) + { + /*if (gbRomType<2) + gbRomType =3; + else if ((gbRomType == 0xc) || (gbRomType == 0xf) || (gbRomType == 0x12) || + (gbRomType == 0x16) || (gbRomType == 0x1a) || (gbRomType == 0x1d)) + gbRomType++; + else if ((gbRomType == 0xb) || (gbRomType == 0x11) || (gbRomType == 0x15) || + (gbRomType == 0x19) || (gbRomType == 0x1c)) + gbRomType+=2; + else if ((gbRomType == 0x5) || (gbRomType == 0x6)) + gbRomType = 0x1a;*/ + gbRomType = 0x1b; + } + else if (gbCheatingDevice == 1) + gbRomType = 0x55; + else if (gbCheatingDevice == 2) + gbRomType = 0x56; + + gbRom[0x147] = gbRomType; + + mapperReadRAM = NULL; + + switch(gbRomType) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x08: + case 0x09: + // MBC 1 + mapper = mapperMBC1ROM; + mapperRAM = mapperMBC1RAM; + mapperReadRAM = mapperMBC1ReadRAM; + break; + case 0x05: + case 0x06: + // MBC2 + mapper = mapperMBC2ROM; + mapperRAM = mapperMBC2RAM; + gbRamSize = 0x200; + gbRamSizeMask = 0x1ff; + break; + case 0x0b: + case 0x0c: + case 0x0d: + // MMM01 + mapper = mapperMMM01ROM; + mapperRAM = mapperMMM01RAM; + break; + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0xfc: + // MBC 3 + mapper = mapperMBC3ROM; + mapperRAM = mapperMBC3RAM; + mapperReadRAM = mapperMBC3ReadRAM; + break; + case 0x19: + case 0x1a: + case 0x1b: + // MBC5 + mapper = mapperMBC5ROM; + mapperRAM = mapperMBC5RAM; + mapperReadRAM = mapperMBC5ReadRAM; + break; + case 0x1c: + case 0x1d: + case 0x1e: + // MBC 5 Rumble + mapper = mapperMBC5ROM; + mapperRAM = mapperMBC5RAM; + mapperReadRAM = mapperMBC5ReadRAM; + break; + case 0x22: + // MBC 7 + mapper = mapperMBC7ROM; + mapperRAM = mapperMBC7RAM; + mapperReadRAM = mapperMBC7ReadRAM; + break; + // GG (GameGenie) + case 0x55: + mapper = mapperGGROM; + break; + case 0x56: + // GS (GameShark) + mapper = mapperGS3ROM; + break; + case 0xfd: + // TAMA5 + if (gbRam!= NULL) + { + free(gbRam); + gbRam = NULL; + } + + ramsize = 3; + gbRamSize = gbRamSizes[3]; + gbRamSizeMask = gbRamSizesMasks[3]; + gbRam = (u8 *)malloc(gbRamSize); + memset(gbRam, 0x0, gbRamSize); + + gbTAMA5ramSize = 0x100; + + if (gbTAMA5ram == NULL) + gbTAMA5ram = (u8 *)malloc(gbTAMA5ramSize); + memset(gbTAMA5ram, 0x0, gbTAMA5ramSize); + + mapperRAM = mapperTAMA5RAM; + mapperReadRAM = mapperTAMA5ReadRAM; + mapperUpdateClock = memoryUpdateTAMA5Clock; + break; + case 0xfe: + // HuC3 + mapper = mapperHuC3ROM; + mapperRAM = mapperHuC3RAM; + mapperReadRAM = mapperHuC3ReadRAM; + break; + case 0xff: + // HuC1 + mapper = mapperHuC1ROM; + mapperRAM = mapperHuC1RAM; + break; + default: + systemMessage(MSG_UNKNOWN_CARTRIDGE_TYPE, + N_("Unknown cartridge type %02x"), gbRomType); + return false; + } + + switch(gbRomType) { + case 0x03: + case 0x06: + case 0x0f: + case 0x10: + case 0x13: + case 0x1b: + case 0x1d: + case 0x1e: + case 0x22: + case 0xfd: + case 0xff: + gbBattery = 1; + break; + } + + gbInit(); + + //gbReset(); + + switch(gbRomType) { + case 0x1c: + case 0x1d: + case 0x1e: + gbDataMBC5.isRumbleCartridge = 1; + } + + return true; +} + +int gbGetNextEvent (int clockTicks) +{ + if (register_LCDC & 0x80) + { + if(gbLcdTicks < clockTicks) + clockTicks = gbLcdTicks; + + if(gbLcdTicksDelayed < clockTicks) + clockTicks = gbLcdTicksDelayed; + + if(gbLcdLYIncrementTicksDelayed < clockTicks) + clockTicks = gbLcdLYIncrementTicksDelayed; + } + + if(gbLcdLYIncrementTicks < clockTicks) + clockTicks = gbLcdLYIncrementTicks; + + if(gbSerialOn && (gbSerialTicks < clockTicks)) + clockTicks = gbSerialTicks; + + if(gbTimerOn && (((gbInternalTimer) & gbTimerMask[gbTimerMode])+1 < clockTicks)) + clockTicks = ((gbInternalTimer) & gbTimerMask[gbTimerMode])+1; + + //if(soundTicks && (soundTicks < clockTicks)) + // clockTicks = soundTicks; + + if ((clockTicks<=0) || (gbInterruptWait)) + clockTicks = 1; + + return clockTicks; +} + +void gbDrawLine() +{ + switch(systemColorDepth) { + case 16: + { + u16 * dest = (u16 *)pix + + (gbBorderLineSkip+2) * (register_LY + gbBorderRowSkip+1) + + gbBorderColumnSkip; + for(int x = 0; x < 160; ) { + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + *dest++ = systemColorMap16[gbLineMix[x++]]; + } + if(gbBorderOn) + dest += gbBorderColumnSkip; + *dest++ = 0; // for filters that read one pixel more + } + break; + + case 24: + { + u8 *dest = (u8 *)pix + + 3*(gbBorderLineSkip * (register_LY + gbBorderRowSkip) + + gbBorderColumnSkip); + for(int x = 0; x < 160;) { + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + *((u32 *)dest) = systemColorMap32[gbLineMix[x++]]; + dest+= 3; + } + } + break; + + case 32: + { + u32 * dest = (u32 *)pix + + (gbBorderLineSkip+1) * (register_LY + gbBorderRowSkip+1) + + gbBorderColumnSkip; + for(int x = 0; x < 160;) { + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + *dest++ = systemColorMap32[gbLineMix[x++]]; + } + } + break; + } +} + +void gbEmulate(int ticksToStop) +{ + gbRegister tempRegister; + u8 tempValue; + s8 offset; + + clockTicks = 0; + gbDmaTicks = 0; + + register int opcode = 0; + + int opcode1 = 0; + int opcode2 = 0; + bool execute = false; + + while(1) { +#ifndef FINAL_VERSION + if(systemDebug) { + if(!(IFF & 0x80)) { + if(systemDebug > 1) { + sprintf(gbBuffer,"PC=%04x AF=%04x BC=%04x DE=%04x HL=%04x SP=%04x I=%04x\n", + PC.W, AF.W, BC.W, DE.W,HL.W,SP.W,IFF); + } else { + sprintf(gbBuffer,"PC=%04x I=%02x\n", PC.W, IFF); + } + log(gbBuffer); + } + } +#endif + + u16 oldPCW = PC.W; + + if(IFF & 0x80) { + if(register_LCDC & 0x80) { + clockTicks = gbLcdTicks; + } else + clockTicks = 1000; + + clockTicks = gbGetNextEvent(clockTicks); + + /*if(gbLcdTicksDelayed < clockTicks) + clockTicks = gbLcdTicksDelayed; + + if(gbLcdLYIncrementTicksDelayed < clockTicks) + clockTicks = gbLcdLYIncrementTicksDelayed; + + if(gbLcdLYIncrementTicks < clockTicks) + clockTicks = gbLcdLYIncrementTicks; + + if(gbSerialOn && (gbSerialTicks < clockTicks)) + clockTicks = gbSerialTicks; + + if(gbTimerOn && (((gbInternalTimer) & gbTimerMask[gbTimerMode])+1 < clockTicks)) + clockTicks = ((gbInternalTimer) & gbTimerMask[gbTimerMode])+1; + + if(soundTicks && (soundTicks < clockTicks)) + clockTicks = soundTicks; + + if ((clockTicks<=0) || (gbInterruptWait)) + clockTicks = 1;*/ + + } else { + + // First we apply the clockTicks, then we execute the opcodes. + opcode1 = 0; + opcode2 = 0; + execute = true; + + opcode2 = opcode1 = opcode = gbReadOpcode(PC.W++); + + // If HALT state was launched while IME = 0 and (register_IF & register_IE & 0x1F), + // PC.W is not incremented for the first byte of the next instruction. + if (IFF & 2) + { + PC.W--; + IFF &= ~2; + } + + clockTicks = gbCycles[opcode]; + + switch(opcode) { + case 0xCB: + // extended opcode + opcode2 = opcode = gbReadOpcode(PC.W++); + clockTicks = gbCyclesCB[opcode]; + break; + } + gbOldClockTicks = clockTicks-1; + gbIntBreak = 1; + } + + + if(!emulating) + return; + + // For 'breakpoint' support (opcode 0xFC is considered as a breakpoint) + if ((clockTicks==0) && execute) + { + PC.W = oldPCW; + return; + } + + + if (!(IFF & 0x80)) + clockTicks = 1; + + gbRedoLoop: + + + + if (gbInterruptWait) + gbInterruptWait = 0; + else + gbInterruptLaunched = 0; + + + // Used for the EI/DI instruction's delay. + if (IFF & 0x38) + { + int tempIFF = (IFF >> 4) & 3; + + if (tempIFF <=clockTicks) + { + tempIFF = 0; + IFF |=1; + } + else + tempIFF -= clockTicks; + IFF = (IFF & 0xCF) | (tempIFF <<4); + + if (IFF & 0x08) + IFF &= 0x82; + } + + + if (register_LCDCBusy) + { + register_LCDCBusy-=clockTicks; + if (register_LCDCBusy<0) + register_LCDCBusy = 0; + } + + + if(gbSgbMode) { + if(gbSgbPacketTimeout) { + gbSgbPacketTimeout -= clockTicks; + + if(gbSgbPacketTimeout <= 0) + gbSgbResetPacketState(); + } + } + + ticksToStop -= clockTicks; + + // DIV register emulation + gbDivTicks -= clockTicks; + while(gbDivTicks <= 0) { + gbMemory[0xff04] = ++register_DIV; + gbDivTicks += GBDIV_CLOCK_TICKS; + } + + if(register_LCDC & 0x80) { + // LCD stuff + + gbLcdTicks -= clockTicks; + gbLcdTicksDelayed -= clockTicks; + gbLcdLYIncrementTicks -= clockTicks; + gbLcdLYIncrementTicksDelayed -= clockTicks; + + + // our counters are off, see what we need to do + + // This looks (and kinda is) complicated, however this + // is the only way I found to emulate properly the way + // the real hardware operates... + while(((gbLcdTicks <= 0) && (gbLCDChangeHappened == false)) || + ((gbLcdTicksDelayed <= 0) && (gbLCDChangeHappened == true)) || + ((gbLcdLYIncrementTicks <= 0) && (gbLYChangeHappened == false)) || + ((gbLcdLYIncrementTicksDelayed<=0) && (gbLYChangeHappened == true))) + { + + if ((gbLcdLYIncrementTicks <= 0) && (!gbLYChangeHappened)) + { + gbLYChangeHappened = true; + gbMemory[0xff44] = register_LY = (register_LY + 1) % 154; + + if (register_LY == 0x91) + { + /* if (IFF & 0x80) + gbScreenOn = !gbScreenOn; + else*/ if (register_LCDC & 0x80) + gbScreenOn = true; + } + + gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS; + + if (gbLcdMode == 1) + { + + if(register_LY == 153) + gbLcdLYIncrementTicks -= GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; + else if(register_LY == 0) + gbLcdLYIncrementTicks += GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; + } + + // GB only 'bug' : Halt state is broken one tick before LY==LYC interrupt + // is reflected in the registers. + if ((gbHardware & 5) && (IFF & 0x80) && (register_LY == register_LYC) + && (register_STAT & 0x40) && (register_LY != 0)) + { + if (!((gbLcdModeDelayed != 1) && (register_LY==0))) + { + gbInt48Signal &= ~9; + gbCompareLYToLYC(); + gbLYChangeHappened = false; + gbMemory[0xff41] = register_STAT; + gbMemory[0xff0f] = register_IF; + } + + gbLcdLYIncrementTicksDelayed += GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks+1; + } + } + + + if ((gbLcdTicks <= 0) && (!gbLCDChangeHappened)) + { + gbLCDChangeHappened = true; + + switch(gbLcdMode) + { + case 0: + { + // H-Blank + // check if we reached the V-Blank period + if(register_LY == 144) { + // Yes, V-Blank + // set the LY increment counter + if (gbHardware & 0x5) + { + register_IF |= 1; // V-Blank interrupt + } + + gbInt48Signal &= ~6; + if(register_STAT & 0x10) + { + // send LCD interrupt only if no interrupt 48h signal... + if ((!(gbInt48Signal & 1)) && ((!(gbInt48Signal & 8)) || (gbHardware & 0x0a))) + { + register_IF |=2; + gbInterruptLaunched |= 2; + if (gbHardware & 0xa) + gbInterruptWait = 1; + } + gbInt48Signal |= 2; + } + gbInt48Signal &= ~1; + + gbLcdTicks += GBLCD_MODE_1_CLOCK_TICKS; + gbLcdMode = 1; + + } else { + // go the the OAM being accessed mode + gbLcdTicks += GBLCD_MODE_2_CLOCK_TICKS; + gbLcdMode = 2; + + gbInt48Signal &= ~6; + if(register_STAT & 0x20) + { + // send LCD interrupt only if no interrupt 48h signal... + if (!gbInt48Signal) + { + register_IF |= 2; + gbInterruptLaunched |= 2; + } + gbInt48Signal |= 4; + } + gbInt48Signal &= ~1; + } + } + break; + case 1: + { + // V-Blank + // next mode is OAM being accessed mode + gbInt48Signal &= ~5; + if(register_STAT & 0x20) + { + // send LCD interrupt only if no interrupt 48h signal... + if (!gbInt48Signal) + { + register_IF |= 2; + gbInterruptLaunched |= 2; + if ((gbHardware & 0xa) && (IFF & 0x80)) + gbInterruptWait = 1; + } + gbInt48Signal |= 4; + } + gbInt48Signal &= ~2; + + gbLcdTicks += GBLCD_MODE_2_CLOCK_TICKS; + + gbLcdMode = 2; + register_LY = 0x00; + + } + break; + case 2: + { + + // OAM being accessed mode + // next mode is OAM and VRAM in use + if ((gbScreenOn) && (register_LCDC & 0x80)) + { + gbDrawSprites(false); + // Used to add a one tick delay when a window line is drawn. + //(fixes a part of Carmaggedon problem) + if((register_LCDC & 0x01 || gbCgbMode) && (register_LCDC & 0x20) && + (gbWindowLine != -2)) { + + int inUseRegister_WY = 0; + int tempgbWindowLine = gbWindowLine; + + if ((tempgbWindowLine == -1) || (tempgbWindowLine>144)) + { + inUseRegister_WY = oldRegister_WY; + if (register_LY>oldRegister_WY) + tempgbWindowLine = 146; + } + + int wy = inUseRegister_WY; + + if(register_LY >= inUseRegister_WY) { + + if (tempgbWindowLine == -1) + tempgbWindowLine = 0; + + int wx = register_WX; + wx -= 7; + if (wx<0) + wx = 0; + + if((wx <= 159) && (tempgbWindowLine <= 143)) + for (int i = wx; i<300; i++) + if (gbSpeed) + gbSpritesTicks[i]+=3; + else + gbSpritesTicks[i]+=1; + } + } + } + + gbInt48Signal &= ~7; + + gbLcdTicks += GBLCD_MODE_3_CLOCK_TICKS+gbSpritesTicks[299]; + gbLcdMode = 3; + } + break; + case 3: + { + // OAM and VRAM in use + // next mode is H-Blank + + + gbInt48Signal &= ~7; + if(register_STAT & 0x08) + { + // send LCD interrupt only if no interrupt 48h signal... + if (!(gbInt48Signal & 8)) + { + register_IF |= 2; + if ((gbHardware & 0xa) && (IFF & 0x80)) + gbInterruptWait = 1; + } + gbInt48Signal |= 1; + } + + gbLcdTicks += GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]; + + gbLcdMode = 0; + + // No HDMA during HALT ! + if(gbHdmaOn && (!(IFF & 0x80) || (register_IE & register_IF & 0x1f))) { + gbDoHdma(); + } + + } + break; + } + } + + + if ((gbLcdTicksDelayed <= 0) && (gbLCDChangeHappened)) { + int framesToSkip = systemFrameSkip; + if(speedup) + framesToSkip = 9; // try 6 FPS during speedup + //gbLcdTicksDelayed = gbLcdTicks+1; + gbLCDChangeHappened = false; + switch(gbLcdModeDelayed) { + case 0: + { + // H-Blank + + memset(gbSCYLine,gbSCYLine[299],sizeof(gbSCYLine)); + memset(gbSCXLine,gbSCXLine[299],sizeof(gbSCXLine)); + memset(gbBgpLine,gbBgpLine[299],sizeof(gbBgpLine)); + memset(gbObp0Line,gbObp0Line[299],sizeof(gbObp0Line)); + memset(gbObp1Line,gbObp1Line[299],sizeof(gbObp1Line)); + memset(gbSpritesTicks,gbSpritesTicks[299],sizeof(gbSpritesTicks)); + + if (gbWindowLine <0) + oldRegister_WY = register_WY; + // check if we reached the V-Blank period + if(register_LY == 144) { + // Yes, V-Blank + // set the LY increment counter + + if(register_LCDC & 0x80) { + if (gbHardware & 0xa) + { + + register_IF |= 1; // V-Blank interrupt + gbInterruptLaunched |=1; + } + + + } + + gbLcdTicksDelayed += GBLCD_MODE_1_CLOCK_TICKS; + gbLcdModeDelayed = 1; + + gbFrameCount++; + systemFrame(); + + if((gbFrameCount % 10) == 0) + system10Frames(60); + + if(gbFrameCount >= 60) { + u32 currentTime = systemGetClock(); + if(currentTime != gbLastTime) + systemShowSpeed(100000/(currentTime - gbLastTime)); + else + systemShowSpeed(0); + gbLastTime = currentTime; + gbFrameCount = 0; + } + + if(systemReadJoypads()) { + // read joystick + if(gbSgbMode && gbSgbMultiplayer) { + if(gbSgbFourPlayers) { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + gbJoymask[2] = systemReadJoypad(2); + gbJoymask[3] = systemReadJoypad(3); + } else { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + } + } else { + gbJoymask[0] = systemReadJoypad(-1); + } + } + int newmask = gbJoymask[0] & 255; + + if(gbRomType == 0x22) { + systemUpdateMotionSensor(); + } + + if(newmask) + { + gbMemory[0xff0f] |= 16; + } + + + newmask = (gbJoymask[0] >> 10); + + speedup = (newmask & 1) ? true : false; + gbCapture = (newmask & 2) ? true : false; + + if(gbCapture && !gbCapturePrevious) { + gbCaptureNumber++; + systemScreenCapture(gbCaptureNumber); + } + gbCapturePrevious = gbCapture; + + if(gbFrameSkipCount >= framesToSkip) { + + if(!gbSgbMask) + { + if (gbBorderOn) + gbSgbRenderBorder(); + //if (gbScreenOn) + systemDrawScreen(); + } + gbFrameSkipCount = 0; + } else + gbFrameSkipCount++; + + } else { + // go the the OAM being accessed mode + gbLcdTicksDelayed += GBLCD_MODE_2_CLOCK_TICKS; + gbLcdModeDelayed = 2; + gbInt48Signal &= ~3; + } + } + break; + case 1: + { + // V-Blank + // next mode is OAM being accessed mode + + // gbScreenOn = true; + + oldRegister_WY = register_WY; + + gbLcdTicksDelayed += GBLCD_MODE_2_CLOCK_TICKS; + gbLcdModeDelayed = 2; + + // reset the window line + gbWindowLine = -1; + } + break; + case 2: + { + // OAM being accessed mode + // next mode is OAM and VRAM in use + gbLcdTicksDelayed += GBLCD_MODE_3_CLOCK_TICKS+gbSpritesTicks[299]; + gbLcdModeDelayed = 3; + } + break; + case 3: + { + + // OAM and VRAM in use + // next mode is H-Blank + if((register_LY < 144) && (register_LCDC & 0x80) && gbScreenOn) { + if(!gbSgbMask) { + if(gbFrameSkipCount >= framesToSkip) { + if (!gbBlackScreen) + { + gbRenderLine(); + gbDrawSprites(true); + } + else if (gbBlackScreen) + { + u16 color = gbColorOption ? gbColorFilter[0] : 0; + if (!gbCgbMode) + color = gbColorOption ? gbColorFilter[gbPalette[3] & 0x7FFF] : + gbPalette[3] & 0x7FFF; + for(int i = 0; i < 160; i++) + { + gbLineMix[i] = color; + gbLineBuffer[i] = 0; + } + } + gbDrawLine(); + } + } + } + gbLcdTicksDelayed += GBLCD_MODE_0_CLOCK_TICKS-gbSpritesTicks[299]; + gbLcdModeDelayed = 0; + } + break; + } + } + + if ((gbLcdLYIncrementTicksDelayed <= 0) && (gbLYChangeHappened == true)) + { + + gbLYChangeHappened = false; + + if (!((gbLcdMode != 1) && (register_LY==0))) + { + { + gbInt48Signal &= ~8; + gbCompareLYToLYC(); + if ((gbInt48Signal == 8) && (!((register_LY == 0) && (gbHardware & 1)))) + gbInterruptLaunched |= 2; + if ((gbHardware & (gbSpeed ? 8 : 2)) && (register_LY==0) && ((register_STAT & 0x44) == 0x44) && (gbLcdLYIncrementTicksDelayed==0)) + { + gbInterruptWait = 1; + + } + } + } + gbLcdLYIncrementTicksDelayed += GBLY_INCREMENT_CLOCK_TICKS; + + if (gbLcdModeDelayed == 1) + { + + if(register_LY == 153) + gbLcdLYIncrementTicksDelayed -= GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; + else if(register_LY == 0) + gbLcdLYIncrementTicksDelayed += GBLY_INCREMENT_CLOCK_TICKS - gbLine99Ticks; + } + gbMemory[0xff0f] = register_IF; + gbMemory[0xff41] = register_STAT; + } + } + gbMemory[0xff0f] = register_IF; + gbMemory[0xff41] = register_STAT = (register_STAT & 0xfc) | gbLcdModeDelayed; + } + else + { + + // Used to update the screen with white lines when it's off. + // (it looks strange, but it's kinda accurate :p) + // You can try the Mario Demo Vx.x for exemple + // (check the bottom 2 lines while moving) + if (!gbWhiteScreen) + { + gbScreenTicks -= clockTicks; + gbLcdLYIncrementTicks -= clockTicks; + while (gbLcdLYIncrementTicks <=0) + { + register_LY = ((register_LY+1)%154); + gbLcdLYIncrementTicks+=GBLY_INCREMENT_CLOCK_TICKS; + } + if (gbScreenTicks <= 0) + { + gbWhiteScreen = 1; + u8 register_LYLcdOff = ((register_LY+154)%154); + for (register_LY=0;register_LY <= 0x90;register_LY++) + { + u16 color = gbColorOption ? gbColorFilter[0x7FFF] : + 0x7FFF; + if (!gbCgbMode) + color = gbColorOption ? gbColorFilter[gbPalette[0] & 0x7FFF] : + gbPalette[0] & 0x7FFF; + for(int i = 0; i < 160; i++) + { + gbLineMix[i] = color; + gbLineBuffer[i] = 0; + } + gbDrawLine(); + } + register_LY = register_LYLcdOff; + } + } + + if (gbWhiteScreen) + { + gbLcdLYIncrementTicks -= clockTicks; + + while (gbLcdLYIncrementTicks <=0) + { + register_LY = ((register_LY+1)%154); + gbLcdLYIncrementTicks+=GBLY_INCREMENT_CLOCK_TICKS; + if (register_LY<144) + { + + u16 color = gbColorOption ? gbColorFilter[0x7FFF] : + 0x7FFF; + if (!gbCgbMode) + color = gbColorOption ? gbColorFilter[gbPalette[0] & 0x7FFF] : + gbPalette[0] & 0x7FFF; + for(int i = 0; i < 160; i++) + { + gbLineMix[i] = color; + gbLineBuffer[i] = 0; + } + gbDrawLine(); + } + else if ((register_LY==144) && (!systemFrameSkip)) + { + int framesToSkip = systemFrameSkip; + if(speedup) + framesToSkip = 9; // try 6 FPS during speedup + if((gbFrameSkipCount >= framesToSkip) || (gbWhiteScreen == 1)) { + gbWhiteScreen = 2; + + if(!gbSgbMask) + { + if (gbBorderOn) + gbSgbRenderBorder(); + //if (gbScreenOn) + systemDrawScreen(); + } + } + if(systemReadJoypads()) { + // read joystick + if(gbSgbMode && gbSgbMultiplayer) { + if(gbSgbFourPlayers) { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + gbJoymask[2] = systemReadJoypad(2); + gbJoymask[3] = systemReadJoypad(3); + } else { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + } + } else { + gbJoymask[0] = systemReadJoypad(-1); + } + } + gbFrameCount++; + + systemFrame(); + + if((gbFrameCount % 10) == 0) + system10Frames(60); + + if(gbFrameCount >= 60) { + u32 currentTime = systemGetClock(); + if(currentTime != gbLastTime) + systemShowSpeed(100000/(currentTime - gbLastTime)); + else + systemShowSpeed(0); + gbLastTime = currentTime; + gbFrameCount = 0; + } + } + } + } + } + + gbMemory[0xff41] = register_STAT; + + // serial emulation + if(gbSerialOn) { +#ifdef LINK_EMULATION + if(linkConnected) { + gbSerialTicks -= clockTicks; + + while(gbSerialTicks <= 0) { + // increment number of shifted bits + gbSerialBits++; + linkProc(); + if(gbSerialOn && (gbMemory[0xff02] & 1)) { + if(gbSerialBits == 8) { + gbSerialBits = 0; + gbMemory[0xff01] = 0xff; + gbMemory[0xff02] &= 0x7f; + gbSerialOn = 0; + gbMemory[0xff0f] = register_IF |= 8; + gbSerialTicks = 0; + } + } + gbSerialTicks += GBSERIAL_CLOCK_TICKS; + } + } else { +#endif + if(gbMemory[0xff02] & 1) { + gbSerialTicks -= clockTicks; + + // overflow + while(gbSerialTicks <= 0) { + // shift serial byte to right and put a 1 bit in its place + // gbMemory[0xff01] = 0x80 | (gbMemory[0xff01]>>1); + // increment number of shifted bits + gbSerialBits++; + if(gbSerialBits == 8) { + // end of transmission + if(gbSerialFunction) // external device + gbMemory[0xff01] = gbSerialFunction(gbMemory[0xff01]); + else + gbMemory[0xff01] = 0xff; + gbSerialTicks = 0; + gbMemory[0xff02] &= 0x7f; + gbSerialOn = 0; + gbMemory[0xff0f] = register_IF |= 8; + gbSerialBits = 0; + } else + gbSerialTicks += GBSERIAL_CLOCK_TICKS; + } + } +#ifdef LINK_EMULATION + } +#endif + } + + + soundTicks -= clockTicks; + if ( !gbSpeed ) + soundTicks -= clockTicks; + + while(soundTicks < 0) { + soundTicks += SOUND_CLOCK_TICKS; + + gbSoundTick(); + } + + + // timer emulation + + if(gbTimerOn) { + gbTimerTicks= ((gbInternalTimer) & gbTimerMask[gbTimerMode])+1-clockTicks; + + while(gbTimerTicks <= 0) { + register_TIMA++; + // timer overflow! + if((register_TIMA & 0xff) == 0) { + // reload timer modulo + register_TIMA = register_TMA; + // flag interrupt + gbMemory[0xff0f] = register_IF |= 4; + } + gbTimerTicks += gbTimerClockTicks; + } + gbTimerOnChange = false; + gbTimerModeChange = false; + + gbMemory[0xff05] = register_TIMA; + + } + + gbInternalTimer -= clockTicks; + while (gbInternalTimer<0) + gbInternalTimer+=0x100; + + /* + if(soundOffFlag) { + if(synchronize && !speedup) { + synchronizeTicks -= clockTicks; + + while(synchronizeTicks < 0) { + synchronizeTicks += SYNCHRONIZE_CLOCK_TICKS; + + DWORD now = timeGetTime(); + gbElapsedTime += (now - timeNow); + + if(gbElapsedTime < 50) { + DWORD diff = 50 - gbElapsedTime; + Sleep(diff); + timeNow = timeGetTime(); + elapsedTime = timeNow - now - diff; + if((int)elapsedTime < 0) + elapsedTime = 0; + } else { + timeNow = timeGetTime(); + elapsedTime = 0; + } + } + } + } + */ + + clockTicks = 0; + + if (gbIntBreak == 1) + { + gbIntBreak = 0; + if ((register_IE & register_IF & gbInterruptLaunched & 0x3) && + ((IFF & 0x81) == 1) && (!gbInterruptWait) && (execute)) + { + gbIntBreak = 2; + PC.W = oldPCW; + execute = false; + gbOldClockTicks = 0; + } + if (gbOldClockTicks) + { + clockTicks = gbOldClockTicks; + gbOldClockTicks = 0; + goto gbRedoLoop; + } + } + + // Executes the opcode(s), and apply the instruction's remaining clockTicks (if any). + if (execute) + { + switch(opcode1) { + case 0xCB: + // extended opcode + switch(opcode2) { +#include "gbCodesCB.h" + } + break; +#include "gbCodes.h" + } + execute = false; + + if (clockTicks) + { + gbDmaTicks += clockTicks; + clockTicks = 0; + } + } + + if (gbDmaTicks) + { + clockTicks = gbGetNextEvent(gbDmaTicks); + + if (clockTicks<=gbDmaTicks) + gbDmaTicks -= clockTicks; + else + { + clockTicks = gbDmaTicks; + gbDmaTicks = 0; + } + + goto gbRedoLoop; + } + + + // Remove the 'if an IE is pending' flag if IE has finished being executed. + if ((IFF & 0x40) && !(IFF & 0x30)) + IFF &= 0x81; + + + + if ((register_IE & register_IF & 0x1f) && (IFF & 0x81) && (!gbInterruptWait)) + { + + if (IFF & 1) + { + // Add 5 ticks for the interrupt execution time + gbDmaTicks += 5; + + if (gbIntBreak == 2) + { + gbDmaTicks--; + gbIntBreak = 0; + } + + + if(register_IF & register_IE & 1) + gbVblank_interrupt(); + else if(register_IF & register_IE & 2) + gbLcd_interrupt(); + else if(register_IF & register_IE & 4) + gbTimer_interrupt(); + else if(register_IF & register_IE & 8) + gbSerial_interrupt(); + else if(register_IF & register_IE & 16) + gbJoypad_interrupt(); + } + + IFF &= ~0x81; + } + + if (IFF & 0x08) + IFF &=~0x79; + + // Used to apply the interrupt's execution time. + if (gbDmaTicks) + { + clockTicks = gbGetNextEvent(gbDmaTicks); + + if (clockTicks<=gbDmaTicks) + gbDmaTicks -= clockTicks; + else + { + clockTicks = gbDmaTicks; + gbDmaTicks = 0; + } + goto gbRedoLoop; + } + + + gbBlackScreen = false; + + if((ticksToStop <= 0)) { + if(!(register_LCDC & 0x80)) { + if(systemReadJoypads()) { + // read joystick + if(gbSgbMode && gbSgbMultiplayer) { + if(gbSgbFourPlayers) { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + gbJoymask[2] = systemReadJoypad(2); + gbJoymask[3] = systemReadJoypad(3); + } else { + gbJoymask[0] = systemReadJoypad(0); + gbJoymask[1] = systemReadJoypad(1); + } + } else { + gbJoymask[0] = systemReadJoypad(-1); + } + } + } + return; + } + } +} + +struct EmulatedSystem GBSystem = { + // emuMain + gbEmulate, + // emuReset + gbReset, + // emuCleanUp + gbCleanUp, + // emuReadBattery + gbReadBatteryFile, + // emuWriteBattery + gbWriteBatteryFile, + // emuReadState + gbReadSaveState, + // emuWriteState + gbWriteSaveState, + // emuReadMemState + gbReadMemSaveState, + // emuWriteMemState + gbWriteMemSaveState, + // emuWritePNG + gbWritePNGFile, + // emuWriteBMP + gbWriteBMPFile, + // emuUpdateCPSR + NULL, + // emuHasDebugger + false, + // emuCount +#ifdef FINAL_VERSION + 70000/4, +#else + 1000, +#endif +}; diff --git a/src/dmg/gb.h b/src/dmg/gb.h new file mode 100644 index 00000000..1ae77248 --- /dev/null +++ b/src/dmg/gb.h @@ -0,0 +1,64 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005-2006 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef VBA_GB_GB_H +#define VBA_GB_GB_H + +#define C_FLAG 0x10 +#define H_FLAG 0x20 +#define N_FLAG 0x40 +#define Z_FLAG 0x80 + +typedef union { + struct { +#ifdef WORDS_BIGENDIAN + u8 B1, B0; +#else + u8 B0,B1; +#endif + } B; + u16 W; +} gbRegister; + +extern bool gbLoadRom(const char *); +extern void gbEmulate(int); +extern void gbWriteMemory(register u16, register u8); +extern void gbDrawLine(); +extern bool gbIsGameboyRom(const char *); +extern void gbSoundReset(); +extern void gbSoundSetQuality(int); +extern void gbGetHardwareType(); +extern void gbReset(); +extern void gbCleanUp(); +extern void gbCPUInit(const char *,bool); +extern bool gbWriteBatteryFile(const char *); +extern bool gbWriteBatteryFile(const char *, bool); +extern bool gbReadBatteryFile(const char *); +extern bool gbWriteSaveState(const char *); +extern bool gbWriteMemSaveState(char *, int); +extern bool gbReadSaveState(const char *); +extern bool gbReadMemSaveState(char *, int); +extern void gbSgbRenderBorder(); +extern bool gbWritePNGFile(const char *); +extern bool gbWriteBMPFile(const char *); +extern bool gbReadGSASnapshot(const char *); + +extern struct EmulatedSystem GBSystem; + +#endif diff --git a/src/dmg/gbCheats.cpp b/src/dmg/gbCheats.cpp new file mode 100644 index 00000000..e7c44282 --- /dev/null +++ b/src/dmg/gbCheats.cpp @@ -0,0 +1,522 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include +#include +#include + +#include "../System.h" +#include "../NLS.h" +#include "../Util.h" + +#include "gbCheats.h" +#include "gbGlobals.h" +#include "gb.h" + +gbCheat gbCheatList[100]; +int gbCheatNumber = 0; +int gbNextCheat = 0; +bool gbCheatMap[0x10000]; + +extern bool cheatsEnabled; + +#define GBCHEAT_IS_HEX(a) ( ((a)>='A' && (a) <='F') || ((a) >='0' && (a) <= '9')) +#define GBCHEAT_HEX_VALUE(a) ( (a) >= 'A' ? (a) - 'A' + 10 : (a) - '0') + +void gbCheatUpdateMap() +{ + memset(gbCheatMap, 0, 0x10000); + + for(int i = 0; i < gbCheatNumber; i++) { + if(gbCheatList[i].enabled) + gbCheatMap[gbCheatList[i].address] = true; + } +} + +void gbCheatsSaveGame(gzFile gzFile) +{ + utilWriteInt(gzFile, gbCheatNumber); + if(gbCheatNumber>0) + utilGzWrite(gzFile, &gbCheatList[0], sizeof(gbCheat)*gbCheatNumber); +} + +void gbCheatsReadGame(gzFile gzFile, int version) +{ + if(version <= 8) { + int gbGgOn = utilReadInt(gzFile); + + if(gbGgOn) { + int n = utilReadInt(gzFile); + gbXxCheat tmpCheat; + for(int i = 0; i < n; i++) { + utilGzRead(gzFile,&tmpCheat, sizeof(gbXxCheat)); + gbAddGgCheat(tmpCheat.cheatCode, tmpCheat.cheatDesc); + } + } + + int gbGsOn = utilReadInt(gzFile); + + if(gbGsOn) { + int n = utilReadInt(gzFile); + gbXxCheat tmpCheat; + for(int i = 0; i < n; i++) { + utilGzRead(gzFile,&tmpCheat, sizeof(gbXxCheat)); + gbAddGsCheat(tmpCheat.cheatCode, tmpCheat.cheatDesc); + } + } + } else { + gbCheatNumber = utilReadInt(gzFile); + + if(gbCheatNumber>0) { + utilGzRead(gzFile, &gbCheatList[0], sizeof(gbCheat)*gbCheatNumber); + } + } + + gbCheatUpdateMap(); +} + +void gbCheatsSaveCheatList(const char *file) +{ + if(gbCheatNumber == 0) + return; + FILE *f = fopen(file, "wb"); + if(f == NULL) + return; + int version = 1; + fwrite(&version, 1, sizeof(version), f); + int type = 1; + fwrite(&type, 1, sizeof(type), f); + fwrite(&gbCheatNumber, 1, sizeof(gbCheatNumber), f); + fwrite(gbCheatList, 1, sizeof(gbCheatList), f); + fclose(f); +} + +bool gbCheatsLoadCheatList(const char *file) +{ + gbCheatNumber = 0; + + gbCheatUpdateMap(); + + int count = 0; + + FILE *f = fopen(file, "rb"); + + if(f == NULL) + return false; + + int version = 0; + + if(fread(&version, 1, sizeof(version), f) != sizeof(version)) { + fclose(f); + return false; + } + + if(version != 1) { + systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_VERSION, + N_("Unsupported cheat list version %d"), version); + fclose(f); + return false; + } + + int type = 0; + if(fread(&type, 1, sizeof(type), f) != sizeof(type)) { + fclose(f); + return false; + } + + if(type != 1) { + systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_TYPE, + N_("Unsupported cheat list type %d"), type); + fclose(f); + return false; + } + + if(fread(&count, 1, sizeof(count), f) != sizeof(count)) { + fclose(f); + return false; + } + + if(fread(gbCheatList, 1, sizeof(gbCheatList), f) != sizeof(gbCheatList)) { + fclose(f); + return false; + } + + gbCheatNumber = count; + gbCheatUpdateMap(); + + return true; +} + +bool gbVerifyGsCode(const char *code) +{ + size_t len = strlen(code); + + if(len == 0) + return true; + + if(len != 8) + return false; + + for(int i = 0; i < 8; i++) + if(!GBCHEAT_IS_HEX(code[i])) + return false; + + int address = GBCHEAT_HEX_VALUE(code[6]) << 12 | + GBCHEAT_HEX_VALUE(code[7]) << 8 | + GBCHEAT_HEX_VALUE(code[4]) << 4 | + GBCHEAT_HEX_VALUE(code[5]); + + return true; +} + +bool gbAddGsCheat(const char *code, const char *desc) +{ + if(gbCheatNumber > 99) { + systemMessage(MSG_MAXIMUM_NUMBER_OF_CHEATS, + N_("Maximum number of cheats reached.")); + return false; + } + + if(!gbVerifyGsCode(code)) { + systemMessage(MSG_INVALID_GAMESHARK_CODE, + N_("Invalid GameShark code: %s"), code); + return false; + } + + int i = gbCheatNumber; + + strcpy(gbCheatList[i].cheatCode, code); + strcpy(gbCheatList[i].cheatDesc, desc); + + gbCheatList[i].code = GBCHEAT_HEX_VALUE(code[0]) << 4 | + GBCHEAT_HEX_VALUE(code[1]); + + gbCheatList[i].value = GBCHEAT_HEX_VALUE(code[2]) << 4 | + GBCHEAT_HEX_VALUE(code[3]); + + gbCheatList[i].address = GBCHEAT_HEX_VALUE(code[6]) << 12 | + GBCHEAT_HEX_VALUE(code[7]) << 8 | + GBCHEAT_HEX_VALUE(code[4]) << 4 | + GBCHEAT_HEX_VALUE(code[5]); + + gbCheatList[i].compare = 0; + + gbCheatList[i].enabled = true; + + int gsCode = gbCheatList[i].code; + + if ((gsCode !=1) && ((gsCode & 0xF0) !=0x80) && ((gsCode & 0xF0) !=0x90) && + ((gsCode & 0xF0) !=0xA0) && ((gsCode) !=0xF0) && ((gsCode) !=0xF1)) + systemMessage(MSG_WRONG_GAMESHARK_CODE, + N_("Wrong GameShark code type : %s"), code); + else if (((gsCode & 0xF0) ==0xA0) || ((gsCode) ==0xF0) || ((gsCode) ==0xF1)) + systemMessage(MSG_UNSUPPORTED_GAMESHARK_CODE, + N_("Unsupported GameShark code type : %s"), code); + + gbCheatNumber++; + + return true; +} + +bool gbVerifyGgCode(const char *code) +{ + size_t len = strlen(code); + + if(len != 11 && + len != 7 && + len != 6 && + len != 0) + return false; + + if(len == 0) + return true; + + if(!GBCHEAT_IS_HEX(code[0])) + return false; + if(!GBCHEAT_IS_HEX(code[1])) + return false; + if(!GBCHEAT_IS_HEX(code[2])) + return false; + if(code[3] != '-') + return false; + if(!GBCHEAT_IS_HEX(code[4])) + return false; + if(!GBCHEAT_IS_HEX(code[5])) + return false; + if(!GBCHEAT_IS_HEX(code[6])) + return false; + if(code[7] != 0) { + if(code[7] != '-') + return false; + if(code[8] != 0) { + if(!GBCHEAT_IS_HEX(code[8])) + return false; + if(!GBCHEAT_IS_HEX(code[9])) + return false; + if(!GBCHEAT_IS_HEX(code[10])) + return false; + } + } + + // int replace = (GBCHEAT_HEX_VALUE(code[0]) << 4) + + // GBCHEAT_HEX_VALUE(code[1]); + + int address = (GBCHEAT_HEX_VALUE(code[2]) << 8) + + (GBCHEAT_HEX_VALUE(code[4]) << 4) + + (GBCHEAT_HEX_VALUE(code[5])) + + ((GBCHEAT_HEX_VALUE(code[6]) ^ 0x0f) << 12); + + if(address >= 0x8000 && address <= 0x9fff) + return false; + + if(address >= 0xc000) + return false; + + if(code[7] == 0 || code[8] == '0') + return true; + + int compare = (GBCHEAT_HEX_VALUE(code[8]) << 4) + + (GBCHEAT_HEX_VALUE(code[10])); + compare = compare ^ 0xff; + compare = (compare >> 2) | ( (compare << 6) & 0xc0); + compare ^= 0x45; + + int cloak = (GBCHEAT_HEX_VALUE(code[8])) ^ (GBCHEAT_HEX_VALUE(code[9])); + + if(cloak >=1 && cloak <= 7) + return false; + + return true; +} + +bool gbAddGgCheat(const char *code, const char *desc) +{ + if(gbCheatNumber > 99) { + systemMessage(MSG_MAXIMUM_NUMBER_OF_CHEATS, + N_("Maximum number of cheats reached.")); + return false; + } + + if(!gbVerifyGgCode(code)) { + systemMessage(MSG_INVALID_GAMEGENIE_CODE, + N_("Invalid GameGenie code: %s"), code); + return false; + } + + int i = gbCheatNumber; + + size_t len = strlen(code); + + strcpy(gbCheatList[i].cheatCode, code); + strcpy(gbCheatList[i].cheatDesc, desc); + + gbCheatList[i].code = 0x101; + gbCheatList[i].value = (GBCHEAT_HEX_VALUE(code[0]) << 4) + + GBCHEAT_HEX_VALUE(code[1]); + + gbCheatList[i].address = (GBCHEAT_HEX_VALUE(code[2]) << 8) + + (GBCHEAT_HEX_VALUE(code[4]) << 4) + + (GBCHEAT_HEX_VALUE(code[5])) + + ((GBCHEAT_HEX_VALUE(code[6]) ^ 0x0f) << 12); + + gbCheatList[i].compare = 0; + + if(len != 7 && len != 8) { + + int compare = (GBCHEAT_HEX_VALUE(code[8]) << 4) + + (GBCHEAT_HEX_VALUE(code[10])); + compare = compare ^ 0xff; + compare = (compare >> 2) | ( (compare << 6) & 0xc0); + compare ^= 0x45; + + gbCheatList[i].compare = compare; + //gbCheatList[i].code = 0; + gbCheatList[i].code = 0x100; // fix for compare value + + } + + + gbCheatList[i].enabled = true; + + gbCheatMap[gbCheatList[i].address] = true; + + gbCheatNumber++; + + return true; +} + +void gbCheatRemove(int i) +{ + if(i < 0 || i >= gbCheatNumber) { + systemMessage(MSG_INVALID_CHEAT_TO_REMOVE, + N_("Invalid cheat to remove %d"), i); + return; + } + + if((i+1) < gbCheatNumber) { + memcpy(&gbCheatList[i], &gbCheatList[i+1], sizeof(gbCheat)* + (gbCheatNumber-i-1)); + } + + gbCheatNumber--; + + gbCheatUpdateMap(); +} + +void gbCheatRemoveAll() +{ + gbCheatNumber = 0; + gbCheatUpdateMap(); +} + +void gbCheatEnable(int i) +{ + if(i >=0 && i < gbCheatNumber) { + if(!gbCheatList[i].enabled) { + gbCheatList[i].enabled = true; + gbCheatUpdateMap(); + } + } +} + +void gbCheatDisable(int i) +{ + if(i >=0 && i < gbCheatNumber) { + if(gbCheatList[i].enabled) { + gbCheatList[i].enabled = false; + gbCheatUpdateMap(); + } + } +} + +bool gbCheatReadGSCodeFile(const char *fileName) +{ + FILE *file = fopen(fileName, "rb"); + + if(!file) { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); + return false; + } + + fseek(file, 0x18, SEEK_SET); + int count = 0; + fread(&count, 1, 2, file); + int dummy = 0; + gbCheatRemoveAll(); + char desc[13]; + char code[9]; + int i; + for(i = 0; i < count; i++) { + fread(&dummy, 1, 2, file); + fread(desc, 1, 12, file); + desc[12] = 0; + fread(code, 1, 8, file); + code[8] = 0; + gbAddGsCheat(code, desc); + } + + for(i = 0; i < gbCheatNumber; i++) + gbCheatDisable(i); + + fclose(file); + return true; +} + +// Used to emulated GG codes +u8 gbCheatRead(u16 address) +{ + if(!cheatsEnabled) + return gbMemoryMap[address>>12][address & 0xFFF]; + + for(int i = 0; i < gbCheatNumber; i++) { + if(gbCheatList[i].enabled && gbCheatList[i].address == address) { + switch(gbCheatList[i].code) { + case 0x100: // GameGenie support + if(gbMemoryMap[address>>12][address&0xFFF] == gbCheatList[i].compare) + return gbCheatList[i].value; + break; + case 0x101: // GameGenie 6 digits code support + return gbCheatList[i].value; + break; + } + } + } + return gbMemoryMap[address>>12][address&0xFFF]; +} + + +// Used to emulate GS codes. +void gbCheatWrite(bool reboot) +{ + if(cheatsEnabled) + { + u16 address = 0; + + if (gbNextCheat >= gbCheatNumber) + gbNextCheat = 0; + + for(int i = gbNextCheat; i < gbCheatNumber; i++) { + if(gbCheatList[i].enabled) { + address = gbCheatList[i].address; + if ((!reboot) && (address >= 0x8000) && !((address>=0xA000) && (address<0xC000))) + { // These codes are executed one per one, at each Vblank + switch(gbCheatList[i].code) { + case 0x01: + gbWriteMemory(address, gbCheatList[i].value); + gbNextCheat = i+1; + return; + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9A: + case 0x9B: + case 0x9C: + case 0x9D: + case 0x9E: + case 0x9F: + int oldbank = gbMemory[0xff70]; + gbWriteMemory(0xff70, gbCheatList[i].code & 0xf); + gbWriteMemory(address, gbCheatList[i].value); + gbWriteMemory(0xff70, oldbank); + gbNextCheat = i+1; + return; + } + } + else // These codes are only executed when the game is booted + { + switch(gbCheatList[i].code & 0xF0) { + case 0x80: + gbWriteMemory(0x0000, 0x0A); + gbWriteMemory(0x4000, gbCheatList[i].value & 0xF); + gbWriteMemory(address, gbCheatList[i].value); + gbNextCheat = i+1; + return; + } + } + } + } + } +} diff --git a/src/dmg/gbCheats.h b/src/dmg/gbCheats.h new file mode 100644 index 00000000..f0ad8b26 --- /dev/null +++ b/src/dmg/gbCheats.h @@ -0,0 +1,62 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef __VBA_GB_GBCHEATS_H +#define __VBA_GB_GBCHEATS_H + +#include "../System.h" + +struct gbXxCheat { + char cheatDesc[100]; + char cheatCode[20]; +}; + +struct gbCheat { + char cheatCode[20]; + char cheatDesc[32]; + u16 address; + int code; + u8 compare; + u8 value; + bool enabled; +}; + +void gbCheatsSaveGame(gzFile); +void gbCheatsReadGame(gzFile, int); +void gbCheatsSaveCheatList(const char *); +bool gbCheatsLoadCheatList(const char *); +bool gbCheatReadGSCodeFile(const char *); + +bool gbAddGsCheat(const char *, const char*); +bool gbAddGgCheat(const char *, const char*); +void gbCheatRemove(int); +void gbCheatRemoveAll(); +void gbCheatEnable(int); +void gbCheatDisable(int); +u8 gbCheatRead(u16); +void gbCheatWrite(bool); +bool gbVerifyGsCode(const char *code); +bool gbVerifyGgCode(const char *code); + + +extern int gbCheatNumber; +extern gbCheat gbCheatList[100]; +extern bool gbCheatMap[0x10000]; +#endif + diff --git a/src/dmg/gbCodes.h b/src/dmg/gbCodes.h new file mode 100644 index 00000000..ff364ee1 --- /dev/null +++ b/src/dmg/gbCodes.h @@ -0,0 +1,1460 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005-2006 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + case 0x00: + // NOP + break; + case 0x01: + // LD BC, NNNN + BC.B.B0=gbReadOpcode(PC.W++); + BC.B.B1=gbReadOpcode(PC.W++); + break; + case 0x02: + // LD (BC),A + gbWriteMemory(BC.W,AF.B.B1); + break; + case 0x03: + // INC BC + BC.W++; + break; + case 0x04: + // INC B + BC.B.B1++; + AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[BC.B.B1]| (BC.B.B1&0x0F? 0:H_FLAG); + break; + case 0x05: + // DEC B + BC.B.B1--; + AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[BC.B.B1]| + ((BC.B.B1&0x0F)==0x0F? H_FLAG:0); + break; + case 0x06: + // LD B, NN + BC.B.B1=gbReadOpcode(PC.W++); + break; + case 0x07: + // RLCA + tempValue=AF.B.B1&0x80? C_FLAG:0; + AF.B.B1=(AF.B.B1<<1)|(AF.B.B1>>7); + AF.B.B0=tempValue; + break; + case 0x08: + // LD (NNNN), SP + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W++); + gbWriteMemory(tempRegister.W++,SP.B.B0); + gbWriteMemory(tempRegister.W,SP.B.B1); + break; + case 0x09: + // ADD HL,BC + tempRegister.W=(HL.W+BC.W)&0xFFFF; + AF.B.B0= (AF.B.B0 & Z_FLAG)| ((HL.W^BC.W^tempRegister.W)&0x1000? H_FLAG:0)| + (((long)HL.W+(long)BC.W)&0x10000? C_FLAG:0); + HL.W=tempRegister.W; + break; + case 0x0a: + // LD A,(BC) + AF.B.B1=gbReadMemory(BC.W); + break; + case 0x0b: + // DEC BC + BC.W--; + break; + case 0x0c: + // INC C + BC.B.B0++; + AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[BC.B.B0]| (BC.B.B0&0x0F? 0:H_FLAG); + break; + case 0x0d: + // DEC C + BC.B.B0--; + AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[BC.B.B0]| + ((BC.B.B0&0x0F)==0x0F? H_FLAG:0); + break; + case 0x0e: + // LD C, NN + BC.B.B0=gbReadOpcode(PC.W++); + break; + case 0x0f: + // RRCA + tempValue=AF.B.B1&0x01; + AF.B.B1=(AF.B.B1>>1)|(tempValue? 0x80:0); + AF.B.B0=(tempValue<<4); + break; + case 0x10: + // STOP + opcode = gbReadOpcode(PC.W++); + if(gbCgbMode) { + if(gbMemory[0xff4d] & 1) { + + gbSpeedSwitch(); + //clockTicks += 228*144-(gbSpeed ? 62 : 63); + + if(gbSpeed == 0) + gbMemory[0xff4d] = 0x00; + else + gbMemory[0xff4d] = 0x80; + } + } + break; + case 0x11: + // LD DE, NNNN + DE.B.B0=gbReadOpcode(PC.W++); + DE.B.B1=gbReadOpcode(PC.W++); + break; + case 0x12: + // LD (DE),A + gbWriteMemory(DE.W,AF.B.B1); + break; + case 0x13: + // INC DE + DE.W++; + break; + case 0x14: + // INC D + DE.B.B1++; + AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[DE.B.B1]| (DE.B.B1&0x0F? 0:H_FLAG); + break; + case 0x15: + // DEC D + DE.B.B1--; + AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[DE.B.B1]| + ((DE.B.B1&0x0F)==0x0F? H_FLAG:0); + break; + case 0x16: + // LD D,NN + DE.B.B1=gbReadOpcode(PC.W++); + break; + case 0x17: + // RLA + tempValue=AF.B.B1&0x80? C_FLAG:0; + AF.B.B1=(AF.B.B1<<1)|((AF.B.B0&C_FLAG)>>4); + AF.B.B0=tempValue; + break; + case 0x18: + // JR NN + PC.W+=(s8)gbReadOpcode(PC.W)+1; + break; + case 0x19: + // ADD HL,DE + tempRegister.W=(HL.W+DE.W)&0xFFFF; + AF.B.B0= (AF.B.B0 & Z_FLAG)| ((HL.W^DE.W^tempRegister.W)&0x1000? H_FLAG:0)| + (((long)HL.W+(long)DE.W)&0x10000? C_FLAG:0); + HL.W=tempRegister.W; + break; + case 0x1a: + // LD A,(DE) + AF.B.B1=gbReadMemory(DE.W); + break; + case 0x1b: + // DEC DE + DE.W--; + break; + case 0x1c: + // INC E + DE.B.B0++; + AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[DE.B.B0]| (DE.B.B0&0x0F? 0:H_FLAG); + break; + case 0x1d: + // DEC E + DE.B.B0--; + AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[DE.B.B0]| + ((DE.B.B0&0x0F)==0x0F? H_FLAG:0); + break; + case 0x1e: + // LD E,NN + DE.B.B0=gbReadOpcode(PC.W++); + break; + case 0x1f: + // RRA + tempValue=AF.B.B1&0x01; + AF.B.B1=(AF.B.B1>>1)|(AF.B.B0&C_FLAG? 0x80:0); + AF.B.B0=(tempValue<<4); + break; + case 0x20: + // JR NZ,NN + if(AF.B.B0&Z_FLAG) + PC.W++; + else { + PC.W+=(s8)gbReadOpcode(PC.W)+1; + clockTicks++; + } + break; + case 0x21: + // LD HL,NNNN + HL.B.B0=gbReadOpcode(PC.W++); + HL.B.B1=gbReadOpcode(PC.W++); + break; + case 0x22: + // LDI (HL),A + gbWriteMemory(HL.W++,AF.B.B1); + break; + case 0x23: + // INC HL + HL.W++; + break; + case 0x24: + // INC H + HL.B.B1++; + AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[HL.B.B1]| (HL.B.B1&0x0F? 0:H_FLAG); + break; + case 0x25: + // DEC H + HL.B.B1--; + AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[HL.B.B1]| + ((HL.B.B1&0x0F)==0x0F? H_FLAG:0); + break; + case 0x26: + // LD H,NN + HL.B.B1=gbReadOpcode(PC.W++); + break; + case 0x27: + // DAA + tempRegister.W=AF.B.B1; + if(AF.B.B0&C_FLAG) tempRegister.W|=256; + if(AF.B.B0&H_FLAG) tempRegister.W|=512; + if(AF.B.B0&N_FLAG) tempRegister.W|=1024; + AF.W=DAATable[tempRegister.W]; + break; + case 0x28: + // JR Z,NN + if(AF.B.B0&Z_FLAG) { + PC.W+=(s8)gbReadOpcode(PC.W)+1; + clockTicks++; + } else + PC.W++; + break; + case 0x29: + // ADD HL,HL + tempRegister.W=(HL.W+HL.W)&0xFFFF; AF.B.B0= (AF.B.B0 & Z_FLAG)| + ((HL.W^HL.W^tempRegister.W)&0x1000? H_FLAG:0)| + (((long)HL.W+(long)HL.W)&0x10000? C_FLAG:0); + HL.W=tempRegister.W; + break; + case 0x2a: + // LDI A,(HL) + AF.B.B1 = gbReadMemory(HL.W++); + break; + case 0x2b: + // DEC HL + HL.W--; + break; + case 0x2c: + // INC L + HL.B.B0++; + AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[HL.B.B0]| (HL.B.B0&0x0F? 0:H_FLAG); + break; + case 0x2d: + // DEC L + HL.B.B0--; + AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[HL.B.B0]| + ((HL.B.B0&0x0F)==0x0F? H_FLAG:0); + break; + case 0x2e: + // LD L,NN + HL.B.B0=gbReadOpcode(PC.W++); + break; + case 0x2f: + // CPL + AF.B.B1 ^= 255; + AF.B.B0|=N_FLAG|H_FLAG; + break; + case 0x30: + // JR NC,NN + if(AF.B.B0&C_FLAG) + PC.W++; + else { + PC.W+=(s8)gbReadOpcode(PC.W)+1; + clockTicks++; + } + break; + case 0x31: + // LD SP,NNNN + SP.B.B0=gbReadOpcode(PC.W++); + SP.B.B1=gbReadOpcode(PC.W++); + break; + case 0x32: + // LDD (HL),A + gbWriteMemory(HL.W--,AF.B.B1); + break; + case 0x33: + // INC SP + SP.W++; + break; + case 0x34: + // INC (HL) + tempValue=gbReadMemory(HL.W)+1; + AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[tempValue]| (tempValue&0x0F? 0:H_FLAG); + gbWriteMemory(HL.W,tempValue); + break; + case 0x35: + // DEC (HL) + tempValue=gbReadMemory(HL.W)-1; + AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[tempValue]| + ((tempValue&0x0F)==0x0F? H_FLAG:0);gbWriteMemory(HL.W,tempValue); + break; + case 0x36: + // LD (HL),NN + gbWriteMemory(HL.W,gbReadOpcode(PC.W++)); + break; + case 0x37: + // SCF + AF.B.B0 = AF.B.B0 & Z_FLAG | C_FLAG; + break; +case 0x38: + // JR C,NN + if(AF.B.B0&C_FLAG) { + PC.W+=(s8)gbReadOpcode(PC.W)+1; + clockTicks ++; + } else + PC.W++; + break; + case 0x39: + // ADD HL,SP + tempRegister.W=(HL.W+SP.W)&0xFFFF; + AF.B.B0= (AF.B.B0 & Z_FLAG)| ((HL.W^SP.W^tempRegister.W)&0x1000? H_FLAG:0)| + (((long)HL.W+(long)SP.W)&0x10000? C_FLAG:0); + HL.W=tempRegister.W; + break; + case 0x3a: + // LDD A,(HL) + AF.B.B1 = gbReadMemory(HL.W--); + break; + case 0x3b: + // DEC SP + SP.W--; + break; + case 0x3c: + // INC A + AF.B.B1++; + AF.B.B0= (AF.B.B0 & C_FLAG)|ZeroTable[AF.B.B1]| (AF.B.B1&0x0F? 0:H_FLAG); + break; + case 0x3d: + // DEC A + AF.B.B1--; + AF.B.B0= N_FLAG|(AF.B.B0 & C_FLAG)|ZeroTable[AF.B.B1]| + ((AF.B.B1&0x0F)==0x0F? H_FLAG:0); + break; + case 0x3e: + // LD A,NN + AF.B.B1=gbReadOpcode(PC.W++); + break; + case 0x3f: + // CCF + AF.B.B0^=C_FLAG;AF.B.B0&=~(N_FLAG|H_FLAG); + break; + case 0x40: + // LD B,B + BC.B.B1=BC.B.B1; + break; + case 0x41: + // LD B,C + BC.B.B1=BC.B.B0; + break; + case 0x42: + // LD B,D + BC.B.B1=DE.B.B1; + break; + case 0x43: + // LD B,E + BC.B.B1=DE.B.B0; + break; + case 0x44: + // LD B,H + BC.B.B1=HL.B.B1; + break; + case 0x45: + // LD B,L + BC.B.B1=HL.B.B0; + break; + case 0x46: + // LD B,(HL) + BC.B.B1=gbReadMemory(HL.W); + break; + case 0x47: + // LD B,A + BC.B.B1=AF.B.B1; + break; + case 0x48: + // LD C,B + BC.B.B0=BC.B.B1; + break; + case 0x49: + // LD C,C + BC.B.B0=BC.B.B0; + break; + case 0x4a: + // LD C,D + BC.B.B0=DE.B.B1; + break; + case 0x4b: + // LD C,E + BC.B.B0=DE.B.B0; + break; + case 0x4c: + // LD C,H + BC.B.B0=HL.B.B1; + break; + case 0x4d: + // LD C,L + BC.B.B0=HL.B.B0; + break; + case 0x4e: + // LD C,(HL) + BC.B.B0=gbReadMemory(HL.W); + break; + case 0x4f: + // LD C,A + BC.B.B0=AF.B.B1; + break; + case 0x50: + // LD D,B + DE.B.B1=BC.B.B1; + break; + case 0x51: + // LD D,C + DE.B.B1=BC.B.B0; + break; + case 0x52: + // LD D,D + DE.B.B1=DE.B.B1; + break; + case 0x53: + // LD D,E + DE.B.B1=DE.B.B0; + break; + case 0x54: + // LD D,H + DE.B.B1=HL.B.B1; + break; + case 0x55: + // LD D,L + DE.B.B1=HL.B.B0; + break; + case 0x56: + // LD D,(HL) + DE.B.B1=gbReadMemory(HL.W); + break; + case 0x57: + // LD D,A + DE.B.B1=AF.B.B1; + break; + case 0x58: + // LD E,B + DE.B.B0=BC.B.B1; + break; + case 0x59: + // LD E,C + DE.B.B0=BC.B.B0; + break; + case 0x5a: + // LD E,D + DE.B.B0=DE.B.B1; + break; + case 0x5b: + // LD E,E + DE.B.B0=DE.B.B0; + break; + case 0x5c: + // LD E,H + DE.B.B0=HL.B.B1; + break; + case 0x5d: + // LD E,L + DE.B.B0=HL.B.B0; + break; + case 0x5e: + // LD E,(HL) + DE.B.B0=gbReadMemory(HL.W); + break; + case 0x5f: + // LD E,A + DE.B.B0=AF.B.B1; + break; + case 0x60: + // LD H,B + HL.B.B1=BC.B.B1; + break; + case 0x61: + // LD H,C + HL.B.B1=BC.B.B0; + break; + case 0x62: + // LD H,D + HL.B.B1=DE.B.B1; + break; + case 0x63: + // LD H,E + HL.B.B1=DE.B.B0; + break; + case 0x64: + // LD H,H + HL.B.B1=HL.B.B1; + break; + case 0x65: + // LD H,L + HL.B.B1=HL.B.B0; + break; + case 0x66: + // LD H,(HL) + HL.B.B1=gbReadMemory(HL.W); + break; + case 0x67: + // LD H,A + HL.B.B1=AF.B.B1; + break; + case 0x68: + // LD L,B + HL.B.B0=BC.B.B1; + break; + case 0x69: + // LD L,C + HL.B.B0=BC.B.B0; + break; + case 0x6a: + // LD L,D + HL.B.B0=DE.B.B1; + break; + case 0x6b: + // LD L,E + HL.B.B0=DE.B.B0; + break; + case 0x6c: + // LD L,H + HL.B.B0=HL.B.B1; + break; + case 0x6d: + // LD L,L + HL.B.B0=HL.B.B0; + break; + case 0x6e: + // LD L,(HL) + HL.B.B0=gbReadMemory(HL.W); + break; + case 0x6f: + // LD L,A + HL.B.B0=AF.B.B1; + break; + case 0x70: + // LD (HL),B + gbWriteMemory(HL.W,BC.B.B1); + break; + case 0x71: + // LD (HL),C + gbWriteMemory(HL.W,BC.B.B0); + break; + case 0x72: + // LD (HL),D + gbWriteMemory(HL.W,DE.B.B1); + break; + case 0x73: + // LD (HL),E + gbWriteMemory(HL.W,DE.B.B0); + break; + case 0x74: + // LD (HL),H + gbWriteMemory(HL.W,HL.B.B1); + break; + case 0x75: + // LD (HL),L + gbWriteMemory(HL.W,HL.B.B0); + break; + case 0x76: + // HALT + // If an EI is pending, the interrupts are triggered before Halt state !! + // Fix Torpedo Range's intro. + if (IFF & 0x40) + { + IFF &= ~0x70; + IFF |=1; + PC.W--; + } + else + { + // if (IE & IF) and interrupts are disabeld, + // Halt is cancelled. + if ((register_IE & register_IF & 0x1f) && !(IFF & 1)) + { + IFF|=2; + } + else + IFF |= 0x80; + } + break; + case 0x77: + // LD (HL),A + gbWriteMemory(HL.W,AF.B.B1); + break; + case 0x78: + // LD A,B + AF.B.B1=BC.B.B1; + break; + case 0x79: + // LD A,C + AF.B.B1=BC.B.B0; + break; + case 0x7a: + // LD A,D + AF.B.B1=DE.B.B1; + break; + case 0x7b: + // LD A,E + AF.B.B1=DE.B.B0; + break; + case 0x7c: + // LD A,H + AF.B.B1=HL.B.B1; + break; + case 0x7d: + // LD A,L + AF.B.B1=HL.B.B0; + break; + case 0x7e: + // LD A,(HL) + AF.B.B1=gbReadMemory(HL.W); + break; + case 0x7f: + // LD A,A + AF.B.B1=AF.B.B1; + break; + case 0x80: + // ADD B + tempRegister.W=AF.B.B1+BC.B.B1; + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B1^tempRegister.B.B0)&0x10 ? H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x81: + // ADD C + tempRegister.W=AF.B.B1+BC.B.B0; + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B0^tempRegister.B.B0)&0x10 ? H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x82: + // ADD D + tempRegister.W=AF.B.B1+DE.B.B1; + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B1^tempRegister.B.B0)&0x10 ? H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x83: + // ADD E + tempRegister.W=AF.B.B1+DE.B.B0; + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B0^tempRegister.B.B0)&0x10 ? H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x84: + // ADD H + tempRegister.W=AF.B.B1+HL.B.B1; + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B1^tempRegister.B.B0)&0x10 ? H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x85: + // ADD L + tempRegister.W=AF.B.B1+HL.B.B0; + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B0^tempRegister.B.B0)&0x10 ? H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x86: + // ADD (HL) + tempValue=gbReadMemory(HL.W); + tempRegister.W=AF.B.B1+tempValue; + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10 ? H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x87: + // ADD A + tempRegister.W=AF.B.B1+AF.B.B1; + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^AF.B.B1^tempRegister.B.B0)&0x10 ? H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x88: + // ADC B: + tempRegister.W=AF.B.B1+BC.B.B1+(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x89: + // ADC C + tempRegister.W=AF.B.B1+BC.B.B0+(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x8a: + // ADC D + tempRegister.W=AF.B.B1+DE.B.B1+(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x8b: + // ADC E + tempRegister.W=AF.B.B1+DE.B.B0+(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x8c: + // ADC H + tempRegister.W=AF.B.B1+HL.B.B1+(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); AF.B.B1=tempRegister.B.B0; + break; + case 0x8d: + // ADC L + tempRegister.W=AF.B.B1+HL.B.B0+(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x8e: + // ADC (HL) + tempValue=gbReadMemory(HL.W); + tempRegister.W=AF.B.B1+tempValue+(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x8f: + // ADC A + tempRegister.W=AF.B.B1+AF.B.B1+(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^AF.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x90: + // SUB B + tempRegister.W=AF.B.B1-BC.B.B1; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x91: + // SUB C + tempRegister.W=AF.B.B1-BC.B.B0; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x92: + // SUB D + tempRegister.W=AF.B.B1-DE.B.B1; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x93: + // SUB E + tempRegister.W=AF.B.B1-DE.B.B0; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x94: + // SUB H + tempRegister.W=AF.B.B1-HL.B.B1; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x95: + // SUB L + tempRegister.W=AF.B.B1-HL.B.B0; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x96: + // SUB (HL) + tempValue=gbReadMemory(HL.W); + tempRegister.W=AF.B.B1-tempValue; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x97: + // SUB A + AF.B.B1=0; + AF.B.B0=N_FLAG|Z_FLAG; + break; + case 0x98: + // SBC B + tempRegister.W=AF.B.B1-BC.B.B1-(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x99: + // SBC C + tempRegister.W=AF.B.B1-BC.B.B0-(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x9a: + // SBC D + tempRegister.W=AF.B.B1-DE.B.B1-(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x9b: + // SBC E + tempRegister.W=AF.B.B1-DE.B.B0-(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x9c: + // SBC H + tempRegister.W=AF.B.B1-HL.B.B1-(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x9d: + // SBC L + tempRegister.W=AF.B.B1-HL.B.B0-(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x9e: + // SBC (HL) + tempValue=gbReadMemory(HL.W); + tempRegister.W=AF.B.B1-tempValue-(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0x9f: + // SBC A + tempRegister.W=AF.B.B1-AF.B.B1-(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^AF.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0xa0: + // AND B + AF.B.B1&=BC.B.B1; + AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; + break; + case 0xa1: + // AND C + AF.B.B1&=BC.B.B0; + AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; + break; + case 0xa2: + // AND_D + AF.B.B1&=DE.B.B1; + AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; + break; + case 0xa3: + // AND E + AF.B.B1&=DE.B.B0; + AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; + break; + case 0xa4: + // AND H + AF.B.B1&=HL.B.B1; + AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; + break; + case 0xa5: + // AND L + AF.B.B1&=HL.B.B0; + AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; + break; + case 0xa6: + // AND (HL) + tempValue=gbReadMemory(HL.W); + AF.B.B1&=tempValue; + AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; + break; + case 0xa7: + // AND A + AF.B.B1&=AF.B.B1; + AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; + break; + case 0xa8: + // XOR B + AF.B.B1^=BC.B.B1; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xa9: + // XOR C + AF.B.B1^=BC.B.B0; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xaa: + // XOR D + AF.B.B1^=DE.B.B1; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xab: + // XOR E + AF.B.B1^=DE.B.B0; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xac: + // XOR H + AF.B.B1^=HL.B.B1; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xad: + // XOR L + AF.B.B1^=HL.B.B0; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xae: + // XOR (HL) + tempValue=gbReadMemory(HL.W); + AF.B.B1^=tempValue; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xaf: + // XOR A + AF.B.B1=0; + AF.B.B0=Z_FLAG; + break; + case 0xb0: + // OR B + AF.B.B1|=BC.B.B1; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xb1: + // OR C + AF.B.B1|=BC.B.B0; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xb2: + // OR D + AF.B.B1|=DE.B.B1; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xb3: + // OR E + AF.B.B1|=DE.B.B0; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xb4: + // OR H + AF.B.B1|=HL.B.B1; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xb5: + // OR L + AF.B.B1|=HL.B.B0; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xb6: + // OR (HL) + tempValue=gbReadMemory(HL.W); + AF.B.B1|=tempValue; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xb7: + // OR A + AF.B.B1|=AF.B.B1; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xb8: + // CP B: + tempRegister.W=AF.B.B1-BC.B.B1; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + break; + case 0xb9: + // CP C + tempRegister.W=AF.B.B1-BC.B.B0; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^BC.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + break; + case 0xba: + // CP D + tempRegister.W=AF.B.B1-DE.B.B1; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + break; + case 0xbb: + // CP E + tempRegister.W=AF.B.B1-DE.B.B0; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^DE.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + break; + case 0xbc: + // CP H + tempRegister.W=AF.B.B1-HL.B.B1; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B1^tempRegister.B.B0)&0x10?H_FLAG:0); + break; + case 0xbd: + // CP L + tempRegister.W=AF.B.B1-HL.B.B0; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^HL.B.B0^tempRegister.B.B0)&0x10?H_FLAG:0); + break; + case 0xbe: + // CP (HL) + tempValue=gbReadMemory(HL.W); + tempRegister.W=AF.B.B1-tempValue; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); + break; + case 0xbf: + // CP A + AF.B.B0=N_FLAG|Z_FLAG; + break; + case 0xc0: + // RET NZ + if(!(AF.B.B0&Z_FLAG)) { + PC.B.B0=gbReadMemory(SP.W++); + PC.B.B1=gbReadMemory(SP.W++); + clockTicks += 3; + } + break; + case 0xc1: + // POP BC + BC.B.B0=gbReadMemory(SP.W++); + BC.B.B1=gbReadMemory(SP.W++); + break; + case 0xc2: + // JP NZ,NNNN + if(AF.B.B0&Z_FLAG) + PC.W+=2; + else { + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W); + PC.W=tempRegister.W; + clockTicks++; + } + break; + case 0xc3: + // JP NNNN + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W); + PC.W=tempRegister.W; + break; + case 0xc4: + // CALL NZ,NNNN + if(AF.B.B0&Z_FLAG) + PC.W+=2; + else { + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W++); + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=tempRegister.W; + clockTicks += 3; + } + break; + case 0xc5: + // PUSH BC + gbWriteMemory(--SP.W,BC.B.B1); + gbWriteMemory(--SP.W,BC.B.B0); + break; + case 0xc6: + // ADD NN + tempValue=gbReadOpcode(PC.W++); + tempRegister.W=AF.B.B1+tempValue; + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10 ? H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0xc7: + // RST 00 + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=0x0000; + break; + case 0xc8: + // RET Z + if(AF.B.B0&Z_FLAG) { + PC.B.B0=gbReadMemory(SP.W++); + PC.B.B1=gbReadMemory(SP.W++); + clockTicks += 3; + } + break; + case 0xc9: + // RET + PC.B.B0=gbReadMemory(SP.W++); + PC.B.B1=gbReadMemory(SP.W++); + break; + case 0xca: + // JP Z,NNNN + if(AF.B.B0&Z_FLAG) { + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W); + PC.W=tempRegister.W; + clockTicks++; + } else + PC.W+=2; + break; + // CB done outside + case 0xcc: + // CALL Z,NNNN + if(AF.B.B0&Z_FLAG) { + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W++); + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=tempRegister.W; + clockTicks += 3; + } else + PC.W+=2; + break; + case 0xcd: + // CALL NNNN + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W++); + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=tempRegister.W; + break; + case 0xce: + // ADC NN + tempValue=gbReadOpcode(PC.W++); + tempRegister.W=AF.B.B1+tempValue+(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= (tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0xcf: + // RST 08 + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=0x0008; + break; + case 0xd0: + // RET NC + if(!(AF.B.B0&C_FLAG)) { + PC.B.B0=gbReadMemory(SP.W++); + PC.B.B1=gbReadMemory(SP.W++); + clockTicks += 3; + } + break; + case 0xd1: + // POP DE + DE.B.B0=gbReadMemory(SP.W++); + DE.B.B1=gbReadMemory(SP.W++); + break; + case 0xd2: + // JP NC,NNNN + if(AF.B.B0&C_FLAG) + PC.W+=2; + else { + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W); + PC.W=tempRegister.W; + clockTicks++; + } + break; + // D3 illegal + case 0xd3: + PC.W--; + IFF = 0; + break; + case 0xd4: + // CALL NC,NNNN + if(AF.B.B0&C_FLAG) + PC.W+=2; + else { + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W++); + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=tempRegister.W; + clockTicks += 3; + } + break; + case 0xd5: + // PUSH DE + gbWriteMemory(--SP.W,DE.B.B1); + gbWriteMemory(--SP.W,DE.B.B0); + break; + case 0xd6: + // SUB NN + tempValue=gbReadOpcode(PC.W++); + tempRegister.W=AF.B.B1-tempValue; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0xd7: + // RST 10 + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=0x0010; + break; + case 0xd8: + // RET C + if(AF.B.B0&C_FLAG) { + PC.B.B0=gbReadMemory(SP.W++); + PC.B.B1=gbReadMemory(SP.W++); + clockTicks += 3; + } + break; + case 0xd9: + // RETI + PC.B.B0=gbReadMemory(SP.W++); + PC.B.B1=gbReadMemory(SP.W++); + IFF |= 0x01; + break; + case 0xda: + // JP C,NNNN + if(AF.B.B0&C_FLAG) { + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W); + PC.W=tempRegister.W; + clockTicks++; + } else + PC.W+=2; + break; + // DB illegal + case 0xdb: + PC.W--; + IFF = 0; + break; + case 0xdc: + // CALL C,NNNN + if(AF.B.B0&C_FLAG) { + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W++); + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=tempRegister.W; + clockTicks += 3; + } else + PC.W+=2; + break; + // DD illegal + case 0xdd: + PC.W--; + IFF = 0; + break; + case 0xde: + // SBC NN + tempValue=gbReadOpcode(PC.W++); + tempRegister.W=AF.B.B1-tempValue-(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); + AF.B.B1=tempRegister.B.B0; + break; + case 0xdf: + // RST 18 + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=0x0018; + break; + case 0xe0: + // LD (FF00+NN),A + gbWriteMemory(0xff00 + gbReadOpcode(PC.W++),AF.B.B1); + break; + case 0xe1: + // POP HL + HL.B.B0=gbReadMemory(SP.W++); + HL.B.B1=gbReadMemory(SP.W++); + break; + case 0xe2: + // LD (FF00+C),A + gbWriteMemory(0xff00 + BC.B.B0,AF.B.B1); + break; + // E3 illegal + // E4 illegal + case 0xe3: + case 0xe4: + PC.W--; + IFF = 0; + break; + case 0xe5: + // PUSH HL + gbWriteMemory(--SP.W,HL.B.B1); + gbWriteMemory(--SP.W,HL.B.B0); + break; + case 0xe6: + // AND NN + tempValue=gbReadOpcode(PC.W++); + AF.B.B1&=tempValue; + AF.B.B0=H_FLAG|ZeroTable[AF.B.B1]; + break; + case 0xe7: + // RST 20 + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=0x0020; + break; + case 0xe8: + // ADD SP,NN + offset = (s8)gbReadOpcode(PC.W++); + + if(offset >= 0) { + tempRegister.W = SP.W + offset; + AF.B.B0 = (SP.W > tempRegister.W ? C_FLAG : 0) | + ((SP.W^offset^tempRegister.W)&0x1000? H_FLAG:0); + SP.W = tempRegister.W; + } else { + tempRegister.W = SP.W + offset; + AF.B.B0 = (SP.W < tempRegister.W ? C_FLAG : 0) | + ((SP.W^offset^tempRegister.W)&0x1000?H_FLAG:0); + SP.W = tempRegister.W; + } + break; + case 0xe9: + // LD PC,HL + PC.W=HL.W; + break; + case 0xea: + // LD (NNNN),A + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W++); + gbWriteMemory(tempRegister.W,AF.B.B1); + break; + // EB illegal + // EC illegal + // ED illegal + case 0xeb: + case 0xec: + case 0xed: + PC.W--; + IFF = 0; + break; + case 0xee: + // XOR NN + tempValue=gbReadOpcode(PC.W++); + AF.B.B1^=tempValue; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xef: + // RST 28 + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=0x0028; + break; + case 0xf0: + // LD A,(FF00+NN) + AF.B.B1 = gbReadMemory(0xff00+gbReadOpcode(PC.W++)); + break; + case 0xf1: + // POP AF + AF.B.B0=gbReadMemory(SP.W++); + AF.B.B1=gbReadMemory(SP.W++); + break; + case 0xf2: + // LD A,(FF00+C) + AF.B.B1 = gbReadMemory(0xff00+BC.B.B0); + break; + case 0xf3: + // DI + // IFF&=0xFE; + IFF|=0x08; + break; + // F4 illegal + case 0xf4: + PC.W--; + IFF = 0; + break; + case 0xf5: + // PUSH AF + gbWriteMemory(--SP.W,AF.B.B1); + gbWriteMemory(--SP.W,AF.B.B0); + break; + case 0xf6: + // OR NN + tempValue=gbReadOpcode(PC.W++); + AF.B.B1|=tempValue; + AF.B.B0=ZeroTable[AF.B.B1]; + break; + case 0xf7: + // RST 30 + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=0x0030; + break; + case 0xf8: + // LD HL,SP+NN + offset = (s8)gbReadOpcode(PC.W++); + if(offset >= 0) { + tempRegister.W = SP.W + offset; + AF.B.B0 = (SP.W > tempRegister.W ? C_FLAG : 0) | + ((SP.W^offset^tempRegister.W)&0x1000? H_FLAG:0); + HL.W = tempRegister.W; + } else { + tempRegister.W = SP.W + offset; + AF.B.B0 = (SP.W < tempRegister.W ? C_FLAG : 0) | + ((SP.W^offset^tempRegister.W)&0x1000?H_FLAG:0); + HL.W = tempRegister.W; + } + break; + case 0xf9: + // LD SP,HL + SP.W=HL.W; + break; + case 0xfa: + // LD A,(NNNN) + tempRegister.B.B0=gbReadOpcode(PC.W++); + tempRegister.B.B1=gbReadOpcode(PC.W++); + AF.B.B1=gbReadMemory(tempRegister.W); + break; + case 0xfb: + // EI + if (!(IFF & 0x30)) + // If an EI is executed right before HALT, + // the interrupts are triggered before the Halt state !! + // Fix Torpedo Range Intro. + // IFF |= 0x10 : 1 ticks before the EI enables the interrupts + // IFF |= 0x40 : marks that an EI is being executed. + IFF|=0x50; + break; + // FC illegal (FC = breakpoint) + case 0xfc: + breakpoint = true; + break; + // FD illegal + case 0xfd: + PC.W--; + IFF = 0; + break; + case 0xfe: + // CP NN + tempValue=gbReadOpcode(PC.W++); + tempRegister.W=AF.B.B1-tempValue; + AF.B.B0= N_FLAG|(tempRegister.B.B1?C_FLAG:0)|ZeroTable[tempRegister.B.B0]| + ((AF.B.B1^tempValue^tempRegister.B.B0)&0x10?H_FLAG:0); + break; + case 0xff: + // RST 38 + gbWriteMemory(--SP.W,PC.B.B1); + gbWriteMemory(--SP.W,PC.B.B0); + PC.W=0x0038; + break; + default: + if (gbSystemMessage == false) + { + systemMessage(0, N_("Unknown opcode %02x at %04x"), + gbReadOpcode(PC.W-1),PC.W-1); + gbSystemMessage =true; + } + return; diff --git a/src/dmg/gbCodesCB.h b/src/dmg/gbCodesCB.h new file mode 100644 index 00000000..64fefef2 --- /dev/null +++ b/src/dmg/gbCodesCB.h @@ -0,0 +1,1291 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + case 0x00: + // RLC B + AF.B.B0 = (BC.B.B1 & 0x80)?C_FLAG:0; + BC.B.B1 = (BC.B.B1<<1) | (BC.B.B1>>7); + AF.B.B0 |= ZeroTable[BC.B.B1]; + break; + case 0x01: + // RLC C + AF.B.B0 = (BC.B.B0 & 0x80)?C_FLAG:0; + BC.B.B0 = (BC.B.B0<<1) | (BC.B.B0>>7); + AF.B.B0 |= ZeroTable[BC.B.B0]; + break; + case 0x02: + // RLC D + AF.B.B0 = (DE.B.B1 & 0x80)?C_FLAG:0; + DE.B.B1 = (DE.B.B1<<1) | (DE.B.B1>>7); + AF.B.B0 |= ZeroTable[DE.B.B1]; + break; + case 0x03: + // RLC E + AF.B.B0 = (DE.B.B0 & 0x80)?C_FLAG:0; + DE.B.B0 = (DE.B.B0<<1) | (DE.B.B0>>7); + AF.B.B0 |= ZeroTable[DE.B.B0]; + break; + case 0x04: + // RLC H + AF.B.B0 = (HL.B.B1 & 0x80)?C_FLAG:0; + HL.B.B1 = (HL.B.B1<<1) | (HL.B.B1>>7); + AF.B.B0 |= ZeroTable[HL.B.B1]; + break; + case 0x05: + // RLC L + AF.B.B0 = (HL.B.B0 & 0x80)?C_FLAG:0; + HL.B.B0 = (HL.B.B0<<1) | (HL.B.B0>>7); + AF.B.B0 |= ZeroTable[HL.B.B0]; + break; + case 0x06: + // RLC (HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0 = (tempValue & 0x80)?C_FLAG:0; + tempValue = (tempValue<<1) | (tempValue>>7); + AF.B.B0 |= ZeroTable[tempValue]; + gbWriteMemory(HL.W,tempValue); + break; + case 0x07: + // RLC A + AF.B.B0 = (AF.B.B1 & 0x80)?C_FLAG:0; + AF.B.B1 = (AF.B.B1<<1) | (AF.B.B1>>7); + AF.B.B0 |= ZeroTable[AF.B.B1]; + break; + case 0x08: + // RRC B + AF.B.B0=(BC.B.B1&0x01 ? C_FLAG : 0); + BC.B.B1=(BC.B.B1>>1)|(BC.B.B1<<7); + AF.B.B0|=ZeroTable[BC.B.B1]; + break; + case 0x09: + // RRC C + AF.B.B0=(BC.B.B0&0x01 ? C_FLAG : 0); + BC.B.B0=(BC.B.B0>>1)|(BC.B.B0<<7); + AF.B.B0|=ZeroTable[BC.B.B0]; + break; + case 0x0a: + // RRC D + AF.B.B0=(DE.B.B1&0x01 ? C_FLAG : 0); + DE.B.B1=(DE.B.B1>>1)|(DE.B.B1<<7); + AF.B.B0|=ZeroTable[DE.B.B1]; + break; + case 0x0b: + // RRC E + AF.B.B0=(DE.B.B0&0x01 ? C_FLAG : 0); + DE.B.B0=(DE.B.B0>>1)|(DE.B.B0<<7); + AF.B.B0|=ZeroTable[DE.B.B0]; + break; + case 0x0c: + // RRC H + AF.B.B0=(HL.B.B1&0x01 ? C_FLAG : 0); + HL.B.B1=(HL.B.B1>>1)|(HL.B.B1<<7); + AF.B.B0|=ZeroTable[HL.B.B1]; + break; + case 0x0d: + // RRC L + AF.B.B0=(HL.B.B0&0x01 ? C_FLAG : 0); + HL.B.B0=(HL.B.B0>>1)|(HL.B.B0<<7); + AF.B.B0|=ZeroTable[HL.B.B0]; + break; + case 0x0e: + // RRC (HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(tempValue&0x01 ? C_FLAG : 0); + tempValue=(tempValue>>1)|(tempValue<<7); + AF.B.B0|=ZeroTable[tempValue]; + gbWriteMemory(HL.W,tempValue); + break; + case 0x0f: + // RRC A + AF.B.B0=(AF.B.B1&0x01 ? C_FLAG : 0); + AF.B.B1=(AF.B.B1>>1)|(AF.B.B1<<7); + AF.B.B0|=ZeroTable[AF.B.B1]; + break; + case 0x10: + // RL B + if(BC.B.B1&0x80) { + BC.B.B1=(BC.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[BC.B.B1]|C_FLAG; + } else { + BC.B.B1=(BC.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[BC.B.B1]; + } + break; + case 0x11: + // RL C + if(BC.B.B0&0x80) { + BC.B.B0=(BC.B.B0<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[BC.B.B0]|C_FLAG; + } else { + BC.B.B0=(BC.B.B0<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[BC.B.B0]; + } + break; + case 0x12: + // RL D + if(DE.B.B1&0x80) { + DE.B.B1=(DE.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[DE.B.B1]|C_FLAG; + } else { + DE.B.B1=(DE.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[DE.B.B1]; + } + break; + case 0x13: + // RL E + if(DE.B.B0&0x80) { + DE.B.B0=(DE.B.B0<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[DE.B.B0]|C_FLAG; + } else { + DE.B.B0=(DE.B.B0<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[DE.B.B0]; + } + break; + case 0x14: + // RL H + if(HL.B.B1&0x80) { + HL.B.B1=(HL.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[HL.B.B1]|C_FLAG; + } else { + HL.B.B1=(HL.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[HL.B.B1]; + } + break; + case 0x15: + // RL L + if(HL.B.B0&0x80) { + HL.B.B0=(HL.B.B0<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[HL.B.B0]|C_FLAG; + } else { + HL.B.B0=(HL.B.B0<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[HL.B.B0]; + } + break; + case 0x16: + // RL (HL) + tempValue=gbReadMemory(HL.W); + if(tempValue&0x80) { + tempValue=(tempValue<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[tempValue]|C_FLAG; + } else { + tempValue=(tempValue<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[tempValue]; + } + gbWriteMemory(HL.W,tempValue); + break; + case 0x17: + // RL A + if(AF.B.B1&0x80) { + AF.B.B1=(AF.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[AF.B.B1]|C_FLAG; + } else { + AF.B.B1=(AF.B.B1<<1)|(AF.B.B0&C_FLAG ? 1 : 0); + AF.B.B0=ZeroTable[AF.B.B1]; + } + break; + case 0x18: + // RR B + if(BC.B.B1&0x01) { + BC.B.B1=(BC.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[BC.B.B1]|C_FLAG; + } else { + BC.B.B1=(BC.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[BC.B.B1]; + } + break; + case 0x19: + // RR C + if(BC.B.B0&0x01) { + BC.B.B0=(BC.B.B0>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[BC.B.B0]|C_FLAG; + } else { + BC.B.B0=(BC.B.B0>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[BC.B.B0]; + } + break; + case 0x1a: + // RR D + if(DE.B.B1&0x01) { + DE.B.B1=(DE.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[DE.B.B1]|C_FLAG; + } else { + DE.B.B1=(DE.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[DE.B.B1]; + } + break; + case 0x1b: + // RR E + if(DE.B.B0&0x01) { + DE.B.B0=(DE.B.B0>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[DE.B.B0]|C_FLAG; + } else { + DE.B.B0=(DE.B.B0>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[DE.B.B0]; + } + break; + case 0x1c: + // RR H + if(HL.B.B1&0x01) { + HL.B.B1=(HL.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[HL.B.B1]|C_FLAG; + } else { + HL.B.B1=(HL.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[HL.B.B1]; + } + break; + case 0x1d: + // RR L + if(HL.B.B0&0x01) { + HL.B.B0=(HL.B.B0>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[HL.B.B0]|C_FLAG; + } else { + HL.B.B0=(HL.B.B0>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[HL.B.B0]; + } + break; + case 0x1e: + // RR (HL) + tempValue=gbReadMemory(HL.W); + if(tempValue&0x01) { + tempValue=(tempValue>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[tempValue]|C_FLAG; + } else { + tempValue=(tempValue>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[tempValue]; + } + gbWriteMemory(HL.W,tempValue); + break; + case 0x1f: + // RR A + if(AF.B.B1&0x01) { + AF.B.B1=(AF.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[AF.B.B1]|C_FLAG; + } else { + AF.B.B1=(AF.B.B1>>1)|(AF.B.B0 & C_FLAG ? 0x80:0); + AF.B.B0=ZeroTable[AF.B.B1]; + } + break; + case 0x20: + // SLA B + AF.B.B0=(BC.B.B1&0x80?C_FLAG : 0); + BC.B.B1<<=1; + AF.B.B0|=ZeroTable[BC.B.B1]; + break; + case 0x21: + // SLA C + AF.B.B0=(BC.B.B0&0x80?C_FLAG : 0); + BC.B.B0<<=1; + AF.B.B0|=ZeroTable[BC.B.B0]; + break; + case 0x22: + // SLA D + AF.B.B0=(DE.B.B1&0x80?C_FLAG : 0); + DE.B.B1<<=1; + AF.B.B0|=ZeroTable[DE.B.B1]; + break; + case 0x23: + // SLA E + AF.B.B0=(DE.B.B0&0x80?C_FLAG : 0); + DE.B.B0<<=1; + AF.B.B0|=ZeroTable[DE.B.B0]; + break; + case 0x24: + // SLA H + AF.B.B0=(HL.B.B1&0x80?C_FLAG : 0); + HL.B.B1<<=1; + AF.B.B0|=ZeroTable[HL.B.B1]; + break; + case 0x25: + // SLA L + AF.B.B0=(HL.B.B0&0x80?C_FLAG : 0); + HL.B.B0<<=1; + AF.B.B0|=ZeroTable[HL.B.B0]; + break; + case 0x26: + // SLA (HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(tempValue&0x80?C_FLAG : 0); + tempValue<<=1; + AF.B.B0|=ZeroTable[tempValue]; + gbWriteMemory(HL.W,tempValue); + break; + case 0x27: + // SLA A + AF.B.B0=(AF.B.B1&0x80?C_FLAG : 0); + AF.B.B1<<=1; + AF.B.B0|=ZeroTable[AF.B.B1]; + break; + case 0x28: + // SRA B + AF.B.B0=(BC.B.B1&0x01 ? C_FLAG: 0); + BC.B.B1=(BC.B.B1>>1)|(BC.B.B1&0x80); + AF.B.B0|=ZeroTable[BC.B.B1]; + break; + case 0x29: + // SRA C + AF.B.B0=(BC.B.B0&0x01 ? C_FLAG: 0); + BC.B.B0=(BC.B.B0>>1)|(BC.B.B0&0x80); + AF.B.B0|=ZeroTable[BC.B.B0]; + break; + case 0x2a: + // SRA D + AF.B.B0=(DE.B.B1&0x01 ? C_FLAG: 0); + DE.B.B1=(DE.B.B1>>1)|(DE.B.B1&0x80); + AF.B.B0|=ZeroTable[DE.B.B1]; + break; + case 0x2b: + // SRA E + AF.B.B0=(DE.B.B0&0x01 ? C_FLAG: 0); + DE.B.B0=(DE.B.B0>>1)|(DE.B.B0&0x80); + AF.B.B0|=ZeroTable[DE.B.B0]; + break; + case 0x2c: + // SRA H + AF.B.B0=(HL.B.B1&0x01 ? C_FLAG: 0); + HL.B.B1=(HL.B.B1>>1)|(HL.B.B1&0x80); + AF.B.B0|=ZeroTable[HL.B.B1]; + break; + case 0x2d: + // SRA L + AF.B.B0=(HL.B.B0&0x01 ? C_FLAG: 0); + HL.B.B0=(HL.B.B0>>1)|(HL.B.B0&0x80); + AF.B.B0|=ZeroTable[HL.B.B0]; + break; + case 0x2e: + // SRA (HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(tempValue&0x01 ? C_FLAG: 0); + tempValue=(tempValue>>1)|(tempValue&0x80); + AF.B.B0|=ZeroTable[tempValue]; + gbWriteMemory(HL.W,tempValue); + break; + case 0x2f: + // SRA A + AF.B.B0=(AF.B.B1&0x01 ? C_FLAG: 0); + AF.B.B1=(AF.B.B1>>1)|(AF.B.B1&0x80); + AF.B.B0|=ZeroTable[AF.B.B1]; + break; + case 0x30: + // SWAP B + BC.B.B1 = (BC.B.B1&0xf0)>>4 | (BC.B.B1&0x0f)<<4; + AF.B.B0 = ZeroTable[BC.B.B1]; + break; + case 0x31: + // SWAP C + BC.B.B0 = (BC.B.B0&0xf0)>>4 | (BC.B.B0&0x0f)<<4; + AF.B.B0 = ZeroTable[BC.B.B0]; + break; + case 0x32: + // SWAP D + DE.B.B1 = (DE.B.B1&0xf0)>>4 | (DE.B.B1&0x0f)<<4; + AF.B.B0 = ZeroTable[DE.B.B1]; + break; + case 0x33: + // SWAP E + DE.B.B0 = (DE.B.B0&0xf0)>>4 | (DE.B.B0&0x0f)<<4; + AF.B.B0 = ZeroTable[DE.B.B0]; + break; + case 0x34: + // SWAP H + HL.B.B1 = (HL.B.B1&0xf0)>>4 | (HL.B.B1&0x0f)<<4; + AF.B.B0 = ZeroTable[HL.B.B1]; + break; + case 0x35: + // SWAP L + HL.B.B0 = (HL.B.B0&0xf0)>>4 | (HL.B.B0&0x0f)<<4; + AF.B.B0 = ZeroTable[HL.B.B0]; + break; + case 0x36: + // SWAP (HL) + tempValue=gbReadMemory(HL.W); + tempValue = (tempValue&0xf0)>>4 | (tempValue&0x0f)<<4; + AF.B.B0 = ZeroTable[tempValue]; + gbWriteMemory(HL.W,tempValue); + break; + case 0x37: + // SWAP A + AF.B.B1 = (AF.B.B1&0xf0)>>4 | (AF.B.B1&0x0f)<<4; + AF.B.B0 = ZeroTable[AF.B.B1]; + break; + case 0x38: + // SRL B + AF.B.B0=(BC.B.B1&0x01)?C_FLAG:0; + BC.B.B1>>=1; + AF.B.B0|=ZeroTable[BC.B.B1]; + break; + case 0x39: + // SRL C + AF.B.B0=(BC.B.B0&0x01)?C_FLAG:0; + BC.B.B0>>=1; + AF.B.B0|=ZeroTable[BC.B.B0]; + break; + case 0x3a: + // SRL D + AF.B.B0=(DE.B.B1&0x01)?C_FLAG:0; + DE.B.B1>>=1; + AF.B.B0|=ZeroTable[DE.B.B1]; + break; + case 0x3b: + // SRL E + AF.B.B0=(DE.B.B0&0x01)?C_FLAG:0; + DE.B.B0>>=1; + AF.B.B0|=ZeroTable[DE.B.B0]; + break; + case 0x3c: + // SRL H + AF.B.B0=(HL.B.B1&0x01)?C_FLAG:0; + HL.B.B1>>=1; + AF.B.B0|=ZeroTable[HL.B.B1]; + break; + case 0x3d: + // SRL L + AF.B.B0=(HL.B.B0&0x01)?C_FLAG:0; + HL.B.B0>>=1; + AF.B.B0|=ZeroTable[HL.B.B0]; + break; + case 0x3e: + // SRL (HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(tempValue&0x01)?C_FLAG:0; + tempValue>>=1; + AF.B.B0|=ZeroTable[tempValue]; + gbWriteMemory(HL.W,tempValue); + break; + case 0x3f: + // SRL A + AF.B.B0=(AF.B.B1&0x01)?C_FLAG:0; + AF.B.B1>>=1; + AF.B.B0|=ZeroTable[AF.B.B1]; + break; + case 0x40: + // BIT 0,B + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<0)? 0:Z_FLAG); + break; + case 0x41: + // BIT 0,C + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<0)? 0:Z_FLAG); + break; + case 0x42: + // BIT 0,D + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<0)? 0:Z_FLAG); + break; + case 0x43: + // BIT 0,E + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<0)? 0:Z_FLAG); + break; + case 0x44: + // BIT 0,H + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<0)? 0:Z_FLAG); + break; + case 0x45: + // BIT 0,L + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<0)? 0:Z_FLAG); + break; + case 0x46: + // BIT 0,(HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<0)? 0:Z_FLAG); + break; + case 0x47: + // BIT 0,A + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<0)? 0:Z_FLAG); + break; + case 0x48: + // BIT 1,B + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<1)? 0:Z_FLAG); + break; + case 0x49: + // BIT 1,C + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<1)? 0:Z_FLAG); + break; + case 0x4a: + // BIT 1,D + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<1)? 0:Z_FLAG); + break; + case 0x4b: + // BIT 1,E + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<1)? 0:Z_FLAG); + break; + case 0x4c: + // BIT 1,H + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<1)? 0:Z_FLAG); + break; + case 0x4d: + // BIT 1,L + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<1)? 0:Z_FLAG); + break; + case 0x4e: + // BIT 1,(HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<1)? 0:Z_FLAG); + break; + case 0x4f: + // BIT 1,A + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<1)? 0:Z_FLAG); + break; + case 0x50: + // BIT 2,B + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<2)? 0:Z_FLAG); + break; + case 0x51: + // BIT 2,C + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<2)? 0:Z_FLAG); + break; + case 0x52: + // BIT 2,D + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<2)? 0:Z_FLAG); + break; + case 0x53: + // BIT 2,E + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<2)? 0:Z_FLAG); + break; + case 0x54: + // BIT 2,H + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<2)? 0:Z_FLAG); + break; + case 0x55: + // BIT 2,L + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<2)? 0:Z_FLAG); + break; + case 0x56: + // BIT 2,(HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<2)? 0:Z_FLAG); + break; + case 0x57: + // BIT 2,A + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<2)? 0:Z_FLAG); + break; + case 0x58: + // BIT 3,B + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<3)? 0:Z_FLAG); + break; + case 0x59: + // BIT 3,C + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<3)? 0:Z_FLAG); + break; + case 0x5a: + // BIT 3,D + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<3)? 0:Z_FLAG); + break; + case 0x5b: + // BIT 3,E + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<3)? 0:Z_FLAG); + break; + case 0x5c: + // BIT 3,H + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<3)? 0:Z_FLAG); + break; + case 0x5d: + // BIT 3,L + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<3)? 0:Z_FLAG); + break; + case 0x5e: + // BIT 3,(HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<3)? 0:Z_FLAG); + break; + case 0x5f: + // BIT 3,A + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<3)? 0:Z_FLAG); + break; + case 0x60: + // BIT 4,B + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<4)? 0:Z_FLAG); + break; + case 0x61: + // BIT 4,C + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<4)? 0:Z_FLAG); + break; + case 0x62: + // BIT 4,D + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<4)? 0:Z_FLAG); + break; + case 0x63: + // BIT 4,E + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<4)? 0:Z_FLAG); + break; + case 0x64: + // BIT 4,H + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<4)? 0:Z_FLAG); + break; + case 0x65: + // BIT 4,L + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<4)? 0:Z_FLAG); + break; + case 0x66: + // BIT 4,(HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<4)? 0:Z_FLAG); + break; + case 0x67: + // BIT 4,A + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<4)? 0:Z_FLAG); + break; + case 0x68: + // BIT 5,B + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<5)? 0:Z_FLAG); + break; + case 0x69: + // BIT 5,C + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<5)? 0:Z_FLAG); + break; + case 0x6a: + // BIT 5,D + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<5)? 0:Z_FLAG); + break; + case 0x6b: + // BIT 5,E + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<5)? 0:Z_FLAG); + break; + case 0x6c: + // BIT 5,H + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<5)? 0:Z_FLAG); + break; + case 0x6d: + // BIT 5,L + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<5)? 0:Z_FLAG); + break; + case 0x6e: + // BIT 5,(HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<5)? 0:Z_FLAG); + break; + case 0x6f: + // BIT 5,A + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<5)? 0:Z_FLAG); + break; + case 0x70: + // BIT 6,B + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<6)? 0:Z_FLAG); + break; + case 0x71: + // BIT 6,C + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<6)? 0:Z_FLAG); + break; + case 0x72: + // BIT 6,D + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<6)? 0:Z_FLAG); + break; + case 0x73: + // BIT 6,E + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<6)? 0:Z_FLAG); + break; + case 0x74: + // BIT 6,H + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<6)? 0:Z_FLAG); + break; + case 0x75: + // BIT 6,L + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<6)? 0:Z_FLAG); + break; + case 0x76: + // BIT 6,(HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<6)? 0:Z_FLAG); + break; + case 0x77: + // BIT 6,A + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<6)? 0:Z_FLAG); + break; + case 0x78: + // BIT 7,B + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B1&(1<<7)? 0:Z_FLAG); + break; + case 0x79: + // BIT 7,C + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(BC.B.B0&(1<<7)? 0:Z_FLAG); + break; + case 0x7a: + // BIT 7,D + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B1&(1<<7)? 0:Z_FLAG); + break; + case 0x7b: + // BIT 7,E + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(DE.B.B0&(1<<7)? 0:Z_FLAG); + break; + case 0x7c: + // BIT 7,H + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B1&(1<<7)? 0:Z_FLAG); + break; + case 0x7d: + // BIT 7,L + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(HL.B.B0&(1<<7)? 0:Z_FLAG); + break; + case 0x7e: + // BIT 7,(HL) + tempValue=gbReadMemory(HL.W); + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(tempValue&(1<<7)? 0:Z_FLAG); + break; + case 0x7f: + // BIT 7,A + AF.B.B0=(AF.B.B0&C_FLAG)|H_FLAG|(AF.B.B1&(1<<7)? 0:Z_FLAG); + break; + case 0x80: + // RES 0,B + BC.B.B1&=~(1<<0); + break; + case 0x81: + // RES 0,C + BC.B.B0&=~(1<<0); + break; + case 0x82: + // RES 0,D + DE.B.B1&=~(1<<0); + break; + case 0x83: + // RES 0,E + DE.B.B0&=~(1<<0); + break; + case 0x84: + // RES 0,H + HL.B.B1&=~(1<<0); + break; + case 0x85: + // RES 0,L + HL.B.B0&=~(1<<0); + break; + case 0x86: + // RES 0,(HL) + tempValue=gbReadMemory(HL.W); + tempValue&=~(1<<0); + gbWriteMemory(HL.W,tempValue); + break; + case 0x87: + // RES 0,A + AF.B.B1&=~(1<<0); + break; + case 0x88: + // RES 1,B + BC.B.B1&=~(1<<1); + break; + case 0x89: + // RES 1,C + BC.B.B0&=~(1<<1); + break; + case 0x8a: + // RES 1,D + DE.B.B1&=~(1<<1); + break; + case 0x8b: + // RES 1,E + DE.B.B0&=~(1<<1); + break; + case 0x8c: + // RES 1,H + HL.B.B1&=~(1<<1); + break; + case 0x8d: + // RES 1,L + HL.B.B0&=~(1<<1); + break; + case 0x8e: + // RES 1,(HL) + tempValue=gbReadMemory(HL.W); + tempValue&=~(1<<1); + gbWriteMemory(HL.W,tempValue); + break; + case 0x8f: + // RES 1,A + AF.B.B1&=~(1<<1); + break; + case 0x90: + // RES 2,B + BC.B.B1&=~(1<<2); + break; + case 0x91: + // RES 2,C + BC.B.B0&=~(1<<2); + break; + case 0x92: + // RES 2,D + DE.B.B1&=~(1<<2); + break; + case 0x93: + // RES 2,E + DE.B.B0&=~(1<<2); + break; + case 0x94: + // RES 2,H + HL.B.B1&=~(1<<2); + break; + case 0x95: + // RES 2,L + HL.B.B0&=~(1<<2); + break; + case 0x96: + // RES 2,(HL) + tempValue=gbReadMemory(HL.W); + tempValue&=~(1<<2); + gbWriteMemory(HL.W,tempValue); + break; + case 0x97: + // RES 2,A + AF.B.B1&=~(1<<2); + break; + case 0x98: + // RES 3,B + BC.B.B1&=~(1<<3); + break; + case 0x99: + // RES 3,C + BC.B.B0&=~(1<<3); + break; + case 0x9a: + // RES 3,D + DE.B.B1&=~(1<<3); + break; + case 0x9b: + // RES 3,E + DE.B.B0&=~(1<<3); + break; + case 0x9c: + // RES 3,H + HL.B.B1&=~(1<<3); + break; + case 0x9d: + // RES 3,L + HL.B.B0&=~(1<<3); + break; + case 0x9e: + // RES 3,(HL) + tempValue=gbReadMemory(HL.W); + tempValue&=~(1<<3); + gbWriteMemory(HL.W,tempValue); + break; + case 0x9f: + // RES 3,A + AF.B.B1&=~(1<<3); + break; + case 0xa0: + // RES 4,B + BC.B.B1&=~(1<<4); + break; + case 0xa1: + // RES 4,C + BC.B.B0&=~(1<<4); + break; + case 0xa2: + // RES 4,D + DE.B.B1&=~(1<<4); + break; + case 0xa3: + // RES 4,E + DE.B.B0&=~(1<<4); + break; + case 0xa4: + // RES 4,H + HL.B.B1&=~(1<<4); + break; + case 0xa5: + // RES 4,L + HL.B.B0&=~(1<<4); + break; + case 0xa6: + // RES 4,(HL) + tempValue=gbReadMemory(HL.W); + tempValue&=~(1<<4); + gbWriteMemory(HL.W,tempValue); + break; + case 0xa7: + // RES 4,A + AF.B.B1&=~(1<<4); + break; + case 0xa8: + // RES 5,B + BC.B.B1&=~(1<<5); + break; + case 0xa9: + // RES 5,C + BC.B.B0&=~(1<<5); + break; + case 0xaa: + // RES 5,D + DE.B.B1&=~(1<<5); + break; + case 0xab: + // RES 5,E + DE.B.B0&=~(1<<5); + break; + case 0xac: + // RES 5,H + HL.B.B1&=~(1<<5); + break; + case 0xad: + // RES 5,L + HL.B.B0&=~(1<<5); + break; + case 0xae: + // RES 5,(HL) + tempValue=gbReadMemory(HL.W); + tempValue&=~(1<<5); + gbWriteMemory(HL.W,tempValue); + break; + case 0xaf: + // RES 5,A + AF.B.B1&=~(1<<5); + break; + case 0xb0: + // RES 6,B + BC.B.B1&=~(1<<6); + break; + case 0xb1: + // RES 6,C + BC.B.B0&=~(1<<6); + break; + case 0xb2: + // RES 6,D + DE.B.B1&=~(1<<6); + break; + case 0xb3: + // RES 6,E + DE.B.B0&=~(1<<6); + break; + case 0xb4: + // RES 6,H + HL.B.B1&=~(1<<6); + break; + case 0xb5: + // RES 6,L + HL.B.B0&=~(1<<6); + break; + case 0xb6: + // RES 6,(HL) + tempValue=gbReadMemory(HL.W); + tempValue&=~(1<<6); + gbWriteMemory(HL.W,tempValue); + break; + case 0xb7: + // RES 6,A + AF.B.B1&=~(1<<6); + break; + case 0xb8: + // RES 7,B + BC.B.B1&=~(1<<7); + break; + case 0xb9: + // RES 7,C + BC.B.B0&=~(1<<7); + break; + case 0xba: + // RES 7,D + DE.B.B1&=~(1<<7); + break; + case 0xbb: + // RES 7,E + DE.B.B0&=~(1<<7); + break; + case 0xbc: + // RES 7,H + HL.B.B1&=~(1<<7); + break; + case 0xbd: + // RES 7,L + HL.B.B0&=~(1<<7); + break; + case 0xbe: + // RES 7,(HL) + tempValue=gbReadMemory(HL.W); + tempValue&=~(1<<7); + gbWriteMemory(HL.W,tempValue); + break; + case 0xbf: + // RES 7,A + AF.B.B1&=~(1<<7); + break; + case 0xc0: + // SET 0,B + BC.B.B1|=1<<0; + break; + case 0xc1: + // SET 0,C + BC.B.B0|=1<<0; + break; + case 0xc2: + // SET 0,D + DE.B.B1|=1<<0; + break; + case 0xc3: + // SET 0,E + DE.B.B0|=1<<0; + break; + case 0xc4: + // SET 0,H + HL.B.B1|=1<<0; + break; + case 0xc5: + // SET 0,L + HL.B.B0|=1<<0; + break; + case 0xc6: + // SET 0,(HL) + tempValue=gbReadMemory(HL.W); + tempValue|=1<<0; + gbWriteMemory(HL.W,tempValue); + break; + case 0xc7: + // SET 0,A + AF.B.B1|=1<<0; + break; + case 0xc8: + // SET 1,B + BC.B.B1|=1<<1; + break; + case 0xc9: + // SET 1,C + BC.B.B0|=1<<1; + break; + case 0xca: + // SET 1,D + DE.B.B1|=1<<1; + break; + case 0xcb: + // SET 1,E + DE.B.B0|=1<<1; + break; + case 0xcc: + // SET 1,H + HL.B.B1|=1<<1; + break; + case 0xcd: + // SET 1,L + HL.B.B0|=1<<1; + break; + case 0xce: + // SET 1,(HL) + tempValue=gbReadMemory(HL.W); + tempValue|=1<<1; + gbWriteMemory(HL.W,tempValue); + break; + case 0xcf: + // SET 1,A + AF.B.B1|=1<<1; + break; + case 0xd0: + // SET 2,B + BC.B.B1|=1<<2; + break; + case 0xd1: + // SET 2,C + BC.B.B0|=1<<2; + break; + case 0xd2: + // SET 2,D + DE.B.B1|=1<<2; + break; + case 0xd3: + // SET 2,E + DE.B.B0|=1<<2; + break; + case 0xd4: + // SET 2,H + HL.B.B1|=1<<2; + break; + case 0xd5: + // SET 2,L + HL.B.B0|=1<<2; + break; + case 0xd6: + // SET 2,(HL) + tempValue=gbReadMemory(HL.W); + tempValue|=1<<2; + gbWriteMemory(HL.W,tempValue); + break; + case 0xd7: + // SET 2,A + AF.B.B1|=1<<2; + break; + case 0xd8: + // SET 3,B + BC.B.B1|=1<<3; + break; + case 0xd9: + // SET 3,C + BC.B.B0|=1<<3; + break; + case 0xda: + // SET 3,D + DE.B.B1|=1<<3; + break; + case 0xdb: + // SET 3,E + DE.B.B0|=1<<3; + break; + case 0xdc: + // SET 3,H + HL.B.B1|=1<<3; + break; + case 0xdd: + // SET 3,L + HL.B.B0|=1<<3; + break; + case 0xde: + // SET 3,(HL) + tempValue=gbReadMemory(HL.W); + tempValue|=1<<3; + gbWriteMemory(HL.W,tempValue); + break; + case 0xdf: + // SET 3,A + AF.B.B1|=1<<3; + break; + case 0xe0: + // SET 4,B + BC.B.B1|=1<<4; + break; + case 0xe1: + // SET 4,C + BC.B.B0|=1<<4; + break; + case 0xe2: + // SET 4,D + DE.B.B1|=1<<4; + break; + case 0xe3: + // SET 4,E + DE.B.B0|=1<<4; + break; + case 0xe4: + // SET 4,H + HL.B.B1|=1<<4; + break; + case 0xe5: + // SET 4,L + HL.B.B0|=1<<4; + break; + case 0xe6: + // SET 4,(HL) + tempValue=gbReadMemory(HL.W); + tempValue|=1<<4; + gbWriteMemory(HL.W,tempValue); + break; + case 0xe7: + // SET 4,A + AF.B.B1|=1<<4; + break; + case 0xe8: + // SET 5,B + BC.B.B1|=1<<5; + break; + case 0xe9: + // SET 5,C + BC.B.B0|=1<<5; + break; + case 0xea: + // SET 5,D + DE.B.B1|=1<<5; + break; + case 0xeb: + // SET 5,E + DE.B.B0|=1<<5; + break; + case 0xec: + // SET 5,H + HL.B.B1|=1<<5; + break; + case 0xed: + // SET 5,L + HL.B.B0|=1<<5; + break; + case 0xee: + // SET 5,(HL) + tempValue=gbReadMemory(HL.W); + tempValue|=1<<5; + gbWriteMemory(HL.W,tempValue); + break; + case 0xef: + // SET 5,A + AF.B.B1|=1<<5; + break; + case 0xf0: + // SET 6,B + BC.B.B1|=1<<6; + break; + case 0xf1: + // SET 6,C + BC.B.B0|=1<<6; + break; + case 0xf2: + // SET 6,D + DE.B.B1|=1<<6; + break; + case 0xf3: + // SET 6,E + DE.B.B0|=1<<6; + break; + case 0xf4: + // SET 6,H + HL.B.B1|=1<<6; + break; + case 0xf5: + // SET 6,L + HL.B.B0|=1<<6; + break; + case 0xf6: + // SET 6,(HL) + tempValue=gbReadMemory(HL.W); + tempValue|=1<<6; + gbWriteMemory(HL.W,tempValue); + break; + case 0xf7: + // SET 6,A + AF.B.B1|=1<<6; + break; + case 0xf8: + // SET 7,B + BC.B.B1|=1<<7; + break; + case 0xf9: + // SET 7,C + BC.B.B0|=1<<7; + break; + case 0xfa: + // SET 7,D + DE.B.B1|=1<<7; + break; + case 0xfb: + // SET 7,E + DE.B.B0|=1<<7; + break; + case 0xfc: + // SET 7,H + HL.B.B1|=1<<7; + break; + case 0xfd: + // SET 7,L + HL.B.B0|=1<<7; + break; + case 0xfe: + // SET 7,(HL) + tempValue=gbReadMemory(HL.W); + tempValue|=1<<7; + gbWriteMemory(HL.W,tempValue); + break; + case 0xff: + // SET 7,A + AF.B.B1|=1<<7; + break; + default: + if (gbSystemMessage == false) + { + systemMessage(0, N_("Unknown opcode %02x at %04x"), + gbReadOpcode(PC.W-1),PC.W-1); + gbSystemMessage =true; + } + return; diff --git a/src/dmg/gbDis.cpp b/src/dmg/gbDis.cpp new file mode 100644 index 00000000..055a5a27 --- /dev/null +++ b/src/dmg/gbDis.cpp @@ -0,0 +1,249 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include + +#include "../System.h" +#include "gbGlobals.h" + +typedef struct { + u8 mask; + u8 value; + const char *mnen; +} GBOPCODE; + +#define GB_READ(x) gbMemoryMap[(x)>>12][(x)&0xfff] + +static const char *registers[] = + { "B", "C", "D", "E", "H", "L", "(HL)", "A" }; + +static const char *registers16[] = + { "BC", "DE", "HL", "SP", // for some operations + "BC", "DE", "HL", "AF" }; // for push/pop + +static const char *cond[] = + { "NZ", "Z", "NC", "C" }; + +static char hexDigits[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' +}; + +static GBOPCODE opcodes[] = { + { 0xff, 0x00, "NOP" }, + { 0xcf, 0x01, "LD %R4,%W" }, + { 0xff, 0x02, "LD (BC),A" }, + { 0xcf, 0x03, "INC %R4" }, + { 0xc7, 0x04, "INC %r3" }, + { 0xc7, 0x05, "DEC %r3" }, + { 0xc7, 0x06, "LD %r3,%B" }, + { 0xff, 0x07, "RLCA" }, + { 0xff, 0x08, "LD (%W),SP" }, + { 0xcf, 0x09, "ADD HL,%R4" }, + { 0xff, 0x0a, "LD A,(BC)" }, + { 0xcf, 0x0b, "DEC %R4" }, + { 0xff, 0x0f, "RRCA" }, + { 0xff, 0x10, "STOP" }, + { 0xff, 0x12, "LD (DE),A" }, + { 0xff, 0x17, "RLA" }, + { 0xff, 0x18, "JR %d" }, + { 0xff, 0x1a, "LD A,(DE)" }, + { 0xff, 0x1f, "RRA" }, + { 0xe7, 0x20, "JR %c3,%d" }, + { 0xff, 0x22, "LDI (HL),A" }, + { 0xff, 0x27, "DAA" }, + { 0xff, 0x2a, "LDI A,(HL)" }, + { 0xff, 0x2f, "CPL" }, + { 0xff, 0x32, "LDD (HL),A" }, + { 0xff, 0x37, "SCF" }, + { 0xff, 0x3a, "LDD A,(HL)" }, + { 0xff, 0x3f, "CCF" }, + { 0xff, 0x76, "HALT" }, + { 0xc0, 0x40, "LD %r3,%r0" }, + { 0xf8, 0x80, "ADD A,%r0" }, + { 0xf8, 0x88, "ADC A,%r0" }, + { 0xf8, 0x90, "SUB %r0" }, + { 0xf8, 0x98, "SBC A,%r0" }, + { 0xf8, 0xa0, "AND %r0" }, + { 0xf8, 0xa8, "XOR %r0" }, + { 0xf8, 0xb0, "OR %r0" }, + { 0xf8, 0xb8, "CP %r0" }, + { 0xe7, 0xc0, "RET %c3" }, + { 0xcf, 0xc1, "POP %t4" }, + { 0xe7, 0xc2, "JP %c3,%W" }, + { 0xff, 0xc3, "JP %W" }, + { 0xe7, 0xc4, "CALL %c3,%W" }, + { 0xcf, 0xc5, "PUSH %t4" }, + { 0xff, 0xc6, "ADD A,%B" }, + { 0xc7, 0xc7, "RST %P" }, + { 0xff, 0xc9, "RET" }, + { 0xff, 0xcd, "CALL %W" }, + { 0xff, 0xce, "ADC %B" }, + { 0xff, 0xd6, "SUB %B" }, + { 0xff, 0xd9, "RETI" }, + { 0xff, 0xde, "SBC %B" }, + { 0xff, 0xe0, "LD (FF%B),A" }, + { 0xff, 0xe2, "LD (FF00h+C),A" }, + { 0xff, 0xe6, "AND %B" }, + { 0xff, 0xe8, "ADD SP,%D" }, + { 0xff, 0xe9, "LD PC,HL" }, + { 0xff, 0xea, "LD (%W),A" }, + { 0xff, 0xee, "XOR %B" }, + { 0xff, 0xf0, "LD A,(FF%B)" }, + { 0xff, 0xf2, "LD A,(FF00h+C)" }, + { 0xff, 0xf3, "DI" }, + { 0xff, 0xf6, "OR %B" }, + { 0xff, 0xf8, "LD HL,SP%D" }, + { 0xff, 0xf9, "LD SP,HL" }, + { 0xff, 0xfa, "LD A,(%W)" }, + { 0xff, 0xfb, "EI" }, + { 0xff, 0xfe, "CP %B" }, + { 0x00, 0x00, "DB %B" } +}; + +static GBOPCODE cbOpcodes[] = { + { 0xf8, 0x00, "RLC %r0" }, + { 0xf8, 0x08, "RRC %r0" }, + { 0xf8, 0x10, "RL %r0" }, + { 0xf8, 0x18, "RR %r0" }, + { 0xf8, 0x20, "SLA %r0" }, + { 0xf8, 0x28, "SRA %r0" }, + { 0xf8, 0x30, "SWAP %r0" }, + { 0xf8, 0x38, "SRL %r0" }, + { 0xc0, 0x40, "BIT %b,%r0" }, + { 0xc0, 0x80, "RES %b,%r0" }, + { 0xc0, 0xc0, "SET %b,%r0" }, + { 0x00, 0x00, "DB CBh,%B" } +}; + +static char *addHex(char *p, u8 value) +{ + *p++ = hexDigits[value >> 4]; + *p++ = hexDigits[value & 15]; + return p; +} + +static char *addHex16(char *p, u16 value) +{ + p = addHex(p, value>>8); + return addHex(p, value & 255); +} + +static char *addStr(char *p, const char *s) +{ + while(*s) { + *p++ = *s++; + } + return p; +} + +int gbDis(char *buffer, u16 address) +{ + char *p = buffer; + int instr = 1; + u16 addr = address; + sprintf(p, "%04x ", address); + p += 12; + + u8 opcode = GB_READ(address); + address++; + const char *mnen; + GBOPCODE *op; + if(opcode == 0xcb) { + opcode = GB_READ(address); + address++; + instr++; + op = cbOpcodes; + } else { + op = opcodes; + } + while(op->value != (opcode & op->mask)) op++; + mnen = op->mnen; + + u8 b0, b1; + s8 disp; + int shift; + + while(*mnen) { + if(*mnen == '%') { + mnen++; + switch(*mnen++) { + case 'W': + b0 = GB_READ(address); + address++; + b1 = GB_READ(address); + address++; + p = addHex16(p, b0|b1<<8); + instr += 2; + *p++ = 'h'; + break; + case 'B': + p = addHex(p, GB_READ(address)); + *p++ = 'h'; + address++; + instr++; + break; + case 'D': + disp = GB_READ(address); + if(disp >= 0) + *p++ = '+'; + p += sprintf(p, "%d", disp); + instr++; + break; + case 'd': + disp = GB_READ(address); + address++; + p = addHex16(p, address+disp); + *p++ = 'h'; + instr++; + break; + case 'b': + // kind of a hack, but it works :-) + *p++ = hexDigits[(opcode >> 3) & 7]; + break; + case 'r': + shift = *mnen++ - '0'; + p = addStr(p, registers[(opcode >> shift) & 7]); + break; + case 'R': + shift = *mnen++ - '0'; + p = addStr(p, registers16[(opcode >> shift) & 3]); + break; + case 't': + shift = *mnen++ - '0'; + p = addStr(p, registers16[4+((opcode >> shift) & 3)]); + break; + case 'P': + p = addHex(p, ((opcode >> 3) & 7) * 8); + break; + case 'c': + shift = *mnen++ - '0'; + p = addStr(p, cond[(opcode >> shift) & 3]); + break; + } + } else + *p++ = *mnen++; + } + for(int i = 0; i < instr; i++) { + u16 a = addr + i; + addHex(buffer+5+i*2, GB_READ(a)); + } + *p = 0; + return instr; +} diff --git a/src/dmg/gbGfx.cpp b/src/dmg/gbGfx.cpp new file mode 100644 index 00000000..59f36868 --- /dev/null +++ b/src/dmg/gbGfx.cpp @@ -0,0 +1,601 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005-2006 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include + +#include "../agb/GBA.h" +#include "gbGlobals.h" +#include "gbSGB.h" + +u8 gbInvertTab[256] = { + 0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0, + 0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0, + 0x08,0x88,0x48,0xc8,0x28,0xa8,0x68,0xe8, + 0x18,0x98,0x58,0xd8,0x38,0xb8,0x78,0xf8, + 0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4, + 0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4, + 0x0c,0x8c,0x4c,0xcc,0x2c,0xac,0x6c,0xec, + 0x1c,0x9c,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc, + 0x02,0x82,0x42,0xc2,0x22,0xa2,0x62,0xe2, + 0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2, + 0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea, + 0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x7a,0xfa, + 0x06,0x86,0x46,0xc6,0x26,0xa6,0x66,0xe6, + 0x16,0x96,0x56,0xd6,0x36,0xb6,0x76,0xf6, + 0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee, + 0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe, + 0x01,0x81,0x41,0xc1,0x21,0xa1,0x61,0xe1, + 0x11,0x91,0x51,0xd1,0x31,0xb1,0x71,0xf1, + 0x09,0x89,0x49,0xc9,0x29,0xa9,0x69,0xe9, + 0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9, + 0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5, + 0x15,0x95,0x55,0xd5,0x35,0xb5,0x75,0xf5, + 0x0d,0x8d,0x4d,0xcd,0x2d,0xad,0x6d,0xed, + 0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,0x7d,0xfd, + 0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3, + 0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3, + 0x0b,0x8b,0x4b,0xcb,0x2b,0xab,0x6b,0xeb, + 0x1b,0x9b,0x5b,0xdb,0x3b,0xbb,0x7b,0xfb, + 0x07,0x87,0x47,0xc7,0x27,0xa7,0x67,0xe7, + 0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7, + 0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef, + 0x1f,0x9f,0x5f,0xdf,0x3f,0xbf,0x7f,0xff +}; + +u16 gbLineMix[160]; +u16 gbWindowColor[160]; +extern int inUseRegister_WY; + +void gbRenderLine() +{ + memset(gbLineMix, 0, sizeof(gbLineMix)); + u8 * bank0; + u8 * bank1; + if(gbCgbMode) { + bank0 = &gbVram[0x0000]; + bank1 = &gbVram[0x2000]; + } else { + bank0 = &gbMemory[0x8000]; + bank1 = NULL; + } + + int tile_map = 0x1800; + if((register_LCDC & 8) != 0) + tile_map = 0x1c00; + + int tile_pattern = 0x0800; + + if((register_LCDC & 16) != 0) + tile_pattern = 0x0000; + + int x = 0; + int y = register_LY; + + if(y >= 144) + return; + + int SpritesTicks = gbSpritesTicks[x]*(gbSpeed ? 2 : 4); + int sx = gbSCXLine[(gbSpeed ? 0 : 4)+SpritesTicks]; + int sy = gbSCYLine[(gbSpeed ? 11 : 5)+SpritesTicks]; + + sy+=y; + + sy &= 255; + + int tx = sx >> 3; + int ty = sy >> 3; + + int bx = 1 << (7 - (sx & 7)); + int by = sy & 7; + + int tile_map_line_y = tile_map + ty * 32; + + int tile_map_address = tile_map_line_y + tx; + + u8 attrs = 0; + if(bank1 != NULL) + attrs = bank1[tile_map_address]; + + u8 tile = bank0[tile_map_address]; + + tile_map_address++; + + if(!(register_LCDC & 0x10)) + tile ^= 0x80; + + int tile_pattern_address = tile_pattern + tile * 16 + by*2; + + if(register_LCDC & 0x80) { + if((register_LCDC & 0x01 || gbCgbMode) && (layerSettings & 0x0100)) { + while(x < 160) { + + + + u8 tile_a = 0; + u8 tile_b = 0; + + if(attrs & 0x40) { + tile_pattern_address = tile_pattern + tile * 16 + (7-by)*2; + } + + if(attrs & 0x08) { + tile_a = bank1[tile_pattern_address++]; + tile_b = bank1[tile_pattern_address]; + } else { + tile_a = bank0[tile_pattern_address++]; + tile_b = bank0[tile_pattern_address]; + } + + if(attrs & 0x20) { + tile_a = gbInvertTab[tile_a]; + tile_b = gbInvertTab[tile_b]; + } + + while(bx > 0) { + u8 c = (tile_a & bx) ? 1 : 0; + c += ((tile_b & bx) ? 2 : 0); + + gbLineBuffer[x] = c; // mark the gbLineBuffer color + + if(attrs & 0x80) + gbLineBuffer[x] |= 0x300; + + if(gbCgbMode) { + c = c + (attrs & 7)*4; + } else { + c = (gbBgpLine[x+(gbSpeed ? 5 : 11)+SpritesTicks]>>(c<<1)) &3; + if(gbSgbMode && !gbCgbMode) { + int dx = x >> 3; + int dy = y >> 3; + + int palette = gbSgbATF[dy * 20 + dx]; + + if(c == 0) + palette = 0; + + c = c + 4*palette; + } + } + gbLineMix[x] = gbColorOption ? gbColorFilter[gbPalette[c] & 0x7FFF] : + gbPalette[c] & 0x7FFF; + x++; + if(x >= 160) + break; + bx >>= 1; + } + + bx = 128; + + SpritesTicks = gbSpritesTicks[x]*(gbSpeed ? 2 : 4); + + sx = gbSCXLine[x+(gbSpeed ? 0 : 4)+SpritesTicks]; + + sy = gbSCYLine[x+(gbSpeed ? 11 : 5)+SpritesTicks]; + + + tx = ((sx+x)>>3) & 0x1f; + + sy+=y; + + sy &= 255; + + ty = sy >> 3; + + by = sy & 7; + + tile_pattern_address = tile_pattern + tile * 16 + by * 2; + + tile_map_line_y = tile_map + ty * 32; + + tile_map_address = tile_map_line_y + tx; + + if(bank1) + attrs = bank1[tile_map_line_y + tx]; + + tile = bank0[tile_map_line_y + tx]; + + if(!(register_LCDC & 0x10)) + tile ^= 0x80; + + tile_pattern_address = tile_pattern + tile * 16 + by * 2; + } + } else { + // Use gbBgp[0] instead of 0 (?) + // (this fixes white flashes on Last Bible II) + // Also added the gbColorOption (fixes Dracula Densetsu II color problems) + for(int i = 0; i < 160; i++) + { + u16 color = gbColorOption ? gbColorFilter[0x7FFF] : + 0x7FFF; + if (!gbCgbMode) + color = gbColorOption ? gbColorFilter[gbPalette[gbBgpLine[i+(gbSpeed ? 5 : 11)+gbSpritesTicks[i]*(gbSpeed ? 2 : 4)]&3] & 0x7FFF] : + gbPalette[gbBgpLine[i+(gbSpeed ? 5 : 11)+gbSpritesTicks[i]*(gbSpeed ? 2 : 4)]&3] & 0x7FFF; + gbLineMix[i] = color; + gbLineBuffer[i] = 0; + } + } + + // do the window display + // LCDC.0 also enables/disables the window in !gbCgbMode ?!?! + // (tested on real hardware) + // This fixes Last Bible II & Zankurou Musouken + if((register_LCDC & 0x01 || gbCgbMode) && (register_LCDC & 0x20) && + (layerSettings & 0x2000) && (gbWindowLine != -2)) { + int i = 0; + // Fix (accurate emulation) for most of the window display problems + // (ie. Zen - Intergalactic Ninja, Urusei Yatsura...). + if ((gbWindowLine == -1) || (gbWindowLine>144)) + { + inUseRegister_WY = oldRegister_WY; + if (register_LY>oldRegister_WY) + gbWindowLine = 146; + // for (i = 0; i<160; i++) + // gbWindowColor[i] = gbLineMix[i]; + } + + int wy = inUseRegister_WY; + + if(y >= inUseRegister_WY) { + + if (gbWindowLine == -1) + gbWindowLine = 0; + + int wx = register_WX; + int swx = 0; + wx -= 7; + + if( wx <= 159 && gbWindowLine <= 143) { + + tile_map = 0x1800; + + if((register_LCDC & 0x40) != 0) + tile_map = 0x1c00; + + + tx = 0; + ty = gbWindowLine >> 3; + + bx = 128; + by = gbWindowLine & 7; + + // Tries to emulate the 'window scrolling bug' when wx == 0 (ie. wx-7 == -7). + // Nothing close to perfect, but good enought for now... + if (wx == -7) + { + swx = 7-((gbSCXLine[0]-1) & 7); + bx >>= ((gbSCXLine[0]+((swx != 1) ? 1 : 0)) & 7); + if (swx == 1) + swx = 2; + + //bx >>= ((gbSCXLine[0]+(((swx>1) && (swx != 7)) ? 1 : 0)) & 7); + + if ((swx == 7)) + { + //wx = 0; + if ((gbWindowLine>0) || (wy == 0)) + swx = 0; + } + } + else + if(wx < 0) { + bx >>= (-wx); + wx = 0; + } + + tile_map_line_y = tile_map + ty * 32; + + tile_map_address = tile_map_line_y + tx; + + x = wx; + + tile = bank0[tile_map_address]; + u8 attrs = 0; + if(bank1) + attrs = bank1[tile_map_address]; + tile_map_address++; + + if((register_LCDC & 16) == 0) { + if(tile < 128) tile += 128; + else tile -= 128; + } + + tile_pattern_address = tile_pattern + tile * 16 + by*2; + + if (wx) + for (i = 0; i 0) { + u8 c = (tile_a & bx) != 0 ? 1 : 0; + c += ((tile_b & bx) != 0 ? 2 : 0); + + if (x>=0) + { + if(attrs & 0x80) + gbLineBuffer[x] = 0x300 + c; + else + gbLineBuffer[x] = 0x100 + c; + + if(gbCgbMode) { + c = c + (attrs & 7) * 4; + } else { + c = (gbBgpLine[x+(gbSpeed ? 5 : 11)+gbSpritesTicks[x]*(gbSpeed ? 2 : 4)]>>(c<<1)) &3; + if(gbSgbMode && !gbCgbMode) { + int dx = x >> 3; + int dy = y >> 3; + + int palette = gbSgbATF[dy * 20 + dx]; + + if(c == 0) + palette = 0; + + c = c + 4*palette; + } + } + gbLineMix[x] = gbColorOption ? gbColorFilter[gbPalette[c] & 0x7FFF] : + gbPalette[c] & 0x7FFF; + } + x++; + if(x >= 160) + break; + bx >>= 1; + } + tx++; + if(tx == 32) + tx = 0; + bx = 128; + tile = bank0[tile_map_line_y + tx]; + if(bank1) + attrs = bank1[tile_map_line_y + tx]; + + if((register_LCDC & 16) == 0) { + if(tile < 128) tile += 128; + else tile -= 128; + } + tile_pattern_address = tile_pattern + tile * 16 + by * 2; + } + + //for (i = swx; i<160; i++) + // gbLineMix[i] = gbWindowColor[i]; + gbWindowLine++; + } + } + } + else if (gbWindowLine == -2) + { + inUseRegister_WY = oldRegister_WY; + if (register_LY>oldRegister_WY) + gbWindowLine = 146; + else + gbWindowLine = 0; + } + } else { + u16 color = gbColorOption ? gbColorFilter[0x7FFF] : + 0x7FFF; + if (!gbCgbMode) + color = gbColorOption ? gbColorFilter[gbPalette[0] & 0x7FFF] : + gbPalette[0] & 0x7FFF; + for(int i = 0; i < 160; i++) + { + gbLineMix[i] = color; + gbLineBuffer[i] = 0; + } + } +} + +void gbDrawSpriteTile(int tile, int x,int y,int t, int flags, + int size,int spriteNumber) +{ + u8 * bank0; + u8 * bank1; + if(gbCgbMode) { + if(register_VBK & 1) { + bank0 = &gbVram[0x0000]; + bank1 = &gbVram[0x2000]; + } else { + bank0 = &gbVram[0x0000]; + bank1 = &gbVram[0x2000]; + } + } else { + bank0 = &gbMemory[0x8000]; + bank1 = NULL; + } + + int init = 0x0000; + + for (int i = 0; i<4; i++) + { + gbObp0[i] = (gbObp0Line[x+11+gbSpritesTicks[x]*(gbSpeed ? 2 : 4)]>>(i<<1)) & 3; + gbObp1[i] = (gbObp1Line[x+11+gbSpritesTicks[x]*(gbSpeed ? 2 : 4)]>>(i<<1)) & 3; + } + u8 *pal = gbObp0; + + int flipx = (flags & 0x20); + int flipy = (flags & 0x40); + + if((flags & 0x10)) + pal = gbObp1; + + if(flipy) { + t = (size ? 15 : 7) - t; + } + + int prio = flags & 0x80; + + int address = init + tile * 16 + 2*t; + int a = 0; + int b = 0; + + if(gbCgbMode && (flags & 0x08)) { + a = bank1[address++]; + b = bank1[address++]; + } else { + a = bank0[address++]; + b = bank0[address++]; + } + + for(int xx = 0; xx < 8; xx++) { + u8 mask = 1 << (7-xx); + u8 c = 0; + if( (a & mask)) + c++; + if( (b & mask)) + c+=2; + + if(c==0) continue; + + int xxx = xx+x; + if(flipx) + xxx = (7-xx+x); + + if(xxx < 0 || xxx > 159) + continue; + + u16 color = gbLineBuffer[xxx]; + + // Fixes OAM-BG priority + if(prio && (register_LCDC & 1)) { + if(color < 0x200 && ((color & 0xFF) != 0)) + continue; + } + // Fixes OAM-BG priority for Moorhuhn 2 + if(color >= 0x300 && color != 0x300 && (register_LCDC & 1)) + continue; + else if(color >= 0x200 && color < 0x300) { + int sprite = color & 0xff; + + int spriteX = gbMemory[0xfe00 + 4 * sprite + 1] - 8; + + if(spriteX == x) { + if(sprite < spriteNumber) + continue; + } else { + if(gbCgbMode) { + if(sprite < spriteNumber) + continue; + } else { + // Fixes GB sprites priorities (was '< x + 8' before) + // ('A boy and his blob...' sprites' emulation is now correct) + if(spriteX < x) + continue; + } + } + } + + + gbLineBuffer[xxx] = 0x200 + spriteNumber; + + // make sure that sprites will work even in CGB mode + if(gbCgbMode) { + c = c + (flags & 0x07)*4 + 32; + } else { + c = pal[c]; + + if(gbSgbMode && !gbCgbMode) { + int dx = xxx >> 3; + int dy = y >> 3; + + int palette = gbSgbATF[dy * 20 + dx]; + + if(c == 0) + palette = 0; + + c = c + 4*palette; + } else { + c += 4; + } + } + + gbLineMix[xxx] = gbColorOption ? gbColorFilter[gbPalette[c] & 0x7FFF] : + gbPalette[c] & 0x7FFF; + } +} + +void gbDrawSprites(bool draw) +{ + int x = 0; + int y = 0; + int count = 0; + + int size = (register_LCDC & 4); + + if (!draw) + memset (gbSpritesTicks, 0, sizeof(gbSpritesTicks)); + + if(!(register_LCDC & 0x80)) + return; + + if((register_LCDC & 2) && (layerSettings & 0x1000)) { + int yc = register_LY; + + int address = 0xfe00; + for(int i = 0; i < 40; i++) { + y = gbMemory[address++]; + x = gbMemory[address++]; + int tile = gbMemory[address++]; + if(size) + tile &= 254; + int flags = gbMemory[address++]; + + if(x > 0 && y > 0 && x < 168 && y < 160) { + // check if sprite intersects current line + int t = yc -y + 16; + if((size && t >=0 && t < 16) || (!size && t >= 0 && t < 8)) { + if (draw) + gbDrawSpriteTile(tile,x-8,yc,t,flags,size,i); + else + { + for (int j = x-8; j<300; j++) + if (j>=0) + if (gbSpeed) + gbSpritesTicks[j] += 5; + else + gbSpritesTicks[j] += 2+(count&1); + + } + count++; + } + } + // sprite limit reached! + if(count >= 10) + break; + } + } + return; +} diff --git a/src/dmg/gbGfx.cpp.bak b/src/dmg/gbGfx.cpp.bak new file mode 100644 index 00000000..59f36868 --- /dev/null +++ b/src/dmg/gbGfx.cpp.bak @@ -0,0 +1,601 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005-2006 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include + +#include "../agb/GBA.h" +#include "gbGlobals.h" +#include "gbSGB.h" + +u8 gbInvertTab[256] = { + 0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0, + 0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0, + 0x08,0x88,0x48,0xc8,0x28,0xa8,0x68,0xe8, + 0x18,0x98,0x58,0xd8,0x38,0xb8,0x78,0xf8, + 0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4, + 0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4, + 0x0c,0x8c,0x4c,0xcc,0x2c,0xac,0x6c,0xec, + 0x1c,0x9c,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc, + 0x02,0x82,0x42,0xc2,0x22,0xa2,0x62,0xe2, + 0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2, + 0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea, + 0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x7a,0xfa, + 0x06,0x86,0x46,0xc6,0x26,0xa6,0x66,0xe6, + 0x16,0x96,0x56,0xd6,0x36,0xb6,0x76,0xf6, + 0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee, + 0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe, + 0x01,0x81,0x41,0xc1,0x21,0xa1,0x61,0xe1, + 0x11,0x91,0x51,0xd1,0x31,0xb1,0x71,0xf1, + 0x09,0x89,0x49,0xc9,0x29,0xa9,0x69,0xe9, + 0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9, + 0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5, + 0x15,0x95,0x55,0xd5,0x35,0xb5,0x75,0xf5, + 0x0d,0x8d,0x4d,0xcd,0x2d,0xad,0x6d,0xed, + 0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,0x7d,0xfd, + 0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3, + 0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3, + 0x0b,0x8b,0x4b,0xcb,0x2b,0xab,0x6b,0xeb, + 0x1b,0x9b,0x5b,0xdb,0x3b,0xbb,0x7b,0xfb, + 0x07,0x87,0x47,0xc7,0x27,0xa7,0x67,0xe7, + 0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7, + 0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef, + 0x1f,0x9f,0x5f,0xdf,0x3f,0xbf,0x7f,0xff +}; + +u16 gbLineMix[160]; +u16 gbWindowColor[160]; +extern int inUseRegister_WY; + +void gbRenderLine() +{ + memset(gbLineMix, 0, sizeof(gbLineMix)); + u8 * bank0; + u8 * bank1; + if(gbCgbMode) { + bank0 = &gbVram[0x0000]; + bank1 = &gbVram[0x2000]; + } else { + bank0 = &gbMemory[0x8000]; + bank1 = NULL; + } + + int tile_map = 0x1800; + if((register_LCDC & 8) != 0) + tile_map = 0x1c00; + + int tile_pattern = 0x0800; + + if((register_LCDC & 16) != 0) + tile_pattern = 0x0000; + + int x = 0; + int y = register_LY; + + if(y >= 144) + return; + + int SpritesTicks = gbSpritesTicks[x]*(gbSpeed ? 2 : 4); + int sx = gbSCXLine[(gbSpeed ? 0 : 4)+SpritesTicks]; + int sy = gbSCYLine[(gbSpeed ? 11 : 5)+SpritesTicks]; + + sy+=y; + + sy &= 255; + + int tx = sx >> 3; + int ty = sy >> 3; + + int bx = 1 << (7 - (sx & 7)); + int by = sy & 7; + + int tile_map_line_y = tile_map + ty * 32; + + int tile_map_address = tile_map_line_y + tx; + + u8 attrs = 0; + if(bank1 != NULL) + attrs = bank1[tile_map_address]; + + u8 tile = bank0[tile_map_address]; + + tile_map_address++; + + if(!(register_LCDC & 0x10)) + tile ^= 0x80; + + int tile_pattern_address = tile_pattern + tile * 16 + by*2; + + if(register_LCDC & 0x80) { + if((register_LCDC & 0x01 || gbCgbMode) && (layerSettings & 0x0100)) { + while(x < 160) { + + + + u8 tile_a = 0; + u8 tile_b = 0; + + if(attrs & 0x40) { + tile_pattern_address = tile_pattern + tile * 16 + (7-by)*2; + } + + if(attrs & 0x08) { + tile_a = bank1[tile_pattern_address++]; + tile_b = bank1[tile_pattern_address]; + } else { + tile_a = bank0[tile_pattern_address++]; + tile_b = bank0[tile_pattern_address]; + } + + if(attrs & 0x20) { + tile_a = gbInvertTab[tile_a]; + tile_b = gbInvertTab[tile_b]; + } + + while(bx > 0) { + u8 c = (tile_a & bx) ? 1 : 0; + c += ((tile_b & bx) ? 2 : 0); + + gbLineBuffer[x] = c; // mark the gbLineBuffer color + + if(attrs & 0x80) + gbLineBuffer[x] |= 0x300; + + if(gbCgbMode) { + c = c + (attrs & 7)*4; + } else { + c = (gbBgpLine[x+(gbSpeed ? 5 : 11)+SpritesTicks]>>(c<<1)) &3; + if(gbSgbMode && !gbCgbMode) { + int dx = x >> 3; + int dy = y >> 3; + + int palette = gbSgbATF[dy * 20 + dx]; + + if(c == 0) + palette = 0; + + c = c + 4*palette; + } + } + gbLineMix[x] = gbColorOption ? gbColorFilter[gbPalette[c] & 0x7FFF] : + gbPalette[c] & 0x7FFF; + x++; + if(x >= 160) + break; + bx >>= 1; + } + + bx = 128; + + SpritesTicks = gbSpritesTicks[x]*(gbSpeed ? 2 : 4); + + sx = gbSCXLine[x+(gbSpeed ? 0 : 4)+SpritesTicks]; + + sy = gbSCYLine[x+(gbSpeed ? 11 : 5)+SpritesTicks]; + + + tx = ((sx+x)>>3) & 0x1f; + + sy+=y; + + sy &= 255; + + ty = sy >> 3; + + by = sy & 7; + + tile_pattern_address = tile_pattern + tile * 16 + by * 2; + + tile_map_line_y = tile_map + ty * 32; + + tile_map_address = tile_map_line_y + tx; + + if(bank1) + attrs = bank1[tile_map_line_y + tx]; + + tile = bank0[tile_map_line_y + tx]; + + if(!(register_LCDC & 0x10)) + tile ^= 0x80; + + tile_pattern_address = tile_pattern + tile * 16 + by * 2; + } + } else { + // Use gbBgp[0] instead of 0 (?) + // (this fixes white flashes on Last Bible II) + // Also added the gbColorOption (fixes Dracula Densetsu II color problems) + for(int i = 0; i < 160; i++) + { + u16 color = gbColorOption ? gbColorFilter[0x7FFF] : + 0x7FFF; + if (!gbCgbMode) + color = gbColorOption ? gbColorFilter[gbPalette[gbBgpLine[i+(gbSpeed ? 5 : 11)+gbSpritesTicks[i]*(gbSpeed ? 2 : 4)]&3] & 0x7FFF] : + gbPalette[gbBgpLine[i+(gbSpeed ? 5 : 11)+gbSpritesTicks[i]*(gbSpeed ? 2 : 4)]&3] & 0x7FFF; + gbLineMix[i] = color; + gbLineBuffer[i] = 0; + } + } + + // do the window display + // LCDC.0 also enables/disables the window in !gbCgbMode ?!?! + // (tested on real hardware) + // This fixes Last Bible II & Zankurou Musouken + if((register_LCDC & 0x01 || gbCgbMode) && (register_LCDC & 0x20) && + (layerSettings & 0x2000) && (gbWindowLine != -2)) { + int i = 0; + // Fix (accurate emulation) for most of the window display problems + // (ie. Zen - Intergalactic Ninja, Urusei Yatsura...). + if ((gbWindowLine == -1) || (gbWindowLine>144)) + { + inUseRegister_WY = oldRegister_WY; + if (register_LY>oldRegister_WY) + gbWindowLine = 146; + // for (i = 0; i<160; i++) + // gbWindowColor[i] = gbLineMix[i]; + } + + int wy = inUseRegister_WY; + + if(y >= inUseRegister_WY) { + + if (gbWindowLine == -1) + gbWindowLine = 0; + + int wx = register_WX; + int swx = 0; + wx -= 7; + + if( wx <= 159 && gbWindowLine <= 143) { + + tile_map = 0x1800; + + if((register_LCDC & 0x40) != 0) + tile_map = 0x1c00; + + + tx = 0; + ty = gbWindowLine >> 3; + + bx = 128; + by = gbWindowLine & 7; + + // Tries to emulate the 'window scrolling bug' when wx == 0 (ie. wx-7 == -7). + // Nothing close to perfect, but good enought for now... + if (wx == -7) + { + swx = 7-((gbSCXLine[0]-1) & 7); + bx >>= ((gbSCXLine[0]+((swx != 1) ? 1 : 0)) & 7); + if (swx == 1) + swx = 2; + + //bx >>= ((gbSCXLine[0]+(((swx>1) && (swx != 7)) ? 1 : 0)) & 7); + + if ((swx == 7)) + { + //wx = 0; + if ((gbWindowLine>0) || (wy == 0)) + swx = 0; + } + } + else + if(wx < 0) { + bx >>= (-wx); + wx = 0; + } + + tile_map_line_y = tile_map + ty * 32; + + tile_map_address = tile_map_line_y + tx; + + x = wx; + + tile = bank0[tile_map_address]; + u8 attrs = 0; + if(bank1) + attrs = bank1[tile_map_address]; + tile_map_address++; + + if((register_LCDC & 16) == 0) { + if(tile < 128) tile += 128; + else tile -= 128; + } + + tile_pattern_address = tile_pattern + tile * 16 + by*2; + + if (wx) + for (i = 0; i 0) { + u8 c = (tile_a & bx) != 0 ? 1 : 0; + c += ((tile_b & bx) != 0 ? 2 : 0); + + if (x>=0) + { + if(attrs & 0x80) + gbLineBuffer[x] = 0x300 + c; + else + gbLineBuffer[x] = 0x100 + c; + + if(gbCgbMode) { + c = c + (attrs & 7) * 4; + } else { + c = (gbBgpLine[x+(gbSpeed ? 5 : 11)+gbSpritesTicks[x]*(gbSpeed ? 2 : 4)]>>(c<<1)) &3; + if(gbSgbMode && !gbCgbMode) { + int dx = x >> 3; + int dy = y >> 3; + + int palette = gbSgbATF[dy * 20 + dx]; + + if(c == 0) + palette = 0; + + c = c + 4*palette; + } + } + gbLineMix[x] = gbColorOption ? gbColorFilter[gbPalette[c] & 0x7FFF] : + gbPalette[c] & 0x7FFF; + } + x++; + if(x >= 160) + break; + bx >>= 1; + } + tx++; + if(tx == 32) + tx = 0; + bx = 128; + tile = bank0[tile_map_line_y + tx]; + if(bank1) + attrs = bank1[tile_map_line_y + tx]; + + if((register_LCDC & 16) == 0) { + if(tile < 128) tile += 128; + else tile -= 128; + } + tile_pattern_address = tile_pattern + tile * 16 + by * 2; + } + + //for (i = swx; i<160; i++) + // gbLineMix[i] = gbWindowColor[i]; + gbWindowLine++; + } + } + } + else if (gbWindowLine == -2) + { + inUseRegister_WY = oldRegister_WY; + if (register_LY>oldRegister_WY) + gbWindowLine = 146; + else + gbWindowLine = 0; + } + } else { + u16 color = gbColorOption ? gbColorFilter[0x7FFF] : + 0x7FFF; + if (!gbCgbMode) + color = gbColorOption ? gbColorFilter[gbPalette[0] & 0x7FFF] : + gbPalette[0] & 0x7FFF; + for(int i = 0; i < 160; i++) + { + gbLineMix[i] = color; + gbLineBuffer[i] = 0; + } + } +} + +void gbDrawSpriteTile(int tile, int x,int y,int t, int flags, + int size,int spriteNumber) +{ + u8 * bank0; + u8 * bank1; + if(gbCgbMode) { + if(register_VBK & 1) { + bank0 = &gbVram[0x0000]; + bank1 = &gbVram[0x2000]; + } else { + bank0 = &gbVram[0x0000]; + bank1 = &gbVram[0x2000]; + } + } else { + bank0 = &gbMemory[0x8000]; + bank1 = NULL; + } + + int init = 0x0000; + + for (int i = 0; i<4; i++) + { + gbObp0[i] = (gbObp0Line[x+11+gbSpritesTicks[x]*(gbSpeed ? 2 : 4)]>>(i<<1)) & 3; + gbObp1[i] = (gbObp1Line[x+11+gbSpritesTicks[x]*(gbSpeed ? 2 : 4)]>>(i<<1)) & 3; + } + u8 *pal = gbObp0; + + int flipx = (flags & 0x20); + int flipy = (flags & 0x40); + + if((flags & 0x10)) + pal = gbObp1; + + if(flipy) { + t = (size ? 15 : 7) - t; + } + + int prio = flags & 0x80; + + int address = init + tile * 16 + 2*t; + int a = 0; + int b = 0; + + if(gbCgbMode && (flags & 0x08)) { + a = bank1[address++]; + b = bank1[address++]; + } else { + a = bank0[address++]; + b = bank0[address++]; + } + + for(int xx = 0; xx < 8; xx++) { + u8 mask = 1 << (7-xx); + u8 c = 0; + if( (a & mask)) + c++; + if( (b & mask)) + c+=2; + + if(c==0) continue; + + int xxx = xx+x; + if(flipx) + xxx = (7-xx+x); + + if(xxx < 0 || xxx > 159) + continue; + + u16 color = gbLineBuffer[xxx]; + + // Fixes OAM-BG priority + if(prio && (register_LCDC & 1)) { + if(color < 0x200 && ((color & 0xFF) != 0)) + continue; + } + // Fixes OAM-BG priority for Moorhuhn 2 + if(color >= 0x300 && color != 0x300 && (register_LCDC & 1)) + continue; + else if(color >= 0x200 && color < 0x300) { + int sprite = color & 0xff; + + int spriteX = gbMemory[0xfe00 + 4 * sprite + 1] - 8; + + if(spriteX == x) { + if(sprite < spriteNumber) + continue; + } else { + if(gbCgbMode) { + if(sprite < spriteNumber) + continue; + } else { + // Fixes GB sprites priorities (was '< x + 8' before) + // ('A boy and his blob...' sprites' emulation is now correct) + if(spriteX < x) + continue; + } + } + } + + + gbLineBuffer[xxx] = 0x200 + spriteNumber; + + // make sure that sprites will work even in CGB mode + if(gbCgbMode) { + c = c + (flags & 0x07)*4 + 32; + } else { + c = pal[c]; + + if(gbSgbMode && !gbCgbMode) { + int dx = xxx >> 3; + int dy = y >> 3; + + int palette = gbSgbATF[dy * 20 + dx]; + + if(c == 0) + palette = 0; + + c = c + 4*palette; + } else { + c += 4; + } + } + + gbLineMix[xxx] = gbColorOption ? gbColorFilter[gbPalette[c] & 0x7FFF] : + gbPalette[c] & 0x7FFF; + } +} + +void gbDrawSprites(bool draw) +{ + int x = 0; + int y = 0; + int count = 0; + + int size = (register_LCDC & 4); + + if (!draw) + memset (gbSpritesTicks, 0, sizeof(gbSpritesTicks)); + + if(!(register_LCDC & 0x80)) + return; + + if((register_LCDC & 2) && (layerSettings & 0x1000)) { + int yc = register_LY; + + int address = 0xfe00; + for(int i = 0; i < 40; i++) { + y = gbMemory[address++]; + x = gbMemory[address++]; + int tile = gbMemory[address++]; + if(size) + tile &= 254; + int flags = gbMemory[address++]; + + if(x > 0 && y > 0 && x < 168 && y < 160) { + // check if sprite intersects current line + int t = yc -y + 16; + if((size && t >=0 && t < 16) || (!size && t >= 0 && t < 8)) { + if (draw) + gbDrawSpriteTile(tile,x-8,yc,t,flags,size,i); + else + { + for (int j = x-8; j<300; j++) + if (j>=0) + if (gbSpeed) + gbSpritesTicks[j] += 5; + else + gbSpritesTicks[j] += 2+(count&1); + + } + count++; + } + } + // sprite limit reached! + if(count >= 10) + break; + } + } + return; +} diff --git a/src/dmg/gbGlobals.cpp b/src/dmg/gbGlobals.cpp new file mode 100644 index 00000000..0bbc3ecc --- /dev/null +++ b/src/dmg/gbGlobals.cpp @@ -0,0 +1,57 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005-2006 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "../agb/GBA.h" + +u8 *gbMemoryMap[16]; + +int gbRomSizeMask = 0; +int gbRomSize = 0; +int gbRamSizeMask = 0; +int gbRamSize = 0; +int gbTAMA5ramSize = 0; + +u8 *gbMemory = NULL; +u8 *gbVram = NULL; +u8 *gbRom = NULL; +u8 *gbRam = NULL; +u8 *gbWram = NULL; +u16 *gbLineBuffer = NULL; +u8 *gbTAMA5ram = NULL; + +u16 gbPalette[128]; +u8 gbBgp[4] = { 0, 1, 2, 3}; +u8 gbObp0[4] = { 0, 1, 2, 3}; +u8 gbObp1[4] = { 0, 1, 2, 3}; +int gbWindowLine = -1; + +bool genericflashcardEnable = false; +int gbCgbMode = 0; + +u16 gbColorFilter[32768]; +int gbColorOption = 0; +int gbPaletteOption = 0; +int gbEmulatorType = 0; +int gbBorderOn = 1; +int gbBorderAutomatic = 0; +int gbBorderLineSkip = 160; +int gbBorderRowSkip = 0; +int gbBorderColumnSkip = 0; +int gbDmaTicks = 0; + +u8 (*gbSerialFunction)(u8) = NULL; diff --git a/src/dmg/gbGlobals.h b/src/dmg/gbGlobals.h new file mode 100644 index 00000000..47e2e15e --- /dev/null +++ b/src/dmg/gbGlobals.h @@ -0,0 +1,89 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005-2006 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +extern int gbRomSizeMask; +extern int gbRomSize; +extern int gbRamSize; +extern int gbRamSizeMask; +extern int gbTAMA5ramSize; + +extern bool useBios; +extern bool skipBios; +extern u8 *bios; + +extern u8 *gbRom; +extern u8 *gbRam; +extern u8 *gbVram; +extern u8 *gbWram; +extern u8 *gbMemory; +extern u16 *gbLineBuffer; +extern u8 *gbTAMA5ram; + +extern u8 *gbMemoryMap[16]; + +extern int gbFrameSkip; +extern u16 gbColorFilter[32768]; +extern int gbColorOption; +extern int gbPaletteOption; +extern int gbEmulatorType; +extern int gbBorderOn; +extern int gbBorderAutomatic; +extern int gbCgbMode; +extern int gbSgbMode; +extern int gbWindowLine; +extern int gbSpeed; +extern u8 gbBgp[4]; +extern u8 gbObp0[4]; +extern u8 gbObp1[4]; +extern u16 gbPalette[128]; +extern bool gbScreenOn; +extern bool gbDrawWindow; +extern u8 gbSCYLine[300]; +// gbSCXLine is used for the emulation (bug) of the SX change +// found in the Artic Zone game. +extern u8 gbSCXLine[300]; +// gbBgpLine is used for the emulation of the +// Prehistorik Man's title screen scroller. +extern u8 gbBgpLine[300]; +extern u8 gbObp0Line [300]; +extern u8 gbObp1Line [300]; +// gbSpritesTicks is used for the emulation of Parodius' Laser Beam. +extern u8 gbSpritesTicks[300]; + +extern u8 register_LCDC; +extern u8 register_LY; +extern u8 register_SCY; +extern u8 register_SCX; +extern u8 register_WY; +extern u8 register_WX; +extern u8 register_VBK; +extern u8 oldRegister_WY; + +extern int emulating; +extern bool genericflashcardEnable; + +extern int gbBorderLineSkip; +extern int gbBorderRowSkip; +extern int gbBorderColumnSkip; +extern int gbDmaTicks; + +extern void gbRenderLine(); +extern void gbDrawSprites(bool); + +extern u8 (*gbSerialFunction)(u8); diff --git a/src/dmg/gbGlobals.h.bak b/src/dmg/gbGlobals.h.bak new file mode 100644 index 00000000..47e2e15e --- /dev/null +++ b/src/dmg/gbGlobals.h.bak @@ -0,0 +1,89 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005-2006 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +extern int gbRomSizeMask; +extern int gbRomSize; +extern int gbRamSize; +extern int gbRamSizeMask; +extern int gbTAMA5ramSize; + +extern bool useBios; +extern bool skipBios; +extern u8 *bios; + +extern u8 *gbRom; +extern u8 *gbRam; +extern u8 *gbVram; +extern u8 *gbWram; +extern u8 *gbMemory; +extern u16 *gbLineBuffer; +extern u8 *gbTAMA5ram; + +extern u8 *gbMemoryMap[16]; + +extern int gbFrameSkip; +extern u16 gbColorFilter[32768]; +extern int gbColorOption; +extern int gbPaletteOption; +extern int gbEmulatorType; +extern int gbBorderOn; +extern int gbBorderAutomatic; +extern int gbCgbMode; +extern int gbSgbMode; +extern int gbWindowLine; +extern int gbSpeed; +extern u8 gbBgp[4]; +extern u8 gbObp0[4]; +extern u8 gbObp1[4]; +extern u16 gbPalette[128]; +extern bool gbScreenOn; +extern bool gbDrawWindow; +extern u8 gbSCYLine[300]; +// gbSCXLine is used for the emulation (bug) of the SX change +// found in the Artic Zone game. +extern u8 gbSCXLine[300]; +// gbBgpLine is used for the emulation of the +// Prehistorik Man's title screen scroller. +extern u8 gbBgpLine[300]; +extern u8 gbObp0Line [300]; +extern u8 gbObp1Line [300]; +// gbSpritesTicks is used for the emulation of Parodius' Laser Beam. +extern u8 gbSpritesTicks[300]; + +extern u8 register_LCDC; +extern u8 register_LY; +extern u8 register_SCY; +extern u8 register_SCX; +extern u8 register_WY; +extern u8 register_WX; +extern u8 register_VBK; +extern u8 oldRegister_WY; + +extern int emulating; +extern bool genericflashcardEnable; + +extern int gbBorderLineSkip; +extern int gbBorderRowSkip; +extern int gbBorderColumnSkip; +extern int gbDmaTicks; + +extern void gbRenderLine(); +extern void gbDrawSprites(bool); + +extern u8 (*gbSerialFunction)(u8); diff --git a/src/dmg/gbMemory.cpp b/src/dmg/gbMemory.cpp new file mode 100644 index 00000000..0670bcdf --- /dev/null +++ b/src/dmg/gbMemory.cpp @@ -0,0 +1,1717 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005-2006 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "../agb/GBA.h" +#include "../Port.h" +#include "gbGlobals.h" +#include "gbMemory.h" +#include "gb.h" +u8 gbDaysinMonth [12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; +const u8 gbDisabledRam [8] = {0x80, 0xff, 0xf0, 0x00, 0x30, 0xbf, 0xbf, 0xbf}; +extern int gbHardware; +extern int gbGBCColorType; +extern gbRegister PC; + +mapperMBC1 gbDataMBC1 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // memory model + 0, // ROM high address + 0, // RAM address + 0 // Rom Bank 0 remapping +}; + +// MBC1 ROM write registers +void mapperMBC1ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: // RAM enable register + gbDataMBC1.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); + break; + case 0x2000: // ROM bank select + // value = value & 0x1f; + if ((value == 1) && (address == 0x2100)) + gbDataMBC1.mapperRomBank0Remapping = 1; + + if((value & 0x1f) == 0) + value += 1; + if(value == gbDataMBC1.mapperROMBank) + break; + + tmpAddress = value << 14; + + // check current model + if (gbDataMBC1.mapperRomBank0Remapping == 3) { + tmpAddress = (value & 0xf) << 14; + tmpAddress |= (gbDataMBC1.mapperROMHighAddress & 3) << 18; + } + else + if(gbDataMBC1.mapperMemoryModel == 0) { + // model is 16/8, so we have a high address in use + tmpAddress |= (gbDataMBC1.mapperROMHighAddress & 3) << 19; + } + + tmpAddress &= gbRomSizeMask; + gbDataMBC1.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + break; + case 0x4000: // RAM bank select + if(gbDataMBC1.mapperMemoryModel == 1) { + if (!gbRamSize) + { + if (gbDataMBC1.mapperRomBank0Remapping == 3) + { + gbDataMBC1.mapperROMHighAddress = value & 0x03; + tmpAddress = (gbDataMBC1.mapperROMHighAddress) << 18; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x00] = &gbRom[tmpAddress]; + gbMemoryMap[0x01] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x02] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x03] = &gbRom[tmpAddress + 0x3000]; + gbMemoryMap[0x04] = &gbRom[tmpAddress + 0x4000]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x5000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x6000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x7000]; + } + else gbDataMBC1.mapperRomBank0Remapping = 0; + } + // 4/32 model, RAM bank switching provided + value = value & 0x03; + if(value == gbDataMBC1.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + if(gbRamSize) { + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + } + gbDataMBC1.mapperRAMBank = value; + gbDataMBC1.mapperRAMAddress = tmpAddress; + + if (gbDataMBC1.mapperRomBank0Remapping != 3) + gbDataMBC1.mapperROMHighAddress = 0; + } else { + // 16/8, set the high address + gbDataMBC1.mapperROMHighAddress = value & 0x03; + tmpAddress = gbDataMBC1.mapperROMBank << 14; + tmpAddress |= (gbDataMBC1.mapperROMHighAddress) << 19; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + if(gbRamSize) { + gbMemoryMap[0x0a] = &gbRam[0]; + gbMemoryMap[0x0b] = &gbRam[0x1000]; + } + + gbDataMBC1.mapperRAMBank = 0; + } + break; + case 0x6000: // memory model select + gbDataMBC1.mapperMemoryModel = value & 1; + + if(gbDataMBC1.mapperMemoryModel == 1) { + // 4/32 model, RAM bank switching provided + + value = gbDataMBC1.mapperRAMBank & 0x03; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + if(gbRamSize) { + gbMemoryMap[0x0a] = &gbRam[gbDataMBC1.mapperRAMAddress]; + gbMemoryMap[0x0b] = &gbRam[gbDataMBC1.mapperRAMAddress + 0x1000]; + gbDataMBC1.mapperRomBank0Remapping = 0; + } + else gbDataMBC1.mapperRomBank0Remapping |=2; + + gbDataMBC1.mapperRAMBank = value; + gbDataMBC1.mapperRAMAddress = tmpAddress; + + tmpAddress = gbDataMBC1.mapperROMBank << 14; + + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + } else { + // 16/8, set the high address + + tmpAddress = gbDataMBC1.mapperROMBank << 14; + tmpAddress |= (gbDataMBC1.mapperROMHighAddress) << 19; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + if(gbRamSize) { + gbMemoryMap[0x0a] = &gbRam[0]; + gbMemoryMap[0x0b] = &gbRam[0x1000]; + } + } + break; + } +} + +// MBC1 RAM write +void mapperMBC1RAM(u16 address, u8 value) +{ + if(gbDataMBC1.mapperRAMEnable) { + if(gbRamSize) { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } +} + +// MBC1 read RAM +u8 mapperMBC1ReadRAM(u16 address) +{ + + if(gbDataMBC1.mapperRAMEnable) + return gbMemoryMap[address>>12][address & 0x0fff]; + + if (!genericflashcardEnable) + return 0xff; + else + if ((address & 0x1000) >= 0x1000) + { + // The value returned when reading RAM while it's disabled + // is constant, exept for the GBASP hardware. + // (actually, is the address that read is out of the ROM, the returned value if 0xff...) + if (PC.W>=0xff80) + return 0xff; + else + if ((gbHardware & 0x08) && (gbGBCColorType == 2)) + { + if (address & 1) + return 0xfb; + else + return 0x7a; + } + else + return 0x0a; + } + else + return gbDisabledRam[address & 7]; +} + +void memoryUpdateMapMBC1() +{ + int tmpAddress = gbDataMBC1.mapperROMBank << 14; + + // check current model + if (gbDataMBC1.mapperRomBank0Remapping == 3) { + tmpAddress = (gbDataMBC1.mapperROMHighAddress & 3) << 18; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x00] = &gbRom[tmpAddress]; + gbMemoryMap[0x01] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x02] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x03] = &gbRom[tmpAddress + 0x3000]; + + tmpAddress |= (gbDataMBC1.mapperROMBank & 0xf) << 14; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + } + else + { + if(gbDataMBC1.mapperMemoryModel == 0) { + // model is 16/8, so we have a high address in use + tmpAddress |= (gbDataMBC1.mapperROMHighAddress & 3) << 19; + } + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + } + + if((gbRamSize) && (gbDataMBC1.mapperMemoryModel == 1)){ + gbMemoryMap[0x0a] = &gbRam[gbDataMBC1.mapperRAMAddress]; + gbMemoryMap[0x0b] = &gbRam[gbDataMBC1.mapperRAMAddress + 0x1000]; + } +} + +mapperMBC2 gbDataMBC2 = { + 0, // RAM enable + 1 // ROM bank +}; + +// MBC2 ROM write registers +void mapperMBC2ROM(u16 address, u8 value) +{ + switch(address & 0x6000) { + case 0x0000: // RAM enable + if(!(address & 0x0100)) { + gbDataMBC2.mapperRAMEnable = (value & 0x0f) == 0x0a; + } + break; + case 0x2000: // ROM bank select + if(address & 0x0100) { + value &= 0x0f; + + if(value == 0) + value = 1; + if(gbDataMBC2.mapperROMBank != value) { + gbDataMBC2.mapperROMBank = value; + + int tmpAddress = value << 14; + + tmpAddress &= gbRomSizeMask; + + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + } + } + break; + } +} + +// MBC2 RAM write +void mapperMBC2RAM(u16 address, u8 value) +{ + if(gbDataMBC2.mapperRAMEnable) { + if(gbRamSize && address < 0xa200) { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } +} + +void memoryUpdateMapMBC2() +{ + int tmpAddress = gbDataMBC2.mapperROMBank << 14; + + tmpAddress &= gbRomSizeMask; + + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; +} + +mapperMBC3 gbDataMBC3 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // RAM address + 0, // timer clock latch + 0, // timer clock register + 0, // timer seconds + 0, // timer minutes + 0, // timer hours + 0, // timer days + 0, // timer control + 0, // timer latched seconds + 0, // timer latched minutes + 0, // timer latched hours + 0, // timer latched days + 0, // timer latched control + (time_t)-1 // last time +}; + +void memoryUpdateMBC3Clock() +{ + time_t now = time(NULL); + time_t diff = now - gbDataMBC3.mapperLastTime; + if(diff > 0) { + // update the clock according to the last update time + gbDataMBC3.mapperSeconds += (int)(diff % 60); + if(gbDataMBC3.mapperSeconds > 59) { + gbDataMBC3.mapperSeconds -= 60; + gbDataMBC3.mapperMinutes++; + } + + diff /= 60; + + gbDataMBC3.mapperMinutes += (int)(diff % 60); + if(gbDataMBC3.mapperMinutes > 59) { + gbDataMBC3.mapperMinutes -= 60; + gbDataMBC3.mapperHours++; + } + + diff /= 60; + + gbDataMBC3.mapperHours += (int)(diff % 24); + if(gbDataMBC3.mapperHours > 23) { + gbDataMBC3.mapperHours -= 24; + gbDataMBC3.mapperDays++; + } + diff /= 24; + + gbDataMBC3.mapperDays += (int)(diff & 0xffffffff); + if(gbDataMBC3.mapperDays > 255) { + if(gbDataMBC3.mapperDays > 511) { + gbDataMBC3.mapperDays %= 512; + gbDataMBC3.mapperControl |= 0x80; + } + gbDataMBC3.mapperControl = (gbDataMBC3.mapperControl & 0xfe) | + (gbDataMBC3.mapperDays>255 ? 1 : 0); + } + } + gbDataMBC3.mapperLastTime = now; +} + +// MBC3 ROM write registers +void mapperMBC3ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: // RAM enable register + gbDataMBC3.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); + break; + case 0x2000: // ROM bank select + value = value & 0x7f; + if(value == 0) + value = 1; + if(value == gbDataMBC3.mapperROMBank) + break; + + tmpAddress = value << 14; + + tmpAddress &= gbRomSizeMask; + gbDataMBC3.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + break; + case 0x4000: // RAM bank select + if(value < 8) { + if(value == gbDataMBC3.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + gbDataMBC3.mapperRAMBank = value; + gbDataMBC3.mapperRAMAddress = tmpAddress; + } else { + if(gbDataMBC3.mapperRAMEnable) { + gbDataMBC3.mapperRAMBank = -1; + + gbDataMBC3.mapperClockRegister = value; + } + } + break; + case 0x6000: // clock latch + if(gbDataMBC3.mapperClockLatch == 0 && value == 1) { + memoryUpdateMBC3Clock(); + gbDataMBC3.mapperLSeconds = gbDataMBC3.mapperSeconds; + gbDataMBC3.mapperLMinutes = gbDataMBC3.mapperMinutes; + gbDataMBC3.mapperLHours = gbDataMBC3.mapperHours; + gbDataMBC3.mapperLDays = gbDataMBC3.mapperDays; + gbDataMBC3.mapperLControl = gbDataMBC3.mapperControl; + } + if(value == 0x00 || value == 0x01) + gbDataMBC3.mapperClockLatch = value; + break; + } +} + +// MBC3 RAM write +void mapperMBC3RAM(u16 address, u8 value) +{ + if(gbDataMBC3.mapperRAMEnable) { + if(gbDataMBC3.mapperRAMBank != -1) { + if(gbRamSize) { + gbMemoryMap[address>>12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } else { + time(&gbDataMBC3.mapperLastTime); + switch(gbDataMBC3.mapperClockRegister) { + case 0x08: + gbDataMBC3.mapperSeconds = value; + break; + case 0x09: + gbDataMBC3.mapperMinutes = value; + break; + case 0x0a: + gbDataMBC3.mapperHours = value; + break; + case 0x0b: + gbDataMBC3.mapperDays = value; + break; + case 0x0c: + if(gbDataMBC3.mapperControl & 0x80) + gbDataMBC3.mapperControl = 0x80 | value; + else + gbDataMBC3.mapperControl = value; + break; + } + } + } +} + +// MBC3 read RAM +u8 mapperMBC3ReadRAM(u16 address) +{ + if(gbDataMBC3.mapperRAMEnable) { + if(gbDataMBC3.mapperRAMBank != -1) { + return gbMemoryMap[address>>12][address & 0x0fff]; + } + + switch(gbDataMBC3.mapperClockRegister) { + case 0x08: + return gbDataMBC3.mapperLSeconds; + break; + case 0x09: + return gbDataMBC3.mapperLMinutes; + break; + case 0x0a: + return gbDataMBC3.mapperLHours; + break; + case 0x0b: + return gbDataMBC3.mapperLDays; + break; + case 0x0c: + return gbDataMBC3.mapperLControl; + } + } + + if (!genericflashcardEnable) + return 0xff; + else + if ((address & 0x1000) >= 0x1000) + { + // The value returned when reading RAM while it's disabled + // is constant, exept for the GBASP hardware. + // (actually, is the address that read is out of the ROM, the returned value if 0xff...) + if (PC.W>=0xff80) + return 0xff; + else + if ((gbHardware & 0x08) && (gbGBCColorType == 2)) + { + if (address & 1) + return 0xfb; + else + return 0x7a; + } + else + return 0x0a; + } + else + return gbDisabledRam[address & 7]; +} + +void memoryUpdateMapMBC3() +{ + int tmpAddress = gbDataMBC3.mapperROMBank << 14; + + tmpAddress &= gbRomSizeMask; + + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + if(gbDataMBC3.mapperRAMBank >= 0 && gbRamSize) { + tmpAddress = gbDataMBC3.mapperRAMBank << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + } +} + +mapperMBC5 gbDataMBC5 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // ROM high address + 0, // RAM address + 0 // is rumble cartridge? +}; + +// MBC5 ROM write registers +void mapperMBC5ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: // RAM enable register + gbDataMBC5.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); + break; + case 0x2000: // ROM bank select + + if(address < 0x3000) { + value = value & 0xff; + if(value == gbDataMBC5.mapperROMBank) + break; + + tmpAddress = (value << 14) | (gbDataMBC5.mapperROMHighAddress << 22) ; + + tmpAddress &= gbRomSizeMask; + gbDataMBC5.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + } else { + value = value & 1; + if(value == gbDataMBC5.mapperROMHighAddress) + break; + + tmpAddress = (gbDataMBC5.mapperROMBank << 14) | (value << 22); + + tmpAddress &= gbRomSizeMask; + gbDataMBC5.mapperROMHighAddress = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + } + break; + case 0x4000: // RAM bank select + if(gbDataMBC5.isRumbleCartridge) + value &= 0x07; + else + value &= 0x0f; + if(value == gbDataMBC5.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + if(gbRamSize) { + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + + gbDataMBC5.mapperRAMBank = value; + gbDataMBC5.mapperRAMAddress = tmpAddress; + } + break; + } +} + +// MBC5 RAM write +void mapperMBC5RAM(u16 address, u8 value) +{ + if(gbDataMBC5.mapperRAMEnable) { + if(gbRamSize) { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } +} + +// MBC5 read RAM +u8 mapperMBC5ReadRAM(u16 address) +{ + + if(gbDataMBC5.mapperRAMEnable) + return gbMemoryMap[address>>12][address & 0x0fff]; + + if (!genericflashcardEnable) + return 0xff; + else + if ((address & 0x1000) >= 0x1000) + { + // The value returned when reading RAM while it's disabled + // is constant, exept for the GBASP hardware. + // (actually, is the address that read is out of the ROM, the returned value if 0xff...) + if (PC.W>=0xff80) + return 0xff; + else + if ((gbHardware & 0x08) && (gbGBCColorType == 2)) + { + if (address & 1) + return 0xfb; + else + return 0x7a; + } + else + return 0x0a; + } + else + return gbDisabledRam[address & 7]; +} + +void memoryUpdateMapMBC5() +{ + int tmpAddress = (gbDataMBC5.mapperROMBank << 14) | + (gbDataMBC5.mapperROMHighAddress << 22) ; + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + if(gbRamSize) { + tmpAddress = gbDataMBC5.mapperRAMBank << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + } +} + +mapperMBC7 gbDataMBC7 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // RAM address + 0, // chip select + 0, // ?? + 0, // mapper state + 0, // buffer for receiving serial data + 0, // idle state + 0, // count of bits received + 0, // command received + 0, // address received + 0, // write enable + 0, // value to return on ram +}; + +// MBC7 ROM write registers +void mapperMBC7ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: + break; + case 0x2000: // ROM bank select + value = value & 0x7f; + if(value == 0) + value = 1; + + if(value == gbDataMBC7.mapperROMBank) + break; + + tmpAddress = (value << 14); + + tmpAddress &= gbRomSizeMask; + gbDataMBC7.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + break; + case 0x4000: // RAM bank select/enable + if(value < 8) { + tmpAddress = (value&3) << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbMemory[0xa000]; + gbMemoryMap[0x0b] = &gbMemory[0xb000]; + + gbDataMBC7.mapperRAMBank = value; + gbDataMBC7.mapperRAMAddress = tmpAddress; + gbDataMBC7.mapperRAMEnable = 0; + } else { + gbDataMBC7.mapperRAMEnable = 0; + } + break; + } +} + +// MBC7 read RAM +u8 mapperMBC7ReadRAM(u16 address) +{ + switch(address & 0xa0f0) { + case 0xa000: + case 0xa010: + case 0xa060: + case 0xa070: + return 0; + case 0xa020: + // sensor X low byte + return systemGetSensorX() & 255; + case 0xa030: + // sensor X high byte + return systemGetSensorX() >> 8; + case 0xa040: + // sensor Y low byte + return systemGetSensorY() & 255; + case 0xa050: + // sensor Y high byte + return systemGetSensorY() >> 8; + case 0xa080: + return gbDataMBC7.value; + } + + if (!genericflashcardEnable) + return 0xff; + else + if ((address & 0x1000) >= 0x1000) + { + // The value returned when reading RAM while it's disabled + // is constant, exept for the GBASP hardware. + // (actually, is the address that read is out of the ROM, the returned value if 0xff...) + if (PC.W>=0xff80) + return 0xff; + else + if ((gbHardware & 0x08) && (gbGBCColorType == 2)) + { + if (address & 1) + return 0xfb; + else + return 0x7a; + } + else + return 0x0a; + } + else + return gbDisabledRam[address & 7]; +} + +// MBC7 RAM write +void mapperMBC7RAM(u16 address, u8 value) +{ + if(address == 0xa080) { + // special processing needed + int oldCs = gbDataMBC7.cs,oldSk=gbDataMBC7.sk; + + gbDataMBC7.cs=value>>7; + gbDataMBC7.sk=(value>>6)&1; + + if(!oldCs && gbDataMBC7.cs) { + if(gbDataMBC7.state==5) { + if(gbDataMBC7.writeEnable) { + gbMemory[0xa000+gbDataMBC7.address*2]=gbDataMBC7.buffer>>8; + gbMemory[0xa000+gbDataMBC7.address*2+1]=gbDataMBC7.buffer&0xff; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + gbDataMBC7.state=0; + gbDataMBC7.value=1; + } else { + gbDataMBC7.idle=true; + gbDataMBC7.state=0; + } + } + + if(!oldSk && gbDataMBC7.sk) { + if(gbDataMBC7.idle) { + if(value & 0x02) { + gbDataMBC7.idle=false; + gbDataMBC7.count=0; + gbDataMBC7.state=1; + } + } else { + switch(gbDataMBC7.state) { + case 1: + // receiving command + gbDataMBC7.buffer <<= 1; + gbDataMBC7.buffer |= (value & 0x02)?1:0; + gbDataMBC7.count++; + if(gbDataMBC7.count==2) { + // finished receiving command + gbDataMBC7.state=2; + gbDataMBC7.count=0; + gbDataMBC7.code=gbDataMBC7.buffer & 3; + } + break; + case 2: + // receive address + gbDataMBC7.buffer <<= 1; + gbDataMBC7.buffer |= (value&0x02)?1:0; + gbDataMBC7.count++; + if(gbDataMBC7.count==8) { + // finish receiving + gbDataMBC7.state=3; + gbDataMBC7.count=0; + gbDataMBC7.address=gbDataMBC7.buffer&0xff; + if(gbDataMBC7.code==0) { + if((gbDataMBC7.address>>6)==0) { + gbDataMBC7.writeEnable=0; + gbDataMBC7.state=0; + } else if((gbDataMBC7.address>>6) == 3) { + gbDataMBC7.writeEnable=1; + gbDataMBC7.state=0; + } + } + } + break; + case 3: + gbDataMBC7.buffer <<= 1; + gbDataMBC7.buffer |= (value&0x02)?1:0; + gbDataMBC7.count++; + + switch(gbDataMBC7.code) { + case 0: + if(gbDataMBC7.count==16) { + if((gbDataMBC7.address>>6)==0) { + gbDataMBC7.writeEnable = 0; + gbDataMBC7.state=0; + } else if((gbDataMBC7.address>>6)==1) { + if (gbDataMBC7.writeEnable) { + for(int i=0;i<256;i++) { + gbMemory[0xa000+i*2] = gbDataMBC7.buffer >> 8; + gbMemory[0xa000+i*2+1] = gbDataMBC7.buffer & 0xff; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } + gbDataMBC7.state=5; + } else if((gbDataMBC7.address>>6) == 2) { + if (gbDataMBC7.writeEnable) { + for(int i=0;i<256;i++) + WRITE16LE((u16 *)&gbMemory[0xa000+i*2], 0xffff); + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + gbDataMBC7.state=5; + } else if((gbDataMBC7.address>>6)==3) { + gbDataMBC7.writeEnable = 1; + gbDataMBC7.state=0; + } + gbDataMBC7.count=0; + } + break; + case 1: + if(gbDataMBC7.count==16) { + gbDataMBC7.count=0; + gbDataMBC7.state=5; + gbDataMBC7.value=0; + } + break; + case 2: + if(gbDataMBC7.count==1) { + gbDataMBC7.state=4; + gbDataMBC7.count=0; + gbDataMBC7.buffer = (gbMemory[0xa000+gbDataMBC7.address*2]<<8)| + (gbMemory[0xa000+gbDataMBC7.address*2+1]); + } + break; + case 3: + if(gbDataMBC7.count==16) { + gbDataMBC7.count=0; + gbDataMBC7.state=5; + gbDataMBC7.value=0; + gbDataMBC7.buffer=0xffff; + } + break; + } + break; + } + } + } + + if (oldSk && !gbDataMBC7.sk) { + if (gbDataMBC7.state==4) { + gbDataMBC7.value = (gbDataMBC7.buffer & 0x8000)?1:0; + gbDataMBC7.buffer <<= 1; + gbDataMBC7.count++; + if (gbDataMBC7.count==16) { + gbDataMBC7.count=0; + gbDataMBC7.state=0; + } + } + } + } +} + +void memoryUpdateMapMBC7() +{ + int tmpAddress = (gbDataMBC7.mapperROMBank << 14); + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; +} + +mapperHuC1 gbDataHuC1 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // memory model + 0, // ROM high address + 0 // RAM address +}; + +// HuC1 ROM write registers +void mapperHuC1ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: // RAM enable register + gbDataHuC1.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); + break; + case 0x2000: // ROM bank select + value = value & 0x3f; + if(value == 0) + value = 1; + if(value == gbDataHuC1.mapperROMBank) + break; + + tmpAddress = value << 14; + + tmpAddress &= gbRomSizeMask; + gbDataHuC1.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + break; + case 0x4000: // RAM bank select + if(gbDataHuC1.mapperMemoryModel == 1) { + // 4/32 model, RAM bank switching provided + value = value & 0x03; + if(value == gbDataHuC1.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + gbDataHuC1.mapperRAMBank = value; + gbDataHuC1.mapperRAMAddress = tmpAddress; + } else { + // 16/8, set the high address + gbDataHuC1.mapperROMHighAddress = value & 0x03; + tmpAddress = gbDataHuC1.mapperROMBank << 14; + tmpAddress |= (gbDataHuC1.mapperROMHighAddress) << 19; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + } + break; + case 0x6000: // memory model select + gbDataHuC1.mapperMemoryModel = value & 1; + break; + } +} + +// HuC1 RAM write +void mapperHuC1RAM(u16 address, u8 value) +{ + if(gbDataHuC1.mapperRAMEnable) { + if(gbRamSize) { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } +} + +void memoryUpdateMapHuC1() +{ + int tmpAddress = gbDataHuC1.mapperROMBank << 14; + + tmpAddress &= gbRomSizeMask; + + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + if(gbRamSize) { + tmpAddress = gbDataHuC1.mapperRAMBank << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + } +} + +mapperHuC3 gbDataHuC3 = { + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // RAM address + 0, // RAM flag + 0 // RAM read value +}; + + +// HuC3 ROM write registers +void mapperHuC3ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: // RAM enable register + gbDataHuC3.mapperRAMEnable = ( value == 0x0a ? 1 : 0); + gbDataHuC3.mapperRAMFlag = value; + if(gbDataHuC3.mapperRAMFlag != 0x0a) + gbDataHuC3.mapperRAMBank = -1; + break; + case 0x2000: // ROM bank select + value = value & 0x7f; + if(value == 0) + value = 1; + if(value == gbDataHuC3.mapperROMBank) + break; + + tmpAddress = value << 14; + + tmpAddress &= gbRomSizeMask; + gbDataHuC3.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + break; + case 0x4000: // RAM bank select + value = value & 0x03; + if(value == gbDataHuC3.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + gbDataHuC3.mapperRAMBank = value; + gbDataHuC3.mapperRAMAddress = tmpAddress; + break; + case 0x6000: // nothing to do! + break; + } +} + +// HuC3 read RAM +u8 mapperHuC3ReadRAM(u16 address) +{ + if(gbDataHuC3.mapperRAMFlag > 0x0b && + gbDataHuC3.mapperRAMFlag < 0x0e) { + if(gbDataHuC3.mapperRAMFlag != 0x0c) + return 1; + return gbDataHuC3.mapperRAMValue; + } else + return gbMemoryMap[address >> 12][address & 0x0fff]; +} + +// HuC3 RAM write +void mapperHuC3RAM(u16 address, u8 value) +{ + int *p; + + if(gbDataHuC3.mapperRAMFlag < 0x0b || + gbDataHuC3.mapperRAMFlag > 0x0e) { + if(gbDataHuC3.mapperRAMEnable) { + if(gbRamSize) { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } + } else { + if(gbDataHuC3.mapperRAMFlag == 0x0b) { + if(value == 0x62) { + gbDataHuC3.mapperRAMValue = 1; + } else { + switch(value & 0xf0) { + case 0x10: + p = &gbDataHuC3.mapperRegister2; + gbDataHuC3.mapperRAMValue = *(p+gbDataHuC3.mapperRegister1++); + if(gbDataHuC3.mapperRegister1 > 6) + gbDataHuC3.mapperRegister1 = 0; + break; + case 0x30: + p = &gbDataHuC3.mapperRegister2; + *(p+gbDataHuC3.mapperRegister1++) = value & 0x0f; + if(gbDataHuC3.mapperRegister1 > 6) + gbDataHuC3.mapperRegister1 = 0; + gbDataHuC3.mapperAddress = + (gbDataHuC3.mapperRegister6 << 24) | + (gbDataHuC3.mapperRegister5 << 16) | + (gbDataHuC3.mapperRegister4 << 8) | + (gbDataHuC3.mapperRegister3 << 4) | + (gbDataHuC3.mapperRegister2); + break; + case 0x40: + gbDataHuC3.mapperRegister1 = (gbDataHuC3.mapperRegister1 & 0xf0) | + (value & 0x0f); + gbDataHuC3.mapperRegister2 = (gbDataHuC3.mapperAddress & 0x0f); + gbDataHuC3.mapperRegister3 = ((gbDataHuC3.mapperAddress>>4)&0x0f); + gbDataHuC3.mapperRegister4 = ((gbDataHuC3.mapperAddress>>8)&0x0f); + gbDataHuC3.mapperRegister5 = ((gbDataHuC3.mapperAddress>>16)&0x0f); + gbDataHuC3.mapperRegister6 = ((gbDataHuC3.mapperAddress>>24)&0x0f); + gbDataHuC3.mapperRegister7 = 0; + gbDataHuC3.mapperRegister8 = 0; + gbDataHuC3.mapperRAMValue = 0; + break; + case 0x50: + gbDataHuC3.mapperRegister1 = (gbDataHuC3.mapperRegister1 & 0x0f) | + ((value << 4)&0x0f); + break; + default: + gbDataHuC3.mapperRAMValue = 1; + break; + } + } + } + } +} + +void memoryUpdateMapHuC3() +{ + int tmpAddress = gbDataHuC3.mapperROMBank << 14; + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + if(gbRamSize) { + tmpAddress = gbDataHuC3.mapperRAMBank << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + } +} + +// TAMA5 (for Tamagotchi 3 (gb)). +// Very basic (and ugly :p) support, only rom bank switching is actually working... +mapperTAMA5 gbDataTAMA5 = { + 1, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // RAM address + 0, // RAM Byte select + 0, // mapper command number + 0, // mapper last command; + 0, // commands 0x0 + 0, // commands 0x1 + 0, // commands 0x2 + 0, // commands 0x3 + 0, // commands 0x4 + 0, // commands 0x5 + 0, // commands 0x6 + 0, // commands 0x7 + 0, // commands 0x8 + 0, // commands 0x9 + 0, // commands 0xa + 0, // commands 0xb + 0, // commands 0xc + 0, // commands 0xd + 0, // commands 0xe + 0, // commands 0xf + 0, // register + 0, // timer clock latch + 0, // timer clock register + 0, // timer seconds + 0, // timer minutes + 0, // timer hours + 0, // timer days + 0, // timer months + 0, // timer years + 0, // timer control + 0, // timer latched seconds + 0, // timer latched minutes + 0, // timer latched hours + 0, // timer latched days + 0, // timer latched months + 0, // timer latched years + 0, // timer latched control + (time_t)-1 // last time +}; + + +void memoryUpdateTAMA5Clock() +{ + if ((gbDataTAMA5.mapperYears & 3) == 0) + gbDaysinMonth[1] = 29; + else + gbDaysinMonth[1] = 28; + + time_t now = time(NULL); + time_t diff = now - gbDataTAMA5.mapperLastTime; + if(diff > 0) { + // update the clock according to the last update time + gbDataTAMA5.mapperSeconds += (int)(diff % 60); + if(gbDataTAMA5.mapperSeconds > 59) { + gbDataTAMA5.mapperSeconds -= 60; + gbDataTAMA5.mapperMinutes++; + } + + diff /= 60; + + gbDataTAMA5.mapperMinutes += (int)(diff % 60); + if(gbDataTAMA5.mapperMinutes > 59) { + gbDataTAMA5.mapperMinutes -= 60; + gbDataTAMA5.mapperHours++; + } + + diff /= 60; + + gbDataTAMA5.mapperHours += (int)(diff % 24); + diff /= 24; + if(gbDataTAMA5.mapperHours > 23) { + gbDataTAMA5.mapperHours -= 24; + diff++; + + } + + time_t days = diff; + while (days) + { + gbDataTAMA5.mapperDays++; + days--; + if (gbDataTAMA5.mapperDays>gbDaysinMonth[gbDataTAMA5.mapperMonths-1]) + { + gbDataTAMA5.mapperDays = 1; + gbDataTAMA5.mapperMonths++; + if (gbDataTAMA5.mapperMonths>12) + { + gbDataTAMA5.mapperMonths = 1; + gbDataTAMA5.mapperYears++; + if ((gbDataTAMA5.mapperYears & 3) == 0) + gbDaysinMonth[1] = 29; + else + gbDaysinMonth[1] = 28; + } + } + } + } + gbDataTAMA5.mapperLastTime = now; + +} + + + +// TAMA5 RAM write +void mapperTAMA5RAM(u16 address, u8 value) +{ + if ((address & 0xffff) <= 0xa001) + { + switch (address & 1) + { + case 0: // 'Values' Register + { + value &= 0xf; + gbDataTAMA5.mapperCommands[gbDataTAMA5.mapperCommandNumber] = value; + gbMemoryMap[0xa][0] = value; + + int test = gbDataTAMA5.mapperCommands[gbDataTAMA5.mapperCommandNumber & 0x0e] | + (gbDataTAMA5.mapperCommands[(gbDataTAMA5.mapperCommandNumber & 0x0e) +1]<<4); + + if ((gbDataTAMA5.mapperCommandNumber & 0xe) == 0) // Read Command !!! + { + gbDataTAMA5.mapperROMBank = gbDataTAMA5.mapperCommands[0] | + (gbDataTAMA5.mapperCommands[1]<<4); + + int tmpAddress = (gbDataTAMA5.mapperROMBank << 14); + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + gbDataTAMA5.mapperCommands[0x0f] = 0; + } + else if ((gbDataTAMA5.mapperCommandNumber & 0xe) == 4) + { + gbDataTAMA5.mapperCommands[0x0f] = 1; + if (gbDataTAMA5.mapperCommandNumber == 4) + gbDataTAMA5.mapperCommands[5] =0; // correct ? + } + else if ((gbDataTAMA5.mapperCommandNumber & 0xe) == 6) + { + gbDataTAMA5.mapperRamByteSelect = (gbDataTAMA5.mapperCommands[7]<<4) | + (gbDataTAMA5.mapperCommands[6]&0x0f); + + // Write Commands !!! + if (gbDataTAMA5.mapperCommands[0x0f] && (gbDataTAMA5.mapperCommandNumber == 7)) + { + int data = gbDataTAMA5.mapperCommands[0x04] & 0x0f | + (gbDataTAMA5.mapperCommands[0x05] <<4); + + // Not sure when the write command should reset... + // but it doesn't seem to matter. + // gbDataTAMA5.mapperCommands[0x0f] = 0; + + if (gbDataTAMA5.mapperRamByteSelect == 0x8) // Timer stuff + { + switch (data & 0xf) + { + case 0x7: + gbDataTAMA5.mapperDays = ((gbDataTAMA5.mapperDays)/10)*10 + (data >> 4); + break; + case 0x8: + gbDataTAMA5.mapperDays = (gbDataTAMA5.mapperDays%10) + (data >>4)*10; + break; + case 0x9: + gbDataTAMA5.mapperMonths = ((gbDataTAMA5.mapperMonths)/10)*10 + (data >> 4); + break; + case 0xa: + gbDataTAMA5.mapperMonths = (gbDataTAMA5.mapperMonths%10) + (data >>4)*10; + break; + case 0xb: + gbDataTAMA5.mapperYears = ((gbDataTAMA5.mapperYears)%1000) + (data >> 4)*1000; + break; + case 0xc: + gbDataTAMA5.mapperYears = (gbDataTAMA5.mapperYears%100) + (gbDataTAMA5.mapperYears/1000)*1000 + + (data >>4)*100; + break; + default : + break; + } + } + else if (gbDataTAMA5.mapperRamByteSelect == 0x18) // Timer stuff again + { + memoryUpdateTAMA5Clock(); + gbDataTAMA5.mapperLSeconds = gbDataTAMA5.mapperSeconds; + gbDataTAMA5.mapperLMinutes = gbDataTAMA5.mapperMinutes; + gbDataTAMA5.mapperLHours = gbDataTAMA5.mapperHours; + gbDataTAMA5.mapperLDays = gbDataTAMA5.mapperDays; + gbDataTAMA5.mapperLMonths = gbDataTAMA5.mapperMonths; + gbDataTAMA5.mapperLYears = gbDataTAMA5.mapperYears; + gbDataTAMA5.mapperLControl = gbDataTAMA5.mapperControl; + + int seconds = (gbDataTAMA5.mapperLSeconds / 10)*16 + gbDataTAMA5.mapperLSeconds %10; + int secondsL = (gbDataTAMA5.mapperLSeconds % 10); + int secondsH = (gbDataTAMA5.mapperLSeconds / 10); + int minutes = (gbDataTAMA5.mapperLMinutes / 10)*16 + gbDataTAMA5.mapperLMinutes %10; + int hours = (gbDataTAMA5.mapperLHours / 10)*16 + gbDataTAMA5.mapperLHours %10; + int DaysL = gbDataTAMA5.mapperLDays % 10; + int DaysH = gbDataTAMA5.mapperLDays /10; + int MonthsL = gbDataTAMA5.mapperLMonths % 10; + int MonthsH = gbDataTAMA5.mapperLMonths / 10; + int Years3 = (gbDataTAMA5.mapperLYears / 100) % 10; + int Years4 = (gbDataTAMA5.mapperLYears / 1000); + + switch (data & 0x0f) + { + // I guess cases 0 and 1 are used for secondsL and secondsH + // so the game would update the timer values on screen when + // the seconds reset to 0... ? + case 0x0: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = secondsL; + break; + case 0x1: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = secondsH; + break; + case 0x7: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = DaysL; // days low + break; + case 0x8: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = DaysH; // days high + break; + case 0x9: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = MonthsL; // month low + break; + case 0xa: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = MonthsH; // month high + break; + case 0xb: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = Years4; // years 4th digit + break; + case 0xc: + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = Years3; // years 3rd digit + break; + default : + break; + } + + gbTAMA5ram[0x54] = seconds; // incorrect ? (not used by the game) ? + gbTAMA5ram[0x64] = minutes; + gbTAMA5ram[0x74] = hours; + gbTAMA5ram[0x84] = DaysH*16+DaysL; // incorrect ? (not used by the game) ? + gbTAMA5ram[0x94] = MonthsH*16+MonthsL; // incorrect ? (not used by the game) ? + + time(&gbDataTAMA5.mapperLastTime); + + gbMemoryMap[0xa][0] = 1; + } + else if (gbDataTAMA5.mapperRamByteSelect == 0x28) // Timer stuff again + { + if ((data & 0xf) == 0xb) + gbDataTAMA5.mapperYears = ((gbDataTAMA5.mapperYears>>2)<<2) + (data & 3); + } + else if (gbDataTAMA5.mapperRamByteSelect == 0x44) + { + gbDataTAMA5.mapperMinutes = (data/16)*10 + data%16; + } + else if (gbDataTAMA5.mapperRamByteSelect == 0x54) + { + gbDataTAMA5.mapperHours = (data/16)*10 + data%16; + } + else + { + gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect] = data; + } + } + } + } + break; + case 1: // 'Commands' Register + { + gbMemoryMap[0xa][1] = gbDataTAMA5.mapperCommandNumber = value; + + // This should be only a 'is the flashrom ready ?' command. + // However as I couldn't find any 'copy' command + // (that seems to be needed for the saving system to work) + // I put it there... + if (value == 0x0a) + { + for (int i = 0; i<0x10; i++) + for (int j = 0; j<0x10; j++) + if (!(j&2)) + gbTAMA5ram[(i*0x10)+j | 2] = gbTAMA5ram[(i*0x10)+j]; + // Enable this to see the content of the flashrom in 0xe000 + /*for (int k = 0; k<0x100; k++) + gbMemoryMap[0xe][k] = gbTAMA5ram[k];*/ + + gbMemoryMap[0xa][0] = gbDataTAMA5.mapperRAMEnable = 1; + } + else + { + if ((value & 0x0e) == 0x0c) + { + gbDataTAMA5.mapperRamByteSelect = gbDataTAMA5.mapperCommands[6] | + (gbDataTAMA5.mapperCommands[7]<<4); + + u8 byte = gbTAMA5ram[gbDataTAMA5.mapperRamByteSelect]; + + gbMemoryMap[0xa][0] = (value & 1) ? byte >> 4 : byte & 0x0f; + + gbDataTAMA5.mapperCommands[0x0f] = 0; + } + } + break; + } + } + } + else + { + if(gbDataTAMA5.mapperRAMEnable) { + if(gbDataTAMA5.mapperRAMBank != -1) { + if(gbRamSize) { + gbMemoryMap[address>>12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } + } + } +} + + +// TAMA5 read RAM +u8 mapperTAMA5ReadRAM(u16 address) +{ + return gbMemoryMap[address>>12][address & 0xfff]; +} + + +void memoryUpdateMapTAMA5() +{ + int tmpAddress = (gbDataTAMA5.mapperROMBank << 14); + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + if(gbRamSize) { + tmpAddress = 0 << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + } +} + +// MMM01 Used in Momotarou collection (however the rom is corrupted) +mapperMMM01 gbDataMMM01 ={ + 0, // RAM enable + 1, // ROM bank + 0, // RAM bank + 0, // memory model + 0, // ROM high address + 0, // RAM address + 0 // Rom Bank 0 remapping +}; + +// MMM01 ROM write registers +void mapperMMM01ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: // RAM enable register + gbDataMMM01.mapperRAMEnable = ( ( value & 0x0a) == 0x0a ? 1 : 0); + break; + case 0x2000: // ROM bank select + // value = value & 0x1f; + if(value == 0) + value = 1; + if(value == gbDataMMM01.mapperROMBank) + break; + + tmpAddress = value << 14; + + // check current model + if(gbDataMMM01.mapperMemoryModel == 0) { + // model is 16/8, so we have a high address in use + tmpAddress |= (gbDataMMM01.mapperROMHighAddress) << 19; + } + else + tmpAddress |= gbDataMMM01.mapperRomBank0Remapping << 18; + + tmpAddress &= gbRomSizeMask; + gbDataMMM01.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + break; + case 0x4000: // RAM bank select + if(gbDataMMM01.mapperMemoryModel == 1) { + // 4/32 model, RAM bank switching provided + value = value & 0x03; + if(value == gbDataMBC1.mapperRAMBank) + break; + tmpAddress = value << 13; + tmpAddress &= gbRamSizeMask; + gbMemoryMap[0x0a] = &gbRam[tmpAddress]; + gbMemoryMap[0x0b] = &gbRam[tmpAddress + 0x1000]; + gbDataMMM01.mapperRAMBank = value; + gbDataMMM01.mapperRAMAddress = tmpAddress; + } else { + // 16/8, set the high address + gbDataMMM01.mapperROMHighAddress = value & 0x03; + tmpAddress = gbDataMMM01.mapperROMBank << 14; + tmpAddress |= (gbDataMMM01.mapperROMHighAddress) << 19; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + gbDataMMM01.mapperRomBank0Remapping = ((value<<1) | (value & 0x40 ? 1 : 0)) & 0xff; + tmpAddress = gbDataMMM01.mapperRomBank0Remapping << 18; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x00] = &gbRom[tmpAddress]; + gbMemoryMap[0x01] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x02] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x03] = &gbRom[tmpAddress + 0x3000]; + } + break; + case 0x6000: // memory model select + gbDataMMM01.mapperMemoryModel = value & 1; + break; + } +} + +// MMM01 RAM write +void mapperMMM01RAM(u16 address, u8 value) +{ + if(gbDataMMM01.mapperRAMEnable) { + if(gbRamSize) { + gbMemoryMap[address >> 12][address & 0x0fff] = value; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + } +} + +void memoryUpdateMapMMM01() +{ + int tmpAddress = gbDataMMM01.mapperROMBank << 14; + + // check current model + if(gbDataMMM01.mapperMemoryModel == 1) { + // model is 16/8, so we have a high address in use + tmpAddress |= (gbDataMMM01.mapperROMHighAddress) << 19; + } + + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x06] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x07] = &gbRom[tmpAddress + 0x3000]; + + tmpAddress = gbDataMMM01.mapperRomBank0Remapping << 18; + tmpAddress &= gbRomSizeMask; + gbMemoryMap[0x00] = &gbRom[tmpAddress]; + gbMemoryMap[0x01] = &gbRom[tmpAddress + 0x1000]; + gbMemoryMap[0x02] = &gbRom[tmpAddress + 0x2000]; + gbMemoryMap[0x03] = &gbRom[tmpAddress + 0x3000]; + + if(gbRamSize) { + gbMemoryMap[0x0a] = &gbRam[gbDataMMM01.mapperRAMAddress]; + gbMemoryMap[0x0b] = &gbRam[gbDataMMM01.mapperRAMAddress + 0x1000]; + } +} + +// GameGenie ROM write registers +void mapperGGROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: // RAM enable register + break; + case 0x2000: // GameGenie has only a half bank + break; + case 0x4000: // GameGenie has no RAM + if ((address >=0x4001) && (address <= 0x4020)) // GG Hardware Registers + gbMemoryMap[address >> 12][address & 0x0fff] = value; + break; + case 0x6000: // GameGenie has only a half bank + break; + } +} + + +// GS3 Used to emulate the GS V3.0 rom bank switching +mapperGS3 gbDataGS3 = { 1 }; // ROM bank + +void mapperGS3ROM(u16 address, u8 value) +{ + int tmpAddress = 0; + + switch(address & 0x6000) { + case 0x0000: // GS has no ram + break; + case 0x2000: // GS has no 'classic' ROM bank select + break; + case 0x4000: // GS has no ram + break; + case 0x6000: // 0x6000 area is RW, and used for GS hardware registers + + if (address == 0x7FE1) // This is the (half) ROM bank select register + { + if(value == gbDataGS3.mapperROMBank) + break; + tmpAddress = value << 13; + + tmpAddress &= gbRomSizeMask; + gbDataGS3.mapperROMBank = value; + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; + } + else + gbMemoryMap[address>>12][address & 0x0fff] = value; + break; + } +} + +void memoryUpdateMapGS3() +{ + int tmpAddress = gbDataGS3.mapperROMBank << 13; + + tmpAddress &= gbRomSizeMask; + // GS can only change a half ROM bank + gbMemoryMap[0x04] = &gbRom[tmpAddress]; + gbMemoryMap[0x05] = &gbRom[tmpAddress + 0x1000]; +} diff --git a/src/dmg/gbMemory.h b/src/dmg/gbMemory.h new file mode 100644 index 00000000..1032639c --- /dev/null +++ b/src/dmg/gbMemory.h @@ -0,0 +1,206 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004-2006 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include + +struct mapperMBC1 { + int mapperRAMEnable; + int mapperROMBank; + int mapperRAMBank; + int mapperMemoryModel; + int mapperROMHighAddress; + int mapperRAMAddress; + int mapperRomBank0Remapping; +}; + +struct mapperMBC2 { + int mapperRAMEnable; + int mapperROMBank; +}; + +struct mapperMBC3 { + int mapperRAMEnable; + int mapperROMBank; + int mapperRAMBank; + int mapperRAMAddress; + int mapperClockLatch; + int mapperClockRegister; + int mapperSeconds; + int mapperMinutes; + int mapperHours; + int mapperDays; + int mapperControl; + int mapperLSeconds; + int mapperLMinutes; + int mapperLHours; + int mapperLDays; + int mapperLControl; + time_t mapperLastTime; +}; + +struct mapperMBC5 { + int mapperRAMEnable; + int mapperROMBank; + int mapperRAMBank; + int mapperROMHighAddress; + int mapperRAMAddress; + int isRumbleCartridge; +}; + +struct mapperMBC7 { + int mapperRAMEnable; + int mapperROMBank; + int mapperRAMBank; + int mapperRAMAddress; + int cs; + int sk; + int state; + int buffer; + int idle; + int count; + int code; + int address; + int writeEnable; + int value; +}; + +struct mapperHuC1 { + int mapperRAMEnable; + int mapperROMBank; + int mapperRAMBank; + int mapperMemoryModel; + int mapperROMHighAddress; + int mapperRAMAddress; +}; + +struct mapperHuC3 { + int mapperRAMEnable; + int mapperROMBank; + int mapperRAMBank; + int mapperRAMAddress; + int mapperAddress; + int mapperRAMFlag; + int mapperRAMValue; + int mapperRegister1; + int mapperRegister2; + int mapperRegister3; + int mapperRegister4; + int mapperRegister5; + int mapperRegister6; + int mapperRegister7; + int mapperRegister8; +}; + +struct mapperTAMA5 { + int mapperRAMEnable; + int mapperROMBank; + int mapperRAMBank; + int mapperRAMAddress; + int mapperRamByteSelect; + int mapperCommandNumber; + int mapperLastCommandNumber; + int mapperCommands[0x10]; + int mapperRegister; + int mapperClockLatch; + int mapperClockRegister; + int mapperSeconds; + int mapperMinutes; + int mapperHours; + int mapperDays; + int mapperMonths; + int mapperYears; + int mapperControl; + int mapperLSeconds; + int mapperLMinutes; + int mapperLHours; + int mapperLDays; + int mapperLMonths; + int mapperLYears; + int mapperLControl; + time_t mapperLastTime; +}; + +struct mapperMMM01 { + int mapperRAMEnable; + int mapperROMBank; + int mapperRAMBank; + int mapperMemoryModel; + int mapperROMHighAddress; + int mapperRAMAddress; + int mapperRomBank0Remapping; +}; + +struct mapperGS3 { + int mapperROMBank; +}; + +extern mapperMBC1 gbDataMBC1; +extern mapperMBC2 gbDataMBC2; +extern mapperMBC3 gbDataMBC3; +extern mapperMBC5 gbDataMBC5; +extern mapperHuC1 gbDataHuC1; +extern mapperHuC3 gbDataHuC3; +extern mapperTAMA5 gbDataTAMA5; +extern mapperMMM01 gbDataMMM01; +extern mapperGS3 gbDataGS3; + +void mapperMBC1ROM(u16,u8); +void mapperMBC1RAM(u16,u8); +u8 mapperMBC1ReadRAM(u16); +void mapperMBC2ROM(u16,u8); +void mapperMBC2RAM(u16,u8); +void mapperMBC3ROM(u16,u8); +void mapperMBC3RAM(u16,u8); +u8 mapperMBC3ReadRAM(u16); +void mapperMBC5ROM(u16,u8); +void mapperMBC5RAM(u16,u8); +u8 mapperMBC5ReadRAM(u16); +void mapperMBC7ROM(u16,u8); +void mapperMBC7RAM(u16,u8); +u8 mapperMBC7ReadRAM(u16); +void mapperHuC1ROM(u16,u8); +void mapperHuC1RAM(u16,u8); +void mapperHuC3ROM(u16,u8); +void mapperHuC3RAM(u16,u8); +u8 mapperHuC3ReadRAM(u16); +void mapperTAMA5RAM(u16,u8); +u8 mapperTAMA5ReadRAM(u16); +void memoryUpdateTAMA5Clock(); +void mapperMMM01ROM(u16,u8); +void mapperMMM01RAM(u16,u8); +void mapperGGROM(u16,u8); +void mapperGS3ROM(u16,u8); +//extern void (*mapper)(u16,u8); +//extern void (*mapperRAM)(u16,u8); +//extern u8 (*mapperReadRAM)(u16); + +extern void memoryUpdateMapMBC1(); +extern void memoryUpdateMapMBC2(); +extern void memoryUpdateMapMBC3(); +extern void memoryUpdateMapMBC5(); +extern void memoryUpdateMapMBC7(); +extern void memoryUpdateMapHuC1(); +extern void memoryUpdateMapHuC3(); +extern void memoryUpdateMapTAMA5(); +extern void memoryUpdateMapMMM01(); +extern void memoryUpdateMapGS3(); + + + + diff --git a/src/dmg/gbPrinter.cpp b/src/dmg/gbPrinter.cpp new file mode 100644 index 00000000..3c2f2872 --- /dev/null +++ b/src/dmg/gbPrinter.cpp @@ -0,0 +1,229 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include +#include "../agb/GBA.h" + +u8 gbPrinterStatus = 0; +int gbPrinterState = 0; +u8 gbPrinterData[0x280*9]; +u8 gbPrinterPacket[0x400]; +int gbPrinterCount = 0; +int gbPrinterDataCount = 0; +int gbPrinterDataSize = 0; +int gbPrinterResult = 0; + +bool gbPrinterCheckCRC() +{ + u16 crc = 0; + + for(int i = 2; i < (6+gbPrinterDataSize); i++) { + crc += gbPrinterPacket[i]; + } + + int msgCrc = gbPrinterPacket[6+gbPrinterDataSize] + + (gbPrinterPacket[7+gbPrinterDataSize]<<8); + + return msgCrc == crc; +} + +void gbPrinterReset() +{ + gbPrinterState = 0; + gbPrinterDataSize = 0; + gbPrinterDataCount = 0; + gbPrinterCount = 0; + gbPrinterStatus = 0; + gbPrinterResult = 0; +} + +void gbPrinterShowData() +{ + systemGbPrint(gbPrinterData, + gbPrinterPacket[6], + gbPrinterPacket[7], + gbPrinterPacket[8], + gbPrinterPacket[9]); + /* + allegro_init(); + install_keyboard(); + set_gfx_mode(GFX_AUTODETECT, 160, 144, 0, 0); + PALETTE pal; + pal[0].r = 255; + pal[0].g = 255; + pal[0].b = 255; + pal[1].r = 168; + pal[1].g = 168; + pal[1].b = 168; + pal[2].r = 96; + pal[2].g = 96; + pal[2].b = 96; + pal[3].r = 0; + pal[3].g = 0; + pal[3].b = 0; + set_palette(pal); + acquire_screen(); + u8 *data = gbPrinterData; + for(int y = 0; y < 0x12; y++) { + for(int x = 0; x < 0x14; x++) { + for(int k = 0; k < 8; k++) { + int a = *data++; + int b = *data++; + for(int j = 0; j < 8; j++) { + int mask = 1 << (7-j); + int c = 0; + if(a & mask) + c++; + if(b & mask) + c+=2; + putpixel(screen, x*8+j, y*8+k, c); + } + } + } + } + release_screen(); + while(!keypressed()) { + } + */ +} + +void gbPrinterReceiveData() +{ + if(gbPrinterPacket[3]) { // compressed + u8 *data = &gbPrinterPacket[6]; + u8 *dest = &gbPrinterData[gbPrinterDataCount]; + int len = 0; + while(len < gbPrinterDataSize) { + u8 control = *data++; + if(control & 0x80) { // repeated data + control &= 0x7f; + control += 2; + memset(dest, *data++, control); + len += control; + dest += control; + } else { // raw data + control++; + memcpy(dest, data, control); + dest += control; + data += control; + len += control; + } + } + } else { + memcpy(&gbPrinterData[gbPrinterDataCount], + &gbPrinterPacket[6], + gbPrinterDataSize); + gbPrinterDataCount += gbPrinterDataSize; + } +} + +void gbPrinterCommand() +{ + switch(gbPrinterPacket[2]) { + case 0x01: + // reset/initialize packet + gbPrinterDataCount = 0; + gbPrinterStatus = 0; + break; + case 0x02: + // print packet + gbPrinterShowData(); + break; + case 0x04: + // data packet + gbPrinterReceiveData(); + break; + case 0x0f: + // NUL packet + break; + } +} + +u8 gbPrinterSend(u8 b) +{ + switch(gbPrinterState) { + case 0: + gbPrinterCount = 0; + // receiving preamble + if(b == 0x88) { + gbPrinterPacket[gbPrinterCount++] = b; + gbPrinterState++; + } else { + // todo: handle failure + gbPrinterReset(); + } + break; + case 1: + // receiving preamble + if(b == 0x33) { + gbPrinterPacket[gbPrinterCount++] = b; + gbPrinterState++; + } else { + // todo: handle failure + gbPrinterReset(); + } + break; + case 2: + // receiving header + gbPrinterPacket[gbPrinterCount++] = b; + if(gbPrinterCount == 6) { + gbPrinterState++; + gbPrinterDataSize = gbPrinterPacket[4] + (gbPrinterPacket[5]<<8); + } + break; + case 3: + // receiving data + if(gbPrinterDataSize) { + gbPrinterPacket[gbPrinterCount++] = b; + if(gbPrinterCount == (6+gbPrinterDataSize)) { + gbPrinterState++; + } + break; + } + gbPrinterState++; + // intentionally move to next if no data to receive + case 4: + // receiving CRC + gbPrinterPacket[gbPrinterCount++] = b; + gbPrinterState++; + break; + case 5: + // receiving CRC-2 + gbPrinterPacket[gbPrinterCount++] = b; + if(gbPrinterCheckCRC()) { + gbPrinterCommand(); + } + gbPrinterState++; + break; + case 6: + // receiving dummy 1 + gbPrinterPacket[gbPrinterCount++] = b; + gbPrinterResult = 0x81; + gbPrinterState++; + break; + case 7: + // receiving dummy 2 + gbPrinterPacket[gbPrinterCount++] = b; + gbPrinterResult = gbPrinterStatus; + gbPrinterState = 0; + gbPrinterCount = 0; + break; + } + return gbPrinterResult; +} diff --git a/src/dmg/gbPrinter.h b/src/dmg/gbPrinter.h new file mode 100644 index 00000000..20a97659 --- /dev/null +++ b/src/dmg/gbPrinter.h @@ -0,0 +1,20 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +extern u8 gbPrinterSend(u8 b); diff --git a/src/dmg/gbSGB.cpp b/src/dmg/gbSGB.cpp new file mode 100644 index 00000000..d10c0cd1 --- /dev/null +++ b/src/dmg/gbSGB.cpp @@ -0,0 +1,917 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005-2006 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include + +#include "../System.h" +#include "../Port.h" +#include "../Util.h" +#include "gb.h" +#include "gbGlobals.h" + +extern u8 *pix; +extern bool speedup; +extern bool gbSgbResetFlag; + +#define GBSGB_NONE 0 +#define GBSGB_RESET 1 +#define GBSGB_PACKET_TRANSMIT 2 + +u8 *gbSgbBorderChar = NULL; +u8 *gbSgbBorder = NULL; + +int gbSgbCGBSupport = 0; +int gbSgbMask = 0; +int gbSgbMode = 0; +int gbSgbPacketState = GBSGB_NONE; +int gbSgbBit = 0; +int gbSgbPacketTimeout = 0; +int GBSGB_PACKET_TIMEOUT = 66666; +u8 gbSgbPacket[16*7]; +int gbSgbPacketNBits = 0; +int gbSgbPacketByte = 0; +int gbSgbPacketNumber = 0; +int gbSgbMultiplayer = 0; +int gbSgbFourPlayers = 0; +u8 gbSgbNextController = 0x0f; +u8 gbSgbReadingController = 0; +u16 gbSgbSCPPalette[4*512]; +u8 gbSgbATF[20 * 18]; +u8 gbSgbATFList[45 * 20 * 18]; +u8 gbSgbScreenBuffer[4160]; + +inline void gbSgbDraw24Bit(u8 *p, u16 v) +{ + *((u32*) p) = systemColorMap32[v]; +} + +inline void gbSgbDraw32Bit(u32 *p, u16 v) +{ + *p = systemColorMap32[v]; +} + +inline void gbSgbDraw16Bit(u16 *p, u16 v) +{ + *p = systemColorMap16[v]; +} + +void gbSgbReset() +{ + gbSgbPacketTimeout = 0; + gbSgbCGBSupport = 0; + gbSgbMask = 0; + gbSgbPacketState = GBSGB_NONE; + gbSgbBit = 0; + gbSgbPacketNBits = 0; + gbSgbPacketNumber = 0; + gbSgbMultiplayer = 0; + gbSgbFourPlayers = 0; + gbSgbNextController = 0x0f; + gbSgbReadingController = 0; + + memset(gbSgbSCPPalette, 0, 512*4); + memset(gbSgbATF, 0, 20*18); + memset(gbSgbATFList, 0, 45 * 20 * 18); + memset(gbSgbPacket, 0, 16 * 7); + memset(gbSgbBorderChar, 0, 32*256); + memset(gbSgbBorder, 0, 2048); + + int i; + for(i = 1; i < 2048; i+=2) { + gbSgbBorder[i] = 1 << 2; + } + + for(i = 0; i < 32; i++) { + gbPalette[i*4] = (0x1f) | (0x1f << 5) | (0x1f << 10); + gbPalette[i*4+1] = (0x15) | (0x15 << 5) | (0x15 << 10); + gbPalette[i*4+2] = (0x0c) | (0x0c << 5) | (0x0c << 10); + gbPalette[i*4+3] = 0; + } +} + +void gbSgbInit() +{ + gbSgbBorderChar = (u8 *)malloc(32 * 256); + gbSgbBorder = (u8 *)malloc(2048); + + gbSgbReset(); +} + +void gbSgbShutdown() +{ + if(gbSgbBorderChar != NULL) { + free(gbSgbBorderChar); + gbSgbBorderChar = NULL; + } + + if(gbSgbBorder != NULL) { + free(gbSgbBorder); + gbSgbBorder = NULL; + } +} + +void gbSgbFillScreen(u16 color) +{ + switch(systemColorDepth) { + case 16: + { + for(int y = 0; y < 144; y++) { + int yLine = (y+gbBorderRowSkip+1)*(gbBorderLineSkip+2) + + gbBorderColumnSkip; + u16 *dest = (u16*)pix + yLine; + for(register int x = 0; x < 160; x++) + gbSgbDraw16Bit(dest++, color); + } + } + break; + case 24: + { + for(int y = 0; y < 144; y++) { + int yLine = (y+gbBorderRowSkip)*gbBorderLineSkip + gbBorderColumnSkip; + u8 *dest = (u8 *)pix + yLine*3; + for(register int x = 0; x < 160; x++) { + gbSgbDraw24Bit(dest, color); + dest += 3; + } + } + } + break; + case 32: + { + for(int y = 0; y < 144; y++) { + int yLine = (y+gbBorderRowSkip+1)*(gbBorderLineSkip+1) + gbBorderColumnSkip; + u32 *dest = (u32 *)pix + yLine; + for(register int x = 0; x < 160; x++) { + gbSgbDraw32Bit(dest++, color); + } + } + } + break; + } +} + +#define getmem(x) gbMemoryMap[(x) >> 12][(x) & 0xfff] + +void gbSgbRenderScreenToBuffer() +{ + u16 mapAddress = 0x9800; + + if(register_LCDC & 0x08) + mapAddress = 0x9c00; + + u16 patternAddress = 0x8800; + + int flag = 1; + + if(register_LCDC & 0x10) { + patternAddress = 0x8000; + flag = 0; + } + + u8 *toAddress = gbSgbScreenBuffer; + + for(int i = 0; i < 13; i++) { + for(int j = 0; j < 20; j++) { + int tile = getmem(mapAddress); + mapAddress++; + + if(flag) { + if(tile > 127) + tile -= 128; + else + tile += 128; + } + for(int k = 0; k < 16; k++) + *toAddress++ = getmem(patternAddress + tile*16 + k); + } + mapAddress += 12; + } +} + +void gbSgbDrawBorderTile(int x, int y, int tile, int attr) +{ + u16 *dest = (u16*)pix + ((y+1) * (256+2)) + x; + u8 *dest8 = (u8*)pix + ((y*256)+x)*3; + u32 *dest32 = (u32*)pix + ((y+1)*257) + x; + + u8 *tileAddress = &gbSgbBorderChar[tile * 32]; + u8 *tileAddress2 = &gbSgbBorderChar[tile * 32 + 16]; + + u8 l = 8; + + u8 palette = ((attr >> 2 ) & 7); + + if(palette < 4) + palette += 4; + + palette *= 16; + + u8 xx = 0; + u8 yy = 0; + + int flipX = attr & 0x40; + int flipY = attr & 0x80; + + while(l > 0) { + u8 mask = 0x80; + u8 a = *tileAddress++; + u8 b = *tileAddress++; + u8 c = *tileAddress2++; + u8 d = *tileAddress2++; + + while(mask > 0) { + + u8 color = 0; + if(a & mask) + color++; + if(b & mask) + color+=2; + if(c & mask) + color+=4; + if(d & mask) + color+=8; + + u8 xxx = xx; + u8 yyy = yy; + + if(flipX) + xxx = 7 - xx; + if(flipY) + yyy = 7 - yy; + + u16 c = gbPalette[palette + color]; + + // Fix for Super Snaky ??? + // (it allows SGB borders to not redraw on the GB screen) + //if(!color) + // c = gbPalette[0]; + if(((yy < 40 || yy >= 184) || (xx < 48 || xx >= 208)) && (color || (gbSgbResetFlag == true))) { + switch(systemColorDepth) { + case 16: + gbSgbDraw16Bit(dest + yyy*(256+2) + xxx, c); + break; + case 24: + gbSgbDraw24Bit(dest8 + (yyy*256+xxx)*3, c); + break; + case 32: + gbSgbDraw32Bit(dest32 + yyy*(256+1)+xxx, c); + break; + } + } + + mask >>= 1; + + xx++; + } + yy++; + xx = 0; + l--; + mask = 0x80; + } +} + +void gbSgbRenderBorder() +{ + if(gbBorderOn) { + u8 *fromAddress = gbSgbBorder; + + for(u8 y = 0; y < 28; y++) { + for(u8 x = 0; x< 32; x++) { + u8 tile = *fromAddress++; + u8 attr = *fromAddress++; + + gbSgbDrawBorderTile(x*8,y*8,tile,attr); + } + } + } +} + +void gbSgbPicture() +{ + gbSgbRenderScreenToBuffer(); + + memcpy(gbSgbBorder, gbSgbScreenBuffer, 2048); + + u16 *paletteAddr = (u16 *)&gbSgbScreenBuffer[2048]; + + for(int i = 64; i < 128; i++) { + gbPalette[i] = READ16LE(paletteAddr++); + } + + gbSgbCGBSupport |= 4; + + if(gbBorderAutomatic && !gbBorderOn && gbSgbCGBSupport > 4) { + gbBorderOn = 1; + systemGbBorderOn(); + } + + if(gbBorderOn && !gbSgbMask) + gbSgbRenderBorder(); + + if(gbSgbMode && gbCgbMode && gbSgbCGBSupport > 4) { + gbSgbCGBSupport = 0; + gbSgbMode = 0; + gbSgbMask = 0; + gbSgbRenderBorder(); + gbReset(); + } + + if(gbSgbCGBSupport > 4) + gbSgbCGBSupport = 0; +} + +void gbSgbSetPalette(int a,int b,u16 *p) +{ + u16 bit00 = READ16LE(p++); + int i; + + for(i = 1; i < 4; i++) { + gbPalette[a*4+i] = READ16LE(p++); + } + + for(i = 1; i < 4; i++) { + gbPalette[b*4+i] = READ16LE(p++); + } + + gbPalette[0] = gbPalette[4] = gbPalette[8] = gbPalette[12] = bit00; + if(gbBorderOn && !gbSgbMask) + gbSgbRenderBorder(); +} + +void gbSgbScpPalette() +{ + gbSgbRenderScreenToBuffer(); + + u16 *fromAddress = (u16 *)gbSgbScreenBuffer; + + for(int i = 0; i < 512*4; i++) { + gbSgbSCPPalette[i] = READ16LE(fromAddress++); + } +} + +void gbSgbSetATF(int n) +{ + if(n < 0) + n = 0; + if(n > 44) + n = 44; + memcpy(gbSgbATF,&gbSgbATFList[n * 20 * 18], 20 * 18); + + if(gbSgbPacket[1] & 0x40) { + gbSgbMask = 0; + if(gbBorderOn) + gbSgbRenderBorder(); + } +} + +void gbSgbSetPalette() +{ + u16 pal = READ16LE((((u16 *)&gbSgbPacket[1])))&511; + memcpy(&gbPalette[0], &gbSgbSCPPalette[pal*4], 4 * sizeof(u16)); + + pal = READ16LE((((u16 *)&gbSgbPacket[3])))&511; + memcpy(&gbPalette[4], &gbSgbSCPPalette[pal*4], 4 * sizeof(u16)); + + pal = READ16LE((((u16 *)&gbSgbPacket[5])))&511; + memcpy(&gbPalette[8], &gbSgbSCPPalette[pal*4], 4 * sizeof(u16)); + + pal = READ16LE((((u16 *)&gbSgbPacket[7])))&511; + memcpy(&gbPalette[12], &gbSgbSCPPalette[pal*4], 4 * sizeof(u16)); + + u8 atf = gbSgbPacket[9]; + + if(atf & 0x80) { + gbSgbSetATF(atf & 0x3f); + } + + if(atf & 0x40) { + gbSgbMask = 0; + if(gbBorderOn) + gbSgbRenderBorder(); + } +} + +void gbSgbAttributeBlock() +{ + u8 *fromAddress = &gbSgbPacket[1]; + + u8 nDataSet = *fromAddress++; + if(nDataSet > 12) + nDataSet = 12; + if(nDataSet == 0) + nDataSet = 1; + + while(nDataSet) { + u8 controlCode = (*fromAddress++) & 7; + u8 paletteDesignation = (*fromAddress++) & 0x3f; + u8 startH = (*fromAddress++) & 0x1f; + u8 startV = (*fromAddress++) & 0x1f; + u8 endH = (*fromAddress++) & 0x1f; + u8 endV = (*fromAddress++) & 0x1f; + + u8 * toAddress = gbSgbATF; + + for(u8 y = 0; y < 18; y++) { + for(u8 x = 0; x < 20; x++) { + if(x < startH || y < startV || + x > endH || y > endV) { + // outside + if(controlCode & 0x04) + *toAddress = (paletteDesignation >> 4) & 0x03; + } else if(x > startH && x < endH && + y > startV && y < endV) { + // inside + if(controlCode & 0x01) + *toAddress = paletteDesignation & 0x03; + } else { + // surrounding line + if(controlCode & 0x02) + *toAddress = (paletteDesignation>>2) & 0x03; + else if(controlCode == 0x01) + *toAddress = paletteDesignation & 0x03; + } + toAddress++; + } + } + nDataSet--; + } +} + +void gbSgbSetColumnPalette(u8 col, u8 p) +{ + // if(col < 0) + // col = 0; + if(col > 19) + col = 19; + + p &= 3; + + u8 *toAddress = &gbSgbATF[col]; + + for(u8 y = 0; y < 18; y++) { + *toAddress = p; + toAddress += 20; + } +} + +void gbSgbSetRowPalette(u8 row, u8 p) +{ + // if(row < 0) + // row = 0; + if(row > 17) + row = 17; + + p &= 3; + + u8 *toAddress = &gbSgbATF[row*20]; + + for(u8 x = 0; x < 20; x++) { + *toAddress++ = p; + } +} + +void gbSgbAttributeDivide() +{ + u8 control = gbSgbPacket[1]; + u8 coord = gbSgbPacket[2]; + u8 colorBR = control & 3; + u8 colorAL = (control >> 2) & 3; + u8 colorOL = (control >> 4) & 3; + + if(control & 0x40) { + if(coord > 17) + coord = 17; + + for(u8 i = 0; i < 18; i++) { + if(i < coord) + gbSgbSetRowPalette(i, colorAL); + else if ( i > coord) + gbSgbSetRowPalette(i, colorBR); + else + gbSgbSetRowPalette(i, colorOL); + } + } else { + if(coord > 19) + coord = 19; + + for(u8 i = 0; i < 20; i++) { + if(i < coord) + gbSgbSetColumnPalette(i, colorAL); + else if ( i > coord) + gbSgbSetColumnPalette(i, colorBR); + else + gbSgbSetColumnPalette(i, colorOL); + } + } +} + +void gbSgbAttributeLine() +{ + u8 *fromAddress = &gbSgbPacket[1]; + + u8 nDataSet = *fromAddress++; + + if(nDataSet > 0x6e) + nDataSet = 0x6e; + + while(nDataSet) { + u8 line = *fromAddress++; + u8 num = line & 0x1f; + u8 pal = (line >> 5) & 0x03; + if(line & 0x80) { + if(num > 17) + num = 17; + gbSgbSetRowPalette(num,pal); + } else { + if(num > 19) + num = 19; + gbSgbSetColumnPalette(num,pal); + } + nDataSet--; + } +} + +void gbSgbAttributeCharacter() +{ + u8 startH = gbSgbPacket[1] & 0x1f; + u8 startV = gbSgbPacket[2] & 0x1f; + int nDataSet = READ16LE(((u16 *)&gbSgbPacket[3])); + int style = gbSgbPacket[5] & 1; + if(startH > 19) + startH = 19; + if(startV > 17) + startV = 17; + + u8 s = 6; + u8 *fromAddress = &gbSgbPacket[6]; + u8 v = *fromAddress++; + + if(style) { + while(nDataSet) { + u8 p = (v >> s) & 3; + gbSgbATF[startV * 20 + startH] = p; + startV++; + if(startV == 18) { + startV = 0; + startH++; + if(startH == 20) + break; + } + + if(s) + s -= 2; + else { + s = 6; + v = *fromAddress++; + nDataSet--; + } + } + } else { + while(nDataSet) { + u8 p = (v >> s) & 3; + gbSgbATF[startV * 20 + startH] = p; + startH++; + if(startH == 20) { + startH = 0; + startV++; + if(startV == 18) + break; + } + + if(s) + s -= 2; + else { + s = 6; + v = *fromAddress++; + nDataSet--; + } + } + } +} + +void gbSgbSetATFList() +{ + gbSgbRenderScreenToBuffer(); + + u8 *fromAddress = gbSgbScreenBuffer; + u8 *toAddress = gbSgbATFList; + + for(int i = 0; i < 45; i++) { + for(int j = 0; j < 90; j++) { + u8 v = *fromAddress++; + u8 s = 6; + if(i == 2) + s = 6; + for(int k = 0; k < 4; k++) { + *toAddress++ = (v >> s) & 0x03; + s -= 2; + } + } + } +} + +void gbSgbMaskEnable() +{ + int gbSgbMaskFlag = gbSgbPacket[1] & 3; + + gbSgbMask = gbSgbMaskFlag; + + switch(gbSgbMaskFlag) { + case 1: + break; + case 2: + gbSgbFillScreen(0x0000); + // memset(&gbPalette[0], 0, 128*sizeof(u16)); + break; + case 3: + gbSgbFillScreen(gbPalette[0]); + break; + } + if(!gbSgbMask) { + if(gbBorderOn) + gbSgbRenderBorder(); + } +} + +void gbSgbChrTransfer() +{ + gbSgbRenderScreenToBuffer(); + + int address = (gbSgbPacket[1] & 1) * (128*32); + + if(gbSgbPacket[1] & 1) + gbSgbCGBSupport |= 2; + else + gbSgbCGBSupport |= 1; + + memcpy(&gbSgbBorderChar[address], gbSgbScreenBuffer, 128 * 32); + + if(gbBorderAutomatic && !gbBorderOn && gbSgbCGBSupport > 4) { + gbBorderOn = 1; + systemGbBorderOn(); + } + + if(gbBorderOn && !gbSgbMask) + gbSgbRenderBorder(); + + if(gbSgbMode && gbCgbMode && gbSgbCGBSupport == 7) { + gbSgbCGBSupport = 0; + gbSgbMode = 0; + gbSgbMask = 0; + gbSgbRenderBorder(); + gbReset(); + } + + if(gbSgbCGBSupport > 4) + gbSgbCGBSupport = 0; +} + +void gbSgbMultiRequest() +{ + if(gbSgbPacket[1] & 1) { + gbSgbMultiplayer = 1; + if(gbSgbPacket[1] & 2) + gbSgbFourPlayers = 1; + else + gbSgbFourPlayers = 0; + gbSgbNextController = 0x0e; + } else { + gbSgbFourPlayers = 0; + gbSgbMultiplayer = 0; + gbSgbNextController = 0x0f; + } +} + +void gbSgbCommand() +{ + int command = gbSgbPacket[0] >> 3; + // int nPacket = gbSgbPacket[0] & 7; + + switch(command) { + case 0x00: + gbSgbSetPalette(0,1,(u16 *)&gbSgbPacket[1]); + break; + case 0x01: + gbSgbSetPalette(2,3,(u16 *)&gbSgbPacket[1]); + break; + case 0x02: + gbSgbSetPalette(0,3,(u16 *)&gbSgbPacket[1]); + break; + case 0x03: + gbSgbSetPalette(1,2,(u16 *)&gbSgbPacket[1]); + break; + case 0x04: + gbSgbAttributeBlock(); + break; + case 0x05: + gbSgbAttributeLine(); + break; + case 0x06: + gbSgbAttributeDivide(); + break; + case 0x07: + gbSgbAttributeCharacter(); + break; + case 0x0a: + gbSgbSetPalette(); + break; + case 0x0b: + gbSgbScpPalette(); + break; + case 0x11: + gbSgbMultiRequest(); + break; + case 0x13: + gbSgbChrTransfer(); + break; + case 0x14: + gbSgbPicture(); + break; + case 0x15: + gbSgbSetATFList(); + break; + case 0x16: + gbSgbSetATF(gbSgbPacket[1] & 0x3f); + break; + case 0x17: + gbSgbMaskEnable(); + break; + } +} + +void gbSgbResetPacketState() +{ + gbSgbPacketState = GBSGB_NONE; + gbSgbPacketTimeout = 0; +} + +void gbSgbDoBitTransfer(u8 value) +{ + value = value & 0x30; + switch(gbSgbPacketState) { + case GBSGB_NONE: + if(value == 0) { + gbSgbPacketState = GBSGB_RESET; + gbSgbPacketTimeout = GBSGB_PACKET_TIMEOUT; + } else if (value == 0x30) { + if(gbSgbMultiplayer) { + if((gbSgbReadingController & 7) == 7) { + gbSgbReadingController = 0; + if(gbSgbMultiplayer) { + gbSgbNextController--; + if(gbSgbFourPlayers) { + if(gbSgbNextController == 0x0b) + gbSgbNextController = 0x0f; + } else { + if(gbSgbNextController == 0x0d) + gbSgbNextController = 0x0f; + } + } + } else { + gbSgbReadingController &= 3; + } + } + gbSgbPacketTimeout = 0; + } else { + if(value == 0x10) + gbSgbReadingController |= 0x2; + else if(value == 0x20) + gbSgbReadingController |= 0x01; + gbSgbPacketTimeout = 0; + } + gbSgbPacketTimeout = 0; + break; + case GBSGB_RESET: + if(value == 0x30) { + gbSgbPacketState = GBSGB_PACKET_TRANSMIT; + gbSgbPacketByte = 0; + gbSgbPacketNBits = 0; + gbSgbPacketTimeout = GBSGB_PACKET_TIMEOUT; + } else if(value == 0x00) { + gbSgbPacketTimeout = GBSGB_PACKET_TIMEOUT; + gbSgbPacketState = GBSGB_RESET; + } else { + gbSgbPacketState = GBSGB_NONE; + gbSgbPacketTimeout = 0; + } + break; + case GBSGB_PACKET_TRANSMIT: + if(value == 0) { + gbSgbPacketState = GBSGB_RESET; + gbSgbPacketTimeout = 0; + } else if (value == 0x30){ + if(gbSgbPacketNBits == 128) { + gbSgbPacketNBits = 0; + gbSgbPacketByte = 0; + gbSgbPacketNumber++; + gbSgbPacketTimeout = 0; + if(gbSgbPacketNumber == (gbSgbPacket[0] & 7)) { + gbSgbCommand(); + gbSgbPacketNumber = 0; + gbSgbPacketState = GBSGB_NONE; + gbSgbPacketTimeout = 0; + } + } else { + if(gbSgbPacketNBits < 128) { + gbSgbPacket[gbSgbPacketNumber * 16 + gbSgbPacketByte] >>= 1; + gbSgbPacket[gbSgbPacketNumber * 16 + gbSgbPacketByte] |= gbSgbBit; + gbSgbPacketNBits++; + if(!(gbSgbPacketNBits & 7)) { + gbSgbPacketByte++; + } + gbSgbPacketTimeout = GBSGB_PACKET_TIMEOUT; + } + } + } else { + if(value == 0x20) + gbSgbBit = 0x00; + else + gbSgbBit = 0x80; + gbSgbPacketTimeout = GBSGB_PACKET_TIMEOUT; + } + gbSgbReadingController = 0; + break; + default: + gbSgbPacketState = GBSGB_NONE; + gbSgbPacketTimeout = 0; + break; + } +} + +variable_desc gbSgbSaveStruct[] = { + { &gbSgbMask, sizeof(int) }, + { &gbSgbPacketState, sizeof(int) }, + { &gbSgbBit, sizeof(int) }, + { &gbSgbPacketNBits, sizeof(int) }, + { &gbSgbPacketByte, sizeof(int) }, + { &gbSgbPacketNumber, sizeof(int) }, + { &gbSgbMultiplayer, sizeof(int) }, + { &gbSgbNextController, sizeof(u8) }, + { &gbSgbReadingController, sizeof(u8) }, + { NULL, 0 } +}; + +variable_desc gbSgbSaveStructV3[] = { + { &gbSgbMask, sizeof(int) }, + { &gbSgbPacketState, sizeof(int) }, + { &gbSgbBit, sizeof(int) }, + { &gbSgbPacketNBits, sizeof(int) }, + { &gbSgbPacketByte, sizeof(int) }, + { &gbSgbPacketNumber, sizeof(int) }, + { &gbSgbMultiplayer, sizeof(int) }, + { &gbSgbNextController, sizeof(u8) }, + { &gbSgbReadingController, sizeof(u8) }, + { &gbSgbFourPlayers, sizeof(int) }, + { NULL, 0 } +}; + +void gbSgbSaveGame(gzFile gzFile) +{ + utilWriteData(gzFile, gbSgbSaveStructV3); + + utilGzWrite(gzFile, gbSgbBorder, 2048); + utilGzWrite(gzFile, gbSgbBorderChar, 32*256); + + utilGzWrite(gzFile, gbSgbPacket, 16*7); + + utilGzWrite(gzFile, gbSgbSCPPalette, 4 * 512 * sizeof(u16)); + utilGzWrite(gzFile, gbSgbATF, 20 * 18); + utilGzWrite(gzFile, gbSgbATFList, 45 * 20 * 18); +} + +void gbSgbReadGame(gzFile gzFile, int version) +{ + if(version >= 3) + utilReadData(gzFile, gbSgbSaveStructV3); + else { + utilReadData(gzFile, gbSgbSaveStruct); + gbSgbFourPlayers = 0; + } + + if(version >= 8) { + utilGzRead(gzFile, gbSgbBorder, 2048); + utilGzRead(gzFile, gbSgbBorderChar, 32*256); + } + + utilGzRead(gzFile, gbSgbPacket, 16*7); + + utilGzRead(gzFile, gbSgbSCPPalette, 4 * 512 * sizeof(u16)); + utilGzRead(gzFile, gbSgbATF, 20 * 18); + utilGzRead(gzFile, gbSgbATFList, 45 * 20 * 18); +} diff --git a/src/dmg/gbSGB.h b/src/dmg/gbSGB.h new file mode 100644 index 00000000..6dcd3b44 --- /dev/null +++ b/src/dmg/gbSGB.h @@ -0,0 +1,39 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +void gbSgbInit(); +void gbSgbShutdown(); +void gbSgbCommand(); +void gbSgbResetPacketState(); +void gbSgbReset(); +void gbSgbDoBitTransfer(u8); +void gbSgbSaveGame(gzFile); +void gbSgbReadGame(gzFile, int version); +void gbSgbRenderBorder(); + +extern u8 gbSgbATF[20*18]; +extern int gbSgbMode; +extern int gbSgbMask; +extern int gbSgbMultiplayer; +extern u8 gbSgbNextController; +extern int gbSgbPacketTimeout; +extern u8 gbSgbReadingController; +extern int gbSgbFourPlayers; + + diff --git a/src/dmg/gbSound.cpp b/src/dmg/gbSound.cpp new file mode 100644 index 00000000..66634243 --- /dev/null +++ b/src/dmg/gbSound.cpp @@ -0,0 +1,412 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2005-2006 Forgotten and the VBA development team +// Copyright (C) 2007-2008 VBA-M development team +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include + +#include "../System.h" +#include "../Util.h" +#include "gbGlobals.h" +#include "gbSound.h" + +#include "gb_apu/Gb_Apu.h" +#include "gb_apu/Effects_Buffer.h" + +static Gb_Apu* gb_apu; +static Simple_Effects_Buffer* stereo_buffer; + +extern u16 soundFinalWave[1470]; +extern int soundVolume; + +extern int gbHardware; + +extern void soundResume(); + +extern int soundBufferLen; +extern int soundQuality; +extern bool soundPaused; +extern int soundTicks; +extern int SOUND_CLOCK_TICKS; +extern u32 soundNextPosition; + +extern int soundDebug; + +extern bool soundEcho; +extern bool soundLowPass; +extern bool soundReverse; +extern bool soundOffFlag; + +int const ticks_to_time = 2 * GB_APU_OVERCLOCK; + +static inline blip_time_t blip_time() +{ + return (SOUND_CLOCK_TICKS - soundTicks) * ticks_to_time; +} + +u8 gbSoundRead( u16 address ) +{ + if ( gb_apu && address >= NR10 && address <= 0xFF3F ) + return gb_apu->read_register( blip_time(), address ); + + return gbMemory[address]; +} + +void gbSoundEvent(register u16 address, register int data) +{ + int freq = 0; + + gbMemory[address] = data; + +#ifndef FINAL_VERSION + if(soundDebug) { + // don't translate. debug only + log("Sound event: %08lx %02x\n", address, data); + } +#endif + + if ( gb_apu && address >= NR10 && address <= 0xFF3F ) + gb_apu->write_register( blip_time(), address, data ); +} + +static void end_frame( blip_time_t time ) +{ + gb_apu ->end_frame( time ); + stereo_buffer->end_frame( time ); +} + +static void flush_samples() +{ + // number of samples in output buffer + int const out_buf_size = soundBufferLen / sizeof *soundFinalWave; + + // Keep filling and writing soundFinalWave until it can't be fully filled + while ( stereo_buffer->samples_avail() >= out_buf_size ) + { + stereo_buffer->read_samples( (blip_sample_t*) soundFinalWave, out_buf_size ); + if(systemSoundOn) + { + if(soundPaused) + soundResume(); + + systemWriteDataToSoundBuffer(); + } + } +} + +int const chan_count = 4; + +gb_effects_config_t gb_effects_config; +static gb_effects_config_t gb_effects_config_current; + +static void apply_effects() +{ + gb_effects_config_current = gb_effects_config; + + stereo_buffer->config().enabled = gb_effects_config_current.enabled; + stereo_buffer->config().echo = gb_effects_config_current.echo; + stereo_buffer->config().stereo = gb_effects_config_current.stereo; + stereo_buffer->config().surround = gb_effects_config_current.surround; + stereo_buffer->apply_config(); + + for ( int i = 0; i < chan_count; i++ ) + { + Multi_Buffer::channel_t ch = stereo_buffer->channel( i ); + gb_apu->set_output( ch.center, ch.left, ch.right, i ); + } +} + +void gbSoundTick() +{ + if ( systemSoundOn && gb_apu && stereo_buffer ) + { + // Run sound hardware to present + end_frame( SOUND_CLOCK_TICKS * ticks_to_time ); + + flush_samples(); + + gb_effects_config.enabled = soundEcho; + + // Update effects config if it was changed + if ( memcmp( &gb_effects_config_current, &gb_effects_config, + sizeof gb_effects_config ) ) + apply_effects(); + } +} + +static void reset_apu() +{ + // Use DMG or CGB sound differences based on type of game + gb_apu->reset( gbHardware & 1 ? gb_apu->mode_dmg : gb_apu->mode_cgb ); + + if ( stereo_buffer ) + stereo_buffer->clear(); + + soundTicks = SOUND_CLOCK_TICKS; +} + +static void remake_stereo_buffer() +{ + // Stereo_Buffer + delete stereo_buffer; + stereo_buffer = 0; + + stereo_buffer = new Simple_Effects_Buffer; // TODO: handle out of memory + stereo_buffer->set_sample_rate( 44100 / soundQuality ); // TODO: handle out of memory + stereo_buffer->clock_rate( gb_apu->clock_rate ); + + // APU + static int const chan_types [chan_count] = { + Multi_Buffer::wave_type+1, Multi_Buffer::wave_type+2, + Multi_Buffer::wave_type+3, Multi_Buffer::mixed_type+1 + }; + stereo_buffer->set_channel_count( chan_count, chan_types ); + + if ( !gb_apu ) + { + gb_apu = new Gb_Apu; + reset_apu(); + } + + apply_effects(); +} + +void gbSoundReset() +{ + gb_effects_config.echo = 0.20f; + gb_effects_config.stereo = 0.15f; + gb_effects_config.surround = false; + + SOUND_CLOCK_TICKS = 20000; + + remake_stereo_buffer(); + reset_apu(); + + soundPaused = 1; + soundNextPosition = 0; + + // don't translate +#ifndef FINAL_VERSION + if(soundDebug) { + log("*** Sound Init ***\n"); + } +#endif + gbSoundEvent(0xff10, 0x80); + gbSoundEvent(0xff11, 0xbf); + gbSoundEvent(0xff12, 0xf3); + gbSoundEvent(0xff14, 0xbf); + gbSoundEvent(0xff16, 0x3f); + gbSoundEvent(0xff17, 0x00); + gbSoundEvent(0xff19, 0xbf); + + gbSoundEvent(0xff1a, 0x7f); + gbSoundEvent(0xff1b, 0xff); + gbSoundEvent(0xff1c, 0xbf); + gbSoundEvent(0xff1e, 0xbf); + + gbSoundEvent(0xff20, 0xff); + gbSoundEvent(0xff21, 0x00); + gbSoundEvent(0xff22, 0x00); + gbSoundEvent(0xff23, 0xbf); + gbSoundEvent(0xff24, 0x77); + gbSoundEvent(0xff25, 0xf3); + + if (gbHardware & 0x4) + gbSoundEvent(0xff26, 0xf0); + else + gbSoundEvent(0xff26, 0xf1); + + // don't translate +#ifndef FINAL_VERSION + if(soundDebug) { + log("*** Sound Init Complete ***\n"); + } +#endif + int addr = 0xff30; + + while(addr < 0xff40) { + gbMemory[addr++] = 0x00; + gbMemory[addr++] = 0xff; + } +} + +extern bool soundInit(); +extern void soundShutdown(); + +void gbSoundSetQuality(int quality) +{ + if ( soundQuality != quality ) + { + if ( systemCanChangeSoundQuality() ) + { + if ( !soundOffFlag ) + soundShutdown(); + + soundQuality = quality; + soundNextPosition = 0; + + if ( !soundOffFlag ) + soundInit(); + } + else + { + soundQuality = quality; + soundNextPosition = 0; + } + + remake_stereo_buffer(); + } +} + +static char dummy_buf [735 * 2]; + +#define SKIP( type, name ) { dummy_buf, sizeof (type) } + +// funny expr at end ensures that type matches type of variable +#define LOAD( type, name ) { &name, sizeof (name) + (&name - (type*) &name) } + +static variable_desc gbsound_format [] = +{ + SKIP( int, soundPaused ), + SKIP( int, soundPlay ), + SKIP( int, soundTicks ), + SKIP( int, SOUND_CLOCK_TICKS ), + SKIP( int, soundLevel1 ), + SKIP( int, soundLevel2 ), + SKIP( int, soundBalance ), + SKIP( int, soundMasterOn ), + SKIP( int, soundIndex ), + SKIP( int, soundVIN ), + SKIP( int, soundOn [0] ), + SKIP( int, soundATL [0] ), + SKIP( int, sound1Skip ), + SKIP( int, soundIndex [0] ), + SKIP( int, sound1Continue ), + SKIP( int, soundEnvelopeVolume [0] ), + SKIP( int, soundEnvelopeATL [0] ), + SKIP( int, sound1EnvelopeATLReload ), + SKIP( int, sound1EnvelopeUpDown ), + SKIP( int, sound1SweepATL ), + SKIP( int, sound1SweepATLReload ), + SKIP( int, sound1SweepSteps ), + SKIP( int, sound1SweepUpDown ), + SKIP( int, sound1SweepStep ), + SKIP( int, soundOn [1] ), + SKIP( int, soundATL [1] ), + SKIP( int, sound2Skip ), + SKIP( int, soundIndex [1] ), + SKIP( int, sound2Continue ), + SKIP( int, soundEnvelopeVolume [1] ), + SKIP( int, soundEnvelopeATL [1] ), + SKIP( int, sound2EnvelopeATLReload ), + SKIP( int, sound2EnvelopeUpDown ), + SKIP( int, soundOn [2] ), + SKIP( int, soundATL [2] ), + SKIP( int, sound3Skip ), + SKIP( int, soundIndex [2] ), + SKIP( int, sound3Continue ), + SKIP( int, sound3OutputLevel ), + SKIP( int, soundOn [3] ), + SKIP( int, soundATL [3] ), + SKIP( int, sound4Skip ), + SKIP( int, soundIndex [3] ), + SKIP( int, sound4Clock ), + SKIP( int, sound4ShiftRight ), + SKIP( int, sound4ShiftSkip ), + SKIP( int, sound4ShiftIndex ), + SKIP( int, sound4NSteps ), + SKIP( int, sound4CountDown ), + SKIP( int, sound4Continue ), + SKIP( int, soundEnvelopeVolume [2] ), + SKIP( int, soundEnvelopeATL [2] ), + SKIP( int, sound4EnvelopeATLReload ), + SKIP( int, sound4EnvelopeUpDown ), + SKIP( int, soundEnableFlag ), + { NULL, 0 } +}; + +static variable_desc gbsound_format2 [] = +{ + SKIP( int, sound1ATLreload ), + SKIP( int, freq1low ), + SKIP( int, freq1high ), + SKIP( int, sound2ATLreload ), + SKIP( int, freq2low ), + SKIP( int, freq2high ), + SKIP( int, sound3ATLreload ), + SKIP( int, freq3low ), + SKIP( int, freq3high ), + SKIP( int, sound4ATLreload ), + SKIP( int, freq4 ), + { NULL, 0 } +}; + +static variable_desc gbsound_format3 [] = +{ + SKIP( u8[2*735], soundBuffer ), + SKIP( u8[2*735], soundBuffer ), + SKIP( u16[735], soundFinalWave ), + { NULL, 0 } +}; + +void gbSoundSaveGame(gzFile gzFile) +{ + // TODO: implement +} + +enum { + nr10 = 0, + nr11, nr12, nr13, nr14, + nr20, nr21, nr22, nr23, nr24, + nr30, nr31, nr32, nr33, nr34, + nr40, nr41, nr42, nr43, nr44, + nr50, nr51, nr52 +}; + +void gbSoundReadGame(int version,gzFile gzFile) +{ + return; // TODO: apparently GB save states don't work in the main emulator + + // Load state + utilReadData( gzFile, gbsound_format ); + + if ( version >= 11 ) + utilReadData( gzFile, gbsound_format2 ); + + utilReadData( gzFile, gbsound_format3 ); + + int quality = 1; + if ( version >= 7 ) + quality = utilReadInt( gzFile ); + + gbSoundSetQuality( quality ); + + // Convert to format Gb_Apu uses + reset_apu(); + gb_apu_state_t s; + gb_apu->save_state( &s ); // use fresh values for anything not restored + + // Only some registers are properly preserved + static int const regs_to_copy [] = { + nr10, nr11, nr12, nr21, nr22, nr30, nr32, nr42, nr43, nr50, nr51, nr52, -1 + }; + for ( int i = 0; regs_to_copy [i] >= 0; i++ ) + s.regs [regs_to_copy [i]] = gbMemory [0xFF10 + regs_to_copy [i]]; + + memcpy( &s.regs [0x20], &gbMemory [0xFF30], 0x10 ); // wave + + gb_apu->load_state( s ); +} diff --git a/src/dmg/gbSound.h b/src/dmg/gbSound.h new file mode 100644 index 00000000..75d5fb0e --- /dev/null +++ b/src/dmg/gbSound.h @@ -0,0 +1,74 @@ +// -*- C++ -*- +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#define NR10 0xff10 +#define NR11 0xff11 +#define NR12 0xff12 +#define NR13 0xff13 +#define NR14 0xff14 +#define NR21 0xff16 +#define NR22 0xff17 +#define NR23 0xff18 +#define NR24 0xff19 +#define NR30 0xff1a +#define NR31 0xff1b +#define NR32 0xff1c +#define NR33 0xff1d +#define NR34 0xff1e +#define NR41 0xff20 +#define NR42 0xff21 +#define NR43 0xff22 +#define NR44 0xff23 +#define NR50 0xff24 +#define NR51 0xff25 +#define NR52 0xff26 + +#define SOUND_EVENT(address,value) \ + gbSoundEvent(address,value) + +extern void gbSoundTick(); +extern void gbSoundPause(); +extern void gbSoundResume(); +extern void gbSoundEnable(int); +extern void gbSoundDisable(int); +extern int gbSoundGetEnable(); +extern void gbSoundReset(); +extern void gbSoundSaveGame(gzFile); +extern void gbSoundReadGame(int,gzFile); +extern void gbSoundEvent(register u16, register int); +extern void gbSoundSetQuality(int); + +extern u8 gbSoundRead(u16 address); + +extern int soundTicks; +extern int soundQuality; +extern int SOUND_CLOCK_TICKS; + +struct gb_effects_config_t +{ + bool enabled; // false = disable all effects + + float echo; // 0.0 = none, 1.0 = lots + float stereo; // 0.0 = channels in center, 1.0 = channels on left/right + bool surround; // true = put some channels in back +}; + +// Can be changed at any time, probably from another thread too. +// Sound will notice changes during next 1/100 second. +extern gb_effects_config_t gb_effects_config; diff --git a/src/dmg/gb_apu/Blip_Buffer.cpp b/src/dmg/gb_apu/Blip_Buffer.cpp new file mode 100644 index 00000000..ab9faa94 --- /dev/null +++ b/src/dmg/gb_apu/Blip_Buffer.cpp @@ -0,0 +1,465 @@ +// Blip_Buffer 0.4.1. http://www.slack.net/~ant/ + +#include "Blip_Buffer.h" + +#include +#include +#include +#include +#include + +/* Copyright (C) 2003-2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +// TODO: use scoped for variables in treble_eq() + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +int const silent_buf_size = 1; // size used for Silent_Blip_Buffer + +Blip_Buffer::Blip_Buffer() +{ + factor_ = LONG_MAX; + buffer_ = 0; + buffer_size_ = 0; + sample_rate_ = 0; + bass_shift_ = 0; + clock_rate_ = 0; + bass_freq_ = 16; + length_ = 0; + + // assumptions code makes about implementation-defined features + #ifndef NDEBUG + // right shift of negative value preserves sign + buf_t_ i = -0x7FFFFFFE; + assert( (i >> 1) == -0x3FFFFFFF ); + + // casting to short truncates to 16 bits and sign-extends + i = 0x18000; + assert( (short) i == -0x8000 ); + #endif + + clear(); +} + +Blip_Buffer::~Blip_Buffer() +{ + if ( buffer_size_ != silent_buf_size ) + free( buffer_ ); +} + +Silent_Blip_Buffer::Silent_Blip_Buffer() +{ + factor_ = 0; + buffer_ = buf; + buffer_size_ = silent_buf_size; + clear(); +} + +void Blip_Buffer::clear( int entire_buffer ) +{ + offset_ = 0; + reader_accum_ = 0; + modified_ = 0; + if ( buffer_ ) + { + long count = (entire_buffer ? buffer_size_ : samples_avail()); + memset( buffer_, 0, (count + blip_buffer_extra_) * sizeof (buf_t_) ); + } +} + +Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec ) +{ + if ( buffer_size_ == silent_buf_size ) + { + assert( 0 ); + return "Internal (tried to resize Silent_Blip_Buffer)"; + } + + // start with maximum length that resampled time can represent + long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - blip_buffer_extra_ - 64; + if ( msec != blip_max_length ) + { + long s = (new_rate * (msec + 1) + 999) / 1000; + if ( s < new_size ) + new_size = s; + else + assert( 0 ); // fails if requested buffer length exceeds limit + } + + if ( buffer_size_ != new_size ) + { + void* p = realloc( buffer_, (new_size + blip_buffer_extra_) * sizeof *buffer_ ); + if ( !p ) + return "Out of memory"; + buffer_ = (buf_t_*) p; + } + + buffer_size_ = new_size; + assert( buffer_size_ != silent_buf_size ); // size should never happen to match this + + // update things based on the sample rate + sample_rate_ = new_rate; + length_ = new_size * 1000 / new_rate - 1; + if ( msec ) + assert( length_ == msec ); // ensure length is same as that passed in + + // update these since they depend on sample rate + if ( clock_rate_ ) + clock_rate( clock_rate_ ); + bass_freq( bass_freq_ ); + + clear(); + + return 0; // success +} + +blip_resampled_time_t Blip_Buffer::clock_rate_factor( long rate ) const +{ + double ratio = (double) sample_rate_ / rate; + blip_long factor = (blip_long) floor( ratio * (1L << BLIP_BUFFER_ACCURACY) + 0.5 ); + assert( factor > 0 || !sample_rate_ ); // fails if clock/output ratio is too large + return (blip_resampled_time_t) factor; +} + +void Blip_Buffer::bass_freq( int freq ) +{ + bass_freq_ = freq; + int shift = 31; + if ( freq > 0 ) + { + shift = 13; + long f = (freq << 16) / sample_rate_; + while ( (f >>= 1) && --shift ) { } + } + bass_shift_ = shift; +} + +void Blip_Buffer::end_frame( blip_time_t t ) +{ + offset_ += t * factor_; + assert( samples_avail() <= (long) buffer_size_ ); // fails if time is past end of buffer +} + +long Blip_Buffer::count_samples( blip_time_t t ) const +{ + blip_resampled_time_t last_sample = resampled_time( t ) >> BLIP_BUFFER_ACCURACY; + blip_resampled_time_t first_sample = offset_ >> BLIP_BUFFER_ACCURACY; + return long (last_sample - first_sample); +} + +blip_time_t Blip_Buffer::count_clocks( long count ) const +{ + if ( !factor_ ) + { + assert( 0 ); // sample rate and clock rates must be set first + return 0; + } + + if ( count > buffer_size_ ) + count = buffer_size_; + blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; + return (blip_time_t) ((time - offset_ + factor_ - 1) / factor_); +} + +void Blip_Buffer::remove_samples( long count ) +{ + if ( count ) + { + remove_silence( count ); + + // copy remaining samples to beginning and clear old samples + long remain = samples_avail() + blip_buffer_extra_; + memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ ); + memset( buffer_ + remain, 0, count * sizeof *buffer_ ); + } +} + +// Blip_Synth_ + +Blip_Synth_Fast_::Blip_Synth_Fast_() +{ + buf = 0; + last_amp = 0; + delta_factor = 0; +} + +void Blip_Synth_Fast_::volume_unit( double new_unit ) +{ + delta_factor = int (new_unit * (1L << blip_sample_bits) + 0.5); +} + +#if !BLIP_BUFFER_FAST + +Blip_Synth_::Blip_Synth_( short* p, int w ) : + impulses( p ), + width( w ) +{ + volume_unit_ = 0.0; + kernel_unit = 0; + buf = 0; + last_amp = 0; + delta_factor = 0; +} + +#undef PI +#define PI 3.1415926535897932384626433832795029 + +static void gen_sinc( float* out, int count, double oversample, double treble, double cutoff ) +{ + if ( cutoff >= 0.999 ) + cutoff = 0.999; + + if ( treble < -300.0 ) + treble = -300.0; + if ( treble > 5.0 ) + treble = 5.0; + + double const maxh = 4096.0; + double const rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - cutoff) ); + double const pow_a_n = pow( rolloff, maxh - maxh * cutoff ); + double const to_angle = PI / 2 / maxh / oversample; + for ( int i = 0; i < count; i++ ) + { + double angle = ((i - count) * 2 + 1) * to_angle; + double c = rolloff * cos( (maxh - 1.0) * angle ) - cos( maxh * angle ); + double cos_nc_angle = cos( maxh * cutoff * angle ); + double cos_nc1_angle = cos( (maxh * cutoff - 1.0) * angle ); + double cos_angle = cos( angle ); + + c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle; + double d = 1.0 + rolloff * (rolloff - cos_angle - cos_angle); + double b = 2.0 - cos_angle - cos_angle; + double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle; + + out [i] = (float) ((a * d + c * b) / (b * d)); // a / b + c / d + } +} + +void blip_eq_t::generate( float* out, int count ) const +{ + // lower cutoff freq for narrow kernels with their wider transition band + // (8 points->1.49, 16 points->1.15) + double oversample = blip_res * 2.25 / count + 0.85; + double half_rate = sample_rate * 0.5; + if ( cutoff_freq ) + oversample = half_rate / cutoff_freq; + double cutoff = rolloff_freq * oversample / half_rate; + + gen_sinc( out, count, blip_res * oversample, treble, cutoff ); + + // apply (half of) hamming window + double to_fraction = PI / (count - 1); + for ( int i = count; i--; ) + out [i] *= 0.54f - 0.46f * (float) cos( i * to_fraction ); +} + +void Blip_Synth_::adjust_impulse() +{ + // sum pairs for each phase and add error correction to end of first half + int const size = impulses_size(); + for ( int p = blip_res; p-- >= blip_res / 2; ) + { + int p2 = blip_res - 2 - p; + long error = kernel_unit; + for ( int i = 1; i < size; i += blip_res ) + { + error -= impulses [i + p ]; + error -= impulses [i + p2]; + } + if ( p == p2 ) + error /= 2; // phase = 0.5 impulse uses same half for both sides + impulses [size - blip_res + p] += (short) error; + //printf( "error: %ld\n", error ); + } + + //for ( int i = blip_res; i--; printf( "\n" ) ) + // for ( int j = 0; j < width / 2; j++ ) + // printf( "%5ld,", impulses [j * blip_res + i + 1] ); +} + +void Blip_Synth_::treble_eq( blip_eq_t const& eq ) +{ + float fimpulse [blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2]; + + int const half_size = blip_res / 2 * (width - 1); + eq.generate( &fimpulse [blip_res], half_size ); + + int i; + + // need mirror slightly past center for calculation + for ( i = blip_res; i--; ) + fimpulse [blip_res + half_size + i] = fimpulse [blip_res + half_size - 1 - i]; + + // starts at 0 + for ( i = 0; i < blip_res; i++ ) + fimpulse [i] = 0.0f; + + // find rescale factor + double total = 0.0; + for ( i = 0; i < half_size; i++ ) + total += fimpulse [blip_res + i]; + + //double const base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB + //double const base_unit = 37888.0; // allows treble to +5 dB + double const base_unit = 32768.0; // necessary for blip_unscaled to work + double rescale = base_unit / 2 / total; + kernel_unit = (long) base_unit; + + // integrate, first difference, rescale, convert to int + double sum = 0.0; + double next = 0.0; + int const size = this->impulses_size(); + for ( i = 0; i < size; i++ ) + { + impulses [i] = (short) (int) floor( (next - sum) * rescale + 0.5 ); + sum += fimpulse [i]; + next += fimpulse [i + blip_res]; + } + adjust_impulse(); + + // volume might require rescaling + double vol = volume_unit_; + if ( vol ) + { + volume_unit_ = 0.0; + volume_unit( vol ); + } +} + +void Blip_Synth_::volume_unit( double new_unit ) +{ + if ( new_unit != volume_unit_ ) + { + // use default eq if it hasn't been set yet + if ( !kernel_unit ) + treble_eq( -8.0 ); + + volume_unit_ = new_unit; + double factor = new_unit * (1L << blip_sample_bits) / kernel_unit; + + if ( factor > 0.0 ) + { + int shift = 0; + + // if unit is really small, might need to attenuate kernel + while ( factor < 2.0 ) + { + shift++; + factor *= 2.0; + } + + if ( shift ) + { + kernel_unit >>= shift; + assert( kernel_unit > 0 ); // fails if volume unit is too low + + // keep values positive to avoid round-towards-zero of sign-preserving + // right shift for negative values + long offset = 0x8000 + (1 << (shift - 1)); + long offset2 = 0x8000 >> shift; + for ( int i = impulses_size(); i--; ) + impulses [i] = (short) (int) (((impulses [i] + offset) >> shift) - offset2); + adjust_impulse(); + } + } + delta_factor = (int) floor( factor + 0.5 ); + //printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit ); + } +} +#endif + +long Blip_Buffer::read_samples( blip_sample_t* out_, long max_samples, int stereo ) +{ + long count = samples_avail(); + if ( count > max_samples ) + count = max_samples; + + if ( count ) + { + int const bass = BLIP_READER_BASS( *this ); + BLIP_READER_BEGIN( reader, *this ); + BLIP_READER_ADJ_( reader, count ); + blip_sample_t* BLIP_RESTRICT out = out_ + count; + blip_long offset = (blip_long) -count; + + if ( !stereo ) + { + do + { + blip_long s = BLIP_READER_READ( reader ); + BLIP_READER_NEXT_IDX_( reader, bass, offset ); + BLIP_CLAMP( s, s ); + out [offset] = (blip_sample_t) s; + } + while ( ++offset ); + } + else + { + do + { + blip_long s = BLIP_READER_READ( reader ); + BLIP_READER_NEXT_IDX_( reader, bass, offset ); + BLIP_CLAMP( s, s ); + out [offset * 2] = (blip_sample_t) s; + } + while ( ++offset ); + } + + BLIP_READER_END( reader, *this ); + + remove_samples( count ); + } + return count; +} + +void Blip_Buffer::mix_samples( blip_sample_t const* in, long count ) +{ + if ( buffer_size_ == silent_buf_size ) + { + assert( 0 ); + return; + } + + buf_t_* out = buffer_ + (offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2; + + int const sample_shift = blip_sample_bits - 16; + int prev = 0; + while ( count-- ) + { + blip_long s = (blip_long) *in++ << sample_shift; + *out += s - prev; + prev = s; + ++out; + } + *out -= prev; +} + +blip_ulong const subsample_mask = (1L << BLIP_BUFFER_ACCURACY) - 1; + +void Blip_Buffer::save_state( blip_buffer_state_t* out ) +{ + assert( samples_avail() == 0 ); + out->offset_ = offset_; + out->reader_accum_ = reader_accum_; + memcpy( out->buf, &buffer_ [offset_ >> BLIP_BUFFER_ACCURACY], sizeof out->buf ); +} + +void Blip_Buffer::load_state( blip_buffer_state_t const& in ) +{ + clear( false ); + + offset_ = in.offset_; + reader_accum_ = in.reader_accum_; + memcpy( buffer_, in.buf, sizeof in.buf ); +} diff --git a/src/dmg/gb_apu/Blip_Buffer.h b/src/dmg/gb_apu/Blip_Buffer.h new file mode 100644 index 00000000..831dcd67 --- /dev/null +++ b/src/dmg/gb_apu/Blip_Buffer.h @@ -0,0 +1,556 @@ +// Band-limited sound synthesis buffer + +// Blip_Buffer 0.4.1 +#ifndef BLIP_BUFFER_H +#define BLIP_BUFFER_H + + // internal + #include + #if INT_MAX < 0x7FFFFFFF || LONG_MAX == 0x7FFFFFFF + typedef long blip_long; + typedef unsigned long blip_ulong; + #else + typedef int blip_long; + typedef unsigned blip_ulong; + #endif + +// Time unit at source clock rate +typedef blip_long blip_time_t; + +// Output samples are 16-bit signed, with a range of -32768 to 32767 +typedef short blip_sample_t; +enum { blip_sample_max = 32767 }; + +struct blip_buffer_state_t; + +class Blip_Buffer { +public: + typedef const char* blargg_err_t; + + // Sets output sample rate and buffer length in milliseconds (1/1000 sec, defaults + // to 1/4 second) and clears buffer. If there isn't enough memory, leaves buffer + // untouched and returns "Out of memory", otherwise returns NULL. + blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 ); + + // Sets number of source time units per second + void clock_rate( long clocks_per_sec ); + + // Ends current time frame of specified duration and makes its samples available + // (along with any still-unread samples) for reading with read_samples(). Begins + // a new time frame at the end of the current frame. + void end_frame( blip_time_t time ); + + // Reads at most 'max_samples' out of buffer into 'dest', removing them from from + // the buffer. Returns number of samples actually read and removed. If stereo is + // true, increments 'dest' one extra time after writing each sample, to allow + // easy interleving of two channels into a stereo output buffer. + long read_samples( blip_sample_t* dest, long max_samples, int stereo = 0 ); + +// Additional features + + // Removes all available samples and clear buffer to silence. If 'entire_buffer' is + // false, just clears out any samples waiting rather than the entire buffer. + void clear( int entire_buffer = 1 ); + + // Number of samples available for reading with read_samples() + long samples_avail() const; + + // Removes 'count' samples from those waiting to be read + void remove_samples( long count ); + + // Sets frequency high-pass filter frequency, where higher values reduce bass more + void bass_freq( int frequency ); + + // Current output sample rate + long sample_rate() const; + + // Length of buffer in milliseconds + int length() const; + + // Number of source time units per second + long clock_rate() const; + +// Experimental features + + // Saves state, including high-pass filter and tails of last deltas. + // All samples must have been read from buffer before calling this. + void save_state( blip_buffer_state_t* out ); + + // Loads state. State must have been saved from Blip_Buffer with same + // settings during same run of program. States can NOT be stored on disk. + // Clears buffer before loading state. + void load_state( blip_buffer_state_t const& in ); + + // Number of samples delay from synthesis to samples read out + int output_latency() const; + + // Counts number of clocks needed until 'count' samples will be available. + // If buffer can't even hold 'count' samples, returns number of clocks until + // buffer becomes full. + blip_time_t count_clocks( long count ) const; + + // Number of raw samples that can be mixed within frame of specified duration. + long count_samples( blip_time_t duration ) const; + + // Mixes in 'count' samples from 'buf_in' + void mix_samples( blip_sample_t const* buf_in, long count ); + + + // Signals that sound has been added to buffer. Could be done automatically in + // Blip_Synth, but that would affect performance more, as you can arrange that + // this is called only once per time frame rather than for every delta. + void set_modified() { modified_ = this; } + + // not documented yet + blip_ulong unsettled() const; + Blip_Buffer* clear_modified() { Blip_Buffer* b = modified_; modified_ = 0; return b; } + void remove_silence( long count ); + typedef blip_ulong blip_resampled_time_t; + blip_resampled_time_t resampled_duration( int t ) const { return t * factor_; } + blip_resampled_time_t resampled_time( blip_time_t t ) const { return t * factor_ + offset_; } + blip_resampled_time_t clock_rate_factor( long clock_rate ) const; +public: + Blip_Buffer(); + ~Blip_Buffer(); + + // Deprecated + typedef blip_resampled_time_t resampled_time_t; + blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); } + blargg_err_t sample_rate( long r, int msec ) { return set_sample_rate( r, msec ); } +private: + // noncopyable + Blip_Buffer( const Blip_Buffer& ); + Blip_Buffer& operator = ( const Blip_Buffer& ); +public: + typedef blip_long buf_t_; + blip_ulong factor_; + blip_resampled_time_t offset_; + buf_t_* buffer_; + blip_long buffer_size_; + blip_long reader_accum_; + int bass_shift_; +private: + long sample_rate_; + long clock_rate_; + int bass_freq_; + int length_; + Blip_Buffer* modified_; // non-zero = true (more optimal than using bool, heh) + friend class Blip_Reader; +}; + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +// Number of bits in resample ratio fraction. Higher values give a more accurate ratio +// but reduce maximum buffer size. +#ifndef BLIP_BUFFER_ACCURACY + #define BLIP_BUFFER_ACCURACY 16 +#endif + +// Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in +// noticeable broadband noise when synthesizing high frequency square waves. +// Affects size of Blip_Synth objects since they store the waveform directly. +#ifndef BLIP_PHASE_BITS + #if BLIP_BUFFER_FAST + #define BLIP_PHASE_BITS 8 + #else + #define BLIP_PHASE_BITS 6 + #endif +#endif + + // Internal + typedef blip_ulong blip_resampled_time_t; + int const blip_widest_impulse_ = 16; + int const blip_buffer_extra_ = blip_widest_impulse_ + 2; + int const blip_res = 1 << BLIP_PHASE_BITS; + class blip_eq_t; + + class Blip_Synth_Fast_ { + public: + Blip_Buffer* buf; + int last_amp; + int delta_factor; + + void volume_unit( double ); + Blip_Synth_Fast_(); + void treble_eq( blip_eq_t const& ) { } + }; + + class Blip_Synth_ { + public: + Blip_Buffer* buf; + int last_amp; + int delta_factor; + + void volume_unit( double ); + Blip_Synth_( short* impulses, int width ); + void treble_eq( blip_eq_t const& ); + private: + double volume_unit_; + short* const impulses; + int const width; + blip_long kernel_unit; + int impulses_size() const { return blip_res / 2 * width + 1; } + void adjust_impulse(); + }; + +// Quality level, better = slower. In general, use blip_good_quality. +const int blip_med_quality = 8; +const int blip_good_quality = 12; +const int blip_high_quality = 16; + +// Range specifies the greatest expected change in amplitude. Calculate it +// by finding the difference between the maximum and minimum expected +// amplitudes (max - min). +template +class Blip_Synth { +public: + // Sets overall volume of waveform + void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); } + + // Configures low-pass filter (see blip_buffer.txt) + void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); } + + // Gets/sets Blip_Buffer used for output + Blip_Buffer* output() const { return impl.buf; } + void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; } + + // Updates amplitude of waveform at given time. Using this requires a separate + // Blip_Synth for each waveform. + void update( blip_time_t time, int amplitude ); + +// Low-level interface + + // Adds an amplitude transition of specified delta, optionally into specified buffer + // rather than the one set with output(). Delta can be positive or negative. + // The actual change in amplitude is delta * (volume / range) + void offset( blip_time_t, int delta, Blip_Buffer* ) const; + void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); } + + // Works directly in terms of fractional output samples. Contact author for more info. + void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; + + // Same as offset(), except code is inlined for higher performance + void offset_inline( blip_time_t t, int delta, Blip_Buffer* buf ) const { + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); + } + void offset_inline( blip_time_t t, int delta ) const { + offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); + } + +private: +#if BLIP_BUFFER_FAST + Blip_Synth_Fast_ impl; +#else + Blip_Synth_ impl; + typedef short imp_t; + imp_t impulses [blip_res * (quality / 2) + 1]; +public: + Blip_Synth() : impl( impulses, quality ) { } +#endif +}; + +// Low-pass equalization parameters +class blip_eq_t { +public: + // Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce + // treble, small positive values (0 to 5.0) increase treble. + blip_eq_t( double treble_db = 0 ); + + // See blip_buffer.txt + blip_eq_t( double treble, long rolloff_freq, long sample_rate, long cutoff_freq = 0 ); + +private: + double treble; + long rolloff_freq; + long sample_rate; + long cutoff_freq; + void generate( float* out, int count ) const; + friend class Blip_Synth_; +}; + +int const blip_sample_bits = 30; + +// Dummy Blip_Buffer to direct sound output to, for easy muting without +// having to stop sound code. +class Silent_Blip_Buffer : public Blip_Buffer { + buf_t_ buf [blip_buffer_extra_ + 1]; +public: + // The following cannot be used (an assertion will fail if attempted): + blargg_err_t set_sample_rate( long samples_per_sec, int msec_length ); + blip_time_t count_clocks( long count ) const; + void mix_samples( blip_sample_t const* buf, long count ); + + Silent_Blip_Buffer(); +}; + + #if __GNUC__ >= 3 || _MSC_VER >= 1100 + #define BLIP_RESTRICT __restrict + #else + #define BLIP_RESTRICT + #endif + +// Optimized reading from Blip_Buffer, for use in custom sample output + +// Begins reading from buffer. Name should be unique to the current block. +#define BLIP_READER_BEGIN( name, blip_buffer ) \ + const Blip_Buffer::buf_t_* BLIP_RESTRICT name##_reader_buf = (blip_buffer).buffer_;\ + blip_long name##_reader_accum = (blip_buffer).reader_accum_ + +// Gets value to pass to BLIP_READER_NEXT() +#define BLIP_READER_BASS( blip_buffer ) ((blip_buffer).bass_shift_) + +// Constant value to use instead of BLIP_READER_BASS(), for slightly more optimal +// code at the cost of having no bass control +int const blip_reader_default_bass = 9; + +// Current sample +#define BLIP_READER_READ( name ) (name##_reader_accum >> (blip_sample_bits - 16)) + +// Current raw sample in full internal resolution +#define BLIP_READER_READ_RAW( name ) (name##_reader_accum) + +// Advances to next sample +#define BLIP_READER_NEXT( name, bass ) \ + (void) (name##_reader_accum += *name##_reader_buf++ - (name##_reader_accum >> (bass))) + +// Ends reading samples from buffer. The number of samples read must now be removed +// using Blip_Buffer::remove_samples(). +#define BLIP_READER_END( name, blip_buffer ) \ + (void) ((blip_buffer).reader_accum_ = name##_reader_accum) + + +// experimental +#define BLIP_READER_ADJ_( name, offset ) (name##_reader_buf += offset) + +blip_long const blip_reader_idx_factor = sizeof (Blip_Buffer::buf_t_); + +#define BLIP_READER_NEXT_IDX_( name, bass, idx ) {\ + name##_reader_accum -= name##_reader_accum >> (bass);\ + name##_reader_accum += name##_reader_buf [(idx)];\ +} + +#define BLIP_READER_NEXT_RAW_IDX_( name, bass, idx ) {\ + name##_reader_accum -= name##_reader_accum >> (bass);\ + name##_reader_accum +=\ + *(Blip_Buffer::buf_t_ const*) ((char const*) name##_reader_buf + (idx));\ +} + +// Compatibility with older version +const long blip_unscaled = 65535; +const int blip_low_quality = blip_med_quality; +const int blip_best_quality = blip_high_quality; + +// Deprecated; use BLIP_READER macros as follows: +// Blip_Reader r; r.begin( buf ); -> BLIP_READER_BEGIN( r, buf ); +// int bass = r.begin( buf ) -> BLIP_READER_BEGIN( r, buf ); int bass = BLIP_READER_BASS( buf ); +// r.read() -> BLIP_READER_READ( r ) +// r.read_raw() -> BLIP_READER_READ_RAW( r ) +// r.next( bass ) -> BLIP_READER_NEXT( r, bass ) +// r.next() -> BLIP_READER_NEXT( r, blip_reader_default_bass ) +// r.end( buf ) -> BLIP_READER_END( r, buf ) +class Blip_Reader { +public: + int begin( Blip_Buffer& ); + blip_long read() const { return accum >> (blip_sample_bits - 16); } + blip_long read_raw() const { return accum; } + void next( int bass_shift = 9 ) { accum += *buf++ - (accum >> bass_shift); } + void end( Blip_Buffer& b ) { b.reader_accum_ = accum; } +private: + const Blip_Buffer::buf_t_* buf; + blip_long accum; +}; + +#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ + defined (__x86_64__) || defined (__ia64__) || defined (__i386__) + #define BLIP_CLAMP_( in ) in < -0x8000 || 0x7FFF < in +#else + #define BLIP_CLAMP_( in ) (blip_sample_t) in != in +#endif + +// Clamp sample to blip_sample_t range +#define BLIP_CLAMP( sample, out )\ + { if ( BLIP_CLAMP_( (sample) ) ) (out) = ((sample) >> 24) ^ 0x7FFF; } + +struct blip_buffer_state_t +{ + blip_resampled_time_t offset_; + blip_long reader_accum_; + blip_long buf [blip_buffer_extra_]; +}; + +// End of public interface + +#ifndef assert + #include +#endif + +template +inline void Blip_Synth::offset_resampled( blip_resampled_time_t time, + int delta, Blip_Buffer* blip_buf ) const +{ + // If this assertion fails, it means that an attempt was made to add a delta + // at a negative time or past the end of the buffer. + assert( (blip_long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ ); + + delta *= impl.delta_factor; + blip_long* BLIP_RESTRICT buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY); + int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1)); + +#if BLIP_BUFFER_FAST + blip_long left = buf [0] + delta; + + // Kind of crappy, but doing shift after multiply results in overflow. + // Alternate way of delaying multiply by delta_factor results in worse + // sub-sample resolution. + blip_long right = (delta >> BLIP_PHASE_BITS) * phase; + left -= right; + right += buf [1]; + + buf [0] = left; + buf [1] = right; +#else + + int const fwd = (blip_widest_impulse_ - quality) / 2; + int const rev = fwd + quality - 2; + int const mid = quality / 2 - 1; + + imp_t const* BLIP_RESTRICT imp = impulses + blip_res - phase; + + #if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ + defined (__x86_64__) || defined (__ia64__) || defined (__i386__) + + // this straight forward version gave in better code on GCC for x86 + + #define ADD_IMP( out, in ) \ + buf [out] += (blip_long) imp [blip_res * (in)] * delta + + #define BLIP_FWD( i ) {\ + ADD_IMP( fwd + i, i );\ + ADD_IMP( fwd + 1 + i, i + 1 );\ + } + #define BLIP_REV( r ) {\ + ADD_IMP( rev - r, r + 1 );\ + ADD_IMP( rev + 1 - r, r );\ + } + + BLIP_FWD( 0 ) + if ( quality > 8 ) BLIP_FWD( 2 ) + if ( quality > 12 ) BLIP_FWD( 4 ) + { + ADD_IMP( fwd + mid - 1, mid - 1 ); + ADD_IMP( fwd + mid , mid ); + imp = impulses + phase; + } + if ( quality > 12 ) BLIP_REV( 6 ) + if ( quality > 8 ) BLIP_REV( 4 ) + BLIP_REV( 2 ) + + ADD_IMP( rev , 1 ); + ADD_IMP( rev + 1, 0 ); + + #undef ADD_IMP + + #else + + // for RISC processors, help compiler by reading ahead of writes + + #define BLIP_FWD( i ) {\ + blip_long t0 = i0 * delta + buf [fwd + i];\ + blip_long t1 = imp [blip_res * (i + 1)] * delta + buf [fwd + 1 + i];\ + i0 = imp [blip_res * (i + 2)];\ + buf [fwd + i] = t0;\ + buf [fwd + 1 + i] = t1;\ + } + #define BLIP_REV( r ) {\ + blip_long t0 = i0 * delta + buf [rev - r];\ + blip_long t1 = imp [blip_res * r] * delta + buf [rev + 1 - r];\ + i0 = imp [blip_res * (r - 1)];\ + buf [rev - r] = t0;\ + buf [rev + 1 - r] = t1;\ + } + + blip_long i0 = *imp; + BLIP_FWD( 0 ) + if ( quality > 8 ) BLIP_FWD( 2 ) + if ( quality > 12 ) BLIP_FWD( 4 ) + { + blip_long t0 = i0 * delta + buf [fwd + mid - 1]; + blip_long t1 = imp [blip_res * mid] * delta + buf [fwd + mid ]; + imp = impulses + phase; + i0 = imp [blip_res * mid]; + buf [fwd + mid - 1] = t0; + buf [fwd + mid ] = t1; + } + if ( quality > 12 ) BLIP_REV( 6 ) + if ( quality > 8 ) BLIP_REV( 4 ) + BLIP_REV( 2 ) + + blip_long t0 = i0 * delta + buf [rev ]; + blip_long t1 = *imp * delta + buf [rev + 1]; + buf [rev ] = t0; + buf [rev + 1] = t1; + #endif + +#endif +} + +#undef BLIP_FWD +#undef BLIP_REV + +template +#if BLIP_BUFFER_FAST + inline +#endif +void Blip_Synth::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const +{ + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); +} + +template +#if BLIP_BUFFER_FAST + inline +#endif +void Blip_Synth::update( blip_time_t t, int amp ) +{ + int delta = amp - impl.last_amp; + impl.last_amp = amp; + offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); +} + +inline blip_eq_t::blip_eq_t( double t ) : + treble( t ), rolloff_freq( 0 ), sample_rate( 44100 ), cutoff_freq( 0 ) { } +inline blip_eq_t::blip_eq_t( double t, long rf, long sr, long cf ) : + treble( t ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { } + +inline int Blip_Buffer::length() const { return length_; } +inline long Blip_Buffer::samples_avail() const { return (long) (offset_ >> BLIP_BUFFER_ACCURACY); } +inline long Blip_Buffer::sample_rate() const { return sample_rate_; } +inline int Blip_Buffer::output_latency() const { return blip_widest_impulse_ / 2; } +inline long Blip_Buffer::clock_rate() const { return clock_rate_; } +inline void Blip_Buffer::clock_rate( long cps ) { factor_ = clock_rate_factor( clock_rate_ = cps ); } + +inline int Blip_Reader::begin( Blip_Buffer& blip_buf ) +{ + buf = blip_buf.buffer_; + accum = blip_buf.reader_accum_; + return blip_buf.bass_shift_; +} + +inline void Blip_Buffer::remove_silence( long count ) +{ + // fails if you try to remove more samples than available + assert( count <= samples_avail() ); + offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; +} + +inline blip_ulong Blip_Buffer::unsettled() const +{ + return reader_accum_ >> (blip_sample_bits - 16); +} + +int const blip_max_length = 0; +int const blip_default_length = 250; // 1/4 second + +#endif diff --git a/src/dmg/gb_apu/Effects_Buffer.cpp b/src/dmg/gb_apu/Effects_Buffer.cpp new file mode 100644 index 00000000..deff3f0e --- /dev/null +++ b/src/dmg/gb_apu/Effects_Buffer.cpp @@ -0,0 +1,638 @@ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Effects_Buffer.h" + +#include + +/* Copyright (C) 2006-2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +int const fixed_shift = 12; +#define TO_FIXED( f ) fixed_t ((f) * ((fixed_t) 1 << fixed_shift)) +#define FROM_FIXED( f ) ((f) >> fixed_shift) + +int const max_read = 2560; // determines minimum delay + +Effects_Buffer::Effects_Buffer( int max_bufs, long echo_size_ ) : Multi_Buffer( stereo ) +{ + echo_size = max( max_read * (long) stereo, echo_size_ & ~1 ); + clock_rate_ = 0; + bass_freq_ = 90; + bufs = 0; + bufs_size = 0; + bufs_max = max( max_bufs, (int) extra_chans ); + no_echo = true; + no_effects = true; + + // defaults + config_.enabled = false; + config_.delay [0] = 120; + config_.delay [1] = 122; + config_.feedback = 0.2f; + config_.treble = 0.4f; + + static float const sep = 0.8f; + config_.side_chans [0].pan = -sep; + config_.side_chans [1].pan = +sep; + config_.side_chans [0].vol = 1.0f; + config_.side_chans [1].vol = 1.0f; + + memset( &s, 0, sizeof s ); + clear(); +} + +Effects_Buffer::~Effects_Buffer() +{ + delete_bufs(); +} + +// avoid using new [] +blargg_err_t Effects_Buffer::new_bufs( int size ) +{ + bufs = (buf_t*) malloc( size * sizeof *bufs ); + CHECK_ALLOC( bufs ); + for ( int i = 0; i < size; i++ ) + new (bufs + i) buf_t; + bufs_size = size; + return 0; +} + +void Effects_Buffer::delete_bufs() +{ + if ( bufs ) + { + for ( int i = bufs_size; --i >= 0; ) + bufs [i].~buf_t(); + free( bufs ); + bufs = 0; + } + bufs_size = 0; +} + +blargg_err_t Effects_Buffer::set_sample_rate( long rate, int msec ) +{ + // extra to allow farther past-the-end pointers + mixer.samples_read = 0; + RETURN_ERR( echo.resize( echo_size + stereo ) ); + return Multi_Buffer::set_sample_rate( rate, msec ); +} + +void Effects_Buffer::clock_rate( long rate ) +{ + clock_rate_ = rate; + for ( int i = bufs_size; --i >= 0; ) + bufs [i].clock_rate( clock_rate_ ); +} + +void Effects_Buffer::bass_freq( int freq ) +{ + bass_freq_ = freq; + for ( int i = bufs_size; --i >= 0; ) + bufs [i].bass_freq( bass_freq_ ); +} + +blargg_err_t Effects_Buffer::set_channel_count( int count, int const* types ) +{ + RETURN_ERR( Multi_Buffer::set_channel_count( count, types ) ); + + delete_bufs(); + + mixer.samples_read = 0; + + RETURN_ERR( chans.resize( count + extra_chans ) ); + + RETURN_ERR( new_bufs( min( bufs_max, count + extra_chans ) ) ); + + for ( int i = bufs_size; --i >= 0; ) + RETURN_ERR( bufs [i].set_sample_rate( sample_rate(), length() ) ); + + for ( int i = chans.size(); --i >= 0; ) + { + chan_t& ch = chans [i]; + ch.cfg.vol = 1.0f; + ch.cfg.pan = 0.0f; + ch.cfg.surround = false; + ch.cfg.echo = false; + } + // side channels with echo + chans [2].cfg.echo = true; + chans [3].cfg.echo = true; + + clock_rate( clock_rate_ ); + bass_freq( bass_freq_ ); + apply_config(); + clear(); + + return 0; +} + +void Effects_Buffer::clear_echo() +{ + if ( echo.size() ) + memset( echo.begin(), 0, echo.size() * sizeof echo [0] ); +} + +void Effects_Buffer::clear() +{ + echo_pos = 0; + s.low_pass [0] = 0; + s.low_pass [1] = 0; + mixer.samples_read = 0; + + for ( int i = bufs_size; --i >= 0; ) + bufs [i].clear(); + clear_echo(); +} + +Effects_Buffer::channel_t Effects_Buffer::channel( int i ) +{ + i += extra_chans; + require( extra_chans <= i && i < (int) chans.size() ); + return chans [i].channel; +} + + +// Configuration + +// 3 wave positions with/without surround, 2 multi (one with same config as wave) +int const simple_bufs = 3 * 2 + 2 - 1; + +Simple_Effects_Buffer::Simple_Effects_Buffer() : + Effects_Buffer( extra_chans + simple_bufs, 18 * 1024L ) +{ + config_.echo = 0.20f; + config_.stereo = 0.20f; + config_.surround = true; + config_.enabled = false; +} + +void Simple_Effects_Buffer::apply_config() +{ + Effects_Buffer::config_t& c = Effects_Buffer::config(); + + c.enabled = config_.enabled; + if ( c.enabled ) + { + c.delay [0] = 120; + c.delay [1] = 122; + c.feedback = config_.echo * 0.7f; + c.treble = 0.6f - 0.3f * config_.echo; + + float sep = config_.stereo + 0.80f; + if ( sep > 1.0f ) + sep = 1.0f; + + c.side_chans [0].pan = -sep; + c.side_chans [1].pan = +sep; + + for ( int i = channel_count(); --i >= 0; ) + { + chan_config_t& ch = Effects_Buffer::chan_config( i ); + + ch.pan = 0.0f; + ch.surround = config_.surround; + ch.echo = false; + + int const type = (channel_types() ? channel_types() [i] : 0); + if ( !(type & noise_type) ) + { + int index = (type & type_index_mask) % 6 - 3; + if ( index < 0 ) + { + index += 3; + ch.surround = false; + ch.echo = true; + } + if ( index >= 1 ) + { + ch.pan = config_.stereo; + if ( index == 1 ) + ch.pan = -ch.pan; + } + } + else if ( type & 1 ) + { + ch.surround = false; + } + } + } + + Effects_Buffer::apply_config(); +} + +int Effects_Buffer::min_delay() const +{ + require( sample_rate() ); + return max_read * 1000L / sample_rate(); +} + +int Effects_Buffer::max_delay() const +{ + require( sample_rate() ); + return (echo_size / stereo - max_read) * 1000L / sample_rate(); +} + +void Effects_Buffer::apply_config() +{ + int i; + + if ( !bufs_size ) + return; + + s.treble = TO_FIXED( config_.treble ); + + bool echo_dirty = false; + + fixed_t old_feedback = s.feedback; + s.feedback = TO_FIXED( config_.feedback ); + if ( !old_feedback && s.feedback ) + echo_dirty = true; + + // delays + for ( i = stereo; --i >= 0; ) + { + long delay = config_.delay [i] * sample_rate() / 1000 * stereo; + delay = max( delay, long (max_read * stereo) ); + delay = min( delay, long (echo_size - max_read * stereo) ); + if ( s.delay [i] != delay ) + { + s.delay [i] = delay; + echo_dirty = true; + } + } + + // side channels + for ( i = 2; --i >= 0; ) + { + chans [i+2].cfg.vol = chans [i].cfg.vol = config_.side_chans [i].vol * 0.5f; + chans [i+2].cfg.pan = chans [i].cfg.pan = config_.side_chans [i].pan; + } + + // convert volumes + for ( i = chans.size(); --i >= 0; ) + { + chan_t& ch = chans [i]; + ch.vol [0] = TO_FIXED( ch.cfg.vol - ch.cfg.vol * ch.cfg.pan ); + ch.vol [1] = TO_FIXED( ch.cfg.vol + ch.cfg.vol * ch.cfg.pan ); + if ( ch.cfg.surround ) + ch.vol [0] = -ch.vol [0]; + } + + assign_buffers(); + + // set side channels + for ( i = chans.size(); --i >= 0; ) + { + chan_t& ch = chans [i]; + ch.channel.left = chans [ch.cfg.echo*2 ].channel.center; + ch.channel.right = chans [ch.cfg.echo*2+1].channel.center; + } + + bool old_echo = !no_echo && !no_effects; + + // determine whether effects and echo are needed at all + no_effects = true; + no_echo = true; + for ( i = chans.size(); --i >= extra_chans; ) + { + chan_t& ch = chans [i]; + if ( ch.cfg.echo && s.feedback ) + no_echo = false; + + if ( ch.vol [0] != TO_FIXED( 1 ) || ch.vol [1] != TO_FIXED( 1 ) ) + no_effects = false; + } + if ( !no_echo ) + no_effects = false; + + if ( chans [0].vol [0] != TO_FIXED( 1 ) || + chans [0].vol [1] != TO_FIXED( 0 ) || + chans [1].vol [0] != TO_FIXED( 0 ) || + chans [1].vol [1] != TO_FIXED( 1 ) ) + no_effects = false; + + if ( !config_.enabled ) + no_effects = true; + + if ( no_effects ) + { + for ( i = chans.size(); --i >= 0; ) + { + chan_t& ch = chans [i]; + ch.channel.center = &bufs [2]; + ch.channel.left = &bufs [0]; + ch.channel.right = &bufs [1]; + } + } + + mixer.bufs [0] = &bufs [0]; + mixer.bufs [1] = &bufs [1]; + mixer.bufs [2] = &bufs [2]; + + if ( echo_dirty || (!old_echo && (!no_echo && !no_effects)) ) + clear_echo(); + + channels_changed(); +} + +void Effects_Buffer::assign_buffers() +{ + // assign channels to buffers + int buf_count = 0; + for ( int i = 0; i < (int) chans.size(); i++ ) + { + // put second two side channels at end to give priority to main channels + // in case closest matching is necessary + int x = i; + if ( i > 1 ) + x += 2; + if ( x >= (int) chans.size() ) + x -= (chans.size() - 2); + chan_t& ch = chans [x]; + + int b = 0; + for ( ; b < buf_count; b++ ) + { + if ( ch.vol [0] == bufs [b].vol [0] && + ch.vol [1] == bufs [b].vol [1] && + (ch.cfg.echo == bufs [b].echo || !s.feedback) ) + break; + } + + if ( b >= buf_count ) + { + if ( buf_count < bufs_max ) + { + bufs [b].vol [0] = ch.vol [0]; + bufs [b].vol [1] = ch.vol [1]; + bufs [b].echo = ch.cfg.echo; + buf_count++; + } + else + { + // TODO: this is a mess, needs refinement + dprintf( "Effects_Buffer ran out of buffers; using closest match\n" ); + b = 0; + fixed_t best_dist = TO_FIXED( 8 ); + for ( int h = buf_count; --h >= 0; ) + { + #define CALC_LEVELS( vols, sum, diff, surround ) \ + fixed_t sum, diff;\ + bool surround = false;\ + {\ + fixed_t vol_0 = vols [0];\ + if ( vol_0 < 0 ) vol_0 = -vol_0, surround = true;\ + fixed_t vol_1 = vols [1];\ + if ( vol_1 < 0 ) vol_1 = -vol_1, surround = true;\ + sum = vol_0 + vol_1;\ + diff = vol_0 - vol_1;\ + } + CALC_LEVELS( ch.vol, ch_sum, ch_diff, ch_surround ); + CALC_LEVELS( bufs [h].vol, buf_sum, buf_diff, buf_surround ); + + fixed_t dist = abs( ch_sum - buf_sum ) + abs( ch_diff - buf_diff ); + + if ( ch_surround != buf_surround ) + dist += TO_FIXED( 1 ) / 2; + + if ( s.feedback && ch.cfg.echo != bufs [h].echo ) + dist += TO_FIXED( 1 ) / 2; + + if ( best_dist > dist ) + { + best_dist = dist; + b = h; + } + } + } + } + + //dprintf( "ch %d->buf %d\n", x, b ); + ch.channel.center = &bufs [b]; + } +} + + +// Mixing + +void Effects_Buffer::end_frame( blip_time_t time ) +{ + for ( int i = bufs_size; --i >= 0; ) + bufs [i].end_frame( time ); +} + +long Effects_Buffer::read_samples( blip_sample_t* out, long out_size ) +{ + out_size = min( out_size, samples_avail() ); + + int pair_count = int (out_size >> 1); + require( pair_count * stereo == out_size ); // must read an even number of samples + if ( pair_count ) + { + if ( no_effects ) + { + mixer.read_pairs( out, pair_count ); + } + else + { + int pairs_remain = pair_count; + do + { + // mix at most max_read pairs at a time + int count = max_read; + if ( count > pairs_remain ) + count = pairs_remain; + + if ( no_echo ) + { + // optimization: clear echo here to keep mix_effects() a leaf function + echo_pos = 0; + memset( echo.begin(), 0, count * stereo * sizeof echo [0] ); + } + mix_effects( out, count ); + + blargg_long new_echo_pos = echo_pos + count * stereo; + if ( new_echo_pos >= echo_size ) + new_echo_pos -= echo_size; + echo_pos = new_echo_pos; + assert( echo_pos < echo_size ); + + out += count * stereo; + mixer.samples_read += count; + pairs_remain -= count; + } + while ( pairs_remain ); + } + + if ( samples_avail() <= 0 || immediate_removal() ) + { + for ( int i = bufs_size; --i >= 0; ) + { + buf_t& b = bufs [i]; + // TODO: might miss non-silence settling since it checks END of last read + if ( b.non_silent() ) + b.remove_samples( mixer.samples_read ); + else + b.remove_silence( mixer.samples_read ); + } + mixer.samples_read = 0; + } + } + return out_size; +} + +void Effects_Buffer::mix_effects( blip_sample_t* out_, int pair_count ) +{ + typedef fixed_t stereo_fixed_t [stereo]; + + // add channels with echo, do echo, add channels without echo, then convert to 16-bit and output + int echo_phase = 1; + do + { + // mix any modified buffers + { + buf_t* buf = bufs; + int bufs_remain = bufs_size; + do + { + if ( buf->non_silent() && ( buf->echo == (bool)echo_phase ) ) + { + stereo_fixed_t* BLIP_RESTRICT out = (stereo_fixed_t*) &echo [echo_pos]; + int const bass = BLIP_READER_BASS( *buf ); + BLIP_READER_BEGIN( in, *buf ); + BLIP_READER_ADJ_( in, mixer.samples_read ); + fixed_t const vol_0 = buf->vol [0]; + fixed_t const vol_1 = buf->vol [1]; + + int count = unsigned (echo_size - echo_pos) / stereo; + int remain = pair_count; + if ( count > remain ) + count = remain; + do + { + remain -= count; + BLIP_READER_ADJ_( in, count ); + + out += count; + int offset = -count; + do + { + fixed_t s = BLIP_READER_READ( in ); + BLIP_READER_NEXT_IDX_( in, bass, offset ); + + out [offset] [0] += s * vol_0; + out [offset] [1] += s * vol_1; + } + while ( ++offset ); + + out = (stereo_fixed_t*) echo.begin(); + count = remain; + } + while ( remain ); + + BLIP_READER_END( in, *buf ); + } + buf++; + } + while ( --bufs_remain ); + } + + // add echo + if ( echo_phase && !no_echo ) + { + fixed_t const feedback = s.feedback; + fixed_t const treble = s.treble; + + int i = 1; + do + { + fixed_t low_pass = s.low_pass [i]; + + fixed_t* echo_end = &echo [echo_size + i]; + fixed_t const* BLIP_RESTRICT in_pos = &echo [echo_pos + i]; + blargg_long out_offset = echo_pos + i + s.delay [i]; + if ( out_offset >= echo_size ) + out_offset -= echo_size; + assert( out_offset < echo_size ); + fixed_t* BLIP_RESTRICT out_pos = &echo [out_offset]; + + // break into up to three chunks to avoid having to handle wrap-around + // in middle of core loop + int remain = pair_count; + do + { + fixed_t const* pos = in_pos; + if ( pos < out_pos ) + pos = out_pos; + int count = blargg_ulong ((char*) echo_end - (char const*) pos) / + unsigned (stereo * sizeof (fixed_t)); + if ( count > remain ) + count = remain; + remain -= count; + + in_pos += count * stereo; + out_pos += count * stereo; + int offset = -count; + do + { + low_pass += FROM_FIXED( in_pos [offset * stereo] - low_pass ) * treble; + out_pos [offset * stereo] = FROM_FIXED( low_pass ) * feedback; + } + while ( ++offset ); + + if ( in_pos >= echo_end ) in_pos -= echo_size; + if ( out_pos >= echo_end ) out_pos -= echo_size; + } + while ( remain ); + + s.low_pass [i] = low_pass; + } + while ( --i >= 0 ); + } + } + while ( --echo_phase >= 0 ); + + // clamp to 16 bits + { + stereo_fixed_t const* BLIP_RESTRICT in = (stereo_fixed_t*) &echo [echo_pos]; + typedef blip_sample_t stereo_blip_sample_t [stereo]; + stereo_blip_sample_t* BLIP_RESTRICT out = (stereo_blip_sample_t*) out_; + int count = unsigned (echo_size - echo_pos) / (unsigned) stereo; + int remain = pair_count; + if ( count > remain ) + count = remain; + do + { + remain -= count; + in += count; + out += count; + int offset = -count; + do + { + fixed_t in_0 = FROM_FIXED( in [offset] [0] ); + fixed_t in_1 = FROM_FIXED( in [offset] [1] ); + + BLIP_CLAMP( in_0, in_0 ); + out [offset] [0] = (blip_sample_t) in_0; + + BLIP_CLAMP( in_1, in_1 ); + out [offset] [1] = (blip_sample_t) in_1; + } + while ( ++offset ); + + in = (stereo_fixed_t*) echo.begin(); + count = remain; + } + while ( remain ); + } +} diff --git a/src/dmg/gb_apu/Effects_Buffer.h b/src/dmg/gb_apu/Effects_Buffer.h new file mode 100644 index 00000000..5d800bb3 --- /dev/null +++ b/src/dmg/gb_apu/Effects_Buffer.h @@ -0,0 +1,143 @@ +// Multi-channel effects buffer with echo and individual panning for each channel + +// Game_Music_Emu $vers +#ifndef EFFECTS_BUFFER_H +#define EFFECTS_BUFFER_H + +#include "Multi_Buffer.h" + +// See Simple_Effects_Buffer (below) for a simpler interface + +class Effects_Buffer : public Multi_Buffer { +public: + // To reduce memory usage, fewer buffers can be used (with a best-fit + // approach if there are too few), and maximum echo delay can be reduced + Effects_Buffer( int max_bufs = 32, long echo_size = 24 * 1024L ); + + struct pan_vol_t + { + float vol; // 0.0 = silent, 0.5 = half volume, 1.0 = normal + float pan; // -1.0 = left, 0.0 = center, +1.0 = right + }; + + // Global configuration + struct config_t + { + bool enabled; // false = disable all effects + + // Current sound is echoed at adjustable left/right delay, + // with reduced treble and volume (feedback). + float treble; // 1.0 = full treble, 0.1 = very little, 0.0 = silent + int delay [2]; // left, right delays (msec) + float feedback; // 0.0 = no echo, 0.5 = each echo half previous, 1.0 = cacophony + pan_vol_t side_chans [2]; // left and right side channel volume and pan + }; + config_t& config() { return config_; } + + // Limits of delay (msec) + int min_delay() const; + int max_delay() const; + + // Per-channel configuration. Two or more channels with matching parameters are + // optimized to internally use the same buffer. + struct chan_config_t : pan_vol_t + { + // (inherited from pan_vol_t) + //float vol; // these only affect center channel + //float pan; + bool surround; // if true, negates left volume to put sound in back + bool echo; // false = channel doesn't have any echo + }; + chan_config_t& chan_config( int i ) { return chans [i + extra_chans].cfg; } + + // Apply any changes made to config() and chan_config() + virtual void apply_config(); + +public: + ~Effects_Buffer(); + blargg_err_t set_sample_rate( long samples_per_sec, int msec = blip_default_length ); + blargg_err_t set_channel_count( int, int const* = 0 ); + void clock_rate( long ); + void bass_freq( int ); + void clear(); + channel_t channel( int ); + void end_frame( blip_time_t ); + long read_samples( blip_sample_t*, long ); + long samples_avail() const { return (bufs [0].samples_avail() - mixer.samples_read) * 2; } + enum { stereo = 2 }; + typedef blargg_long fixed_t; +protected: + enum { extra_chans = stereo * stereo }; +private: + config_t config_; + long clock_rate_; + int bass_freq_; + + blargg_long echo_size; + + struct chan_t + { + fixed_t vol [stereo]; + chan_config_t cfg; + channel_t channel; + }; + blargg_vector chans; + + struct buf_t : Tracked_Blip_Buffer + { + fixed_t vol [stereo]; + bool echo; + + void* operator new ( size_t, void* p ) { return p; } + void operator delete ( void* ) { } + + ~buf_t() { } + }; + buf_t* bufs; + int bufs_size; + int bufs_max; // bufs_size <= bufs_max, to limit memory usage + Stereo_Mixer mixer; + + struct { + long delay [stereo]; + fixed_t treble; + fixed_t feedback; + fixed_t low_pass [stereo]; + } s; + + blargg_vector echo; + blargg_long echo_pos; + + bool no_effects; + bool no_echo; + + void assign_buffers(); + void clear_echo(); + void mix_effects( blip_sample_t* out, int pair_count ); + blargg_err_t new_bufs( int size ); + void delete_bufs(); +}; + +// Simpler interface and lower memory usage +class Simple_Effects_Buffer : public Effects_Buffer { +public: + struct config_t + { + bool enabled; // false = disable all effects + float echo; // 0.0 = none, 1.0 = lots + float stereo; // 0.0 = channels in center, 1.0 = channels on left/right + bool surround; // true = put some channels in back + }; + config_t& config() { return config_; } + + // Apply any changes made to config() + void apply_config(); + +public: + Simple_Effects_Buffer(); +private: + config_t config_; + void chan_config(); // hide +}; + +#endif diff --git a/src/dmg/gb_apu/Gb_Apu.cpp b/src/dmg/gb_apu/Gb_Apu.cpp new file mode 100644 index 00000000..f86b29fb --- /dev/null +++ b/src/dmg/gb_apu/Gb_Apu.cpp @@ -0,0 +1,394 @@ +// Gb_Snd_Emu 0.2.0. http://www.slack.net/~ant/ + +#include "Gb_Apu.h" + +/* Copyright (C) 2003-2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +unsigned const vol_reg = 0xFF24; +unsigned const stereo_reg = 0xFF25; +unsigned const status_reg = 0xFF26; +unsigned const wave_ram = 0xFF30; + +int const power_mask = 0x80; + +void Gb_Apu::treble_eq( blip_eq_t const& eq ) +{ + good_synth.treble_eq( eq ); + med_synth .treble_eq( eq ); +} + +inline int Gb_Apu::calc_output( int osc ) const +{ + int bits = regs [stereo_reg - start_addr] >> osc; + return (bits >> 3 & 2) | (bits & 1); +} + +void Gb_Apu::set_output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right, int osc ) +{ + // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL) + require( !center || (center && !left && !right) || (center && left && right) ); + require( (unsigned) osc <= osc_count ); // fails if you pass invalid osc index + + if ( !center || !left || !right ) + { + left = center; + right = center; + } + + int i = (unsigned) osc % osc_count; + do + { + Gb_Osc& o = *oscs [i]; + o.outputs [1] = right; + o.outputs [2] = left; + o.outputs [3] = center; + o.output = o.outputs [calc_output( i )]; + } + while ( ++i < osc ); +} + +void Gb_Apu::synth_volume( int iv ) +{ + double v = volume_ * 0.60 / osc_count / 15 /*steps*/ / 8 /*master vol range*/ * iv; + good_synth.volume( v ); + med_synth .volume( v ); +} + +void Gb_Apu::apply_volume() +{ + // TODO: Doesn't handle differing left and right volumes (panning). + // Not worth the complexity. + int data = regs [vol_reg - start_addr]; + int left = data >> 4 & 7; + int right = data & 7; + //if ( data & 0x88 ) dprintf( "Vin: %02X\n", data & 0x88 ); + //if ( left != right ) dprintf( "l: %d r: %d\n", left, right ); + synth_volume( max( left, right ) + 1 ); +} + +void Gb_Apu::volume( double v ) +{ + if ( volume_ != v ) + { + volume_ = v; + apply_volume(); + } +} + +void Gb_Apu::reset_regs() +{ + for ( int i = 0; i < 0x20; i++ ) + regs [i] = 0; + + square1.reset(); + square2.reset(); + wave .reset(); + noise .reset(); + + apply_volume(); +} + +void Gb_Apu::reset_lengths() +{ + square1.length_ctr = 64; + square2.length_ctr = 64; + wave .length_ctr = 256; + noise .length_ctr = 64; +} + +void Gb_Apu::reduce_clicks( bool reduce ) +{ + reduce_clicks_ = reduce; + + // Click reduction makes DAC off generate same output as volume 0 + int dac_off_amp = 0; + if ( reduce && wave.mode != mode_agb ) // AGB already eliminates clicks + dac_off_amp = -Gb_Osc::dac_bias; + + for ( int i = 0; i < osc_count; i++ ) + oscs [i]->dac_off_amp = dac_off_amp; + + // AGB always eliminates clicks on wave channel using same method + if ( wave.mode == mode_agb ) + wave.dac_off_amp = -Gb_Osc::dac_bias; +} + +void Gb_Apu::reset( mode_t mode, bool agb_wave ) +{ + // Hardware mode + if ( agb_wave ) + mode = mode_agb; // using AGB wave features implies AGB hardware + wave.agb_mask = agb_wave ? 0xFF : 0; + for ( int i = 0; i < osc_count; i++ ) + oscs [i]->mode = mode; + reduce_clicks( reduce_clicks_ ); + + // Reset state + frame_time = 0; + last_time = 0; + frame_phase = 0; + + reset_regs(); + reset_lengths(); + + // Load initial wave RAM + static byte const initial_wave [2] [16] = { + {0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C,0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA}, + {0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF}, + }; + for ( int b = 2; --b >= 0; ) + { + // Init both banks (does nothing if not in AGB mode) + // TODO: verify that this works + write_register( 0, 0xFF1A, b * 0x40 ); + for ( unsigned i = 0; i < sizeof initial_wave [0]; i++ ) + write_register( 0, i + wave_ram, initial_wave [(mode != mode_dmg)] [i] ); + } +} + +void Gb_Apu::set_tempo( double t ) +{ + frame_period = 4194304 / 512; // 512 Hz + if ( t != 1.0 ) + frame_period = blip_time_t (frame_period / t); +} + +Gb_Apu::Gb_Apu() +{ + wave.wave_ram = ®s [wave_ram - start_addr]; + + oscs [0] = &square1; + oscs [1] = &square2; + oscs [2] = &wave; + oscs [3] = &noise; + + for ( int i = osc_count; --i >= 0; ) + { + Gb_Osc& o = *oscs [i]; + o.regs = ®s [i * 5]; + o.output = 0; + o.outputs [0] = 0; + o.outputs [1] = 0; + o.outputs [2] = 0; + o.outputs [3] = 0; + o.good_synth = &good_synth; + o.med_synth = &med_synth; + } + + reduce_clicks_ = false; + set_tempo( 1.0 ); + volume_ = 1.0; + reset(); +} + +void Gb_Apu::run_until_( blip_time_t end_time ) +{ + while ( true ) + { + // run oscillators + blip_time_t time = end_time; + if ( time > frame_time ) + time = frame_time; + + square1.run( last_time, time ); + square2.run( last_time, time ); + wave .run( last_time, time ); + noise .run( last_time, time ); + last_time = time; + + if ( time == end_time ) + break; + + // run frame sequencer + frame_time += frame_period * Gb_Osc::clk_mul; + switch ( frame_phase++ ) + { + case 2: + case 6: + // 128 Hz + square1.clock_sweep(); + case 0: + case 4: + // 256 Hz + square1.clock_length(); + square2.clock_length(); + wave .clock_length(); + noise .clock_length(); + break; + + case 7: + // 64 Hz + frame_phase = 0; + square1.clock_envelope(); + square2.clock_envelope(); + noise .clock_envelope(); + } + } +} + +inline void Gb_Apu::run_until( blip_time_t time ) +{ + require( time >= last_time ); // end_time must not be before previous time + if ( time > last_time ) + run_until_( time ); +} + +void Gb_Apu::end_frame( blip_time_t end_time ) +{ + if ( end_time > last_time ) + run_until( end_time ); + + frame_time -= end_time; + assert( frame_time >= 0 ); + + last_time -= end_time; + assert( last_time >= 0 ); +} + +void Gb_Apu::silence_osc( Gb_Osc& o ) +{ + int delta = -o.last_amp; + if ( delta ) + { + o.last_amp = 0; + if ( o.output ) + { + o.output->set_modified(); + med_synth.offset( last_time, delta, o.output ); + } + } +} + +void Gb_Apu::apply_stereo() +{ + for ( int i = osc_count; --i >= 0; ) + { + Gb_Osc& o = *oscs [i]; + Blip_Buffer* out = o.outputs [calc_output( i )]; + if ( o.output != out ) + { + silence_osc( o ); + o.output = out; + } + } +} + +void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data ) +{ + require( (unsigned) data < 0x100 ); + + int reg = addr - start_addr; + if ( (unsigned) reg >= register_count ) + { + require( false ); + return; + } + + if ( addr < status_reg && !(regs [status_reg - start_addr] & power_mask) ) + { + // Power is off + + // length counters can only be written in DMG mode + if ( wave.mode != mode_dmg || (reg != 1 && reg != 5+1 && reg != 10+1 && reg != 15+1) ) + return; + + if ( reg < 10 ) + data &= 0x3F; // clear square duty + } + + run_until( time ); + + if ( addr >= wave_ram ) + { + wave.write( addr, data ); + } + else + { + int old_data = regs [reg]; + regs [reg] = data; + + if ( addr < vol_reg ) + { + // Oscillator + write_osc( reg / 5, reg, old_data, data ); + } + else if ( addr == vol_reg && data != old_data ) + { + // Master volume + for ( int i = osc_count; --i >= 0; ) + silence_osc( *oscs [i] ); + + apply_volume(); + } + else if ( addr == stereo_reg ) + { + // Stereo panning + apply_stereo(); + } + else if ( addr == status_reg && (data ^ old_data) & power_mask ) + { + // Power control + frame_phase = 0; + for ( int i = osc_count; --i >= 0; ) + silence_osc( *oscs [i] ); + + reset_regs(); + if ( wave.mode != mode_dmg ) + reset_lengths(); + + regs [status_reg - start_addr] = data; + } + } +} + +int Gb_Apu::read_register( blip_time_t time, unsigned addr ) +{ + run_until( time ); + + int reg = addr - start_addr; + if ( (unsigned) reg >= register_count ) + { + require( false ); + return 0; + } + + if ( addr >= wave_ram ) + return wave.read( addr ); + + // Value read back has some bits always set + static byte const masks [] = { + 0x80,0x3F,0x00,0xFF,0xBF, + 0xFF,0x3F,0x00,0xFF,0xBF, + 0x7F,0xFF,0x9F,0xFF,0xBF, + 0xFF,0xFF,0x00,0x00,0xBF, + 0x00,0x00,0x70, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + }; + int mask = masks [reg]; + if ( wave.agb_mask && (reg == 10 || reg == 12) ) + mask = 0x1F; // extra implemented bits in wave regs on AGB + int data = regs [reg] | mask; + + // Status register + if ( addr == status_reg ) + { + data &= 0xF0; + data |= (int) square1.enabled << 0; + data |= (int) square2.enabled << 1; + data |= (int) wave .enabled << 2; + data |= (int) noise .enabled << 3; + } + + return data; +} diff --git a/src/dmg/gb_apu/Gb_Apu.h b/src/dmg/gb_apu/Gb_Apu.h new file mode 100644 index 00000000..4db1c3b5 --- /dev/null +++ b/src/dmg/gb_apu/Gb_Apu.h @@ -0,0 +1,182 @@ +// Nintendo Game Boy sound hardware emulator with save state support + +// Gb_Snd_Emu 0.2.0 +#ifndef GB_APU_H +#define GB_APU_H + +#include "Gb_Oscs.h" + +struct gb_apu_state_t; + +class Gb_Apu { +public: +// Basics + + // Clock rate that sound hardware runs at. + enum { clock_rate = 4194304 * GB_APU_OVERCLOCK }; + + // Sets buffer(s) to generate sound into. If left and right are NULL, output is mono. + // If all are NULL, no output is generated but other emulation still runs. + // If chan is specified, only that channel's output is changed, otherwise all are. + enum { osc_count = 4 }; // 0: Square 1, 1: Square 2, 2: Wave, 3: Noise + void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL, + int chan = osc_count ); + + // Resets hardware to initial power on state BEFORE boot ROM runs. Mode selects + // sound hardware. Additional AGB wave features are enabled separately. + enum mode_t { + mode_dmg, // Game Boy monochrome + mode_cgb, // Game Boy Color + mode_agb // Game Boy Advance + }; + void reset( mode_t mode = mode_cgb, bool agb_wave = false ); + + // Reads and writes must be within the start_addr to end_addr range, inclusive. + // Addresses outside this range are not mapped to the sound hardware. + enum { start_addr = 0xFF10 }; + enum { end_addr = 0xFF3F }; + enum { register_count = end_addr - start_addr + 1 }; + + // Times are specified as the number of clocks since the beginning of the + // current time frame. + + // Emulates CPU write of data to addr at specified time. + void write_register( blip_time_t time, unsigned addr, int data ); + + // Emulates CPU read from addr at specified time. + int read_register( blip_time_t time, unsigned addr ); + + // Emulates sound hardware up to specified time, ends current time frame, then + // starts a new frame at time 0. + void end_frame( blip_time_t frame_length ); + +// Sound adjustments + + // Sets overall volume, where 1.0 is normal. + void volume( double ); + + // If true, reduces clicking by disabling DAC biasing. Note that this reduces + // emulation accuracy, since the clicks are authentic. + void reduce_clicks( bool reduce = true ); + + // Sets treble equalization. + void treble_eq( blip_eq_t const& ); + + // Treble and bass values for various hardware. + enum { + speaker_treble = -47, // speaker on system + speaker_bass = 2000, + dmg_treble = 0, // headphones on each system + dmg_bass = 30, + cgb_treble = 0, + cgb_bass = 300, // CGB has much less bass + agb_treble = 0, + agb_bass = 30 + }; + + // Sets frame sequencer rate, where 1.0 is normal. Meant for adjusting the + // tempo in a game music player. + void set_tempo( double ); + +// Save states + + // Saves full emulation state to state_out. Data format is portable and + // includes some extra space to avoid expansion in case more state needs + // to be stored in the future. + void save_state( gb_apu_state_t* state_out ); + + // Loads state. You should call reset() BEFORE this. + blargg_err_t load_state( gb_apu_state_t const& in ); + +public: + Gb_Apu(); + + // Use set_output() in place of these + BLARGG_DEPRECATED void output ( Blip_Buffer* c ) { set_output( c, c, c ); } + BLARGG_DEPRECATED void output ( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( c, l, r ); } + BLARGG_DEPRECATED void osc_output( int i, Blip_Buffer* c ) { set_output( c, c, c, i ); } + BLARGG_DEPRECATED void osc_output( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( c, l, r, i ); } + +private: + // noncopyable + Gb_Apu( const Gb_Apu& ); + Gb_Apu& operator = ( const Gb_Apu& ); + + Gb_Osc* oscs [osc_count]; + blip_time_t last_time; // time sound emulator has been run to + blip_time_t frame_period; // clocks between each frame sequencer step + double volume_; + bool reduce_clicks_; + + Gb_Sweep_Square square1; + Gb_Square square2; + Gb_Wave wave; + Gb_Noise noise; + blip_time_t frame_time; // time of next frame sequencer action + int frame_phase; // phase of next frame sequencer step + enum { regs_size = register_count + 0x10 }; + BOOST::uint8_t regs [regs_size];// last values written to registers + + // large objects after everything else + Gb_Osc::Good_Synth good_synth; + Gb_Osc::Med_Synth med_synth; + + void reset_lengths(); + void reset_regs(); + int calc_output( int osc ) const; + void apply_stereo(); + void apply_volume(); + void synth_volume( int ); + void run_until_( blip_time_t ); + void run_until( blip_time_t ); + void silence_osc( Gb_Osc& ); + void write_osc( int index, int reg, int old_data, int data ); + const char* save_load( gb_apu_state_t*, bool save ); + void save_load2( gb_apu_state_t*, bool save ); + friend class Gb_Apu_Tester; +}; + +// Format of save state. Should be stable across versions of the library, +// with earlier versions properly opening later save states. Includes some +// room for expansion so the state size shouldn't increase. +struct gb_apu_state_t +{ +#if GB_APU_CUSTOM_STATE + // Values stored as plain int so your code can read/write them easily. + // Structure can NOT be written to disk, since format is not portable. + typedef int val_t; +#else + // Values written in portable little-endian format, allowing structure + // to be written directly to disk. + typedef unsigned char val_t [4]; +#endif + + enum { format0 = 0x50414247 }; + + val_t format; // format of all following data + val_t version; // later versions just add fields to end + + unsigned char regs [0x40]; + val_t frame_time; + val_t frame_phase; + + val_t sweep_freq; + val_t sweep_delay; + val_t sweep_enabled; + val_t sweep_neg; + val_t noise_divider; + val_t wave_buf; + + val_t delay [4]; + val_t length_ctr [4]; + val_t phase [4]; + val_t enabled [4]; + + val_t env_delay [3]; + val_t env_volume [3]; + val_t env_enabled [3]; + + val_t unused [13]; // for future expansion +}; + +#endif diff --git a/src/dmg/gb_apu/Gb_Apu_State.cpp b/src/dmg/gb_apu/Gb_Apu_State.cpp new file mode 100644 index 00000000..7dedcc37 --- /dev/null +++ b/src/dmg/gb_apu/Gb_Apu_State.cpp @@ -0,0 +1,118 @@ +// Gb_Snd_Emu $vers. http://www.slack.net/~ant/ + +#include "Gb_Apu.h" + +#include + +/* Copyright (C) 2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#if GB_APU_CUSTOM_STATE + #define REFLECT( x, y ) (save ? (io->y) = (x) : (x) = (io->y) ) +#else + #define REFLECT( x, y ) (save ? set_val( io->y, x ) : (void) ((x) = get_val( io->y ))) + + static blargg_ulong get_val( byte const* p ) + { + return p [3] * 0x1000000 + p [2] * 0x10000 + p [1] * 0x100 + p [0]; + } + + static void set_val( byte* p, blargg_ulong n ) + { + p [0] = (byte) (n ); + p [1] = (byte) (n >> 8); + p [2] = (byte) (n >> 16); + p [3] = (byte) (n >> 24); + } +#endif + +inline const char* Gb_Apu::save_load( gb_apu_state_t* io, bool save ) +{ + #if !GB_APU_CUSTOM_STATE + assert( sizeof (gb_apu_state_t) == 256 ); + #endif + + int format = io->format0; + REFLECT( format, format ); + if ( format != io->format0 ) + return "Unsupported sound save state format"; + + int version = 0; + REFLECT( version, version ); + + // Registers and wave RAM + assert( regs_size == sizeof io->regs ); + if ( save ) + memcpy( io->regs, regs, sizeof io->regs ); + else + memcpy( regs, io->regs, sizeof regs ); + + // Frame sequencer + REFLECT( frame_time, frame_time ); + REFLECT( frame_phase, frame_phase ); + + REFLECT( square1.sweep_freq, sweep_freq ); + REFLECT( square1.sweep_delay, sweep_delay ); + REFLECT( square1.sweep_enabled, sweep_enabled ); + REFLECT( square1.sweep_neg, sweep_neg ); + + REFLECT( noise.divider, noise_divider ); + REFLECT( wave.sample_buf, wave_buf ); + + return 0; +} + +// second function to avoid inline limits of some compilers +inline void Gb_Apu::save_load2( gb_apu_state_t* io, bool save ) +{ + for ( int i = osc_count; --i >= 0; ) + { + Gb_Osc& osc = *oscs [i]; + REFLECT( osc.delay, delay [i] ); + REFLECT( osc.length_ctr, length_ctr [i] ); + REFLECT( osc.phase, phase [i] ); + REFLECT( osc.enabled, enabled [i] ); + + if ( i != 2 ) + { + int j = min( i, 2 ); + Gb_Env& env = STATIC_CAST(Gb_Env&,osc); + REFLECT( env.env_delay, env_delay [j] ); + REFLECT( env.volume, env_volume [j] ); + REFLECT( env.env_enabled, env_enabled [j] ); + } + } +} + +void Gb_Apu::save_state( gb_apu_state_t* out ) +{ + (void) save_load( out, true ); + save_load2( out, true ); + + #if !GB_APU_CUSTOM_STATE + memset( out->unused, 0, sizeof out->unused ); + #endif +} + +blargg_err_t Gb_Apu::load_state( gb_apu_state_t const& in ) +{ + RETURN_ERR( save_load( CONST_CAST(gb_apu_state_t*,&in), false ) ); + save_load2( CONST_CAST(gb_apu_state_t*,&in), false ); + + apply_stereo(); + synth_volume( 0 ); // suppress output for the moment + run_until_( last_time ); // get last_amp updated + apply_volume(); // now use correct volume + + return 0; +} diff --git a/src/dmg/gb_apu/Gb_Oscs.cpp b/src/dmg/gb_apu/Gb_Oscs.cpp new file mode 100644 index 00000000..b77cbad2 --- /dev/null +++ b/src/dmg/gb_apu/Gb_Oscs.cpp @@ -0,0 +1,665 @@ +// Gb_Snd_Emu 0.2.0. http://www.slack.net/~ant/ + +#include "Gb_Apu.h" + +/* Copyright (C) 2003-2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +bool const cgb_02 = false; // enables bug in early CGB units that causes problems in some games +bool const cgb_05 = false; // enables CGB-05 zombie behavior + +int const trigger_mask = 0x80; +int const length_enabled = 0x40; + +void Gb_Osc::reset() +{ + output = 0; + last_amp = 0; + delay = 0; + phase = 0; + enabled = false; +} + +inline void Gb_Osc::update_amp( blip_time_t time, int new_amp ) +{ + output->set_modified(); + int delta = new_amp - last_amp; + if ( delta ) + { + last_amp = new_amp; + med_synth->offset( time, delta, output ); + } +} + +// Units + +void Gb_Osc::clock_length() +{ + if ( (regs [4] & length_enabled) && length_ctr ) + { + if ( --length_ctr <= 0 ) + enabled = false; + } +} + +inline int Gb_Env::reload_env_timer() +{ + int raw = regs [2] & 7; + env_delay = (raw ? raw : 8); + return raw; +} + +void Gb_Env::clock_envelope() +{ + if ( env_enabled && --env_delay <= 0 && reload_env_timer() ) + { + int v = volume + (regs [2] & 0x08 ? +1 : -1); + if ( 0 <= v && v <= 15 ) + volume = v; + else + env_enabled = false; + } +} + +inline void Gb_Sweep_Square::reload_sweep_timer() +{ + sweep_delay = (regs [0] & period_mask) >> 4; + if ( !sweep_delay ) + sweep_delay = 8; +} + +void Gb_Sweep_Square::calc_sweep( bool update ) +{ + int const shift = regs [0] & shift_mask; + int const delta = sweep_freq >> shift; + sweep_neg = (regs [0] & 0x08) != 0; + int const freq = sweep_freq + (sweep_neg ? -delta : delta); + + if ( freq > 0x7FF ) + { + enabled = false; + } + else if ( shift && update ) + { + sweep_freq = freq; + + regs [3] = freq & 0xFF; + regs [4] = (regs [4] & ~0x07) | (freq >> 8 & 0x07); + } +} + +void Gb_Sweep_Square::clock_sweep() +{ + if ( --sweep_delay <= 0 ) + { + reload_sweep_timer(); + if ( sweep_enabled && (regs [0] & period_mask) ) + { + calc_sweep( true ); + calc_sweep( false ); + } + } +} + +int Gb_Wave::access( unsigned addr ) const +{ + if ( enabled ) + { + addr = phase & (bank_size - 1); + if ( mode == Gb_Apu::mode_dmg ) + { + addr++; + if ( delay > clk_mul ) + return -1; // can only access within narrow time window while playing + } + addr >>= 1; + } + return addr & 0x0F; +} + +// write_register + +int Gb_Osc::write_trig( int frame_phase, int max_len, int old_data ) +{ + int data = regs [4]; + + if ( (frame_phase & 1) && !(old_data & length_enabled) && length_ctr ) + { + if ( (data & length_enabled) || cgb_02 ) + length_ctr--; + } + + if ( data & trigger_mask ) + { + enabled = true; + if ( !length_ctr ) + { + length_ctr = max_len; + if ( (frame_phase & 1) && (data & length_enabled) ) + length_ctr--; + } + } + + if ( !length_ctr ) + enabled = false; + + return data & trigger_mask; +} + +inline void Gb_Env::zombie_volume( int old, int data ) +{ + int v = volume; + if ( mode == Gb_Apu::mode_agb || cgb_05 ) + { + // CGB-05 behavior, very close to AGB behavior as well + if ( (old ^ data) & 8 ) + { + if ( !(old & 8) ) + { + v++; + if ( old & 7 ) + v++; + } + + v = 16 - v; + } + else if ( (old & 0x0F) == 8 ) + { + v++; + } + } + else + { + // CGB-04&02 behavior, very close to MGB behavior as well + if ( !(old & 7) && env_enabled ) + v++; + else if ( !(old & 8) ) + v += 2; + + if ( (old ^ data) & 8 ) + v = 16 - v; + } + volume = v & 0x0F; +} + +bool Gb_Env::write_register( int frame_phase, int reg, int old, int data ) +{ + int const max_len = 64; + + switch ( reg ) + { + case 1: + length_ctr = max_len - (data & (max_len - 1)); + break; + + case 2: + if ( !dac_enabled() ) + enabled = false; + + zombie_volume( old, data ); + + if ( (data & 7) && env_delay == 8 ) + { + env_delay = 1; + clock_envelope(); // TODO: really happens at next length clock + } + break; + + case 4: + if ( write_trig( frame_phase, max_len, old ) ) + { + volume = regs [2] >> 4; + reload_env_timer(); + env_enabled = true; + if ( frame_phase == 7 ) + env_delay++; + if ( !dac_enabled() ) + enabled = false; + return true; + } + } + return false; +} + +bool Gb_Square::write_register( int frame_phase, int reg, int old_data, int data ) +{ + bool result = Gb_Env::write_register( frame_phase, reg, old_data, data ); + if ( result ) + delay = (delay & (4 * clk_mul - 1)) + period(); + return result; +} + +inline void Gb_Noise::write_register( int frame_phase, int reg, int old_data, int data ) +{ + if ( Gb_Env::write_register( frame_phase, reg, old_data, data ) ) + { + phase = 0x7FFF; + delay += 8 * clk_mul; + } +} + +inline void Gb_Sweep_Square::write_register( int frame_phase, int reg, int old_data, int data ) +{ + if ( reg == 0 && sweep_enabled && sweep_neg && !(data & 0x08) ) + enabled = false; // sweep negate disabled after used + + if ( Gb_Square::write_register( frame_phase, reg, old_data, data ) ) + { + sweep_freq = frequency(); + sweep_neg = false; + reload_sweep_timer(); + sweep_enabled = (regs [0] & (period_mask | shift_mask)) != 0; + if ( regs [0] & shift_mask ) + calc_sweep( false ); + } +} + +void Gb_Wave::corrupt_wave() +{ + int pos = ((phase + 1) & (bank_size - 1)) >> 1; + if ( pos < 4 ) + wave_ram [0] = wave_ram [pos]; + else + for ( int i = 4; --i >= 0; ) + wave_ram [i] = wave_ram [(pos & ~3) + i]; +} + +inline void Gb_Wave::write_register( int frame_phase, int reg, int old_data, int data ) +{ + int const max_len = 256; + + switch ( reg ) + { + case 0: + if ( !dac_enabled() ) + enabled = false; + break; + + case 1: + length_ctr = max_len - data; + break; + + case 4: + bool was_enabled = enabled; + if ( write_trig( frame_phase, max_len, old_data ) ) + { + if ( !dac_enabled() ) + enabled = false; + else if ( mode == Gb_Apu::mode_dmg && was_enabled && + (unsigned) (delay - 2 * clk_mul) < 2 * clk_mul ) + corrupt_wave(); + + phase = 0; + delay = period() + 6 * clk_mul; + } + } +} + +void Gb_Apu::write_osc( int index, int reg, int old_data, int data ) +{ + reg -= index * 5; + switch ( index ) + { + case 0: square1.write_register( frame_phase, reg, old_data, data ); break; + case 1: square2.write_register( frame_phase, reg, old_data, data ); break; + case 2: wave .write_register( frame_phase, reg, old_data, data ); break; + case 3: noise .write_register( frame_phase, reg, old_data, data ); break; + } +} + +// Synthesis + +void Gb_Square::run( blip_time_t time, blip_time_t end_time ) +{ + // Calc duty and phase + static byte const duty_offsets [4] = { 1, 1, 3, 7 }; + static byte const duties [4] = { 1, 2, 4, 6 }; + int const duty_code = regs [1] >> 6; + int duty_offset = duty_offsets [duty_code]; + int duty = duties [duty_code]; + if ( mode == Gb_Apu::mode_agb ) + { + // AGB uses inverted duty + duty_offset -= duty; + duty = 8 - duty; + } + int ph = (this->phase + duty_offset) & 7; + + // Determine what will be generated + int vol = 0; + Blip_Buffer* const out = this->output; + if ( out ) + { + int amp = dac_off_amp; + if ( dac_enabled() ) + { + if ( enabled ) + vol = this->volume; + + amp = -dac_bias; + if ( mode == Gb_Apu::mode_agb ) + amp = -(vol >> 1); + + // Play inaudible frequencies as constant amplitude + if ( frequency() >= 0x7FA && delay < 32 * clk_mul ) + { + amp += (vol * duty) >> 3; + vol = 0; + } + + if ( ph < duty ) + { + amp += vol; + vol = -vol; + } + } + update_amp( time, amp ); + } + + // Generate wave + time += delay; + if ( time < end_time ) + { + int const per = this->period(); + if ( !vol ) + { + // Maintain phase when not playing + int count = (end_time - time + per - 1) / per; + ph += count; // will be masked below + time += (blip_time_t) count * per; + } + else + { + // Output amplitude transitions + int delta = vol; + do + { + ph = (ph + 1) & 7; + if ( ph == 0 || ph == duty ) + { + good_synth->offset_inline( time, delta, out ); + delta = -delta; + } + time += per; + } + while ( time < end_time ); + + if ( delta != vol ) + last_amp -= delta; + } + this->phase = (ph - duty_offset) & 7; + } + delay = time - end_time; +} + +// Quickly runs LFSR for a large number of clocks. For use when noise is generating +// no sound. +static unsigned run_lfsr( unsigned s, unsigned mask, int count ) +{ + bool const optimized = true; // set to false to use only unoptimized loop in middle + + // optimization used in several places: + // ((s & (1 << b)) << n) ^ ((s & (1 << b)) << (n + 1)) = (s & (1 << b)) * (3 << n) + + if ( mask == 0x4000 && optimized ) + { + if ( count >= 32767 ) + count %= 32767; + + // Convert from Fibonacci to Galois configuration, + // shifted left 1 bit + s ^= (s & 1) * 0x8000; + + // Each iteration is equivalent to clocking LFSR 255 times + while ( (count -= 255) > 0 ) + s ^= ((s & 0xE) << 12) ^ ((s & 0xE) << 11) ^ (s >> 3); + count += 255; + + // Each iteration is equivalent to clocking LFSR 15 times + // (interesting similarity to single clocking below) + while ( (count -= 15) > 0 ) + s ^= ((s & 2) * (3 << 13)) ^ (s >> 1); + count += 15; + + // Remaining singles + while ( --count >= 0 ) + s = ((s & 2) * (3 << 13)) ^ (s >> 1); + + // Convert back to Fibonacci configuration + s &= 0x7FFF; + } + else if ( count < 8 || !optimized ) + { + // won't fully replace upper 8 bits, so have to do the unoptimized way + while ( --count >= 0 ) + s = (s >> 1 | mask) ^ (mask & -((s - 1) & 2)); + } + else + { + if ( count > 127 ) + { + count %= 127; + if ( !count ) + count = 127; // must run at least once + } + + // Need to keep one extra bit of history + s = s << 1 & 0xFF; + + // Convert from Fibonacci to Galois configuration, + // shifted left 2 bits + s ^= (s & 2) * 0x80; + + // Each iteration is equivalent to clocking LFSR 7 times + // (interesting similarity to single clocking below) + while ( (count -= 7) > 0 ) + s ^= ((s & 4) * (3 << 5)) ^ (s >> 1); + count += 7; + + // Remaining singles + while ( --count >= 0 ) + s = ((s & 4) * (3 << 5)) ^ (s >> 1); + + // Convert back to Fibonacci configuration and + // repeat last 8 bits above significant 7 + s = (s << 7 & 0x7F80) | (s >> 1 & 0x7F); + } + + return s; +} + +void Gb_Noise::run( blip_time_t time, blip_time_t end_time ) +{ + // Determine what will be generated + int vol = 0; + Blip_Buffer* const out = this->output; + if ( out ) + { + int amp = dac_off_amp; + if ( dac_enabled() ) + { + if ( enabled ) + vol = this->volume; + + amp = -dac_bias; + if ( mode == Gb_Apu::mode_agb ) + amp = -(vol >> 1); + + if ( !(phase & 1) ) + { + amp += vol; + vol = -vol; + } + } + + // AGB negates final output + if ( mode == Gb_Apu::mode_agb ) + { + vol = -vol; + amp = -amp; + } + + update_amp( time, amp ); + } + + // Run timer and calculate time of next LFSR clock + static byte const period1s [8] = { 1, 2, 4, 6, 8, 10, 12, 14 }; + int const period1 = period1s [regs [3] & 7] * clk_mul; + { + int extra = (end_time - time) - delay; + int const per2 = this->period2(); + time += delay + ((divider ^ (per2 >> 1)) & (per2 - 1)) * period1; + + int count = (extra < 0 ? 0 : (extra + period1 - 1) / period1); + divider = (divider - count) & period2_mask; + delay = count * period1 - extra; + } + + // Generate wave + if ( time < end_time ) + { + unsigned const mask = this->lfsr_mask(); + unsigned bits = this->phase; + + int per = period2( period1 * 8 ); + if ( period2_index() >= 0xE ) + { + time = end_time; + } + else if ( !vol ) + { + // Maintain phase when not playing + int count = (end_time - time + per - 1) / per; + time += (blip_time_t) count * per; + bits = run_lfsr( bits, ~mask, count ); + } + else + { + // Output amplitude transitions + int delta = -vol; + do + { + unsigned changed = bits + 1; + bits = bits >> 1 & mask; + if ( changed & 2 ) + { + bits |= ~mask; + delta = -delta; + med_synth->offset_inline( time, delta, out ); + } + time += per; + } + while ( time < end_time ); + + if ( delta == vol ) + last_amp += delta; + } + this->phase = bits; + } +} + +void Gb_Wave::run( blip_time_t time, blip_time_t end_time ) +{ + // Calc volume + static byte const volumes [8] = { 0, 4, 2, 1, 3, 3, 3, 3 }; + int const volume_shift = 2; + int const volume_idx = regs [2] >> 5 & (agb_mask | 3); // 2 bits on DMG/CGB, 3 on AGB + int const volume_mul = volumes [volume_idx]; + + // Determine what will be generated + int playing = false; + Blip_Buffer* const out = this->output; + if ( out ) + { + int amp = dac_off_amp; + if ( dac_enabled() ) + { + // Play inaudible frequencies as constant amplitude + amp = 8 << 4; // really depends on average of all samples in wave + + // if delay is larger, constant amplitude won't start yet + if ( frequency() <= 0x7FB || delay > 15 * clk_mul ) + { + if ( volume_mul ) + playing = (int) enabled; + + amp = (sample_buf << (phase << 2 & 4) & 0xF0) * playing; + } + + amp = ((amp * volume_mul) >> (volume_shift + 4)) - dac_bias; + } + update_amp( time, amp ); + } + + // Generate wave + time += delay; + if ( time < end_time ) + { + byte const* wave = this->wave_ram; + + // wave size and bank + int const size20_mask = 0x20; + int const flags = regs [0] & agb_mask; + int const wave_mask = (flags & size20_mask) | 0x1F; + int swap_banks = 0; + if ( flags & bank40_mask ) + { + swap_banks = flags & size20_mask; + wave += bank_size/2 - (swap_banks >> 1); + } + + int ph = this->phase ^ swap_banks; + ph = (ph + 1) & wave_mask; // pre-advance + + int const per = this->period(); + if ( !playing ) + { + // Maintain phase when not playing + int count = (end_time - time + per - 1) / per; + ph += count; // will be masked below + time += (blip_time_t) count * per; + } + else + { + // Output amplitude transitions + int lamp = this->last_amp + dac_bias; + do + { + // Extract nybble + int nybble = wave [ph >> 1] << (ph << 2 & 4) & 0xF0; + ph = (ph + 1) & wave_mask; + + // Scale by volume + int amp = (nybble * volume_mul) >> (volume_shift + 4); + + int delta = amp - lamp; + if ( delta ) + { + lamp = amp; + med_synth->offset_inline( time, delta, out ); + } + time += per; + } + while ( time < end_time ); + this->last_amp = lamp - dac_bias; + } + ph = (ph - 1) & wave_mask; // undo pre-advance and mask position + + // Keep track of last byte read + if ( enabled ) + sample_buf = wave [ph >> 1]; + + this->phase = ph ^ swap_banks; // undo swapped banks + } + delay = time - end_time; +} diff --git a/src/dmg/gb_apu/Gb_Oscs.h b/src/dmg/gb_apu/Gb_Oscs.h new file mode 100644 index 00000000..260bb061 --- /dev/null +++ b/src/dmg/gb_apu/Gb_Oscs.h @@ -0,0 +1,190 @@ +// Private oscillators used by Gb_Apu + +// Gb_Snd_Emu 0.2.0 +#ifndef GB_OSCS_H +#define GB_OSCS_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +#ifndef GB_APU_OVERCLOCK + #define GB_APU_OVERCLOCK 1 +#endif + +#if GB_APU_OVERCLOCK & (GB_APU_OVERCLOCK - 1) + #error "GB_APU_OVERCLOCK must be a power of 2" +#endif + +class Gb_Osc { +protected: + + // 11-bit frequency in NRx3 and NRx4 + int frequency() const { return (regs [4] & 7) * 0x100 + regs [3]; } + + void update_amp( blip_time_t, int new_amp ); + int write_trig( int frame_phase, int max_len, int old_data ); +public: + + enum { clk_mul = GB_APU_OVERCLOCK }; + enum { dac_bias = 7 }; + + Blip_Buffer* outputs [4];// NULL, right, left, center + Blip_Buffer* output; // where to output sound + BOOST::uint8_t* regs; // osc's 5 registers + int mode; // mode_dmg, mode_cgb, mode_agb + int dac_off_amp;// amplitude when DAC is off + int last_amp; // current amplitude in Blip_Buffer + typedef Blip_Synth Good_Synth; + typedef Blip_Synth Med_Synth; + Good_Synth const* good_synth; + Med_Synth const* med_synth; + + int delay; // clocks until frequency timer expires + int length_ctr; // length counter + unsigned phase; // waveform phase (or equivalent) + bool enabled; // internal enabled flag + + void clock_length(); + void reset(); +}; + +class Gb_Env : public Gb_Osc { +public: + int env_delay; + int volume; + bool env_enabled; + + void clock_envelope(); + bool write_register( int frame_phase, int reg, int old_data, int data ); + + void reset() + { + env_delay = 0; + volume = 0; + Gb_Osc::reset(); + } +protected: + // Non-zero if DAC is enabled + int dac_enabled() const { return regs [2] & 0xF8; } +private: + void zombie_volume( int old, int data ); + int reload_env_timer(); +}; + +class Gb_Square : public Gb_Env { +public: + bool write_register( int frame_phase, int reg, int old_data, int data ); + void run( blip_time_t, blip_time_t ); + + void reset() + { + Gb_Env::reset(); + delay = 0x40000000; // TODO: something less hacky (never clocked until first trigger) + } +private: + // Frequency timer period + int period() const { return (2048 - frequency()) * (4 * clk_mul); } +}; + +class Gb_Sweep_Square : public Gb_Square { +public: + int sweep_freq; + int sweep_delay; + bool sweep_enabled; + bool sweep_neg; + + void clock_sweep(); + void write_register( int frame_phase, int reg, int old_data, int data ); + + void reset() + { + sweep_freq = 0; + sweep_delay = 0; + sweep_enabled = false; + sweep_neg = false; + Gb_Square::reset(); + } +private: + enum { period_mask = 0x70 }; + enum { shift_mask = 0x07 }; + + void calc_sweep( bool update ); + void reload_sweep_timer(); +}; + +class Gb_Noise : public Gb_Env { +public: + + int divider; // noise has more complex frequency divider setup + + void run( blip_time_t, blip_time_t ); + void write_register( int frame_phase, int reg, int old_data, int data ); + + void reset() + { + divider = 0; + Gb_Env::reset(); + delay = 4 * clk_mul; // TODO: remove? + } +private: + enum { period2_mask = 0x1FFFF }; + + int period2_index() const { return regs [3] >> 4; } + int period2( int base = 8 ) const { return base << period2_index(); } + unsigned lfsr_mask() const { return (regs [3] & 0x08) ? ~0x4040 : ~0x4000; } +}; + +class Gb_Wave : public Gb_Osc { +public: + int sample_buf; // last wave RAM byte read (hardware has this as well) + + void write_register( int frame_phase, int reg, int old_data, int data ); + void run( blip_time_t, blip_time_t ); + + // Reads/writes wave RAM + int read( unsigned addr ) const; + void write( unsigned addr, int data ); + + void reset() + { + sample_buf = 0; + Gb_Osc::reset(); + } + +private: + enum { bank40_mask = 0x40 }; + enum { bank_size = 32 }; + + int agb_mask; // 0xFF if AGB features enabled, 0 otherwise + BOOST::uint8_t* wave_ram; // 32 bytes (64 nybbles), stored in APU + + friend class Gb_Apu; + + // Frequency timer period + int period() const { return (2048 - frequency()) * (2 * clk_mul); } + + // Non-zero if DAC is enabled + int dac_enabled() const { return regs [0] & 0x80; } + + void corrupt_wave(); + + BOOST::uint8_t* wave_bank() const { return &wave_ram [(~regs [0] & bank40_mask) >> 2 & agb_mask]; } + + // Wave index that would be accessed, or -1 if no access would occur + int access( unsigned addr ) const; +}; + +inline int Gb_Wave::read( unsigned addr ) const +{ + int index = access( addr ); + return (index < 0 ? 0xFF : wave_bank() [index]); +} + +inline void Gb_Wave::write( unsigned addr, int data ) +{ + int index = access( addr ); + if ( index >= 0 ) + wave_bank() [index] = data;; +} + +#endif diff --git a/src/dmg/gb_apu/Multi_Buffer.cpp b/src/dmg/gb_apu/Multi_Buffer.cpp new file mode 100644 index 00000000..01e127d4 --- /dev/null +++ b/src/dmg/gb_apu/Multi_Buffer.cpp @@ -0,0 +1,281 @@ +// Blip_Buffer 0.4.1. http://www.slack.net/~ant/ + +#include "Multi_Buffer.h" + +/* Copyright (C) 2003-2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf ) +{ + length_ = 0; + sample_rate_ = 0; + channels_changed_count_ = 1; + channel_types_ = 0; + channel_count_ = 0; + immediate_removal_ = true; +} + +Multi_Buffer::channel_t Multi_Buffer::channel( int /*index*/ ) +{ + static channel_t const ch = { 0, 0, 0 }; + return ch; +} + +// Silent_Buffer + +Silent_Buffer::Silent_Buffer() : Multi_Buffer( 1 ) // 0 channels would probably confuse +{ + // TODO: better to use empty Blip_Buffer so caller never has to check for NULL? + chan.left = 0; + chan.center = 0; + chan.right = 0; +} + +// Mono_Buffer + +Mono_Buffer::Mono_Buffer() : Multi_Buffer( 1 ) +{ + chan.center = &buf; + chan.left = &buf; + chan.right = &buf; +} + +Mono_Buffer::~Mono_Buffer() { } + +blargg_err_t Mono_Buffer::set_sample_rate( long rate, int msec ) +{ + RETURN_ERR( buf.set_sample_rate( rate, msec ) ); + return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() ); +} + + +// Tracked_Blip_Buffer + +Tracked_Blip_Buffer::Tracked_Blip_Buffer() +{ + last_non_silence = 0; +} + +void Tracked_Blip_Buffer::clear() +{ + last_non_silence = 0; + Blip_Buffer::clear(); +} + +void Tracked_Blip_Buffer::end_frame( blip_time_t t ) +{ + Blip_Buffer::end_frame( t ); + if ( clear_modified() ) + last_non_silence = samples_avail() + blip_buffer_extra_; +} + +blip_ulong Tracked_Blip_Buffer::non_silent() const +{ + return last_non_silence | unsettled(); +} + +inline void Tracked_Blip_Buffer::remove_( long n ) +{ + if ( (last_non_silence -= n) < 0 ) + last_non_silence = 0; +} + +void Tracked_Blip_Buffer::remove_silence( long n ) +{ + remove_( n ); + Blip_Buffer::remove_silence( n ); +} + +void Tracked_Blip_Buffer::remove_samples( long n ) +{ + remove_( n ); + Blip_Buffer::remove_samples( n ); +} + +void Tracked_Blip_Buffer::remove_all_samples() +{ + long avail = samples_avail(); + if ( !non_silent() ) + remove_silence( avail ); + else + remove_samples( avail ); +} + +long Tracked_Blip_Buffer::read_samples( blip_sample_t* out, long count ) +{ + count = Blip_Buffer::read_samples( out, count ); + remove_( count ); + return count; +} + +// Stereo_Buffer + +int const stereo = 2; + +Stereo_Buffer::Stereo_Buffer() : Multi_Buffer( 2 ) +{ + chan.center = mixer.bufs [2] = &bufs [2]; + chan.left = mixer.bufs [0] = &bufs [0]; + chan.right = mixer.bufs [1] = &bufs [1]; + mixer.samples_read = 0; +} + +Stereo_Buffer::~Stereo_Buffer() { } + +blargg_err_t Stereo_Buffer::set_sample_rate( long rate, int msec ) +{ + mixer.samples_read = 0; + for ( int i = bufs_size; --i >= 0; ) + RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) ); + return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() ); +} + +void Stereo_Buffer::clock_rate( long rate ) +{ + for ( int i = bufs_size; --i >= 0; ) + bufs [i].clock_rate( rate ); +} + +void Stereo_Buffer::bass_freq( int bass ) +{ + for ( int i = bufs_size; --i >= 0; ) + bufs [i].bass_freq( bass ); +} + +void Stereo_Buffer::clear() +{ + mixer.samples_read = 0; + for ( int i = bufs_size; --i >= 0; ) + bufs [i].clear(); +} + +void Stereo_Buffer::end_frame( blip_time_t time ) +{ + for ( int i = bufs_size; --i >= 0; ) + bufs [i].end_frame( time ); +} + +long Stereo_Buffer::read_samples( blip_sample_t* out, long out_size ) +{ + require( (out_size & 1) == 0 ); // must read an even number of samples + out_size = min( out_size, samples_avail() ); + + int pair_count = int (out_size >> 1); + if ( pair_count ) + { + mixer.read_pairs( out, pair_count ); + + if ( samples_avail() <= 0 || immediate_removal() ) + { + for ( int i = bufs_size; --i >= 0; ) + { + buf_t& b = bufs [i]; + // TODO: might miss non-silence settling since it checks END of last read + if ( !b.non_silent() ) + b.remove_silence( mixer.samples_read ); + else + b.remove_samples( mixer.samples_read ); + } + mixer.samples_read = 0; + } + } + return out_size; +} + + +// Stereo_Mixer + +// mixers use a single index value to improve performance on register-challenged processors +// offset goes from negative to zero + +void Stereo_Mixer::read_pairs( blip_sample_t* out, int count ) +{ + // TODO: if caller never marks buffers as modified, uses mono + // except that buffer isn't cleared, so caller can encounter + // subtle problems and not realize the cause. + samples_read += count; + if ( bufs [0]->non_silent() | bufs [1]->non_silent() ) + mix_stereo( out, count ); + else + mix_mono( out, count ); +} + +void Stereo_Mixer::mix_mono( blip_sample_t* out_, int count ) +{ + int const bass = BLIP_READER_BASS( *bufs [2] ); + BLIP_READER_BEGIN( center, *bufs [2] ); + BLIP_READER_ADJ_( center, samples_read ); + + typedef blip_sample_t stereo_blip_sample_t [stereo]; + stereo_blip_sample_t* BLIP_RESTRICT out = (stereo_blip_sample_t*) out_ + count; + int offset = -count; + do + { + blargg_long s = BLIP_READER_READ( center ); + BLIP_READER_NEXT_IDX_( center, bass, offset ); + BLIP_CLAMP( s, s ); + + out [offset] [0] = (blip_sample_t) s; + out [offset] [1] = (blip_sample_t) s; + } + while ( ++offset ); + + BLIP_READER_END( center, *bufs [2] ); +} + +void Stereo_Mixer::mix_stereo( blip_sample_t* out_, int count ) +{ + blip_sample_t* BLIP_RESTRICT out = out_ + count * stereo; + + // do left + center and right + center separately to reduce register load + Tracked_Blip_Buffer* const* buf = &bufs [2]; + while ( true ) // loop runs twice + { + --buf; + --out; + + int const bass = BLIP_READER_BASS( *bufs [2] ); + BLIP_READER_BEGIN( side, **buf ); + BLIP_READER_BEGIN( center, *bufs [2] ); + + BLIP_READER_ADJ_( side, samples_read ); + BLIP_READER_ADJ_( center, samples_read ); + + int offset = -count; + do + { + blargg_long s = BLIP_READER_READ_RAW( center ) + BLIP_READER_READ_RAW( side ); + s >>= blip_sample_bits - 16; + BLIP_READER_NEXT_IDX_( side, bass, offset ); + BLIP_READER_NEXT_IDX_( center, bass, offset ); + BLIP_CLAMP( s, s ); + + ++offset; // before write since out is decremented to slightly before end + out [offset * stereo] = (blip_sample_t) s; + } + while ( offset ); + + BLIP_READER_END( side, **buf ); + + if ( buf != bufs ) + continue; + + // only end center once + BLIP_READER_END( center, *bufs [2] ); + break; + } +} diff --git a/src/dmg/gb_apu/Multi_Buffer.h b/src/dmg/gb_apu/Multi_Buffer.h new file mode 100644 index 00000000..bc5214c8 --- /dev/null +++ b/src/dmg/gb_apu/Multi_Buffer.h @@ -0,0 +1,205 @@ +// Multi-channel sound buffer interface, and basic mono and stereo buffers + +// Blip_Buffer 0.4.1 +#ifndef MULTI_BUFFER_H +#define MULTI_BUFFER_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +// Interface to one or more Blip_Buffers mapped to one or more channels +// consisting of left, center, and right buffers. +class Multi_Buffer { +public: + Multi_Buffer( int samples_per_frame ); + virtual ~Multi_Buffer() { } + + // Sets the number of channels available and optionally their types + // (type information used by Effects_Buffer) + enum { type_index_mask = 0xFF }; + enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; + virtual blargg_err_t set_channel_count( int, int const* types = 0 ); + int channel_count() const { return channel_count_; } + + // Gets indexed channel, from 0 to channel count - 1 + struct channel_t { + Blip_Buffer* center; + Blip_Buffer* left; + Blip_Buffer* right; + }; + virtual channel_t channel( int index ) BLARGG_PURE( ; ) + + // See Blip_Buffer.h + virtual blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) BLARGG_PURE( ; ) + virtual void clock_rate( long ) BLARGG_PURE( { } ) + virtual void bass_freq( int ) BLARGG_PURE( { } ) + virtual void clear() BLARGG_PURE( { } ) + long sample_rate() const; + + // Length of buffer, in milliseconds + int length() const; + + // See Blip_Buffer.h + virtual void end_frame( blip_time_t ) BLARGG_PURE( { } ) + + // Number of samples per output frame (1 = mono, 2 = stereo) + int samples_per_frame() const; + + // Count of changes to channel configuration. Incremented whenever + // a change is made to any of the Blip_Buffers for any channel. + unsigned channels_changed_count() { return channels_changed_count_; } + + // See Blip_Buffer.h + virtual long read_samples( blip_sample_t*, long ) BLARGG_PURE( { return 0; } ) + virtual long samples_avail() const BLARGG_PURE( { return 0; } ) + +public: + BLARGG_DISABLE_NOTHROW + void disable_immediate_removal() { immediate_removal_ = false; } +protected: + bool immediate_removal() const { return immediate_removal_; } + int const* channel_types() const { return channel_types_; } + void channels_changed() { channels_changed_count_++; } +private: + // noncopyable + Multi_Buffer( const Multi_Buffer& ); + Multi_Buffer& operator = ( const Multi_Buffer& ); + + unsigned channels_changed_count_; + long sample_rate_; + int length_; + int channel_count_; + int const samples_per_frame_; + int const* channel_types_; + bool immediate_removal_; +}; + +// Uses a single buffer and outputs mono samples. +class Mono_Buffer : public Multi_Buffer { + Blip_Buffer buf; + channel_t chan; +public: + // Buffer used for all channels + Blip_Buffer* center() { return &buf; } + +public: + Mono_Buffer(); + ~Mono_Buffer(); + blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ); + void clock_rate( long rate ) { buf.clock_rate( rate ); } + void bass_freq( int freq ) { buf.bass_freq( freq ); } + void clear() { buf.clear(); } + long samples_avail() const { return buf.samples_avail(); } + long read_samples( blip_sample_t* p, long s ) { return buf.read_samples( p, s ); } + channel_t channel( int ) { return chan; } + void end_frame( blip_time_t t ) { buf.end_frame( t ); } +}; + + class Tracked_Blip_Buffer : public Blip_Buffer { + public: + // Non-zero if buffer still has non-silent samples in it. Requires that you call + // set_modified() appropriately. + blip_ulong non_silent() const; + + // remove_samples( samples_avail() ) + void remove_all_samples(); + + public: + BLARGG_DISABLE_NOTHROW + + long read_samples( blip_sample_t*, long ); + void remove_silence( long ); + void remove_samples( long ); + Tracked_Blip_Buffer(); + void clear(); + void end_frame( blip_time_t ); + private: + blip_long last_non_silence; + void remove_( long ); + }; + + class Stereo_Mixer { + public: + Tracked_Blip_Buffer* bufs [3]; + blargg_long samples_read; + + Stereo_Mixer() : samples_read( 0 ) { } + void read_pairs( blip_sample_t* out, int count ); + private: + void mix_mono ( blip_sample_t* out, int pair_count ); + void mix_stereo( blip_sample_t* out, int pair_count ); + }; + +// Uses three buffers (one for center) and outputs stereo sample pairs. +class Stereo_Buffer : public Multi_Buffer { +public: + + // Buffers used for all channels + Blip_Buffer* center() { return &bufs [2]; } + Blip_Buffer* left() { return &bufs [0]; } + Blip_Buffer* right() { return &bufs [1]; } + +public: + Stereo_Buffer(); + ~Stereo_Buffer(); + blargg_err_t set_sample_rate( long, int msec = blip_default_length ); + void clock_rate( long ); + void bass_freq( int ); + void clear(); + channel_t channel( int ) { return chan; } + void end_frame( blip_time_t ); + + long samples_avail() const { return (bufs [0].samples_avail() - mixer.samples_read) * 2; } + long read_samples( blip_sample_t*, long ); + +private: + enum { bufs_size = 3 }; + typedef Tracked_Blip_Buffer buf_t; + buf_t bufs [bufs_size]; + Stereo_Mixer mixer; + channel_t chan; + long samples_avail_; +}; + +// Silent_Buffer generates no samples, useful where no sound is wanted +class Silent_Buffer : public Multi_Buffer { + channel_t chan; +public: + Silent_Buffer(); + blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ); + void clock_rate( long ) { } + void bass_freq( int ) { } + void clear() { } + channel_t channel( int ) { return chan; } + void end_frame( blip_time_t ) { } + long samples_avail() const { return 0; } + long read_samples( blip_sample_t*, long ) { return 0; } +}; + + +inline blargg_err_t Multi_Buffer::set_sample_rate( long rate, int msec ) +{ + sample_rate_ = rate; + length_ = msec; + return 0; +} + +inline blargg_err_t Silent_Buffer::set_sample_rate( long rate, int msec ) +{ + return Multi_Buffer::set_sample_rate( rate, msec ); +} + +inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; } + +inline long Multi_Buffer::sample_rate() const { return sample_rate_; } + +inline int Multi_Buffer::length() const { return length_; } + +inline blargg_err_t Multi_Buffer::set_channel_count( int n, int const* types ) +{ + channel_count_ = n; + channel_types_ = types; + return 0; +} + +#endif diff --git a/src/dmg/gb_apu/blargg_common.h b/src/dmg/gb_apu/blargg_common.h new file mode 100644 index 00000000..85508541 --- /dev/null +++ b/src/dmg/gb_apu/blargg_common.h @@ -0,0 +1,206 @@ +// Sets up common environment for Shay Green's libraries. +// To change configuration options, modify blargg_config.h, not this file. + +// Gb_Snd_Emu 0.2.0 +#ifndef BLARGG_COMMON_H +#define BLARGG_COMMON_H + +#include +#include +#include +#include + +#undef BLARGG_COMMON_H +// allow blargg_config.h to #include blargg_common.h +#include "blargg_config.h" +#ifndef BLARGG_COMMON_H +#define BLARGG_COMMON_H + +// BLARGG_RESTRICT: equivalent to restrict, where supported +#if __GNUC__ >= 3 || _MSC_VER >= 1100 + #define BLARGG_RESTRICT __restrict +#else + #define BLARGG_RESTRICT +#endif + +// STATIC_CAST(T,expr): Used in place of static_cast (expr) +// CONST_CAST( T,expr): Used in place of const_cast (expr) +#ifndef STATIC_CAST + #if __GNUC__ >= 4 + #define STATIC_CAST(T,expr) static_cast (expr) + #define CONST_CAST( T,expr) const_cast (expr) + #else + #define STATIC_CAST(T,expr) ((T) (expr)) + #define CONST_CAST( T,expr) ((T) (expr)) + #endif +#endif + +// blargg_err_t (0 on success, otherwise error string) +#ifndef blargg_err_t + typedef const char* blargg_err_t; +#endif + +// blargg_vector - very lightweight vector of POD types (no constructor/destructor) +template +class blargg_vector { + T* begin_; + size_t size_; +public: + blargg_vector() : begin_( 0 ), size_( 0 ) { } + ~blargg_vector() { free( begin_ ); } + size_t size() const { return size_; } + T* begin() const { return begin_; } + T* end() const { return begin_ + size_; } + blargg_err_t resize( size_t n ) + { + // TODO: blargg_common.cpp to hold this as an outline function, ugh + void* p = realloc( begin_, n * sizeof (T) ); + if ( p ) + begin_ = (T*) p; + else if ( n > size_ ) // realloc failure only a problem if expanding + return "Out of memory"; + size_ = n; + return 0; + } + void clear() { void* p = begin_; begin_ = 0; size_ = 0; free( p ); } + T& operator [] ( size_t n ) const + { + assert( n <= size_ ); // <= to allow past-the-end value + return begin_ [n]; + } +}; + +#ifndef BLARGG_DISABLE_NOTHROW + // throw spec mandatory in ISO C++ if operator new can return NULL + #if __cplusplus >= 199711 || __GNUC__ >= 3 + #define BLARGG_THROWS( spec ) throw spec + #else + #define BLARGG_THROWS( spec ) + #endif + #define BLARGG_DISABLE_NOTHROW \ + void* operator new ( size_t s ) BLARGG_THROWS(()) { return malloc( s ); }\ + void operator delete ( void* p ) { free( p ); } + #define BLARGG_NEW new +#else + #include + #define BLARGG_NEW new (std::nothrow) +#endif + +// BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant) +#define BLARGG_4CHAR( a, b, c, d ) \ + ((a&0xFF)*0x1000000 + (b&0xFF)*0x10000 + (c&0xFF)*0x100 + (d&0xFF)) + +// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0. +#ifndef BOOST_STATIC_ASSERT + #ifdef _MSC_VER + // MSVC6 (_MSC_VER < 1300) fails for use of __LINE__ when /Zl is specified + #define BOOST_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] ) + #else + // Some other compilers fail when declaring same function multiple times in class, + // so differentiate them by line + #define BOOST_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] ) + #endif +#endif + +// BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1, +// compiler is assumed to support bool. If undefined, availability is determined. +#ifndef BLARGG_COMPILER_HAS_BOOL + #if defined (__MWERKS__) + #if !__option(bool) + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif + #elif defined (_MSC_VER) + #if _MSC_VER < 1100 + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif + #elif defined (__GNUC__) + // supports bool + #elif __cplusplus < 199711 + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif +#endif +#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL + // If you get errors here, modify your blargg_config.h file + typedef int bool; + const bool true = 1; + const bool false = 0; +#endif + +// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough + +#if INT_MAX < 0x7FFFFFFF || LONG_MAX == 0x7FFFFFFF + typedef long blargg_long; +#else + typedef int blargg_long; +#endif + +#if UINT_MAX < 0xFFFFFFFF || ULONG_MAX == 0xFFFFFFFF + typedef unsigned long blargg_ulong; +#else + typedef unsigned blargg_ulong; +#endif + +// BOOST::int8_t etc. + +// HAVE_STDINT_H: If defined, use for int8_t etc. +#if defined (HAVE_STDINT_H) + #include + #define BOOST + +// HAVE_INTTYPES_H: If defined, use for int8_t etc. +#elif defined (HAVE_INTTYPES_H) + #include + #define BOOST + +#else + struct BOOST + { + #if UCHAR_MAX == 0xFF && SCHAR_MAX == 0x7F + typedef signed char int8_t; + typedef unsigned char uint8_t; + #else + // No suitable 8-bit type available + typedef struct see_blargg_common_h int8_t; + typedef struct see_blargg_common_h uint8_t; + #endif + + #if USHRT_MAX == 0xFFFF + typedef short int16_t; + typedef unsigned short uint16_t; + #else + // No suitable 16-bit type available + typedef struct see_blargg_common_h int16_t; + typedef struct see_blargg_common_h uint16_t; + #endif + + #if ULONG_MAX == 0xFFFFFFFF + typedef long int32_t; + typedef unsigned long uint32_t; + #elif UINT_MAX == 0xFFFFFFFF + typedef int int32_t; + typedef unsigned int uint32_t; + #else + // No suitable 32-bit type available + typedef struct see_blargg_common_h int32_t; + typedef struct see_blargg_common_h uint32_t; + #endif + }; +#endif + +#if __GNUC__ >= 3 + #define BLARGG_DEPRECATED __attribute__ ((deprecated)) +#else + #define BLARGG_DEPRECATED +#endif + +// Use in place of "= 0;" for a pure virtual, since these cause calls to std C++ lib. +// During development, BLARGG_PURE( x ) expands to = 0; +// virtual int func() BLARGG_PURE( { return 0; } ) +#ifndef BLARGG_PURE + #define BLARGG_PURE( def ) def +#endif + +#endif +#endif diff --git a/src/dmg/gb_apu/blargg_config.h b/src/dmg/gb_apu/blargg_config.h new file mode 100644 index 00000000..b861487f --- /dev/null +++ b/src/dmg/gb_apu/blargg_config.h @@ -0,0 +1,33 @@ +// $package user configuration file. Don't replace when updating library. + +#ifndef BLARGG_CONFIG_H +#define BLARGG_CONFIG_H + +// Uncomment to have Gb_Apu run at 4x normal clock rate (16777216 Hz), useful in +// a Game Boy Advance emulator. +#define GB_APU_OVERCLOCK 4 + +#define GB_APU_CUSTOM_STATE 1 + +// Uncomment to enable platform-specific (and possibly non-portable) optimizations. +//#define BLARGG_NONPORTABLE 1 + +// Uncomment if automatic byte-order determination doesn't work +//#define BLARGG_BIG_ENDIAN 1 + +// Uncomment to use zlib for transparent decompression of gzipped files +//#define HAVE_ZLIB_H + +// Uncomment if you get errors in the bool section of blargg_common.h +//#define BLARGG_COMPILER_HAS_BOOL 1 + +// Uncomment to disable out-of-memory exceptions +//#include +//#define BLARGG_NEW new (std::nothrow) + +// Use standard config.h if present +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#endif diff --git a/src/dmg/gb_apu/blargg_source.h b/src/dmg/gb_apu/blargg_source.h new file mode 100644 index 00000000..53c354aa --- /dev/null +++ b/src/dmg/gb_apu/blargg_source.h @@ -0,0 +1,92 @@ +/* Included at the beginning of library source files, AFTER all other #include lines. +Sets up helpful macros and services used in my source code. Since this is only "active" +in my source code, I don't have to worry about polluting the global namespace with +unprefixed names. */ + +// Gb_Snd_Emu 0.2.0 +#ifndef BLARGG_SOURCE_H +#define BLARGG_SOURCE_H + +// The following four macros are for debugging only. Some or all might be defined +// to do nothing, depending on the circumstances. Described is what happens when +// a particular macro is defined to do something. When defined to do nothing, the +// macros do NOT evaluate their argument(s). + +// If expr is false, prints file and line number, then aborts program. Meant for +// checking internal state and consistency. A failed assertion indicates a bug +// in MY code. +// +// void assert( bool expr ); +#include + +// If expr is false, prints file and line number, then aborts program. Meant for +// checking caller-supplied parameters and operations that are outside the control +// of the module. A failed requirement probably indicates a bug in YOUR code. +// +// void require( bool expr ); +#undef require +#define require( expr ) assert( expr ) + +// Like printf() except output goes to debugging console/file. +// +// void dprintf( const char* format, ... ); +static inline void blargg_dprintf_( const char*, ... ) { } +#undef dprintf +#define dprintf (1) ? (void) 0 : blargg_dprintf_ + +// If expr is false, prints file and line number to debug console/log, then +// continues execution normally. Meant for flagging potential problems or things +// that should be looked into, but that aren't serious problems. +// +// void check( bool expr ); +#undef check +#define check( expr ) ((void) 0) + +// If expr yields non-NULL error string, returns it from current function, +// otherwise continues normally. +#undef RETURN_ERR +#define RETURN_ERR( expr ) do { \ + blargg_err_t blargg_return_err_ = (expr); \ + if ( blargg_return_err_ ) return blargg_return_err_; \ + } while ( 0 ) + +// If ptr is NULL, returns "Out of memory" error string, otherwise continues normally. +#undef CHECK_ALLOC +#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 ) + +// The usual min/max functions for built-in types. +// +// template T min( T x, T y ) { return x < y ? x : y; } +// template T max( T x, T y ) { return x > y ? x : y; } +#define BLARGG_DEF_MIN_MAX( type ) \ + static inline type blargg_min( type x, type y ) { if ( y < x ) x = y; return x; }\ + static inline type blargg_max( type x, type y ) { if ( x < y ) x = y; return x; } + +BLARGG_DEF_MIN_MAX( int ) +BLARGG_DEF_MIN_MAX( unsigned ) +BLARGG_DEF_MIN_MAX( long ) +BLARGG_DEF_MIN_MAX( unsigned long ) +BLARGG_DEF_MIN_MAX( float ) +BLARGG_DEF_MIN_MAX( double ) + +#undef min +#define min blargg_min + +#undef max +#define max blargg_max + +// typedef unsigned char byte; +typedef unsigned char blargg_byte; +#undef byte +#define byte blargg_byte + +// deprecated +#define BLARGG_CHECK_ALLOC CHECK_ALLOC +#define BLARGG_RETURN_ERR RETURN_ERR + +// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf and check +#ifdef BLARGG_SOURCE_BEGIN + #include BLARGG_SOURCE_BEGIN +#endif + +#endif