Removed file/folder
This commit is contained in:
parent
c19478fd2e
commit
52e6dae36f
2966
src/agb/GBA-arm.cpp
2966
src/agb/GBA-arm.cpp
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
3983
src/agb/GBA.cpp
3983
src/agb/GBA.cpp
File diff suppressed because it is too large
Load Diff
160
src/agb/GBA.h
160
src/agb/GBA.h
|
@ -1,160 +0,0 @@
|
|||
// -*- 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
|
|
@ -1,47 +0,0 @@
|
|||
// 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;
|
1602
src/agb/GBAGfx.h
1602
src/agb/GBAGfx.h
File diff suppressed because it is too large
Load Diff
1083
src/agb/GBALink.cpp
1083
src/agb/GBALink.cpp
File diff suppressed because it is too large
Load Diff
|
@ -1,124 +0,0 @@
|
|||
// 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<winsock.h>
|
||||
|
||||
#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
|
302
src/agb/GBAcpu.h
302
src/agb/GBAcpu.h
|
@ -1,302 +0,0 @@
|
|||
// -*- 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)<<waitState) - 1;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
inline int dataTicksAccess32(u32 address) // DATA 32bits NON SEQ
|
||||
{
|
||||
int addr = (address>>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)<<waitState) - 1;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
inline int dataTicksAccessSeq16(u32 address)// DATA 8/16bits SEQ
|
||||
{
|
||||
int addr = (address>>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)<<waitState) - 1;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
inline int dataTicksAccessSeq32(u32 address)// DATA 32bits SEQ
|
||||
{
|
||||
int addr = (address>>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)<<waitState) - 1;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
// Waitstates when executing opcode
|
||||
inline int codeTicksAccess16(u32 address) // THUMB 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 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
|
|
@ -1,739 +0,0 @@
|
|||
// -*- 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
|
|
@ -1,99 +0,0 @@
|
|||
// 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 <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// -*- 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
|
|
@ -1,227 +0,0 @@
|
|||
#include "gbafilter.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
*/
|
|
@ -1,5 +0,0 @@
|
|||
#include "../System.h"
|
||||
|
||||
void gbafilter_pal(u16 * buf, int count);
|
||||
void gbafilter_pal32(u32 * buf, int count);
|
||||
void gbafilter_pad(u8 * buf, int count);
|
5455
src/dmg/GB.cpp
5455
src/dmg/GB.cpp
File diff suppressed because it is too large
Load Diff
64
src/dmg/gb.h
64
src/dmg/gb.h
|
@ -1,64 +0,0 @@
|
|||
// -*- 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
|
|
@ -1,522 +0,0 @@
|
|||
// 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 <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
// -*- 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
|
||||
|
1460
src/dmg/gbCodes.h
1460
src/dmg/gbCodes.h
File diff suppressed because it is too large
Load Diff
1291
src/dmg/gbCodesCB.h
1291
src/dmg/gbCodesCB.h
File diff suppressed because it is too large
Load Diff
|
@ -1,249 +0,0 @@
|
|||
// 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 <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
|
@ -1,742 +0,0 @@
|
|||
// 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 <memory.h>
|
||||
#include "../Brunni/common.h"
|
||||
#include "../agb/GBA.h"
|
||||
#include "gbGlobals.h"
|
||||
#include "gbSGB.h"
|
||||
|
||||
/** BRUNNI ADDED CODE **/
|
||||
int gblModeColorIt=0;
|
||||
byte tilePalList[384];
|
||||
short tilePalPalette[NB_PALETTES][4];
|
||||
byte tilePalCrc[384 / 8];
|
||||
short tileMapTileList[384];
|
||||
int gb_lastVramCrc;
|
||||
u8 *gbExternalVram;
|
||||
|
||||
void tilePalReinit() {
|
||||
int i;
|
||||
memset(tilePalList, 0, sizeof(tilePalList));
|
||||
for (i=0;i<numberof(tileMapTileList);i++)
|
||||
tileMapTileList[i] = i;
|
||||
}
|
||||
|
||||
void tilePalInit() {
|
||||
memset(tilePalCrc, 0, sizeof(tilePalCrc));
|
||||
gb_lastVramCrc = 0;
|
||||
}
|
||||
|
||||
int tileMapGetVramChecksum() {
|
||||
int i;
|
||||
u32 crc = 0;
|
||||
u8 *vram = &gbMemory[0x8000];
|
||||
|
||||
for (i=0;i<384;i++) {
|
||||
if (tilePalCrc[i >> 3] & (1 << (i & 7)))
|
||||
crc = crc32(crc, vram + i * 16, 16);
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
void exiting_lcdc() {
|
||||
//Do nothing in GBC mode (VBA uses another VRAM address)
|
||||
if (!gblModeColorIt || gbCgbMode)
|
||||
return;
|
||||
|
||||
u32 crc = tileMapGetVramChecksum();
|
||||
if (crc != gb_lastVramCrc /*|| gblConfigAutoShowCrc*/) {
|
||||
OuvreFichierPalette(crc, NULL);
|
||||
if (gblConfigAutoShowCrc) {
|
||||
char temp[256];
|
||||
sprintf(temp, "New VRAM CRC: %08x", crc);
|
||||
systemScreenMessage(temp);
|
||||
}
|
||||
gb_lastVramCrc = crc;
|
||||
}
|
||||
}
|
||||
|
||||
/** END BRUNNI **/
|
||||
|
||||
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;
|
||||
//Brunni/
|
||||
u8 * externalVram = NULL;
|
||||
if(gbCgbMode) {
|
||||
bank0 = &gbVram[0x0000];
|
||||
bank1 = &gbVram[0x2000];
|
||||
} else {
|
||||
bank0 = &gbMemory[0x8000];
|
||||
bank1 = NULL;
|
||||
//Brunni/ Additional memory for custom tiles - we should have done just like the GBC, two banks
|
||||
if (gblModeColorIt)
|
||||
externalVram = getGbExtVramAddr();
|
||||
}
|
||||
|
||||
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;
|
||||
u8 colorItPaletteNb = 0, colorItUseExtVram = 0;
|
||||
|
||||
//Brunni/ Pattern change (custom tile)
|
||||
if (gblModeColorIt) {
|
||||
//Brunni/ Get the tile number (+128 if LCDC bit4 is zero)
|
||||
s16 tilenb = (tile_pattern >> 4) + tile;
|
||||
colorItPaletteNb = tilePalList[tilenb];
|
||||
|
||||
//Brunni/ Select the associated custom tile
|
||||
tilenb = tileMapTileList[tilenb];
|
||||
|
||||
//Brunni/ Does it use additional GB vram? ("bank 1")
|
||||
if (tilenb >= 512)
|
||||
colorItUseExtVram = 1, tilenb -= 512;
|
||||
|
||||
//Brunni/ Nothing to do => return to the normal range (within current bank)
|
||||
tilenb -= (tile_pattern >> 4);
|
||||
tile_pattern_address = tile_pattern + tilenb * 16 + by * 2;
|
||||
}
|
||||
|
||||
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 if (colorItUseExtVram) {
|
||||
//Brunni/ "bank 1" (additional GB vram)
|
||||
tile_a = externalVram[tile_pattern_address++];
|
||||
tile_b = externalVram[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;
|
||||
}
|
||||
|
||||
//Brunni/ Find the right palette
|
||||
if (gblModeColorIt)
|
||||
c += 4 * colorItPaletteNb;
|
||||
}
|
||||
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<swx; i++)
|
||||
gbLineMix[i] = gbWindowColor[i];
|
||||
|
||||
while(x < 160) {
|
||||
u8 tile_a = 0;
|
||||
u8 tile_b = 0;
|
||||
u8 colorItPaletteNb = 0, colorItUseExtVram = 0;
|
||||
|
||||
//Brunni/ Define the actual palette and custom pattern for this tile
|
||||
if (gblModeColorIt) {
|
||||
//Brunni/ Get the tile number (+128 if LCDC bit4 is zero)
|
||||
s16 tilenb = (tile_pattern >> 4) + tile;
|
||||
colorItPaletteNb = tilePalList[tilenb];
|
||||
|
||||
//Brunni/ Select the associated custom tile
|
||||
tilenb = tileMapTileList[tilenb];
|
||||
|
||||
//Brunni/ Does it use additional GB vram? ("bank 1")
|
||||
if (tilenb >= 512)
|
||||
colorItUseExtVram = 1, tilenb -= 512;
|
||||
|
||||
//Brunni/ Nothing to do => return to the normal range (within current bank)
|
||||
tilenb -= (tile_pattern >> 4);
|
||||
tile_pattern_address = tile_pattern + tilenb * 16 + by * 2;
|
||||
}
|
||||
|
||||
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 if (colorItUseExtVram) {
|
||||
//Brunni/ "bank 1" (additional GB vram)
|
||||
tile_a = externalVram[tile_pattern_address++];
|
||||
tile_b = externalVram[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) != 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;
|
||||
}
|
||||
//Brunni/ Find the right palette
|
||||
if (gblModeColorIt)
|
||||
c += 4 * colorItPaletteNb;
|
||||
}
|
||||
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;
|
||||
//Brunni/
|
||||
u8 * externalVram = NULL;
|
||||
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;
|
||||
if (gblModeColorIt)
|
||||
externalVram = getGbExtVramAddr();
|
||||
}
|
||||
|
||||
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;
|
||||
u8 colorItPaletteNb = 0, colorItUseExtVram = 0;
|
||||
|
||||
//Brunni/ Define the actual palette and custom pattern for this tile
|
||||
if (gblModeColorIt) {
|
||||
//Brunni/ Get the tile number (+128 if LCDC bit4 is zero)
|
||||
colorItPaletteNb = tilePalList[tile];
|
||||
//Brunni/ Associated custom tile
|
||||
tile = tileMapTileList[tile];
|
||||
//Brunni/ Same way as BGs => additional GB vram
|
||||
if (tile >= 512)
|
||||
colorItUseExtVram = 1, tile -= 512;
|
||||
address = init + tile * 16 + 2*t;
|
||||
}
|
||||
|
||||
if(gbCgbMode && (flags & 0x08)) {
|
||||
a = bank1[address++];
|
||||
b = bank1[address++];
|
||||
} else if (colorItUseExtVram) {
|
||||
//Brunni/ "bank 1" (additional GB vram)
|
||||
a = externalVram[address++];
|
||||
b = externalVram[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 {
|
||||
//Brunni/ Set the color palette
|
||||
if (gblModeColorIt)
|
||||
c += 4 * colorItPaletteNb;
|
||||
else
|
||||
//Brunni/ Normal GB behaviour (first 4 colors for the BG, etc.)
|
||||
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++;
|
||||
} else if(!size && t >= 0 && t < 8) {
|
||||
gbDrawSpriteTile(tile, x-8, yc, t, flags,size,i);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
// sprite limit reached!
|
||||
if(count >= 10)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
// 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;
|
|
@ -1,93 +0,0 @@
|
|||
// -*- 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;
|
||||
//Brunni/ Used for custom tiles
|
||||
extern u8 *gbExternalVram;
|
||||
|
||||
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);
|
||||
//Brunni/ Needs to be called when the screen switches from "blank" to "normal"
|
||||
extern void exiting_lcdc();
|
||||
|
||||
extern u8 (*gbSerialFunction)(u8);
|
1717
src/dmg/gbMemory.cpp
1717
src/dmg/gbMemory.cpp
File diff suppressed because it is too large
Load Diff
|
@ -1,206 +0,0 @@
|
|||
// -*- 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 <time.h>
|
||||
|
||||
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();
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,229 +0,0 @@
|
|||
// 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 <stdio.h>
|
||||
#include <memory.h>
|
||||
#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;
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
// -*- 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);
|
|
@ -1,917 +0,0 @@
|
|||
// 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 <stdlib.h>
|
||||
#include <memory.h>
|
||||
|
||||
#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);
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
// -*- 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;
|
||||
|
||||
|
|
@ -1,412 +0,0 @@
|
|||
// 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 <memory.h>
|
||||
|
||||
#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 );
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
// -*- 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;
|
|
@ -1,465 +0,0 @@
|
|||
// Blip_Buffer 0.4.1. http://www.slack.net/~ant/
|
||||
|
||||
#include "Blip_Buffer.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.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 */
|
||||
|
||||
// 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 );
|
||||
}
|
|
@ -1,556 +0,0 @@
|
|||
// Band-limited sound synthesis buffer
|
||||
|
||||
// Blip_Buffer 0.4.1
|
||||
#ifndef BLIP_BUFFER_H
|
||||
#define BLIP_BUFFER_H
|
||||
|
||||
// internal
|
||||
#include <limits.h>
|
||||
#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<int quality,int range>
|
||||
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 <assert.h>
|
||||
#endif
|
||||
|
||||
template<int quality,int range>
|
||||
inline void Blip_Synth<quality,range>::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<int quality,int range>
|
||||
#if BLIP_BUFFER_FAST
|
||||
inline
|
||||
#endif
|
||||
void Blip_Synth<quality,range>::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const
|
||||
{
|
||||
offset_resampled( t * buf->factor_ + buf->offset_, delta, buf );
|
||||
}
|
||||
|
||||
template<int quality,int range>
|
||||
#if BLIP_BUFFER_FAST
|
||||
inline
|
||||
#endif
|
||||
void Blip_Synth<quality,range>::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
|
|
@ -1,638 +0,0 @@
|
|||
// Game_Music_Emu $vers. http://www.slack.net/~ant/
|
||||
|
||||
#include "Effects_Buffer.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* 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 );
|
||||
}
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
// 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<chan_t> 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<fixed_t> 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
|
|
@ -1,394 +0,0 @@
|
|||
// 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;
|
||||
}
|
|
@ -1,182 +0,0 @@
|
|||
// 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
|
|
@ -1,118 +0,0 @@
|
|||
// Gb_Snd_Emu $vers. http://www.slack.net/~ant/
|
||||
|
||||
#include "Gb_Apu.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* 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;
|
||||
}
|
|
@ -1,665 +0,0 @@
|
|||
// 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;
|
||||
}
|
|
@ -1,190 +0,0 @@
|
|||
// 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<blip_good_quality,1> Good_Synth;
|
||||
typedef Blip_Synth<blip_med_quality ,1> 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
|
|
@ -1,281 +0,0 @@
|
|||
// 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;
|
||||
}
|
||||
}
|
|
@ -1,205 +0,0 @@
|
|||
// 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
|
|
@ -1,206 +0,0 @@
|
|||
// 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 <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#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<T> (expr)
|
||||
// CONST_CAST( T,expr): Used in place of const_cast<T> (expr)
|
||||
#ifndef STATIC_CAST
|
||||
#if __GNUC__ >= 4
|
||||
#define STATIC_CAST(T,expr) static_cast<T> (expr)
|
||||
#define CONST_CAST( T,expr) const_cast<T> (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 T>
|
||||
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 <new>
|
||||
#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 <stdint.h> for int8_t etc.
|
||||
#if defined (HAVE_STDINT_H)
|
||||
#include <stdint.h>
|
||||
#define BOOST
|
||||
|
||||
// HAVE_INTTYPES_H: If defined, use <stdint.h> for int8_t etc.
|
||||
#elif defined (HAVE_INTTYPES_H)
|
||||
#include <inttypes.h>
|
||||
#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
|
|
@ -1,33 +0,0 @@
|
|||
// $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 <memory>
|
||||
//#define BLARGG_NEW new (std::nothrow)
|
||||
|
||||
// Use standard config.h if present
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,92 +0,0 @@
|
|||
/* 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 <assert.h>
|
||||
|
||||
// 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<typename T> T min( T x, T y ) { return x < y ? x : y; }
|
||||
// template<typename T> 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
|
Loading…
Reference in New Issue