mirror of
https://github.com/dborth/vbagx.git
synced 2024-11-29 05:54:17 +01:00
upgrade sound core and GB engine
This commit is contained in:
parent
3976f85610
commit
5fb5767632
@ -18,7 +18,7 @@ include $(DEVKITPPC)/gamecube_rules
|
||||
TARGET := vbagx_gc
|
||||
TARGETDIR := executables
|
||||
BUILD := build_gc
|
||||
SOURCES := source/vba source/vba/agb source/vba/dmg source/ngc source/sz
|
||||
SOURCES := source/vba source/vba/agb source/vba/dmg source/vba/dmg/gb_apu source/ngc source/sz
|
||||
INCLUDES := source/vba source/ngc
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
|
@ -18,7 +18,7 @@ include $(DEVKITPPC)/wii_rules
|
||||
TARGET := vbagx_wii
|
||||
TARGETDIR := executables
|
||||
BUILD := build_wii
|
||||
SOURCES := source/vba source/vba/agb source/vba/dmg source/ngc source/sz
|
||||
SOURCES := source/vba source/vba/agb source/vba/dmg source/vba/dmg/gb_apu source/ngc source/sz
|
||||
INCLUDES := source/vba source/ngc
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
|
@ -13,18 +13,19 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "agb/GBA.h"
|
||||
#include "agb/agbprint.h"
|
||||
#include "unzip.h"
|
||||
#include "Util.h"
|
||||
#include "Flash.h"
|
||||
#include "Port.h"
|
||||
#include "RTC.h"
|
||||
#include "Sound.h"
|
||||
#include "unzip.h"
|
||||
#include "Util.h"
|
||||
#include "dmg/GB.h"
|
||||
#include "Cheats.h"
|
||||
#include "agb/GBA.h"
|
||||
#include "agb/agbprint.h"
|
||||
#include "dmg/gb.h"
|
||||
#include "dmg/gbGlobals.h"
|
||||
#include "images/saveicon.h"
|
||||
//#include "dmg/gbSound.h"
|
||||
#include "dmg/gbCheats.h"
|
||||
#include "dmg/gbSound.h"
|
||||
|
||||
#include "vba.h"
|
||||
#include "fileop.h"
|
||||
@ -37,6 +38,7 @@
|
||||
#include "video.h"
|
||||
#include "menudraw.h"
|
||||
#include "gcunzip.h"
|
||||
#include "images/saveicon.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@ -63,22 +65,17 @@ int sensorX = 2047;
|
||||
int sensorY = 2047;
|
||||
|
||||
int systemFrameSkip = 0;
|
||||
bool systemSoundOn = false;
|
||||
int systemVerbose = 0;
|
||||
int cartridgeType = 0;
|
||||
int srcWidth = 0;
|
||||
int srcHeight = 0;
|
||||
int destWidth = 0;
|
||||
int destHeight = 0;
|
||||
int srcPitch = 0;
|
||||
int saveExists = 0;
|
||||
int systemRedShift = 0;
|
||||
int systemBlueShift = 0;
|
||||
int systemGreenShift = 0;
|
||||
int systemColorDepth = 0;
|
||||
u16 systemGbPalette[24];
|
||||
u16 systemColorMap16[0x10000];
|
||||
//u32 systemColorMap32[0x10000];
|
||||
u32 *systemColorMap32 = NULL;
|
||||
|
||||
struct EmulatedSystem emulator =
|
||||
@ -148,7 +145,7 @@ void system10Frames(int rate)
|
||||
else
|
||||
timeOff = 0; // timeoff was not valid
|
||||
|
||||
if(diff > 175 && systemFrameSkip < 9)
|
||||
if(diff > 170 && systemFrameSkip < 9)
|
||||
systemFrameSkip++;
|
||||
else if(diff < 150 && systemFrameSkip > 0)
|
||||
systemFrameSkip--;
|
||||
@ -602,8 +599,6 @@ bool LoadVBAROM(int method)
|
||||
cartridgeType = 0;
|
||||
srcWidth = 0;
|
||||
srcHeight = 0;
|
||||
destWidth = 0;
|
||||
destHeight = 0;
|
||||
srcPitch = 0;
|
||||
|
||||
switch( type )
|
||||
@ -620,7 +615,7 @@ bool LoadVBAROM(int method)
|
||||
vAspect = 46;
|
||||
srcPitch = 484;
|
||||
soundQuality = 2;
|
||||
soundBufferLen = 1470;
|
||||
soundBufferLen = 736 * 2;
|
||||
cpuSaveType = 0;
|
||||
break;
|
||||
|
||||
@ -628,8 +623,26 @@ bool LoadVBAROM(int method)
|
||||
//WaitPrompt("GameBoy Image");
|
||||
cartridgeType = 1;
|
||||
emulator = GBSystem;
|
||||
|
||||
gbBorderOn = 0;
|
||||
|
||||
if(gbBorderOn)
|
||||
{
|
||||
srcWidth = 256;
|
||||
srcHeight = 224;
|
||||
gbBorderLineSkip = 256;
|
||||
gbBorderColumnSkip = 48;
|
||||
gbBorderRowSkip = 40;
|
||||
}
|
||||
else
|
||||
{
|
||||
srcWidth = 160;
|
||||
srcHeight = 144;
|
||||
gbBorderLineSkip = 160;
|
||||
gbBorderColumnSkip = 0;
|
||||
gbBorderRowSkip = 0;
|
||||
}
|
||||
|
||||
loaded = LoadGBROM(method);
|
||||
// Actual physical aspect is 1.0
|
||||
hAspect = 60;
|
||||
@ -651,27 +664,30 @@ bool LoadVBAROM(int method)
|
||||
flashSetSize(0x20000); // 128K saves
|
||||
rtcEnable(true);
|
||||
agbPrintEnable(false);
|
||||
soundOffFlag = false;
|
||||
soundLowPass = true;
|
||||
|
||||
// Setup GX
|
||||
GX_Render_Init( srcWidth, srcHeight, hAspect, vAspect );
|
||||
|
||||
if ( cartridgeType == 1 )
|
||||
{
|
||||
gbGetHardwareType();
|
||||
|
||||
// used for the handling of the gb Boot Rom
|
||||
//if (gbHardware & 5)
|
||||
//gbCPUInit(gbBiosFileName, useBios);
|
||||
|
||||
gbSoundReset();
|
||||
gbSoundSetQuality(soundQuality);
|
||||
gbReset();
|
||||
}
|
||||
else
|
||||
{
|
||||
soundReset();
|
||||
soundSetQuality(soundQuality);
|
||||
CPUInit("/VBA/BIOS/BIOS.GBA", 1);
|
||||
CPUReset();
|
||||
}
|
||||
|
||||
soundVolume = 0;
|
||||
systemSoundOn = true;
|
||||
|
||||
soundInit();
|
||||
|
||||
emulating = 1;
|
||||
|
1858
source/vba/Sound.cpp
1858
source/vba/Sound.cpp
File diff suppressed because it is too large
Load Diff
@ -1,84 +1,83 @@
|
||||
// -*- C++ -*-
|
||||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2004 Forgotten and the VBA development team
|
||||
// Copyright (C) 2004-2006 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.
|
||||
// Sound emulation setup/options and GBA sound emulation
|
||||
|
||||
#ifndef VBA_SOUND_H
|
||||
#define VBA_SOUND_H
|
||||
|
||||
#include "System.h"
|
||||
|
||||
#define NR10 0x60
|
||||
#define NR11 0x62
|
||||
#define NR12 0x63
|
||||
#define NR13 0x64
|
||||
#define NR14 0x65
|
||||
#define NR21 0x68
|
||||
#define NR22 0x69
|
||||
#define NR23 0x6c
|
||||
#define NR24 0x6d
|
||||
#define NR30 0x70
|
||||
#define NR31 0x72
|
||||
#define NR32 0x73
|
||||
#define NR33 0x74
|
||||
#define NR34 0x75
|
||||
#define NR41 0x78
|
||||
#define NR42 0x79
|
||||
#define NR43 0x7c
|
||||
#define NR44 0x7d
|
||||
#define NR50 0x80
|
||||
#define NR51 0x81
|
||||
#define NR52 0x84
|
||||
//// Setup/options (these affect GBA and GB sound)
|
||||
|
||||
// Initializes sound and returns true if successful. Sets sound quality to
|
||||
// current value in soundQuality global.
|
||||
bool soundInit();
|
||||
|
||||
// Manages sound volume, where 1.0 is normal
|
||||
void soundSetVolume( float );
|
||||
float soundGetVolume();
|
||||
|
||||
// Manages muting bitmask. The bits control the following channels:
|
||||
// 0x001 Pulse 1
|
||||
// 0x002 Pulse 2
|
||||
// 0x004 Wave
|
||||
// 0x008 Noise
|
||||
// 0x100 PCM 1
|
||||
// 0x200 PCM 2
|
||||
void soundSetEnable( int mask );
|
||||
int soundGetEnable();
|
||||
|
||||
// Pauses/resumes system sound output
|
||||
void soundPause();
|
||||
void soundResume();
|
||||
extern bool soundPaused; // current paused state
|
||||
|
||||
// Cleans up sound. Afterwards, soundInit() can be called again.
|
||||
void soundShutdown();
|
||||
|
||||
// Sound buffering
|
||||
extern int soundBufferLen; // size of sound buffer in BYTES
|
||||
extern u16 soundFinalWave[1470];// 16-bit SIGNED stereo sample buffer
|
||||
|
||||
|
||||
//// GBA sound options
|
||||
|
||||
// Sets sample rate to 44100 / quality
|
||||
void soundSetQuality( int quality );
|
||||
extern int soundQuality; // current sound quality
|
||||
|
||||
// Sound settings
|
||||
extern bool soundInterpolation; // 1 if PCM should have low-pass filtering
|
||||
extern float soundFiltering; // 0.0 = none, 1.0 = max
|
||||
|
||||
|
||||
//// GBA sound emulation
|
||||
|
||||
// GBA sound registers
|
||||
#define SGCNT0_H 0x82
|
||||
#define FIFOA_L 0xa0
|
||||
#define FIFOA_H 0xa2
|
||||
#define FIFOB_L 0xa4
|
||||
#define FIFOB_H 0xa6
|
||||
|
||||
void soundTick();
|
||||
void soundShutdown();
|
||||
bool soundInit();
|
||||
void soundPause();
|
||||
void soundResume();
|
||||
void soundEnable(int);
|
||||
void soundDisable(int);
|
||||
int soundGetEnable();
|
||||
// Resets emulated sound hardware
|
||||
void soundReset();
|
||||
void soundSaveGame(gzFile);
|
||||
void soundReadGame(gzFile, int);
|
||||
void soundEvent(u32, u8);
|
||||
void soundEvent(u32, u16);
|
||||
void soundTimerOverflow(int);
|
||||
void soundSetQuality(int);
|
||||
|
||||
extern int SOUND_CLOCK_TICKS;
|
||||
extern int soundTicks;
|
||||
extern int soundPaused;
|
||||
extern bool soundOffFlag;
|
||||
extern int soundQuality;
|
||||
extern int soundBufferLen;
|
||||
extern int soundBufferTotalLen;
|
||||
extern u32 soundNextPosition;
|
||||
extern u16 soundFinalWave[1470];
|
||||
extern int soundVolume;
|
||||
// Emulates write to sound hardware
|
||||
void soundEvent( u32 addr, u8 data );
|
||||
void soundEvent( u32 addr, u16 data ); // TODO: error-prone to overload like this
|
||||
|
||||
extern bool soundEcho;
|
||||
extern bool soundLowPass;
|
||||
extern bool soundReverse;
|
||||
// Notifies emulator that a timer has overflowed
|
||||
void soundTimerOverflow( int which );
|
||||
|
||||
// Notifies emulator that PCM rate may have changed
|
||||
void interp_rate();
|
||||
|
||||
// Notifies emulator that SOUND_CLOCK_TICKS clocks have passed
|
||||
void psoundTickfn();
|
||||
extern int SOUND_CLOCK_TICKS; // Number of 16.8 MHz clocks between calls to soundTick()
|
||||
extern int soundTicks; // Number of 16.8 MHz clocks until soundTick() will be called
|
||||
|
||||
// Saves/loads emulator state
|
||||
void soundSaveGame( gzFile );
|
||||
void soundReadGame( gzFile, int version );
|
||||
|
||||
#endif // VBA_SOUND_H
|
||||
|
@ -21,7 +21,8 @@
|
||||
#define VBA_SYSTEM_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "unzip.h"
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
@ -39,11 +40,6 @@ typedef int32_t s32;
|
||||
typedef int64_t s64;
|
||||
|
||||
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
|
||||
struct EmulatedSystem {
|
||||
// main emulation function
|
||||
void (*emuMain)(int);
|
||||
@ -113,7 +109,6 @@ extern void winlog(const char *,...);
|
||||
extern void (*dbgOutput)(const char *s, u32 addr);
|
||||
extern void (*dbgSignal)(int sig,int number);
|
||||
|
||||
extern bool systemSoundOn; // old sound system
|
||||
extern u16 systemColorMap16[0x10000];
|
||||
//extern u32 systemColorMap32[0x10000];
|
||||
extern u32 *systemColorMap32;
|
||||
|
@ -35,6 +35,7 @@ extern "C" {
|
||||
#include "Globals.h"
|
||||
#include "RTC.h"
|
||||
#include "Port.h"
|
||||
#include "unzip.h"
|
||||
|
||||
extern "C" {
|
||||
#include "memgzio.h"
|
||||
|
@ -3740,7 +3740,7 @@ void CPULoop(int ticks)
|
||||
// mute sound
|
||||
soundTicks -= clockTicks;
|
||||
if(soundTicks <= 0) {
|
||||
soundTick(); // psoundTickfn();
|
||||
psoundTickfn();
|
||||
soundTicks += SOUND_CLOCK_TICKS;
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
65
source/vba/dmg/gb.h
Normal file
65
source/vba/dmg/gb.h
Normal file
@ -0,0 +1,65 @@
|
||||
// -*- 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;
|
||||
|
||||
bool gbLoadRom(const char *);
|
||||
void gbEmulate(int);
|
||||
void gbWriteMemory(register u16, register u8);
|
||||
void gbDrawLine();
|
||||
bool gbIsGameboyRom(const char *);
|
||||
void gbGetHardwareType();
|
||||
void gbReset();
|
||||
void gbCleanUp();
|
||||
void gbCPUInit(const char *,bool);
|
||||
bool gbWriteBatteryFile(const char *);
|
||||
bool gbWriteBatteryFile(const char *, bool);
|
||||
bool gbReadBatteryFile(const char *);
|
||||
bool gbWriteSaveState(const char *);
|
||||
bool gbWriteMemSaveState(char *, int);
|
||||
bool gbReadSaveState(const char *);
|
||||
bool gbReadMemSaveState(char *, int);
|
||||
void gbSgbRenderBorder();
|
||||
bool gbWritePNGFile(const char *);
|
||||
bool gbWriteBMPFile(const char *);
|
||||
bool gbReadGSASnapshot(const char *);
|
||||
|
||||
extern struct EmulatedSystem GBSystem;
|
||||
|
||||
bool MemgbReadBatteryFile(char * membuffer, int read);
|
||||
int MemgbWriteBatteryFile(char * membuffer);
|
||||
|
||||
#endif
|
@ -1,6 +1,6 @@
|
||||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2004 Forgotten and the VBA development team
|
||||
// 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
|
||||
@ -27,9 +27,11 @@
|
||||
|
||||
#include "gbCheats.h"
|
||||
#include "gbGlobals.h"
|
||||
#include "gb.h"
|
||||
|
||||
gbCheat gbCheatList[100];
|
||||
int gbCheatNumber = 0;
|
||||
int gbNextCheat = 0;
|
||||
bool gbCheatMap[0x10000];
|
||||
|
||||
extern bool cheatsEnabled;
|
||||
@ -41,8 +43,7 @@ void gbCheatUpdateMap()
|
||||
{
|
||||
memset(gbCheatMap, 0, 0x10000);
|
||||
|
||||
for(int i = 0; i < gbCheatNumber; i++)
|
||||
{
|
||||
for(int i = 0; i < gbCheatNumber; i++) {
|
||||
if(gbCheatList[i].enabled)
|
||||
gbCheatMap[gbCheatList[i].address] = true;
|
||||
}
|
||||
@ -51,22 +52,19 @@ void gbCheatUpdateMap()
|
||||
void gbCheatsSaveGame(gzFile gzFile)
|
||||
{
|
||||
utilWriteInt(gzFile, gbCheatNumber);
|
||||
if(gbCheatNumber)
|
||||
if(gbCheatNumber>0)
|
||||
utilGzWrite(gzFile, &gbCheatList[0], sizeof(gbCheat)*gbCheatNumber);
|
||||
}
|
||||
|
||||
void gbCheatsReadGame(gzFile gzFile, int version)
|
||||
{
|
||||
if(version <= 8)
|
||||
{
|
||||
if(version <= 8) {
|
||||
int gbGgOn = utilReadInt(gzFile);
|
||||
|
||||
if(gbGgOn)
|
||||
{
|
||||
if(gbGgOn) {
|
||||
int n = utilReadInt(gzFile);
|
||||
gbXxCheat tmpCheat;
|
||||
for(int i = 0; i < n; i++)
|
||||
{
|
||||
for(int i = 0; i < n; i++) {
|
||||
utilGzRead(gzFile,&tmpCheat, sizeof(gbXxCheat));
|
||||
gbAddGgCheat(tmpCheat.cheatCode, tmpCheat.cheatDesc);
|
||||
}
|
||||
@ -74,23 +72,18 @@ void gbCheatsReadGame(gzFile gzFile, int version)
|
||||
|
||||
int gbGsOn = utilReadInt(gzFile);
|
||||
|
||||
if(gbGsOn)
|
||||
{
|
||||
if(gbGsOn) {
|
||||
int n = utilReadInt(gzFile);
|
||||
gbXxCheat tmpCheat;
|
||||
for(int i = 0; i < n; i++)
|
||||
{
|
||||
for(int i = 0; i < n; i++) {
|
||||
utilGzRead(gzFile,&tmpCheat, sizeof(gbXxCheat));
|
||||
gbAddGsCheat(tmpCheat.cheatCode, tmpCheat.cheatDesc);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
gbCheatNumber = utilReadInt(gzFile);
|
||||
|
||||
if(gbCheatNumber)
|
||||
{
|
||||
if(gbCheatNumber>0) {
|
||||
utilGzRead(gzFile, &gbCheatList[0], sizeof(gbCheat)*gbCheatNumber);
|
||||
}
|
||||
}
|
||||
@ -102,7 +95,7 @@ void gbCheatsSaveCheatList(const char *file)
|
||||
{
|
||||
if(gbCheatNumber == 0)
|
||||
return;
|
||||
FILE* f = fopen(file, "wb");
|
||||
FILE *f = fopen(file, "wb");
|
||||
if(f == NULL)
|
||||
return;
|
||||
int version = 1;
|
||||
@ -122,21 +115,19 @@ bool gbCheatsLoadCheatList(const char *file)
|
||||
|
||||
int count = 0;
|
||||
|
||||
FILE* f = fopen(file, "rb");
|
||||
FILE *f = fopen(file, "rb");
|
||||
|
||||
if(f == NULL)
|
||||
return false;
|
||||
|
||||
int version = 0;
|
||||
|
||||
if(fread(&version, 1, sizeof(version), f) != sizeof(version))
|
||||
{
|
||||
if(fread(&version, 1, sizeof(version), f) != sizeof(version)) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(version != 1)
|
||||
{
|
||||
if(version != 1) {
|
||||
systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_VERSION,
|
||||
N_("Unsupported cheat list version %d"), version);
|
||||
fclose(f);
|
||||
@ -144,28 +135,24 @@ bool gbCheatsLoadCheatList(const char *file)
|
||||
}
|
||||
|
||||
int type = 0;
|
||||
if(fread(&type, 1, sizeof(type), f) != sizeof(type))
|
||||
{
|
||||
if(fread(&type, 1, sizeof(type), f) != sizeof(type)) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(type != 1)
|
||||
{
|
||||
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))
|
||||
{
|
||||
if(fread(&count, 1, sizeof(count), f) != sizeof(count)) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(fread(gbCheatList, 1, sizeof(gbCheatList), f) != sizeof(gbCheatList))
|
||||
{
|
||||
if(fread(gbCheatList, 1, sizeof(gbCheatList), f) != sizeof(gbCheatList)) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
@ -178,7 +165,7 @@ bool gbCheatsLoadCheatList(const char *file)
|
||||
|
||||
bool gbVerifyGsCode(const char *code)
|
||||
{
|
||||
int len = strlen(code);
|
||||
size_t len = strlen(code);
|
||||
|
||||
if(len == 0)
|
||||
return true;
|
||||
@ -195,27 +182,21 @@ bool gbVerifyGsCode(const char *code)
|
||||
GBCHEAT_HEX_VALUE(code[4]) << 4 |
|
||||
GBCHEAT_HEX_VALUE(code[5]);
|
||||
|
||||
if(address < 0xa000 ||
|
||||
address > 0xdfff)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void gbAddGsCheat(const char *code, const char *desc)
|
||||
bool gbAddGsCheat(const char *code, const char *desc)
|
||||
{
|
||||
if(gbCheatNumber > 99)
|
||||
{
|
||||
if(gbCheatNumber > 99) {
|
||||
systemMessage(MSG_MAXIMUM_NUMBER_OF_CHEATS,
|
||||
N_("Maximum number of cheats reached."));
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!gbVerifyGsCode(code))
|
||||
{
|
||||
if(!gbVerifyGsCode(code)) {
|
||||
systemMessage(MSG_INVALID_GAMESHARK_CODE,
|
||||
N_("Invalid GameShark code: %s"), code);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
int i = gbCheatNumber;
|
||||
@ -238,14 +219,24 @@ void gbAddGsCheat(const char *code, const char *desc)
|
||||
|
||||
gbCheatList[i].enabled = true;
|
||||
|
||||
gbCheatMap[gbCheatList[i].address] = 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)
|
||||
{
|
||||
int len = strlen(code);
|
||||
size_t len = strlen(code);
|
||||
|
||||
if(len != 11 &&
|
||||
len != 7 &&
|
||||
@ -270,12 +261,10 @@ bool gbVerifyGgCode(const char *code)
|
||||
return false;
|
||||
if(!GBCHEAT_IS_HEX(code[6]))
|
||||
return false;
|
||||
if(code[7] != 0)
|
||||
{
|
||||
if(code[7] != 0) {
|
||||
if(code[7] != '-')
|
||||
return false;
|
||||
if(code[8] != 0)
|
||||
{
|
||||
if(code[8] != 0) {
|
||||
if(!GBCHEAT_IS_HEX(code[8]))
|
||||
return false;
|
||||
if(!GBCHEAT_IS_HEX(code[9]))
|
||||
@ -316,30 +305,28 @@ bool gbVerifyGgCode(const char *code)
|
||||
return true;
|
||||
}
|
||||
|
||||
void gbAddGgCheat(const char *code, const char *desc)
|
||||
bool gbAddGgCheat(const char *code, const char *desc)
|
||||
{
|
||||
if(gbCheatNumber > 99)
|
||||
{
|
||||
if(gbCheatNumber > 99) {
|
||||
systemMessage(MSG_MAXIMUM_NUMBER_OF_CHEATS,
|
||||
N_("Maximum number of cheats reached."));
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!gbVerifyGgCode(code))
|
||||
{
|
||||
if(!gbVerifyGgCode(code)) {
|
||||
systemMessage(MSG_INVALID_GAMEGENIE_CODE,
|
||||
N_("Invalid GameGenie code: %s"), code);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
int i = gbCheatNumber;
|
||||
|
||||
int len = strlen(code);
|
||||
size_t len = strlen(code);
|
||||
|
||||
strcpy(gbCheatList[i].cheatCode, code);
|
||||
strcpy(gbCheatList[i].cheatDesc, desc);
|
||||
|
||||
gbCheatList[i].code = 1;
|
||||
gbCheatList[i].code = 0x101;
|
||||
gbCheatList[i].value = (GBCHEAT_HEX_VALUE(code[0]) << 4) +
|
||||
GBCHEAT_HEX_VALUE(code[1]);
|
||||
|
||||
@ -350,8 +337,7 @@ void gbAddGgCheat(const char *code, const char *desc)
|
||||
|
||||
gbCheatList[i].compare = 0;
|
||||
|
||||
if(len != 7 && len != 8)
|
||||
{
|
||||
if(len != 7 && len != 8) {
|
||||
|
||||
int compare = (GBCHEAT_HEX_VALUE(code[8]) << 4) +
|
||||
(GBCHEAT_HEX_VALUE(code[10]));
|
||||
@ -360,27 +346,30 @@ void gbAddGgCheat(const char *code, const char *desc)
|
||||
compare ^= 0x45;
|
||||
|
||||
gbCheatList[i].compare = compare;
|
||||
gbCheatList[i].code = 0;
|
||||
//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)
|
||||
{
|
||||
if(i < 0 || i >= gbCheatNumber) {
|
||||
systemMessage(MSG_INVALID_CHEAT_TO_REMOVE,
|
||||
N_("Invalid cheat to remove %d"), i);
|
||||
return;
|
||||
}
|
||||
|
||||
if((i+1) < gbCheatNumber)
|
||||
{
|
||||
if((i+1) < gbCheatNumber) {
|
||||
memcpy(&gbCheatList[i], &gbCheatList[i+1], sizeof(gbCheat)*
|
||||
(gbCheatNumber-i-1));
|
||||
}
|
||||
@ -398,10 +387,8 @@ void gbCheatRemoveAll()
|
||||
|
||||
void gbCheatEnable(int i)
|
||||
{
|
||||
if(i >=0 && i < gbCheatNumber)
|
||||
{
|
||||
if(!gbCheatList[i].enabled)
|
||||
{
|
||||
if(i >=0 && i < gbCheatNumber) {
|
||||
if(!gbCheatList[i].enabled) {
|
||||
gbCheatList[i].enabled = true;
|
||||
gbCheatUpdateMap();
|
||||
}
|
||||
@ -410,10 +397,8 @@ void gbCheatEnable(int i)
|
||||
|
||||
void gbCheatDisable(int i)
|
||||
{
|
||||
if(i >=0 && i < gbCheatNumber)
|
||||
{
|
||||
if(gbCheatList[i].enabled)
|
||||
{
|
||||
if(i >=0 && i < gbCheatNumber) {
|
||||
if(gbCheatList[i].enabled) {
|
||||
gbCheatList[i].enabled = false;
|
||||
gbCheatUpdateMap();
|
||||
}
|
||||
@ -422,10 +407,9 @@ void gbCheatDisable(int i)
|
||||
|
||||
bool gbCheatReadGSCodeFile(const char *fileName)
|
||||
{
|
||||
FILE* file = fopen(fileName, "rb");
|
||||
FILE *file = fopen(fileName, "rb");
|
||||
|
||||
if(!file)
|
||||
{
|
||||
if(!file) {
|
||||
systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName);
|
||||
return false;
|
||||
}
|
||||
@ -438,8 +422,7 @@ bool gbCheatReadGSCodeFile(const char *fileName)
|
||||
char desc[13];
|
||||
char code[9];
|
||||
int i;
|
||||
for(i = 0; i < count; i++)
|
||||
{
|
||||
for(i = 0; i < count; i++) {
|
||||
fread(&dummy, 1, 2, file);
|
||||
fread(desc, 1, 12, file);
|
||||
desc[12] = 0;
|
||||
@ -455,25 +438,49 @@ bool gbCheatReadGSCodeFile(const char *fileName)
|
||||
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)
|
||||
{
|
||||
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 0x00:
|
||||
case 0x01:
|
||||
case 0x80:
|
||||
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:
|
||||
@ -482,16 +489,34 @@ u8 gbCheatRead(u16 address)
|
||||
case 0x95:
|
||||
case 0x96:
|
||||
case 0x97:
|
||||
if(address >= 0xd000 && address < 0xe000)
|
||||
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
|
||||
{
|
||||
if(((gbMemoryMap[0x0d] - gbWram)/0x1000) ==
|
||||
(gbCheatList[i].code - 0x90))
|
||||
return gbCheatList[i].value;
|
||||
}
|
||||
else
|
||||
return gbCheatList[i].value;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return gbMemoryMap[address>>12][address&0xFFF];
|
||||
}
|
||||
|
@ -22,14 +22,12 @@
|
||||
|
||||
#include "../System.h"
|
||||
|
||||
struct gbXxCheat
|
||||
{
|
||||
struct gbXxCheat {
|
||||
char cheatDesc[100];
|
||||
char cheatCode[20];
|
||||
};
|
||||
};
|
||||
|
||||
struct gbCheat
|
||||
{
|
||||
struct gbCheat {
|
||||
char cheatCode[20];
|
||||
char cheatDesc[32];
|
||||
u16 address;
|
||||
@ -37,21 +35,25 @@ struct gbCheat
|
||||
u8 compare;
|
||||
u8 value;
|
||||
bool enabled;
|
||||
};
|
||||
};
|
||||
|
||||
extern void gbCheatsSaveGame(gzFile);
|
||||
extern void gbCheatsReadGame(gzFile, int);
|
||||
extern void gbCheatsSaveCheatList(const char *);
|
||||
extern bool gbCheatsLoadCheatList(const char *);
|
||||
extern bool gbCheatReadGSCodeFile(const char *);
|
||||
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 void gbAddGsCheat(const char *, const char*);
|
||||
extern void gbAddGgCheat(const char *, const char*);
|
||||
extern void gbCheatRemove(int);
|
||||
extern void gbCheatRemoveAll();
|
||||
extern void gbCheatEnable(int);
|
||||
extern void gbCheatDisable(int);
|
||||
extern u8 gbCheatRead(u16);
|
||||
|
||||
extern int gbCheatNumber;
|
||||
extern gbCheat gbCheatList[100];
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -22,30 +22,28 @@
|
||||
#include "../System.h"
|
||||
#include "gbGlobals.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
typedef struct {
|
||||
u8 mask;
|
||||
u8 value;
|
||||
char *mnen;
|
||||
}
|
||||
GBOPCODE;
|
||||
const char *mnen;
|
||||
} GBOPCODE;
|
||||
|
||||
#define GB_READ(x) gbMemoryMap[(x)>>12][(x)&0xfff]
|
||||
|
||||
static char *registers[] =
|
||||
static const char *registers[] =
|
||||
{ "B", "C", "D", "E", "H", "L", "(HL)", "A" };
|
||||
|
||||
static char *registers16[] =
|
||||
static const char *registers16[] =
|
||||
{ "BC", "DE", "HL", "SP", // for some operations
|
||||
"BC", "DE", "HL", "AF" }; // for push/pop
|
||||
|
||||
static char *cond[] =
|
||||
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" },
|
||||
@ -117,7 +115,7 @@ static GBOPCODE opcodes[] = {
|
||||
{ 0xff, 0xfb, "EI" },
|
||||
{ 0xff, 0xfe, "CP %B" },
|
||||
{ 0x00, 0x00, "DB %B" }
|
||||
};
|
||||
};
|
||||
|
||||
static GBOPCODE cbOpcodes[] = {
|
||||
{ 0xf8, 0x00, "RLC %r0" },
|
||||
@ -132,7 +130,7 @@ static GBOPCODE cbOpcodes[] = {
|
||||
{ 0xc0, 0x80, "RES %b,%r0" },
|
||||
{ 0xc0, 0xc0, "SET %b,%r0" },
|
||||
{ 0x00, 0x00, "DB CBh,%B" }
|
||||
};
|
||||
};
|
||||
|
||||
static char *addHex(char *p, u8 value)
|
||||
{
|
||||
@ -147,10 +145,9 @@ static char *addHex16(char *p, u16 value)
|
||||
return addHex(p, value & 255);
|
||||
}
|
||||
|
||||
static char *addStr(char *p, char *s)
|
||||
static char *addStr(char *p, const char *s)
|
||||
{
|
||||
while(*s)
|
||||
{
|
||||
while(*s) {
|
||||
*p++ = *s++;
|
||||
}
|
||||
return p;
|
||||
@ -166,17 +163,14 @@ int gbDis(char *buffer, u16 address)
|
||||
|
||||
u8 opcode = GB_READ(address);
|
||||
address++;
|
||||
char *mnen;
|
||||
const char *mnen;
|
||||
GBOPCODE *op;
|
||||
if(opcode == 0xcb)
|
||||
{
|
||||
if(opcode == 0xcb) {
|
||||
opcode = GB_READ(address);
|
||||
address++;
|
||||
instr++;
|
||||
op = cbOpcodes;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
op = opcodes;
|
||||
}
|
||||
while(op->value != (opcode & op->mask)) op++;
|
||||
@ -186,13 +180,10 @@ int gbDis(char *buffer, u16 address)
|
||||
s8 disp;
|
||||
int shift;
|
||||
|
||||
while(*mnen)
|
||||
{
|
||||
if(*mnen == '%')
|
||||
{
|
||||
while(*mnen) {
|
||||
if(*mnen == '%') {
|
||||
mnen++;
|
||||
switch(*mnen++)
|
||||
{
|
||||
switch(*mnen++) {
|
||||
case 'W':
|
||||
b0 = GB_READ(address);
|
||||
address++;
|
||||
@ -246,12 +237,10 @@ int gbDis(char *buffer, u16 address)
|
||||
p = addStr(p, cond[(opcode >> shift) & 3]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
} else
|
||||
*p++ = *mnen++;
|
||||
}
|
||||
for(int i = 0; i < instr; i++)
|
||||
{
|
||||
for(int i = 0; i < instr; i++) {
|
||||
u16 a = addr + i;
|
||||
addHex(buffer+5+i*2, GB_READ(a));
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2004 Forgotten and the VBA development team
|
||||
// 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
|
||||
@ -55,21 +55,21 @@ u8 gbInvertTab[256] = {
|
||||
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)
|
||||
{
|
||||
if(gbCgbMode) {
|
||||
bank0 = &gbVram[0x0000];
|
||||
bank1 = &gbVram[0x2000];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
bank0 = &gbMemory[0x8000];
|
||||
bank1 = NULL;
|
||||
}
|
||||
@ -89,10 +89,9 @@ void gbRenderLine()
|
||||
if(y >= 144)
|
||||
return;
|
||||
|
||||
// int yLine = (y + gbBorderRowSkip) * gbBorderLineSkip;
|
||||
|
||||
int sx = register_SCX;
|
||||
int sy = register_SCY;
|
||||
int SpritesTicks = gbSpritesTicks[x]*(gbSpeed ? 2 : 4);
|
||||
int sx = gbSCXLine[(gbSpeed ? 0 : 4)+SpritesTicks];
|
||||
int sy = gbSCYLine[(gbSpeed ? 11 : 5)+SpritesTicks];
|
||||
|
||||
sy+=y;
|
||||
|
||||
@ -116,47 +115,38 @@ void gbRenderLine()
|
||||
|
||||
tile_map_address++;
|
||||
|
||||
if((register_LCDC & 16) == 0)
|
||||
{
|
||||
if(tile < 128) tile += 128;
|
||||
else tile -= 128;
|
||||
}
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if(attrs & 0x40) {
|
||||
tile_pattern_address = tile_pattern + tile * 16 + (7-by)*2;
|
||||
}
|
||||
|
||||
if(attrs & 0x08)
|
||||
{
|
||||
if(attrs & 0x08) {
|
||||
tile_a = bank1[tile_pattern_address++];
|
||||
tile_b = bank1[tile_pattern_address];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
tile_a = bank0[tile_pattern_address++];
|
||||
tile_b = bank0[tile_pattern_address];
|
||||
}
|
||||
|
||||
if(attrs & 0x20)
|
||||
{
|
||||
if(attrs & 0x20) {
|
||||
tile_a = gbInvertTab[tile_a];
|
||||
tile_b = gbInvertTab[tile_b];
|
||||
}
|
||||
|
||||
while(bx > 0)
|
||||
{
|
||||
while(bx > 0) {
|
||||
u8 c = (tile_a & bx) ? 1 : 0;
|
||||
c += ((tile_b & bx) ? 2 : 0);
|
||||
|
||||
@ -165,15 +155,11 @@ void gbRenderLine()
|
||||
if(attrs & 0x80)
|
||||
gbLineBuffer[x] |= 0x300;
|
||||
|
||||
if(gbCgbMode)
|
||||
{
|
||||
if(gbCgbMode) {
|
||||
c = c + (attrs & 7)*4;
|
||||
}
|
||||
else
|
||||
{
|
||||
c = gbBgp[c];
|
||||
if(gbSgbMode && !gbCgbMode)
|
||||
{
|
||||
} else {
|
||||
c = (gbBgpLine[x+(gbSpeed ? 5 : 11)+SpritesTicks]>>(c<<1)) &3;
|
||||
if(gbSgbMode && !gbCgbMode) {
|
||||
int dx = x >> 3;
|
||||
int dy = y >> 3;
|
||||
|
||||
@ -185,62 +171,101 @@ void gbRenderLine()
|
||||
c = c + 4*palette;
|
||||
}
|
||||
}
|
||||
gbLineMix[x] = gbColorOption ? gbColorFilter[gbPalette[c]] :
|
||||
gbPalette[c];
|
||||
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;
|
||||
|
||||
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 & 16) == 0)
|
||||
{
|
||||
if(tile < 128) tile += 128;
|
||||
else tile -= 128;
|
||||
}
|
||||
if(!(register_LCDC & 0x10))
|
||||
tile ^= 0x80;
|
||||
|
||||
tile_pattern_address = tile_pattern + tile * 16 + by * 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} 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++)
|
||||
{
|
||||
gbLineMix[i] = gbPalette[0];
|
||||
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
|
||||
if((register_LCDC & 0x20) && (layerSettings & 0x2000))
|
||||
// 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))
|
||||
{
|
||||
int wy = register_WY;
|
||||
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;
|
||||
|
||||
if(y >= wy)
|
||||
{
|
||||
int wx = register_WX;
|
||||
int swx = 0;
|
||||
wx -= 7;
|
||||
|
||||
if( wx <= 159 && gbWindowLine <= 143)
|
||||
{
|
||||
if( wx <= 159 && gbWindowLine <= 143) {
|
||||
|
||||
tile_map = 0x1800;
|
||||
|
||||
if((register_LCDC & 0x40) != 0)
|
||||
tile_map = 0x1c00;
|
||||
|
||||
if(gbWindowLine == -1)
|
||||
{
|
||||
gbWindowLine = 0;
|
||||
}
|
||||
|
||||
tx = 0;
|
||||
ty = gbWindowLine >> 3;
|
||||
@ -248,8 +273,26 @@ void gbRenderLine()
|
||||
bx = 128;
|
||||
by = gbWindowLine & 7;
|
||||
|
||||
if(wx < 0)
|
||||
// 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;
|
||||
}
|
||||
@ -266,60 +309,54 @@ void gbRenderLine()
|
||||
attrs = bank1[tile_map_address];
|
||||
tile_map_address++;
|
||||
|
||||
if((register_LCDC & 16) == 0)
|
||||
{
|
||||
if((register_LCDC & 16) == 0) {
|
||||
if(tile < 128) tile += 128;
|
||||
else tile -= 128;
|
||||
}
|
||||
|
||||
tile_pattern_address = tile_pattern + tile * 16 + by*2;
|
||||
|
||||
while(x < 160)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if(attrs & 0x40) {
|
||||
tile_pattern_address = tile_pattern + tile * 16 + (7-by)*2;
|
||||
}
|
||||
|
||||
if(attrs & 0x08)
|
||||
{
|
||||
if(attrs & 0x08) {
|
||||
tile_a = bank1[tile_pattern_address++];
|
||||
tile_b = bank1[tile_pattern_address];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
tile_a = bank0[tile_pattern_address++];
|
||||
tile_b = bank0[tile_pattern_address];
|
||||
}
|
||||
|
||||
if(attrs & 0x20)
|
||||
{
|
||||
if(attrs & 0x20) {
|
||||
tile_a = gbInvertTab[tile_a];
|
||||
tile_b = gbInvertTab[tile_b];
|
||||
}
|
||||
|
||||
while(bx > 0)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if(gbCgbMode) {
|
||||
c = c + (attrs & 7) * 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
c = gbBgp[c];
|
||||
if(gbSgbMode && ! gbCgbMode)
|
||||
{
|
||||
} 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;
|
||||
|
||||
@ -331,8 +368,9 @@ void gbRenderLine()
|
||||
c = c + 4*palette;
|
||||
}
|
||||
}
|
||||
gbLineMix[x] = gbColorOption ? gbColorFilter[gbPalette[c]] :
|
||||
gbPalette[c];
|
||||
gbLineMix[x] = gbColorOption ? gbColorFilter[gbPalette[c] & 0x7FFF] :
|
||||
gbPalette[c] & 0x7FFF;
|
||||
}
|
||||
x++;
|
||||
if(x >= 160)
|
||||
break;
|
||||
@ -346,23 +384,36 @@ void gbRenderLine()
|
||||
if(bank1)
|
||||
attrs = bank1[tile_map_line_y + tx];
|
||||
|
||||
if((register_LCDC & 16) == 0)
|
||||
{
|
||||
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
|
||||
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] = gbPalette[0];
|
||||
gbLineMix[i] = color;
|
||||
gbLineBuffer[i] = 0;
|
||||
}
|
||||
}
|
||||
@ -373,29 +424,26 @@ void gbDrawSpriteTile(int tile, int x,int y,int t, int flags,
|
||||
{
|
||||
u8 * bank0;
|
||||
u8 * bank1;
|
||||
if(gbCgbMode)
|
||||
{
|
||||
if(register_VBK & 1)
|
||||
{
|
||||
if(gbCgbMode) {
|
||||
if(register_VBK & 1) {
|
||||
bank0 = &gbVram[0x0000];
|
||||
bank1 = &gbVram[0x2000];
|
||||
} else {
|
||||
bank0 = &gbVram[0x0000];
|
||||
bank1 = &gbVram[0x2000];
|
||||
}
|
||||
else
|
||||
{
|
||||
bank0 = &gbVram[0x0000];
|
||||
bank1 = &gbVram[0x2000];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
bank0 = &gbMemory[0x8000];
|
||||
bank1 = NULL;
|
||||
}
|
||||
|
||||
int init = 0x0000;
|
||||
|
||||
// int yLine = (y+gbBorderRowSkip) * gbBorderLineSkip;
|
||||
|
||||
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);
|
||||
@ -404,8 +452,7 @@ void gbDrawSpriteTile(int tile, int x,int y,int t, int flags,
|
||||
if((flags & 0x10))
|
||||
pal = gbObp1;
|
||||
|
||||
if(flipy)
|
||||
{
|
||||
if(flipy) {
|
||||
t = (size ? 15 : 7) - t;
|
||||
}
|
||||
|
||||
@ -415,19 +462,15 @@ void gbDrawSpriteTile(int tile, int x,int y,int t, int flags,
|
||||
int a = 0;
|
||||
int b = 0;
|
||||
|
||||
if(gbCgbMode && flags & 0x08)
|
||||
{
|
||||
if(gbCgbMode && (flags & 0x08)) {
|
||||
a = bank1[address++];
|
||||
b = bank1[address++];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
a = bank0[address++];
|
||||
b = bank0[address++];
|
||||
}
|
||||
|
||||
for(int xx = 0; xx < 8; xx++)
|
||||
{
|
||||
for(int xx = 0; xx < 8; xx++) {
|
||||
u8 mask = 1 << (7-xx);
|
||||
u8 c = 0;
|
||||
if( (a & mask))
|
||||
@ -446,34 +489,30 @@ void gbDrawSpriteTile(int tile, int x,int y,int t, int flags,
|
||||
|
||||
u16 color = gbLineBuffer[xxx];
|
||||
|
||||
if(prio)
|
||||
{
|
||||
// Fixes OAM-BG priority
|
||||
if(prio && (register_LCDC & 1)) {
|
||||
if(color < 0x200 && ((color & 0xFF) != 0))
|
||||
continue;
|
||||
}
|
||||
if(color >= 0x300 && color != 0x300)
|
||||
// Fixes OAM-BG priority for Moorhuhn 2
|
||||
if(color >= 0x300 && color != 0x300 && (register_LCDC & 1))
|
||||
continue;
|
||||
else if(color >= 0x200 && color < 0x300)
|
||||
{
|
||||
else if(color >= 0x200 && color < 0x300) {
|
||||
int sprite = color & 0xff;
|
||||
|
||||
int spriteX = gbMemory[0xfe00 + 4 * sprite + 1] - 8;
|
||||
|
||||
if(spriteX == x)
|
||||
{
|
||||
if(spriteX == x) {
|
||||
if(sprite < spriteNumber)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(gbCgbMode)
|
||||
{
|
||||
} else {
|
||||
if(gbCgbMode) {
|
||||
if(sprite < spriteNumber)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(spriteX < x+8)
|
||||
} else {
|
||||
// Fixes GB sprites priorities (was '< x + 8' before)
|
||||
// ('A boy and his blob...' sprites' emulation is now correct)
|
||||
if(spriteX < x)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -483,16 +522,12 @@ void gbDrawSpriteTile(int tile, int x,int y,int t, int flags,
|
||||
gbLineBuffer[xxx] = 0x200 + spriteNumber;
|
||||
|
||||
// make sure that sprites will work even in CGB mode
|
||||
if(gbCgbMode)
|
||||
{
|
||||
if(gbCgbMode) {
|
||||
c = c + (flags & 0x07)*4 + 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
c = pal[c];
|
||||
|
||||
if(gbSgbMode && !gbCgbMode)
|
||||
{
|
||||
if(gbSgbMode && !gbCgbMode) {
|
||||
int dx = xxx >> 3;
|
||||
int dy = y >> 3;
|
||||
|
||||
@ -502,19 +537,17 @@ void gbDrawSpriteTile(int tile, int x,int y,int t, int flags,
|
||||
palette = 0;
|
||||
|
||||
c = c + 4*palette;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
c += 4;
|
||||
}
|
||||
}
|
||||
|
||||
gbLineMix[xxx] = gbColorOption ? gbColorFilter[gbPalette[c]] :
|
||||
gbPalette[c];
|
||||
gbLineMix[xxx] = gbColorOption ? gbColorFilter[gbPalette[c] & 0x7FFF] :
|
||||
gbPalette[c] & 0x7FFF;
|
||||
}
|
||||
}
|
||||
|
||||
void gbDrawSprites()
|
||||
void gbDrawSprites(bool draw)
|
||||
{
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
@ -522,16 +555,17 @@ void gbDrawSprites()
|
||||
|
||||
int size = (register_LCDC & 4);
|
||||
|
||||
if (!draw)
|
||||
memset (gbSpritesTicks, 0, sizeof(gbSpritesTicks));
|
||||
|
||||
if(!(register_LCDC & 0x80))
|
||||
return;
|
||||
|
||||
if((register_LCDC & 2) && (layerSettings & 0x1000))
|
||||
{
|
||||
if((register_LCDC & 2) && (layerSettings & 0x1000)) {
|
||||
int yc = register_LY;
|
||||
|
||||
int address = 0xfe00;
|
||||
for(int i = 0; i < 40; i++)
|
||||
{
|
||||
for(int i = 0; i < 40; i++) {
|
||||
y = gbMemory[address++];
|
||||
x = gbMemory[address++];
|
||||
int tile = gbMemory[address++];
|
||||
@ -539,18 +573,22 @@ void gbDrawSprites()
|
||||
tile &= 254;
|
||||
int flags = gbMemory[address++];
|
||||
|
||||
if(x > 0 && y > 0 && x < 168 && y < 160)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if((size && t >=0 && t < 16) || (!size && t >= 0 && t < 8)) {
|
||||
if (draw)
|
||||
gbDrawSpriteTile(tile,x-8,yc,t,flags,size,i);
|
||||
count++;
|
||||
}
|
||||
else if(!size && t >= 0 && t < 8)
|
||||
else
|
||||
{
|
||||
gbDrawSpriteTile(tile, x-8, yc, t, flags,size,i);
|
||||
for (int j = x-8; j<300; j++)
|
||||
if (j>=0)
|
||||
if (gbSpeed)
|
||||
gbSpritesTicks[j] += 5;
|
||||
else
|
||||
gbSpritesTicks[j] += 2+(count&1);
|
||||
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
@ -559,5 +597,5 @@ void gbDrawSprites()
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2004 Forgotten and the VBA development team
|
||||
// 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
|
||||
@ -24,6 +24,7 @@ int gbRomSizeMask = 0;
|
||||
int gbRomSize = 0;
|
||||
int gbRamSizeMask = 0;
|
||||
int gbRamSize = 0;
|
||||
int gbTAMA5ramSize = 0;
|
||||
|
||||
u8 *gbMemory = NULL;
|
||||
u8 *gbVram = NULL;
|
||||
@ -31,6 +32,7 @@ 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};
|
||||
@ -38,6 +40,7 @@ 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];
|
||||
|
@ -1,7 +1,7 @@
|
||||
// -*- C++ -*-
|
||||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2004 Forgotten and the VBA development team
|
||||
// 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
|
||||
@ -21,6 +21,12 @@ 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 bool skipSaveGameBattery;
|
||||
|
||||
extern u8 *gbRom;
|
||||
extern u8 *gbRam;
|
||||
@ -28,6 +34,7 @@ extern u8 *gbVram;
|
||||
extern u8 *gbWram;
|
||||
extern u8 *gbMemory;
|
||||
extern u16 *gbLineBuffer;
|
||||
extern u8 *gbTAMA5ram;
|
||||
|
||||
extern u8 *gbMemoryMap[16];
|
||||
|
||||
@ -46,6 +53,19 @@ 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;
|
||||
@ -54,8 +74,10 @@ 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;
|
||||
@ -63,6 +85,6 @@ extern int gbBorderColumnSkip;
|
||||
extern int gbDmaTicks;
|
||||
|
||||
extern void gbRenderLine();
|
||||
extern void gbDrawSprites();
|
||||
extern void gbDrawSprites(bool);
|
||||
|
||||
extern u8 (*gbSerialFunction)(u8);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
||||
// -*- C++ -*-
|
||||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2004 Forgotten and the VBA development team
|
||||
// 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
|
||||
@ -19,24 +19,22 @@
|
||||
|
||||
#include <time.h>
|
||||
|
||||
struct mapperMBC1
|
||||
{
|
||||
struct mapperMBC1 {
|
||||
int mapperRAMEnable;
|
||||
int mapperROMBank;
|
||||
int mapperRAMBank;
|
||||
int mapperMemoryModel;
|
||||
int mapperROMHighAddress;
|
||||
int mapperRAMAddress;
|
||||
};
|
||||
int mapperRomBank0Remapping;
|
||||
};
|
||||
|
||||
struct mapperMBC2
|
||||
{
|
||||
struct mapperMBC2 {
|
||||
int mapperRAMEnable;
|
||||
int mapperROMBank;
|
||||
};
|
||||
};
|
||||
|
||||
struct mapperMBC3
|
||||
{
|
||||
struct mapperMBC3 {
|
||||
int mapperRAMEnable;
|
||||
int mapperROMBank;
|
||||
int mapperRAMBank;
|
||||
@ -54,20 +52,18 @@ struct mapperMBC3
|
||||
int mapperLDays;
|
||||
int mapperLControl;
|
||||
time_t mapperLastTime;
|
||||
};
|
||||
};
|
||||
|
||||
struct mapperMBC5
|
||||
{
|
||||
struct mapperMBC5 {
|
||||
int mapperRAMEnable;
|
||||
int mapperROMBank;
|
||||
int mapperRAMBank;
|
||||
int mapperROMHighAddress;
|
||||
int mapperRAMAddress;
|
||||
int isRumbleCartridge;
|
||||
};
|
||||
};
|
||||
|
||||
struct mapperMBC7
|
||||
{
|
||||
struct mapperMBC7 {
|
||||
int mapperRAMEnable;
|
||||
int mapperROMBank;
|
||||
int mapperRAMBank;
|
||||
@ -82,20 +78,18 @@ struct mapperMBC7
|
||||
int address;
|
||||
int writeEnable;
|
||||
int value;
|
||||
};
|
||||
};
|
||||
|
||||
struct mapperHuC1
|
||||
{
|
||||
struct mapperHuC1 {
|
||||
int mapperRAMEnable;
|
||||
int mapperROMBank;
|
||||
int mapperRAMBank;
|
||||
int mapperMemoryModel;
|
||||
int mapperROMHighAddress;
|
||||
int mapperRAMAddress;
|
||||
};
|
||||
};
|
||||
|
||||
struct mapperHuC3
|
||||
{
|
||||
struct mapperHuC3 {
|
||||
int mapperRAMEnable;
|
||||
int mapperROMBank;
|
||||
int mapperRAMBank;
|
||||
@ -111,7 +105,50 @@ struct mapperHuC3
|
||||
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;
|
||||
@ -119,9 +156,13 @@ 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);
|
||||
@ -129,6 +170,7 @@ 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);
|
||||
@ -137,7 +179,13 @@ 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);
|
||||
@ -149,7 +197,9 @@ extern void memoryUpdateMapMBC5();
|
||||
extern void memoryUpdateMapMBC7();
|
||||
extern void memoryUpdateMapHuC1();
|
||||
extern void memoryUpdateMapHuC3();
|
||||
|
||||
extern void memoryUpdateMapTAMA5();
|
||||
extern void memoryUpdateMapMMM01();
|
||||
extern void memoryUpdateMapGS3();
|
||||
|
||||
|
||||
|
||||
|
@ -33,8 +33,7 @@ bool gbPrinterCheckCRC()
|
||||
{
|
||||
u16 crc = 0;
|
||||
|
||||
for(int i = 2; i < (6+gbPrinterDataSize); i++)
|
||||
{
|
||||
for(int i = 2; i < (6+gbPrinterDataSize); i++) {
|
||||
crc += gbPrinterPacket[i];
|
||||
}
|
||||
|
||||
@ -106,24 +105,19 @@ void gbPrinterShowData()
|
||||
|
||||
void gbPrinterReceiveData()
|
||||
{
|
||||
if(gbPrinterPacket[3])
|
||||
{ // compressed
|
||||
if(gbPrinterPacket[3]) { // compressed
|
||||
u8 *data = &gbPrinterPacket[6];
|
||||
u8 *dest = &gbPrinterData[gbPrinterDataCount];
|
||||
int len = 0;
|
||||
while(len < gbPrinterDataSize)
|
||||
{
|
||||
while(len < gbPrinterDataSize) {
|
||||
u8 control = *data++;
|
||||
if(control & 0x80)
|
||||
{ // repeated data
|
||||
if(control & 0x80) { // repeated data
|
||||
control &= 0x7f;
|
||||
control += 2;
|
||||
memset(dest, *data++, control);
|
||||
len += control;
|
||||
dest += control;
|
||||
}
|
||||
else
|
||||
{ // raw data
|
||||
} else { // raw data
|
||||
control++;
|
||||
memcpy(dest, data, control);
|
||||
dest += control;
|
||||
@ -131,9 +125,7 @@ void gbPrinterReceiveData()
|
||||
len += control;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
memcpy(&gbPrinterData[gbPrinterDataCount],
|
||||
&gbPrinterPacket[6],
|
||||
gbPrinterDataSize);
|
||||
@ -143,8 +135,7 @@ void gbPrinterReceiveData()
|
||||
|
||||
void gbPrinterCommand()
|
||||
{
|
||||
switch(gbPrinterPacket[2])
|
||||
{
|
||||
switch(gbPrinterPacket[2]) {
|
||||
case 0x01:
|
||||
// reset/initialize packet
|
||||
gbPrinterDataCount = 0;
|
||||
@ -166,31 +157,24 @@ void gbPrinterCommand()
|
||||
|
||||
u8 gbPrinterSend(u8 b)
|
||||
{
|
||||
switch(gbPrinterState)
|
||||
{
|
||||
switch(gbPrinterState) {
|
||||
case 0:
|
||||
gbPrinterCount = 0;
|
||||
// receiving preamble
|
||||
if(b == 0x88)
|
||||
{
|
||||
if(b == 0x88) {
|
||||
gbPrinterPacket[gbPrinterCount++] = b;
|
||||
gbPrinterState++;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// todo: handle failure
|
||||
gbPrinterReset();
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
// receiving preamble
|
||||
if(b == 0x33)
|
||||
{
|
||||
if(b == 0x33) {
|
||||
gbPrinterPacket[gbPrinterCount++] = b;
|
||||
gbPrinterState++;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// todo: handle failure
|
||||
gbPrinterReset();
|
||||
}
|
||||
@ -198,19 +182,16 @@ u8 gbPrinterSend(u8 b)
|
||||
case 2:
|
||||
// receiving header
|
||||
gbPrinterPacket[gbPrinterCount++] = b;
|
||||
if(gbPrinterCount == 6)
|
||||
{
|
||||
if(gbPrinterCount == 6) {
|
||||
gbPrinterState++;
|
||||
gbPrinterDataSize = gbPrinterPacket[4] + (gbPrinterPacket[5]<<8);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
// receiving data
|
||||
if(gbPrinterDataSize)
|
||||
{
|
||||
if(gbPrinterDataSize) {
|
||||
gbPrinterPacket[gbPrinterCount++] = b;
|
||||
if(gbPrinterCount == (6+gbPrinterDataSize))
|
||||
{
|
||||
if(gbPrinterCount == (6+gbPrinterDataSize)) {
|
||||
gbPrinterState++;
|
||||
}
|
||||
break;
|
||||
@ -225,8 +206,7 @@ u8 gbPrinterSend(u8 b)
|
||||
case 5:
|
||||
// receiving CRC-2
|
||||
gbPrinterPacket[gbPrinterCount++] = b;
|
||||
if(gbPrinterCheckCRC())
|
||||
{
|
||||
if(gbPrinterCheckCRC()) {
|
||||
gbPrinterCommand();
|
||||
}
|
||||
gbPrinterState++;
|
||||
|
@ -1,6 +1,6 @@
|
||||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2004 Forgotten and the VBA development team
|
||||
// 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
|
||||
@ -22,11 +22,12 @@
|
||||
#include "../System.h"
|
||||
#include "../Port.h"
|
||||
#include "../Util.h"
|
||||
#include "GB.h"
|
||||
#include "gb.h"
|
||||
#include "gbGlobals.h"
|
||||
|
||||
extern u8 *pix;
|
||||
extern bool speedup;
|
||||
extern bool gbSgbResetFlag;
|
||||
|
||||
#define GBSGB_NONE 0
|
||||
#define GBSGB_RESET 1
|
||||
@ -92,13 +93,11 @@ void gbSgbReset()
|
||||
memset(gbSgbBorder, 0, 2048);
|
||||
|
||||
int i;
|
||||
for(i = 1; i < 2048; i+=2)
|
||||
{
|
||||
for(i = 1; i < 2048; i+=2) {
|
||||
gbSgbBorder[i] = 1 << 2;
|
||||
}
|
||||
|
||||
for(i = 0; i < 4; i++)
|
||||
{
|
||||
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);
|
||||
@ -116,14 +115,12 @@ void gbSgbInit()
|
||||
|
||||
void gbSgbShutdown()
|
||||
{
|
||||
if(gbSgbBorderChar != NULL)
|
||||
{
|
||||
if(gbSgbBorderChar != NULL) {
|
||||
free(gbSgbBorderChar);
|
||||
gbSgbBorderChar = NULL;
|
||||
}
|
||||
|
||||
if(gbSgbBorder != NULL)
|
||||
{
|
||||
if(gbSgbBorder != NULL) {
|
||||
free(gbSgbBorder);
|
||||
gbSgbBorder = NULL;
|
||||
}
|
||||
@ -131,12 +128,10 @@ void gbSgbShutdown()
|
||||
|
||||
void gbSgbFillScreen(u16 color)
|
||||
{
|
||||
switch(systemColorDepth)
|
||||
{
|
||||
switch(systemColorDepth) {
|
||||
case 16:
|
||||
{
|
||||
for(int y = 0; y < 144; y++)
|
||||
{
|
||||
for(int y = 0; y < 144; y++) {
|
||||
int yLine = (y+gbBorderRowSkip+1)*(gbBorderLineSkip+2) +
|
||||
gbBorderColumnSkip;
|
||||
u16 *dest = (u16*)pix + yLine;
|
||||
@ -147,12 +142,10 @@ void gbSgbFillScreen(u16 color)
|
||||
break;
|
||||
case 24:
|
||||
{
|
||||
for(int y = 0; y < 144; y++)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
for(register int x = 0; x < 160; x++) {
|
||||
gbSgbDraw24Bit(dest, color);
|
||||
dest += 3;
|
||||
}
|
||||
@ -161,12 +154,10 @@ void gbSgbFillScreen(u16 color)
|
||||
break;
|
||||
case 32:
|
||||
{
|
||||
for(int y = 0; y < 144; y++)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
for(register int x = 0; x < 160; x++) {
|
||||
gbSgbDraw32Bit(dest++, color);
|
||||
}
|
||||
}
|
||||
@ -188,23 +179,19 @@ void gbSgbRenderScreenToBuffer()
|
||||
|
||||
int flag = 1;
|
||||
|
||||
if(register_LCDC & 0x10)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
for(int i = 0; i < 13; i++) {
|
||||
for(int j = 0; j < 20; j++) {
|
||||
int tile = getmem(mapAddress);
|
||||
mapAddress++;
|
||||
|
||||
if(flag)
|
||||
{
|
||||
if(flag) {
|
||||
if(tile > 127)
|
||||
tile -= 128;
|
||||
else
|
||||
@ -241,16 +228,22 @@ void gbSgbDrawBorderTile(int x, int y, int tile, int attr)
|
||||
int flipX = attr & 0x40;
|
||||
int flipY = attr & 0x80;
|
||||
|
||||
while(l > 0)
|
||||
{
|
||||
while(l > 0) {
|
||||
u8 mask = 0x80;
|
||||
u8 a = *tileAddress++;
|
||||
u8 b = *tileAddress++;
|
||||
u8 c = *tileAddress2++;
|
||||
u8 d = *tileAddress2++;
|
||||
|
||||
while(mask > 0)
|
||||
{
|
||||
|
||||
|
||||
u8 yyy;
|
||||
if(!flipY)
|
||||
yyy = yy;
|
||||
else
|
||||
yyy = 7 - yy;
|
||||
|
||||
while(mask > 0) {
|
||||
|
||||
u8 color = 0;
|
||||
if(a & mask)
|
||||
@ -262,21 +255,22 @@ void gbSgbDrawBorderTile(int x, int y, int tile, int attr)
|
||||
if(d & mask)
|
||||
color+=8;
|
||||
|
||||
u8 xxx = xx;
|
||||
u8 yyy = yy;
|
||||
if (color || (y + yy < 40 || y + yy >= 184) || (x + xx < 48 || x + xx >= 208)) {
|
||||
u8 xxx;
|
||||
|
||||
if(flipX)
|
||||
if(!flipX)
|
||||
xxx = xx;
|
||||
else
|
||||
xxx = 7 - xx;
|
||||
if(flipY)
|
||||
yyy = 7 - yy;
|
||||
|
||||
u16 c = gbPalette[palette + color];
|
||||
if(!color)
|
||||
u16 c;
|
||||
if (color) {
|
||||
c = gbPalette[palette + color];
|
||||
} else {
|
||||
c = gbPalette[0];
|
||||
if((yy < 40 || yy >= 184) || (xx < 48 || xx >= 208))
|
||||
{
|
||||
switch(systemColorDepth)
|
||||
{
|
||||
}
|
||||
|
||||
switch(systemColorDepth) {
|
||||
case 16:
|
||||
gbSgbDraw16Bit(dest + yyy*(256+2) + xxx, c);
|
||||
break;
|
||||
@ -302,14 +296,11 @@ void gbSgbDrawBorderTile(int x, int y, int tile, int attr)
|
||||
|
||||
void gbSgbRenderBorder()
|
||||
{
|
||||
if(gbBorderOn)
|
||||
{
|
||||
if(gbBorderOn) {
|
||||
u8 *fromAddress = gbSgbBorder;
|
||||
|
||||
for(u8 y = 0; y < 28; y++)
|
||||
{
|
||||
for(u8 x = 0; x< 32; x++)
|
||||
{
|
||||
for(u8 y = 0; y < 28; y++) {
|
||||
for(u8 x = 0; x< 32; x++) {
|
||||
u8 tile = *fromAddress++;
|
||||
u8 attr = *fromAddress++;
|
||||
|
||||
@ -327,15 +318,13 @@ void gbSgbPicture()
|
||||
|
||||
u16 *paletteAddr = (u16 *)&gbSgbScreenBuffer[2048];
|
||||
|
||||
for(int i = 64; i < 128; i++)
|
||||
{
|
||||
for(int i = 64; i < 128; i++) {
|
||||
gbPalette[i] = READ16LE(paletteAddr++);
|
||||
}
|
||||
|
||||
gbSgbCGBSupport |= 4;
|
||||
|
||||
if(gbBorderAutomatic && !gbBorderOn && gbSgbCGBSupport > 4)
|
||||
{
|
||||
if(gbBorderAutomatic && !gbBorderOn && gbSgbCGBSupport > 4) {
|
||||
gbBorderOn = 1;
|
||||
systemGbBorderOn();
|
||||
}
|
||||
@ -343,8 +332,7 @@ void gbSgbPicture()
|
||||
if(gbBorderOn && !gbSgbMask)
|
||||
gbSgbRenderBorder();
|
||||
|
||||
if(gbSgbMode && gbCgbMode && gbSgbCGBSupport > 4)
|
||||
{
|
||||
if(gbSgbMode && gbCgbMode && gbSgbCGBSupport > 4) {
|
||||
gbSgbCGBSupport = 0;
|
||||
gbSgbMode = 0;
|
||||
gbSgbMask = 0;
|
||||
@ -361,13 +349,11 @@ void gbSgbSetPalette(int a,int b,u16 *p)
|
||||
u16 bit00 = READ16LE(p++);
|
||||
int i;
|
||||
|
||||
for(i = 1; i < 4; i++)
|
||||
{
|
||||
for(i = 1; i < 4; i++) {
|
||||
gbPalette[a*4+i] = READ16LE(p++);
|
||||
}
|
||||
|
||||
for(i = 1; i < 4; i++)
|
||||
{
|
||||
for(i = 1; i < 4; i++) {
|
||||
gbPalette[b*4+i] = READ16LE(p++);
|
||||
}
|
||||
|
||||
@ -382,8 +368,7 @@ void gbSgbScpPalette()
|
||||
|
||||
u16 *fromAddress = (u16 *)gbSgbScreenBuffer;
|
||||
|
||||
for(int i = 0; i < 512*4; i++)
|
||||
{
|
||||
for(int i = 0; i < 512*4; i++) {
|
||||
gbSgbSCPPalette[i] = READ16LE(fromAddress++);
|
||||
}
|
||||
}
|
||||
@ -396,8 +381,7 @@ void gbSgbSetATF(int n)
|
||||
n = 44;
|
||||
memcpy(gbSgbATF,&gbSgbATFList[n * 20 * 18], 20 * 18);
|
||||
|
||||
if(gbSgbPacket[1] & 0x40)
|
||||
{
|
||||
if(gbSgbPacket[1] & 0x40) {
|
||||
gbSgbMask = 0;
|
||||
if(gbBorderOn)
|
||||
gbSgbRenderBorder();
|
||||
@ -420,13 +404,11 @@ void gbSgbSetPalette()
|
||||
|
||||
u8 atf = gbSgbPacket[9];
|
||||
|
||||
if(atf & 0x80)
|
||||
{
|
||||
if(atf & 0x80) {
|
||||
gbSgbSetATF(atf & 0x3f);
|
||||
}
|
||||
|
||||
if(atf & 0x40)
|
||||
{
|
||||
if(atf & 0x40) {
|
||||
gbSgbMask = 0;
|
||||
if(gbBorderOn)
|
||||
gbSgbRenderBorder();
|
||||
@ -443,8 +425,7 @@ void gbSgbAttributeBlock()
|
||||
if(nDataSet == 0)
|
||||
nDataSet = 1;
|
||||
|
||||
while(nDataSet)
|
||||
{
|
||||
while(nDataSet) {
|
||||
u8 controlCode = (*fromAddress++) & 7;
|
||||
u8 paletteDesignation = (*fromAddress++) & 0x3f;
|
||||
u8 startH = (*fromAddress++) & 0x1f;
|
||||
@ -454,26 +435,19 @@ void gbSgbAttributeBlock()
|
||||
|
||||
u8 * toAddress = gbSgbATF;
|
||||
|
||||
for(u8 y = 0; y < 18; y++)
|
||||
{
|
||||
for(u8 x = 0; x < 20; x++)
|
||||
{
|
||||
for(u8 y = 0; y < 18; y++) {
|
||||
for(u8 x = 0; x < 20; x++) {
|
||||
if(x < startH || y < startV ||
|
||||
x > endH || y > endV)
|
||||
{
|
||||
x > endH || y > endV) {
|
||||
// outside
|
||||
if(controlCode & 0x04)
|
||||
*toAddress = (paletteDesignation >> 4) & 0x03;
|
||||
}
|
||||
else if(x > startH && x < endH &&
|
||||
y > startV && y < endV)
|
||||
{
|
||||
} else if(x > startH && x < endH &&
|
||||
y > startV && y < endV) {
|
||||
// inside
|
||||
if(controlCode & 0x01)
|
||||
*toAddress = paletteDesignation & 0x03;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// surrounding line
|
||||
if(controlCode & 0x02)
|
||||
*toAddress = (paletteDesignation>>2) & 0x03;
|
||||
@ -498,8 +472,7 @@ void gbSgbSetColumnPalette(u8 col, u8 p)
|
||||
|
||||
u8 *toAddress = &gbSgbATF[col];
|
||||
|
||||
for(u8 y = 0; y < 18; y++)
|
||||
{
|
||||
for(u8 y = 0; y < 18; y++) {
|
||||
*toAddress = p;
|
||||
toAddress += 20;
|
||||
}
|
||||
@ -516,8 +489,7 @@ void gbSgbSetRowPalette(u8 row, u8 p)
|
||||
|
||||
u8 *toAddress = &gbSgbATF[row*20];
|
||||
|
||||
for(u8 x = 0; x < 20; x++)
|
||||
{
|
||||
for(u8 x = 0; x < 20; x++) {
|
||||
*toAddress++ = p;
|
||||
}
|
||||
}
|
||||
@ -530,13 +502,11 @@ void gbSgbAttributeDivide()
|
||||
u8 colorAL = (control >> 2) & 3;
|
||||
u8 colorOL = (control >> 4) & 3;
|
||||
|
||||
if(control & 0x40)
|
||||
{
|
||||
if(control & 0x40) {
|
||||
if(coord > 17)
|
||||
coord = 17;
|
||||
|
||||
for(u8 i = 0; i < 18; i++)
|
||||
{
|
||||
for(u8 i = 0; i < 18; i++) {
|
||||
if(i < coord)
|
||||
gbSgbSetRowPalette(i, colorAL);
|
||||
else if ( i > coord)
|
||||
@ -544,14 +514,11 @@ void gbSgbAttributeDivide()
|
||||
else
|
||||
gbSgbSetRowPalette(i, colorOL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
if(coord > 19)
|
||||
coord = 19;
|
||||
|
||||
for(u8 i = 0; i < 20; i++)
|
||||
{
|
||||
for(u8 i = 0; i < 20; i++) {
|
||||
if(i < coord)
|
||||
gbSgbSetColumnPalette(i, colorAL);
|
||||
else if ( i > coord)
|
||||
@ -571,19 +538,15 @@ void gbSgbAttributeLine()
|
||||
if(nDataSet > 0x6e)
|
||||
nDataSet = 0x6e;
|
||||
|
||||
while(nDataSet)
|
||||
{
|
||||
while(nDataSet) {
|
||||
u8 line = *fromAddress++;
|
||||
u8 num = line & 0x1f;
|
||||
u8 pal = (line >> 5) & 0x03;
|
||||
if(line & 0x80)
|
||||
{
|
||||
if(line & 0x80) {
|
||||
if(num > 17)
|
||||
num = 17;
|
||||
gbSgbSetRowPalette(num,pal);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
if(num > 19)
|
||||
num = 19;
|
||||
gbSgbSetColumnPalette(num,pal);
|
||||
@ -607,15 +570,12 @@ void gbSgbAttributeCharacter()
|
||||
u8 *fromAddress = &gbSgbPacket[6];
|
||||
u8 v = *fromAddress++;
|
||||
|
||||
if(style)
|
||||
{
|
||||
while(nDataSet)
|
||||
{
|
||||
if(style) {
|
||||
while(nDataSet) {
|
||||
u8 p = (v >> s) & 3;
|
||||
gbSgbATF[startV * 20 + startH] = p;
|
||||
startV++;
|
||||
if(startV == 18)
|
||||
{
|
||||
if(startV == 18) {
|
||||
startV = 0;
|
||||
startH++;
|
||||
if(startH == 20)
|
||||
@ -624,23 +584,18 @@ void gbSgbAttributeCharacter()
|
||||
|
||||
if(s)
|
||||
s -= 2;
|
||||
else
|
||||
{
|
||||
else {
|
||||
s = 6;
|
||||
v = *fromAddress++;
|
||||
nDataSet--;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while(nDataSet)
|
||||
{
|
||||
} else {
|
||||
while(nDataSet) {
|
||||
u8 p = (v >> s) & 3;
|
||||
gbSgbATF[startV * 20 + startH] = p;
|
||||
startH++;
|
||||
if(startH == 20)
|
||||
{
|
||||
if(startH == 20) {
|
||||
startH = 0;
|
||||
startV++;
|
||||
if(startV == 18)
|
||||
@ -649,8 +604,7 @@ void gbSgbAttributeCharacter()
|
||||
|
||||
if(s)
|
||||
s -= 2;
|
||||
else
|
||||
{
|
||||
else {
|
||||
s = 6;
|
||||
v = *fromAddress++;
|
||||
nDataSet--;
|
||||
@ -666,16 +620,13 @@ void gbSgbSetATFList()
|
||||
u8 *fromAddress = gbSgbScreenBuffer;
|
||||
u8 *toAddress = gbSgbATFList;
|
||||
|
||||
for(int i = 0; i < 45; i++)
|
||||
{
|
||||
for(int j = 0; j < 90; j++)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
for(int k = 0; k < 4; k++) {
|
||||
*toAddress++ = (v >> s) & 0x03;
|
||||
s -= 2;
|
||||
}
|
||||
@ -689,8 +640,7 @@ void gbSgbMaskEnable()
|
||||
|
||||
gbSgbMask = gbSgbMaskFlag;
|
||||
|
||||
switch(gbSgbMaskFlag)
|
||||
{
|
||||
switch(gbSgbMaskFlag) {
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
@ -701,8 +651,7 @@ void gbSgbMaskEnable()
|
||||
gbSgbFillScreen(gbPalette[0]);
|
||||
break;
|
||||
}
|
||||
if(!gbSgbMask)
|
||||
{
|
||||
if(!gbSgbMask) {
|
||||
if(gbBorderOn)
|
||||
gbSgbRenderBorder();
|
||||
}
|
||||
@ -721,8 +670,7 @@ void gbSgbChrTransfer()
|
||||
|
||||
memcpy(&gbSgbBorderChar[address], gbSgbScreenBuffer, 128 * 32);
|
||||
|
||||
if(gbBorderAutomatic && !gbBorderOn && gbSgbCGBSupport > 4)
|
||||
{
|
||||
if(gbBorderAutomatic && !gbBorderOn && gbSgbCGBSupport > 4) {
|
||||
gbBorderOn = 1;
|
||||
systemGbBorderOn();
|
||||
}
|
||||
@ -730,8 +678,7 @@ void gbSgbChrTransfer()
|
||||
if(gbBorderOn && !gbSgbMask)
|
||||
gbSgbRenderBorder();
|
||||
|
||||
if(gbSgbMode && gbCgbMode && gbSgbCGBSupport == 7)
|
||||
{
|
||||
if(gbSgbMode && gbCgbMode && gbSgbCGBSupport == 7) {
|
||||
gbSgbCGBSupport = 0;
|
||||
gbSgbMode = 0;
|
||||
gbSgbMask = 0;
|
||||
@ -745,17 +692,14 @@ void gbSgbChrTransfer()
|
||||
|
||||
void gbSgbMultiRequest()
|
||||
{
|
||||
if(gbSgbPacket[1] & 1)
|
||||
{
|
||||
if(gbSgbPacket[1] & 1) {
|
||||
gbSgbMultiplayer = 1;
|
||||
if(gbSgbPacket[1] & 2)
|
||||
gbSgbFourPlayers = 1;
|
||||
else
|
||||
gbSgbFourPlayers = 0;
|
||||
gbSgbNextController = 0x0e;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
gbSgbFourPlayers = 0;
|
||||
gbSgbMultiplayer = 0;
|
||||
gbSgbNextController = 0x0f;
|
||||
@ -767,8 +711,7 @@ void gbSgbCommand()
|
||||
int command = gbSgbPacket[0] >> 3;
|
||||
// int nPacket = gbSgbPacket[0] & 7;
|
||||
|
||||
switch(command)
|
||||
{
|
||||
switch(command) {
|
||||
case 0x00:
|
||||
gbSgbSetPalette(0,1,(u16 *)&gbSgbPacket[1]);
|
||||
break;
|
||||
@ -829,45 +772,31 @@ void gbSgbResetPacketState()
|
||||
void gbSgbDoBitTransfer(u8 value)
|
||||
{
|
||||
value = value & 0x30;
|
||||
switch(gbSgbPacketState)
|
||||
{
|
||||
switch(gbSgbPacketState) {
|
||||
case GBSGB_NONE:
|
||||
if(value == 0)
|
||||
{
|
||||
if(value == 0) {
|
||||
gbSgbPacketState = GBSGB_RESET;
|
||||
gbSgbPacketTimeout = GBSGB_PACKET_TIMEOUT;
|
||||
}
|
||||
else if (value == 0x30)
|
||||
{
|
||||
if(gbSgbMultiplayer)
|
||||
{
|
||||
if((gbSgbReadingController & 7) == 7)
|
||||
{
|
||||
} else if (value == 0x30) {
|
||||
if(gbSgbMultiplayer) {
|
||||
if((gbSgbReadingController & 7) == 7) {
|
||||
gbSgbReadingController = 0;
|
||||
if(gbSgbMultiplayer)
|
||||
{
|
||||
if(gbSgbMultiplayer) {
|
||||
gbSgbNextController--;
|
||||
if(gbSgbFourPlayers)
|
||||
{
|
||||
if(gbSgbFourPlayers) {
|
||||
if(gbSgbNextController == 0x0b)
|
||||
gbSgbNextController = 0x0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
if(gbSgbNextController == 0x0d)
|
||||
gbSgbNextController = 0x0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
gbSgbReadingController &= 3;
|
||||
}
|
||||
}
|
||||
gbSgbPacketTimeout = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
if(value == 0x10)
|
||||
gbSgbReadingController |= 0x2;
|
||||
else if(value == 0x20)
|
||||
@ -877,63 +806,47 @@ void gbSgbDoBitTransfer(u8 value)
|
||||
gbSgbPacketTimeout = 0;
|
||||
break;
|
||||
case GBSGB_RESET:
|
||||
if(value == 0x30)
|
||||
{
|
||||
if(value == 0x30) {
|
||||
gbSgbPacketState = GBSGB_PACKET_TRANSMIT;
|
||||
gbSgbPacketByte = 0;
|
||||
gbSgbPacketNBits = 0;
|
||||
gbSgbPacketTimeout = GBSGB_PACKET_TIMEOUT;
|
||||
}
|
||||
else if(value == 0x00)
|
||||
{
|
||||
} else if(value == 0x00) {
|
||||
gbSgbPacketTimeout = GBSGB_PACKET_TIMEOUT;
|
||||
gbSgbPacketState = GBSGB_RESET;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
gbSgbPacketState = GBSGB_NONE;
|
||||
gbSgbPacketTimeout = 0;
|
||||
}
|
||||
break;
|
||||
case GBSGB_PACKET_TRANSMIT:
|
||||
if(value == 0)
|
||||
{
|
||||
if(value == 0) {
|
||||
gbSgbPacketState = GBSGB_RESET;
|
||||
gbSgbPacketTimeout = 0;
|
||||
}
|
||||
else if (value == 0x30)
|
||||
{
|
||||
if(gbSgbPacketNBits == 128)
|
||||
{
|
||||
} else if (value == 0x30){
|
||||
if(gbSgbPacketNBits == 128) {
|
||||
gbSgbPacketNBits = 0;
|
||||
gbSgbPacketByte = 0;
|
||||
gbSgbPacketNumber++;
|
||||
gbSgbPacketTimeout = 0;
|
||||
if(gbSgbPacketNumber == (gbSgbPacket[0] & 7))
|
||||
{
|
||||
if(gbSgbPacketNumber == (gbSgbPacket[0] & 7)) {
|
||||
gbSgbCommand();
|
||||
gbSgbPacketNumber = 0;
|
||||
gbSgbPacketState = GBSGB_NONE;
|
||||
gbSgbPacketTimeout = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(gbSgbPacketNBits < 128)
|
||||
{
|
||||
} else {
|
||||
if(gbSgbPacketNBits < 128) {
|
||||
gbSgbPacket[gbSgbPacketNumber * 16 + gbSgbPacketByte] >>= 1;
|
||||
gbSgbPacket[gbSgbPacketNumber * 16 + gbSgbPacketByte] |= gbSgbBit;
|
||||
gbSgbPacketNBits++;
|
||||
if(!(gbSgbPacketNBits & 7))
|
||||
{
|
||||
if(!(gbSgbPacketNBits & 7)) {
|
||||
gbSgbPacketByte++;
|
||||
}
|
||||
gbSgbPacketTimeout = GBSGB_PACKET_TIMEOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
if(value == 0x20)
|
||||
gbSgbBit = 0x00;
|
||||
else
|
||||
@ -960,7 +873,7 @@ variable_desc gbSgbSaveStruct[] = {
|
||||
{ &gbSgbNextController, sizeof(u8) },
|
||||
{ &gbSgbReadingController, sizeof(u8) },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
};
|
||||
|
||||
variable_desc gbSgbSaveStructV3[] = {
|
||||
{ &gbSgbMask, sizeof(int) },
|
||||
@ -974,7 +887,7 @@ variable_desc gbSgbSaveStructV3[] = {
|
||||
{ &gbSgbReadingController, sizeof(u8) },
|
||||
{ &gbSgbFourPlayers, sizeof(int) },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
};
|
||||
|
||||
void gbSgbSaveGame(gzFile gzFile)
|
||||
{
|
||||
@ -994,14 +907,12 @@ void gbSgbReadGame(gzFile gzFile, int version)
|
||||
{
|
||||
if(version >= 3)
|
||||
utilReadData(gzFile, gbSgbSaveStructV3);
|
||||
else
|
||||
{
|
||||
else {
|
||||
utilReadData(gzFile, gbSgbSaveStruct);
|
||||
gbSgbFourPlayers = 0;
|
||||
}
|
||||
|
||||
if(version >= 8)
|
||||
{
|
||||
if(version >= 8) {
|
||||
utilGzRead(gzFile, gbSgbBorder, 2048);
|
||||
utilGzRead(gzFile, gbSgbBorderChar, 32*256);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,22 +1,39 @@
|
||||
// -*- C++ -*-
|
||||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
||||
// Copyright (C) 1999-2003 Forgotten
|
||||
// Copyright (C) 2004 Forgotten and the VBA development team
|
||||
// GB sound emulation
|
||||
|
||||
// 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_GBSOUND_H
|
||||
#define VBA_GBSOUND_H
|
||||
|
||||
// See Sound.h for sound setup/options
|
||||
|
||||
//// GB sound options
|
||||
|
||||
// Sets sample rate to 44100 / quality
|
||||
void gbSoundSetQuality( int quality );
|
||||
extern int soundQuality; // current sound quality
|
||||
|
||||
// Manages declicking mode. When enabled, clicks are reduced. Note that clicks
|
||||
// are normal for GB and GBC sound hardware.
|
||||
void gbSoundSetDeclicking( bool enable );
|
||||
bool gbSoundGetDeclicking();
|
||||
|
||||
// Effects configuration
|
||||
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
|
||||
};
|
||||
|
||||
// Changes effects configuration
|
||||
void gbSoundConfigEffects( gb_effects_config_t const& );
|
||||
extern gb_effects_config_t gb_effects_config; // current configuration
|
||||
|
||||
|
||||
//// GB sound emulation
|
||||
|
||||
// GB sound registers
|
||||
#define NR10 0xff10
|
||||
#define NR11 0xff11
|
||||
#define NR12 0xff12
|
||||
@ -39,21 +56,23 @@
|
||||
#define NR51 0xff25
|
||||
#define NR52 0xff26
|
||||
|
||||
#define SOUND_EVENT(address,value) \
|
||||
gbSoundEvent(address,value)
|
||||
// Resets emulated sound hardware
|
||||
void gbSoundReset();
|
||||
|
||||
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);
|
||||
// Emulates write to sound hardware
|
||||
void gbSoundEvent( u16 address, int data );
|
||||
#define SOUND_EVENT gbSoundEvent
|
||||
|
||||
extern int soundTicks;
|
||||
extern int soundQuality;
|
||||
extern int SOUND_CLOCK_TICKS;
|
||||
// Emulates read from sound hardware
|
||||
u8 gbSoundRead( u16 address );
|
||||
|
||||
// Notifies emulator that SOUND_CLOCK_TICKS clocks have passed
|
||||
void gbSoundTick();
|
||||
extern int SOUND_CLOCK_TICKS; // Number of 16.8 MHz clocks between calls to gbSoundTick()
|
||||
extern int soundTicks; // Number of 16.8 MHz clocks until gbSoundTick() will be called
|
||||
|
||||
// Saves/loads emulator state
|
||||
void gbSoundSaveGame( gzFile out );
|
||||
void gbSoundReadGame( int version, gzFile in );
|
||||
|
||||
#endif
|
||||
|
465
source/vba/dmg/gb_apu/Blip_Buffer.cpp
Normal file
465
source/vba/dmg/gb_apu/Blip_Buffer.cpp
Normal file
@ -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 );
|
||||
}
|
556
source/vba/dmg/gb_apu/Blip_Buffer.h
Normal file
556
source/vba/dmg/gb_apu/Blip_Buffer.h
Normal file
@ -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
|
638
source/vba/dmg/gb_apu/Effects_Buffer.cpp
Normal file
638
source/vba/dmg/gb_apu/Effects_Buffer.cpp
Normal file
@ -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 );
|
||||
}
|
||||
}
|
143
source/vba/dmg/gb_apu/Effects_Buffer.h
Normal file
143
source/vba/dmg/gb_apu/Effects_Buffer.h
Normal file
@ -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
|
394
source/vba/dmg/gb_apu/Gb_Apu.cpp
Normal file
394
source/vba/dmg/gb_apu/Gb_Apu.cpp
Normal file
@ -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;
|
||||
}
|
182
source/vba/dmg/gb_apu/Gb_Apu.h
Normal file
182
source/vba/dmg/gb_apu/Gb_Apu.h
Normal file
@ -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
|
118
source/vba/dmg/gb_apu/Gb_Apu_State.cpp
Normal file
118
source/vba/dmg/gb_apu/Gb_Apu_State.cpp
Normal file
@ -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;
|
||||
}
|
665
source/vba/dmg/gb_apu/Gb_Oscs.cpp
Normal file
665
source/vba/dmg/gb_apu/Gb_Oscs.cpp
Normal file
@ -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;
|
||||
}
|
190
source/vba/dmg/gb_apu/Gb_Oscs.h
Normal file
190
source/vba/dmg/gb_apu/Gb_Oscs.h
Normal file
@ -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
|
281
source/vba/dmg/gb_apu/Multi_Buffer.cpp
Normal file
281
source/vba/dmg/gb_apu/Multi_Buffer.cpp
Normal file
@ -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;
|
||||
}
|
||||
}
|
205
source/vba/dmg/gb_apu/Multi_Buffer.h
Normal file
205
source/vba/dmg/gb_apu/Multi_Buffer.h
Normal file
@ -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
|
206
source/vba/dmg/gb_apu/blargg_common.h
Normal file
206
source/vba/dmg/gb_apu/blargg_common.h
Normal file
@ -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
|
33
source/vba/dmg/gb_apu/blargg_config.h
Normal file
33
source/vba/dmg/gb_apu/blargg_config.h
Normal file
@ -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
|
92
source/vba/dmg/gb_apu/blargg_source.h
Normal file
92
source/vba/dmg/gb_apu/blargg_source.h
Normal file
@ -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
Block a user