This commit is contained in:
parent
3411dd3094
commit
5c8fa49d23
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,160 @@
|
|||
// -*- C++ -*-
|
||||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2005 Forgotten and the VBA development team
|
||||
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2, or(at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#ifndef VBA_GBA_H
|
||||
#define VBA_GBA_H
|
||||
|
||||
#include "../System.h"
|
||||
|
||||
#define SAVE_GAME_VERSION_1 1
|
||||
#define SAVE_GAME_VERSION_2 2
|
||||
#define SAVE_GAME_VERSION_3 3
|
||||
#define SAVE_GAME_VERSION_4 4
|
||||
#define SAVE_GAME_VERSION_5 5
|
||||
#define SAVE_GAME_VERSION_6 6
|
||||
#define SAVE_GAME_VERSION_7 7
|
||||
#define SAVE_GAME_VERSION_8 8
|
||||
#define SAVE_GAME_VERSION_9 9
|
||||
#define SAVE_GAME_VERSION_10 10
|
||||
#define SAVE_GAME_VERSION SAVE_GAME_VERSION_10
|
||||
|
||||
typedef struct {
|
||||
u8 *address;
|
||||
u32 mask;
|
||||
} memoryMap;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
u8 B3;
|
||||
u8 B2;
|
||||
u8 B1;
|
||||
u8 B0;
|
||||
#else
|
||||
u8 B0;
|
||||
u8 B1;
|
||||
u8 B2;
|
||||
u8 B3;
|
||||
#endif
|
||||
} B;
|
||||
struct {
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
u16 W1;
|
||||
u16 W0;
|
||||
#else
|
||||
u16 W0;
|
||||
u16 W1;
|
||||
#endif
|
||||
} W;
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
volatile u32 I;
|
||||
#else
|
||||
u32 I;
|
||||
#endif
|
||||
} reg_pair;
|
||||
|
||||
#ifndef NO_GBA_MAP
|
||||
extern memoryMap map[256];
|
||||
#endif
|
||||
|
||||
extern reg_pair reg[45];
|
||||
extern u8 biosProtected[4];
|
||||
|
||||
extern bool N_FLAG;
|
||||
extern bool Z_FLAG;
|
||||
extern bool C_FLAG;
|
||||
extern bool V_FLAG;
|
||||
extern bool armIrqEnable;
|
||||
extern bool armState;
|
||||
extern int armMode;
|
||||
extern void (*cpuSaveGameFunc)(u32,u8);
|
||||
|
||||
#ifdef BKPT_SUPPORT
|
||||
extern u8 freezeWorkRAM[0x40000];
|
||||
extern u8 freezeInternalRAM[0x8000];
|
||||
extern u8 freezeVRAM[0x18000];
|
||||
extern u8 freezeOAM[0x400];
|
||||
extern u8 freezePRAM[0x400];
|
||||
extern bool debugger_last;
|
||||
extern int oldreg[17];
|
||||
extern char oldbuffer[10];
|
||||
#endif
|
||||
|
||||
extern bool CPUReadGSASnapshot(const char *);
|
||||
extern bool CPUWriteGSASnapshot(const char *, const char *, const char *, const char *);
|
||||
extern bool CPUWriteBatteryFile(const char *);
|
||||
extern bool CPUReadBatteryFile(const char *);
|
||||
extern bool CPUExportEepromFile(const char *);
|
||||
extern bool CPUImportEepromFile(const char *);
|
||||
extern bool CPUWritePNGFile(const char *);
|
||||
extern bool CPUWriteBMPFile(const char *);
|
||||
extern void CPUCleanUp();
|
||||
extern void CPUUpdateRender();
|
||||
extern void CPUUpdateRenderBuffers(bool);
|
||||
extern bool CPUReadMemState(char *, int);
|
||||
extern bool CPUReadState(const char *);
|
||||
extern bool CPUWriteMemState(char *, int);
|
||||
extern bool CPUWriteState(const char *);
|
||||
extern int CPULoadRom(const char *);
|
||||
extern void doMirroring(bool);
|
||||
extern void CPUUpdateRegister(u32, u16);
|
||||
extern void applyTimer ();
|
||||
extern void CPUInit(const char *,bool);
|
||||
extern void CPUReset();
|
||||
extern void CPULoop(int);
|
||||
extern void CPUCheckDMA(int,int);
|
||||
extern bool CPUIsGBAImage(const char *);
|
||||
extern bool CPUIsZipFile(const char *);
|
||||
#ifdef PROFILING
|
||||
#include "prof/prof.h"
|
||||
extern void cpuProfil(profile_segment *seg);
|
||||
extern void cpuEnableProfiling(int hz);
|
||||
#endif
|
||||
|
||||
extern struct EmulatedSystem GBASystem;
|
||||
|
||||
#define R13_IRQ 18
|
||||
#define R14_IRQ 19
|
||||
#define SPSR_IRQ 20
|
||||
#define R13_USR 26
|
||||
#define R14_USR 27
|
||||
#define R13_SVC 28
|
||||
#define R14_SVC 29
|
||||
#define SPSR_SVC 30
|
||||
#define R13_ABT 31
|
||||
#define R14_ABT 32
|
||||
#define SPSR_ABT 33
|
||||
#define R13_UND 34
|
||||
#define R14_UND 35
|
||||
#define SPSR_UND 36
|
||||
#define R8_FIQ 37
|
||||
#define R9_FIQ 38
|
||||
#define R10_FIQ 39
|
||||
#define R11_FIQ 40
|
||||
#define R12_FIQ 41
|
||||
#define R13_FIQ 42
|
||||
#define R14_FIQ 43
|
||||
#define SPSR_FIQ 44
|
||||
|
||||
#include "../Cheats.h"
|
||||
#include "../Globals.h"
|
||||
#include "../EEprom.h"
|
||||
#include "../Flash.h"
|
||||
|
||||
#endif //VBA_GBA_H
|
|
@ -0,0 +1,47 @@
|
|||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2004 Forgotten and the VBA development team
|
||||
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2, or(at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include "../System.h"
|
||||
|
||||
int coeff[32] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16};
|
||||
|
||||
u32 line0[240];
|
||||
u32 line1[240];
|
||||
u32 line2[240];
|
||||
u32 line3[240];
|
||||
u32 lineOBJ[240];
|
||||
u32 lineOBJWin[240];
|
||||
u32 lineMix[240];
|
||||
bool gfxInWin0[240];
|
||||
bool gfxInWin1[240];
|
||||
int lineOBJpixleft[128];
|
||||
|
||||
int gfxBG2Changed = 0;
|
||||
int gfxBG3Changed = 0;
|
||||
|
||||
int gfxBG2X = 0;
|
||||
int gfxBG2Y = 0;
|
||||
int gfxBG2LastX = 0;
|
||||
int gfxBG2LastY = 0;
|
||||
int gfxBG3X = 0;
|
||||
int gfxBG3Y = 0;
|
||||
int gfxBG3LastX = 0;
|
||||
int gfxBG3LastY = 0;
|
||||
int gfxLastVCOUNT = 0;
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,124 @@
|
|||
// 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
|
|
@ -0,0 +1,302 @@
|
|||
// -*- C++ -*-
|
||||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2005 Forgotten and the VBA development team
|
||||
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2, or(at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#ifndef VBA_GBAcpu_H
|
||||
#define VBA_GBAcpu_H
|
||||
|
||||
extern int armExecute();
|
||||
extern int thumbExecute();
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define INSN_REGPARM __attribute__((regparm(1)))
|
||||
# define LIKELY(x) __builtin_expect(!!(x),1)
|
||||
# define UNLIKELY(x) __builtin_expect(!!(x),0)
|
||||
#else
|
||||
# define INSN_REGPARM /*nothing*/
|
||||
# define LIKELY(x) (x)
|
||||
# define UNLIKELY(x) (x)
|
||||
#endif
|
||||
|
||||
#define UPDATE_REG(address, value)\
|
||||
{\
|
||||
WRITE16LE(((u16 *)&ioMem[address]),value);\
|
||||
}\
|
||||
|
||||
#define ARM_PREFETCH \
|
||||
{\
|
||||
cpuPrefetch[0] = CPUReadMemoryQuick(armNextPC);\
|
||||
cpuPrefetch[1] = CPUReadMemoryQuick(armNextPC+4);\
|
||||
}
|
||||
|
||||
#define THUMB_PREFETCH \
|
||||
{\
|
||||
cpuPrefetch[0] = CPUReadHalfWordQuick(armNextPC);\
|
||||
cpuPrefetch[1] = CPUReadHalfWordQuick(armNextPC+2);\
|
||||
}
|
||||
|
||||
#define ARM_PREFETCH_NEXT \
|
||||
cpuPrefetch[1] = CPUReadMemoryQuick(armNextPC+4);
|
||||
|
||||
#define THUMB_PREFETCH_NEXT\
|
||||
cpuPrefetch[1] = CPUReadHalfWordQuick(armNextPC+2);
|
||||
|
||||
|
||||
extern int SWITicks;
|
||||
extern u32 mastercode;
|
||||
extern bool busPrefetch;
|
||||
extern bool busPrefetchEnable;
|
||||
extern u32 busPrefetchCount;
|
||||
extern int cpuNextEvent;
|
||||
extern bool holdState;
|
||||
extern u32 cpuPrefetch[2];
|
||||
extern int cpuTotalTicks;
|
||||
extern u8 memoryWait[16];
|
||||
extern u8 memoryWait32[16];
|
||||
extern u8 memoryWaitSeq[16];
|
||||
extern u8 memoryWaitSeq32[16];
|
||||
extern u8 cpuBitsSet[256];
|
||||
extern u8 cpuLowestBitSet[256];
|
||||
extern void CPUSwitchMode(int mode, bool saveState, bool breakLoop);
|
||||
extern void CPUSwitchMode(int mode, bool saveState);
|
||||
extern void CPUUpdateCPSR();
|
||||
extern void CPUUpdateFlags(bool breakLoop);
|
||||
extern void CPUUpdateFlags();
|
||||
extern void CPUUndefinedException();
|
||||
extern void CPUSoftwareInterrupt();
|
||||
extern void CPUSoftwareInterrupt(int comment);
|
||||
|
||||
|
||||
// Waitstates when accessing data
|
||||
inline int dataTicksAccess16(u32 address) // DATA 8/16bits NON SEQ
|
||||
{
|
||||
int addr = (address>>24)&15;
|
||||
int value = memoryWait[addr];
|
||||
|
||||
if ((addr>=0x08) || (addr < 0x02))
|
||||
{
|
||||
busPrefetchCount=0;
|
||||
busPrefetch=false;
|
||||
}
|
||||
else if (busPrefetch)
|
||||
{
|
||||
int waitState = value;
|
||||
if (!waitState)
|
||||
waitState = 1;
|
||||
busPrefetchCount = ((busPrefetchCount+1)<<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
|
|
@ -0,0 +1,739 @@
|
|||
// -*- C++ -*-
|
||||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2005 Forgotten and the VBA development team
|
||||
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2, or(at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#ifndef VBA_GBAinline_H
|
||||
#define VBA_GBAinline_H
|
||||
|
||||
#include "../System.h"
|
||||
#include "../Port.h"
|
||||
#include "../RTC.h"
|
||||
#include "../Sound.h"
|
||||
#include "agbprint.h"
|
||||
|
||||
extern const u32 objTilesAddress[3];
|
||||
|
||||
extern bool stopState;
|
||||
extern bool holdState;
|
||||
extern int holdType;
|
||||
extern int cpuNextEvent;
|
||||
extern bool cpuSramEnabled;
|
||||
extern bool cpuFlashEnabled;
|
||||
extern bool cpuEEPROMEnabled;
|
||||
extern bool cpuEEPROMSensorEnabled;
|
||||
extern bool cpuDmaHack;
|
||||
extern u32 cpuDmaLast;
|
||||
extern bool timer0On;
|
||||
extern int timer0Ticks;
|
||||
extern int timer0ClockReload;
|
||||
extern bool timer1On;
|
||||
extern int timer1Ticks;
|
||||
extern int timer1ClockReload;
|
||||
extern bool timer2On;
|
||||
extern int timer2Ticks;
|
||||
extern int timer2ClockReload;
|
||||
extern bool timer3On;
|
||||
extern int timer3Ticks;
|
||||
extern int timer3ClockReload;
|
||||
extern int cpuTotalTicks;
|
||||
|
||||
#define CPUReadByteQuick(addr) \
|
||||
map[(addr)>>24].address[(addr) & map[(addr)>>24].mask]
|
||||
|
||||
#define CPUReadHalfWordQuick(addr) \
|
||||
READ16LE(((u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask]))
|
||||
|
||||
#define CPUReadMemoryQuick(addr) \
|
||||
READ32LE(((u32*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask]))
|
||||
|
||||
static inline u32 CPUReadMemory(u32 address)
|
||||
{
|
||||
|
||||
#ifdef GBA_LOGGING
|
||||
if(address & 3) {
|
||||
if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) {
|
||||
log("Unaligned word read: %08x at %08x\n", address, armMode ?
|
||||
armNextPC - 4 : armNextPC - 2);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
u32 value;
|
||||
switch(address >> 24) {
|
||||
case 0:
|
||||
if(reg[15].I >> 24) {
|
||||
if(address < 0x4000) {
|
||||
#ifdef GBA_LOGGING
|
||||
if(systemVerbose & VERBOSE_ILLEGAL_READ) {
|
||||
log("Illegal word read: %08x at %08x\n", address, armMode ?
|
||||
armNextPC - 4 : armNextPC - 2);
|
||||
}
|
||||
#endif
|
||||
|
||||
value = READ32LE(((u32 *)&biosProtected));
|
||||
}
|
||||
else goto unreadable;
|
||||
} else
|
||||
value = READ32LE(((u32 *)&bios[address & 0x3FFC]));
|
||||
break;
|
||||
case 2:
|
||||
value = READ32LE(((u32 *)&workRAM[address & 0x3FFFC]));
|
||||
break;
|
||||
case 3:
|
||||
value = READ32LE(((u32 *)&internalRAM[address & 0x7ffC]));
|
||||
break;
|
||||
case 4:
|
||||
if((address < 0x4000400) && ioReadable[address & 0x3fc]) {
|
||||
if(ioReadable[(address & 0x3fc) + 2])
|
||||
value = READ32LE(((u32 *)&ioMem[address & 0x3fC]));
|
||||
else
|
||||
value = READ16LE(((u16 *)&ioMem[address & 0x3fc]));
|
||||
} else goto unreadable;
|
||||
break;
|
||||
case 5:
|
||||
value = READ32LE(((u32 *)&paletteRAM[address & 0x3fC]));
|
||||
break;
|
||||
case 6:
|
||||
address = (address & 0x1fffc);
|
||||
if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000))
|
||||
{
|
||||
value = 0;
|
||||
break;
|
||||
}
|
||||
if ((address & 0x18000) == 0x18000)
|
||||
address &= 0x17fff;
|
||||
value = READ32LE(((u32 *)&vram[address]));
|
||||
break;
|
||||
case 7:
|
||||
value = READ32LE(((u32 *)&oam[address & 0x3FC]));
|
||||
break;
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
value = READ32LE(((u32 *)&rom[address&0x1FFFFFC]));
|
||||
break;
|
||||
case 13:
|
||||
if(cpuEEPROMEnabled)
|
||||
// no need to swap this
|
||||
return eepromRead(address);
|
||||
goto unreadable;
|
||||
case 14:
|
||||
if(cpuFlashEnabled | cpuSramEnabled)
|
||||
// no need to swap this
|
||||
return flashRead(address);
|
||||
// default
|
||||
default:
|
||||
unreadable:
|
||||
#ifdef GBA_LOGGING
|
||||
if(systemVerbose & VERBOSE_ILLEGAL_READ) {
|
||||
log("Illegal word read: %08x at %08x\n", address, armMode ?
|
||||
armNextPC - 4 : armNextPC - 2);
|
||||
}
|
||||
#endif
|
||||
|
||||
if(cpuDmaHack) {
|
||||
value = cpuDmaLast;
|
||||
} else {
|
||||
if(armState) {
|
||||
value = CPUReadMemoryQuick(reg[15].I);
|
||||
} else {
|
||||
value = CPUReadHalfWordQuick(reg[15].I) |
|
||||
CPUReadHalfWordQuick(reg[15].I) << 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(address & 3) {
|
||||
#ifdef C_CORE
|
||||
int shift = (address & 3) << 3;
|
||||
value = (value >> shift) | (value << (32 - shift));
|
||||
#else
|
||||
#ifdef __GNUC__
|
||||
asm("and $3, %%ecx;"
|
||||
"shl $3 ,%%ecx;"
|
||||
"ror %%cl, %0"
|
||||
: "=r" (value)
|
||||
: "r" (value), "c" (address));
|
||||
#else
|
||||
__asm {
|
||||
mov ecx, address;
|
||||
and ecx, 3;
|
||||
shl ecx, 3;
|
||||
ror [dword ptr value], cl;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
extern u32 myROM[];
|
||||
|
||||
static inline u32 CPUReadHalfWord(u32 address)
|
||||
{
|
||||
#ifdef GBA_LOGGING
|
||||
if(address & 1) {
|
||||
if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) {
|
||||
log("Unaligned halfword read: %08x at %08x\n", address, armMode ?
|
||||
armNextPC - 4 : armNextPC - 2);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
u32 value;
|
||||
|
||||
switch(address >> 24) {
|
||||
case 0:
|
||||
if (reg[15].I >> 24) {
|
||||
if(address < 0x4000) {
|
||||
#ifdef GBA_LOGGING
|
||||
if(systemVerbose & VERBOSE_ILLEGAL_READ) {
|
||||
log("Illegal halfword read: %08x at %08x\n", address, armMode ?
|
||||
armNextPC - 4 : armNextPC - 2);
|
||||
}
|
||||
#endif
|
||||
value = READ16LE(((u16 *)&biosProtected[address&2]));
|
||||
} else goto unreadable;
|
||||
} else
|
||||
value = READ16LE(((u16 *)&bios[address & 0x3FFE]));
|
||||
break;
|
||||
case 2:
|
||||
value = READ16LE(((u16 *)&workRAM[address & 0x3FFFE]));
|
||||
break;
|
||||
case 3:
|
||||
value = READ16LE(((u16 *)&internalRAM[address & 0x7ffe]));
|
||||
break;
|
||||
case 4:
|
||||
if((address < 0x4000400) && ioReadable[address & 0x3fe])
|
||||
{
|
||||
value = READ16LE(((u16 *)&ioMem[address & 0x3fe]));
|
||||
if (((address & 0x3fe)>0xFF) && ((address & 0x3fe)<0x10E))
|
||||
{
|
||||
if (((address & 0x3fe) == 0x100) && timer0On)
|
||||
value = 0xFFFF - ((timer0Ticks-cpuTotalTicks) >> timer0ClockReload);
|
||||
else
|
||||
if (((address & 0x3fe) == 0x104) && timer1On && !(TM1CNT & 4))
|
||||
value = 0xFFFF - ((timer1Ticks-cpuTotalTicks) >> timer1ClockReload);
|
||||
else
|
||||
if (((address & 0x3fe) == 0x108) && timer2On && !(TM2CNT & 4))
|
||||
value = 0xFFFF - ((timer2Ticks-cpuTotalTicks) >> timer2ClockReload);
|
||||
else
|
||||
if (((address & 0x3fe) == 0x10C) && timer3On && !(TM3CNT & 4))
|
||||
value = 0xFFFF - ((timer3Ticks-cpuTotalTicks) >> timer3ClockReload);
|
||||
}
|
||||
}
|
||||
else goto unreadable;
|
||||
break;
|
||||
case 5:
|
||||
value = READ16LE(((u16 *)&paletteRAM[address & 0x3fe]));
|
||||
break;
|
||||
case 6:
|
||||
address = (address & 0x1fffe);
|
||||
if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000))
|
||||
{
|
||||
value = 0;
|
||||
break;
|
||||
}
|
||||
if ((address & 0x18000) == 0x18000)
|
||||
address &= 0x17fff;
|
||||
value = READ16LE(((u16 *)&vram[address]));
|
||||
break;
|
||||
case 7:
|
||||
value = READ16LE(((u16 *)&oam[address & 0x3fe]));
|
||||
break;
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8)
|
||||
value = rtcRead(address);
|
||||
else
|
||||
value = READ16LE(((u16 *)&rom[address & 0x1FFFFFE]));
|
||||
break;
|
||||
case 13:
|
||||
if(cpuEEPROMEnabled)
|
||||
// no need to swap this
|
||||
return eepromRead(address);
|
||||
goto unreadable;
|
||||
case 14:
|
||||
if(cpuFlashEnabled | cpuSramEnabled)
|
||||
// no need to swap this
|
||||
return flashRead(address);
|
||||
// default
|
||||
default:
|
||||
unreadable:
|
||||
#ifdef GBA_LOGGING
|
||||
if(systemVerbose & VERBOSE_ILLEGAL_READ) {
|
||||
log("Illegal halfword read: %08x at %08x\n", address, armMode ?
|
||||
armNextPC - 4 : armNextPC - 2);
|
||||
}
|
||||
#endif
|
||||
if(cpuDmaHack) {
|
||||
value = cpuDmaLast & 0xFFFF;
|
||||
} else {
|
||||
if(armState) {
|
||||
value = CPUReadHalfWordQuick(reg[15].I + (address & 2));
|
||||
} else {
|
||||
value = CPUReadHalfWordQuick(reg[15].I);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(address & 1) {
|
||||
value = (value >> 8) | (value << 24);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline u16 CPUReadHalfWordSigned(u32 address)
|
||||
{
|
||||
u16 value = CPUReadHalfWord(address);
|
||||
if((address & 1))
|
||||
value = (s8)value;
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline u8 CPUReadByte(u32 address)
|
||||
{
|
||||
switch(address >> 24) {
|
||||
case 0:
|
||||
if (reg[15].I >> 24) {
|
||||
if(address < 0x4000) {
|
||||
#ifdef GBA_LOGGING
|
||||
if(systemVerbose & VERBOSE_ILLEGAL_READ) {
|
||||
log("Illegal byte read: %08x at %08x\n", address, armMode ?
|
||||
armNextPC - 4 : armNextPC - 2);
|
||||
}
|
||||
#endif
|
||||
return biosProtected[address & 3];
|
||||
} else goto unreadable;
|
||||
}
|
||||
return bios[address & 0x3FFF];
|
||||
case 2:
|
||||
return workRAM[address & 0x3FFFF];
|
||||
case 3:
|
||||
return internalRAM[address & 0x7fff];
|
||||
case 4:
|
||||
if((address < 0x4000400) && ioReadable[address & 0x3ff])
|
||||
return ioMem[address & 0x3ff];
|
||||
else goto unreadable;
|
||||
case 5:
|
||||
return paletteRAM[address & 0x3ff];
|
||||
case 6:
|
||||
address = (address & 0x1ffff);
|
||||
if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000))
|
||||
return 0;
|
||||
if ((address & 0x18000) == 0x18000)
|
||||
address &= 0x17fff;
|
||||
return vram[address];
|
||||
case 7:
|
||||
return oam[address & 0x3ff];
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
return rom[address & 0x1FFFFFF];
|
||||
case 13:
|
||||
if(cpuEEPROMEnabled)
|
||||
return eepromRead(address);
|
||||
goto unreadable;
|
||||
case 14:
|
||||
if(cpuSramEnabled | cpuFlashEnabled)
|
||||
return flashRead(address);
|
||||
if(cpuEEPROMSensorEnabled) {
|
||||
switch(address & 0x00008f00) {
|
||||
case 0x8200:
|
||||
return systemGetSensorX() & 255;
|
||||
case 0x8300:
|
||||
return (systemGetSensorX() >> 8)|0x80;
|
||||
case 0x8400:
|
||||
return systemGetSensorY() & 255;
|
||||
case 0x8500:
|
||||
return systemGetSensorY() >> 8;
|
||||
}
|
||||
}
|
||||
// default
|
||||
default:
|
||||
unreadable:
|
||||
#ifdef GBA_LOGGING
|
||||
if(systemVerbose & VERBOSE_ILLEGAL_READ) {
|
||||
log("Illegal byte read: %08x at %08x\n", address, armMode ?
|
||||
armNextPC - 4 : armNextPC - 2);
|
||||
}
|
||||
#endif
|
||||
if(cpuDmaHack) {
|
||||
return cpuDmaLast & 0xFF;
|
||||
} else {
|
||||
if(armState) {
|
||||
return CPUReadByteQuick(reg[15].I+(address & 3));
|
||||
} else {
|
||||
return CPUReadByteQuick(reg[15].I+(address & 1));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void CPUWriteMemory(u32 address, u32 value)
|
||||
{
|
||||
|
||||
#ifdef GBA_LOGGING
|
||||
if(address & 3) {
|
||||
if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) {
|
||||
log("Unaligned word write: %08x to %08x from %08x\n",
|
||||
value,
|
||||
address,
|
||||
armMode ? armNextPC - 4 : armNextPC - 2);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
switch(address >> 24) {
|
||||
case 0x02:
|
||||
#ifdef BKPT_SUPPORT
|
||||
if(*((u32 *)&freezeWorkRAM[address & 0x3FFFC]))
|
||||
cheatsWriteMemory(address & 0x203FFFC,
|
||||
value);
|
||||
else
|
||||
#endif
|
||||
WRITE32LE(((u32 *)&workRAM[address & 0x3FFFC]), value);
|
||||
break;
|
||||
case 0x03:
|
||||
#ifdef BKPT_SUPPORT
|
||||
if(*((u32 *)&freezeInternalRAM[address & 0x7ffc]))
|
||||
cheatsWriteMemory(address & 0x3007FFC,
|
||||
value);
|
||||
else
|
||||
#endif
|
||||
WRITE32LE(((u32 *)&internalRAM[address & 0x7ffC]), value);
|
||||
break;
|
||||
case 0x04:
|
||||
if(address < 0x4000400) {
|
||||
CPUUpdateRegister((address & 0x3FC), value & 0xFFFF);
|
||||
CPUUpdateRegister((address & 0x3FC) + 2, (value >> 16));
|
||||
} else goto unwritable;
|
||||
break;
|
||||
case 0x05:
|
||||
#ifdef BKPT_SUPPORT
|
||||
if(*((u32 *)&freezePRAM[address & 0x3fc]))
|
||||
cheatsWriteMemory(address & 0x70003FC,
|
||||
value);
|
||||
else
|
||||
#endif
|
||||
WRITE32LE(((u32 *)&paletteRAM[address & 0x3FC]), value);
|
||||
break;
|
||||
case 0x06:
|
||||
address = (address & 0x1fffc);
|
||||
if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000))
|
||||
return;
|
||||
if ((address & 0x18000) == 0x18000)
|
||||
address &= 0x17fff;
|
||||
|
||||
#ifdef BKPT_SUPPORT
|
||||
if(*((u32 *)&freezeVRAM[address]))
|
||||
cheatsWriteMemory(address + 0x06000000, value);
|
||||
else
|
||||
#endif
|
||||
|
||||
WRITE32LE(((u32 *)&vram[address]), value);
|
||||
break;
|
||||
case 0x07:
|
||||
#ifdef BKPT_SUPPORT
|
||||
if(*((u32 *)&freezeOAM[address & 0x3fc]))
|
||||
cheatsWriteMemory(address & 0x70003FC,
|
||||
value);
|
||||
else
|
||||
#endif
|
||||
WRITE32LE(((u32 *)&oam[address & 0x3fc]), value);
|
||||
break;
|
||||
case 0x0D:
|
||||
if(cpuEEPROMEnabled) {
|
||||
eepromWrite(address, value);
|
||||
break;
|
||||
}
|
||||
goto unwritable;
|
||||
case 0x0E:
|
||||
if(!eepromInUse | cpuSramEnabled | cpuFlashEnabled) {
|
||||
(*cpuSaveGameFunc)(address, (u8)value);
|
||||
break;
|
||||
}
|
||||
// default
|
||||
default:
|
||||
unwritable:
|
||||
#ifdef GBA_LOGGING
|
||||
if(systemVerbose & VERBOSE_ILLEGAL_WRITE) {
|
||||
log("Illegal word write: %08x to %08x from %08x\n",
|
||||
value,
|
||||
address,
|
||||
armMode ? armNextPC - 4 : armNextPC - 2);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void CPUWriteHalfWord(u32 address, u16 value)
|
||||
{
|
||||
#ifdef GBA_LOGGING
|
||||
if(address & 1) {
|
||||
if(systemVerbose & VERBOSE_UNALIGNED_MEMORY) {
|
||||
log("Unaligned halfword write: %04x to %08x from %08x\n",
|
||||
value,
|
||||
address,
|
||||
armMode ? armNextPC - 4 : armNextPC - 2);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
switch(address >> 24) {
|
||||
case 2:
|
||||
#ifdef BKPT_SUPPORT
|
||||
if(*((u16 *)&freezeWorkRAM[address & 0x3FFFE]))
|
||||
cheatsWriteHalfWord(address & 0x203FFFE,
|
||||
value);
|
||||
else
|
||||
#endif
|
||||
WRITE16LE(((u16 *)&workRAM[address & 0x3FFFE]),value);
|
||||
break;
|
||||
case 3:
|
||||
#ifdef BKPT_SUPPORT
|
||||
if(*((u16 *)&freezeInternalRAM[address & 0x7ffe]))
|
||||
cheatsWriteHalfWord(address & 0x3007ffe,
|
||||
value);
|
||||
else
|
||||
#endif
|
||||
WRITE16LE(((u16 *)&internalRAM[address & 0x7ffe]), value);
|
||||
break;
|
||||
case 4:
|
||||
if(address < 0x4000400)
|
||||
CPUUpdateRegister(address & 0x3fe, value);
|
||||
else goto unwritable;
|
||||
break;
|
||||
case 5:
|
||||
#ifdef BKPT_SUPPORT
|
||||
if(*((u16 *)&freezePRAM[address & 0x03fe]))
|
||||
cheatsWriteHalfWord(address & 0x70003fe,
|
||||
value);
|
||||
else
|
||||
#endif
|
||||
WRITE16LE(((u16 *)&paletteRAM[address & 0x3fe]), value);
|
||||
break;
|
||||
case 6:
|
||||
address = (address & 0x1fffe);
|
||||
if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000))
|
||||
return;
|
||||
if ((address & 0x18000) == 0x18000)
|
||||
address &= 0x17fff;
|
||||
#ifdef BKPT_SUPPORT
|
||||
if(*((u16 *)&freezeVRAM[address]))
|
||||
cheatsWriteHalfWord(address + 0x06000000,
|
||||
value);
|
||||
else
|
||||
#endif
|
||||
WRITE16LE(((u16 *)&vram[address]), value);
|
||||
break;
|
||||
case 7:
|
||||
#ifdef BKPT_SUPPORT
|
||||
if(*((u16 *)&freezeOAM[address & 0x03fe]))
|
||||
cheatsWriteHalfWord(address & 0x70003fe,
|
||||
value);
|
||||
else
|
||||
#endif
|
||||
WRITE16LE(((u16 *)&oam[address & 0x3fe]), value);
|
||||
break;
|
||||
case 8:
|
||||
case 9:
|
||||
if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8) {
|
||||
if(!rtcWrite(address, value))
|
||||
goto unwritable;
|
||||
} else if(!agbPrintWrite(address, value)) goto unwritable;
|
||||
break;
|
||||
case 13:
|
||||
if(cpuEEPROMEnabled) {
|
||||
eepromWrite(address, (u8)value);
|
||||
break;
|
||||
}
|
||||
goto unwritable;
|
||||
case 14:
|
||||
if(!eepromInUse | cpuSramEnabled | cpuFlashEnabled) {
|
||||
(*cpuSaveGameFunc)(address, (u8)value);
|
||||
break;
|
||||
}
|
||||
goto unwritable;
|
||||
default:
|
||||
unwritable:
|
||||
#ifdef GBA_LOGGING
|
||||
if(systemVerbose & VERBOSE_ILLEGAL_WRITE) {
|
||||
log("Illegal halfword write: %04x to %08x from %08x\n",
|
||||
value,
|
||||
address,
|
||||
armMode ? armNextPC - 4 : armNextPC - 2);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void CPUWriteByte(u32 address, u8 b)
|
||||
{
|
||||
switch(address >> 24) {
|
||||
case 2:
|
||||
#ifdef BKPT_SUPPORT
|
||||
if(freezeWorkRAM[address & 0x3FFFF])
|
||||
cheatsWriteByte(address & 0x203FFFF, b);
|
||||
else
|
||||
#endif
|
||||
workRAM[address & 0x3FFFF] = b;
|
||||
break;
|
||||
case 3:
|
||||
#ifdef BKPT_SUPPORT
|
||||
if(freezeInternalRAM[address & 0x7fff])
|
||||
cheatsWriteByte(address & 0x3007fff, b);
|
||||
else
|
||||
#endif
|
||||
internalRAM[address & 0x7fff] = b;
|
||||
break;
|
||||
case 4:
|
||||
if(address < 0x4000400) {
|
||||
switch(address & 0x3FF) {
|
||||
case 0x301:
|
||||
if(b == 0x80)
|
||||
stopState = true;
|
||||
holdState = 1;
|
||||
holdType = -1;
|
||||
cpuNextEvent = cpuTotalTicks;
|
||||
break;
|
||||
case 0x60:
|
||||
case 0x61:
|
||||
case 0x62:
|
||||
case 0x63:
|
||||
case 0x64:
|
||||
case 0x65:
|
||||
case 0x68:
|
||||
case 0x69:
|
||||
case 0x6c:
|
||||
case 0x6d:
|
||||
case 0x70:
|
||||
case 0x71:
|
||||
case 0x72:
|
||||
case 0x73:
|
||||
case 0x74:
|
||||
case 0x75:
|
||||
case 0x78:
|
||||
case 0x79:
|
||||
case 0x7c:
|
||||
case 0x7d:
|
||||
case 0x80:
|
||||
case 0x81:
|
||||
case 0x84:
|
||||
case 0x85:
|
||||
case 0x90:
|
||||
case 0x91:
|
||||
case 0x92:
|
||||
case 0x93:
|
||||
case 0x94:
|
||||
case 0x95:
|
||||
case 0x96:
|
||||
case 0x97:
|
||||
case 0x98:
|
||||
case 0x99:
|
||||
case 0x9a:
|
||||
case 0x9b:
|
||||
case 0x9c:
|
||||
case 0x9d:
|
||||
case 0x9e:
|
||||
case 0x9f:
|
||||
soundEvent(address&0xFF, b);
|
||||
break;
|
||||
default:
|
||||
if(address & 1)
|
||||
CPUUpdateRegister(address & 0x3fe,
|
||||
((READ16LE(((u16 *)&ioMem[address & 0x3fe])))
|
||||
& 0x00FF) |
|
||||
b<<8);
|
||||
else
|
||||
CPUUpdateRegister(address & 0x3fe,
|
||||
((READ16LE(((u16 *)&ioMem[address & 0x3fe])) & 0xFF00) | b));
|
||||
}
|
||||
break;
|
||||
} else goto unwritable;
|
||||
break;
|
||||
case 5:
|
||||
// no need to switch
|
||||
*((u16 *)&paletteRAM[address & 0x3FE]) = (b << 8) | b;
|
||||
break;
|
||||
case 6:
|
||||
address = (address & 0x1fffe);
|
||||
if (((DISPCNT & 7) >2) && ((address & 0x1C000) == 0x18000))
|
||||
return;
|
||||
if ((address & 0x18000) == 0x18000)
|
||||
address &= 0x17fff;
|
||||
|
||||
// no need to switch
|
||||
// byte writes to OBJ VRAM are ignored
|
||||
if ((address) < objTilesAddress[((DISPCNT&7)+1)>>2])
|
||||
{
|
||||
#ifdef BKPT_SUPPORT
|
||||
if(freezeVRAM[address])
|
||||
cheatsWriteByte(address + 0x06000000, b);
|
||||
else
|
||||
#endif
|
||||
*((u16 *)&vram[address]) = (b << 8) | b;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
// no need to switch
|
||||
// byte writes to OAM are ignored
|
||||
// *((u16 *)&oam[address & 0x3FE]) = (b << 8) | b;
|
||||
break;
|
||||
case 13:
|
||||
if(cpuEEPROMEnabled) {
|
||||
eepromWrite(address, b);
|
||||
break;
|
||||
}
|
||||
goto unwritable;
|
||||
case 14:
|
||||
if (!(saveType == 5) && (!eepromInUse | cpuSramEnabled | cpuFlashEnabled)) {
|
||||
|
||||
//if(!cpuEEPROMEnabled && (cpuSramEnabled | cpuFlashEnabled)) {
|
||||
|
||||
(*cpuSaveGameFunc)(address, b);
|
||||
break;
|
||||
}
|
||||
// default
|
||||
default:
|
||||
unwritable:
|
||||
#ifdef GBA_LOGGING
|
||||
if(systemVerbose & VERBOSE_ILLEGAL_WRITE) {
|
||||
log("Illegal byte write: %02x to %08x from %08x\n",
|
||||
b,
|
||||
address,
|
||||
armMode ? armNextPC - 4 : armNextPC -2 );
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif //VBA_GBAinline_H
|
|
@ -0,0 +1,99 @@
|
|||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2004 Forgotten and the VBA development team
|
||||
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2, or(at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include <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);
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// -*- C++ -*-
|
||||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2004 Forgotten and the VBA development team
|
||||
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2, or(at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#ifndef VBA_AGBPRINT_H
|
||||
#define VBA_AGBPRINT_H
|
||||
extern void agbPrintEnable(bool);
|
||||
extern bool agbPrintIsEnabled();
|
||||
extern void agbPrintReset();
|
||||
extern bool agbPrintWrite(u32, u16);
|
||||
extern void agbPrintFlush();
|
||||
#endif
|
|
@ -0,0 +1,227 @@
|
|||
#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;
|
||||
}
|
||||
}
|
||||
*/
|
|
@ -0,0 +1,5 @@
|
|||
#include "../System.h"
|
||||
|
||||
void gbafilter_pal(u16 * buf, int count);
|
||||
void gbafilter_pal32(u32 * buf, int count);
|
||||
void gbafilter_pad(u8 * buf, int count);
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,64 @@
|
|||
// -*- C++ -*-
|
||||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2005-2006 Forgotten and the VBA development team
|
||||
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2, or(at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#ifndef VBA_GB_GB_H
|
||||
#define VBA_GB_GB_H
|
||||
|
||||
#define C_FLAG 0x10
|
||||
#define H_FLAG 0x20
|
||||
#define N_FLAG 0x40
|
||||
#define Z_FLAG 0x80
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
u8 B1, B0;
|
||||
#else
|
||||
u8 B0,B1;
|
||||
#endif
|
||||
} B;
|
||||
u16 W;
|
||||
} gbRegister;
|
||||
|
||||
extern bool gbLoadRom(const char *);
|
||||
extern void gbEmulate(int);
|
||||
extern void gbWriteMemory(register u16, register u8);
|
||||
extern void gbDrawLine();
|
||||
extern bool gbIsGameboyRom(const char *);
|
||||
extern void gbSoundReset();
|
||||
extern void gbSoundSetQuality(int);
|
||||
extern void gbGetHardwareType();
|
||||
extern void gbReset();
|
||||
extern void gbCleanUp();
|
||||
extern void gbCPUInit(const char *,bool);
|
||||
extern bool gbWriteBatteryFile(const char *);
|
||||
extern bool gbWriteBatteryFile(const char *, bool);
|
||||
extern bool gbReadBatteryFile(const char *);
|
||||
extern bool gbWriteSaveState(const char *);
|
||||
extern bool gbWriteMemSaveState(char *, int);
|
||||
extern bool gbReadSaveState(const char *);
|
||||
extern bool gbReadMemSaveState(char *, int);
|
||||
extern void gbSgbRenderBorder();
|
||||
extern bool gbWritePNGFile(const char *);
|
||||
extern bool gbWriteBMPFile(const char *);
|
||||
extern bool gbReadGSASnapshot(const char *);
|
||||
|
||||
extern struct EmulatedSystem GBSystem;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,522 @@
|
|||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2005 Forgotten and the VBA development team
|
||||
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2, or(at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include <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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
// -*- C++ -*-
|
||||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2004 Forgotten and the VBA development team
|
||||
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2, or(at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#ifndef __VBA_GB_GBCHEATS_H
|
||||
#define __VBA_GB_GBCHEATS_H
|
||||
|
||||
#include "../System.h"
|
||||
|
||||
struct gbXxCheat {
|
||||
char cheatDesc[100];
|
||||
char cheatCode[20];
|
||||
};
|
||||
|
||||
struct gbCheat {
|
||||
char cheatCode[20];
|
||||
char cheatDesc[32];
|
||||
u16 address;
|
||||
int code;
|
||||
u8 compare;
|
||||
u8 value;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
void gbCheatsSaveGame(gzFile);
|
||||
void gbCheatsReadGame(gzFile, int);
|
||||
void gbCheatsSaveCheatList(const char *);
|
||||
bool gbCheatsLoadCheatList(const char *);
|
||||
bool gbCheatReadGSCodeFile(const char *);
|
||||
|
||||
bool gbAddGsCheat(const char *, const char*);
|
||||
bool gbAddGgCheat(const char *, const char*);
|
||||
void gbCheatRemove(int);
|
||||
void gbCheatRemoveAll();
|
||||
void gbCheatEnable(int);
|
||||
void gbCheatDisable(int);
|
||||
u8 gbCheatRead(u16);
|
||||
void gbCheatWrite(bool);
|
||||
bool gbVerifyGsCode(const char *code);
|
||||
bool gbVerifyGgCode(const char *code);
|
||||
|
||||
|
||||
extern int gbCheatNumber;
|
||||
extern gbCheat gbCheatList[100];
|
||||
extern bool gbCheatMap[0x10000];
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,249 @@
|
|||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2004 Forgotten and the VBA development team
|
||||
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2, or(at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include <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;
|
||||
}
|
|
@ -0,0 +1,601 @@
|
|||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2005-2006 Forgotten and the VBA development team
|
||||
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2, or(at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include <memory.h>
|
||||
|
||||
#include "../agb/GBA.h"
|
||||
#include "gbGlobals.h"
|
||||
#include "gbSGB.h"
|
||||
|
||||
u8 gbInvertTab[256] = {
|
||||
0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0,
|
||||
0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0,
|
||||
0x08,0x88,0x48,0xc8,0x28,0xa8,0x68,0xe8,
|
||||
0x18,0x98,0x58,0xd8,0x38,0xb8,0x78,0xf8,
|
||||
0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4,
|
||||
0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4,
|
||||
0x0c,0x8c,0x4c,0xcc,0x2c,0xac,0x6c,0xec,
|
||||
0x1c,0x9c,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc,
|
||||
0x02,0x82,0x42,0xc2,0x22,0xa2,0x62,0xe2,
|
||||
0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2,
|
||||
0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea,
|
||||
0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x7a,0xfa,
|
||||
0x06,0x86,0x46,0xc6,0x26,0xa6,0x66,0xe6,
|
||||
0x16,0x96,0x56,0xd6,0x36,0xb6,0x76,0xf6,
|
||||
0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee,
|
||||
0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe,
|
||||
0x01,0x81,0x41,0xc1,0x21,0xa1,0x61,0xe1,
|
||||
0x11,0x91,0x51,0xd1,0x31,0xb1,0x71,0xf1,
|
||||
0x09,0x89,0x49,0xc9,0x29,0xa9,0x69,0xe9,
|
||||
0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9,
|
||||
0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5,
|
||||
0x15,0x95,0x55,0xd5,0x35,0xb5,0x75,0xf5,
|
||||
0x0d,0x8d,0x4d,0xcd,0x2d,0xad,0x6d,0xed,
|
||||
0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,0x7d,0xfd,
|
||||
0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3,
|
||||
0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3,
|
||||
0x0b,0x8b,0x4b,0xcb,0x2b,0xab,0x6b,0xeb,
|
||||
0x1b,0x9b,0x5b,0xdb,0x3b,0xbb,0x7b,0xfb,
|
||||
0x07,0x87,0x47,0xc7,0x27,0xa7,0x67,0xe7,
|
||||
0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7,
|
||||
0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef,
|
||||
0x1f,0x9f,0x5f,0xdf,0x3f,0xbf,0x7f,0xff
|
||||
};
|
||||
|
||||
u16 gbLineMix[160];
|
||||
u16 gbWindowColor[160];
|
||||
extern int inUseRegister_WY;
|
||||
|
||||
void gbRenderLine()
|
||||
{
|
||||
memset(gbLineMix, 0, sizeof(gbLineMix));
|
||||
u8 * bank0;
|
||||
u8 * bank1;
|
||||
if(gbCgbMode) {
|
||||
bank0 = &gbVram[0x0000];
|
||||
bank1 = &gbVram[0x2000];
|
||||
} else {
|
||||
bank0 = &gbMemory[0x8000];
|
||||
bank1 = NULL;
|
||||
}
|
||||
|
||||
int tile_map = 0x1800;
|
||||
if((register_LCDC & 8) != 0)
|
||||
tile_map = 0x1c00;
|
||||
|
||||
int tile_pattern = 0x0800;
|
||||
|
||||
if((register_LCDC & 16) != 0)
|
||||
tile_pattern = 0x0000;
|
||||
|
||||
int x = 0;
|
||||
int y = register_LY;
|
||||
|
||||
if(y >= 144)
|
||||
return;
|
||||
|
||||
int SpritesTicks = gbSpritesTicks[x]*(gbSpeed ? 2 : 4);
|
||||
int sx = gbSCXLine[(gbSpeed ? 0 : 4)+SpritesTicks];
|
||||
int sy = gbSCYLine[(gbSpeed ? 11 : 5)+SpritesTicks];
|
||||
|
||||
sy+=y;
|
||||
|
||||
sy &= 255;
|
||||
|
||||
int tx = sx >> 3;
|
||||
int ty = sy >> 3;
|
||||
|
||||
int bx = 1 << (7 - (sx & 7));
|
||||
int by = sy & 7;
|
||||
|
||||
int tile_map_line_y = tile_map + ty * 32;
|
||||
|
||||
int tile_map_address = tile_map_line_y + tx;
|
||||
|
||||
u8 attrs = 0;
|
||||
if(bank1 != NULL)
|
||||
attrs = bank1[tile_map_address];
|
||||
|
||||
u8 tile = bank0[tile_map_address];
|
||||
|
||||
tile_map_address++;
|
||||
|
||||
if(!(register_LCDC & 0x10))
|
||||
tile ^= 0x80;
|
||||
|
||||
int tile_pattern_address = tile_pattern + tile * 16 + by*2;
|
||||
|
||||
if(register_LCDC & 0x80) {
|
||||
if((register_LCDC & 0x01 || gbCgbMode) && (layerSettings & 0x0100)) {
|
||||
while(x < 160) {
|
||||
|
||||
|
||||
|
||||
u8 tile_a = 0;
|
||||
u8 tile_b = 0;
|
||||
|
||||
if(attrs & 0x40) {
|
||||
tile_pattern_address = tile_pattern + tile * 16 + (7-by)*2;
|
||||
}
|
||||
|
||||
if(attrs & 0x08) {
|
||||
tile_a = bank1[tile_pattern_address++];
|
||||
tile_b = bank1[tile_pattern_address];
|
||||
} else {
|
||||
tile_a = bank0[tile_pattern_address++];
|
||||
tile_b = bank0[tile_pattern_address];
|
||||
}
|
||||
|
||||
if(attrs & 0x20) {
|
||||
tile_a = gbInvertTab[tile_a];
|
||||
tile_b = gbInvertTab[tile_b];
|
||||
}
|
||||
|
||||
while(bx > 0) {
|
||||
u8 c = (tile_a & bx) ? 1 : 0;
|
||||
c += ((tile_b & bx) ? 2 : 0);
|
||||
|
||||
gbLineBuffer[x] = c; // mark the gbLineBuffer color
|
||||
|
||||
if(attrs & 0x80)
|
||||
gbLineBuffer[x] |= 0x300;
|
||||
|
||||
if(gbCgbMode) {
|
||||
c = c + (attrs & 7)*4;
|
||||
} else {
|
||||
c = (gbBgpLine[x+(gbSpeed ? 5 : 11)+SpritesTicks]>>(c<<1)) &3;
|
||||
if(gbSgbMode && !gbCgbMode) {
|
||||
int dx = x >> 3;
|
||||
int dy = y >> 3;
|
||||
|
||||
int palette = gbSgbATF[dy * 20 + dx];
|
||||
|
||||
if(c == 0)
|
||||
palette = 0;
|
||||
|
||||
c = c + 4*palette;
|
||||
}
|
||||
}
|
||||
gbLineMix[x] = gbColorOption ? gbColorFilter[gbPalette[c] & 0x7FFF] :
|
||||
gbPalette[c] & 0x7FFF;
|
||||
x++;
|
||||
if(x >= 160)
|
||||
break;
|
||||
bx >>= 1;
|
||||
}
|
||||
|
||||
bx = 128;
|
||||
|
||||
SpritesTicks = gbSpritesTicks[x]*(gbSpeed ? 2 : 4);
|
||||
|
||||
sx = gbSCXLine[x+(gbSpeed ? 0 : 4)+SpritesTicks];
|
||||
|
||||
sy = gbSCYLine[x+(gbSpeed ? 11 : 5)+SpritesTicks];
|
||||
|
||||
|
||||
tx = ((sx+x)>>3) & 0x1f;
|
||||
|
||||
sy+=y;
|
||||
|
||||
sy &= 255;
|
||||
|
||||
ty = sy >> 3;
|
||||
|
||||
by = sy & 7;
|
||||
|
||||
tile_pattern_address = tile_pattern + tile * 16 + by * 2;
|
||||
|
||||
tile_map_line_y = tile_map + ty * 32;
|
||||
|
||||
tile_map_address = tile_map_line_y + tx;
|
||||
|
||||
if(bank1)
|
||||
attrs = bank1[tile_map_line_y + tx];
|
||||
|
||||
tile = bank0[tile_map_line_y + tx];
|
||||
|
||||
if(!(register_LCDC & 0x10))
|
||||
tile ^= 0x80;
|
||||
|
||||
tile_pattern_address = tile_pattern + tile * 16 + by * 2;
|
||||
}
|
||||
} else {
|
||||
// Use gbBgp[0] instead of 0 (?)
|
||||
// (this fixes white flashes on Last Bible II)
|
||||
// Also added the gbColorOption (fixes Dracula Densetsu II color problems)
|
||||
for(int i = 0; i < 160; i++)
|
||||
{
|
||||
u16 color = gbColorOption ? gbColorFilter[0x7FFF] :
|
||||
0x7FFF;
|
||||
if (!gbCgbMode)
|
||||
color = gbColorOption ? gbColorFilter[gbPalette[gbBgpLine[i+(gbSpeed ? 5 : 11)+gbSpritesTicks[i]*(gbSpeed ? 2 : 4)]&3] & 0x7FFF] :
|
||||
gbPalette[gbBgpLine[i+(gbSpeed ? 5 : 11)+gbSpritesTicks[i]*(gbSpeed ? 2 : 4)]&3] & 0x7FFF;
|
||||
gbLineMix[i] = color;
|
||||
gbLineBuffer[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// do the window display
|
||||
// LCDC.0 also enables/disables the window in !gbCgbMode ?!?!
|
||||
// (tested on real hardware)
|
||||
// This fixes Last Bible II & Zankurou Musouken
|
||||
if((register_LCDC & 0x01 || gbCgbMode) && (register_LCDC & 0x20) &&
|
||||
(layerSettings & 0x2000) && (gbWindowLine != -2)) {
|
||||
int i = 0;
|
||||
// Fix (accurate emulation) for most of the window display problems
|
||||
// (ie. Zen - Intergalactic Ninja, Urusei Yatsura...).
|
||||
if ((gbWindowLine == -1) || (gbWindowLine>144))
|
||||
{
|
||||
inUseRegister_WY = oldRegister_WY;
|
||||
if (register_LY>oldRegister_WY)
|
||||
gbWindowLine = 146;
|
||||
// for (i = 0; i<160; i++)
|
||||
// gbWindowColor[i] = gbLineMix[i];
|
||||
}
|
||||
|
||||
int wy = inUseRegister_WY;
|
||||
|
||||
if(y >= inUseRegister_WY) {
|
||||
|
||||
if (gbWindowLine == -1)
|
||||
gbWindowLine = 0;
|
||||
|
||||
int wx = register_WX;
|
||||
int swx = 0;
|
||||
wx -= 7;
|
||||
|
||||
if( wx <= 159 && gbWindowLine <= 143) {
|
||||
|
||||
tile_map = 0x1800;
|
||||
|
||||
if((register_LCDC & 0x40) != 0)
|
||||
tile_map = 0x1c00;
|
||||
|
||||
|
||||
tx = 0;
|
||||
ty = gbWindowLine >> 3;
|
||||
|
||||
bx = 128;
|
||||
by = gbWindowLine & 7;
|
||||
|
||||
// Tries to emulate the 'window scrolling bug' when wx == 0 (ie. wx-7 == -7).
|
||||
// Nothing close to perfect, but good enought for now...
|
||||
if (wx == -7)
|
||||
{
|
||||
swx = 7-((gbSCXLine[0]-1) & 7);
|
||||
bx >>= ((gbSCXLine[0]+((swx != 1) ? 1 : 0)) & 7);
|
||||
if (swx == 1)
|
||||
swx = 2;
|
||||
|
||||
//bx >>= ((gbSCXLine[0]+(((swx>1) && (swx != 7)) ? 1 : 0)) & 7);
|
||||
|
||||
if ((swx == 7))
|
||||
{
|
||||
//wx = 0;
|
||||
if ((gbWindowLine>0) || (wy == 0))
|
||||
swx = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
if(wx < 0) {
|
||||
bx >>= (-wx);
|
||||
wx = 0;
|
||||
}
|
||||
|
||||
tile_map_line_y = tile_map + ty * 32;
|
||||
|
||||
tile_map_address = tile_map_line_y + tx;
|
||||
|
||||
x = wx;
|
||||
|
||||
tile = bank0[tile_map_address];
|
||||
u8 attrs = 0;
|
||||
if(bank1)
|
||||
attrs = bank1[tile_map_address];
|
||||
tile_map_address++;
|
||||
|
||||
if((register_LCDC & 16) == 0) {
|
||||
if(tile < 128) tile += 128;
|
||||
else tile -= 128;
|
||||
}
|
||||
|
||||
tile_pattern_address = tile_pattern + tile * 16 + by*2;
|
||||
|
||||
if (wx)
|
||||
for (i = 0; i<swx; i++)
|
||||
gbLineMix[i] = gbWindowColor[i];
|
||||
|
||||
while(x < 160) {
|
||||
u8 tile_a = 0;
|
||||
u8 tile_b = 0;
|
||||
|
||||
if(attrs & 0x40) {
|
||||
tile_pattern_address = tile_pattern + tile * 16 + (7-by)*2;
|
||||
}
|
||||
|
||||
if(attrs & 0x08) {
|
||||
tile_a = bank1[tile_pattern_address++];
|
||||
tile_b = bank1[tile_pattern_address];
|
||||
} else {
|
||||
tile_a = bank0[tile_pattern_address++];
|
||||
tile_b = bank0[tile_pattern_address];
|
||||
}
|
||||
|
||||
if(attrs & 0x20) {
|
||||
tile_a = gbInvertTab[tile_a];
|
||||
tile_b = gbInvertTab[tile_b];
|
||||
}
|
||||
|
||||
while(bx > 0) {
|
||||
u8 c = (tile_a & bx) != 0 ? 1 : 0;
|
||||
c += ((tile_b & bx) != 0 ? 2 : 0);
|
||||
|
||||
if (x>=0)
|
||||
{
|
||||
if(attrs & 0x80)
|
||||
gbLineBuffer[x] = 0x300 + c;
|
||||
else
|
||||
gbLineBuffer[x] = 0x100 + c;
|
||||
|
||||
if(gbCgbMode) {
|
||||
c = c + (attrs & 7) * 4;
|
||||
} else {
|
||||
c = (gbBgpLine[x+(gbSpeed ? 5 : 11)+gbSpritesTicks[x]*(gbSpeed ? 2 : 4)]>>(c<<1)) &3;
|
||||
if(gbSgbMode && !gbCgbMode) {
|
||||
int dx = x >> 3;
|
||||
int dy = y >> 3;
|
||||
|
||||
int palette = gbSgbATF[dy * 20 + dx];
|
||||
|
||||
if(c == 0)
|
||||
palette = 0;
|
||||
|
||||
c = c + 4*palette;
|
||||
}
|
||||
}
|
||||
gbLineMix[x] = gbColorOption ? gbColorFilter[gbPalette[c] & 0x7FFF] :
|
||||
gbPalette[c] & 0x7FFF;
|
||||
}
|
||||
x++;
|
||||
if(x >= 160)
|
||||
break;
|
||||
bx >>= 1;
|
||||
}
|
||||
tx++;
|
||||
if(tx == 32)
|
||||
tx = 0;
|
||||
bx = 128;
|
||||
tile = bank0[tile_map_line_y + tx];
|
||||
if(bank1)
|
||||
attrs = bank1[tile_map_line_y + tx];
|
||||
|
||||
if((register_LCDC & 16) == 0) {
|
||||
if(tile < 128) tile += 128;
|
||||
else tile -= 128;
|
||||
}
|
||||
tile_pattern_address = tile_pattern + tile * 16 + by * 2;
|
||||
}
|
||||
|
||||
//for (i = swx; i<160; i++)
|
||||
// gbLineMix[i] = gbWindowColor[i];
|
||||
gbWindowLine++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (gbWindowLine == -2)
|
||||
{
|
||||
inUseRegister_WY = oldRegister_WY;
|
||||
if (register_LY>oldRegister_WY)
|
||||
gbWindowLine = 146;
|
||||
else
|
||||
gbWindowLine = 0;
|
||||
}
|
||||
} else {
|
||||
u16 color = gbColorOption ? gbColorFilter[0x7FFF] :
|
||||
0x7FFF;
|
||||
if (!gbCgbMode)
|
||||
color = gbColorOption ? gbColorFilter[gbPalette[0] & 0x7FFF] :
|
||||
gbPalette[0] & 0x7FFF;
|
||||
for(int i = 0; i < 160; i++)
|
||||
{
|
||||
gbLineMix[i] = color;
|
||||
gbLineBuffer[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gbDrawSpriteTile(int tile, int x,int y,int t, int flags,
|
||||
int size,int spriteNumber)
|
||||
{
|
||||
u8 * bank0;
|
||||
u8 * bank1;
|
||||
if(gbCgbMode) {
|
||||
if(register_VBK & 1) {
|
||||
bank0 = &gbVram[0x0000];
|
||||
bank1 = &gbVram[0x2000];
|
||||
} else {
|
||||
bank0 = &gbVram[0x0000];
|
||||
bank1 = &gbVram[0x2000];
|
||||
}
|
||||
} else {
|
||||
bank0 = &gbMemory[0x8000];
|
||||
bank1 = NULL;
|
||||
}
|
||||
|
||||
int init = 0x0000;
|
||||
|
||||
for (int i = 0; i<4; i++)
|
||||
{
|
||||
gbObp0[i] = (gbObp0Line[x+11+gbSpritesTicks[x]*(gbSpeed ? 2 : 4)]>>(i<<1)) & 3;
|
||||
gbObp1[i] = (gbObp1Line[x+11+gbSpritesTicks[x]*(gbSpeed ? 2 : 4)]>>(i<<1)) & 3;
|
||||
}
|
||||
u8 *pal = gbObp0;
|
||||
|
||||
int flipx = (flags & 0x20);
|
||||
int flipy = (flags & 0x40);
|
||||
|
||||
if((flags & 0x10))
|
||||
pal = gbObp1;
|
||||
|
||||
if(flipy) {
|
||||
t = (size ? 15 : 7) - t;
|
||||
}
|
||||
|
||||
int prio = flags & 0x80;
|
||||
|
||||
int address = init + tile * 16 + 2*t;
|
||||
int a = 0;
|
||||
int b = 0;
|
||||
|
||||
if(gbCgbMode && (flags & 0x08)) {
|
||||
a = bank1[address++];
|
||||
b = bank1[address++];
|
||||
} else {
|
||||
a = bank0[address++];
|
||||
b = bank0[address++];
|
||||
}
|
||||
|
||||
for(int xx = 0; xx < 8; xx++) {
|
||||
u8 mask = 1 << (7-xx);
|
||||
u8 c = 0;
|
||||
if( (a & mask))
|
||||
c++;
|
||||
if( (b & mask))
|
||||
c+=2;
|
||||
|
||||
if(c==0) continue;
|
||||
|
||||
int xxx = xx+x;
|
||||
if(flipx)
|
||||
xxx = (7-xx+x);
|
||||
|
||||
if(xxx < 0 || xxx > 159)
|
||||
continue;
|
||||
|
||||
u16 color = gbLineBuffer[xxx];
|
||||
|
||||
// Fixes OAM-BG priority
|
||||
if(prio && (register_LCDC & 1)) {
|
||||
if(color < 0x200 && ((color & 0xFF) != 0))
|
||||
continue;
|
||||
}
|
||||
// Fixes OAM-BG priority for Moorhuhn 2
|
||||
if(color >= 0x300 && color != 0x300 && (register_LCDC & 1))
|
||||
continue;
|
||||
else if(color >= 0x200 && color < 0x300) {
|
||||
int sprite = color & 0xff;
|
||||
|
||||
int spriteX = gbMemory[0xfe00 + 4 * sprite + 1] - 8;
|
||||
|
||||
if(spriteX == x) {
|
||||
if(sprite < spriteNumber)
|
||||
continue;
|
||||
} else {
|
||||
if(gbCgbMode) {
|
||||
if(sprite < spriteNumber)
|
||||
continue;
|
||||
} else {
|
||||
// Fixes GB sprites priorities (was '< x + 8' before)
|
||||
// ('A boy and his blob...' sprites' emulation is now correct)
|
||||
if(spriteX < x)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gbLineBuffer[xxx] = 0x200 + spriteNumber;
|
||||
|
||||
// make sure that sprites will work even in CGB mode
|
||||
if(gbCgbMode) {
|
||||
c = c + (flags & 0x07)*4 + 32;
|
||||
} else {
|
||||
c = pal[c];
|
||||
|
||||
if(gbSgbMode && !gbCgbMode) {
|
||||
int dx = xxx >> 3;
|
||||
int dy = y >> 3;
|
||||
|
||||
int palette = gbSgbATF[dy * 20 + dx];
|
||||
|
||||
if(c == 0)
|
||||
palette = 0;
|
||||
|
||||
c = c + 4*palette;
|
||||
} else {
|
||||
c += 4;
|
||||
}
|
||||
}
|
||||
|
||||
gbLineMix[xxx] = gbColorOption ? gbColorFilter[gbPalette[c] & 0x7FFF] :
|
||||
gbPalette[c] & 0x7FFF;
|
||||
}
|
||||
}
|
||||
|
||||
void gbDrawSprites(bool draw)
|
||||
{
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int count = 0;
|
||||
|
||||
int size = (register_LCDC & 4);
|
||||
|
||||
if (!draw)
|
||||
memset (gbSpritesTicks, 0, sizeof(gbSpritesTicks));
|
||||
|
||||
if(!(register_LCDC & 0x80))
|
||||
return;
|
||||
|
||||
if((register_LCDC & 2) && (layerSettings & 0x1000)) {
|
||||
int yc = register_LY;
|
||||
|
||||
int address = 0xfe00;
|
||||
for(int i = 0; i < 40; i++) {
|
||||
y = gbMemory[address++];
|
||||
x = gbMemory[address++];
|
||||
int tile = gbMemory[address++];
|
||||
if(size)
|
||||
tile &= 254;
|
||||
int flags = gbMemory[address++];
|
||||
|
||||
if(x > 0 && y > 0 && x < 168 && y < 160) {
|
||||
// check if sprite intersects current line
|
||||
int t = yc -y + 16;
|
||||
if((size && t >=0 && t < 16) || (!size && t >= 0 && t < 8)) {
|
||||
if (draw)
|
||||
gbDrawSpriteTile(tile,x-8,yc,t,flags,size,i);
|
||||
else
|
||||
{
|
||||
for (int j = x-8; j<300; j++)
|
||||
if (j>=0)
|
||||
if (gbSpeed)
|
||||
gbSpritesTicks[j] += 5;
|
||||
else
|
||||
gbSpritesTicks[j] += 2+(count&1);
|
||||
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
// sprite limit reached!
|
||||
if(count >= 10)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,601 @@
|
|||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2005-2006 Forgotten and the VBA development team
|
||||
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2, or(at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include <memory.h>
|
||||
|
||||
#include "../agb/GBA.h"
|
||||
#include "gbGlobals.h"
|
||||
#include "gbSGB.h"
|
||||
|
||||
u8 gbInvertTab[256] = {
|
||||
0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0,
|
||||
0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0,
|
||||
0x08,0x88,0x48,0xc8,0x28,0xa8,0x68,0xe8,
|
||||
0x18,0x98,0x58,0xd8,0x38,0xb8,0x78,0xf8,
|
||||
0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4,
|
||||
0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4,
|
||||
0x0c,0x8c,0x4c,0xcc,0x2c,0xac,0x6c,0xec,
|
||||
0x1c,0x9c,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc,
|
||||
0x02,0x82,0x42,0xc2,0x22,0xa2,0x62,0xe2,
|
||||
0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2,
|
||||
0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea,
|
||||
0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x7a,0xfa,
|
||||
0x06,0x86,0x46,0xc6,0x26,0xa6,0x66,0xe6,
|
||||
0x16,0x96,0x56,0xd6,0x36,0xb6,0x76,0xf6,
|
||||
0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee,
|
||||
0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe,
|
||||
0x01,0x81,0x41,0xc1,0x21,0xa1,0x61,0xe1,
|
||||
0x11,0x91,0x51,0xd1,0x31,0xb1,0x71,0xf1,
|
||||
0x09,0x89,0x49,0xc9,0x29,0xa9,0x69,0xe9,
|
||||
0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9,
|
||||
0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5,
|
||||
0x15,0x95,0x55,0xd5,0x35,0xb5,0x75,0xf5,
|
||||
0x0d,0x8d,0x4d,0xcd,0x2d,0xad,0x6d,0xed,
|
||||
0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,0x7d,0xfd,
|
||||
0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3,
|
||||
0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3,
|
||||
0x0b,0x8b,0x4b,0xcb,0x2b,0xab,0x6b,0xeb,
|
||||
0x1b,0x9b,0x5b,0xdb,0x3b,0xbb,0x7b,0xfb,
|
||||
0x07,0x87,0x47,0xc7,0x27,0xa7,0x67,0xe7,
|
||||
0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7,
|
||||
0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef,
|
||||
0x1f,0x9f,0x5f,0xdf,0x3f,0xbf,0x7f,0xff
|
||||
};
|
||||
|
||||
u16 gbLineMix[160];
|
||||
u16 gbWindowColor[160];
|
||||
extern int inUseRegister_WY;
|
||||
|
||||
void gbRenderLine()
|
||||
{
|
||||
memset(gbLineMix, 0, sizeof(gbLineMix));
|
||||
u8 * bank0;
|
||||
u8 * bank1;
|
||||
if(gbCgbMode) {
|
||||
bank0 = &gbVram[0x0000];
|
||||
bank1 = &gbVram[0x2000];
|
||||
} else {
|
||||
bank0 = &gbMemory[0x8000];
|
||||
bank1 = NULL;
|
||||
}
|
||||
|
||||
int tile_map = 0x1800;
|
||||
if((register_LCDC & 8) != 0)
|
||||
tile_map = 0x1c00;
|
||||
|
||||
int tile_pattern = 0x0800;
|
||||
|
||||
if((register_LCDC & 16) != 0)
|
||||
tile_pattern = 0x0000;
|
||||
|
||||
int x = 0;
|
||||
int y = register_LY;
|
||||
|
||||
if(y >= 144)
|
||||
return;
|
||||
|
||||
int SpritesTicks = gbSpritesTicks[x]*(gbSpeed ? 2 : 4);
|
||||
int sx = gbSCXLine[(gbSpeed ? 0 : 4)+SpritesTicks];
|
||||
int sy = gbSCYLine[(gbSpeed ? 11 : 5)+SpritesTicks];
|
||||
|
||||
sy+=y;
|
||||
|
||||
sy &= 255;
|
||||
|
||||
int tx = sx >> 3;
|
||||
int ty = sy >> 3;
|
||||
|
||||
int bx = 1 << (7 - (sx & 7));
|
||||
int by = sy & 7;
|
||||
|
||||
int tile_map_line_y = tile_map + ty * 32;
|
||||
|
||||
int tile_map_address = tile_map_line_y + tx;
|
||||
|
||||
u8 attrs = 0;
|
||||
if(bank1 != NULL)
|
||||
attrs = bank1[tile_map_address];
|
||||
|
||||
u8 tile = bank0[tile_map_address];
|
||||
|
||||
tile_map_address++;
|
||||
|
||||
if(!(register_LCDC & 0x10))
|
||||
tile ^= 0x80;
|
||||
|
||||
int tile_pattern_address = tile_pattern + tile * 16 + by*2;
|
||||
|
||||
if(register_LCDC & 0x80) {
|
||||
if((register_LCDC & 0x01 || gbCgbMode) && (layerSettings & 0x0100)) {
|
||||
while(x < 160) {
|
||||
|
||||
|
||||
|
||||
u8 tile_a = 0;
|
||||
u8 tile_b = 0;
|
||||
|
||||
if(attrs & 0x40) {
|
||||
tile_pattern_address = tile_pattern + tile * 16 + (7-by)*2;
|
||||
}
|
||||
|
||||
if(attrs & 0x08) {
|
||||
tile_a = bank1[tile_pattern_address++];
|
||||
tile_b = bank1[tile_pattern_address];
|
||||
} else {
|
||||
tile_a = bank0[tile_pattern_address++];
|
||||
tile_b = bank0[tile_pattern_address];
|
||||
}
|
||||
|
||||
if(attrs & 0x20) {
|
||||
tile_a = gbInvertTab[tile_a];
|
||||
tile_b = gbInvertTab[tile_b];
|
||||
}
|
||||
|
||||
while(bx > 0) {
|
||||
u8 c = (tile_a & bx) ? 1 : 0;
|
||||
c += ((tile_b & bx) ? 2 : 0);
|
||||
|
||||
gbLineBuffer[x] = c; // mark the gbLineBuffer color
|
||||
|
||||
if(attrs & 0x80)
|
||||
gbLineBuffer[x] |= 0x300;
|
||||
|
||||
if(gbCgbMode) {
|
||||
c = c + (attrs & 7)*4;
|
||||
} else {
|
||||
c = (gbBgpLine[x+(gbSpeed ? 5 : 11)+SpritesTicks]>>(c<<1)) &3;
|
||||
if(gbSgbMode && !gbCgbMode) {
|
||||
int dx = x >> 3;
|
||||
int dy = y >> 3;
|
||||
|
||||
int palette = gbSgbATF[dy * 20 + dx];
|
||||
|
||||
if(c == 0)
|
||||
palette = 0;
|
||||
|
||||
c = c + 4*palette;
|
||||
}
|
||||
}
|
||||
gbLineMix[x] = gbColorOption ? gbColorFilter[gbPalette[c] & 0x7FFF] :
|
||||
gbPalette[c] & 0x7FFF;
|
||||
x++;
|
||||
if(x >= 160)
|
||||
break;
|
||||
bx >>= 1;
|
||||
}
|
||||
|
||||
bx = 128;
|
||||
|
||||
SpritesTicks = gbSpritesTicks[x]*(gbSpeed ? 2 : 4);
|
||||
|
||||
sx = gbSCXLine[x+(gbSpeed ? 0 : 4)+SpritesTicks];
|
||||
|
||||
sy = gbSCYLine[x+(gbSpeed ? 11 : 5)+SpritesTicks];
|
||||
|
||||
|
||||
tx = ((sx+x)>>3) & 0x1f;
|
||||
|
||||
sy+=y;
|
||||
|
||||
sy &= 255;
|
||||
|
||||
ty = sy >> 3;
|
||||
|
||||
by = sy & 7;
|
||||
|
||||
tile_pattern_address = tile_pattern + tile * 16 + by * 2;
|
||||
|
||||
tile_map_line_y = tile_map + ty * 32;
|
||||
|
||||
tile_map_address = tile_map_line_y + tx;
|
||||
|
||||
if(bank1)
|
||||
attrs = bank1[tile_map_line_y + tx];
|
||||
|
||||
tile = bank0[tile_map_line_y + tx];
|
||||
|
||||
if(!(register_LCDC & 0x10))
|
||||
tile ^= 0x80;
|
||||
|
||||
tile_pattern_address = tile_pattern + tile * 16 + by * 2;
|
||||
}
|
||||
} else {
|
||||
// Use gbBgp[0] instead of 0 (?)
|
||||
// (this fixes white flashes on Last Bible II)
|
||||
// Also added the gbColorOption (fixes Dracula Densetsu II color problems)
|
||||
for(int i = 0; i < 160; i++)
|
||||
{
|
||||
u16 color = gbColorOption ? gbColorFilter[0x7FFF] :
|
||||
0x7FFF;
|
||||
if (!gbCgbMode)
|
||||
color = gbColorOption ? gbColorFilter[gbPalette[gbBgpLine[i+(gbSpeed ? 5 : 11)+gbSpritesTicks[i]*(gbSpeed ? 2 : 4)]&3] & 0x7FFF] :
|
||||
gbPalette[gbBgpLine[i+(gbSpeed ? 5 : 11)+gbSpritesTicks[i]*(gbSpeed ? 2 : 4)]&3] & 0x7FFF;
|
||||
gbLineMix[i] = color;
|
||||
gbLineBuffer[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// do the window display
|
||||
// LCDC.0 also enables/disables the window in !gbCgbMode ?!?!
|
||||
// (tested on real hardware)
|
||||
// This fixes Last Bible II & Zankurou Musouken
|
||||
if((register_LCDC & 0x01 || gbCgbMode) && (register_LCDC & 0x20) &&
|
||||
(layerSettings & 0x2000) && (gbWindowLine != -2)) {
|
||||
int i = 0;
|
||||
// Fix (accurate emulation) for most of the window display problems
|
||||
// (ie. Zen - Intergalactic Ninja, Urusei Yatsura...).
|
||||
if ((gbWindowLine == -1) || (gbWindowLine>144))
|
||||
{
|
||||
inUseRegister_WY = oldRegister_WY;
|
||||
if (register_LY>oldRegister_WY)
|
||||
gbWindowLine = 146;
|
||||
// for (i = 0; i<160; i++)
|
||||
// gbWindowColor[i] = gbLineMix[i];
|
||||
}
|
||||
|
||||
int wy = inUseRegister_WY;
|
||||
|
||||
if(y >= inUseRegister_WY) {
|
||||
|
||||
if (gbWindowLine == -1)
|
||||
gbWindowLine = 0;
|
||||
|
||||
int wx = register_WX;
|
||||
int swx = 0;
|
||||
wx -= 7;
|
||||
|
||||
if( wx <= 159 && gbWindowLine <= 143) {
|
||||
|
||||
tile_map = 0x1800;
|
||||
|
||||
if((register_LCDC & 0x40) != 0)
|
||||
tile_map = 0x1c00;
|
||||
|
||||
|
||||
tx = 0;
|
||||
ty = gbWindowLine >> 3;
|
||||
|
||||
bx = 128;
|
||||
by = gbWindowLine & 7;
|
||||
|
||||
// Tries to emulate the 'window scrolling bug' when wx == 0 (ie. wx-7 == -7).
|
||||
// Nothing close to perfect, but good enought for now...
|
||||
if (wx == -7)
|
||||
{
|
||||
swx = 7-((gbSCXLine[0]-1) & 7);
|
||||
bx >>= ((gbSCXLine[0]+((swx != 1) ? 1 : 0)) & 7);
|
||||
if (swx == 1)
|
||||
swx = 2;
|
||||
|
||||
//bx >>= ((gbSCXLine[0]+(((swx>1) && (swx != 7)) ? 1 : 0)) & 7);
|
||||
|
||||
if ((swx == 7))
|
||||
{
|
||||
//wx = 0;
|
||||
if ((gbWindowLine>0) || (wy == 0))
|
||||
swx = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
if(wx < 0) {
|
||||
bx >>= (-wx);
|
||||
wx = 0;
|
||||
}
|
||||
|
||||
tile_map_line_y = tile_map + ty * 32;
|
||||
|
||||
tile_map_address = tile_map_line_y + tx;
|
||||
|
||||
x = wx;
|
||||
|
||||
tile = bank0[tile_map_address];
|
||||
u8 attrs = 0;
|
||||
if(bank1)
|
||||
attrs = bank1[tile_map_address];
|
||||
tile_map_address++;
|
||||
|
||||
if((register_LCDC & 16) == 0) {
|
||||
if(tile < 128) tile += 128;
|
||||
else tile -= 128;
|
||||
}
|
||||
|
||||
tile_pattern_address = tile_pattern + tile * 16 + by*2;
|
||||
|
||||
if (wx)
|
||||
for (i = 0; i<swx; i++)
|
||||
gbLineMix[i] = gbWindowColor[i];
|
||||
|
||||
while(x < 160) {
|
||||
u8 tile_a = 0;
|
||||
u8 tile_b = 0;
|
||||
|
||||
if(attrs & 0x40) {
|
||||
tile_pattern_address = tile_pattern + tile * 16 + (7-by)*2;
|
||||
}
|
||||
|
||||
if(attrs & 0x08) {
|
||||
tile_a = bank1[tile_pattern_address++];
|
||||
tile_b = bank1[tile_pattern_address];
|
||||
} else {
|
||||
tile_a = bank0[tile_pattern_address++];
|
||||
tile_b = bank0[tile_pattern_address];
|
||||
}
|
||||
|
||||
if(attrs & 0x20) {
|
||||
tile_a = gbInvertTab[tile_a];
|
||||
tile_b = gbInvertTab[tile_b];
|
||||
}
|
||||
|
||||
while(bx > 0) {
|
||||
u8 c = (tile_a & bx) != 0 ? 1 : 0;
|
||||
c += ((tile_b & bx) != 0 ? 2 : 0);
|
||||
|
||||
if (x>=0)
|
||||
{
|
||||
if(attrs & 0x80)
|
||||
gbLineBuffer[x] = 0x300 + c;
|
||||
else
|
||||
gbLineBuffer[x] = 0x100 + c;
|
||||
|
||||
if(gbCgbMode) {
|
||||
c = c + (attrs & 7) * 4;
|
||||
} else {
|
||||
c = (gbBgpLine[x+(gbSpeed ? 5 : 11)+gbSpritesTicks[x]*(gbSpeed ? 2 : 4)]>>(c<<1)) &3;
|
||||
if(gbSgbMode && !gbCgbMode) {
|
||||
int dx = x >> 3;
|
||||
int dy = y >> 3;
|
||||
|
||||
int palette = gbSgbATF[dy * 20 + dx];
|
||||
|
||||
if(c == 0)
|
||||
palette = 0;
|
||||
|
||||
c = c + 4*palette;
|
||||
}
|
||||
}
|
||||
gbLineMix[x] = gbColorOption ? gbColorFilter[gbPalette[c] & 0x7FFF] :
|
||||
gbPalette[c] & 0x7FFF;
|
||||
}
|
||||
x++;
|
||||
if(x >= 160)
|
||||
break;
|
||||
bx >>= 1;
|
||||
}
|
||||
tx++;
|
||||
if(tx == 32)
|
||||
tx = 0;
|
||||
bx = 128;
|
||||
tile = bank0[tile_map_line_y + tx];
|
||||
if(bank1)
|
||||
attrs = bank1[tile_map_line_y + tx];
|
||||
|
||||
if((register_LCDC & 16) == 0) {
|
||||
if(tile < 128) tile += 128;
|
||||
else tile -= 128;
|
||||
}
|
||||
tile_pattern_address = tile_pattern + tile * 16 + by * 2;
|
||||
}
|
||||
|
||||
//for (i = swx; i<160; i++)
|
||||
// gbLineMix[i] = gbWindowColor[i];
|
||||
gbWindowLine++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (gbWindowLine == -2)
|
||||
{
|
||||
inUseRegister_WY = oldRegister_WY;
|
||||
if (register_LY>oldRegister_WY)
|
||||
gbWindowLine = 146;
|
||||
else
|
||||
gbWindowLine = 0;
|
||||
}
|
||||
} else {
|
||||
u16 color = gbColorOption ? gbColorFilter[0x7FFF] :
|
||||
0x7FFF;
|
||||
if (!gbCgbMode)
|
||||
color = gbColorOption ? gbColorFilter[gbPalette[0] & 0x7FFF] :
|
||||
gbPalette[0] & 0x7FFF;
|
||||
for(int i = 0; i < 160; i++)
|
||||
{
|
||||
gbLineMix[i] = color;
|
||||
gbLineBuffer[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gbDrawSpriteTile(int tile, int x,int y,int t, int flags,
|
||||
int size,int spriteNumber)
|
||||
{
|
||||
u8 * bank0;
|
||||
u8 * bank1;
|
||||
if(gbCgbMode) {
|
||||
if(register_VBK & 1) {
|
||||
bank0 = &gbVram[0x0000];
|
||||
bank1 = &gbVram[0x2000];
|
||||
} else {
|
||||
bank0 = &gbVram[0x0000];
|
||||
bank1 = &gbVram[0x2000];
|
||||
}
|
||||
} else {
|
||||
bank0 = &gbMemory[0x8000];
|
||||
bank1 = NULL;
|
||||
}
|
||||
|
||||
int init = 0x0000;
|
||||
|
||||
for (int i = 0; i<4; i++)
|
||||
{
|
||||
gbObp0[i] = (gbObp0Line[x+11+gbSpritesTicks[x]*(gbSpeed ? 2 : 4)]>>(i<<1)) & 3;
|
||||
gbObp1[i] = (gbObp1Line[x+11+gbSpritesTicks[x]*(gbSpeed ? 2 : 4)]>>(i<<1)) & 3;
|
||||
}
|
||||
u8 *pal = gbObp0;
|
||||
|
||||
int flipx = (flags & 0x20);
|
||||
int flipy = (flags & 0x40);
|
||||
|
||||
if((flags & 0x10))
|
||||
pal = gbObp1;
|
||||
|
||||
if(flipy) {
|
||||
t = (size ? 15 : 7) - t;
|
||||
}
|
||||
|
||||
int prio = flags & 0x80;
|
||||
|
||||
int address = init + tile * 16 + 2*t;
|
||||
int a = 0;
|
||||
int b = 0;
|
||||
|
||||
if(gbCgbMode && (flags & 0x08)) {
|
||||
a = bank1[address++];
|
||||
b = bank1[address++];
|
||||
} else {
|
||||
a = bank0[address++];
|
||||
b = bank0[address++];
|
||||
}
|
||||
|
||||
for(int xx = 0; xx < 8; xx++) {
|
||||
u8 mask = 1 << (7-xx);
|
||||
u8 c = 0;
|
||||
if( (a & mask))
|
||||
c++;
|
||||
if( (b & mask))
|
||||
c+=2;
|
||||
|
||||
if(c==0) continue;
|
||||
|
||||
int xxx = xx+x;
|
||||
if(flipx)
|
||||
xxx = (7-xx+x);
|
||||
|
||||
if(xxx < 0 || xxx > 159)
|
||||
continue;
|
||||
|
||||
u16 color = gbLineBuffer[xxx];
|
||||
|
||||
// Fixes OAM-BG priority
|
||||
if(prio && (register_LCDC & 1)) {
|
||||
if(color < 0x200 && ((color & 0xFF) != 0))
|
||||
continue;
|
||||
}
|
||||
// Fixes OAM-BG priority for Moorhuhn 2
|
||||
if(color >= 0x300 && color != 0x300 && (register_LCDC & 1))
|
||||
continue;
|
||||
else if(color >= 0x200 && color < 0x300) {
|
||||
int sprite = color & 0xff;
|
||||
|
||||
int spriteX = gbMemory[0xfe00 + 4 * sprite + 1] - 8;
|
||||
|
||||
if(spriteX == x) {
|
||||
if(sprite < spriteNumber)
|
||||
continue;
|
||||
} else {
|
||||
if(gbCgbMode) {
|
||||
if(sprite < spriteNumber)
|
||||
continue;
|
||||
} else {
|
||||
// Fixes GB sprites priorities (was '< x + 8' before)
|
||||
// ('A boy and his blob...' sprites' emulation is now correct)
|
||||
if(spriteX < x)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gbLineBuffer[xxx] = 0x200 + spriteNumber;
|
||||
|
||||
// make sure that sprites will work even in CGB mode
|
||||
if(gbCgbMode) {
|
||||
c = c + (flags & 0x07)*4 + 32;
|
||||
} else {
|
||||
c = pal[c];
|
||||
|
||||
if(gbSgbMode && !gbCgbMode) {
|
||||
int dx = xxx >> 3;
|
||||
int dy = y >> 3;
|
||||
|
||||
int palette = gbSgbATF[dy * 20 + dx];
|
||||
|
||||
if(c == 0)
|
||||
palette = 0;
|
||||
|
||||
c = c + 4*palette;
|
||||
} else {
|
||||
c += 4;
|
||||
}
|
||||
}
|
||||
|
||||
gbLineMix[xxx] = gbColorOption ? gbColorFilter[gbPalette[c] & 0x7FFF] :
|
||||
gbPalette[c] & 0x7FFF;
|
||||
}
|
||||
}
|
||||
|
||||
void gbDrawSprites(bool draw)
|
||||
{
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int count = 0;
|
||||
|
||||
int size = (register_LCDC & 4);
|
||||
|
||||
if (!draw)
|
||||
memset (gbSpritesTicks, 0, sizeof(gbSpritesTicks));
|
||||
|
||||
if(!(register_LCDC & 0x80))
|
||||
return;
|
||||
|
||||
if((register_LCDC & 2) && (layerSettings & 0x1000)) {
|
||||
int yc = register_LY;
|
||||
|
||||
int address = 0xfe00;
|
||||
for(int i = 0; i < 40; i++) {
|
||||
y = gbMemory[address++];
|
||||
x = gbMemory[address++];
|
||||
int tile = gbMemory[address++];
|
||||
if(size)
|
||||
tile &= 254;
|
||||
int flags = gbMemory[address++];
|
||||
|
||||
if(x > 0 && y > 0 && x < 168 && y < 160) {
|
||||
// check if sprite intersects current line
|
||||
int t = yc -y + 16;
|
||||
if((size && t >=0 && t < 16) || (!size && t >= 0 && t < 8)) {
|
||||
if (draw)
|
||||
gbDrawSpriteTile(tile,x-8,yc,t,flags,size,i);
|
||||
else
|
||||
{
|
||||
for (int j = x-8; j<300; j++)
|
||||
if (j>=0)
|
||||
if (gbSpeed)
|
||||
gbSpritesTicks[j] += 5;
|
||||
else
|
||||
gbSpritesTicks[j] += 2+(count&1);
|
||||
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
// sprite limit reached!
|
||||
if(count >= 10)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2005-2006 Forgotten and the VBA development team
|
||||
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2, or(at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include "../agb/GBA.h"
|
||||
|
||||
u8 *gbMemoryMap[16];
|
||||
|
||||
int gbRomSizeMask = 0;
|
||||
int gbRomSize = 0;
|
||||
int gbRamSizeMask = 0;
|
||||
int gbRamSize = 0;
|
||||
int gbTAMA5ramSize = 0;
|
||||
|
||||
u8 *gbMemory = NULL;
|
||||
u8 *gbVram = NULL;
|
||||
u8 *gbRom = NULL;
|
||||
u8 *gbRam = NULL;
|
||||
u8 *gbWram = NULL;
|
||||
u16 *gbLineBuffer = NULL;
|
||||
u8 *gbTAMA5ram = NULL;
|
||||
|
||||
u16 gbPalette[128];
|
||||
u8 gbBgp[4] = { 0, 1, 2, 3};
|
||||
u8 gbObp0[4] = { 0, 1, 2, 3};
|
||||
u8 gbObp1[4] = { 0, 1, 2, 3};
|
||||
int gbWindowLine = -1;
|
||||
|
||||
bool genericflashcardEnable = false;
|
||||
int gbCgbMode = 0;
|
||||
|
||||
u16 gbColorFilter[32768];
|
||||
int gbColorOption = 0;
|
||||
int gbPaletteOption = 0;
|
||||
int gbEmulatorType = 0;
|
||||
int gbBorderOn = 1;
|
||||
int gbBorderAutomatic = 0;
|
||||
int gbBorderLineSkip = 160;
|
||||
int gbBorderRowSkip = 0;
|
||||
int gbBorderColumnSkip = 0;
|
||||
int gbDmaTicks = 0;
|
||||
|
||||
u8 (*gbSerialFunction)(u8) = NULL;
|
|
@ -0,0 +1,89 @@
|
|||
// -*- C++ -*-
|
||||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2005-2006 Forgotten and the VBA development team
|
||||
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2, or(at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
extern int gbRomSizeMask;
|
||||
extern int gbRomSize;
|
||||
extern int gbRamSize;
|
||||
extern int gbRamSizeMask;
|
||||
extern int gbTAMA5ramSize;
|
||||
|
||||
extern bool useBios;
|
||||
extern bool skipBios;
|
||||
extern u8 *bios;
|
||||
|
||||
extern u8 *gbRom;
|
||||
extern u8 *gbRam;
|
||||
extern u8 *gbVram;
|
||||
extern u8 *gbWram;
|
||||
extern u8 *gbMemory;
|
||||
extern u16 *gbLineBuffer;
|
||||
extern u8 *gbTAMA5ram;
|
||||
|
||||
extern u8 *gbMemoryMap[16];
|
||||
|
||||
extern int gbFrameSkip;
|
||||
extern u16 gbColorFilter[32768];
|
||||
extern int gbColorOption;
|
||||
extern int gbPaletteOption;
|
||||
extern int gbEmulatorType;
|
||||
extern int gbBorderOn;
|
||||
extern int gbBorderAutomatic;
|
||||
extern int gbCgbMode;
|
||||
extern int gbSgbMode;
|
||||
extern int gbWindowLine;
|
||||
extern int gbSpeed;
|
||||
extern u8 gbBgp[4];
|
||||
extern u8 gbObp0[4];
|
||||
extern u8 gbObp1[4];
|
||||
extern u16 gbPalette[128];
|
||||
extern bool gbScreenOn;
|
||||
extern bool gbDrawWindow;
|
||||
extern u8 gbSCYLine[300];
|
||||
// gbSCXLine is used for the emulation (bug) of the SX change
|
||||
// found in the Artic Zone game.
|
||||
extern u8 gbSCXLine[300];
|
||||
// gbBgpLine is used for the emulation of the
|
||||
// Prehistorik Man's title screen scroller.
|
||||
extern u8 gbBgpLine[300];
|
||||
extern u8 gbObp0Line [300];
|
||||
extern u8 gbObp1Line [300];
|
||||
// gbSpritesTicks is used for the emulation of Parodius' Laser Beam.
|
||||
extern u8 gbSpritesTicks[300];
|
||||
|
||||
extern u8 register_LCDC;
|
||||
extern u8 register_LY;
|
||||
extern u8 register_SCY;
|
||||
extern u8 register_SCX;
|
||||
extern u8 register_WY;
|
||||
extern u8 register_WX;
|
||||
extern u8 register_VBK;
|
||||
extern u8 oldRegister_WY;
|
||||
|
||||
extern int emulating;
|
||||
extern bool genericflashcardEnable;
|
||||
|
||||
extern int gbBorderLineSkip;
|
||||
extern int gbBorderRowSkip;
|
||||
extern int gbBorderColumnSkip;
|
||||
extern int gbDmaTicks;
|
||||
|
||||
extern void gbRenderLine();
|
||||
extern void gbDrawSprites(bool);
|
||||
|
||||
extern u8 (*gbSerialFunction)(u8);
|
|
@ -0,0 +1,89 @@
|
|||
// -*- C++ -*-
|
||||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2005-2006 Forgotten and the VBA development team
|
||||
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2, or(at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
extern int gbRomSizeMask;
|
||||
extern int gbRomSize;
|
||||
extern int gbRamSize;
|
||||
extern int gbRamSizeMask;
|
||||
extern int gbTAMA5ramSize;
|
||||
|
||||
extern bool useBios;
|
||||
extern bool skipBios;
|
||||
extern u8 *bios;
|
||||
|
||||
extern u8 *gbRom;
|
||||
extern u8 *gbRam;
|
||||
extern u8 *gbVram;
|
||||
extern u8 *gbWram;
|
||||
extern u8 *gbMemory;
|
||||
extern u16 *gbLineBuffer;
|
||||
extern u8 *gbTAMA5ram;
|
||||
|
||||
extern u8 *gbMemoryMap[16];
|
||||
|
||||
extern int gbFrameSkip;
|
||||
extern u16 gbColorFilter[32768];
|
||||
extern int gbColorOption;
|
||||
extern int gbPaletteOption;
|
||||
extern int gbEmulatorType;
|
||||
extern int gbBorderOn;
|
||||
extern int gbBorderAutomatic;
|
||||
extern int gbCgbMode;
|
||||
extern int gbSgbMode;
|
||||
extern int gbWindowLine;
|
||||
extern int gbSpeed;
|
||||
extern u8 gbBgp[4];
|
||||
extern u8 gbObp0[4];
|
||||
extern u8 gbObp1[4];
|
||||
extern u16 gbPalette[128];
|
||||
extern bool gbScreenOn;
|
||||
extern bool gbDrawWindow;
|
||||
extern u8 gbSCYLine[300];
|
||||
// gbSCXLine is used for the emulation (bug) of the SX change
|
||||
// found in the Artic Zone game.
|
||||
extern u8 gbSCXLine[300];
|
||||
// gbBgpLine is used for the emulation of the
|
||||
// Prehistorik Man's title screen scroller.
|
||||
extern u8 gbBgpLine[300];
|
||||
extern u8 gbObp0Line [300];
|
||||
extern u8 gbObp1Line [300];
|
||||
// gbSpritesTicks is used for the emulation of Parodius' Laser Beam.
|
||||
extern u8 gbSpritesTicks[300];
|
||||
|
||||
extern u8 register_LCDC;
|
||||
extern u8 register_LY;
|
||||
extern u8 register_SCY;
|
||||
extern u8 register_SCX;
|
||||
extern u8 register_WY;
|
||||
extern u8 register_WX;
|
||||
extern u8 register_VBK;
|
||||
extern u8 oldRegister_WY;
|
||||
|
||||
extern int emulating;
|
||||
extern bool genericflashcardEnable;
|
||||
|
||||
extern int gbBorderLineSkip;
|
||||
extern int gbBorderRowSkip;
|
||||
extern int gbBorderColumnSkip;
|
||||
extern int gbDmaTicks;
|
||||
|
||||
extern void gbRenderLine();
|
||||
extern void gbDrawSprites(bool);
|
||||
|
||||
extern u8 (*gbSerialFunction)(u8);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,206 @@
|
|||
// -*- C++ -*-
|
||||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2004-2006 Forgotten and the VBA development team
|
||||
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2, or(at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include <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();
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,229 @@
|
|||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2004 Forgotten and the VBA development team
|
||||
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2, or(at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include <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;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// -*- C++ -*-
|
||||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2004 Forgotten and the VBA development team
|
||||
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2, or(at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
extern u8 gbPrinterSend(u8 b);
|
|
@ -0,0 +1,917 @@
|
|||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2005-2006 Forgotten and the VBA development team
|
||||
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2, or(at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include <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);
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
// -*- C++ -*-
|
||||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2004 Forgotten and the VBA development team
|
||||
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2, or(at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
void gbSgbInit();
|
||||
void gbSgbShutdown();
|
||||
void gbSgbCommand();
|
||||
void gbSgbResetPacketState();
|
||||
void gbSgbReset();
|
||||
void gbSgbDoBitTransfer(u8);
|
||||
void gbSgbSaveGame(gzFile);
|
||||
void gbSgbReadGame(gzFile, int version);
|
||||
void gbSgbRenderBorder();
|
||||
|
||||
extern u8 gbSgbATF[20*18];
|
||||
extern int gbSgbMode;
|
||||
extern int gbSgbMask;
|
||||
extern int gbSgbMultiplayer;
|
||||
extern u8 gbSgbNextController;
|
||||
extern int gbSgbPacketTimeout;
|
||||
extern u8 gbSgbReadingController;
|
||||
extern int gbSgbFourPlayers;
|
||||
|
||||
|
|
@ -0,0 +1,412 @@
|
|||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2005-2006 Forgotten and the VBA development team
|
||||
// Copyright (C) 2007-2008 VBA-M development team
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2, or(at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include <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 );
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
// -*- C++ -*-
|
||||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2004 Forgotten and the VBA development team
|
||||
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2, or(at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#define NR10 0xff10
|
||||
#define NR11 0xff11
|
||||
#define NR12 0xff12
|
||||
#define NR13 0xff13
|
||||
#define NR14 0xff14
|
||||
#define NR21 0xff16
|
||||
#define NR22 0xff17
|
||||
#define NR23 0xff18
|
||||
#define NR24 0xff19
|
||||
#define NR30 0xff1a
|
||||
#define NR31 0xff1b
|
||||
#define NR32 0xff1c
|
||||
#define NR33 0xff1d
|
||||
#define NR34 0xff1e
|
||||
#define NR41 0xff20
|
||||
#define NR42 0xff21
|
||||
#define NR43 0xff22
|
||||
#define NR44 0xff23
|
||||
#define NR50 0xff24
|
||||
#define NR51 0xff25
|
||||
#define NR52 0xff26
|
||||
|
||||
#define SOUND_EVENT(address,value) \
|
||||
gbSoundEvent(address,value)
|
||||
|
||||
extern void gbSoundTick();
|
||||
extern void gbSoundPause();
|
||||
extern void gbSoundResume();
|
||||
extern void gbSoundEnable(int);
|
||||
extern void gbSoundDisable(int);
|
||||
extern int gbSoundGetEnable();
|
||||
extern void gbSoundReset();
|
||||
extern void gbSoundSaveGame(gzFile);
|
||||
extern void gbSoundReadGame(int,gzFile);
|
||||
extern void gbSoundEvent(register u16, register int);
|
||||
extern void gbSoundSetQuality(int);
|
||||
|
||||
extern u8 gbSoundRead(u16 address);
|
||||
|
||||
extern int soundTicks;
|
||||
extern int soundQuality;
|
||||
extern int SOUND_CLOCK_TICKS;
|
||||
|
||||
struct gb_effects_config_t
|
||||
{
|
||||
bool enabled; // false = disable all effects
|
||||
|
||||
float echo; // 0.0 = none, 1.0 = lots
|
||||
float stereo; // 0.0 = channels in center, 1.0 = channels on left/right
|
||||
bool surround; // true = put some channels in back
|
||||
};
|
||||
|
||||
// Can be changed at any time, probably from another thread too.
|
||||
// Sound will notice changes during next 1/100 second.
|
||||
extern gb_effects_config_t gb_effects_config;
|
|
@ -0,0 +1,465 @@
|
|||
// 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 );
|
||||
}
|
|
@ -0,0 +1,556 @@
|
|||
// 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
|
|
@ -0,0 +1,638 @@
|
|||
// 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 );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
// Multi-channel effects buffer with echo and individual panning for each channel
|
||||
|
||||
// Game_Music_Emu $vers
|
||||
#ifndef EFFECTS_BUFFER_H
|
||||
#define EFFECTS_BUFFER_H
|
||||
|
||||
#include "Multi_Buffer.h"
|
||||
|
||||
// See Simple_Effects_Buffer (below) for a simpler interface
|
||||
|
||||
class Effects_Buffer : public Multi_Buffer {
|
||||
public:
|
||||
// To reduce memory usage, fewer buffers can be used (with a best-fit
|
||||
// approach if there are too few), and maximum echo delay can be reduced
|
||||
Effects_Buffer( int max_bufs = 32, long echo_size = 24 * 1024L );
|
||||
|
||||
struct pan_vol_t
|
||||
{
|
||||
float vol; // 0.0 = silent, 0.5 = half volume, 1.0 = normal
|
||||
float pan; // -1.0 = left, 0.0 = center, +1.0 = right
|
||||
};
|
||||
|
||||
// Global configuration
|
||||
struct config_t
|
||||
{
|
||||
bool enabled; // false = disable all effects
|
||||
|
||||
// Current sound is echoed at adjustable left/right delay,
|
||||
// with reduced treble and volume (feedback).
|
||||
float treble; // 1.0 = full treble, 0.1 = very little, 0.0 = silent
|
||||
int delay [2]; // left, right delays (msec)
|
||||
float feedback; // 0.0 = no echo, 0.5 = each echo half previous, 1.0 = cacophony
|
||||
pan_vol_t side_chans [2]; // left and right side channel volume and pan
|
||||
};
|
||||
config_t& config() { return config_; }
|
||||
|
||||
// Limits of delay (msec)
|
||||
int min_delay() const;
|
||||
int max_delay() const;
|
||||
|
||||
// Per-channel configuration. Two or more channels with matching parameters are
|
||||
// optimized to internally use the same buffer.
|
||||
struct chan_config_t : pan_vol_t
|
||||
{
|
||||
// (inherited from pan_vol_t)
|
||||
//float vol; // these only affect center channel
|
||||
//float pan;
|
||||
bool surround; // if true, negates left volume to put sound in back
|
||||
bool echo; // false = channel doesn't have any echo
|
||||
};
|
||||
chan_config_t& chan_config( int i ) { return chans [i + extra_chans].cfg; }
|
||||
|
||||
// Apply any changes made to config() and chan_config()
|
||||
virtual void apply_config();
|
||||
|
||||
public:
|
||||
~Effects_Buffer();
|
||||
blargg_err_t set_sample_rate( long samples_per_sec, int msec = blip_default_length );
|
||||
blargg_err_t set_channel_count( int, int const* = 0 );
|
||||
void clock_rate( long );
|
||||
void bass_freq( int );
|
||||
void clear();
|
||||
channel_t channel( int );
|
||||
void end_frame( blip_time_t );
|
||||
long read_samples( blip_sample_t*, long );
|
||||
long samples_avail() const { return (bufs [0].samples_avail() - mixer.samples_read) * 2; }
|
||||
enum { stereo = 2 };
|
||||
typedef blargg_long fixed_t;
|
||||
protected:
|
||||
enum { extra_chans = stereo * stereo };
|
||||
private:
|
||||
config_t config_;
|
||||
long clock_rate_;
|
||||
int bass_freq_;
|
||||
|
||||
blargg_long echo_size;
|
||||
|
||||
struct chan_t
|
||||
{
|
||||
fixed_t vol [stereo];
|
||||
chan_config_t cfg;
|
||||
channel_t channel;
|
||||
};
|
||||
blargg_vector<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
|
|
@ -0,0 +1,394 @@
|
|||
// Gb_Snd_Emu 0.2.0. http://www.slack.net/~ant/
|
||||
|
||||
#include "Gb_Apu.h"
|
||||
|
||||
/* Copyright (C) 2003-2007 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details. You should have received a copy of the GNU Lesser General Public
|
||||
License along with this module; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#include "blargg_source.h"
|
||||
|
||||
unsigned const vol_reg = 0xFF24;
|
||||
unsigned const stereo_reg = 0xFF25;
|
||||
unsigned const status_reg = 0xFF26;
|
||||
unsigned const wave_ram = 0xFF30;
|
||||
|
||||
int const power_mask = 0x80;
|
||||
|
||||
void Gb_Apu::treble_eq( blip_eq_t const& eq )
|
||||
{
|
||||
good_synth.treble_eq( eq );
|
||||
med_synth .treble_eq( eq );
|
||||
}
|
||||
|
||||
inline int Gb_Apu::calc_output( int osc ) const
|
||||
{
|
||||
int bits = regs [stereo_reg - start_addr] >> osc;
|
||||
return (bits >> 3 & 2) | (bits & 1);
|
||||
}
|
||||
|
||||
void Gb_Apu::set_output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right, int osc )
|
||||
{
|
||||
// Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL)
|
||||
require( !center || (center && !left && !right) || (center && left && right) );
|
||||
require( (unsigned) osc <= osc_count ); // fails if you pass invalid osc index
|
||||
|
||||
if ( !center || !left || !right )
|
||||
{
|
||||
left = center;
|
||||
right = center;
|
||||
}
|
||||
|
||||
int i = (unsigned) osc % osc_count;
|
||||
do
|
||||
{
|
||||
Gb_Osc& o = *oscs [i];
|
||||
o.outputs [1] = right;
|
||||
o.outputs [2] = left;
|
||||
o.outputs [3] = center;
|
||||
o.output = o.outputs [calc_output( i )];
|
||||
}
|
||||
while ( ++i < osc );
|
||||
}
|
||||
|
||||
void Gb_Apu::synth_volume( int iv )
|
||||
{
|
||||
double v = volume_ * 0.60 / osc_count / 15 /*steps*/ / 8 /*master vol range*/ * iv;
|
||||
good_synth.volume( v );
|
||||
med_synth .volume( v );
|
||||
}
|
||||
|
||||
void Gb_Apu::apply_volume()
|
||||
{
|
||||
// TODO: Doesn't handle differing left and right volumes (panning).
|
||||
// Not worth the complexity.
|
||||
int data = regs [vol_reg - start_addr];
|
||||
int left = data >> 4 & 7;
|
||||
int right = data & 7;
|
||||
//if ( data & 0x88 ) dprintf( "Vin: %02X\n", data & 0x88 );
|
||||
//if ( left != right ) dprintf( "l: %d r: %d\n", left, right );
|
||||
synth_volume( max( left, right ) + 1 );
|
||||
}
|
||||
|
||||
void Gb_Apu::volume( double v )
|
||||
{
|
||||
if ( volume_ != v )
|
||||
{
|
||||
volume_ = v;
|
||||
apply_volume();
|
||||
}
|
||||
}
|
||||
|
||||
void Gb_Apu::reset_regs()
|
||||
{
|
||||
for ( int i = 0; i < 0x20; i++ )
|
||||
regs [i] = 0;
|
||||
|
||||
square1.reset();
|
||||
square2.reset();
|
||||
wave .reset();
|
||||
noise .reset();
|
||||
|
||||
apply_volume();
|
||||
}
|
||||
|
||||
void Gb_Apu::reset_lengths()
|
||||
{
|
||||
square1.length_ctr = 64;
|
||||
square2.length_ctr = 64;
|
||||
wave .length_ctr = 256;
|
||||
noise .length_ctr = 64;
|
||||
}
|
||||
|
||||
void Gb_Apu::reduce_clicks( bool reduce )
|
||||
{
|
||||
reduce_clicks_ = reduce;
|
||||
|
||||
// Click reduction makes DAC off generate same output as volume 0
|
||||
int dac_off_amp = 0;
|
||||
if ( reduce && wave.mode != mode_agb ) // AGB already eliminates clicks
|
||||
dac_off_amp = -Gb_Osc::dac_bias;
|
||||
|
||||
for ( int i = 0; i < osc_count; i++ )
|
||||
oscs [i]->dac_off_amp = dac_off_amp;
|
||||
|
||||
// AGB always eliminates clicks on wave channel using same method
|
||||
if ( wave.mode == mode_agb )
|
||||
wave.dac_off_amp = -Gb_Osc::dac_bias;
|
||||
}
|
||||
|
||||
void Gb_Apu::reset( mode_t mode, bool agb_wave )
|
||||
{
|
||||
// Hardware mode
|
||||
if ( agb_wave )
|
||||
mode = mode_agb; // using AGB wave features implies AGB hardware
|
||||
wave.agb_mask = agb_wave ? 0xFF : 0;
|
||||
for ( int i = 0; i < osc_count; i++ )
|
||||
oscs [i]->mode = mode;
|
||||
reduce_clicks( reduce_clicks_ );
|
||||
|
||||
// Reset state
|
||||
frame_time = 0;
|
||||
last_time = 0;
|
||||
frame_phase = 0;
|
||||
|
||||
reset_regs();
|
||||
reset_lengths();
|
||||
|
||||
// Load initial wave RAM
|
||||
static byte const initial_wave [2] [16] = {
|
||||
{0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C,0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA},
|
||||
{0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF},
|
||||
};
|
||||
for ( int b = 2; --b >= 0; )
|
||||
{
|
||||
// Init both banks (does nothing if not in AGB mode)
|
||||
// TODO: verify that this works
|
||||
write_register( 0, 0xFF1A, b * 0x40 );
|
||||
for ( unsigned i = 0; i < sizeof initial_wave [0]; i++ )
|
||||
write_register( 0, i + wave_ram, initial_wave [(mode != mode_dmg)] [i] );
|
||||
}
|
||||
}
|
||||
|
||||
void Gb_Apu::set_tempo( double t )
|
||||
{
|
||||
frame_period = 4194304 / 512; // 512 Hz
|
||||
if ( t != 1.0 )
|
||||
frame_period = blip_time_t (frame_period / t);
|
||||
}
|
||||
|
||||
Gb_Apu::Gb_Apu()
|
||||
{
|
||||
wave.wave_ram = ®s [wave_ram - start_addr];
|
||||
|
||||
oscs [0] = &square1;
|
||||
oscs [1] = &square2;
|
||||
oscs [2] = &wave;
|
||||
oscs [3] = &noise;
|
||||
|
||||
for ( int i = osc_count; --i >= 0; )
|
||||
{
|
||||
Gb_Osc& o = *oscs [i];
|
||||
o.regs = ®s [i * 5];
|
||||
o.output = 0;
|
||||
o.outputs [0] = 0;
|
||||
o.outputs [1] = 0;
|
||||
o.outputs [2] = 0;
|
||||
o.outputs [3] = 0;
|
||||
o.good_synth = &good_synth;
|
||||
o.med_synth = &med_synth;
|
||||
}
|
||||
|
||||
reduce_clicks_ = false;
|
||||
set_tempo( 1.0 );
|
||||
volume_ = 1.0;
|
||||
reset();
|
||||
}
|
||||
|
||||
void Gb_Apu::run_until_( blip_time_t end_time )
|
||||
{
|
||||
while ( true )
|
||||
{
|
||||
// run oscillators
|
||||
blip_time_t time = end_time;
|
||||
if ( time > frame_time )
|
||||
time = frame_time;
|
||||
|
||||
square1.run( last_time, time );
|
||||
square2.run( last_time, time );
|
||||
wave .run( last_time, time );
|
||||
noise .run( last_time, time );
|
||||
last_time = time;
|
||||
|
||||
if ( time == end_time )
|
||||
break;
|
||||
|
||||
// run frame sequencer
|
||||
frame_time += frame_period * Gb_Osc::clk_mul;
|
||||
switch ( frame_phase++ )
|
||||
{
|
||||
case 2:
|
||||
case 6:
|
||||
// 128 Hz
|
||||
square1.clock_sweep();
|
||||
case 0:
|
||||
case 4:
|
||||
// 256 Hz
|
||||
square1.clock_length();
|
||||
square2.clock_length();
|
||||
wave .clock_length();
|
||||
noise .clock_length();
|
||||
break;
|
||||
|
||||
case 7:
|
||||
// 64 Hz
|
||||
frame_phase = 0;
|
||||
square1.clock_envelope();
|
||||
square2.clock_envelope();
|
||||
noise .clock_envelope();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void Gb_Apu::run_until( blip_time_t time )
|
||||
{
|
||||
require( time >= last_time ); // end_time must not be before previous time
|
||||
if ( time > last_time )
|
||||
run_until_( time );
|
||||
}
|
||||
|
||||
void Gb_Apu::end_frame( blip_time_t end_time )
|
||||
{
|
||||
if ( end_time > last_time )
|
||||
run_until( end_time );
|
||||
|
||||
frame_time -= end_time;
|
||||
assert( frame_time >= 0 );
|
||||
|
||||
last_time -= end_time;
|
||||
assert( last_time >= 0 );
|
||||
}
|
||||
|
||||
void Gb_Apu::silence_osc( Gb_Osc& o )
|
||||
{
|
||||
int delta = -o.last_amp;
|
||||
if ( delta )
|
||||
{
|
||||
o.last_amp = 0;
|
||||
if ( o.output )
|
||||
{
|
||||
o.output->set_modified();
|
||||
med_synth.offset( last_time, delta, o.output );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Gb_Apu::apply_stereo()
|
||||
{
|
||||
for ( int i = osc_count; --i >= 0; )
|
||||
{
|
||||
Gb_Osc& o = *oscs [i];
|
||||
Blip_Buffer* out = o.outputs [calc_output( i )];
|
||||
if ( o.output != out )
|
||||
{
|
||||
silence_osc( o );
|
||||
o.output = out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data )
|
||||
{
|
||||
require( (unsigned) data < 0x100 );
|
||||
|
||||
int reg = addr - start_addr;
|
||||
if ( (unsigned) reg >= register_count )
|
||||
{
|
||||
require( false );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( addr < status_reg && !(regs [status_reg - start_addr] & power_mask) )
|
||||
{
|
||||
// Power is off
|
||||
|
||||
// length counters can only be written in DMG mode
|
||||
if ( wave.mode != mode_dmg || (reg != 1 && reg != 5+1 && reg != 10+1 && reg != 15+1) )
|
||||
return;
|
||||
|
||||
if ( reg < 10 )
|
||||
data &= 0x3F; // clear square duty
|
||||
}
|
||||
|
||||
run_until( time );
|
||||
|
||||
if ( addr >= wave_ram )
|
||||
{
|
||||
wave.write( addr, data );
|
||||
}
|
||||
else
|
||||
{
|
||||
int old_data = regs [reg];
|
||||
regs [reg] = data;
|
||||
|
||||
if ( addr < vol_reg )
|
||||
{
|
||||
// Oscillator
|
||||
write_osc( reg / 5, reg, old_data, data );
|
||||
}
|
||||
else if ( addr == vol_reg && data != old_data )
|
||||
{
|
||||
// Master volume
|
||||
for ( int i = osc_count; --i >= 0; )
|
||||
silence_osc( *oscs [i] );
|
||||
|
||||
apply_volume();
|
||||
}
|
||||
else if ( addr == stereo_reg )
|
||||
{
|
||||
// Stereo panning
|
||||
apply_stereo();
|
||||
}
|
||||
else if ( addr == status_reg && (data ^ old_data) & power_mask )
|
||||
{
|
||||
// Power control
|
||||
frame_phase = 0;
|
||||
for ( int i = osc_count; --i >= 0; )
|
||||
silence_osc( *oscs [i] );
|
||||
|
||||
reset_regs();
|
||||
if ( wave.mode != mode_dmg )
|
||||
reset_lengths();
|
||||
|
||||
regs [status_reg - start_addr] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Gb_Apu::read_register( blip_time_t time, unsigned addr )
|
||||
{
|
||||
run_until( time );
|
||||
|
||||
int reg = addr - start_addr;
|
||||
if ( (unsigned) reg >= register_count )
|
||||
{
|
||||
require( false );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( addr >= wave_ram )
|
||||
return wave.read( addr );
|
||||
|
||||
// Value read back has some bits always set
|
||||
static byte const masks [] = {
|
||||
0x80,0x3F,0x00,0xFF,0xBF,
|
||||
0xFF,0x3F,0x00,0xFF,0xBF,
|
||||
0x7F,0xFF,0x9F,0xFF,0xBF,
|
||||
0xFF,0xFF,0x00,0x00,0xBF,
|
||||
0x00,0x00,0x70,
|
||||
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
|
||||
};
|
||||
int mask = masks [reg];
|
||||
if ( wave.agb_mask && (reg == 10 || reg == 12) )
|
||||
mask = 0x1F; // extra implemented bits in wave regs on AGB
|
||||
int data = regs [reg] | mask;
|
||||
|
||||
// Status register
|
||||
if ( addr == status_reg )
|
||||
{
|
||||
data &= 0xF0;
|
||||
data |= (int) square1.enabled << 0;
|
||||
data |= (int) square2.enabled << 1;
|
||||
data |= (int) wave .enabled << 2;
|
||||
data |= (int) noise .enabled << 3;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
// Nintendo Game Boy sound hardware emulator with save state support
|
||||
|
||||
// Gb_Snd_Emu 0.2.0
|
||||
#ifndef GB_APU_H
|
||||
#define GB_APU_H
|
||||
|
||||
#include "Gb_Oscs.h"
|
||||
|
||||
struct gb_apu_state_t;
|
||||
|
||||
class Gb_Apu {
|
||||
public:
|
||||
// Basics
|
||||
|
||||
// Clock rate that sound hardware runs at.
|
||||
enum { clock_rate = 4194304 * GB_APU_OVERCLOCK };
|
||||
|
||||
// Sets buffer(s) to generate sound into. If left and right are NULL, output is mono.
|
||||
// If all are NULL, no output is generated but other emulation still runs.
|
||||
// If chan is specified, only that channel's output is changed, otherwise all are.
|
||||
enum { osc_count = 4 }; // 0: Square 1, 1: Square 2, 2: Wave, 3: Noise
|
||||
void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL,
|
||||
int chan = osc_count );
|
||||
|
||||
// Resets hardware to initial power on state BEFORE boot ROM runs. Mode selects
|
||||
// sound hardware. Additional AGB wave features are enabled separately.
|
||||
enum mode_t {
|
||||
mode_dmg, // Game Boy monochrome
|
||||
mode_cgb, // Game Boy Color
|
||||
mode_agb // Game Boy Advance
|
||||
};
|
||||
void reset( mode_t mode = mode_cgb, bool agb_wave = false );
|
||||
|
||||
// Reads and writes must be within the start_addr to end_addr range, inclusive.
|
||||
// Addresses outside this range are not mapped to the sound hardware.
|
||||
enum { start_addr = 0xFF10 };
|
||||
enum { end_addr = 0xFF3F };
|
||||
enum { register_count = end_addr - start_addr + 1 };
|
||||
|
||||
// Times are specified as the number of clocks since the beginning of the
|
||||
// current time frame.
|
||||
|
||||
// Emulates CPU write of data to addr at specified time.
|
||||
void write_register( blip_time_t time, unsigned addr, int data );
|
||||
|
||||
// Emulates CPU read from addr at specified time.
|
||||
int read_register( blip_time_t time, unsigned addr );
|
||||
|
||||
// Emulates sound hardware up to specified time, ends current time frame, then
|
||||
// starts a new frame at time 0.
|
||||
void end_frame( blip_time_t frame_length );
|
||||
|
||||
// Sound adjustments
|
||||
|
||||
// Sets overall volume, where 1.0 is normal.
|
||||
void volume( double );
|
||||
|
||||
// If true, reduces clicking by disabling DAC biasing. Note that this reduces
|
||||
// emulation accuracy, since the clicks are authentic.
|
||||
void reduce_clicks( bool reduce = true );
|
||||
|
||||
// Sets treble equalization.
|
||||
void treble_eq( blip_eq_t const& );
|
||||
|
||||
// Treble and bass values for various hardware.
|
||||
enum {
|
||||
speaker_treble = -47, // speaker on system
|
||||
speaker_bass = 2000,
|
||||
dmg_treble = 0, // headphones on each system
|
||||
dmg_bass = 30,
|
||||
cgb_treble = 0,
|
||||
cgb_bass = 300, // CGB has much less bass
|
||||
agb_treble = 0,
|
||||
agb_bass = 30
|
||||
};
|
||||
|
||||
// Sets frame sequencer rate, where 1.0 is normal. Meant for adjusting the
|
||||
// tempo in a game music player.
|
||||
void set_tempo( double );
|
||||
|
||||
// Save states
|
||||
|
||||
// Saves full emulation state to state_out. Data format is portable and
|
||||
// includes some extra space to avoid expansion in case more state needs
|
||||
// to be stored in the future.
|
||||
void save_state( gb_apu_state_t* state_out );
|
||||
|
||||
// Loads state. You should call reset() BEFORE this.
|
||||
blargg_err_t load_state( gb_apu_state_t const& in );
|
||||
|
||||
public:
|
||||
Gb_Apu();
|
||||
|
||||
// Use set_output() in place of these
|
||||
BLARGG_DEPRECATED void output ( Blip_Buffer* c ) { set_output( c, c, c ); }
|
||||
BLARGG_DEPRECATED void output ( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( c, l, r ); }
|
||||
BLARGG_DEPRECATED void osc_output( int i, Blip_Buffer* c ) { set_output( c, c, c, i ); }
|
||||
BLARGG_DEPRECATED void osc_output( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( c, l, r, i ); }
|
||||
|
||||
private:
|
||||
// noncopyable
|
||||
Gb_Apu( const Gb_Apu& );
|
||||
Gb_Apu& operator = ( const Gb_Apu& );
|
||||
|
||||
Gb_Osc* oscs [osc_count];
|
||||
blip_time_t last_time; // time sound emulator has been run to
|
||||
blip_time_t frame_period; // clocks between each frame sequencer step
|
||||
double volume_;
|
||||
bool reduce_clicks_;
|
||||
|
||||
Gb_Sweep_Square square1;
|
||||
Gb_Square square2;
|
||||
Gb_Wave wave;
|
||||
Gb_Noise noise;
|
||||
blip_time_t frame_time; // time of next frame sequencer action
|
||||
int frame_phase; // phase of next frame sequencer step
|
||||
enum { regs_size = register_count + 0x10 };
|
||||
BOOST::uint8_t regs [regs_size];// last values written to registers
|
||||
|
||||
// large objects after everything else
|
||||
Gb_Osc::Good_Synth good_synth;
|
||||
Gb_Osc::Med_Synth med_synth;
|
||||
|
||||
void reset_lengths();
|
||||
void reset_regs();
|
||||
int calc_output( int osc ) const;
|
||||
void apply_stereo();
|
||||
void apply_volume();
|
||||
void synth_volume( int );
|
||||
void run_until_( blip_time_t );
|
||||
void run_until( blip_time_t );
|
||||
void silence_osc( Gb_Osc& );
|
||||
void write_osc( int index, int reg, int old_data, int data );
|
||||
const char* save_load( gb_apu_state_t*, bool save );
|
||||
void save_load2( gb_apu_state_t*, bool save );
|
||||
friend class Gb_Apu_Tester;
|
||||
};
|
||||
|
||||
// Format of save state. Should be stable across versions of the library,
|
||||
// with earlier versions properly opening later save states. Includes some
|
||||
// room for expansion so the state size shouldn't increase.
|
||||
struct gb_apu_state_t
|
||||
{
|
||||
#if GB_APU_CUSTOM_STATE
|
||||
// Values stored as plain int so your code can read/write them easily.
|
||||
// Structure can NOT be written to disk, since format is not portable.
|
||||
typedef int val_t;
|
||||
#else
|
||||
// Values written in portable little-endian format, allowing structure
|
||||
// to be written directly to disk.
|
||||
typedef unsigned char val_t [4];
|
||||
#endif
|
||||
|
||||
enum { format0 = 0x50414247 };
|
||||
|
||||
val_t format; // format of all following data
|
||||
val_t version; // later versions just add fields to end
|
||||
|
||||
unsigned char regs [0x40];
|
||||
val_t frame_time;
|
||||
val_t frame_phase;
|
||||
|
||||
val_t sweep_freq;
|
||||
val_t sweep_delay;
|
||||
val_t sweep_enabled;
|
||||
val_t sweep_neg;
|
||||
val_t noise_divider;
|
||||
val_t wave_buf;
|
||||
|
||||
val_t delay [4];
|
||||
val_t length_ctr [4];
|
||||
val_t phase [4];
|
||||
val_t enabled [4];
|
||||
|
||||
val_t env_delay [3];
|
||||
val_t env_volume [3];
|
||||
val_t env_enabled [3];
|
||||
|
||||
val_t unused [13]; // for future expansion
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,118 @@
|
|||
// 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;
|
||||
}
|
|
@ -0,0 +1,665 @@
|
|||
// Gb_Snd_Emu 0.2.0. http://www.slack.net/~ant/
|
||||
|
||||
#include "Gb_Apu.h"
|
||||
|
||||
/* Copyright (C) 2003-2007 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details. You should have received a copy of the GNU Lesser General Public
|
||||
License along with this module; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#include "blargg_source.h"
|
||||
|
||||
bool const cgb_02 = false; // enables bug in early CGB units that causes problems in some games
|
||||
bool const cgb_05 = false; // enables CGB-05 zombie behavior
|
||||
|
||||
int const trigger_mask = 0x80;
|
||||
int const length_enabled = 0x40;
|
||||
|
||||
void Gb_Osc::reset()
|
||||
{
|
||||
output = 0;
|
||||
last_amp = 0;
|
||||
delay = 0;
|
||||
phase = 0;
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
inline void Gb_Osc::update_amp( blip_time_t time, int new_amp )
|
||||
{
|
||||
output->set_modified();
|
||||
int delta = new_amp - last_amp;
|
||||
if ( delta )
|
||||
{
|
||||
last_amp = new_amp;
|
||||
med_synth->offset( time, delta, output );
|
||||
}
|
||||
}
|
||||
|
||||
// Units
|
||||
|
||||
void Gb_Osc::clock_length()
|
||||
{
|
||||
if ( (regs [4] & length_enabled) && length_ctr )
|
||||
{
|
||||
if ( --length_ctr <= 0 )
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
inline int Gb_Env::reload_env_timer()
|
||||
{
|
||||
int raw = regs [2] & 7;
|
||||
env_delay = (raw ? raw : 8);
|
||||
return raw;
|
||||
}
|
||||
|
||||
void Gb_Env::clock_envelope()
|
||||
{
|
||||
if ( env_enabled && --env_delay <= 0 && reload_env_timer() )
|
||||
{
|
||||
int v = volume + (regs [2] & 0x08 ? +1 : -1);
|
||||
if ( 0 <= v && v <= 15 )
|
||||
volume = v;
|
||||
else
|
||||
env_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
inline void Gb_Sweep_Square::reload_sweep_timer()
|
||||
{
|
||||
sweep_delay = (regs [0] & period_mask) >> 4;
|
||||
if ( !sweep_delay )
|
||||
sweep_delay = 8;
|
||||
}
|
||||
|
||||
void Gb_Sweep_Square::calc_sweep( bool update )
|
||||
{
|
||||
int const shift = regs [0] & shift_mask;
|
||||
int const delta = sweep_freq >> shift;
|
||||
sweep_neg = (regs [0] & 0x08) != 0;
|
||||
int const freq = sweep_freq + (sweep_neg ? -delta : delta);
|
||||
|
||||
if ( freq > 0x7FF )
|
||||
{
|
||||
enabled = false;
|
||||
}
|
||||
else if ( shift && update )
|
||||
{
|
||||
sweep_freq = freq;
|
||||
|
||||
regs [3] = freq & 0xFF;
|
||||
regs [4] = (regs [4] & ~0x07) | (freq >> 8 & 0x07);
|
||||
}
|
||||
}
|
||||
|
||||
void Gb_Sweep_Square::clock_sweep()
|
||||
{
|
||||
if ( --sweep_delay <= 0 )
|
||||
{
|
||||
reload_sweep_timer();
|
||||
if ( sweep_enabled && (regs [0] & period_mask) )
|
||||
{
|
||||
calc_sweep( true );
|
||||
calc_sweep( false );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Gb_Wave::access( unsigned addr ) const
|
||||
{
|
||||
if ( enabled )
|
||||
{
|
||||
addr = phase & (bank_size - 1);
|
||||
if ( mode == Gb_Apu::mode_dmg )
|
||||
{
|
||||
addr++;
|
||||
if ( delay > clk_mul )
|
||||
return -1; // can only access within narrow time window while playing
|
||||
}
|
||||
addr >>= 1;
|
||||
}
|
||||
return addr & 0x0F;
|
||||
}
|
||||
|
||||
// write_register
|
||||
|
||||
int Gb_Osc::write_trig( int frame_phase, int max_len, int old_data )
|
||||
{
|
||||
int data = regs [4];
|
||||
|
||||
if ( (frame_phase & 1) && !(old_data & length_enabled) && length_ctr )
|
||||
{
|
||||
if ( (data & length_enabled) || cgb_02 )
|
||||
length_ctr--;
|
||||
}
|
||||
|
||||
if ( data & trigger_mask )
|
||||
{
|
||||
enabled = true;
|
||||
if ( !length_ctr )
|
||||
{
|
||||
length_ctr = max_len;
|
||||
if ( (frame_phase & 1) && (data & length_enabled) )
|
||||
length_ctr--;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !length_ctr )
|
||||
enabled = false;
|
||||
|
||||
return data & trigger_mask;
|
||||
}
|
||||
|
||||
inline void Gb_Env::zombie_volume( int old, int data )
|
||||
{
|
||||
int v = volume;
|
||||
if ( mode == Gb_Apu::mode_agb || cgb_05 )
|
||||
{
|
||||
// CGB-05 behavior, very close to AGB behavior as well
|
||||
if ( (old ^ data) & 8 )
|
||||
{
|
||||
if ( !(old & 8) )
|
||||
{
|
||||
v++;
|
||||
if ( old & 7 )
|
||||
v++;
|
||||
}
|
||||
|
||||
v = 16 - v;
|
||||
}
|
||||
else if ( (old & 0x0F) == 8 )
|
||||
{
|
||||
v++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// CGB-04&02 behavior, very close to MGB behavior as well
|
||||
if ( !(old & 7) && env_enabled )
|
||||
v++;
|
||||
else if ( !(old & 8) )
|
||||
v += 2;
|
||||
|
||||
if ( (old ^ data) & 8 )
|
||||
v = 16 - v;
|
||||
}
|
||||
volume = v & 0x0F;
|
||||
}
|
||||
|
||||
bool Gb_Env::write_register( int frame_phase, int reg, int old, int data )
|
||||
{
|
||||
int const max_len = 64;
|
||||
|
||||
switch ( reg )
|
||||
{
|
||||
case 1:
|
||||
length_ctr = max_len - (data & (max_len - 1));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if ( !dac_enabled() )
|
||||
enabled = false;
|
||||
|
||||
zombie_volume( old, data );
|
||||
|
||||
if ( (data & 7) && env_delay == 8 )
|
||||
{
|
||||
env_delay = 1;
|
||||
clock_envelope(); // TODO: really happens at next length clock
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if ( write_trig( frame_phase, max_len, old ) )
|
||||
{
|
||||
volume = regs [2] >> 4;
|
||||
reload_env_timer();
|
||||
env_enabled = true;
|
||||
if ( frame_phase == 7 )
|
||||
env_delay++;
|
||||
if ( !dac_enabled() )
|
||||
enabled = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Gb_Square::write_register( int frame_phase, int reg, int old_data, int data )
|
||||
{
|
||||
bool result = Gb_Env::write_register( frame_phase, reg, old_data, data );
|
||||
if ( result )
|
||||
delay = (delay & (4 * clk_mul - 1)) + period();
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void Gb_Noise::write_register( int frame_phase, int reg, int old_data, int data )
|
||||
{
|
||||
if ( Gb_Env::write_register( frame_phase, reg, old_data, data ) )
|
||||
{
|
||||
phase = 0x7FFF;
|
||||
delay += 8 * clk_mul;
|
||||
}
|
||||
}
|
||||
|
||||
inline void Gb_Sweep_Square::write_register( int frame_phase, int reg, int old_data, int data )
|
||||
{
|
||||
if ( reg == 0 && sweep_enabled && sweep_neg && !(data & 0x08) )
|
||||
enabled = false; // sweep negate disabled after used
|
||||
|
||||
if ( Gb_Square::write_register( frame_phase, reg, old_data, data ) )
|
||||
{
|
||||
sweep_freq = frequency();
|
||||
sweep_neg = false;
|
||||
reload_sweep_timer();
|
||||
sweep_enabled = (regs [0] & (period_mask | shift_mask)) != 0;
|
||||
if ( regs [0] & shift_mask )
|
||||
calc_sweep( false );
|
||||
}
|
||||
}
|
||||
|
||||
void Gb_Wave::corrupt_wave()
|
||||
{
|
||||
int pos = ((phase + 1) & (bank_size - 1)) >> 1;
|
||||
if ( pos < 4 )
|
||||
wave_ram [0] = wave_ram [pos];
|
||||
else
|
||||
for ( int i = 4; --i >= 0; )
|
||||
wave_ram [i] = wave_ram [(pos & ~3) + i];
|
||||
}
|
||||
|
||||
inline void Gb_Wave::write_register( int frame_phase, int reg, int old_data, int data )
|
||||
{
|
||||
int const max_len = 256;
|
||||
|
||||
switch ( reg )
|
||||
{
|
||||
case 0:
|
||||
if ( !dac_enabled() )
|
||||
enabled = false;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
length_ctr = max_len - data;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
bool was_enabled = enabled;
|
||||
if ( write_trig( frame_phase, max_len, old_data ) )
|
||||
{
|
||||
if ( !dac_enabled() )
|
||||
enabled = false;
|
||||
else if ( mode == Gb_Apu::mode_dmg && was_enabled &&
|
||||
(unsigned) (delay - 2 * clk_mul) < 2 * clk_mul )
|
||||
corrupt_wave();
|
||||
|
||||
phase = 0;
|
||||
delay = period() + 6 * clk_mul;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Gb_Apu::write_osc( int index, int reg, int old_data, int data )
|
||||
{
|
||||
reg -= index * 5;
|
||||
switch ( index )
|
||||
{
|
||||
case 0: square1.write_register( frame_phase, reg, old_data, data ); break;
|
||||
case 1: square2.write_register( frame_phase, reg, old_data, data ); break;
|
||||
case 2: wave .write_register( frame_phase, reg, old_data, data ); break;
|
||||
case 3: noise .write_register( frame_phase, reg, old_data, data ); break;
|
||||
}
|
||||
}
|
||||
|
||||
// Synthesis
|
||||
|
||||
void Gb_Square::run( blip_time_t time, blip_time_t end_time )
|
||||
{
|
||||
// Calc duty and phase
|
||||
static byte const duty_offsets [4] = { 1, 1, 3, 7 };
|
||||
static byte const duties [4] = { 1, 2, 4, 6 };
|
||||
int const duty_code = regs [1] >> 6;
|
||||
int duty_offset = duty_offsets [duty_code];
|
||||
int duty = duties [duty_code];
|
||||
if ( mode == Gb_Apu::mode_agb )
|
||||
{
|
||||
// AGB uses inverted duty
|
||||
duty_offset -= duty;
|
||||
duty = 8 - duty;
|
||||
}
|
||||
int ph = (this->phase + duty_offset) & 7;
|
||||
|
||||
// Determine what will be generated
|
||||
int vol = 0;
|
||||
Blip_Buffer* const out = this->output;
|
||||
if ( out )
|
||||
{
|
||||
int amp = dac_off_amp;
|
||||
if ( dac_enabled() )
|
||||
{
|
||||
if ( enabled )
|
||||
vol = this->volume;
|
||||
|
||||
amp = -dac_bias;
|
||||
if ( mode == Gb_Apu::mode_agb )
|
||||
amp = -(vol >> 1);
|
||||
|
||||
// Play inaudible frequencies as constant amplitude
|
||||
if ( frequency() >= 0x7FA && delay < 32 * clk_mul )
|
||||
{
|
||||
amp += (vol * duty) >> 3;
|
||||
vol = 0;
|
||||
}
|
||||
|
||||
if ( ph < duty )
|
||||
{
|
||||
amp += vol;
|
||||
vol = -vol;
|
||||
}
|
||||
}
|
||||
update_amp( time, amp );
|
||||
}
|
||||
|
||||
// Generate wave
|
||||
time += delay;
|
||||
if ( time < end_time )
|
||||
{
|
||||
int const per = this->period();
|
||||
if ( !vol )
|
||||
{
|
||||
// Maintain phase when not playing
|
||||
int count = (end_time - time + per - 1) / per;
|
||||
ph += count; // will be masked below
|
||||
time += (blip_time_t) count * per;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Output amplitude transitions
|
||||
int delta = vol;
|
||||
do
|
||||
{
|
||||
ph = (ph + 1) & 7;
|
||||
if ( ph == 0 || ph == duty )
|
||||
{
|
||||
good_synth->offset_inline( time, delta, out );
|
||||
delta = -delta;
|
||||
}
|
||||
time += per;
|
||||
}
|
||||
while ( time < end_time );
|
||||
|
||||
if ( delta != vol )
|
||||
last_amp -= delta;
|
||||
}
|
||||
this->phase = (ph - duty_offset) & 7;
|
||||
}
|
||||
delay = time - end_time;
|
||||
}
|
||||
|
||||
// Quickly runs LFSR for a large number of clocks. For use when noise is generating
|
||||
// no sound.
|
||||
static unsigned run_lfsr( unsigned s, unsigned mask, int count )
|
||||
{
|
||||
bool const optimized = true; // set to false to use only unoptimized loop in middle
|
||||
|
||||
// optimization used in several places:
|
||||
// ((s & (1 << b)) << n) ^ ((s & (1 << b)) << (n + 1)) = (s & (1 << b)) * (3 << n)
|
||||
|
||||
if ( mask == 0x4000 && optimized )
|
||||
{
|
||||
if ( count >= 32767 )
|
||||
count %= 32767;
|
||||
|
||||
// Convert from Fibonacci to Galois configuration,
|
||||
// shifted left 1 bit
|
||||
s ^= (s & 1) * 0x8000;
|
||||
|
||||
// Each iteration is equivalent to clocking LFSR 255 times
|
||||
while ( (count -= 255) > 0 )
|
||||
s ^= ((s & 0xE) << 12) ^ ((s & 0xE) << 11) ^ (s >> 3);
|
||||
count += 255;
|
||||
|
||||
// Each iteration is equivalent to clocking LFSR 15 times
|
||||
// (interesting similarity to single clocking below)
|
||||
while ( (count -= 15) > 0 )
|
||||
s ^= ((s & 2) * (3 << 13)) ^ (s >> 1);
|
||||
count += 15;
|
||||
|
||||
// Remaining singles
|
||||
while ( --count >= 0 )
|
||||
s = ((s & 2) * (3 << 13)) ^ (s >> 1);
|
||||
|
||||
// Convert back to Fibonacci configuration
|
||||
s &= 0x7FFF;
|
||||
}
|
||||
else if ( count < 8 || !optimized )
|
||||
{
|
||||
// won't fully replace upper 8 bits, so have to do the unoptimized way
|
||||
while ( --count >= 0 )
|
||||
s = (s >> 1 | mask) ^ (mask & -((s - 1) & 2));
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( count > 127 )
|
||||
{
|
||||
count %= 127;
|
||||
if ( !count )
|
||||
count = 127; // must run at least once
|
||||
}
|
||||
|
||||
// Need to keep one extra bit of history
|
||||
s = s << 1 & 0xFF;
|
||||
|
||||
// Convert from Fibonacci to Galois configuration,
|
||||
// shifted left 2 bits
|
||||
s ^= (s & 2) * 0x80;
|
||||
|
||||
// Each iteration is equivalent to clocking LFSR 7 times
|
||||
// (interesting similarity to single clocking below)
|
||||
while ( (count -= 7) > 0 )
|
||||
s ^= ((s & 4) * (3 << 5)) ^ (s >> 1);
|
||||
count += 7;
|
||||
|
||||
// Remaining singles
|
||||
while ( --count >= 0 )
|
||||
s = ((s & 4) * (3 << 5)) ^ (s >> 1);
|
||||
|
||||
// Convert back to Fibonacci configuration and
|
||||
// repeat last 8 bits above significant 7
|
||||
s = (s << 7 & 0x7F80) | (s >> 1 & 0x7F);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void Gb_Noise::run( blip_time_t time, blip_time_t end_time )
|
||||
{
|
||||
// Determine what will be generated
|
||||
int vol = 0;
|
||||
Blip_Buffer* const out = this->output;
|
||||
if ( out )
|
||||
{
|
||||
int amp = dac_off_amp;
|
||||
if ( dac_enabled() )
|
||||
{
|
||||
if ( enabled )
|
||||
vol = this->volume;
|
||||
|
||||
amp = -dac_bias;
|
||||
if ( mode == Gb_Apu::mode_agb )
|
||||
amp = -(vol >> 1);
|
||||
|
||||
if ( !(phase & 1) )
|
||||
{
|
||||
amp += vol;
|
||||
vol = -vol;
|
||||
}
|
||||
}
|
||||
|
||||
// AGB negates final output
|
||||
if ( mode == Gb_Apu::mode_agb )
|
||||
{
|
||||
vol = -vol;
|
||||
amp = -amp;
|
||||
}
|
||||
|
||||
update_amp( time, amp );
|
||||
}
|
||||
|
||||
// Run timer and calculate time of next LFSR clock
|
||||
static byte const period1s [8] = { 1, 2, 4, 6, 8, 10, 12, 14 };
|
||||
int const period1 = period1s [regs [3] & 7] * clk_mul;
|
||||
{
|
||||
int extra = (end_time - time) - delay;
|
||||
int const per2 = this->period2();
|
||||
time += delay + ((divider ^ (per2 >> 1)) & (per2 - 1)) * period1;
|
||||
|
||||
int count = (extra < 0 ? 0 : (extra + period1 - 1) / period1);
|
||||
divider = (divider - count) & period2_mask;
|
||||
delay = count * period1 - extra;
|
||||
}
|
||||
|
||||
// Generate wave
|
||||
if ( time < end_time )
|
||||
{
|
||||
unsigned const mask = this->lfsr_mask();
|
||||
unsigned bits = this->phase;
|
||||
|
||||
int per = period2( period1 * 8 );
|
||||
if ( period2_index() >= 0xE )
|
||||
{
|
||||
time = end_time;
|
||||
}
|
||||
else if ( !vol )
|
||||
{
|
||||
// Maintain phase when not playing
|
||||
int count = (end_time - time + per - 1) / per;
|
||||
time += (blip_time_t) count * per;
|
||||
bits = run_lfsr( bits, ~mask, count );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Output amplitude transitions
|
||||
int delta = -vol;
|
||||
do
|
||||
{
|
||||
unsigned changed = bits + 1;
|
||||
bits = bits >> 1 & mask;
|
||||
if ( changed & 2 )
|
||||
{
|
||||
bits |= ~mask;
|
||||
delta = -delta;
|
||||
med_synth->offset_inline( time, delta, out );
|
||||
}
|
||||
time += per;
|
||||
}
|
||||
while ( time < end_time );
|
||||
|
||||
if ( delta == vol )
|
||||
last_amp += delta;
|
||||
}
|
||||
this->phase = bits;
|
||||
}
|
||||
}
|
||||
|
||||
void Gb_Wave::run( blip_time_t time, blip_time_t end_time )
|
||||
{
|
||||
// Calc volume
|
||||
static byte const volumes [8] = { 0, 4, 2, 1, 3, 3, 3, 3 };
|
||||
int const volume_shift = 2;
|
||||
int const volume_idx = regs [2] >> 5 & (agb_mask | 3); // 2 bits on DMG/CGB, 3 on AGB
|
||||
int const volume_mul = volumes [volume_idx];
|
||||
|
||||
// Determine what will be generated
|
||||
int playing = false;
|
||||
Blip_Buffer* const out = this->output;
|
||||
if ( out )
|
||||
{
|
||||
int amp = dac_off_amp;
|
||||
if ( dac_enabled() )
|
||||
{
|
||||
// Play inaudible frequencies as constant amplitude
|
||||
amp = 8 << 4; // really depends on average of all samples in wave
|
||||
|
||||
// if delay is larger, constant amplitude won't start yet
|
||||
if ( frequency() <= 0x7FB || delay > 15 * clk_mul )
|
||||
{
|
||||
if ( volume_mul )
|
||||
playing = (int) enabled;
|
||||
|
||||
amp = (sample_buf << (phase << 2 & 4) & 0xF0) * playing;
|
||||
}
|
||||
|
||||
amp = ((amp * volume_mul) >> (volume_shift + 4)) - dac_bias;
|
||||
}
|
||||
update_amp( time, amp );
|
||||
}
|
||||
|
||||
// Generate wave
|
||||
time += delay;
|
||||
if ( time < end_time )
|
||||
{
|
||||
byte const* wave = this->wave_ram;
|
||||
|
||||
// wave size and bank
|
||||
int const size20_mask = 0x20;
|
||||
int const flags = regs [0] & agb_mask;
|
||||
int const wave_mask = (flags & size20_mask) | 0x1F;
|
||||
int swap_banks = 0;
|
||||
if ( flags & bank40_mask )
|
||||
{
|
||||
swap_banks = flags & size20_mask;
|
||||
wave += bank_size/2 - (swap_banks >> 1);
|
||||
}
|
||||
|
||||
int ph = this->phase ^ swap_banks;
|
||||
ph = (ph + 1) & wave_mask; // pre-advance
|
||||
|
||||
int const per = this->period();
|
||||
if ( !playing )
|
||||
{
|
||||
// Maintain phase when not playing
|
||||
int count = (end_time - time + per - 1) / per;
|
||||
ph += count; // will be masked below
|
||||
time += (blip_time_t) count * per;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Output amplitude transitions
|
||||
int lamp = this->last_amp + dac_bias;
|
||||
do
|
||||
{
|
||||
// Extract nybble
|
||||
int nybble = wave [ph >> 1] << (ph << 2 & 4) & 0xF0;
|
||||
ph = (ph + 1) & wave_mask;
|
||||
|
||||
// Scale by volume
|
||||
int amp = (nybble * volume_mul) >> (volume_shift + 4);
|
||||
|
||||
int delta = amp - lamp;
|
||||
if ( delta )
|
||||
{
|
||||
lamp = amp;
|
||||
med_synth->offset_inline( time, delta, out );
|
||||
}
|
||||
time += per;
|
||||
}
|
||||
while ( time < end_time );
|
||||
this->last_amp = lamp - dac_bias;
|
||||
}
|
||||
ph = (ph - 1) & wave_mask; // undo pre-advance and mask position
|
||||
|
||||
// Keep track of last byte read
|
||||
if ( enabled )
|
||||
sample_buf = wave [ph >> 1];
|
||||
|
||||
this->phase = ph ^ swap_banks; // undo swapped banks
|
||||
}
|
||||
delay = time - end_time;
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
// Private oscillators used by Gb_Apu
|
||||
|
||||
// Gb_Snd_Emu 0.2.0
|
||||
#ifndef GB_OSCS_H
|
||||
#define GB_OSCS_H
|
||||
|
||||
#include "blargg_common.h"
|
||||
#include "Blip_Buffer.h"
|
||||
|
||||
#ifndef GB_APU_OVERCLOCK
|
||||
#define GB_APU_OVERCLOCK 1
|
||||
#endif
|
||||
|
||||
#if GB_APU_OVERCLOCK & (GB_APU_OVERCLOCK - 1)
|
||||
#error "GB_APU_OVERCLOCK must be a power of 2"
|
||||
#endif
|
||||
|
||||
class Gb_Osc {
|
||||
protected:
|
||||
|
||||
// 11-bit frequency in NRx3 and NRx4
|
||||
int frequency() const { return (regs [4] & 7) * 0x100 + regs [3]; }
|
||||
|
||||
void update_amp( blip_time_t, int new_amp );
|
||||
int write_trig( int frame_phase, int max_len, int old_data );
|
||||
public:
|
||||
|
||||
enum { clk_mul = GB_APU_OVERCLOCK };
|
||||
enum { dac_bias = 7 };
|
||||
|
||||
Blip_Buffer* outputs [4];// NULL, right, left, center
|
||||
Blip_Buffer* output; // where to output sound
|
||||
BOOST::uint8_t* regs; // osc's 5 registers
|
||||
int mode; // mode_dmg, mode_cgb, mode_agb
|
||||
int dac_off_amp;// amplitude when DAC is off
|
||||
int last_amp; // current amplitude in Blip_Buffer
|
||||
typedef Blip_Synth<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
|
|
@ -0,0 +1,281 @@
|
|||
// Blip_Buffer 0.4.1. http://www.slack.net/~ant/
|
||||
|
||||
#include "Multi_Buffer.h"
|
||||
|
||||
/* Copyright (C) 2003-2007 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details. You should have received a copy of the GNU Lesser General Public
|
||||
License along with this module; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#include "blargg_source.h"
|
||||
|
||||
#ifdef BLARGG_ENABLE_OPTIMIZER
|
||||
#include BLARGG_ENABLE_OPTIMIZER
|
||||
#endif
|
||||
|
||||
Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf )
|
||||
{
|
||||
length_ = 0;
|
||||
sample_rate_ = 0;
|
||||
channels_changed_count_ = 1;
|
||||
channel_types_ = 0;
|
||||
channel_count_ = 0;
|
||||
immediate_removal_ = true;
|
||||
}
|
||||
|
||||
Multi_Buffer::channel_t Multi_Buffer::channel( int /*index*/ )
|
||||
{
|
||||
static channel_t const ch = { 0, 0, 0 };
|
||||
return ch;
|
||||
}
|
||||
|
||||
// Silent_Buffer
|
||||
|
||||
Silent_Buffer::Silent_Buffer() : Multi_Buffer( 1 ) // 0 channels would probably confuse
|
||||
{
|
||||
// TODO: better to use empty Blip_Buffer so caller never has to check for NULL?
|
||||
chan.left = 0;
|
||||
chan.center = 0;
|
||||
chan.right = 0;
|
||||
}
|
||||
|
||||
// Mono_Buffer
|
||||
|
||||
Mono_Buffer::Mono_Buffer() : Multi_Buffer( 1 )
|
||||
{
|
||||
chan.center = &buf;
|
||||
chan.left = &buf;
|
||||
chan.right = &buf;
|
||||
}
|
||||
|
||||
Mono_Buffer::~Mono_Buffer() { }
|
||||
|
||||
blargg_err_t Mono_Buffer::set_sample_rate( long rate, int msec )
|
||||
{
|
||||
RETURN_ERR( buf.set_sample_rate( rate, msec ) );
|
||||
return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() );
|
||||
}
|
||||
|
||||
|
||||
// Tracked_Blip_Buffer
|
||||
|
||||
Tracked_Blip_Buffer::Tracked_Blip_Buffer()
|
||||
{
|
||||
last_non_silence = 0;
|
||||
}
|
||||
|
||||
void Tracked_Blip_Buffer::clear()
|
||||
{
|
||||
last_non_silence = 0;
|
||||
Blip_Buffer::clear();
|
||||
}
|
||||
|
||||
void Tracked_Blip_Buffer::end_frame( blip_time_t t )
|
||||
{
|
||||
Blip_Buffer::end_frame( t );
|
||||
if ( clear_modified() )
|
||||
last_non_silence = samples_avail() + blip_buffer_extra_;
|
||||
}
|
||||
|
||||
blip_ulong Tracked_Blip_Buffer::non_silent() const
|
||||
{
|
||||
return last_non_silence | unsettled();
|
||||
}
|
||||
|
||||
inline void Tracked_Blip_Buffer::remove_( long n )
|
||||
{
|
||||
if ( (last_non_silence -= n) < 0 )
|
||||
last_non_silence = 0;
|
||||
}
|
||||
|
||||
void Tracked_Blip_Buffer::remove_silence( long n )
|
||||
{
|
||||
remove_( n );
|
||||
Blip_Buffer::remove_silence( n );
|
||||
}
|
||||
|
||||
void Tracked_Blip_Buffer::remove_samples( long n )
|
||||
{
|
||||
remove_( n );
|
||||
Blip_Buffer::remove_samples( n );
|
||||
}
|
||||
|
||||
void Tracked_Blip_Buffer::remove_all_samples()
|
||||
{
|
||||
long avail = samples_avail();
|
||||
if ( !non_silent() )
|
||||
remove_silence( avail );
|
||||
else
|
||||
remove_samples( avail );
|
||||
}
|
||||
|
||||
long Tracked_Blip_Buffer::read_samples( blip_sample_t* out, long count )
|
||||
{
|
||||
count = Blip_Buffer::read_samples( out, count );
|
||||
remove_( count );
|
||||
return count;
|
||||
}
|
||||
|
||||
// Stereo_Buffer
|
||||
|
||||
int const stereo = 2;
|
||||
|
||||
Stereo_Buffer::Stereo_Buffer() : Multi_Buffer( 2 )
|
||||
{
|
||||
chan.center = mixer.bufs [2] = &bufs [2];
|
||||
chan.left = mixer.bufs [0] = &bufs [0];
|
||||
chan.right = mixer.bufs [1] = &bufs [1];
|
||||
mixer.samples_read = 0;
|
||||
}
|
||||
|
||||
Stereo_Buffer::~Stereo_Buffer() { }
|
||||
|
||||
blargg_err_t Stereo_Buffer::set_sample_rate( long rate, int msec )
|
||||
{
|
||||
mixer.samples_read = 0;
|
||||
for ( int i = bufs_size; --i >= 0; )
|
||||
RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) );
|
||||
return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() );
|
||||
}
|
||||
|
||||
void Stereo_Buffer::clock_rate( long rate )
|
||||
{
|
||||
for ( int i = bufs_size; --i >= 0; )
|
||||
bufs [i].clock_rate( rate );
|
||||
}
|
||||
|
||||
void Stereo_Buffer::bass_freq( int bass )
|
||||
{
|
||||
for ( int i = bufs_size; --i >= 0; )
|
||||
bufs [i].bass_freq( bass );
|
||||
}
|
||||
|
||||
void Stereo_Buffer::clear()
|
||||
{
|
||||
mixer.samples_read = 0;
|
||||
for ( int i = bufs_size; --i >= 0; )
|
||||
bufs [i].clear();
|
||||
}
|
||||
|
||||
void Stereo_Buffer::end_frame( blip_time_t time )
|
||||
{
|
||||
for ( int i = bufs_size; --i >= 0; )
|
||||
bufs [i].end_frame( time );
|
||||
}
|
||||
|
||||
long Stereo_Buffer::read_samples( blip_sample_t* out, long out_size )
|
||||
{
|
||||
require( (out_size & 1) == 0 ); // must read an even number of samples
|
||||
out_size = min( out_size, samples_avail() );
|
||||
|
||||
int pair_count = int (out_size >> 1);
|
||||
if ( pair_count )
|
||||
{
|
||||
mixer.read_pairs( out, pair_count );
|
||||
|
||||
if ( samples_avail() <= 0 || immediate_removal() )
|
||||
{
|
||||
for ( int i = bufs_size; --i >= 0; )
|
||||
{
|
||||
buf_t& b = bufs [i];
|
||||
// TODO: might miss non-silence settling since it checks END of last read
|
||||
if ( !b.non_silent() )
|
||||
b.remove_silence( mixer.samples_read );
|
||||
else
|
||||
b.remove_samples( mixer.samples_read );
|
||||
}
|
||||
mixer.samples_read = 0;
|
||||
}
|
||||
}
|
||||
return out_size;
|
||||
}
|
||||
|
||||
|
||||
// Stereo_Mixer
|
||||
|
||||
// mixers use a single index value to improve performance on register-challenged processors
|
||||
// offset goes from negative to zero
|
||||
|
||||
void Stereo_Mixer::read_pairs( blip_sample_t* out, int count )
|
||||
{
|
||||
// TODO: if caller never marks buffers as modified, uses mono
|
||||
// except that buffer isn't cleared, so caller can encounter
|
||||
// subtle problems and not realize the cause.
|
||||
samples_read += count;
|
||||
if ( bufs [0]->non_silent() | bufs [1]->non_silent() )
|
||||
mix_stereo( out, count );
|
||||
else
|
||||
mix_mono( out, count );
|
||||
}
|
||||
|
||||
void Stereo_Mixer::mix_mono( blip_sample_t* out_, int count )
|
||||
{
|
||||
int const bass = BLIP_READER_BASS( *bufs [2] );
|
||||
BLIP_READER_BEGIN( center, *bufs [2] );
|
||||
BLIP_READER_ADJ_( center, samples_read );
|
||||
|
||||
typedef blip_sample_t stereo_blip_sample_t [stereo];
|
||||
stereo_blip_sample_t* BLIP_RESTRICT out = (stereo_blip_sample_t*) out_ + count;
|
||||
int offset = -count;
|
||||
do
|
||||
{
|
||||
blargg_long s = BLIP_READER_READ( center );
|
||||
BLIP_READER_NEXT_IDX_( center, bass, offset );
|
||||
BLIP_CLAMP( s, s );
|
||||
|
||||
out [offset] [0] = (blip_sample_t) s;
|
||||
out [offset] [1] = (blip_sample_t) s;
|
||||
}
|
||||
while ( ++offset );
|
||||
|
||||
BLIP_READER_END( center, *bufs [2] );
|
||||
}
|
||||
|
||||
void Stereo_Mixer::mix_stereo( blip_sample_t* out_, int count )
|
||||
{
|
||||
blip_sample_t* BLIP_RESTRICT out = out_ + count * stereo;
|
||||
|
||||
// do left + center and right + center separately to reduce register load
|
||||
Tracked_Blip_Buffer* const* buf = &bufs [2];
|
||||
while ( true ) // loop runs twice
|
||||
{
|
||||
--buf;
|
||||
--out;
|
||||
|
||||
int const bass = BLIP_READER_BASS( *bufs [2] );
|
||||
BLIP_READER_BEGIN( side, **buf );
|
||||
BLIP_READER_BEGIN( center, *bufs [2] );
|
||||
|
||||
BLIP_READER_ADJ_( side, samples_read );
|
||||
BLIP_READER_ADJ_( center, samples_read );
|
||||
|
||||
int offset = -count;
|
||||
do
|
||||
{
|
||||
blargg_long s = BLIP_READER_READ_RAW( center ) + BLIP_READER_READ_RAW( side );
|
||||
s >>= blip_sample_bits - 16;
|
||||
BLIP_READER_NEXT_IDX_( side, bass, offset );
|
||||
BLIP_READER_NEXT_IDX_( center, bass, offset );
|
||||
BLIP_CLAMP( s, s );
|
||||
|
||||
++offset; // before write since out is decremented to slightly before end
|
||||
out [offset * stereo] = (blip_sample_t) s;
|
||||
}
|
||||
while ( offset );
|
||||
|
||||
BLIP_READER_END( side, **buf );
|
||||
|
||||
if ( buf != bufs )
|
||||
continue;
|
||||
|
||||
// only end center once
|
||||
BLIP_READER_END( center, *bufs [2] );
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
// Multi-channel sound buffer interface, and basic mono and stereo buffers
|
||||
|
||||
// Blip_Buffer 0.4.1
|
||||
#ifndef MULTI_BUFFER_H
|
||||
#define MULTI_BUFFER_H
|
||||
|
||||
#include "blargg_common.h"
|
||||
#include "Blip_Buffer.h"
|
||||
|
||||
// Interface to one or more Blip_Buffers mapped to one or more channels
|
||||
// consisting of left, center, and right buffers.
|
||||
class Multi_Buffer {
|
||||
public:
|
||||
Multi_Buffer( int samples_per_frame );
|
||||
virtual ~Multi_Buffer() { }
|
||||
|
||||
// Sets the number of channels available and optionally their types
|
||||
// (type information used by Effects_Buffer)
|
||||
enum { type_index_mask = 0xFF };
|
||||
enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type };
|
||||
virtual blargg_err_t set_channel_count( int, int const* types = 0 );
|
||||
int channel_count() const { return channel_count_; }
|
||||
|
||||
// Gets indexed channel, from 0 to channel count - 1
|
||||
struct channel_t {
|
||||
Blip_Buffer* center;
|
||||
Blip_Buffer* left;
|
||||
Blip_Buffer* right;
|
||||
};
|
||||
virtual channel_t channel( int index ) BLARGG_PURE( ; )
|
||||
|
||||
// See Blip_Buffer.h
|
||||
virtual blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) BLARGG_PURE( ; )
|
||||
virtual void clock_rate( long ) BLARGG_PURE( { } )
|
||||
virtual void bass_freq( int ) BLARGG_PURE( { } )
|
||||
virtual void clear() BLARGG_PURE( { } )
|
||||
long sample_rate() const;
|
||||
|
||||
// Length of buffer, in milliseconds
|
||||
int length() const;
|
||||
|
||||
// See Blip_Buffer.h
|
||||
virtual void end_frame( blip_time_t ) BLARGG_PURE( { } )
|
||||
|
||||
// Number of samples per output frame (1 = mono, 2 = stereo)
|
||||
int samples_per_frame() const;
|
||||
|
||||
// Count of changes to channel configuration. Incremented whenever
|
||||
// a change is made to any of the Blip_Buffers for any channel.
|
||||
unsigned channels_changed_count() { return channels_changed_count_; }
|
||||
|
||||
// See Blip_Buffer.h
|
||||
virtual long read_samples( blip_sample_t*, long ) BLARGG_PURE( { return 0; } )
|
||||
virtual long samples_avail() const BLARGG_PURE( { return 0; } )
|
||||
|
||||
public:
|
||||
BLARGG_DISABLE_NOTHROW
|
||||
void disable_immediate_removal() { immediate_removal_ = false; }
|
||||
protected:
|
||||
bool immediate_removal() const { return immediate_removal_; }
|
||||
int const* channel_types() const { return channel_types_; }
|
||||
void channels_changed() { channels_changed_count_++; }
|
||||
private:
|
||||
// noncopyable
|
||||
Multi_Buffer( const Multi_Buffer& );
|
||||
Multi_Buffer& operator = ( const Multi_Buffer& );
|
||||
|
||||
unsigned channels_changed_count_;
|
||||
long sample_rate_;
|
||||
int length_;
|
||||
int channel_count_;
|
||||
int const samples_per_frame_;
|
||||
int const* channel_types_;
|
||||
bool immediate_removal_;
|
||||
};
|
||||
|
||||
// Uses a single buffer and outputs mono samples.
|
||||
class Mono_Buffer : public Multi_Buffer {
|
||||
Blip_Buffer buf;
|
||||
channel_t chan;
|
||||
public:
|
||||
// Buffer used for all channels
|
||||
Blip_Buffer* center() { return &buf; }
|
||||
|
||||
public:
|
||||
Mono_Buffer();
|
||||
~Mono_Buffer();
|
||||
blargg_err_t set_sample_rate( long rate, int msec = blip_default_length );
|
||||
void clock_rate( long rate ) { buf.clock_rate( rate ); }
|
||||
void bass_freq( int freq ) { buf.bass_freq( freq ); }
|
||||
void clear() { buf.clear(); }
|
||||
long samples_avail() const { return buf.samples_avail(); }
|
||||
long read_samples( blip_sample_t* p, long s ) { return buf.read_samples( p, s ); }
|
||||
channel_t channel( int ) { return chan; }
|
||||
void end_frame( blip_time_t t ) { buf.end_frame( t ); }
|
||||
};
|
||||
|
||||
class Tracked_Blip_Buffer : public Blip_Buffer {
|
||||
public:
|
||||
// Non-zero if buffer still has non-silent samples in it. Requires that you call
|
||||
// set_modified() appropriately.
|
||||
blip_ulong non_silent() const;
|
||||
|
||||
// remove_samples( samples_avail() )
|
||||
void remove_all_samples();
|
||||
|
||||
public:
|
||||
BLARGG_DISABLE_NOTHROW
|
||||
|
||||
long read_samples( blip_sample_t*, long );
|
||||
void remove_silence( long );
|
||||
void remove_samples( long );
|
||||
Tracked_Blip_Buffer();
|
||||
void clear();
|
||||
void end_frame( blip_time_t );
|
||||
private:
|
||||
blip_long last_non_silence;
|
||||
void remove_( long );
|
||||
};
|
||||
|
||||
class Stereo_Mixer {
|
||||
public:
|
||||
Tracked_Blip_Buffer* bufs [3];
|
||||
blargg_long samples_read;
|
||||
|
||||
Stereo_Mixer() : samples_read( 0 ) { }
|
||||
void read_pairs( blip_sample_t* out, int count );
|
||||
private:
|
||||
void mix_mono ( blip_sample_t* out, int pair_count );
|
||||
void mix_stereo( blip_sample_t* out, int pair_count );
|
||||
};
|
||||
|
||||
// Uses three buffers (one for center) and outputs stereo sample pairs.
|
||||
class Stereo_Buffer : public Multi_Buffer {
|
||||
public:
|
||||
|
||||
// Buffers used for all channels
|
||||
Blip_Buffer* center() { return &bufs [2]; }
|
||||
Blip_Buffer* left() { return &bufs [0]; }
|
||||
Blip_Buffer* right() { return &bufs [1]; }
|
||||
|
||||
public:
|
||||
Stereo_Buffer();
|
||||
~Stereo_Buffer();
|
||||
blargg_err_t set_sample_rate( long, int msec = blip_default_length );
|
||||
void clock_rate( long );
|
||||
void bass_freq( int );
|
||||
void clear();
|
||||
channel_t channel( int ) { return chan; }
|
||||
void end_frame( blip_time_t );
|
||||
|
||||
long samples_avail() const { return (bufs [0].samples_avail() - mixer.samples_read) * 2; }
|
||||
long read_samples( blip_sample_t*, long );
|
||||
|
||||
private:
|
||||
enum { bufs_size = 3 };
|
||||
typedef Tracked_Blip_Buffer buf_t;
|
||||
buf_t bufs [bufs_size];
|
||||
Stereo_Mixer mixer;
|
||||
channel_t chan;
|
||||
long samples_avail_;
|
||||
};
|
||||
|
||||
// Silent_Buffer generates no samples, useful where no sound is wanted
|
||||
class Silent_Buffer : public Multi_Buffer {
|
||||
channel_t chan;
|
||||
public:
|
||||
Silent_Buffer();
|
||||
blargg_err_t set_sample_rate( long rate, int msec = blip_default_length );
|
||||
void clock_rate( long ) { }
|
||||
void bass_freq( int ) { }
|
||||
void clear() { }
|
||||
channel_t channel( int ) { return chan; }
|
||||
void end_frame( blip_time_t ) { }
|
||||
long samples_avail() const { return 0; }
|
||||
long read_samples( blip_sample_t*, long ) { return 0; }
|
||||
};
|
||||
|
||||
|
||||
inline blargg_err_t Multi_Buffer::set_sample_rate( long rate, int msec )
|
||||
{
|
||||
sample_rate_ = rate;
|
||||
length_ = msec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline blargg_err_t Silent_Buffer::set_sample_rate( long rate, int msec )
|
||||
{
|
||||
return Multi_Buffer::set_sample_rate( rate, msec );
|
||||
}
|
||||
|
||||
inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; }
|
||||
|
||||
inline long Multi_Buffer::sample_rate() const { return sample_rate_; }
|
||||
|
||||
inline int Multi_Buffer::length() const { return length_; }
|
||||
|
||||
inline blargg_err_t Multi_Buffer::set_channel_count( int n, int const* types )
|
||||
{
|
||||
channel_count_ = n;
|
||||
channel_types_ = types;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,206 @@
|
|||
// Sets up common environment for Shay Green's libraries.
|
||||
// To change configuration options, modify blargg_config.h, not this file.
|
||||
|
||||
// Gb_Snd_Emu 0.2.0
|
||||
#ifndef BLARGG_COMMON_H
|
||||
#define BLARGG_COMMON_H
|
||||
|
||||
#include <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
|
|
@ -0,0 +1,33 @@
|
|||
// $package user configuration file. Don't replace when updating library.
|
||||
|
||||
#ifndef BLARGG_CONFIG_H
|
||||
#define BLARGG_CONFIG_H
|
||||
|
||||
// Uncomment to have Gb_Apu run at 4x normal clock rate (16777216 Hz), useful in
|
||||
// a Game Boy Advance emulator.
|
||||
#define GB_APU_OVERCLOCK 4
|
||||
|
||||
#define GB_APU_CUSTOM_STATE 1
|
||||
|
||||
// Uncomment to enable platform-specific (and possibly non-portable) optimizations.
|
||||
//#define BLARGG_NONPORTABLE 1
|
||||
|
||||
// Uncomment if automatic byte-order determination doesn't work
|
||||
//#define BLARGG_BIG_ENDIAN 1
|
||||
|
||||
// Uncomment to use zlib for transparent decompression of gzipped files
|
||||
//#define HAVE_ZLIB_H
|
||||
|
||||
// Uncomment if you get errors in the bool section of blargg_common.h
|
||||
//#define BLARGG_COMPILER_HAS_BOOL 1
|
||||
|
||||
// Uncomment to disable out-of-memory exceptions
|
||||
//#include <memory>
|
||||
//#define BLARGG_NEW new (std::nothrow)
|
||||
|
||||
// Use standard config.h if present
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,92 @@
|
|||
/* Included at the beginning of library source files, AFTER all other #include lines.
|
||||
Sets up helpful macros and services used in my source code. Since this is only "active"
|
||||
in my source code, I don't have to worry about polluting the global namespace with
|
||||
unprefixed names. */
|
||||
|
||||
// Gb_Snd_Emu 0.2.0
|
||||
#ifndef BLARGG_SOURCE_H
|
||||
#define BLARGG_SOURCE_H
|
||||
|
||||
// The following four macros are for debugging only. Some or all might be defined
|
||||
// to do nothing, depending on the circumstances. Described is what happens when
|
||||
// a particular macro is defined to do something. When defined to do nothing, the
|
||||
// macros do NOT evaluate their argument(s).
|
||||
|
||||
// If expr is false, prints file and line number, then aborts program. Meant for
|
||||
// checking internal state and consistency. A failed assertion indicates a bug
|
||||
// in MY code.
|
||||
//
|
||||
// void assert( bool expr );
|
||||
#include <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