Restyling
This commit is contained in:
parent
dec1772b7e
commit
d8513ef867
|
@ -1,183 +1,204 @@
|
|||
#pragma once
|
||||
|
||||
#include <utils.hpp>
|
||||
#include "sha1/sha1.hpp"
|
||||
#include <utils.hpp>
|
||||
|
||||
#define _LOW_MEM_SIZE 0x800
|
||||
#define _HIGH_MEM_SIZE 0x2000
|
||||
#define _NAMETABLES_MEM_SIZE 0x1000
|
||||
|
||||
// Size of image generated in graphics buffer
|
||||
static const uint16_t image_width = 256;
|
||||
static const uint16_t image_height = 240;
|
||||
static const uint16_t image_width = 256;
|
||||
static const uint16_t image_height = 240;
|
||||
|
||||
class EmuInstance
|
||||
{
|
||||
public:
|
||||
public:
|
||||
typedef uint8_t inputType;
|
||||
|
||||
typedef uint8_t inputType;
|
||||
// Deleting default constructors
|
||||
virtual ~EmuInstance() = default;
|
||||
|
||||
// Deleting default constructors
|
||||
virtual ~EmuInstance() = default;
|
||||
// Controller input bits
|
||||
// 0 - A / 1
|
||||
// 1 - B / 2
|
||||
// 2 - Select / 4
|
||||
// 3 - Start / 8
|
||||
// 4 - Up / 16
|
||||
// 5 - Down / 32
|
||||
// 6 - Left / 64
|
||||
// 7 - Right / 128
|
||||
|
||||
// Controller input bits
|
||||
// 0 - A / 1
|
||||
// 1 - B / 2
|
||||
// 2 - Select / 4
|
||||
// 3 - Start / 8
|
||||
// 4 - Up / 16
|
||||
// 5 - Down / 32
|
||||
// 6 - Left / 64
|
||||
// 7 - Right / 128
|
||||
// Move Format:
|
||||
// RLDUTSBA
|
||||
// ........
|
||||
|
||||
// Move Format:
|
||||
// RLDUTSBA
|
||||
// ........
|
||||
|
||||
static inline inputType moveStringToCode(const std::string& move)
|
||||
{
|
||||
inputType moveCode = 0;
|
||||
|
||||
for (size_t i = 0; i < move.size(); i++) switch(move[i])
|
||||
static inline inputType moveStringToCode(const std::string &move)
|
||||
{
|
||||
case 'U': moveCode |= 0b00010000; break;
|
||||
case 'D': moveCode |= 0b00100000; break;
|
||||
case 'L': moveCode |= 0b01000000; break;
|
||||
case 'R': moveCode |= 0b10000000; break;
|
||||
case 'S': moveCode |= 0b00001000; break;
|
||||
case 's': moveCode |= 0b00000100; break;
|
||||
case 'B': moveCode |= 0b00000010; break;
|
||||
case 'A': moveCode |= 0b00000001; break;
|
||||
case 'r': break;
|
||||
case '.': break;
|
||||
case '|': break;
|
||||
default: EXIT_WITH_ERROR("Move provided cannot be parsed: '%s', unrecognized character: '%c'\n", move.c_str(), move[i]);
|
||||
inputType moveCode = 0;
|
||||
|
||||
for (size_t i = 0; i < move.size(); i++) switch (move[i])
|
||||
{
|
||||
case 'U': moveCode |= 0b00010000; break;
|
||||
case 'D': moveCode |= 0b00100000; break;
|
||||
case 'L': moveCode |= 0b01000000; break;
|
||||
case 'R': moveCode |= 0b10000000; break;
|
||||
case 'S': moveCode |= 0b00001000; break;
|
||||
case 's': moveCode |= 0b00000100; break;
|
||||
case 'B': moveCode |= 0b00000010; break;
|
||||
case 'A': moveCode |= 0b00000001; break;
|
||||
case 'r': break;
|
||||
case '.': break;
|
||||
case '|': break;
|
||||
default: EXIT_WITH_ERROR("Move provided cannot be parsed: '%s', unrecognized character: '%c'\n", move.c_str(), move[i]);
|
||||
}
|
||||
|
||||
return moveCode;
|
||||
}
|
||||
|
||||
return moveCode;
|
||||
}
|
||||
|
||||
static inline std::string moveCodeToString(const inputType move)
|
||||
{
|
||||
static inline std::string moveCodeToString(const inputType move)
|
||||
{
|
||||
#ifndef _NES_PLAYER_2
|
||||
std::string moveString = "|..|";
|
||||
std::string moveString = "|..|";
|
||||
#else
|
||||
std::string moveString = "|..|........|";
|
||||
std::string moveString = "|..|........|";
|
||||
#endif
|
||||
|
||||
if (move & 0b00010000) moveString += 'U'; else moveString += '.';
|
||||
if (move & 0b00100000) moveString += 'D'; else moveString += '.';
|
||||
if (move & 0b01000000) moveString += 'L'; else moveString += '.';
|
||||
if (move & 0b10000000) moveString += 'R'; else moveString += '.';
|
||||
if (move & 0b00001000) moveString += 'S'; else moveString += '.';
|
||||
if (move & 0b00000100) moveString += 's'; else moveString += '.';
|
||||
if (move & 0b00000010) moveString += 'B'; else moveString += '.';
|
||||
if (move & 0b00000001) moveString += 'A'; else moveString += '.';
|
||||
if (move & 0b00010000)
|
||||
moveString += 'U';
|
||||
else
|
||||
moveString += '.';
|
||||
if (move & 0b00100000)
|
||||
moveString += 'D';
|
||||
else
|
||||
moveString += '.';
|
||||
if (move & 0b01000000)
|
||||
moveString += 'L';
|
||||
else
|
||||
moveString += '.';
|
||||
if (move & 0b10000000)
|
||||
moveString += 'R';
|
||||
else
|
||||
moveString += '.';
|
||||
if (move & 0b00001000)
|
||||
moveString += 'S';
|
||||
else
|
||||
moveString += '.';
|
||||
if (move & 0b00000100)
|
||||
moveString += 's';
|
||||
else
|
||||
moveString += '.';
|
||||
if (move & 0b00000010)
|
||||
moveString += 'B';
|
||||
else
|
||||
moveString += '.';
|
||||
if (move & 0b00000001)
|
||||
moveString += 'A';
|
||||
else
|
||||
moveString += '.';
|
||||
|
||||
moveString += "|";
|
||||
return moveString;
|
||||
}
|
||||
moveString += "|";
|
||||
return moveString;
|
||||
}
|
||||
|
||||
inline void advanceState(const std::string& move)
|
||||
{
|
||||
if (move.find("r") != std::string::npos) doSoftReset();
|
||||
inline void advanceState(const std::string &move)
|
||||
{
|
||||
if (move.find("r") != std::string::npos) doSoftReset();
|
||||
|
||||
advanceStateImpl(moveStringToCode(move), 0);
|
||||
}
|
||||
advanceStateImpl(moveStringToCode(move), 0);
|
||||
}
|
||||
|
||||
inline size_t getStateSize() const { return _stateSize; }
|
||||
inline size_t getLiteStateSize() const { return _liteStateSize; }
|
||||
inline std::string getRomSHA1() const { return _romSHA1String; }
|
||||
inline size_t getStateSize() const { return _stateSize; }
|
||||
inline size_t getLiteStateSize() const { return _liteStateSize; }
|
||||
inline std::string getRomSHA1() const { return _romSHA1String; }
|
||||
|
||||
inline hash_t getStateHash() const
|
||||
{
|
||||
MetroHash128 hash;
|
||||
inline hash_t getStateHash() const
|
||||
{
|
||||
MetroHash128 hash;
|
||||
|
||||
hash.Update(getLowMem(), _LOW_MEM_SIZE);
|
||||
hash.Update(getHighMem(), _HIGH_MEM_SIZE);
|
||||
hash.Update(getNametableMem(), _NAMETABLES_MEM_SIZE);
|
||||
hash.Update(getChrMem(), getChrMemSize());
|
||||
hash.Update(getLowMem(), _LOW_MEM_SIZE);
|
||||
hash.Update(getHighMem(), _HIGH_MEM_SIZE);
|
||||
hash.Update(getNametableMem(), _NAMETABLES_MEM_SIZE);
|
||||
hash.Update(getChrMem(), getChrMemSize());
|
||||
|
||||
hash_t result;
|
||||
hash.Finalize(reinterpret_cast<uint8_t *>(&result));
|
||||
return result;
|
||||
}
|
||||
hash_t result;
|
||||
hash.Finalize(reinterpret_cast<uint8_t *>(&result));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void enableRendering() { _doRendering = true; };
|
||||
inline void disableRendering() { _doRendering = false; };
|
||||
inline void enableRendering() { _doRendering = true; };
|
||||
inline void disableRendering() { _doRendering = false; };
|
||||
|
||||
inline void loadStateFile(const std::string& stateFilePath)
|
||||
{
|
||||
std::string stateData;
|
||||
bool status = loadStringFromFile(stateData, stateFilePath);
|
||||
if (status == false) EXIT_WITH_ERROR("Could not find/read state file: %s\n", stateFilePath.c_str());
|
||||
deserializeState((uint8_t*)stateData.data());
|
||||
}
|
||||
inline void loadStateFile(const std::string &stateFilePath)
|
||||
{
|
||||
std::string stateData;
|
||||
bool status = loadStringFromFile(stateData, stateFilePath);
|
||||
if (status == false) EXIT_WITH_ERROR("Could not find/read state file: %s\n", stateFilePath.c_str());
|
||||
deserializeState((uint8_t *)stateData.data());
|
||||
}
|
||||
|
||||
inline void saveStateFile(const std::string& stateFilePath) const
|
||||
{
|
||||
std::string stateData;
|
||||
stateData.resize(_stateSize);
|
||||
serializeState((uint8_t*)stateData.data());
|
||||
saveStringToFile(stateData, stateFilePath.c_str());
|
||||
}
|
||||
inline void saveStateFile(const std::string &stateFilePath) const
|
||||
{
|
||||
std::string stateData;
|
||||
stateData.resize(_stateSize);
|
||||
serializeState((uint8_t *)stateData.data());
|
||||
saveStringToFile(stateData, stateFilePath.c_str());
|
||||
}
|
||||
|
||||
inline void loadROMFile(const std::string& romFilePath)
|
||||
{
|
||||
// Loading ROM data
|
||||
bool status = loadStringFromFile(_romData, romFilePath);
|
||||
if (status == false) EXIT_WITH_ERROR("Could not find/read ROM file: %s\n", romFilePath.c_str());
|
||||
inline void loadROMFile(const std::string &romFilePath)
|
||||
{
|
||||
// Loading ROM data
|
||||
bool status = loadStringFromFile(_romData, romFilePath);
|
||||
if (status == false) EXIT_WITH_ERROR("Could not find/read ROM file: %s\n", romFilePath.c_str());
|
||||
|
||||
// Calculating ROM hash value
|
||||
_romSHA1String = SHA1::GetHash((uint8_t*)_romData.data(), _romData.size());
|
||||
// Calculating ROM hash value
|
||||
_romSHA1String = SHA1::GetHash((uint8_t *)_romData.data(), _romData.size());
|
||||
|
||||
// Actually loading rom file
|
||||
status = loadROMFileImpl(_romData);
|
||||
if (status == false) EXIT_WITH_ERROR("Could not process ROM file: %s\n", romFilePath.c_str());
|
||||
// Actually loading rom file
|
||||
status = loadROMFileImpl(_romData);
|
||||
if (status == false) EXIT_WITH_ERROR("Could not process ROM file: %s\n", romFilePath.c_str());
|
||||
|
||||
// Detecting full state size
|
||||
_stateSize = getStateSizeImpl();
|
||||
// Detecting full state size
|
||||
_stateSize = getStateSizeImpl();
|
||||
|
||||
// Detecting lite state size
|
||||
_liteStateSize = getLiteStateSizeImpl();
|
||||
}
|
||||
// Detecting lite state size
|
||||
_liteStateSize = getLiteStateSizeImpl();
|
||||
}
|
||||
|
||||
// Virtual functions
|
||||
// Virtual functions
|
||||
|
||||
virtual bool loadROMFileImpl(const std::string& romFilePath) = 0;
|
||||
virtual void advanceStateImpl(const inputType controller1, const inputType controller2) = 0;
|
||||
virtual uint8_t* getLowMem() const = 0;
|
||||
virtual uint8_t* getNametableMem() const = 0;
|
||||
virtual uint8_t* getHighMem() const = 0;
|
||||
virtual const uint8_t* getChrMem() const = 0;
|
||||
virtual size_t getChrMemSize() const = 0;
|
||||
virtual void serializeState(uint8_t* state) const = 0;
|
||||
virtual void deserializeState(const uint8_t* state) = 0;
|
||||
virtual size_t getStateSizeImpl() const = 0;
|
||||
virtual size_t getLiteStateSizeImpl() const { return getStateSizeImpl(); };
|
||||
virtual void doSoftReset() = 0;
|
||||
virtual void doHardReset() = 0;
|
||||
virtual std::string getCoreName() const = 0;
|
||||
virtual void* getInternalEmulatorPointer() const = 0;
|
||||
virtual bool loadROMFileImpl(const std::string &romFilePath) = 0;
|
||||
virtual void advanceStateImpl(const inputType controller1, const inputType controller2) = 0;
|
||||
virtual uint8_t *getLowMem() const = 0;
|
||||
virtual uint8_t *getNametableMem() const = 0;
|
||||
virtual uint8_t *getHighMem() const = 0;
|
||||
virtual const uint8_t *getChrMem() const = 0;
|
||||
virtual size_t getChrMemSize() const = 0;
|
||||
virtual void serializeState(uint8_t *state) const = 0;
|
||||
virtual void deserializeState(const uint8_t *state) = 0;
|
||||
virtual size_t getStateSizeImpl() const = 0;
|
||||
virtual size_t getLiteStateSizeImpl() const { return getStateSizeImpl(); };
|
||||
virtual void doSoftReset() = 0;
|
||||
virtual void doHardReset() = 0;
|
||||
virtual std::string getCoreName() const = 0;
|
||||
virtual void *getInternalEmulatorPointer() const = 0;
|
||||
|
||||
protected:
|
||||
protected:
|
||||
EmuInstance() = default;
|
||||
|
||||
EmuInstance() = default;
|
||||
// Storage for the light state size
|
||||
size_t _liteStateSize;
|
||||
|
||||
// Storage for the light state size
|
||||
size_t _liteStateSize;
|
||||
// Storage for the full state size
|
||||
size_t _stateSize;
|
||||
|
||||
// Storage for the full state size
|
||||
size_t _stateSize;
|
||||
// Flag to determine whether to enable/disable rendering
|
||||
bool _doRendering = true;
|
||||
|
||||
// Flag to determine whether to enable/disable rendering
|
||||
bool _doRendering = true;
|
||||
private:
|
||||
// Storage for the ROM data
|
||||
std::string _romData;
|
||||
|
||||
private:
|
||||
|
||||
// Storage for the ROM data
|
||||
std::string _romData;
|
||||
|
||||
// SHA1 rom hash
|
||||
std::string _romSHA1String;
|
||||
// SHA1 rom hash
|
||||
std::string _romSHA1String;
|
||||
};
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include "emuInstance.hpp"
|
||||
#include <SDL.h>
|
||||
#include <SDL_image.h>
|
||||
#include <utils.hpp>
|
||||
#include <hqn/hqn.h>
|
||||
#include <hqn/hqn_gui_controller.h>
|
||||
#include "emuInstance.hpp"
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include <utils.hpp>
|
||||
|
||||
#define _INVERSE_FRAME_RATE 16667
|
||||
|
||||
|
@ -16,19 +16,18 @@ class Nes_Emu;
|
|||
struct stepData_t
|
||||
{
|
||||
std::string input;
|
||||
uint8_t* stateData;
|
||||
uint8_t *stateData;
|
||||
hash_t hash;
|
||||
};
|
||||
|
||||
class PlaybackInstance
|
||||
{
|
||||
public:
|
||||
|
||||
void addStep(const std::string& input)
|
||||
public:
|
||||
void addStep(const std::string &input)
|
||||
{
|
||||
stepData_t step;
|
||||
step.input = input;
|
||||
step.stateData = (uint8_t*) malloc(_emu->getStateSize());
|
||||
step.stateData = (uint8_t *)malloc(_emu->getStateSize());
|
||||
_emu->serializeState(step.stateData);
|
||||
step.hash = _emu->getStateHash();
|
||||
|
||||
|
@ -37,219 +36,217 @@ class PlaybackInstance
|
|||
}
|
||||
|
||||
// Initializes the playback module instance
|
||||
PlaybackInstance(EmuInstance* emu, const std::vector<std::string>& sequence, const std::string& overlayPath = "") : _emu(emu)
|
||||
{
|
||||
// Enabling emulation rendering
|
||||
_emu->enableRendering();
|
||||
|
||||
// Loading Emulator instance HQN
|
||||
_hqnState.setEmulatorPointer(_emu->getInternalEmulatorPointer());
|
||||
static uint8_t video_buffer[Nes_Emu::image_width * Nes_Emu::image_height];
|
||||
_hqnState.m_emu->set_pixels(video_buffer, Nes_Emu::image_width+8);
|
||||
|
||||
// Building sequence information
|
||||
for (const auto& input : sequence)
|
||||
PlaybackInstance(EmuInstance *emu, const std::vector<std::string> &sequence, const std::string &overlayPath = "") : _emu(emu)
|
||||
{
|
||||
// Adding new step
|
||||
addStep(input);
|
||||
// Enabling emulation rendering
|
||||
_emu->enableRendering();
|
||||
|
||||
// Advance state based on the input received
|
||||
_emu->advanceState(input);
|
||||
// Loading Emulator instance HQN
|
||||
_hqnState.setEmulatorPointer(_emu->getInternalEmulatorPointer());
|
||||
static uint8_t video_buffer[Nes_Emu::image_width * Nes_Emu::image_height];
|
||||
_hqnState.m_emu->set_pixels(video_buffer, Nes_Emu::image_width + 8);
|
||||
|
||||
// Building sequence information
|
||||
for (const auto &input : sequence)
|
||||
{
|
||||
// Adding new step
|
||||
addStep(input);
|
||||
|
||||
// Advance state based on the input received
|
||||
_emu->advanceState(input);
|
||||
}
|
||||
|
||||
// Adding last step with no input
|
||||
addStep("<End Of Sequence>");
|
||||
|
||||
// Loading overlay, if provided
|
||||
if (overlayPath != "")
|
||||
{
|
||||
// Using overlay
|
||||
_useOverlay = true;
|
||||
|
||||
// Loading overlay images
|
||||
std::string imagePath;
|
||||
|
||||
imagePath = _overlayPath + std::string("/base.png");
|
||||
_overlayBaseSurface = IMG_Load(imagePath.c_str());
|
||||
if (_overlayBaseSurface == NULL) EXIT_WITH_ERROR("[Error] Could not load image: %s, Reason: %s\n", imagePath.c_str(), SDL_GetError());
|
||||
|
||||
imagePath = _overlayPath + std::string("/button_a.png");
|
||||
_overlayButtonASurface = IMG_Load(imagePath.c_str());
|
||||
if (_overlayButtonASurface == NULL) EXIT_WITH_ERROR("[Error] Could not load image: %s, Reason: %s\n", imagePath.c_str(), SDL_GetError());
|
||||
|
||||
imagePath = _overlayPath + std::string("/button_b.png");
|
||||
_overlayButtonBSurface = IMG_Load(imagePath.c_str());
|
||||
if (_overlayButtonBSurface == NULL) EXIT_WITH_ERROR("[Error] Could not load image: %s, Reason: %s\n", imagePath.c_str(), SDL_GetError());
|
||||
|
||||
imagePath = _overlayPath + std::string("/button_select.png");
|
||||
_overlayButtonSelectSurface = IMG_Load(imagePath.c_str());
|
||||
if (_overlayButtonSelectSurface == NULL) EXIT_WITH_ERROR("[Error] Could not load image: %s, Reason: %s\n", imagePath.c_str(), SDL_GetError());
|
||||
|
||||
imagePath = _overlayPath + std::string("/button_start.png");
|
||||
_overlayButtonStartSurface = IMG_Load(imagePath.c_str());
|
||||
if (_overlayButtonStartSurface == NULL) EXIT_WITH_ERROR("[Error] Could not load image: %s, Reason: %s\n", imagePath.c_str(), SDL_GetError());
|
||||
|
||||
imagePath = _overlayPath + std::string("/button_left.png");
|
||||
_overlayButtonLeftSurface = IMG_Load(imagePath.c_str());
|
||||
if (_overlayButtonLeftSurface == NULL) EXIT_WITH_ERROR("[Error] Could not load image: %s, Reason: %s\n", imagePath.c_str(), SDL_GetError());
|
||||
|
||||
imagePath = _overlayPath + std::string("/button_right.png");
|
||||
_overlayButtonRightSurface = IMG_Load(imagePath.c_str());
|
||||
if (_overlayButtonRightSurface == NULL) EXIT_WITH_ERROR("[Error] Could not load image: %s, Reason: %s\n", imagePath.c_str(), SDL_GetError());
|
||||
|
||||
imagePath = _overlayPath + std::string("/button_up.png");
|
||||
_overlayButtonUpSurface = IMG_Load(imagePath.c_str());
|
||||
if (_overlayButtonUpSurface == NULL) EXIT_WITH_ERROR("[Error] Could not load image: %s, Reason: %s\n", imagePath.c_str(), SDL_GetError());
|
||||
|
||||
imagePath = _overlayPath + std::string("/button_down.png");
|
||||
_overlayButtonDownSurface = IMG_Load(imagePath.c_str());
|
||||
if (_overlayButtonDownSurface == NULL) EXIT_WITH_ERROR("[Error] Could not load image: %s, Reason: %s\n", imagePath.c_str(), SDL_GetError());
|
||||
}
|
||||
|
||||
// Opening rendering window
|
||||
SDL_SetMainReady();
|
||||
|
||||
// We can only call SDL_InitSubSystem once
|
||||
if (!SDL_WasInit(SDL_INIT_VIDEO))
|
||||
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0)
|
||||
EXIT_WITH_ERROR("Failed to initialize video: %s", SDL_GetError());
|
||||
|
||||
// Creating HQN GUI
|
||||
_hqnGUI = hqn::GUIController::create(_hqnState);
|
||||
_hqnGUI->setScale(1);
|
||||
}
|
||||
|
||||
// Adding last step with no input
|
||||
addStep("<End Of Sequence>");
|
||||
|
||||
// Loading overlay, if provided
|
||||
if (overlayPath != "")
|
||||
// Function to render frame
|
||||
void renderFrame(const size_t stepId)
|
||||
{
|
||||
// Using overlay
|
||||
_useOverlay = true;
|
||||
// Checking the required step id does not exceed contents of the sequence
|
||||
if (stepId > _stepSequence.size()) EXIT_WITH_ERROR("[Error] Attempting to render a step larger than the step sequence");
|
||||
|
||||
// Loading overlay images
|
||||
std::string imagePath;
|
||||
// Getting step information
|
||||
const auto &step = _stepSequence[stepId];
|
||||
|
||||
imagePath = _overlayPath + std::string("/base.png");
|
||||
_overlayBaseSurface = IMG_Load(imagePath.c_str());
|
||||
if (_overlayBaseSurface == NULL) EXIT_WITH_ERROR("[Error] Could not load image: %s, Reason: %s\n", imagePath.c_str(), SDL_GetError());
|
||||
// Pointer to overlay images (NULL if unused)
|
||||
SDL_Surface *overlayButtonASurface = NULL;
|
||||
SDL_Surface *overlayButtonBSurface = NULL;
|
||||
SDL_Surface *overlayButtonSelectSurface = NULL;
|
||||
SDL_Surface *overlayButtonStartSurface = NULL;
|
||||
SDL_Surface *overlayButtonLeftSurface = NULL;
|
||||
SDL_Surface *overlayButtonRightSurface = NULL;
|
||||
SDL_Surface *overlayButtonUpSurface = NULL;
|
||||
SDL_Surface *overlayButtonDownSurface = NULL;
|
||||
|
||||
imagePath = _overlayPath + std::string("/button_a.png");
|
||||
_overlayButtonASurface = IMG_Load(imagePath.c_str());
|
||||
if (_overlayButtonASurface == NULL) EXIT_WITH_ERROR("[Error] Could not load image: %s, Reason: %s\n", imagePath.c_str(), SDL_GetError());
|
||||
// Load correct overlay images, if using overlay
|
||||
if (_useOverlay == true)
|
||||
{
|
||||
if (step.input.find("A") != std::string::npos) overlayButtonASurface = _overlayButtonASurface;
|
||||
if (step.input.find("B") != std::string::npos) overlayButtonBSurface = _overlayButtonBSurface;
|
||||
if (step.input.find("S") != std::string::npos) overlayButtonSelectSurface = _overlayButtonSelectSurface;
|
||||
if (step.input.find("T") != std::string::npos) overlayButtonStartSurface = _overlayButtonStartSurface;
|
||||
if (step.input.find("L") != std::string::npos) overlayButtonLeftSurface = _overlayButtonLeftSurface;
|
||||
if (step.input.find("R") != std::string::npos) overlayButtonRightSurface = _overlayButtonRightSurface;
|
||||
if (step.input.find("U") != std::string::npos) overlayButtonUpSurface = _overlayButtonUpSurface;
|
||||
if (step.input.find("D") != std::string::npos) overlayButtonDownSurface = _overlayButtonDownSurface;
|
||||
}
|
||||
|
||||
imagePath = _overlayPath + std::string("/button_b.png");
|
||||
_overlayButtonBSurface = IMG_Load(imagePath.c_str());
|
||||
if (_overlayButtonBSurface == NULL) EXIT_WITH_ERROR("[Error] Could not load image: %s, Reason: %s\n", imagePath.c_str(), SDL_GetError());
|
||||
// Since we do not store the blit information (too much memory), we need to load the previous frame and re-run the input
|
||||
|
||||
imagePath = _overlayPath + std::string("/button_select.png");
|
||||
_overlayButtonSelectSurface = IMG_Load(imagePath.c_str());
|
||||
if (_overlayButtonSelectSurface == NULL) EXIT_WITH_ERROR("[Error] Could not load image: %s, Reason: %s\n", imagePath.c_str(), SDL_GetError());
|
||||
// If its the first step, then simply reset
|
||||
if (stepId == 0) _emu->doHardReset();
|
||||
|
||||
imagePath = _overlayPath + std::string("/button_start.png");
|
||||
_overlayButtonStartSurface = IMG_Load(imagePath.c_str());
|
||||
if (_overlayButtonStartSurface == NULL) EXIT_WITH_ERROR("[Error] Could not load image: %s, Reason: %s\n", imagePath.c_str(), SDL_GetError());
|
||||
// Else we load the previous frame
|
||||
if (stepId > 0)
|
||||
{
|
||||
const auto stateData = getStateData(stepId - 1);
|
||||
_emu->deserializeState(stateData);
|
||||
_emu->advanceState(getStateInput(stepId - 1));
|
||||
}
|
||||
|
||||
imagePath = _overlayPath + std::string("/button_left.png");
|
||||
_overlayButtonLeftSurface = IMG_Load(imagePath.c_str());
|
||||
if (_overlayButtonLeftSurface == NULL) EXIT_WITH_ERROR("[Error] Could not load image: %s, Reason: %s\n", imagePath.c_str(), SDL_GetError());
|
||||
|
||||
imagePath = _overlayPath + std::string("/button_right.png");
|
||||
_overlayButtonRightSurface = IMG_Load(imagePath.c_str());
|
||||
if (_overlayButtonRightSurface == NULL) EXIT_WITH_ERROR("[Error] Could not load image: %s, Reason: %s\n", imagePath.c_str(), SDL_GetError());
|
||||
|
||||
imagePath = _overlayPath + std::string("/button_up.png");
|
||||
_overlayButtonUpSurface = IMG_Load(imagePath.c_str());
|
||||
if (_overlayButtonUpSurface == NULL) EXIT_WITH_ERROR("[Error] Could not load image: %s, Reason: %s\n", imagePath.c_str(), SDL_GetError());
|
||||
|
||||
imagePath = _overlayPath + std::string("/button_down.png");
|
||||
_overlayButtonDownSurface = IMG_Load(imagePath.c_str());
|
||||
if (_overlayButtonDownSurface == NULL) EXIT_WITH_ERROR("[Error] Could not load image: %s, Reason: %s\n", imagePath.c_str(), SDL_GetError());
|
||||
// Updating image
|
||||
int32_t curBlit[BLIT_SIZE];
|
||||
saveBlit(_emu->getInternalEmulatorPointer(), curBlit, hqn::HQNState::NES_VIDEO_PALETTE, 0, 0, 0, 0);
|
||||
_hqnGUI->update_blit(curBlit, _overlayBaseSurface, overlayButtonASurface, overlayButtonBSurface, overlayButtonSelectSurface, overlayButtonStartSurface, overlayButtonLeftSurface, overlayButtonRightSurface, overlayButtonUpSurface, overlayButtonDownSurface);
|
||||
}
|
||||
|
||||
// Opening rendering window
|
||||
SDL_SetMainReady();
|
||||
|
||||
// We can only call SDL_InitSubSystem once
|
||||
if (!SDL_WasInit(SDL_INIT_VIDEO))
|
||||
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0)
|
||||
EXIT_WITH_ERROR("Failed to initialize video: %s", SDL_GetError());
|
||||
|
||||
// Creating HQN GUI
|
||||
_hqnGUI = hqn::GUIController::create(_hqnState);
|
||||
_hqnGUI->setScale(1);
|
||||
}
|
||||
|
||||
// Function to render frame
|
||||
void renderFrame(const size_t stepId)
|
||||
{
|
||||
// Checking the required step id does not exceed contents of the sequence
|
||||
if (stepId > _stepSequence.size()) EXIT_WITH_ERROR("[Error] Attempting to render a step larger than the step sequence");
|
||||
|
||||
// Getting step information
|
||||
const auto& step = _stepSequence[stepId];
|
||||
|
||||
// Pointer to overlay images (NULL if unused)
|
||||
SDL_Surface* overlayButtonASurface = NULL;
|
||||
SDL_Surface* overlayButtonBSurface = NULL;
|
||||
SDL_Surface* overlayButtonSelectSurface = NULL;
|
||||
SDL_Surface* overlayButtonStartSurface = NULL;
|
||||
SDL_Surface* overlayButtonLeftSurface = NULL;
|
||||
SDL_Surface* overlayButtonRightSurface = NULL;
|
||||
SDL_Surface* overlayButtonUpSurface = NULL;
|
||||
SDL_Surface* overlayButtonDownSurface = NULL;
|
||||
|
||||
// Load correct overlay images, if using overlay
|
||||
if (_useOverlay == true)
|
||||
size_t getSequenceLength() const
|
||||
{
|
||||
if (step.input.find("A") != std::string::npos) overlayButtonASurface = _overlayButtonASurface;
|
||||
if (step.input.find("B") != std::string::npos) overlayButtonBSurface = _overlayButtonBSurface;
|
||||
if (step.input.find("S") != std::string::npos) overlayButtonSelectSurface = _overlayButtonSelectSurface;
|
||||
if (step.input.find("T") != std::string::npos) overlayButtonStartSurface = _overlayButtonStartSurface;
|
||||
if (step.input.find("L") != std::string::npos) overlayButtonLeftSurface = _overlayButtonLeftSurface;
|
||||
if (step.input.find("R") != std::string::npos) overlayButtonRightSurface = _overlayButtonRightSurface;
|
||||
if (step.input.find("U") != std::string::npos) overlayButtonUpSurface = _overlayButtonUpSurface;
|
||||
if (step.input.find("D") != std::string::npos) overlayButtonDownSurface = _overlayButtonDownSurface;
|
||||
return _stepSequence.size();
|
||||
}
|
||||
|
||||
// Since we do not store the blit information (too much memory), we need to load the previous frame and re-run the input
|
||||
|
||||
// If its the first step, then simply reset
|
||||
if (stepId == 0) _emu->doHardReset();
|
||||
|
||||
// Else we load the previous frame
|
||||
if (stepId > 0)
|
||||
const std::string getInput(const size_t stepId) const
|
||||
{
|
||||
const auto stateData = getStateData(stepId-1);
|
||||
_emu->deserializeState(stateData);
|
||||
_emu->advanceState(getStateInput(stepId-1));
|
||||
// Checking the required step id does not exceed contents of the sequence
|
||||
if (stepId > _stepSequence.size()) EXIT_WITH_ERROR("[Error] Attempting to render a step larger than the step sequence");
|
||||
|
||||
// Getting step information
|
||||
const auto &step = _stepSequence[stepId];
|
||||
|
||||
// Returning step input
|
||||
return step.input;
|
||||
}
|
||||
|
||||
// Updating image
|
||||
int32_t curBlit[BLIT_SIZE];
|
||||
saveBlit(_emu->getInternalEmulatorPointer(), curBlit, hqn::HQNState::NES_VIDEO_PALETTE, 0, 0, 0, 0);
|
||||
_hqnGUI->update_blit(curBlit, _overlayBaseSurface, overlayButtonASurface, overlayButtonBSurface, overlayButtonSelectSurface, overlayButtonStartSurface, overlayButtonLeftSurface, overlayButtonRightSurface, overlayButtonUpSurface, overlayButtonDownSurface);
|
||||
}
|
||||
const uint8_t *getStateData(const size_t stepId) const
|
||||
{
|
||||
// Checking the required step id does not exceed contents of the sequence
|
||||
if (stepId > _stepSequence.size()) EXIT_WITH_ERROR("[Error] Attempting to render a step larger than the step sequence");
|
||||
|
||||
size_t getSequenceLength() const
|
||||
{
|
||||
return _stepSequence.size();
|
||||
}
|
||||
// Getting step information
|
||||
const auto &step = _stepSequence[stepId];
|
||||
|
||||
const std::string getInput(const size_t stepId) const
|
||||
{
|
||||
// Checking the required step id does not exceed contents of the sequence
|
||||
if (stepId > _stepSequence.size()) EXIT_WITH_ERROR("[Error] Attempting to render a step larger than the step sequence");
|
||||
// Returning step input
|
||||
return step.stateData;
|
||||
}
|
||||
|
||||
// Getting step information
|
||||
const auto& step = _stepSequence[stepId];
|
||||
const hash_t getStateHash(const size_t stepId) const
|
||||
{
|
||||
// Checking the required step id does not exceed contents of the sequence
|
||||
if (stepId > _stepSequence.size()) EXIT_WITH_ERROR("[Error] Attempting to render a step larger than the step sequence");
|
||||
|
||||
// Returning step input
|
||||
return step.input;
|
||||
}
|
||||
// Getting step information
|
||||
const auto &step = _stepSequence[stepId];
|
||||
|
||||
const uint8_t* getStateData(const size_t stepId) const
|
||||
{
|
||||
// Checking the required step id does not exceed contents of the sequence
|
||||
if (stepId > _stepSequence.size()) EXIT_WITH_ERROR("[Error] Attempting to render a step larger than the step sequence");
|
||||
// Returning step input
|
||||
return step.hash;
|
||||
}
|
||||
|
||||
// Getting step information
|
||||
const auto& step = _stepSequence[stepId];
|
||||
const std::string getStateInput(const size_t stepId) const
|
||||
{
|
||||
// Checking the required step id does not exceed contents of the sequence
|
||||
if (stepId > _stepSequence.size()) EXIT_WITH_ERROR("[Error] Attempting to render a step larger than the step sequence");
|
||||
|
||||
// Returning step input
|
||||
return step.stateData;
|
||||
}
|
||||
// Getting step information
|
||||
const auto &step = _stepSequence[stepId];
|
||||
|
||||
const hash_t getStateHash(const size_t stepId) const
|
||||
{
|
||||
// Checking the required step id does not exceed contents of the sequence
|
||||
if (stepId > _stepSequence.size()) EXIT_WITH_ERROR("[Error] Attempting to render a step larger than the step sequence");
|
||||
// Returning step input
|
||||
return step.input;
|
||||
}
|
||||
|
||||
// Getting step information
|
||||
const auto& step = _stepSequence[stepId];
|
||||
private:
|
||||
// Internal sequence information
|
||||
std::vector<stepData_t> _stepSequence;
|
||||
|
||||
// Returning step input
|
||||
return step.hash;
|
||||
}
|
||||
// Storage for the HQN state
|
||||
hqn::HQNState _hqnState;
|
||||
|
||||
const std::string getStateInput(const size_t stepId) const
|
||||
{
|
||||
// Checking the required step id does not exceed contents of the sequence
|
||||
if (stepId > _stepSequence.size()) EXIT_WITH_ERROR("[Error] Attempting to render a step larger than the step sequence");
|
||||
// Storage for the HQN GUI controller
|
||||
hqn::GUIController *_hqnGUI;
|
||||
|
||||
// Getting step information
|
||||
const auto& step = _stepSequence[stepId];
|
||||
// Pointer to the contained emulator instance
|
||||
EmuInstance *const _emu;
|
||||
|
||||
// Returning step input
|
||||
return step.input;
|
||||
}
|
||||
// Flag to store whether to use the button overlay
|
||||
bool _useOverlay = false;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Internal sequence information
|
||||
std::vector<stepData_t> _stepSequence;
|
||||
|
||||
// Storage for the HQN state
|
||||
hqn::HQNState _hqnState;
|
||||
|
||||
// Storage for the HQN GUI controller
|
||||
hqn::GUIController* _hqnGUI;
|
||||
|
||||
// Pointer to the contained emulator instance
|
||||
EmuInstance* const _emu;
|
||||
|
||||
// Flag to store whether to use the button overlay
|
||||
bool _useOverlay = false;
|
||||
|
||||
// Overlay info
|
||||
std::string _overlayPath;
|
||||
SDL_Surface* _overlayBaseSurface = NULL;
|
||||
SDL_Surface* _overlayButtonASurface;
|
||||
SDL_Surface* _overlayButtonBSurface;
|
||||
SDL_Surface* _overlayButtonSelectSurface;
|
||||
SDL_Surface* _overlayButtonStartSurface;
|
||||
SDL_Surface* _overlayButtonLeftSurface;
|
||||
SDL_Surface* _overlayButtonRightSurface;
|
||||
SDL_Surface* _overlayButtonUpSurface;
|
||||
SDL_Surface* _overlayButtonDownSurface;
|
||||
// Overlay info
|
||||
std::string _overlayPath;
|
||||
SDL_Surface *_overlayBaseSurface = NULL;
|
||||
SDL_Surface *_overlayButtonASurface;
|
||||
SDL_Surface *_overlayButtonBSurface;
|
||||
SDL_Surface *_overlayButtonSelectSurface;
|
||||
SDL_Surface *_overlayButtonStartSurface;
|
||||
SDL_Surface *_overlayButtonLeftSurface;
|
||||
SDL_Surface *_overlayButtonRightSurface;
|
||||
SDL_Surface *_overlayButtonUpSurface;
|
||||
SDL_Surface *_overlayButtonDownSurface;
|
||||
};
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
#include <cstdlib>
|
||||
#include "argparse/argparse.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "emuInstance.hpp"
|
||||
#include "playbackInstance.hpp"
|
||||
#include "utils.hpp"
|
||||
#include <cstdlib>
|
||||
|
||||
#ifdef _USE_QUICKNES
|
||||
#include "quickNESInstance.hpp"
|
||||
#include "quickNESInstance.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef _USE_QUICKERNES
|
||||
#include "quickerNESInstance.hpp"
|
||||
#include "quickerNESInstance.hpp"
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
@ -26,8 +26,8 @@ int main(int argc, char *argv[])
|
|||
.required();
|
||||
|
||||
program.add_argument("stateFile")
|
||||
.help("(Optional) Path to the initial state file to load.")
|
||||
.default_value(std::string(""));
|
||||
.help("(Optional) Path to the initial state file to load.")
|
||||
.default_value(std::string(""));
|
||||
|
||||
program.add_argument("--reproduce")
|
||||
.help("Plays the entire sequence without interruptions and exit at the end.")
|
||||
|
@ -40,8 +40,14 @@ int main(int argc, char *argv[])
|
|||
.implicit_value(true);
|
||||
|
||||
// Try to parse arguments
|
||||
try { program.parse_args(argc, argv); }
|
||||
catch (const std::runtime_error &err) { EXIT_WITH_ERROR("%s\n%s", err.what(), program.help().str().c_str()); }
|
||||
try
|
||||
{
|
||||
program.parse_args(argc, argv);
|
||||
}
|
||||
catch (const std::runtime_error &err)
|
||||
{
|
||||
EXIT_WITH_ERROR("%s\n%s", err.what(), program.help().str().c_str());
|
||||
}
|
||||
|
||||
// Getting ROM file path
|
||||
std::string romFilePath = program.get<std::string>("romFile");
|
||||
|
@ -78,14 +84,14 @@ int main(int argc, char *argv[])
|
|||
|
||||
refreshTerminal();
|
||||
|
||||
// Creating emulator instance
|
||||
#ifdef _USE_QUICKNES
|
||||
// Creating emulator instance
|
||||
#ifdef _USE_QUICKNES
|
||||
auto e = QuickNESInstance();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _USE_QUICKERNES
|
||||
#ifdef _USE_QUICKERNES
|
||||
auto e = QuickerNESInstance();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Loading ROM File
|
||||
e.loadROMFile(romFilePath);
|
||||
|
@ -110,13 +116,13 @@ int main(int argc, char *argv[])
|
|||
bool showFrameInfo = true;
|
||||
|
||||
// Interactive section
|
||||
while(continueRunning)
|
||||
while (continueRunning)
|
||||
{
|
||||
// Updating display
|
||||
if (disableRender == false) p.renderFrame(currentStep);
|
||||
|
||||
// Getting input
|
||||
const auto& input = p.getStateInput(currentStep);
|
||||
const auto &input = p.getStateInput(currentStep);
|
||||
|
||||
// Getting state hash
|
||||
const auto hash = p.getStateHash(currentStep);
|
||||
|
@ -158,12 +164,12 @@ int main(int argc, char *argv[])
|
|||
|
||||
// Correct current step if requested more than possible
|
||||
if (currentStep < 0) currentStep = 0;
|
||||
if (currentStep >= sequenceLength) currentStep = sequenceLength-1;
|
||||
if (currentStep >= sequenceLength) currentStep = sequenceLength - 1;
|
||||
|
||||
// Quicksave creation command
|
||||
if (command == 's')
|
||||
{
|
||||
// Storing state file
|
||||
// Storing state file
|
||||
std::string saveFileName = "quicksave.state";
|
||||
|
||||
std::string saveData;
|
||||
|
@ -186,4 +192,3 @@ int main(int argc, char *argv[])
|
|||
// Ending ncurses window
|
||||
finalizeTerminal();
|
||||
}
|
||||
|
||||
|
|
|
@ -23,58 +23,59 @@
|
|||
|
||||
#include "Nes_Mapper.h"
|
||||
|
||||
template < bool _is152 >
|
||||
class Mapper_74x161x162x32 : public Nes_Mapper {
|
||||
public:
|
||||
Mapper_74x161x162x32()
|
||||
{
|
||||
register_state( &bank, 1 );
|
||||
}
|
||||
template <bool _is152>
|
||||
class Mapper_74x161x162x32 : public Nes_Mapper
|
||||
{
|
||||
public:
|
||||
Mapper_74x161x162x32()
|
||||
{
|
||||
register_state(&bank, 1);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{
|
||||
if ( _is152 == 0 )
|
||||
bank = ~0;
|
||||
}
|
||||
virtual void reset_state()
|
||||
{
|
||||
if (_is152 == 0)
|
||||
bank = ~0;
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
if ( _is152 )
|
||||
write( 0, 0, bank );
|
||||
else
|
||||
{
|
||||
intercept_writes( 0x6000, 1 );
|
||||
write_intercepted( 0, 0x6000, bank );
|
||||
}
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
if (_is152)
|
||||
write(0, 0, bank);
|
||||
else
|
||||
{
|
||||
intercept_writes(0x6000, 1);
|
||||
write_intercepted(0, 0x6000, bank);
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool write_intercepted( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
if ( ( addr != 0x6000 ) || _is152 )
|
||||
return false;
|
||||
virtual bool write_intercepted(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
if ((addr != 0x6000) || _is152)
|
||||
return false;
|
||||
|
||||
bank = data;
|
||||
set_prg_bank( 0x8000, bank_32k, ( bank >> 4 ) & 0x03 );
|
||||
set_chr_bank( 0x0000, bank_8k, ( ( bank >> 4 ) & 0x04 ) | ( bank & 0x03 ) );
|
||||
bank = data;
|
||||
set_prg_bank(0x8000, bank_32k, (bank >> 4) & 0x03);
|
||||
set_chr_bank(0x0000, bank_8k, ((bank >> 4) & 0x04) | (bank & 0x03));
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
if ( _is152 == 0) return;
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
if (_is152 == 0) return;
|
||||
|
||||
bank = handle_bus_conflict (addr, data );
|
||||
set_prg_bank( 0x8000, bank_16k, ( bank >> 4 ) & 0x07 );
|
||||
set_chr_bank( 0x0000, bank_8k, bank & 0x0F );
|
||||
mirror_single( ( bank >> 7) & 0x01 );
|
||||
}
|
||||
bank = handle_bus_conflict(addr, data);
|
||||
set_prg_bank(0x8000, bank_16k, (bank >> 4) & 0x07);
|
||||
set_chr_bank(0x0000, bank_8k, bank & 0x0F);
|
||||
mirror_single((bank >> 7) & 0x01);
|
||||
}
|
||||
|
||||
uint8_t bank;
|
||||
uint8_t bank;
|
||||
};
|
||||
|
||||
void register_mapper_70();
|
||||
void register_mapper_70()
|
||||
{
|
||||
register_mapper< Mapper_74x161x162x32 <true> > ( 70 );
|
||||
register_mapper<Mapper_74x161x162x32<true>>(70);
|
||||
}
|
||||
|
|
|
@ -12,81 +12,79 @@ extern void register_mapper_70();
|
|||
|
||||
class QuickNESInstance : public EmuInstance
|
||||
{
|
||||
public:
|
||||
public:
|
||||
QuickNESInstance() : EmuInstance()
|
||||
{
|
||||
// Creating new emulator
|
||||
_nes = new Nes_Emu;
|
||||
|
||||
QuickNESInstance() : EmuInstance()
|
||||
{
|
||||
// Creating new emulator
|
||||
_nes = new Nes_Emu;
|
||||
// Allocating video buffer
|
||||
video_buffer = (uint8_t *)malloc(image_width * image_height);
|
||||
|
||||
// Allocating video buffer
|
||||
video_buffer = (uint8_t*) malloc(image_width * image_height);
|
||||
// Setting video buffer
|
||||
_nes->set_pixels(video_buffer, image_width + 8);
|
||||
|
||||
// Setting video buffer
|
||||
_nes->set_pixels(video_buffer, image_width+8);
|
||||
// If running the original QuickNES, register extra mappers now
|
||||
register_misc_mappers();
|
||||
register_extra_mappers();
|
||||
register_mapper_70();
|
||||
}
|
||||
|
||||
// If running the original QuickNES, register extra mappers now
|
||||
register_misc_mappers();
|
||||
register_extra_mappers();
|
||||
register_mapper_70();
|
||||
}
|
||||
virtual bool loadROMFileImpl(const std::string &romData) override
|
||||
{
|
||||
// Loading rom data
|
||||
Mem_File_Reader romReader(romData.data(), (int)romData.size());
|
||||
Auto_File_Reader romFile(romReader);
|
||||
auto result = _nes->load_ines(romFile);
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
virtual bool loadROMFileImpl(const std::string& romData) override
|
||||
{
|
||||
// Loading rom data
|
||||
Mem_File_Reader romReader(romData.data(), (int)romData.size());
|
||||
Auto_File_Reader romFile(romReader);
|
||||
auto result = _nes->load_ines(romFile);
|
||||
return result == 0;
|
||||
}
|
||||
uint8_t *getLowMem() const override { return _nes->low_mem(); };
|
||||
uint8_t *getNametableMem() const override { return _nes->nametable_mem(); };
|
||||
uint8_t *getHighMem() const override { return _nes->high_mem(); };
|
||||
const uint8_t *getChrMem() const override { return _nes->chr_mem(); };
|
||||
size_t getChrMemSize() const override { return _nes->chr_size(); };
|
||||
|
||||
uint8_t* getLowMem() const override { return _nes->low_mem(); };
|
||||
uint8_t* getNametableMem() const override { return _nes->nametable_mem(); };
|
||||
uint8_t* getHighMem() const override { return _nes->high_mem();};
|
||||
const uint8_t* getChrMem() const override { return _nes->chr_mem();};
|
||||
size_t getChrMemSize() const override { return _nes->chr_size();};
|
||||
void serializeState(uint8_t *state) const override
|
||||
{
|
||||
Mem_Writer w(state, _stateSize, 0);
|
||||
Auto_File_Writer a(w);
|
||||
_nes->save_state(a);
|
||||
}
|
||||
|
||||
void serializeState(uint8_t* state) const override
|
||||
{
|
||||
Mem_Writer w(state, _stateSize, 0);
|
||||
Auto_File_Writer a(w);
|
||||
_nes->save_state(a);
|
||||
}
|
||||
void deserializeState(const uint8_t *state) override
|
||||
{
|
||||
Mem_File_Reader r(state, _stateSize);
|
||||
Auto_File_Reader a(r);
|
||||
_nes->load_state(a);
|
||||
}
|
||||
|
||||
void deserializeState(const uint8_t* state) override
|
||||
{
|
||||
Mem_File_Reader r(state, _stateSize);
|
||||
Auto_File_Reader a(r);
|
||||
_nes->load_state(a);
|
||||
}
|
||||
void advanceStateImpl(const inputType controller1, const inputType controller2) override
|
||||
{
|
||||
if (_doRendering == true) _nes->emulate_frame(controller1, controller2);
|
||||
if (_doRendering == false) _nes->emulate_skip_frame(controller1, controller2);
|
||||
}
|
||||
|
||||
void advanceStateImpl(const inputType controller1, const inputType controller2) override
|
||||
{
|
||||
if (_doRendering == true) _nes->emulate_frame(controller1, controller2);
|
||||
if (_doRendering == false) _nes->emulate_skip_frame(controller1, controller2);
|
||||
}
|
||||
std::string getCoreName() const override { return "QuickNES"; }
|
||||
void doSoftReset() override { _nes->reset(false); }
|
||||
void doHardReset() override { _nes->reset(true); }
|
||||
|
||||
std::string getCoreName() const override { return "QuickNES"; }
|
||||
void doSoftReset() override { _nes->reset(false); }
|
||||
void doHardReset() override { _nes->reset(true); }
|
||||
void *getInternalEmulatorPointer() const override { return _nes; }
|
||||
|
||||
void* getInternalEmulatorPointer() const override { return _nes; }
|
||||
private:
|
||||
inline size_t getStateSizeImpl() const override
|
||||
{
|
||||
uint8_t *data = (uint8_t *)malloc(_DUMMY_SIZE);
|
||||
Mem_Writer w(data, _DUMMY_SIZE);
|
||||
Auto_File_Writer a(w);
|
||||
_nes->save_state(a);
|
||||
free(data);
|
||||
return w.size();
|
||||
}
|
||||
|
||||
private:
|
||||
// Video buffer
|
||||
uint8_t *video_buffer;
|
||||
|
||||
inline size_t getStateSizeImpl() const override
|
||||
{
|
||||
uint8_t* data = (uint8_t*) malloc (_DUMMY_SIZE);
|
||||
Mem_Writer w(data, _DUMMY_SIZE);
|
||||
Auto_File_Writer a(w);
|
||||
_nes->save_state(a);
|
||||
free(data);
|
||||
return w.size();
|
||||
}
|
||||
|
||||
// Video buffer
|
||||
uint8_t* video_buffer;
|
||||
|
||||
// Emulator instance
|
||||
Nes_Emu* _nes;
|
||||
// Emulator instance
|
||||
Nes_Emu *_nes;
|
||||
};
|
||||
|
|
|
@ -15,86 +15,98 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
// Nes_Emu 0.7.0. http://www.slack.net/~ant/
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
class Nes_Cart {
|
||||
public:
|
||||
class Nes_Cart
|
||||
{
|
||||
public:
|
||||
Nes_Cart() = default;
|
||||
|
||||
Nes_Cart() = default;
|
||||
struct ines_header_t
|
||||
{
|
||||
uint8_t signature[4];
|
||||
uint8_t prg_count; // number of 16K PRG banks
|
||||
uint8_t chr_count; // number of 8K CHR banks
|
||||
uint8_t flags; // MMMM FTBV Mapper low, Four-screen, Trainer, Battery, V mirror
|
||||
uint8_t flags2; // MMMM --XX Mapper high 4 bits
|
||||
uint8_t zero[8]; // if zero [7] is non-zero, treat flags2 as zero
|
||||
};
|
||||
static_assert(sizeof(ines_header_t) == 16);
|
||||
|
||||
struct ines_header_t {
|
||||
uint8_t signature [4];
|
||||
uint8_t prg_count; // number of 16K PRG banks
|
||||
uint8_t chr_count; // number of 8K CHR banks
|
||||
uint8_t flags; // MMMM FTBV Mapper low, Four-screen, Trainer, Battery, V mirror
|
||||
uint8_t flags2; // MMMM --XX Mapper high 4 bits
|
||||
uint8_t zero [8]; // if zero [7] is non-zero, treat flags2 as zero
|
||||
};
|
||||
static_assert( sizeof (ines_header_t) == 16 );
|
||||
// Load iNES file
|
||||
void load_ines(const uint8_t *buffer)
|
||||
{
|
||||
ines_header_t h;
|
||||
|
||||
// Load iNES file
|
||||
void load_ines( const uint8_t* buffer )
|
||||
{
|
||||
ines_header_t h;
|
||||
size_t bufferPos = 0;
|
||||
{
|
||||
size_t copySize = sizeof(ines_header_t);
|
||||
memcpy(&h, &buffer[bufferPos], copySize);
|
||||
bufferPos += copySize;
|
||||
}
|
||||
if (h.zero[7]) h.flags2 = 0;
|
||||
set_mapper(h.flags, h.flags2);
|
||||
|
||||
size_t bufferPos = 0;
|
||||
{ size_t copySize = sizeof(ines_header_t); memcpy(&h, &buffer[bufferPos], copySize); bufferPos += copySize; }
|
||||
if ( h.zero [7] ) h.flags2 = 0;
|
||||
set_mapper( h.flags, h.flags2 );
|
||||
// skip trainer
|
||||
if (h.flags & 0x04) bufferPos += 512;
|
||||
|
||||
// skip trainer
|
||||
if ( h.flags & 0x04 ) bufferPos += 512;
|
||||
// Allocating memory for prg and chr
|
||||
prg_size_ = h.prg_count * 16 * 1024L;
|
||||
chr_size_ = h.chr_count * 8 * 1024L;
|
||||
|
||||
// Allocating memory for prg and chr
|
||||
prg_size_ = h.prg_count * 16 * 1024L;
|
||||
chr_size_ = h.chr_count * 8 * 1024L;
|
||||
auto p = malloc(prg_size_ + chr_size_);
|
||||
prg_ = (uint8_t *)p;
|
||||
chr_ = &prg_[prg_size_];
|
||||
|
||||
auto p = malloc(prg_size_ + chr_size_);
|
||||
prg_ = (uint8_t*)p;
|
||||
chr_ = &prg_[prg_size_];
|
||||
{
|
||||
size_t copySize = prg_size();
|
||||
memcpy(prg(), &buffer[bufferPos], copySize);
|
||||
bufferPos += copySize;
|
||||
}
|
||||
{
|
||||
size_t copySize = chr_size();
|
||||
memcpy(chr(), &buffer[bufferPos], copySize);
|
||||
bufferPos += copySize;
|
||||
}
|
||||
}
|
||||
|
||||
{ size_t copySize = prg_size(); memcpy(prg(), &buffer[bufferPos], copySize); bufferPos += copySize; }
|
||||
{ size_t copySize = chr_size(); memcpy(chr(), &buffer[bufferPos], copySize); bufferPos += copySize; }
|
||||
}
|
||||
inline bool has_battery_ram() const { return mapper & 0x02; }
|
||||
|
||||
inline bool has_battery_ram() const { return mapper & 0x02; }
|
||||
// Set mapper and information bytes. LSB and MSB are the standard iNES header
|
||||
// bytes at offsets 6 and 7.
|
||||
inline void set_mapper(int mapper_lsb, int mapper_msb)
|
||||
{
|
||||
mapper = mapper_msb * 0x100 + mapper_lsb;
|
||||
}
|
||||
|
||||
// Set mapper and information bytes. LSB and MSB are the standard iNES header
|
||||
// bytes at offsets 6 and 7.
|
||||
inline void set_mapper( int mapper_lsb, int mapper_msb )
|
||||
{
|
||||
mapper = mapper_msb * 0x100 + mapper_lsb;
|
||||
}
|
||||
inline int mapper_code() const { return ((mapper >> 8) & 0xf0) | ((mapper >> 4) & 0x0f); }
|
||||
|
||||
inline int mapper_code() const { return ((mapper >> 8) & 0xf0) | ((mapper >> 4) & 0x0f); }
|
||||
// Size of PRG data
|
||||
long prg_size() const { return prg_size_; }
|
||||
|
||||
// Size of PRG data
|
||||
long prg_size() const { return prg_size_; }
|
||||
// Size of CHR data
|
||||
long chr_size() const { return chr_size_; }
|
||||
|
||||
// Size of CHR data
|
||||
long chr_size() const { return chr_size_; }
|
||||
unsigned mapper_data() const { return mapper; }
|
||||
|
||||
unsigned mapper_data() const { return mapper; }
|
||||
// Initial mirroring setup
|
||||
int mirroring() const { return mapper & 0x09; }
|
||||
|
||||
// Initial mirroring setup
|
||||
int mirroring() const { return mapper & 0x09; }
|
||||
// Pointer to beginning of PRG data
|
||||
inline uint8_t *prg() { return prg_; }
|
||||
inline uint8_t const *prg() const { return prg_; }
|
||||
|
||||
// Pointer to beginning of PRG data
|
||||
inline uint8_t * prg() { return prg_; }
|
||||
inline uint8_t const* prg() const { return prg_; }
|
||||
// Pointer to beginning of CHR data
|
||||
inline uint8_t *chr() { return chr_; }
|
||||
inline uint8_t const *chr() const { return chr_; }
|
||||
|
||||
// Pointer to beginning of CHR data
|
||||
inline uint8_t * chr() { return chr_; }
|
||||
inline uint8_t const* chr() const { return chr_; }
|
||||
|
||||
// End of public interface
|
||||
private:
|
||||
uint8_t *prg_;
|
||||
uint8_t *chr_;
|
||||
long prg_size_;
|
||||
long chr_size_;
|
||||
unsigned mapper;
|
||||
// End of public interface
|
||||
private:
|
||||
uint8_t *prg_;
|
||||
uint8_t *chr_;
|
||||
long prg_size_;
|
||||
long chr_size_;
|
||||
unsigned mapper;
|
||||
};
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -5,119 +5,134 @@
|
|||
|
||||
#include <cstdint>
|
||||
|
||||
typedef long nes_time_t; // clock cycle count
|
||||
typedef long nes_time_t; // clock cycle count
|
||||
typedef unsigned nes_addr_t; // 16-bit address
|
||||
|
||||
class Nes_Cpu {
|
||||
public:
|
||||
class Nes_Cpu
|
||||
{
|
||||
public:
|
||||
// NES 6502 registers. *Not* kept updated during a call to run().
|
||||
struct registers_t
|
||||
{
|
||||
uint16_t pc; // Should be more than 16 bits to allow overflow detection -- but I (eien86) removed it to maximize performance.
|
||||
uint8_t a;
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
uint8_t status;
|
||||
uint8_t sp;
|
||||
};
|
||||
|
||||
// NES 6502 registers. *Not* kept updated during a call to run().
|
||||
struct registers_t {
|
||||
uint16_t pc; // Should be more than 16 bits to allow overflow detection -- but I (eien86) removed it to maximize performance.
|
||||
uint8_t a;
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
uint8_t status;
|
||||
uint8_t sp;
|
||||
};
|
||||
// Map code memory (memory accessed via the program counter). Start and size
|
||||
// must be multiple of page_size.
|
||||
enum
|
||||
{
|
||||
page_bits = 11
|
||||
};
|
||||
enum
|
||||
{
|
||||
page_count = 0x10000 >> page_bits
|
||||
};
|
||||
enum
|
||||
{
|
||||
page_size = 1L << page_bits
|
||||
};
|
||||
|
||||
// Map code memory (memory accessed via the program counter). Start and size
|
||||
// must be multiple of page_size.
|
||||
enum { page_bits = 11 };
|
||||
enum { page_count = 0x10000 >> page_bits };
|
||||
enum { page_size = 1L << page_bits };
|
||||
// Clear registers, unmap memory, and map code pages to unmapped_page.
|
||||
void reset(void const *unmapped_page = 0);
|
||||
|
||||
// Clear registers, unmap memory, and map code pages to unmapped_page.
|
||||
void reset( void const* unmapped_page = 0 );
|
||||
inline void map_code(nes_addr_t start, unsigned size, const void *data)
|
||||
{
|
||||
unsigned first_page = start / page_size;
|
||||
const uint8_t *newPtr = (uint8_t *)data - start;
|
||||
for (unsigned i = size / page_size; i--;) code_map[first_page + i] = newPtr;
|
||||
}
|
||||
|
||||
inline void map_code( nes_addr_t start, unsigned size, const void* data )
|
||||
{
|
||||
unsigned first_page = start / page_size;
|
||||
const uint8_t* newPtr = (uint8_t*) data - start;
|
||||
for ( unsigned i = size / page_size; i--; ) code_map [first_page + i] = newPtr;
|
||||
}
|
||||
// Access memory as the emulated CPU does.
|
||||
int read(nes_addr_t);
|
||||
void write(nes_addr_t, int data);
|
||||
|
||||
// Access memory as the emulated CPU does.
|
||||
int read( nes_addr_t );
|
||||
void write( nes_addr_t, int data );
|
||||
// Push a byte on the stack
|
||||
inline void push_byte(int data)
|
||||
{
|
||||
int sp = r.sp;
|
||||
r.sp = (sp - 1) & 0xFF;
|
||||
low_mem[0x100 + sp] = data;
|
||||
}
|
||||
|
||||
// Push a byte on the stack
|
||||
inline void push_byte( int data )
|
||||
{
|
||||
int sp = r.sp;
|
||||
r.sp = (sp - 1) & 0xFF;
|
||||
low_mem [0x100 + sp] = data;
|
||||
}
|
||||
// Reasons that run() returns
|
||||
enum result_t
|
||||
{
|
||||
result_cycles, // Requested number of cycles (or more) were executed
|
||||
result_sei, // I flag just set and IRQ time would generate IRQ now
|
||||
result_cli, // I flag just cleared but IRQ should occur *after* next instr
|
||||
result_badop // unimplemented/illegal instruction
|
||||
};
|
||||
|
||||
// Reasons that run() returns
|
||||
enum result_t {
|
||||
result_cycles, // Requested number of cycles (or more) were executed
|
||||
result_sei, // I flag just set and IRQ time would generate IRQ now
|
||||
result_cli, // I flag just cleared but IRQ should occur *after* next instr
|
||||
result_badop // unimplemented/illegal instruction
|
||||
};
|
||||
result_t run(nes_time_t end_time);
|
||||
|
||||
result_t run( nes_time_t end_time );
|
||||
nes_time_t time() const { return clock_count; }
|
||||
|
||||
nes_time_t time() const { return clock_count; }
|
||||
inline void reduce_limit(int offset)
|
||||
{
|
||||
clock_limit -= offset;
|
||||
end_time_ -= offset;
|
||||
irq_time_ -= offset;
|
||||
}
|
||||
|
||||
inline void reduce_limit( int offset )
|
||||
{
|
||||
clock_limit -= offset;
|
||||
end_time_ -= offset;
|
||||
irq_time_ -= offset;
|
||||
}
|
||||
inline void set_end_time_(nes_time_t t)
|
||||
{
|
||||
end_time_ = t;
|
||||
update_clock_limit();
|
||||
}
|
||||
|
||||
inline void set_end_time_( nes_time_t t )
|
||||
{
|
||||
end_time_ = t;
|
||||
update_clock_limit();
|
||||
}
|
||||
inline void set_irq_time_(nes_time_t t)
|
||||
{
|
||||
irq_time_ = t;
|
||||
update_clock_limit();
|
||||
}
|
||||
|
||||
inline void set_irq_time_( nes_time_t t )
|
||||
{
|
||||
irq_time_ = t;
|
||||
update_clock_limit();
|
||||
}
|
||||
unsigned long error_count() const { return error_count_; }
|
||||
|
||||
unsigned long error_count() const { return error_count_; }
|
||||
// If PC exceeds 0xFFFF and encounters page_wrap_opcode, it will be silently wrapped.
|
||||
enum
|
||||
{
|
||||
page_wrap_opcode = 0xF2
|
||||
};
|
||||
|
||||
// If PC exceeds 0xFFFF and encounters page_wrap_opcode, it will be silently wrapped.
|
||||
enum { page_wrap_opcode = 0xF2 };
|
||||
// One of the many opcodes that are undefined and stop CPU emulation.
|
||||
enum
|
||||
{
|
||||
bad_opcode = 0xD2
|
||||
};
|
||||
|
||||
// One of the many opcodes that are undefined and stop CPU emulation.
|
||||
enum { bad_opcode = 0xD2 };
|
||||
uint8_t const *code_map[page_count + 1];
|
||||
nes_time_t clock_limit;
|
||||
nes_time_t clock_count;
|
||||
nes_time_t irq_time_;
|
||||
nes_time_t end_time_;
|
||||
unsigned long error_count_;
|
||||
|
||||
uint8_t const* code_map [page_count + 1];
|
||||
nes_time_t clock_limit;
|
||||
nes_time_t clock_count;
|
||||
nes_time_t irq_time_;
|
||||
nes_time_t end_time_;
|
||||
unsigned long error_count_;
|
||||
enum
|
||||
{
|
||||
irq_inhibit = 0x04
|
||||
};
|
||||
|
||||
enum { irq_inhibit = 0x04 };
|
||||
inline void update_clock_limit()
|
||||
{
|
||||
nes_time_t t = end_time_;
|
||||
if (t > irq_time_ && !(r.status & irq_inhibit))
|
||||
t = irq_time_;
|
||||
clock_limit = t;
|
||||
}
|
||||
|
||||
inline void update_clock_limit()
|
||||
{
|
||||
nes_time_t t = end_time_;
|
||||
if ( t > irq_time_ && !(r.status & irq_inhibit) )
|
||||
t = irq_time_;
|
||||
clock_limit = t;
|
||||
}
|
||||
|
||||
registers_t r;
|
||||
bool isCorrectExecution = true;
|
||||
|
||||
// low_mem is a full page size so it can be mapped with code_map
|
||||
uint8_t low_mem [page_size > 0x800 ? page_size : 0x800];
|
||||
|
||||
inline uint8_t* get_code( nes_addr_t addr )
|
||||
{
|
||||
return (uint8_t*) code_map [addr >> page_bits] + addr;
|
||||
}
|
||||
registers_t r;
|
||||
bool isCorrectExecution = true;
|
||||
|
||||
// low_mem is a full page size so it can be mapped with code_map
|
||||
uint8_t low_mem[page_size > 0x800 ? page_size : 0x800];
|
||||
|
||||
inline uint8_t *get_code(nes_addr_t addr)
|
||||
{
|
||||
return (uint8_t *)code_map[addr >> page_bits] + addr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -4,259 +4,280 @@
|
|||
|
||||
// Nes_Emu 0.7.0
|
||||
|
||||
#include "apu/Multi_Buffer.hpp"
|
||||
#include "Nes_Cart.hpp"
|
||||
#include "Nes_Core.hpp"
|
||||
#include "apu/Multi_Buffer.hpp"
|
||||
|
||||
class Nes_State;
|
||||
|
||||
class Nes_Emu {
|
||||
public:
|
||||
Nes_Emu();
|
||||
virtual ~Nes_Emu();
|
||||
class Nes_Emu
|
||||
{
|
||||
public:
|
||||
Nes_Emu();
|
||||
virtual ~Nes_Emu();
|
||||
|
||||
// Basic setup
|
||||
// Basic setup
|
||||
|
||||
// Load iNES file into emulator and clear recording
|
||||
void load_ines( const uint8_t* buffer );
|
||||
// Load iNES file into emulator and clear recording
|
||||
void load_ines(const uint8_t *buffer);
|
||||
|
||||
// Set sample rate for sound generation
|
||||
const char * set_sample_rate( long );
|
||||
// Set sample rate for sound generation
|
||||
const char *set_sample_rate(long);
|
||||
|
||||
// Size and depth of graphics buffer required for rendering. Note that this
|
||||
// is larger than the actual image, with a temporary area around the edge
|
||||
// that gets filled with junk.
|
||||
static const uint16_t buffer_width = Nes_Ppu::buffer_width;
|
||||
uint16_t buffer_height() const { return buffer_height_; }
|
||||
static const uint8_t bits_per_pixel = 8;
|
||||
// Size and depth of graphics buffer required for rendering. Note that this
|
||||
// is larger than the actual image, with a temporary area around the edge
|
||||
// that gets filled with junk.
|
||||
static const uint16_t buffer_width = Nes_Ppu::buffer_width;
|
||||
uint16_t buffer_height() const { return buffer_height_; }
|
||||
static const uint8_t bits_per_pixel = 8;
|
||||
|
||||
// Set graphics buffer to render pixels to. Pixels points to top-left pixel and
|
||||
// row_bytes is the number of bytes to get to the next line (positive or negative).
|
||||
void set_pixels( void* pixels, long row_bytes );
|
||||
// Set graphics buffer to render pixels to. Pixels points to top-left pixel and
|
||||
// row_bytes is the number of bytes to get to the next line (positive or negative).
|
||||
void set_pixels(void *pixels, long row_bytes);
|
||||
|
||||
// Size of image generated in graphics buffer
|
||||
static const uint16_t image_width = 256;
|
||||
static const uint16_t image_height = 240;
|
||||
// Size of image generated in graphics buffer
|
||||
static const uint16_t image_width = 256;
|
||||
static const uint16_t image_height = 240;
|
||||
|
||||
const uint8_t* getHostPixels () const { return emu.ppu.host_pixels; }
|
||||
const uint8_t *getHostPixels() const { return emu.ppu.host_pixels; }
|
||||
|
||||
size_t getLiteStateSize() const { return emu.getLiteStateSize(); }
|
||||
size_t getStateSize() const { return emu.getStateSize(); }
|
||||
size_t getLiteStateSize() const { return emu.getLiteStateSize(); }
|
||||
size_t getStateSize() const { return emu.getStateSize(); }
|
||||
|
||||
// Basic emulation
|
||||
// Basic emulation
|
||||
|
||||
// Emulate one video frame using joypad1 and joypad2 as input. Afterwards, image
|
||||
// and sound are available for output using the accessors below.
|
||||
virtual const char * emulate_frame( int joypad1, int joypad2 = 0 );
|
||||
// Emulate one video frame using joypad1 and joypad2 as input. Afterwards, image
|
||||
// and sound are available for output using the accessors below.
|
||||
virtual const char *emulate_frame(int joypad1, int joypad2 = 0);
|
||||
|
||||
// Emulate one video frame using joypad1 and joypad2 as input, but skips drawing.
|
||||
// Afterwards, audio is available for output using the accessors below.
|
||||
virtual const char * emulate_skip_frame( int joypad1, int joypad2 = 0 );
|
||||
// Emulate one video frame using joypad1 and joypad2 as input, but skips drawing.
|
||||
// Afterwards, audio is available for output using the accessors below.
|
||||
virtual const char *emulate_skip_frame(int joypad1, int joypad2 = 0);
|
||||
|
||||
// Maximum size of palette that can be generated
|
||||
static const uint16_t max_palette_size = 256;
|
||||
// Maximum size of palette that can be generated
|
||||
static const uint16_t max_palette_size = 256;
|
||||
|
||||
// Result of current frame
|
||||
struct frame_t
|
||||
{
|
||||
static const uint8_t left = 8;
|
||||
// Result of current frame
|
||||
struct frame_t
|
||||
{
|
||||
static const uint8_t left = 8;
|
||||
|
||||
int joypad_read_count; // number of times joypads were strobed (read)
|
||||
int burst_phase; // NTSC burst phase for frame (0, 1, or 2)
|
||||
int joypad_read_count; // number of times joypads were strobed (read)
|
||||
int burst_phase; // NTSC burst phase for frame (0, 1, or 2)
|
||||
|
||||
int sample_count; // number of samples (always a multiple of chan_count)
|
||||
int chan_count; // 1: mono, 2: stereo
|
||||
int sample_count; // number of samples (always a multiple of chan_count)
|
||||
int chan_count; // 1: mono, 2: stereo
|
||||
|
||||
int top; // top-left position of image in graphics buffer
|
||||
unsigned char* pixels; // pointer to top-left pixel of image
|
||||
long pitch; // number of bytes to get to next row of image
|
||||
int top; // top-left position of image in graphics buffer
|
||||
unsigned char *pixels; // pointer to top-left pixel of image
|
||||
long pitch; // number of bytes to get to next row of image
|
||||
|
||||
int palette_begin; // first host palette entry, as set by set_palette_range()
|
||||
int palette_size; // number of entries used for current frame
|
||||
short palette [max_palette_size]; // [palette_begin to palette_begin+palette_size-1]
|
||||
};
|
||||
frame_t const& frame() const { return *frame_; }
|
||||
int palette_begin; // first host palette entry, as set by set_palette_range()
|
||||
int palette_size; // number of entries used for current frame
|
||||
short palette[max_palette_size]; // [palette_begin to palette_begin+palette_size-1]
|
||||
};
|
||||
frame_t const &frame() const { return *frame_; }
|
||||
|
||||
// Read samples for the current frame. Returns number of samples read into buffer.
|
||||
// Currently all samples must be read in one call.
|
||||
virtual long read_samples( short* out, long max_samples );
|
||||
// Read samples for the current frame. Returns number of samples read into buffer.
|
||||
// Currently all samples must be read in one call.
|
||||
virtual long read_samples(short *out, long max_samples);
|
||||
|
||||
// Additional features
|
||||
// Additional features
|
||||
|
||||
// Use already-loaded cartridge. Retains pointer, so it must be kept around until
|
||||
// closed. A cartridge can be shared among multiple emulators. After opening,
|
||||
// cartridge's CHR data shouldn't be modified since a copy is cached internally.
|
||||
void set_cart( Nes_Cart const* );
|
||||
// Use already-loaded cartridge. Retains pointer, so it must be kept around until
|
||||
// closed. A cartridge can be shared among multiple emulators. After opening,
|
||||
// cartridge's CHR data shouldn't be modified since a copy is cached internally.
|
||||
void set_cart(Nes_Cart const *);
|
||||
|
||||
// Pointer to current cartridge, or NULL if none is loaded
|
||||
Nes_Cart const* cart() const { return emu.cart; }
|
||||
// Pointer to current cartridge, or NULL if none is loaded
|
||||
Nes_Cart const *cart() const { return emu.cart; }
|
||||
|
||||
// Emulate powering NES off and then back on. If full_reset is false, emulates
|
||||
// pressing the reset button only, which doesn't affect memory, otherwise
|
||||
// emulates powering system off then on.
|
||||
virtual void reset( bool full_reset = true, bool erase_battery_ram = false );
|
||||
// Emulate powering NES off and then back on. If full_reset is false, emulates
|
||||
// pressing the reset button only, which doesn't affect memory, otherwise
|
||||
// emulates powering system off then on.
|
||||
virtual void reset(bool full_reset = true, bool erase_battery_ram = false);
|
||||
|
||||
// Number of undefined CPU instructions encountered. Cleared after reset() and
|
||||
// load_state(). A non-zero value indicates that cartridge is probably
|
||||
// incompatible.
|
||||
unsigned long error_count() const { return emu.error_count; }
|
||||
// Number of undefined CPU instructions encountered. Cleared after reset() and
|
||||
// load_state(). A non-zero value indicates that cartridge is probably
|
||||
// incompatible.
|
||||
unsigned long error_count() const { return emu.error_count; }
|
||||
|
||||
// Sound
|
||||
// Sound
|
||||
|
||||
// Set sample rate and use a custom sound buffer instead of the default
|
||||
// mono buffer, i.e. Nes_Buffer, Effects_Buffer, etc..
|
||||
const char * set_sample_rate( long rate, Multi_Buffer* );
|
||||
// Set sample rate and use a custom sound buffer instead of the default
|
||||
// mono buffer, i.e. Nes_Buffer, Effects_Buffer, etc..
|
||||
const char *set_sample_rate(long rate, Multi_Buffer *);
|
||||
|
||||
// Adjust effective frame rate by changing how many samples are generated each frame.
|
||||
// Allows fine tuning of frame rate to improve synchronization.
|
||||
void set_frame_rate( double rate );
|
||||
// Adjust effective frame rate by changing how many samples are generated each frame.
|
||||
// Allows fine tuning of frame rate to improve synchronization.
|
||||
void set_frame_rate(double rate);
|
||||
|
||||
// Number of sound channels for current cartridge
|
||||
int channel_count() const { return channel_count_; }
|
||||
// Number of sound channels for current cartridge
|
||||
int channel_count() const { return channel_count_; }
|
||||
|
||||
// Frequency equalizer parameters
|
||||
struct equalizer_t {
|
||||
double treble; // 5.0 = extra-crisp, -200.0 = muffled
|
||||
long bass; // 0 = deep, 20000 = tinny
|
||||
};
|
||||
// Frequency equalizer parameters
|
||||
struct equalizer_t
|
||||
{
|
||||
double treble; // 5.0 = extra-crisp, -200.0 = muffled
|
||||
long bass; // 0 = deep, 20000 = tinny
|
||||
};
|
||||
|
||||
// Current frequency equalization
|
||||
equalizer_t const& equalizer() const { return equalizer_; }
|
||||
// Current frequency equalization
|
||||
equalizer_t const &equalizer() const { return equalizer_; }
|
||||
|
||||
// Change frequency equalization
|
||||
void set_equalizer( equalizer_t const& );
|
||||
// Change frequency equalization
|
||||
void set_equalizer(equalizer_t const &);
|
||||
|
||||
// Equalizer presets
|
||||
static equalizer_t const nes_eq; // NES
|
||||
static equalizer_t const famicom_eq; // Famicom
|
||||
static equalizer_t const tv_eq; // TV speaker
|
||||
static equalizer_t const flat_eq; // Flat EQ
|
||||
static equalizer_t const crisp_eq; // Crisp EQ (Treble boost)
|
||||
static equalizer_t const tinny_eq; // Tinny EQ (Like a handheld speaker)
|
||||
// Equalizer presets
|
||||
static equalizer_t const nes_eq; // NES
|
||||
static equalizer_t const famicom_eq; // Famicom
|
||||
static equalizer_t const tv_eq; // TV speaker
|
||||
static equalizer_t const flat_eq; // Flat EQ
|
||||
static equalizer_t const crisp_eq; // Crisp EQ (Treble boost)
|
||||
static equalizer_t const tinny_eq; // Tinny EQ (Like a handheld speaker)
|
||||
|
||||
// File save/load
|
||||
// File save/load
|
||||
|
||||
// Save emulator state
|
||||
size_t serializeState (uint8_t* buffer) const { return emu.serializeState(buffer); }
|
||||
size_t deserializeState (const uint8_t* buffer) { return emu.deserializeState(buffer); }
|
||||
// Save emulator state
|
||||
size_t serializeState(uint8_t *buffer) const { return emu.serializeState(buffer); }
|
||||
size_t deserializeState(const uint8_t *buffer) { return emu.deserializeState(buffer); }
|
||||
|
||||
// True if current cartridge claims it uses battery-backed memory
|
||||
bool has_battery_ram() const { return cart()->has_battery_ram(); }
|
||||
// True if current cartridge claims it uses battery-backed memory
|
||||
bool has_battery_ram() const { return cart()->has_battery_ram(); }
|
||||
|
||||
// Graphics
|
||||
// Graphics
|
||||
|
||||
// Number of frames generated per second
|
||||
enum { frame_rate = 60 };
|
||||
// Number of frames generated per second
|
||||
enum
|
||||
{
|
||||
frame_rate = 60
|
||||
};
|
||||
|
||||
// Size of fixed NES color table (including the 8 color emphasis modes)
|
||||
enum { color_table_size = 8 * 64 };
|
||||
// Size of fixed NES color table (including the 8 color emphasis modes)
|
||||
enum
|
||||
{
|
||||
color_table_size = 8 * 64
|
||||
};
|
||||
|
||||
// NES color lookup table based on standard NTSC TV decoder. Use nes_ntsc.h to
|
||||
// generate a palette with custom parameters.
|
||||
struct rgb_t { unsigned char red, green, blue; };
|
||||
static rgb_t const nes_colors [color_table_size];
|
||||
// NES color lookup table based on standard NTSC TV decoder. Use nes_ntsc.h to
|
||||
// generate a palette with custom parameters.
|
||||
struct rgb_t
|
||||
{
|
||||
unsigned char red, green, blue;
|
||||
};
|
||||
static rgb_t const nes_colors[color_table_size];
|
||||
|
||||
// Hide/show/enhance sprites. Sprite mode does not affect emulation accuracy.
|
||||
enum sprite_mode_t {
|
||||
sprites_hidden = 0,
|
||||
sprites_visible = 8, // limit of 8 sprites per scanline as on NES (default)
|
||||
sprites_enhanced = 64 // unlimited sprites per scanline (no flickering)
|
||||
};
|
||||
void set_sprite_mode( sprite_mode_t n ) { emu.ppu.sprite_limit = n; }
|
||||
// Hide/show/enhance sprites. Sprite mode does not affect emulation accuracy.
|
||||
enum sprite_mode_t
|
||||
{
|
||||
sprites_hidden = 0,
|
||||
sprites_visible = 8, // limit of 8 sprites per scanline as on NES (default)
|
||||
sprites_enhanced = 64 // unlimited sprites per scanline (no flickering)
|
||||
};
|
||||
void set_sprite_mode(sprite_mode_t n) { emu.ppu.sprite_limit = n; }
|
||||
|
||||
// Set range of host palette entries to use in graphics buffer; default uses
|
||||
// all of them. Begin will be rounded up to next multiple of palette_alignment.
|
||||
// Use frame().palette_begin to find the adjusted beginning entry used.
|
||||
enum { palette_alignment = 64 };
|
||||
void set_palette_range( int begin, int end = 256 );
|
||||
// Set range of host palette entries to use in graphics buffer; default uses
|
||||
// all of them. Begin will be rounded up to next multiple of palette_alignment.
|
||||
// Use frame().palette_begin to find the adjusted beginning entry used.
|
||||
enum
|
||||
{
|
||||
palette_alignment = 64
|
||||
};
|
||||
void set_palette_range(int begin, int end = 256);
|
||||
|
||||
// Access to emulated memory, for viewer/cheater/debugger
|
||||
// Access to emulated memory, for viewer/cheater/debugger
|
||||
|
||||
// CHR
|
||||
uint8_t const* chr_mem();
|
||||
long chr_size() const;
|
||||
void write_chr( void const*, long count, long offset );
|
||||
// CHR
|
||||
uint8_t const *chr_mem();
|
||||
long chr_size() const;
|
||||
void write_chr(void const *, long count, long offset);
|
||||
|
||||
// Nametable
|
||||
uint8_t* nametable_mem() { return emu.ppu.impl->nt_ram; }
|
||||
long nametable_size() const { return 0x1000; }
|
||||
// Nametable
|
||||
uint8_t *nametable_mem() { return emu.ppu.impl->nt_ram; }
|
||||
long nametable_size() const { return 0x1000; }
|
||||
|
||||
// Built-in 2K memory
|
||||
enum { low_mem_size = 0x800 };
|
||||
uint8_t* low_mem() { return emu.low_mem; }
|
||||
// Built-in 2K memory
|
||||
enum
|
||||
{
|
||||
low_mem_size = 0x800
|
||||
};
|
||||
uint8_t *low_mem() { return emu.low_mem; }
|
||||
|
||||
// Optional 8K memory
|
||||
enum { high_mem_size = 0x2000 };
|
||||
uint8_t* high_mem() { return emu.impl->sram; }
|
||||
// Optional 8K memory
|
||||
enum
|
||||
{
|
||||
high_mem_size = 0x2000
|
||||
};
|
||||
uint8_t *high_mem() { return emu.impl->sram; }
|
||||
|
||||
// Sprite memory
|
||||
uint8_t* spr_mem() { return emu.ppu.getSpriteRAM(); }
|
||||
uint16_t spr_mem_size() { return emu.ppu.getSpriteRAMSize(); }
|
||||
// Sprite memory
|
||||
uint8_t *spr_mem() { return emu.ppu.getSpriteRAM(); }
|
||||
uint16_t spr_mem_size() { return emu.ppu.getSpriteRAMSize(); }
|
||||
|
||||
// End of public interface
|
||||
public:
|
||||
const char * set_sample_rate( long rate, class Nes_Buffer* );
|
||||
const char * set_sample_rate( long rate, class Nes_Effects_Buffer* );
|
||||
void irq_changed() { emu.irq_changed(); }
|
||||
private:
|
||||
// End of public interface
|
||||
public:
|
||||
const char *set_sample_rate(long rate, class Nes_Buffer *);
|
||||
const char *set_sample_rate(long rate, class Nes_Effects_Buffer *);
|
||||
void irq_changed() { emu.irq_changed(); }
|
||||
|
||||
frame_t* frame_;
|
||||
int buffer_height_;
|
||||
bool fade_sound_in;
|
||||
bool fade_sound_out;
|
||||
virtual const char * init_();
|
||||
private:
|
||||
frame_t *frame_;
|
||||
int buffer_height_;
|
||||
bool fade_sound_in;
|
||||
bool fade_sound_out;
|
||||
virtual const char *init_();
|
||||
|
||||
virtual void loading_state( Nes_State const& ) { }
|
||||
long timestamp() const { return emu.nes.frame_count; }
|
||||
void set_timestamp( long t ) { emu.nes.frame_count = t; }
|
||||
virtual void loading_state(Nes_State const &) {}
|
||||
long timestamp() const { return emu.nes.frame_count; }
|
||||
void set_timestamp(long t) { emu.nes.frame_count = t; }
|
||||
|
||||
private:
|
||||
// noncopyable
|
||||
Nes_Emu( const Nes_Emu& );
|
||||
Nes_Emu& operator = ( const Nes_Emu& );
|
||||
private:
|
||||
// noncopyable
|
||||
Nes_Emu(const Nes_Emu &);
|
||||
Nes_Emu &operator=(const Nes_Emu &);
|
||||
|
||||
// sound
|
||||
Multi_Buffer* default_sound_buf;
|
||||
Multi_Buffer* sound_buf;
|
||||
unsigned sound_buf_changed_count;
|
||||
Silent_Buffer silent_buffer;
|
||||
equalizer_t equalizer_;
|
||||
int channel_count_;
|
||||
bool sound_enabled;
|
||||
void enable_sound( bool );
|
||||
void clear_sound_buf();
|
||||
void fade_samples( blip_sample_t*, int size, int step );
|
||||
// sound
|
||||
Multi_Buffer *default_sound_buf;
|
||||
Multi_Buffer *sound_buf;
|
||||
unsigned sound_buf_changed_count;
|
||||
Silent_Buffer silent_buffer;
|
||||
equalizer_t equalizer_;
|
||||
int channel_count_;
|
||||
bool sound_enabled;
|
||||
void enable_sound(bool);
|
||||
void clear_sound_buf();
|
||||
void fade_samples(blip_sample_t *, int size, int step);
|
||||
|
||||
char* host_pixels;
|
||||
int host_palette_size;
|
||||
frame_t single_frame;
|
||||
Nes_Cart private_cart;
|
||||
Nes_Core emu; // large; keep at end
|
||||
char *host_pixels;
|
||||
int host_palette_size;
|
||||
frame_t single_frame;
|
||||
Nes_Cart private_cart;
|
||||
Nes_Core emu; // large; keep at end
|
||||
|
||||
bool init_called;
|
||||
const char * auto_init();
|
||||
bool init_called;
|
||||
const char *auto_init();
|
||||
|
||||
bool extra_fade_sound_in;
|
||||
bool extra_fade_sound_out;
|
||||
unsigned extra_sound_buf_changed_count;
|
||||
public:
|
||||
void SaveAudioBufferState();
|
||||
void RestoreAudioBufferState();
|
||||
bool extra_fade_sound_in;
|
||||
bool extra_fade_sound_out;
|
||||
unsigned extra_sound_buf_changed_count;
|
||||
|
||||
public:
|
||||
void SaveAudioBufferState();
|
||||
void RestoreAudioBufferState();
|
||||
};
|
||||
|
||||
inline void Nes_Emu::set_pixels( void* p, long n )
|
||||
inline void Nes_Emu::set_pixels(void *p, long n)
|
||||
{
|
||||
host_pixels = (char*) p + n;
|
||||
emu.ppu.host_row_bytes = n;
|
||||
host_pixels = (char *)p + n;
|
||||
emu.ppu.host_row_bytes = n;
|
||||
}
|
||||
|
||||
inline uint8_t const* Nes_Emu::chr_mem()
|
||||
inline uint8_t const *Nes_Emu::chr_mem()
|
||||
{
|
||||
return cart()->chr_size() ? (uint8_t*) cart()->chr() : emu.ppu.impl->chr_ram;
|
||||
return cart()->chr_size() ? (uint8_t *)cart()->chr() : emu.ppu.impl->chr_ram;
|
||||
}
|
||||
|
||||
inline long Nes_Emu::chr_size() const
|
||||
{
|
||||
return cart()->chr_size() ? cart()->chr_size() : emu.ppu.chr_addr_size;
|
||||
return cart()->chr_size() ? cart()->chr_size() : emu.ppu.chr_addr_size;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
|
||||
// Blip_Buffer 0.4.0. http://www.slack.net/~ant/
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "Blip_Buffer.hpp"
|
||||
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
|
@ -23,397 +22,396 @@ int const buffer_extra = blip_widest_impulse_ + 2;
|
|||
|
||||
Blip_Buffer::Blip_Buffer()
|
||||
{
|
||||
factor_ = LONG_MAX;
|
||||
offset_ = 0;
|
||||
buffer_ = 0;
|
||||
buffer_size_ = 0;
|
||||
sample_rate_ = 0;
|
||||
reader_accum = 0;
|
||||
bass_shift = 0;
|
||||
clock_rate_ = 0;
|
||||
bass_freq_ = 16;
|
||||
length_ = 0;
|
||||
factor_ = LONG_MAX;
|
||||
offset_ = 0;
|
||||
buffer_ = 0;
|
||||
buffer_size_ = 0;
|
||||
sample_rate_ = 0;
|
||||
reader_accum = 0;
|
||||
bass_shift = 0;
|
||||
clock_rate_ = 0;
|
||||
bass_freq_ = 16;
|
||||
length_ = 0;
|
||||
|
||||
extra_length = length_;
|
||||
extra_offset = offset_;
|
||||
extra_reader_accum = reader_accum;
|
||||
memset(extra_buffer, 0, sizeof(extra_buffer));
|
||||
extra_length = length_;
|
||||
extra_offset = offset_;
|
||||
extra_reader_accum = reader_accum;
|
||||
memset(extra_buffer, 0, sizeof(extra_buffer));
|
||||
}
|
||||
|
||||
Blip_Buffer::~Blip_Buffer()
|
||||
{
|
||||
if ( buffer_ )
|
||||
free( buffer_ );
|
||||
if (buffer_)
|
||||
free(buffer_);
|
||||
}
|
||||
|
||||
void Blip_Buffer::clear( int entire_buffer )
|
||||
void Blip_Buffer::clear(int entire_buffer)
|
||||
{
|
||||
offset_ = 0;
|
||||
reader_accum = 0;
|
||||
if ( buffer_ )
|
||||
{
|
||||
long count = (entire_buffer ? buffer_size_ : samples_avail());
|
||||
memset( buffer_, 0, (count + buffer_extra) * sizeof (buf_t_) );
|
||||
}
|
||||
offset_ = 0;
|
||||
reader_accum = 0;
|
||||
if (buffer_)
|
||||
{
|
||||
long count = (entire_buffer ? buffer_size_ : samples_avail());
|
||||
memset(buffer_, 0, (count + buffer_extra) * sizeof(buf_t_));
|
||||
}
|
||||
}
|
||||
|
||||
const char *Blip_Buffer::set_sample_rate( long new_rate, int msec )
|
||||
const char *Blip_Buffer::set_sample_rate(long new_rate, int msec)
|
||||
{
|
||||
// start with maximum length that resampled time can represent
|
||||
long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - buffer_extra - 64;
|
||||
if ( msec != blip_max_length )
|
||||
{
|
||||
long s = (new_rate * (msec + 1) + 999) / 1000;
|
||||
if ( s < new_size )
|
||||
new_size = s;
|
||||
}
|
||||
// start with maximum length that resampled time can represent
|
||||
long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - buffer_extra - 64;
|
||||
if (msec != blip_max_length)
|
||||
{
|
||||
long s = (new_rate * (msec + 1) + 999) / 1000;
|
||||
if (s < new_size)
|
||||
new_size = s;
|
||||
}
|
||||
|
||||
if ( buffer_size_ != new_size )
|
||||
{
|
||||
void* p = realloc( buffer_, (new_size + buffer_extra) * sizeof *buffer_ );
|
||||
if ( !p )
|
||||
return "Out of memory";
|
||||
buffer_ = (buf_t_*) p;
|
||||
}
|
||||
if (buffer_size_ != new_size)
|
||||
{
|
||||
void *p = realloc(buffer_, (new_size + buffer_extra) * sizeof *buffer_);
|
||||
if (!p)
|
||||
return "Out of memory";
|
||||
buffer_ = (buf_t_ *)p;
|
||||
}
|
||||
|
||||
buffer_size_ = new_size;
|
||||
// update things based on the sample rate
|
||||
sample_rate_ = new_rate;
|
||||
length_ = new_size * 1000 / new_rate - 1;
|
||||
if ( clock_rate_ )
|
||||
clock_rate( clock_rate_ );
|
||||
bass_freq( bass_freq_ );
|
||||
buffer_size_ = new_size;
|
||||
// update things based on the sample rate
|
||||
sample_rate_ = new_rate;
|
||||
length_ = new_size * 1000 / new_rate - 1;
|
||||
if (clock_rate_)
|
||||
clock_rate(clock_rate_);
|
||||
bass_freq(bass_freq_);
|
||||
|
||||
clear();
|
||||
clear();
|
||||
|
||||
return 0; // success
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
blip_resampled_time_t Blip_Buffer::clock_rate_factor( long clock_rate ) const
|
||||
blip_resampled_time_t Blip_Buffer::clock_rate_factor(long clock_rate) const
|
||||
{
|
||||
double ratio = (double) sample_rate_ / clock_rate;
|
||||
long factor = (long) floor( ratio * (1L << BLIP_BUFFER_ACCURACY) + 0.5 );
|
||||
return (blip_resampled_time_t) factor;
|
||||
double ratio = (double)sample_rate_ / clock_rate;
|
||||
long factor = (long)floor(ratio * (1L << BLIP_BUFFER_ACCURACY) + 0.5);
|
||||
return (blip_resampled_time_t)factor;
|
||||
}
|
||||
|
||||
void Blip_Buffer::bass_freq( int freq )
|
||||
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;
|
||||
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 )
|
||||
void Blip_Buffer::end_frame(blip_time_t t)
|
||||
{
|
||||
offset_ += t * factor_;
|
||||
offset_ += t * factor_;
|
||||
}
|
||||
|
||||
void Blip_Buffer::remove_silence( long count )
|
||||
void Blip_Buffer::remove_silence(long count)
|
||||
{
|
||||
offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
|
||||
offset_ -= (blip_resampled_time_t)count << BLIP_BUFFER_ACCURACY;
|
||||
}
|
||||
|
||||
long Blip_Buffer::count_samples( blip_time_t t ) const
|
||||
long Blip_Buffer::count_samples(blip_time_t t) const
|
||||
{
|
||||
unsigned long last_sample = resampled_time( t ) >> BLIP_BUFFER_ACCURACY;
|
||||
unsigned long first_sample = offset_ >> BLIP_BUFFER_ACCURACY;
|
||||
return (long) (last_sample - first_sample);
|
||||
unsigned long last_sample = resampled_time(t) >> BLIP_BUFFER_ACCURACY;
|
||||
unsigned long first_sample = offset_ >> BLIP_BUFFER_ACCURACY;
|
||||
return (long)(last_sample - first_sample);
|
||||
}
|
||||
|
||||
blip_time_t Blip_Buffer::count_clocks( long count ) const
|
||||
blip_time_t Blip_Buffer::count_clocks(long count) const
|
||||
{
|
||||
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_);
|
||||
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 )
|
||||
void Blip_Buffer::remove_samples(long count)
|
||||
{
|
||||
if ( count )
|
||||
{
|
||||
remove_silence( count );
|
||||
if (count)
|
||||
{
|
||||
remove_silence(count);
|
||||
|
||||
// copy remaining samples to beginning and clear old samples
|
||||
long remain = samples_avail() + buffer_extra;
|
||||
memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ );
|
||||
memset( buffer_ + remain, 0, count * sizeof *buffer_ );
|
||||
}
|
||||
// copy remaining samples to beginning and clear old samples
|
||||
long remain = samples_avail() + buffer_extra;
|
||||
memmove(buffer_, buffer_ + count, remain * sizeof *buffer_);
|
||||
memset(buffer_ + remain, 0, count * sizeof *buffer_);
|
||||
}
|
||||
}
|
||||
|
||||
// Blip_Synth_
|
||||
|
||||
Blip_Synth_::Blip_Synth_( short* p, int w ) :
|
||||
impulses( p ),
|
||||
width( w )
|
||||
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;
|
||||
volume_unit_ = 0.0;
|
||||
kernel_unit = 0;
|
||||
buf = 0;
|
||||
last_amp = 0;
|
||||
delta_factor = 0;
|
||||
}
|
||||
|
||||
// TODO: apparently this is defined elsewhere too
|
||||
#define pi my_pi
|
||||
static double const pi = 3.1415926535897932384626433832795029;
|
||||
|
||||
static void gen_sinc( float* out, int count, double oversample, double treble, double cutoff )
|
||||
static void gen_sinc(float *out, int count, double oversample, double treble, double cutoff)
|
||||
{
|
||||
if ( cutoff >= 0.999 )
|
||||
cutoff = 0.999;
|
||||
if (cutoff >= 0.999)
|
||||
cutoff = 0.999;
|
||||
|
||||
if ( treble < -300.0 )
|
||||
treble = -300.0;
|
||||
if ( treble > 5.0 )
|
||||
treble = 5.0;
|
||||
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 );
|
||||
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;
|
||||
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
|
||||
}
|
||||
out[i] = (float)((a * d + c * b) / (b * d)); // a / b + c / d
|
||||
}
|
||||
}
|
||||
|
||||
void blip_eq_t::generate( float* out, int count ) const
|
||||
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;
|
||||
// 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 );
|
||||
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.54 - 0.46 * cos( i * to_fraction );
|
||||
// apply (half of) hamming window
|
||||
double to_fraction = pi / (count - 1);
|
||||
for (int i = count; i--;)
|
||||
out[i] *= 0.54 - 0.46 * 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] += error;
|
||||
//printf( "error: %ld\n", error );
|
||||
}
|
||||
// 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] += 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] );
|
||||
// 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 )
|
||||
void Blip_Synth_::treble_eq(blip_eq_t const &eq)
|
||||
{
|
||||
float fimpulse [blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2];
|
||||
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 const half_size = blip_res / 2 * (width - 1);
|
||||
eq.generate(&fimpulse[blip_res], half_size);
|
||||
|
||||
int i;
|
||||
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];
|
||||
// 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;
|
||||
// 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];
|
||||
// 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;
|
||||
// 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 impulses_size = this->impulses_size();
|
||||
for ( i = 0; i < impulses_size; i++ )
|
||||
{
|
||||
impulses [i] = (short) floor( (next - sum) * rescale + 0.5 );
|
||||
sum += fimpulse [i];
|
||||
next += fimpulse [i + blip_res];
|
||||
}
|
||||
adjust_impulse();
|
||||
// integrate, first difference, rescale, convert to int
|
||||
double sum = 0.0;
|
||||
double next = 0.0;
|
||||
int const impulses_size = this->impulses_size();
|
||||
for (i = 0; i < impulses_size; i++)
|
||||
{
|
||||
impulses[i] = (short)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 );
|
||||
}
|
||||
// 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 )
|
||||
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 );
|
||||
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;
|
||||
volume_unit_ = new_unit;
|
||||
double factor = new_unit * (1L << blip_sample_bits) / kernel_unit;
|
||||
|
||||
if ( factor > 0.0 )
|
||||
{
|
||||
int shift = 0;
|
||||
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 unit is really small, might need to attenuate kernel
|
||||
while (factor < 2.0)
|
||||
{
|
||||
shift++;
|
||||
factor *= 2.0;
|
||||
}
|
||||
|
||||
if ( shift )
|
||||
{
|
||||
kernel_unit >>= shift;
|
||||
if (shift)
|
||||
{
|
||||
kernel_unit >>= shift;
|
||||
|
||||
// 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) (((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 );
|
||||
}
|
||||
// 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)(((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 );
|
||||
}
|
||||
}
|
||||
|
||||
long Blip_Buffer::read_samples( blip_sample_t* out, long max_samples, int stereo )
|
||||
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;
|
||||
long count = samples_avail();
|
||||
if (count > max_samples)
|
||||
count = max_samples;
|
||||
|
||||
if ( count )
|
||||
{
|
||||
int const sample_shift = blip_sample_bits - 16;
|
||||
int const bass_shift = this->bass_shift;
|
||||
long accum = reader_accum;
|
||||
buf_t_* in = buffer_;
|
||||
if (count)
|
||||
{
|
||||
int const sample_shift = blip_sample_bits - 16;
|
||||
int const bass_shift = this->bass_shift;
|
||||
long accum = reader_accum;
|
||||
buf_t_ *in = buffer_;
|
||||
|
||||
if (out != NULL)
|
||||
{
|
||||
if ( !stereo )
|
||||
{
|
||||
for ( long n = count; n--; )
|
||||
{
|
||||
long s = accum >> sample_shift;
|
||||
accum -= accum >> bass_shift;
|
||||
accum += *in++;
|
||||
*out++ = (blip_sample_t) s;
|
||||
if (out != NULL)
|
||||
{
|
||||
if (!stereo)
|
||||
{
|
||||
for (long n = count; n--;)
|
||||
{
|
||||
long s = accum >> sample_shift;
|
||||
accum -= accum >> bass_shift;
|
||||
accum += *in++;
|
||||
*out++ = (blip_sample_t)s;
|
||||
|
||||
// clamp sample
|
||||
if ( (blip_sample_t) s != s )
|
||||
out [-1] = (blip_sample_t) (0x7FFF - (s >> 24));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( long n = count; n--; )
|
||||
{
|
||||
long s = accum >> sample_shift;
|
||||
accum -= accum >> bass_shift;
|
||||
accum += *in++;
|
||||
*out = (blip_sample_t) s;
|
||||
out += 2;
|
||||
// clamp sample
|
||||
if ((blip_sample_t)s != s)
|
||||
out[-1] = (blip_sample_t)(0x7FFF - (s >> 24));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (long n = count; n--;)
|
||||
{
|
||||
long s = accum >> sample_shift;
|
||||
accum -= accum >> bass_shift;
|
||||
accum += *in++;
|
||||
*out = (blip_sample_t)s;
|
||||
out += 2;
|
||||
|
||||
// clamp sample
|
||||
if ( (blip_sample_t) s != s )
|
||||
out [-2] = (blip_sample_t) (0x7FFF - (s >> 24));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//only run accumulator, do not output anything
|
||||
for (long n = count; n--; )
|
||||
{
|
||||
accum -= accum >> bass_shift;
|
||||
accum += *in++;
|
||||
}
|
||||
}
|
||||
// clamp sample
|
||||
if ((blip_sample_t)s != s)
|
||||
out[-2] = (blip_sample_t)(0x7FFF - (s >> 24));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// only run accumulator, do not output anything
|
||||
for (long n = count; n--;)
|
||||
{
|
||||
accum -= accum >> bass_shift;
|
||||
accum += *in++;
|
||||
}
|
||||
}
|
||||
|
||||
reader_accum = accum;
|
||||
remove_samples( count );
|
||||
}
|
||||
return count;
|
||||
reader_accum = accum;
|
||||
remove_samples(count);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void Blip_Buffer::mix_samples( blip_sample_t const* in, long count )
|
||||
void Blip_Buffer::mix_samples(blip_sample_t const *in, long count)
|
||||
{
|
||||
buf_t_* out = buffer_ + (offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2;
|
||||
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-- )
|
||||
{
|
||||
long s = (long) *in++ << sample_shift;
|
||||
*out += s - prev;
|
||||
prev = s;
|
||||
++out;
|
||||
}
|
||||
*out -= prev;
|
||||
int const sample_shift = blip_sample_bits - 16;
|
||||
int prev = 0;
|
||||
while (count--)
|
||||
{
|
||||
long s = (long)*in++ << sample_shift;
|
||||
*out += s - prev;
|
||||
prev = s;
|
||||
++out;
|
||||
}
|
||||
*out -= prev;
|
||||
}
|
||||
|
||||
void Blip_Buffer::SaveAudioBufferState()
|
||||
{
|
||||
extra_length = length_;
|
||||
extra_offset = offset_;
|
||||
extra_reader_accum = reader_accum;
|
||||
memcpy(extra_buffer, buffer_, sizeof(extra_buffer));
|
||||
extra_length = length_;
|
||||
extra_offset = offset_;
|
||||
extra_reader_accum = reader_accum;
|
||||
memcpy(extra_buffer, buffer_, sizeof(extra_buffer));
|
||||
}
|
||||
void Blip_Buffer::RestoreAudioBufferState()
|
||||
{
|
||||
length_ = extra_length;
|
||||
offset_ = extra_offset;
|
||||
reader_accum = extra_reader_accum;
|
||||
memcpy(buffer_, extra_buffer, sizeof(extra_buffer));
|
||||
length_ = extra_length;
|
||||
offset_ = extra_offset;
|
||||
reader_accum = extra_reader_accum;
|
||||
memcpy(buffer_, extra_buffer, sizeof(extra_buffer));
|
||||
}
|
||||
|
|
|
@ -11,345 +11,367 @@ typedef 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 };
|
||||
enum
|
||||
{
|
||||
blip_sample_max = 32767
|
||||
};
|
||||
|
||||
class Blip_Buffer {
|
||||
public:
|
||||
// Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults
|
||||
// to 1/4 second), then clear buffer. Returns NULL on success, otherwise if there
|
||||
// isn't enough memory, returns error without affecting current buffer setup.
|
||||
const char *set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 );
|
||||
class Blip_Buffer
|
||||
{
|
||||
public:
|
||||
// Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults
|
||||
// to 1/4 second), then clear buffer. Returns NULL on success, otherwise if there
|
||||
// isn't enough memory, returns error without affecting current buffer setup.
|
||||
const char *set_sample_rate(long samples_per_sec, int msec_length = 1000 / 4);
|
||||
|
||||
// Set number of source time units per second
|
||||
void clock_rate( long );
|
||||
// Set number of source time units per second
|
||||
void clock_rate(long);
|
||||
|
||||
// End current time frame of specified duration and make 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 );
|
||||
// End current time frame of specified duration and make 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);
|
||||
|
||||
// Read 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 );
|
||||
// Read 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 optional features
|
||||
// Additional optional features
|
||||
|
||||
// Current output sample rate
|
||||
long sample_rate() const;
|
||||
// Current output sample rate
|
||||
long sample_rate() const;
|
||||
|
||||
// Length of buffer, in milliseconds
|
||||
int length() const;
|
||||
// Length of buffer, in milliseconds
|
||||
int length() const;
|
||||
|
||||
// Number of source time units per second
|
||||
long clock_rate() const;
|
||||
// Number of source time units per second
|
||||
long clock_rate() const;
|
||||
|
||||
// Set frequency high-pass filter frequency, where higher values reduce bass more
|
||||
void bass_freq( int frequency );
|
||||
// Set frequency high-pass filter frequency, where higher values reduce bass more
|
||||
void bass_freq(int frequency);
|
||||
|
||||
// Number of samples delay from synthesis to samples read out
|
||||
int output_latency() const;
|
||||
// Number of samples delay from synthesis to samples read out
|
||||
int output_latency() const;
|
||||
|
||||
// Remove 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 );
|
||||
// Remove 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;
|
||||
// Number of samples available for reading with read_samples()
|
||||
long samples_avail() const;
|
||||
|
||||
// Remove 'count' samples from those waiting to be read
|
||||
void remove_samples( long count );
|
||||
// Remove 'count' samples from those waiting to be read
|
||||
void remove_samples(long count);
|
||||
|
||||
// Experimental features
|
||||
// Experimental features
|
||||
|
||||
// Number of raw samples that can be mixed within frame of specified duration.
|
||||
long count_samples( blip_time_t duration ) const;
|
||||
// Number of raw samples that can be mixed within frame of specified duration.
|
||||
long count_samples(blip_time_t duration) const;
|
||||
|
||||
// Mix 'count' samples from 'buf' into buffer.
|
||||
void mix_samples( blip_sample_t const* buf, long count );
|
||||
// Mix 'count' samples from 'buf' into buffer.
|
||||
void mix_samples(blip_sample_t const *buf, long count);
|
||||
|
||||
// Count 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;
|
||||
// Count 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;
|
||||
|
||||
// not documented yet
|
||||
typedef unsigned long blip_resampled_time_t;
|
||||
void remove_silence( long count );
|
||||
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();
|
||||
// not documented yet
|
||||
typedef unsigned long blip_resampled_time_t;
|
||||
void remove_silence(long count);
|
||||
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;
|
||||
|
||||
// Deprecated
|
||||
typedef blip_resampled_time_t resampled_time_t;
|
||||
const char *sample_rate( long r ) { return set_sample_rate( r ); }
|
||||
const char *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 long buf_t_;
|
||||
unsigned long factor_;
|
||||
blip_resampled_time_t offset_;
|
||||
buf_t_* buffer_;
|
||||
long buffer_size_;
|
||||
private:
|
||||
long reader_accum;
|
||||
int bass_shift;
|
||||
long sample_rate_;
|
||||
long clock_rate_;
|
||||
int bass_freq_;
|
||||
int length_;
|
||||
friend class Blip_Reader;
|
||||
public:
|
||||
Blip_Buffer();
|
||||
~Blip_Buffer();
|
||||
|
||||
private:
|
||||
//extra information necessary to load state to an exact sample
|
||||
buf_t_ extra_buffer[32];
|
||||
int extra_length;
|
||||
long extra_reader_accum;
|
||||
blip_resampled_time_t extra_offset;
|
||||
public:
|
||||
void SaveAudioBufferState();
|
||||
void RestoreAudioBufferState();
|
||||
// Deprecated
|
||||
typedef blip_resampled_time_t resampled_time_t;
|
||||
const char *sample_rate(long r) { return set_sample_rate(r); }
|
||||
const char *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 long buf_t_;
|
||||
unsigned long factor_;
|
||||
blip_resampled_time_t offset_;
|
||||
buf_t_ *buffer_;
|
||||
long buffer_size_;
|
||||
|
||||
private:
|
||||
long reader_accum;
|
||||
int bass_shift;
|
||||
long sample_rate_;
|
||||
long clock_rate_;
|
||||
int bass_freq_;
|
||||
int length_;
|
||||
friend class Blip_Reader;
|
||||
|
||||
private:
|
||||
// extra information necessary to load state to an exact sample
|
||||
buf_t_ extra_buffer[32];
|
||||
int extra_length;
|
||||
long extra_reader_accum;
|
||||
blip_resampled_time_t extra_offset;
|
||||
|
||||
public:
|
||||
void SaveAudioBufferState();
|
||||
void RestoreAudioBufferState();
|
||||
};
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "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
|
||||
#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
|
||||
#define BLIP_PHASE_BITS 6
|
||||
#define BLIP_PHASE_BITS 6
|
||||
#endif
|
||||
|
||||
// Internal
|
||||
typedef unsigned long blip_resampled_time_t;
|
||||
int const blip_widest_impulse_ = 16;
|
||||
int const blip_res = 1 << BLIP_PHASE_BITS;
|
||||
class blip_eq_t;
|
||||
// Internal
|
||||
typedef unsigned long blip_resampled_time_t;
|
||||
int const blip_widest_impulse_ = 16;
|
||||
int const blip_res = 1 << BLIP_PHASE_BITS;
|
||||
class blip_eq_t;
|
||||
|
||||
class Blip_Synth_ {
|
||||
double volume_unit_;
|
||||
short* const impulses;
|
||||
int const width;
|
||||
long kernel_unit;
|
||||
int impulses_size() const { return blip_res / 2 * width + 1; }
|
||||
void adjust_impulse();
|
||||
public:
|
||||
Blip_Buffer* buf;
|
||||
int last_amp;
|
||||
int delta_factor;
|
||||
class Blip_Synth_
|
||||
{
|
||||
double volume_unit_;
|
||||
short *const impulses;
|
||||
int const width;
|
||||
long kernel_unit;
|
||||
int impulses_size() const { return blip_res / 2 * width + 1; }
|
||||
void adjust_impulse();
|
||||
|
||||
Blip_Synth_( short* impulses, int width );
|
||||
void treble_eq( blip_eq_t const& );
|
||||
void volume_unit( double );
|
||||
};
|
||||
public:
|
||||
Blip_Buffer *buf;
|
||||
int last_amp;
|
||||
int delta_factor;
|
||||
|
||||
Blip_Synth_(short *impulses, int width);
|
||||
void treble_eq(blip_eq_t const &);
|
||||
void volume_unit(double);
|
||||
};
|
||||
|
||||
// Quality level. Start with blip_good_quality.
|
||||
const int blip_med_quality = 8;
|
||||
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:
|
||||
// Set overall volume of waveform
|
||||
void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); }
|
||||
template <int quality, int range>
|
||||
class Blip_Synth
|
||||
{
|
||||
public:
|
||||
// Set overall volume of waveform
|
||||
void volume(double v) { impl.volume_unit(v * (1.0 / (range < 0 ? -range : range))); }
|
||||
|
||||
// Configure low-pass filter (see notes.txt)
|
||||
void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); }
|
||||
// Configure low-pass filter (see notes.txt)
|
||||
void treble_eq(blip_eq_t const &eq) { impl.treble_eq(eq); }
|
||||
|
||||
// Get/set Blip_Buffer used for output
|
||||
Blip_Buffer* output() const { return impl.buf; }
|
||||
void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; }
|
||||
// Get/set Blip_Buffer used for output
|
||||
Blip_Buffer *output() const { return impl.buf; }
|
||||
void output(Blip_Buffer *b)
|
||||
{
|
||||
impl.buf = b;
|
||||
impl.last_amp = 0;
|
||||
}
|
||||
|
||||
// Update amplitude of waveform at given time. Using this requires a separate
|
||||
// Blip_Synth for each waveform.
|
||||
void update( blip_time_t time, int amplitude );
|
||||
// Update 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
|
||||
// Low-level interface
|
||||
|
||||
// Add 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 ); }
|
||||
// Add 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.
|
||||
void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const;
|
||||
// Works directly in terms of fractional output samples. Contact author for more.
|
||||
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 );
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
public:
|
||||
Blip_Synth() : impl( impulses, quality ) { }
|
||||
private:
|
||||
typedef short imp_t;
|
||||
imp_t impulses [blip_res * (quality / 2) + 1];
|
||||
Blip_Synth_ impl;
|
||||
public:
|
||||
Blip_Synth() : impl(impulses, quality) {}
|
||||
|
||||
private:
|
||||
typedef short imp_t;
|
||||
imp_t impulses[blip_res * (quality / 2) + 1];
|
||||
Blip_Synth_ impl;
|
||||
};
|
||||
|
||||
// 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 );
|
||||
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 notes.txt
|
||||
blip_eq_t( double treble, long rolloff_freq, long sample_rate, long cutoff_freq = 0 );
|
||||
// See notes.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_;
|
||||
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;
|
||||
|
||||
// Optimized inline sample reader for custom sample formats and mixing of Blip_Buffer samples
|
||||
class Blip_Reader {
|
||||
public:
|
||||
// Begin reading samples from buffer. Returns value to pass to next() (can
|
||||
// be ignored if default bass_freq is acceptable).
|
||||
int begin( Blip_Buffer& );
|
||||
class Blip_Reader
|
||||
{
|
||||
public:
|
||||
// Begin reading samples from buffer. Returns value to pass to next() (can
|
||||
// be ignored if default bass_freq is acceptable).
|
||||
int begin(Blip_Buffer &);
|
||||
|
||||
// Current sample
|
||||
long read() const { return accum >> (blip_sample_bits - 16); }
|
||||
// Current sample
|
||||
long read() const { return accum >> (blip_sample_bits - 16); }
|
||||
|
||||
// Current raw sample in full internal resolution
|
||||
long read_raw() const { return accum; }
|
||||
// Current raw sample in full internal resolution
|
||||
long read_raw() const { return accum; }
|
||||
|
||||
// Advance to next sample
|
||||
void next( int bass_shift = 9 ) { accum += *buf++ - (accum >> bass_shift); }
|
||||
// Advance to next sample
|
||||
void next(int bass_shift = 9) { accum += *buf++ - (accum >> bass_shift); }
|
||||
|
||||
// End reading samples from buffer. The number of samples read must now be removed
|
||||
// using Blip_Buffer::remove_samples().
|
||||
void end( Blip_Buffer& b ) { b.reader_accum = accum; }
|
||||
// End reading samples from buffer. The number of samples read must now be removed
|
||||
// using Blip_Buffer::remove_samples().
|
||||
void end(Blip_Buffer &b) { b.reader_accum = accum; }
|
||||
|
||||
private:
|
||||
const Blip_Buffer::buf_t_* buf;
|
||||
long accum;
|
||||
private:
|
||||
const Blip_Buffer::buf_t_ *buf;
|
||||
long accum;
|
||||
};
|
||||
|
||||
|
||||
// End of public interface
|
||||
|
||||
|
||||
// Compatibility with older version
|
||||
const long blip_unscaled = 65535;
|
||||
const int blip_low_quality = blip_med_quality;
|
||||
const int blip_low_quality = blip_med_quality;
|
||||
const int blip_best_quality = blip_high_quality;
|
||||
|
||||
#define BLIP_FWD( i ) { \
|
||||
long t0 = i0 * delta + buf [fwd + i]; \
|
||||
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_FWD(i) \
|
||||
{ \
|
||||
long t0 = i0 * delta + buf[fwd + i]; \
|
||||
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 ) { \
|
||||
long t0 = i0 * delta + buf [rev - r]; \
|
||||
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; }
|
||||
#define BLIP_REV(r) \
|
||||
{ \
|
||||
long t0 = i0 * delta + buf[rev - r]; \
|
||||
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; \
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
{
|
||||
// Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the
|
||||
// need for a longer buffer as set by set_sample_rate().
|
||||
delta *= impl.delta_factor;
|
||||
int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1));
|
||||
imp_t const* imp = impulses + blip_res - phase;
|
||||
long* buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY);
|
||||
long i0 = *imp;
|
||||
// Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the
|
||||
// need for a longer buffer as set by set_sample_rate().
|
||||
delta *= impl.delta_factor;
|
||||
int phase = (int)(time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1));
|
||||
imp_t const *imp = impulses + blip_res - phase;
|
||||
long *buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY);
|
||||
long i0 = *imp;
|
||||
|
||||
int const fwd = (blip_widest_impulse_ - quality) / 2;
|
||||
int const rev = fwd + quality - 2;
|
||||
int const fwd = (blip_widest_impulse_ - quality) / 2;
|
||||
int const rev = fwd + quality - 2;
|
||||
|
||||
BLIP_FWD( 0 )
|
||||
if ( quality > 8 ) BLIP_FWD( 2 )
|
||||
if ( quality > 12 ) BLIP_FWD( 4 )
|
||||
{
|
||||
int const mid = quality / 2 - 1;
|
||||
long t0 = i0 * delta + buf [fwd + mid - 1];
|
||||
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_FWD(0)
|
||||
if (quality > 8) BLIP_FWD(2)
|
||||
if (quality > 12) BLIP_FWD(4)
|
||||
{
|
||||
int const mid = quality / 2 - 1;
|
||||
long t0 = i0 * delta + buf[fwd + mid - 1];
|
||||
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)
|
||||
|
||||
long t0 = i0 * delta + buf [rev];
|
||||
long t1 = *imp * delta + buf [rev + 1];
|
||||
buf [rev] = t0;
|
||||
buf [rev + 1] = t1;
|
||||
long t0 = i0 * delta + buf[rev];
|
||||
long t1 = *imp * delta + buf[rev + 1];
|
||||
buf[rev] = t0;
|
||||
buf[rev + 1] = t1;
|
||||
}
|
||||
|
||||
#undef BLIP_FWD
|
||||
#undef BLIP_REV
|
||||
|
||||
template<int quality,int range>
|
||||
void Blip_Synth<quality,range>::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const
|
||||
template <int quality, int range>
|
||||
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 );
|
||||
offset_resampled(t * buf->factor_ + buf->offset_, delta, buf);
|
||||
}
|
||||
|
||||
template<int quality,int range>
|
||||
void Blip_Synth<quality,range>::update( blip_time_t t, int amp )
|
||||
template <int quality, int range>
|
||||
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 );
|
||||
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 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_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 )
|
||||
inline int Blip_Reader::begin(Blip_Buffer &blip_buf)
|
||||
{
|
||||
buf = blip_buf.buffer_;
|
||||
accum = blip_buf.reader_accum;
|
||||
return blip_buf.bass_shift;
|
||||
buf = blip_buf.buffer_;
|
||||
accum = blip_buf.reader_accum;
|
||||
return blip_buf.bass_shift;
|
||||
}
|
||||
|
||||
int const blip_max_length = 0;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
|
||||
|
||||
#include <cstring>
|
||||
#include "Effects_Buffer.hpp"
|
||||
#include <cstring>
|
||||
|
||||
/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
|
@ -16,489 +16,498 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
typedef long fixed_t;
|
||||
|
||||
#define TO_FIXED( f ) fixed_t ((f) * (1L << 15) + 0.5)
|
||||
#define FMUL( x, y ) (((x) * (y)) >> 15)
|
||||
#define TO_FIXED(f) fixed_t((f) * (1L << 15) + 0.5)
|
||||
#define FMUL(x, y) (((x) * (y)) >> 15)
|
||||
|
||||
const unsigned echo_size = 4096;
|
||||
const unsigned echo_mask = echo_size - 1;
|
||||
static_assert( (echo_size & echo_mask) == 0 ); // must be power of 2
|
||||
static_assert((echo_size & echo_mask) == 0); // must be power of 2
|
||||
|
||||
const unsigned reverb_size = 8192 * 2;
|
||||
const unsigned reverb_mask = reverb_size - 1;
|
||||
static_assert( (reverb_size & reverb_mask) == 0 ); // must be power of 2
|
||||
static_assert((reverb_size & reverb_mask) == 0); // must be power of 2
|
||||
|
||||
Effects_Buffer::config_t::config_t()
|
||||
{
|
||||
pan_1 = -0.15f;
|
||||
pan_2 = 0.15f;
|
||||
reverb_delay = 88.0f;
|
||||
reverb_level = 0.12f;
|
||||
echo_delay = 61.0f;
|
||||
echo_level = 0.10f;
|
||||
delay_variance = 18.0f;
|
||||
effects_enabled = false;
|
||||
pan_1 = -0.15f;
|
||||
pan_2 = 0.15f;
|
||||
reverb_delay = 88.0f;
|
||||
reverb_level = 0.12f;
|
||||
echo_delay = 61.0f;
|
||||
echo_level = 0.10f;
|
||||
delay_variance = 18.0f;
|
||||
effects_enabled = false;
|
||||
}
|
||||
|
||||
void Effects_Buffer::set_depth( double d )
|
||||
void Effects_Buffer::set_depth(double d)
|
||||
{
|
||||
float f = (float) d;
|
||||
config_t c;
|
||||
c.pan_1 = -0.6f * f;
|
||||
c.pan_2 = 0.6f * f;
|
||||
c.reverb_delay = 880 * 0.1f;
|
||||
c.echo_delay = 610 * 0.1f;
|
||||
if ( f > 0.5 )
|
||||
f = 0.5; // TODO: more linear reduction of extreme reverb/echo
|
||||
c.reverb_level = 0.5f * f;
|
||||
c.echo_level = 0.30f * f;
|
||||
c.delay_variance = 180 * 0.1f;
|
||||
c.effects_enabled = (d > 0.0f);
|
||||
config( c );
|
||||
float f = (float)d;
|
||||
config_t c;
|
||||
c.pan_1 = -0.6f * f;
|
||||
c.pan_2 = 0.6f * f;
|
||||
c.reverb_delay = 880 * 0.1f;
|
||||
c.echo_delay = 610 * 0.1f;
|
||||
if (f > 0.5)
|
||||
f = 0.5; // TODO: more linear reduction of extreme reverb/echo
|
||||
c.reverb_level = 0.5f * f;
|
||||
c.echo_level = 0.30f * f;
|
||||
c.delay_variance = 180 * 0.1f;
|
||||
c.effects_enabled = (d > 0.0f);
|
||||
config(c);
|
||||
}
|
||||
|
||||
Effects_Buffer::Effects_Buffer( bool center_only ) : Multi_Buffer( 2 )
|
||||
Effects_Buffer::Effects_Buffer(bool center_only) : Multi_Buffer(2)
|
||||
{
|
||||
buf_count = center_only ? max_buf_count - 4 : max_buf_count;
|
||||
buf_count = center_only ? max_buf_count - 4 : max_buf_count;
|
||||
|
||||
echo_buf = NULL;
|
||||
echo_pos = 0;
|
||||
echo_buf = NULL;
|
||||
echo_pos = 0;
|
||||
|
||||
reverb_buf = NULL;
|
||||
reverb_pos = 0;
|
||||
reverb_buf = NULL;
|
||||
reverb_pos = 0;
|
||||
|
||||
stereo_remain = 0;
|
||||
effect_remain = 0;
|
||||
effects_enabled = false;
|
||||
set_depth( 0 );
|
||||
stereo_remain = 0;
|
||||
effect_remain = 0;
|
||||
effects_enabled = false;
|
||||
set_depth(0);
|
||||
}
|
||||
|
||||
Effects_Buffer::~Effects_Buffer()
|
||||
{
|
||||
delete [] echo_buf;
|
||||
delete [] reverb_buf;
|
||||
delete[] echo_buf;
|
||||
delete[] reverb_buf;
|
||||
}
|
||||
|
||||
const char *Effects_Buffer::set_sample_rate( long rate, int msec )
|
||||
const char *Effects_Buffer::set_sample_rate(long rate, int msec)
|
||||
{
|
||||
if ( !echo_buf )
|
||||
{
|
||||
echo_buf = new blip_sample_t [echo_size];
|
||||
}
|
||||
if (!echo_buf)
|
||||
{
|
||||
echo_buf = new blip_sample_t[echo_size];
|
||||
}
|
||||
|
||||
if ( !reverb_buf )
|
||||
{
|
||||
reverb_buf = new blip_sample_t [reverb_size];
|
||||
}
|
||||
if (!reverb_buf)
|
||||
{
|
||||
reverb_buf = new blip_sample_t[reverb_size];
|
||||
}
|
||||
|
||||
for ( int i = 0; i < buf_count; i++ ) bufs [i].set_sample_rate( rate, msec );
|
||||
for (int i = 0; i < buf_count; i++) bufs[i].set_sample_rate(rate, msec);
|
||||
|
||||
config( config_ );
|
||||
clear();
|
||||
config(config_);
|
||||
clear();
|
||||
|
||||
return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() );
|
||||
return Multi_Buffer::set_sample_rate(bufs[0].sample_rate(), bufs[0].length());
|
||||
}
|
||||
|
||||
void Effects_Buffer::clock_rate( long rate )
|
||||
void Effects_Buffer::clock_rate(long rate)
|
||||
{
|
||||
for ( int i = 0; i < buf_count; i++ )
|
||||
bufs [i].clock_rate( rate );
|
||||
for (int i = 0; i < buf_count; i++)
|
||||
bufs[i].clock_rate(rate);
|
||||
}
|
||||
|
||||
void Effects_Buffer::bass_freq( int freq )
|
||||
void Effects_Buffer::bass_freq(int freq)
|
||||
{
|
||||
for ( int i = 0; i < buf_count; i++ )
|
||||
bufs [i].bass_freq( freq );
|
||||
for (int i = 0; i < buf_count; i++)
|
||||
bufs[i].bass_freq(freq);
|
||||
}
|
||||
|
||||
void Effects_Buffer::clear()
|
||||
{
|
||||
stereo_remain = 0;
|
||||
effect_remain = 0;
|
||||
if ( echo_buf )
|
||||
memset( echo_buf, 0, echo_size * sizeof *echo_buf );
|
||||
if ( reverb_buf )
|
||||
memset( reverb_buf, 0, reverb_size * sizeof *reverb_buf );
|
||||
for ( int i = 0; i < buf_count; i++ )
|
||||
bufs [i].clear();
|
||||
stereo_remain = 0;
|
||||
effect_remain = 0;
|
||||
if (echo_buf)
|
||||
memset(echo_buf, 0, echo_size * sizeof *echo_buf);
|
||||
if (reverb_buf)
|
||||
memset(reverb_buf, 0, reverb_size * sizeof *reverb_buf);
|
||||
for (int i = 0; i < buf_count; i++)
|
||||
bufs[i].clear();
|
||||
}
|
||||
|
||||
inline int pin_range( int n, int max, int min = 0 )
|
||||
inline int pin_range(int n, int max, int min = 0)
|
||||
{
|
||||
if ( n < min )
|
||||
return min;
|
||||
if ( n > max )
|
||||
return max;
|
||||
return n;
|
||||
if (n < min)
|
||||
return min;
|
||||
if (n > max)
|
||||
return max;
|
||||
return n;
|
||||
}
|
||||
|
||||
void Effects_Buffer::config( const config_t& cfg )
|
||||
void Effects_Buffer::config(const config_t &cfg)
|
||||
{
|
||||
channels_changed();
|
||||
channels_changed();
|
||||
|
||||
// clear echo and reverb buffers
|
||||
if ( !config_.effects_enabled && cfg.effects_enabled && echo_buf )
|
||||
{
|
||||
memset( echo_buf, 0, echo_size * sizeof (blip_sample_t) );
|
||||
memset( reverb_buf, 0, reverb_size * sizeof (blip_sample_t) );
|
||||
}
|
||||
// clear echo and reverb buffers
|
||||
if (!config_.effects_enabled && cfg.effects_enabled && echo_buf)
|
||||
{
|
||||
memset(echo_buf, 0, echo_size * sizeof(blip_sample_t));
|
||||
memset(reverb_buf, 0, reverb_size * sizeof(blip_sample_t));
|
||||
}
|
||||
|
||||
config_ = cfg;
|
||||
config_ = cfg;
|
||||
|
||||
if ( config_.effects_enabled )
|
||||
{
|
||||
// convert to internal format
|
||||
if (config_.effects_enabled)
|
||||
{
|
||||
// convert to internal format
|
||||
|
||||
chans.pan_1_levels [0] = TO_FIXED( 1 ) - TO_FIXED( config_.pan_1 );
|
||||
chans.pan_1_levels [1] = TO_FIXED( 2 ) - chans.pan_1_levels [0];
|
||||
chans.pan_1_levels[0] = TO_FIXED(1) - TO_FIXED(config_.pan_1);
|
||||
chans.pan_1_levels[1] = TO_FIXED(2) - chans.pan_1_levels[0];
|
||||
|
||||
chans.pan_2_levels [0] = TO_FIXED( 1 ) - TO_FIXED( config_.pan_2 );
|
||||
chans.pan_2_levels [1] = TO_FIXED( 2 ) - chans.pan_2_levels [0];
|
||||
chans.pan_2_levels[0] = TO_FIXED(1) - TO_FIXED(config_.pan_2);
|
||||
chans.pan_2_levels[1] = TO_FIXED(2) - chans.pan_2_levels[0];
|
||||
|
||||
chans.reverb_level = TO_FIXED( config_.reverb_level );
|
||||
chans.echo_level = TO_FIXED( config_.echo_level );
|
||||
chans.reverb_level = TO_FIXED(config_.reverb_level);
|
||||
chans.echo_level = TO_FIXED(config_.echo_level);
|
||||
|
||||
int delay_offset = int (1.0 / 2000 * config_.delay_variance * sample_rate());
|
||||
int delay_offset = int(1.0 / 2000 * config_.delay_variance * sample_rate());
|
||||
|
||||
int reverb_sample_delay = int (1.0 / 1000 * config_.reverb_delay * sample_rate());
|
||||
chans.reverb_delay_l = pin_range( reverb_size -
|
||||
(reverb_sample_delay - delay_offset) * 2, reverb_size - 2, 0 );
|
||||
chans.reverb_delay_r = pin_range( reverb_size + 1 -
|
||||
(reverb_sample_delay + delay_offset) * 2, reverb_size - 1, 1 );
|
||||
int reverb_sample_delay = int(1.0 / 1000 * config_.reverb_delay * sample_rate());
|
||||
chans.reverb_delay_l = pin_range(reverb_size -
|
||||
(reverb_sample_delay - delay_offset) * 2,
|
||||
reverb_size - 2,
|
||||
0);
|
||||
chans.reverb_delay_r = pin_range(reverb_size + 1 -
|
||||
(reverb_sample_delay + delay_offset) * 2,
|
||||
reverb_size - 1,
|
||||
1);
|
||||
|
||||
int echo_sample_delay = int (1.0 / 1000 * config_.echo_delay * sample_rate());
|
||||
chans.echo_delay_l = pin_range( echo_size - 1 - (echo_sample_delay - delay_offset),
|
||||
echo_size - 1 );
|
||||
chans.echo_delay_r = pin_range( echo_size - 1 - (echo_sample_delay + delay_offset),
|
||||
echo_size - 1 );
|
||||
int echo_sample_delay = int(1.0 / 1000 * config_.echo_delay * sample_rate());
|
||||
chans.echo_delay_l = pin_range(echo_size - 1 - (echo_sample_delay - delay_offset),
|
||||
echo_size - 1);
|
||||
chans.echo_delay_r = pin_range(echo_size - 1 - (echo_sample_delay + delay_offset),
|
||||
echo_size - 1);
|
||||
|
||||
// set up outputs
|
||||
for ( unsigned i = 0; i < chan_count; i++ )
|
||||
{
|
||||
channel_t& o = channels [i];
|
||||
if ( i < 2 )
|
||||
{
|
||||
o.center = &bufs [i];
|
||||
o.left = &bufs [3];
|
||||
o.right = &bufs [4];
|
||||
}
|
||||
else
|
||||
{
|
||||
o.center = &bufs [2];
|
||||
o.left = &bufs [5];
|
||||
o.right = &bufs [6];
|
||||
}
|
||||
}
|
||||
// set up outputs
|
||||
for (unsigned i = 0; i < chan_count; i++)
|
||||
{
|
||||
channel_t &o = channels[i];
|
||||
if (i < 2)
|
||||
{
|
||||
o.center = &bufs[i];
|
||||
o.left = &bufs[3];
|
||||
o.right = &bufs[4];
|
||||
}
|
||||
else
|
||||
{
|
||||
o.center = &bufs[2];
|
||||
o.left = &bufs[5];
|
||||
o.right = &bufs[6];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// set up outputs
|
||||
for (unsigned i = 0; i < chan_count; i++)
|
||||
{
|
||||
channel_t &o = channels[i];
|
||||
o.center = &bufs[0];
|
||||
o.left = &bufs[1];
|
||||
o.right = &bufs[2];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// set up outputs
|
||||
for ( unsigned i = 0; i < chan_count; i++ )
|
||||
{
|
||||
channel_t& o = channels [i];
|
||||
o.center = &bufs [0];
|
||||
o.left = &bufs [1];
|
||||
o.right = &bufs [2];
|
||||
}
|
||||
}
|
||||
|
||||
if ( buf_count < max_buf_count )
|
||||
{
|
||||
for ( unsigned i = 0; i < chan_count; i++ )
|
||||
{
|
||||
channel_t& o = channels [i];
|
||||
o.left = o.center;
|
||||
o.right = o.center;
|
||||
}
|
||||
}
|
||||
if (buf_count < max_buf_count)
|
||||
{
|
||||
for (unsigned i = 0; i < chan_count; i++)
|
||||
{
|
||||
channel_t &o = channels[i];
|
||||
o.left = o.center;
|
||||
o.right = o.center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Effects_Buffer::end_frame( blip_time_t clock_count, bool stereo )
|
||||
void Effects_Buffer::end_frame(blip_time_t clock_count, bool stereo)
|
||||
{
|
||||
for ( int i = 0; i < buf_count; i++ )
|
||||
bufs [i].end_frame( clock_count );
|
||||
for (int i = 0; i < buf_count; i++)
|
||||
bufs[i].end_frame(clock_count);
|
||||
|
||||
if ( stereo && buf_count == max_buf_count )
|
||||
stereo_remain = bufs [0].samples_avail() + bufs [0].output_latency();
|
||||
if (stereo && buf_count == max_buf_count)
|
||||
stereo_remain = bufs[0].samples_avail() + bufs[0].output_latency();
|
||||
|
||||
if ( effects_enabled || config_.effects_enabled )
|
||||
effect_remain = bufs [0].samples_avail() + bufs [0].output_latency();
|
||||
if (effects_enabled || config_.effects_enabled)
|
||||
effect_remain = bufs[0].samples_avail() + bufs[0].output_latency();
|
||||
|
||||
effects_enabled = config_.effects_enabled;
|
||||
effects_enabled = config_.effects_enabled;
|
||||
}
|
||||
|
||||
long Effects_Buffer::samples_avail() const
|
||||
{
|
||||
return bufs [0].samples_avail() * 2;
|
||||
return bufs[0].samples_avail() * 2;
|
||||
}
|
||||
|
||||
long Effects_Buffer::read_samples( blip_sample_t* out, long total_samples )
|
||||
long Effects_Buffer::read_samples(blip_sample_t *out, long total_samples)
|
||||
{
|
||||
long remain = bufs [0].samples_avail();
|
||||
if ( remain > (total_samples >> 1) )
|
||||
remain = (total_samples >> 1);
|
||||
total_samples = remain;
|
||||
while ( remain )
|
||||
{
|
||||
int active_bufs = buf_count;
|
||||
long count = remain;
|
||||
long remain = bufs[0].samples_avail();
|
||||
if (remain > (total_samples >> 1))
|
||||
remain = (total_samples >> 1);
|
||||
total_samples = remain;
|
||||
while (remain)
|
||||
{
|
||||
int active_bufs = buf_count;
|
||||
long count = remain;
|
||||
|
||||
// optimizing mixing to skip any channels which had nothing added
|
||||
if ( effect_remain )
|
||||
{
|
||||
if ( count > effect_remain )
|
||||
count = effect_remain;
|
||||
// optimizing mixing to skip any channels which had nothing added
|
||||
if (effect_remain)
|
||||
{
|
||||
if (count > effect_remain)
|
||||
count = effect_remain;
|
||||
|
||||
if ( stereo_remain )
|
||||
{
|
||||
mix_enhanced( out, count );
|
||||
}
|
||||
else
|
||||
{
|
||||
mix_mono_enhanced( out, count );
|
||||
active_bufs = 3;
|
||||
}
|
||||
}
|
||||
else if ( stereo_remain )
|
||||
{
|
||||
mix_stereo( out, count );
|
||||
active_bufs = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
mix_mono( out, count );
|
||||
active_bufs = 1;
|
||||
}
|
||||
if (stereo_remain)
|
||||
{
|
||||
mix_enhanced(out, count);
|
||||
}
|
||||
else
|
||||
{
|
||||
mix_mono_enhanced(out, count);
|
||||
active_bufs = 3;
|
||||
}
|
||||
}
|
||||
else if (stereo_remain)
|
||||
{
|
||||
mix_stereo(out, count);
|
||||
active_bufs = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
mix_mono(out, count);
|
||||
active_bufs = 1;
|
||||
}
|
||||
|
||||
out += count * 2;
|
||||
remain -= count;
|
||||
out += count * 2;
|
||||
remain -= count;
|
||||
|
||||
stereo_remain -= count;
|
||||
if ( stereo_remain < 0 )
|
||||
stereo_remain = 0;
|
||||
stereo_remain -= count;
|
||||
if (stereo_remain < 0)
|
||||
stereo_remain = 0;
|
||||
|
||||
effect_remain -= count;
|
||||
if ( effect_remain < 0 )
|
||||
effect_remain = 0;
|
||||
effect_remain -= count;
|
||||
if (effect_remain < 0)
|
||||
effect_remain = 0;
|
||||
|
||||
for ( int i = 0; i < buf_count; i++ )
|
||||
{
|
||||
if ( i < active_bufs )
|
||||
bufs [i].remove_samples( count );
|
||||
else
|
||||
bufs [i].remove_silence( count ); // keep time synchronized
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < buf_count; i++)
|
||||
{
|
||||
if (i < active_bufs)
|
||||
bufs[i].remove_samples(count);
|
||||
else
|
||||
bufs[i].remove_silence(count); // keep time synchronized
|
||||
}
|
||||
}
|
||||
|
||||
return total_samples * 2;
|
||||
return total_samples * 2;
|
||||
}
|
||||
|
||||
void Effects_Buffer::mix_mono( blip_sample_t* out, long count )
|
||||
void Effects_Buffer::mix_mono(blip_sample_t *out, long count)
|
||||
{
|
||||
Blip_Reader c;
|
||||
int shift = c.begin( bufs [0] );
|
||||
Blip_Reader c;
|
||||
int shift = c.begin(bufs[0]);
|
||||
|
||||
// unrolled loop
|
||||
for ( long n = count >> 1; n--; )
|
||||
{
|
||||
long cs0 = c.read();
|
||||
c.next( shift );
|
||||
// unrolled loop
|
||||
for (long n = count >> 1; n--;)
|
||||
{
|
||||
long cs0 = c.read();
|
||||
c.next(shift);
|
||||
|
||||
long cs1 = c.read();
|
||||
c.next( shift );
|
||||
long cs1 = c.read();
|
||||
c.next(shift);
|
||||
|
||||
if ( (int16_t) cs0 != cs0 )
|
||||
cs0 = 0x7FFF - (cs0 >> 24);
|
||||
((uint32_t*) out) [0] = ((uint16_t) cs0) | (cs0 << 16);
|
||||
if ((int16_t)cs0 != cs0)
|
||||
cs0 = 0x7FFF - (cs0 >> 24);
|
||||
((uint32_t *)out)[0] = ((uint16_t)cs0) | (cs0 << 16);
|
||||
|
||||
if ( (int16_t) cs1 != cs1 )
|
||||
cs1 = 0x7FFF - (cs1 >> 24);
|
||||
((uint32_t*) out) [1] = ((uint16_t) cs1) | (cs1 << 16);
|
||||
out += 4;
|
||||
}
|
||||
if ((int16_t)cs1 != cs1)
|
||||
cs1 = 0x7FFF - (cs1 >> 24);
|
||||
((uint32_t *)out)[1] = ((uint16_t)cs1) | (cs1 << 16);
|
||||
out += 4;
|
||||
}
|
||||
|
||||
if ( count & 1 )
|
||||
{
|
||||
int s = c.read();
|
||||
c.next( shift );
|
||||
out [0] = s;
|
||||
out [1] = s;
|
||||
if ( (int16_t) s != s )
|
||||
{
|
||||
s = 0x7FFF - (s >> 24);
|
||||
out [0] = s;
|
||||
out [1] = s;
|
||||
}
|
||||
}
|
||||
if (count & 1)
|
||||
{
|
||||
int s = c.read();
|
||||
c.next(shift);
|
||||
out[0] = s;
|
||||
out[1] = s;
|
||||
if ((int16_t)s != s)
|
||||
{
|
||||
s = 0x7FFF - (s >> 24);
|
||||
out[0] = s;
|
||||
out[1] = s;
|
||||
}
|
||||
}
|
||||
|
||||
c.end( bufs [0] );
|
||||
c.end(bufs[0]);
|
||||
}
|
||||
|
||||
void Effects_Buffer::mix_stereo( blip_sample_t* out, long count )
|
||||
void Effects_Buffer::mix_stereo(blip_sample_t *out, long count)
|
||||
{
|
||||
Blip_Reader l; l.begin( bufs [1] );
|
||||
Blip_Reader r; r.begin( bufs [2] );
|
||||
Blip_Reader c;
|
||||
int shift = c.begin( bufs [0] );
|
||||
Blip_Reader l;
|
||||
l.begin(bufs[1]);
|
||||
Blip_Reader r;
|
||||
r.begin(bufs[2]);
|
||||
Blip_Reader c;
|
||||
int shift = c.begin(bufs[0]);
|
||||
|
||||
while ( count-- )
|
||||
{
|
||||
int cs = c.read();
|
||||
c.next( shift );
|
||||
int left = cs + l.read();
|
||||
int right = cs + r.read();
|
||||
l.next( shift );
|
||||
r.next( shift );
|
||||
while (count--)
|
||||
{
|
||||
int cs = c.read();
|
||||
c.next(shift);
|
||||
int left = cs + l.read();
|
||||
int right = cs + r.read();
|
||||
l.next(shift);
|
||||
r.next(shift);
|
||||
|
||||
if ( (int16_t) left != left )
|
||||
left = 0x7FFF - (left >> 24);
|
||||
if ((int16_t)left != left)
|
||||
left = 0x7FFF - (left >> 24);
|
||||
|
||||
out [0] = left;
|
||||
out [1] = right;
|
||||
out[0] = left;
|
||||
out[1] = right;
|
||||
|
||||
out += 2;
|
||||
out += 2;
|
||||
|
||||
if ( (int16_t) right != right )
|
||||
out [-1] = 0x7FFF - (right >> 24);
|
||||
}
|
||||
if ((int16_t)right != right)
|
||||
out[-1] = 0x7FFF - (right >> 24);
|
||||
}
|
||||
|
||||
c.end( bufs [0] );
|
||||
r.end( bufs [2] );
|
||||
l.end( bufs [1] );
|
||||
c.end(bufs[0]);
|
||||
r.end(bufs[2]);
|
||||
l.end(bufs[1]);
|
||||
}
|
||||
|
||||
void Effects_Buffer::mix_mono_enhanced( blip_sample_t* out, long count )
|
||||
void Effects_Buffer::mix_mono_enhanced(blip_sample_t *out, long count)
|
||||
{
|
||||
Blip_Reader sq1; sq1.begin( bufs [0] );
|
||||
Blip_Reader sq2; sq2.begin( bufs [1] );
|
||||
Blip_Reader center;
|
||||
int shift = center.begin( bufs [2] );
|
||||
Blip_Reader sq1;
|
||||
sq1.begin(bufs[0]);
|
||||
Blip_Reader sq2;
|
||||
sq2.begin(bufs[1]);
|
||||
Blip_Reader center;
|
||||
int shift = center.begin(bufs[2]);
|
||||
|
||||
int echo_pos = this->echo_pos;
|
||||
int reverb_pos = this->reverb_pos;
|
||||
int echo_pos = this->echo_pos;
|
||||
int reverb_pos = this->reverb_pos;
|
||||
|
||||
while ( count-- )
|
||||
{
|
||||
int sum1_s = sq1.read();
|
||||
int sum2_s = sq2.read();
|
||||
while (count--)
|
||||
{
|
||||
int sum1_s = sq1.read();
|
||||
int sum2_s = sq2.read();
|
||||
|
||||
sq1.next( shift );
|
||||
sq2.next( shift );
|
||||
sq1.next(shift);
|
||||
sq2.next(shift);
|
||||
|
||||
int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) +
|
||||
FMUL( sum2_s, chans.pan_2_levels [0] ) +
|
||||
reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask];
|
||||
int new_reverb_l = FMUL(sum1_s, chans.pan_1_levels[0]) +
|
||||
FMUL(sum2_s, chans.pan_2_levels[0]) +
|
||||
reverb_buf[(reverb_pos + chans.reverb_delay_l) & reverb_mask];
|
||||
|
||||
int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) +
|
||||
FMUL( sum2_s, chans.pan_2_levels [1] ) +
|
||||
reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask];
|
||||
int new_reverb_r = FMUL(sum1_s, chans.pan_1_levels[1]) +
|
||||
FMUL(sum2_s, chans.pan_2_levels[1]) +
|
||||
reverb_buf[(reverb_pos + chans.reverb_delay_r) & reverb_mask];
|
||||
|
||||
fixed_t reverb_level = chans.reverb_level;
|
||||
reverb_buf [reverb_pos] = FMUL( new_reverb_l, reverb_level );
|
||||
reverb_buf [reverb_pos + 1] = FMUL( new_reverb_r, reverb_level );
|
||||
reverb_pos = (reverb_pos + 2) & reverb_mask;
|
||||
fixed_t reverb_level = chans.reverb_level;
|
||||
reverb_buf[reverb_pos] = FMUL(new_reverb_l, reverb_level);
|
||||
reverb_buf[reverb_pos + 1] = FMUL(new_reverb_r, reverb_level);
|
||||
reverb_pos = (reverb_pos + 2) & reverb_mask;
|
||||
|
||||
int sum3_s = center.read();
|
||||
center.next( shift );
|
||||
int sum3_s = center.read();
|
||||
center.next(shift);
|
||||
|
||||
int left = new_reverb_l + sum3_s + FMUL( chans.echo_level,
|
||||
echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] );
|
||||
int right = new_reverb_r + sum3_s + FMUL( chans.echo_level,
|
||||
echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] );
|
||||
int left = new_reverb_l + sum3_s + FMUL(chans.echo_level, echo_buf[(echo_pos + chans.echo_delay_l) & echo_mask]);
|
||||
int right = new_reverb_r + sum3_s + FMUL(chans.echo_level, echo_buf[(echo_pos + chans.echo_delay_r) & echo_mask]);
|
||||
|
||||
echo_buf [echo_pos] = sum3_s;
|
||||
echo_pos = (echo_pos + 1) & echo_mask;
|
||||
echo_buf[echo_pos] = sum3_s;
|
||||
echo_pos = (echo_pos + 1) & echo_mask;
|
||||
|
||||
if ( (int16_t) left != left )
|
||||
left = 0x7FFF - (left >> 24);
|
||||
if ((int16_t)left != left)
|
||||
left = 0x7FFF - (left >> 24);
|
||||
|
||||
out [0] = left;
|
||||
out [1] = right;
|
||||
out[0] = left;
|
||||
out[1] = right;
|
||||
|
||||
out += 2;
|
||||
out += 2;
|
||||
|
||||
if ( (int16_t) right != right )
|
||||
out [-1] = 0x7FFF - (right >> 24);
|
||||
}
|
||||
this->reverb_pos = reverb_pos;
|
||||
this->echo_pos = echo_pos;
|
||||
if ((int16_t)right != right)
|
||||
out[-1] = 0x7FFF - (right >> 24);
|
||||
}
|
||||
this->reverb_pos = reverb_pos;
|
||||
this->echo_pos = echo_pos;
|
||||
|
||||
sq1.end( bufs [0] );
|
||||
sq2.end( bufs [1] );
|
||||
center.end( bufs [2] );
|
||||
sq1.end(bufs[0]);
|
||||
sq2.end(bufs[1]);
|
||||
center.end(bufs[2]);
|
||||
}
|
||||
|
||||
void Effects_Buffer::mix_enhanced( blip_sample_t* out, long count )
|
||||
void Effects_Buffer::mix_enhanced(blip_sample_t *out, long count)
|
||||
{
|
||||
Blip_Reader l1; l1.begin( bufs [3] );
|
||||
Blip_Reader r1; r1.begin( bufs [4] );
|
||||
Blip_Reader l2; l2.begin( bufs [5] );
|
||||
Blip_Reader r2; r2.begin( bufs [6] );
|
||||
Blip_Reader sq1; sq1.begin( bufs [0] );
|
||||
Blip_Reader sq2; sq2.begin( bufs [1] );
|
||||
Blip_Reader center;
|
||||
int shift = center.begin( bufs [2] );
|
||||
Blip_Reader l1;
|
||||
l1.begin(bufs[3]);
|
||||
Blip_Reader r1;
|
||||
r1.begin(bufs[4]);
|
||||
Blip_Reader l2;
|
||||
l2.begin(bufs[5]);
|
||||
Blip_Reader r2;
|
||||
r2.begin(bufs[6]);
|
||||
Blip_Reader sq1;
|
||||
sq1.begin(bufs[0]);
|
||||
Blip_Reader sq2;
|
||||
sq2.begin(bufs[1]);
|
||||
Blip_Reader center;
|
||||
int shift = center.begin(bufs[2]);
|
||||
|
||||
int echo_pos = this->echo_pos;
|
||||
int reverb_pos = this->reverb_pos;
|
||||
int echo_pos = this->echo_pos;
|
||||
int reverb_pos = this->reverb_pos;
|
||||
|
||||
while ( count-- )
|
||||
{
|
||||
int sum1_s = sq1.read();
|
||||
int sum2_s = sq2.read();
|
||||
while (count--)
|
||||
{
|
||||
int sum1_s = sq1.read();
|
||||
int sum2_s = sq2.read();
|
||||
|
||||
sq1.next( shift );
|
||||
sq2.next( shift );
|
||||
sq1.next(shift);
|
||||
sq2.next(shift);
|
||||
|
||||
int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) +
|
||||
FMUL( sum2_s, chans.pan_2_levels [0] ) + l1.read() +
|
||||
reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask];
|
||||
int new_reverb_l = FMUL(sum1_s, chans.pan_1_levels[0]) +
|
||||
FMUL(sum2_s, chans.pan_2_levels[0]) + l1.read() +
|
||||
reverb_buf[(reverb_pos + chans.reverb_delay_l) & reverb_mask];
|
||||
|
||||
int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) +
|
||||
FMUL( sum2_s, chans.pan_2_levels [1] ) + r1.read() +
|
||||
reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask];
|
||||
int new_reverb_r = FMUL(sum1_s, chans.pan_1_levels[1]) +
|
||||
FMUL(sum2_s, chans.pan_2_levels[1]) + r1.read() +
|
||||
reverb_buf[(reverb_pos + chans.reverb_delay_r) & reverb_mask];
|
||||
|
||||
l1.next( shift );
|
||||
r1.next( shift );
|
||||
l1.next(shift);
|
||||
r1.next(shift);
|
||||
|
||||
fixed_t reverb_level = chans.reverb_level;
|
||||
reverb_buf [reverb_pos] = FMUL( new_reverb_l, reverb_level );
|
||||
reverb_buf [reverb_pos + 1] = FMUL( new_reverb_r, reverb_level );
|
||||
reverb_pos = (reverb_pos + 2) & reverb_mask;
|
||||
fixed_t reverb_level = chans.reverb_level;
|
||||
reverb_buf[reverb_pos] = FMUL(new_reverb_l, reverb_level);
|
||||
reverb_buf[reverb_pos + 1] = FMUL(new_reverb_r, reverb_level);
|
||||
reverb_pos = (reverb_pos + 2) & reverb_mask;
|
||||
|
||||
int sum3_s = center.read();
|
||||
center.next( shift );
|
||||
int sum3_s = center.read();
|
||||
center.next(shift);
|
||||
|
||||
int left = new_reverb_l + sum3_s + l2.read() + FMUL( chans.echo_level,
|
||||
echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] );
|
||||
int right = new_reverb_r + sum3_s + r2.read() + FMUL( chans.echo_level,
|
||||
echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] );
|
||||
int left = new_reverb_l + sum3_s + l2.read() + FMUL(chans.echo_level, echo_buf[(echo_pos + chans.echo_delay_l) & echo_mask]);
|
||||
int right = new_reverb_r + sum3_s + r2.read() + FMUL(chans.echo_level, echo_buf[(echo_pos + chans.echo_delay_r) & echo_mask]);
|
||||
|
||||
l2.next( shift );
|
||||
r2.next( shift );
|
||||
l2.next(shift);
|
||||
r2.next(shift);
|
||||
|
||||
echo_buf [echo_pos] = sum3_s;
|
||||
echo_pos = (echo_pos + 1) & echo_mask;
|
||||
echo_buf[echo_pos] = sum3_s;
|
||||
echo_pos = (echo_pos + 1) & echo_mask;
|
||||
|
||||
if ( (int16_t) left != left )
|
||||
left = 0x7FFF - (left >> 24);
|
||||
if ((int16_t)left != left)
|
||||
left = 0x7FFF - (left >> 24);
|
||||
|
||||
out [0] = left;
|
||||
out [1] = right;
|
||||
out[0] = left;
|
||||
out[1] = right;
|
||||
|
||||
out += 2;
|
||||
out += 2;
|
||||
|
||||
if ( (int16_t) right != right )
|
||||
out [-1] = 0x7FFF - (right >> 24);
|
||||
}
|
||||
this->reverb_pos = reverb_pos;
|
||||
this->echo_pos = echo_pos;
|
||||
if ((int16_t)right != right)
|
||||
out[-1] = 0x7FFF - (right >> 24);
|
||||
}
|
||||
this->reverb_pos = reverb_pos;
|
||||
this->echo_pos = echo_pos;
|
||||
|
||||
sq1.end( bufs [0] );
|
||||
sq2.end( bufs [1] );
|
||||
center.end( bufs [2] );
|
||||
l1.end( bufs [3] );
|
||||
r1.end( bufs [4] );
|
||||
l2.end( bufs [5] );
|
||||
r2.end( bufs [6] );
|
||||
sq1.end(bufs[0]);
|
||||
sq2.end(bufs[1]);
|
||||
center.end(bufs[2]);
|
||||
l1.end(bufs[3]);
|
||||
r1.end(bufs[4]);
|
||||
l2.end(bufs[5]);
|
||||
r2.end(bufs[6]);
|
||||
}
|
||||
|
|
|
@ -3,87 +3,97 @@
|
|||
// Multi-channel effects buffer with panning, echo and reverb
|
||||
// Game_Music_Emu 0.3.0
|
||||
|
||||
#include <stdint.h>
|
||||
#include "Multi_Buffer.hpp"
|
||||
#include <stdint.h>
|
||||
|
||||
// Effects_Buffer uses several buffers and outputs stereo sample pairs.
|
||||
class Effects_Buffer : public Multi_Buffer {
|
||||
public:
|
||||
// If center_only is true, only center buffers are created and
|
||||
// less memory is used.
|
||||
Effects_Buffer( bool center_only = false );
|
||||
class Effects_Buffer : public Multi_Buffer
|
||||
{
|
||||
public:
|
||||
// If center_only is true, only center buffers are created and
|
||||
// less memory is used.
|
||||
Effects_Buffer(bool center_only = false);
|
||||
|
||||
// Channel Effect Center Pan
|
||||
// ---------------------------------
|
||||
// 0,5 reverb pan_1
|
||||
// 1,6 reverb pan_2
|
||||
// 2,7 echo -
|
||||
// 3 echo -
|
||||
// 4 echo -
|
||||
// Channel Effect Center Pan
|
||||
// ---------------------------------
|
||||
// 0,5 reverb pan_1
|
||||
// 1,6 reverb pan_2
|
||||
// 2,7 echo -
|
||||
// 3 echo -
|
||||
// 4 echo -
|
||||
|
||||
// Channel configuration
|
||||
struct config_t {
|
||||
double pan_1; // -1.0 = left, 0.0 = center, 1.0 = right
|
||||
double pan_2;
|
||||
double echo_delay; // msec
|
||||
double echo_level; // 0.0 to 1.0
|
||||
double reverb_delay; // msec
|
||||
double delay_variance; // difference between left/right delays (msec)
|
||||
double reverb_level; // 0.0 to 1.0
|
||||
bool effects_enabled; // if false, use optimized simple mixer
|
||||
config_t();
|
||||
};
|
||||
// Channel configuration
|
||||
struct config_t
|
||||
{
|
||||
double pan_1; // -1.0 = left, 0.0 = center, 1.0 = right
|
||||
double pan_2;
|
||||
double echo_delay; // msec
|
||||
double echo_level; // 0.0 to 1.0
|
||||
double reverb_delay; // msec
|
||||
double delay_variance; // difference between left/right delays (msec)
|
||||
double reverb_level; // 0.0 to 1.0
|
||||
bool effects_enabled; // if false, use optimized simple mixer
|
||||
config_t();
|
||||
};
|
||||
|
||||
// Set configuration of buffer
|
||||
virtual void config( const config_t& );
|
||||
void set_depth( double );
|
||||
// Set configuration of buffer
|
||||
virtual void config(const config_t &);
|
||||
void set_depth(double);
|
||||
|
||||
public:
|
||||
~Effects_Buffer();
|
||||
const char *set_sample_rate( long samples_per_sec, int msec = blip_default_length );
|
||||
void clock_rate( long );
|
||||
void bass_freq( int );
|
||||
void clear();
|
||||
channel_t channel( int );
|
||||
void end_frame( blip_time_t, bool was_stereo = true );
|
||||
long read_samples( blip_sample_t*, long );
|
||||
long samples_avail() const;
|
||||
private:
|
||||
typedef long fixed_t;
|
||||
public:
|
||||
~Effects_Buffer();
|
||||
const char *set_sample_rate(long samples_per_sec, int msec = blip_default_length);
|
||||
void clock_rate(long);
|
||||
void bass_freq(int);
|
||||
void clear();
|
||||
channel_t channel(int);
|
||||
void end_frame(blip_time_t, bool was_stereo = true);
|
||||
long read_samples(blip_sample_t *, long);
|
||||
long samples_avail() const;
|
||||
|
||||
enum { max_buf_count = 7 };
|
||||
Blip_Buffer bufs [max_buf_count];
|
||||
enum { chan_count = 5 };
|
||||
channel_t channels [chan_count];
|
||||
config_t config_;
|
||||
long stereo_remain;
|
||||
long effect_remain;
|
||||
int buf_count;
|
||||
bool effects_enabled;
|
||||
private:
|
||||
typedef long fixed_t;
|
||||
|
||||
blip_sample_t* reverb_buf;
|
||||
blip_sample_t* echo_buf;
|
||||
int reverb_pos;
|
||||
int echo_pos;
|
||||
enum
|
||||
{
|
||||
max_buf_count = 7
|
||||
};
|
||||
Blip_Buffer bufs[max_buf_count];
|
||||
enum
|
||||
{
|
||||
chan_count = 5
|
||||
};
|
||||
channel_t channels[chan_count];
|
||||
config_t config_;
|
||||
long stereo_remain;
|
||||
long effect_remain;
|
||||
int buf_count;
|
||||
bool effects_enabled;
|
||||
|
||||
struct {
|
||||
fixed_t pan_1_levels [2];
|
||||
fixed_t pan_2_levels [2];
|
||||
int echo_delay_l;
|
||||
int echo_delay_r;
|
||||
fixed_t echo_level;
|
||||
int reverb_delay_l;
|
||||
int reverb_delay_r;
|
||||
fixed_t reverb_level;
|
||||
} chans;
|
||||
blip_sample_t *reverb_buf;
|
||||
blip_sample_t *echo_buf;
|
||||
int reverb_pos;
|
||||
int echo_pos;
|
||||
|
||||
void mix_mono( blip_sample_t*, long );
|
||||
void mix_stereo( blip_sample_t*, long );
|
||||
void mix_enhanced( blip_sample_t*, long );
|
||||
void mix_mono_enhanced( blip_sample_t*, long );
|
||||
struct
|
||||
{
|
||||
fixed_t pan_1_levels[2];
|
||||
fixed_t pan_2_levels[2];
|
||||
int echo_delay_l;
|
||||
int echo_delay_r;
|
||||
fixed_t echo_level;
|
||||
int reverb_delay_l;
|
||||
int reverb_delay_r;
|
||||
fixed_t reverb_level;
|
||||
} chans;
|
||||
|
||||
void mix_mono(blip_sample_t *, long);
|
||||
void mix_stereo(blip_sample_t *, long);
|
||||
void mix_enhanced(blip_sample_t *, long);
|
||||
void mix_mono_enhanced(blip_sample_t *, long);
|
||||
};
|
||||
|
||||
inline Effects_Buffer::channel_t Effects_Buffer::channel( int i ) {
|
||||
return channels [i % chan_count];
|
||||
}
|
||||
|
||||
inline Effects_Buffer::channel_t Effects_Buffer::channel(int i)
|
||||
{
|
||||
return channels[i % chan_count];
|
||||
}
|
||||
|
|
|
@ -15,30 +15,30 @@ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf )
|
||||
Multi_Buffer::Multi_Buffer(int spf) : samples_per_frame_(spf)
|
||||
{
|
||||
length_ = 0;
|
||||
sample_rate_ = 0;
|
||||
channels_changed_count_ = 1;
|
||||
channels_changed_count_save_ = 1;
|
||||
length_ = 0;
|
||||
sample_rate_ = 0;
|
||||
channels_changed_count_ = 1;
|
||||
channels_changed_count_save_ = 1;
|
||||
}
|
||||
|
||||
const char * Multi_Buffer::set_channel_count( int )
|
||||
const char *Multi_Buffer::set_channel_count(int)
|
||||
{
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Multi_Buffer::SaveAudioBufferStatePrivate()
|
||||
{
|
||||
channels_changed_count_save_ = channels_changed_count_;
|
||||
channels_changed_count_save_ = channels_changed_count_;
|
||||
}
|
||||
|
||||
void Multi_Buffer::RestoreAudioBufferStatePrivate()
|
||||
{
|
||||
channels_changed_count_ = channels_changed_count_save_;
|
||||
channels_changed_count_ = channels_changed_count_save_;
|
||||
}
|
||||
|
||||
Mono_Buffer::Mono_Buffer() : Multi_Buffer( 1 )
|
||||
Mono_Buffer::Mono_Buffer() : Multi_Buffer(1)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -46,235 +46,237 @@ Mono_Buffer::~Mono_Buffer()
|
|||
{
|
||||
}
|
||||
|
||||
const char * Mono_Buffer::set_sample_rate( long rate, int msec )
|
||||
const char *Mono_Buffer::set_sample_rate(long rate, int msec)
|
||||
{
|
||||
buf.set_sample_rate( rate, msec );
|
||||
return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() );
|
||||
buf.set_sample_rate(rate, msec);
|
||||
return Multi_Buffer::set_sample_rate(buf.sample_rate(), buf.length());
|
||||
}
|
||||
|
||||
void Mono_Buffer::SaveAudioBufferState()
|
||||
{
|
||||
SaveAudioBufferStatePrivate();
|
||||
center()->SaveAudioBufferState();
|
||||
SaveAudioBufferStatePrivate();
|
||||
center()->SaveAudioBufferState();
|
||||
}
|
||||
|
||||
void Mono_Buffer::RestoreAudioBufferState()
|
||||
{
|
||||
RestoreAudioBufferStatePrivate();
|
||||
center()->RestoreAudioBufferState();
|
||||
RestoreAudioBufferStatePrivate();
|
||||
center()->RestoreAudioBufferState();
|
||||
}
|
||||
|
||||
// Silent_Buffer
|
||||
|
||||
Silent_Buffer::Silent_Buffer() : Multi_Buffer( 1 ) // 0 channels would probably confuse
|
||||
Silent_Buffer::Silent_Buffer() : Multi_Buffer(1) // 0 channels would probably confuse
|
||||
{
|
||||
chan.left = 0;
|
||||
chan.center = 0;
|
||||
chan.right = 0;
|
||||
chan.left = 0;
|
||||
chan.center = 0;
|
||||
chan.right = 0;
|
||||
}
|
||||
|
||||
void Silent_Buffer::SaveAudioBufferState()
|
||||
{
|
||||
SaveAudioBufferStatePrivate();
|
||||
SaveAudioBufferStatePrivate();
|
||||
}
|
||||
|
||||
void Silent_Buffer::RestoreAudioBufferState()
|
||||
{
|
||||
RestoreAudioBufferStatePrivate();
|
||||
RestoreAudioBufferStatePrivate();
|
||||
}
|
||||
|
||||
// Mono_Buffer
|
||||
|
||||
Mono_Buffer::channel_t Mono_Buffer::channel( int )
|
||||
Mono_Buffer::channel_t Mono_Buffer::channel(int)
|
||||
{
|
||||
channel_t ch;
|
||||
ch.center = &buf;
|
||||
ch.left = &buf;
|
||||
ch.right = &buf;
|
||||
return ch;
|
||||
channel_t ch;
|
||||
ch.center = &buf;
|
||||
ch.left = &buf;
|
||||
ch.right = &buf;
|
||||
return ch;
|
||||
}
|
||||
|
||||
void Mono_Buffer::end_frame( blip_time_t t, bool )
|
||||
void Mono_Buffer::end_frame(blip_time_t t, bool)
|
||||
{
|
||||
buf.end_frame( t );
|
||||
buf.end_frame(t);
|
||||
}
|
||||
|
||||
// Stereo_Buffer
|
||||
|
||||
Stereo_Buffer::Stereo_Buffer() : Multi_Buffer( 2 )
|
||||
Stereo_Buffer::Stereo_Buffer() : Multi_Buffer(2)
|
||||
{
|
||||
chan.center = &bufs [0];
|
||||
chan.left = &bufs [1];
|
||||
chan.right = &bufs [2];
|
||||
chan.center = &bufs[0];
|
||||
chan.left = &bufs[1];
|
||||
chan.right = &bufs[2];
|
||||
}
|
||||
|
||||
Stereo_Buffer::~Stereo_Buffer()
|
||||
{
|
||||
}
|
||||
|
||||
const char * Stereo_Buffer::set_sample_rate( long rate, int msec )
|
||||
const char *Stereo_Buffer::set_sample_rate(long rate, int msec)
|
||||
{
|
||||
for ( int i = 0; i < buf_count; i++ ) bufs [i].set_sample_rate( rate, msec ) ;
|
||||
return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() );
|
||||
for (int i = 0; i < buf_count; i++) 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 )
|
||||
void Stereo_Buffer::clock_rate(long rate)
|
||||
{
|
||||
for ( int i = 0; i < buf_count; i++ )
|
||||
bufs [i].clock_rate( rate );
|
||||
for (int i = 0; i < buf_count; i++)
|
||||
bufs[i].clock_rate(rate);
|
||||
}
|
||||
|
||||
void Stereo_Buffer::bass_freq( int bass )
|
||||
void Stereo_Buffer::bass_freq(int bass)
|
||||
{
|
||||
for ( unsigned i = 0; i < buf_count; i++ )
|
||||
bufs [i].bass_freq( bass );
|
||||
for (unsigned i = 0; i < buf_count; i++)
|
||||
bufs[i].bass_freq(bass);
|
||||
}
|
||||
|
||||
void Stereo_Buffer::clear()
|
||||
{
|
||||
stereo_added = false;
|
||||
was_stereo = false;
|
||||
for ( int i = 0; i < buf_count; i++ )
|
||||
bufs [i].clear();
|
||||
stereo_added = false;
|
||||
was_stereo = false;
|
||||
for (int i = 0; i < buf_count; i++)
|
||||
bufs[i].clear();
|
||||
}
|
||||
|
||||
void Stereo_Buffer::end_frame( blip_time_t clock_count, bool stereo )
|
||||
void Stereo_Buffer::end_frame(blip_time_t clock_count, bool stereo)
|
||||
{
|
||||
for ( unsigned i = 0; i < buf_count; i++ )
|
||||
bufs [i].end_frame( clock_count );
|
||||
for (unsigned i = 0; i < buf_count; i++)
|
||||
bufs[i].end_frame(clock_count);
|
||||
|
||||
stereo_added |= stereo;
|
||||
stereo_added |= stereo;
|
||||
}
|
||||
|
||||
long Stereo_Buffer::read_samples( blip_sample_t* out, long count )
|
||||
long Stereo_Buffer::read_samples(blip_sample_t *out, long count)
|
||||
{
|
||||
count = (unsigned) count / 2;
|
||||
count = (unsigned)count / 2;
|
||||
|
||||
long avail = bufs [0].samples_avail();
|
||||
if ( count > avail )
|
||||
count = avail;
|
||||
if ( count )
|
||||
{
|
||||
if ( stereo_added || was_stereo )
|
||||
{
|
||||
mix_stereo( out, count );
|
||||
long avail = bufs[0].samples_avail();
|
||||
if (count > avail)
|
||||
count = avail;
|
||||
if (count)
|
||||
{
|
||||
if (stereo_added || was_stereo)
|
||||
{
|
||||
mix_stereo(out, count);
|
||||
|
||||
bufs [0].remove_samples( count );
|
||||
bufs [1].remove_samples( count );
|
||||
bufs [2].remove_samples( count );
|
||||
}
|
||||
else
|
||||
{
|
||||
mix_mono( out, count );
|
||||
bufs[0].remove_samples(count);
|
||||
bufs[1].remove_samples(count);
|
||||
bufs[2].remove_samples(count);
|
||||
}
|
||||
else
|
||||
{
|
||||
mix_mono(out, count);
|
||||
|
||||
bufs [0].remove_samples( count );
|
||||
bufs[0].remove_samples(count);
|
||||
|
||||
bufs [1].remove_silence( count );
|
||||
bufs [2].remove_silence( count );
|
||||
}
|
||||
bufs[1].remove_silence(count);
|
||||
bufs[2].remove_silence(count);
|
||||
}
|
||||
|
||||
// to do: this might miss opportunities for optimization
|
||||
if ( !bufs [0].samples_avail() ) {
|
||||
was_stereo = stereo_added;
|
||||
stereo_added = false;
|
||||
}
|
||||
}
|
||||
// to do: this might miss opportunities for optimization
|
||||
if (!bufs[0].samples_avail())
|
||||
{
|
||||
was_stereo = stereo_added;
|
||||
stereo_added = false;
|
||||
}
|
||||
}
|
||||
|
||||
return count * 2;
|
||||
return count * 2;
|
||||
}
|
||||
|
||||
void Stereo_Buffer::mix_stereo( blip_sample_t* out, long count )
|
||||
void Stereo_Buffer::mix_stereo(blip_sample_t *out, long count)
|
||||
{
|
||||
Blip_Reader left;
|
||||
Blip_Reader right;
|
||||
Blip_Reader center;
|
||||
Blip_Reader left;
|
||||
Blip_Reader right;
|
||||
Blip_Reader center;
|
||||
|
||||
left.begin( bufs [1] );
|
||||
right.begin( bufs [2] );
|
||||
int bass = center.begin( bufs [0] );
|
||||
left.begin(bufs[1]);
|
||||
right.begin(bufs[2]);
|
||||
int bass = center.begin(bufs[0]);
|
||||
|
||||
if (out != 0)
|
||||
{
|
||||
while ( count-- )
|
||||
{
|
||||
int c = center.read();
|
||||
long l = c + left.read();
|
||||
long r = c + right.read();
|
||||
center.next( bass );
|
||||
out [0] = l;
|
||||
out [1] = r;
|
||||
out += 2;
|
||||
if (out != 0)
|
||||
{
|
||||
while (count--)
|
||||
{
|
||||
int c = center.read();
|
||||
long l = c + left.read();
|
||||
long r = c + right.read();
|
||||
center.next(bass);
|
||||
out[0] = l;
|
||||
out[1] = r;
|
||||
out += 2;
|
||||
|
||||
if ( (int16_t) l != l )
|
||||
out [-2] = 0x7FFF - (l >> 24);
|
||||
if ((int16_t)l != l)
|
||||
out[-2] = 0x7FFF - (l >> 24);
|
||||
|
||||
left.next( bass );
|
||||
right.next( bass );
|
||||
left.next(bass);
|
||||
right.next(bass);
|
||||
|
||||
if ( (int16_t) r != r )
|
||||
out [-1] = 0x7FFF - (r >> 24);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//only run accumulators, do not output any audio
|
||||
while (count--)
|
||||
{
|
||||
center.next(bass);
|
||||
left.next(bass);
|
||||
right.next(bass);
|
||||
}
|
||||
}
|
||||
if ((int16_t)r != r)
|
||||
out[-1] = 0x7FFF - (r >> 24);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// only run accumulators, do not output any audio
|
||||
while (count--)
|
||||
{
|
||||
center.next(bass);
|
||||
left.next(bass);
|
||||
right.next(bass);
|
||||
}
|
||||
}
|
||||
|
||||
center.end( bufs [0] );
|
||||
right.end( bufs [2] );
|
||||
left.end( bufs [1] );
|
||||
center.end(bufs[0]);
|
||||
right.end(bufs[2]);
|
||||
left.end(bufs[1]);
|
||||
}
|
||||
|
||||
void Stereo_Buffer::mix_mono( blip_sample_t* out, long count )
|
||||
void Stereo_Buffer::mix_mono(blip_sample_t *out, long count)
|
||||
{
|
||||
Blip_Reader in;
|
||||
int bass = in.begin( bufs [0] );
|
||||
Blip_Reader in;
|
||||
int bass = in.begin(bufs[0]);
|
||||
|
||||
if (out != 0)
|
||||
{
|
||||
while ( count-- )
|
||||
{
|
||||
long s = in.read();
|
||||
in.next( bass );
|
||||
out [0] = s;
|
||||
out [1] = s;
|
||||
out += 2;
|
||||
if (out != 0)
|
||||
{
|
||||
while (count--)
|
||||
{
|
||||
long s = in.read();
|
||||
in.next(bass);
|
||||
out[0] = s;
|
||||
out[1] = s;
|
||||
out += 2;
|
||||
|
||||
if ( (int16_t) s != s ) {
|
||||
s = 0x7FFF - (s >> 24);
|
||||
out [-2] = s;
|
||||
out [-1] = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (count--)
|
||||
{
|
||||
in.next(bass);
|
||||
}
|
||||
}
|
||||
if ((int16_t)s != s)
|
||||
{
|
||||
s = 0x7FFF - (s >> 24);
|
||||
out[-2] = s;
|
||||
out[-1] = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (count--)
|
||||
{
|
||||
in.next(bass);
|
||||
}
|
||||
}
|
||||
|
||||
in.end( bufs [0] );
|
||||
in.end(bufs[0]);
|
||||
}
|
||||
|
||||
void Stereo_Buffer::SaveAudioBufferState()
|
||||
{
|
||||
SaveAudioBufferStatePrivate();
|
||||
left()->SaveAudioBufferState();
|
||||
center()->SaveAudioBufferState();
|
||||
right()->SaveAudioBufferState();
|
||||
SaveAudioBufferStatePrivate();
|
||||
left()->SaveAudioBufferState();
|
||||
center()->SaveAudioBufferState();
|
||||
right()->SaveAudioBufferState();
|
||||
}
|
||||
void Stereo_Buffer::RestoreAudioBufferState()
|
||||
{
|
||||
RestoreAudioBufferStatePrivate();
|
||||
left()->RestoreAudioBufferState();
|
||||
center()->RestoreAudioBufferState();
|
||||
right()->RestoreAudioBufferState();
|
||||
RestoreAudioBufferStatePrivate();
|
||||
left()->RestoreAudioBufferState();
|
||||
center()->RestoreAudioBufferState();
|
||||
right()->RestoreAudioBufferState();
|
||||
}
|
||||
|
|
|
@ -8,179 +8,190 @@
|
|||
|
||||
// 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() { }
|
||||
class Multi_Buffer
|
||||
{
|
||||
public:
|
||||
Multi_Buffer(int samples_per_frame);
|
||||
virtual ~Multi_Buffer() {}
|
||||
|
||||
// Set the number of channels available
|
||||
virtual const char* set_channel_count( int );
|
||||
// Set the number of channels available
|
||||
virtual const char *set_channel_count(int);
|
||||
|
||||
// Get 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 ) = 0;
|
||||
// Get 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) = 0;
|
||||
|
||||
// See Blip_Buffer.h
|
||||
virtual const char* set_sample_rate( long rate, int msec = blip_default_length ) = 0;
|
||||
virtual void clock_rate( long ) = 0;
|
||||
virtual void bass_freq( int ) = 0;
|
||||
virtual void clear() = 0;
|
||||
long sample_rate() const;
|
||||
// See Blip_Buffer.h
|
||||
virtual const char *set_sample_rate(long rate, int msec = blip_default_length) = 0;
|
||||
virtual void clock_rate(long) = 0;
|
||||
virtual void bass_freq(int) = 0;
|
||||
virtual void clear() = 0;
|
||||
long sample_rate() const;
|
||||
|
||||
// Length of buffer, in milliseconds
|
||||
int length() const;
|
||||
// Length of buffer, in milliseconds
|
||||
int length() const;
|
||||
|
||||
// See Blip_Buffer.h. For optimal operation, pass false for 'added_stereo'
|
||||
// if nothing was added to the left and right buffers of any channel for
|
||||
// this time frame.
|
||||
virtual void end_frame( blip_time_t, bool added_stereo = true ) = 0;
|
||||
// See Blip_Buffer.h. For optimal operation, pass false for 'added_stereo'
|
||||
// if nothing was added to the left and right buffers of any channel for
|
||||
// this time frame.
|
||||
virtual void end_frame(blip_time_t, bool added_stereo = true) = 0;
|
||||
|
||||
// Number of samples per output frame (1 = mono, 2 = stereo)
|
||||
int samples_per_frame() const;
|
||||
// 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_; }
|
||||
// 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 ) = 0;
|
||||
virtual long samples_avail() const = 0;
|
||||
// See Blip_Buffer.h
|
||||
virtual long read_samples(blip_sample_t *, long) = 0;
|
||||
virtual long samples_avail() const = 0;
|
||||
|
||||
protected:
|
||||
void channels_changed() { channels_changed_count_++; }
|
||||
private:
|
||||
// noncopyable
|
||||
Multi_Buffer( const Multi_Buffer& );
|
||||
Multi_Buffer& operator = ( const Multi_Buffer& );
|
||||
protected:
|
||||
void channels_changed() { channels_changed_count_++; }
|
||||
|
||||
unsigned channels_changed_count_;
|
||||
long sample_rate_;
|
||||
int length_;
|
||||
int const samples_per_frame_;
|
||||
unsigned channels_changed_count_save_;
|
||||
protected:
|
||||
void SaveAudioBufferStatePrivate();
|
||||
void RestoreAudioBufferStatePrivate();
|
||||
public:
|
||||
virtual void SaveAudioBufferState() = 0;
|
||||
virtual void RestoreAudioBufferState() = 0;
|
||||
private:
|
||||
// noncopyable
|
||||
Multi_Buffer(const Multi_Buffer &);
|
||||
Multi_Buffer &operator=(const Multi_Buffer &);
|
||||
|
||||
unsigned channels_changed_count_;
|
||||
long sample_rate_;
|
||||
int length_;
|
||||
int const samples_per_frame_;
|
||||
unsigned channels_changed_count_save_;
|
||||
|
||||
protected:
|
||||
void SaveAudioBufferStatePrivate();
|
||||
void RestoreAudioBufferStatePrivate();
|
||||
|
||||
public:
|
||||
virtual void SaveAudioBufferState() = 0;
|
||||
virtual void RestoreAudioBufferState() = 0;
|
||||
};
|
||||
|
||||
// Uses a single buffer and outputs mono samples.
|
||||
class Mono_Buffer : public Multi_Buffer {
|
||||
Blip_Buffer buf;
|
||||
public:
|
||||
Mono_Buffer();
|
||||
~Mono_Buffer();
|
||||
class Mono_Buffer : public Multi_Buffer
|
||||
{
|
||||
Blip_Buffer buf;
|
||||
|
||||
// Buffer used for all channels
|
||||
Blip_Buffer* center() { return &buf; }
|
||||
public:
|
||||
Mono_Buffer();
|
||||
~Mono_Buffer();
|
||||
|
||||
// See Multi_Buffer
|
||||
const char* set_sample_rate( long rate, int msec = blip_default_length );
|
||||
void clock_rate( long );
|
||||
void bass_freq( int );
|
||||
void clear();
|
||||
channel_t channel( int );
|
||||
void end_frame( blip_time_t, bool unused = true );
|
||||
long samples_avail() const;
|
||||
long read_samples( blip_sample_t*, long );
|
||||
// Buffer used for all channels
|
||||
Blip_Buffer *center() { return &buf; }
|
||||
|
||||
virtual void SaveAudioBufferState();
|
||||
virtual void RestoreAudioBufferState();
|
||||
// See Multi_Buffer
|
||||
const char *set_sample_rate(long rate, int msec = blip_default_length);
|
||||
void clock_rate(long);
|
||||
void bass_freq(int);
|
||||
void clear();
|
||||
channel_t channel(int);
|
||||
void end_frame(blip_time_t, bool unused = true);
|
||||
long samples_avail() const;
|
||||
long read_samples(blip_sample_t *, long);
|
||||
|
||||
virtual void SaveAudioBufferState();
|
||||
virtual void RestoreAudioBufferState();
|
||||
};
|
||||
|
||||
// Uses three buffers (one for center) and outputs stereo sample pairs.
|
||||
class Stereo_Buffer : public Multi_Buffer {
|
||||
public:
|
||||
Stereo_Buffer();
|
||||
~Stereo_Buffer();
|
||||
class Stereo_Buffer : public Multi_Buffer
|
||||
{
|
||||
public:
|
||||
Stereo_Buffer();
|
||||
~Stereo_Buffer();
|
||||
|
||||
// Buffers used for all channels
|
||||
Blip_Buffer* center() { return &bufs [0]; }
|
||||
Blip_Buffer* left() { return &bufs [1]; }
|
||||
Blip_Buffer* right() { return &bufs [2]; }
|
||||
// Buffers used for all channels
|
||||
Blip_Buffer *center() { return &bufs[0]; }
|
||||
Blip_Buffer *left() { return &bufs[1]; }
|
||||
Blip_Buffer *right() { return &bufs[2]; }
|
||||
|
||||
// See Multi_Buffer
|
||||
const char* set_sample_rate( long, int msec = blip_default_length );
|
||||
void clock_rate( long );
|
||||
void bass_freq( int );
|
||||
void clear();
|
||||
channel_t channel( int index );
|
||||
void end_frame( blip_time_t, bool added_stereo = true );
|
||||
// See Multi_Buffer
|
||||
const char *set_sample_rate(long, int msec = blip_default_length);
|
||||
void clock_rate(long);
|
||||
void bass_freq(int);
|
||||
void clear();
|
||||
channel_t channel(int index);
|
||||
void end_frame(blip_time_t, bool added_stereo = true);
|
||||
|
||||
long samples_avail() const;
|
||||
long read_samples( blip_sample_t*, long );
|
||||
long samples_avail() const;
|
||||
long read_samples(blip_sample_t *, long);
|
||||
|
||||
private:
|
||||
enum { buf_count = 3 };
|
||||
Blip_Buffer bufs [buf_count];
|
||||
channel_t chan;
|
||||
bool stereo_added;
|
||||
bool was_stereo;
|
||||
private:
|
||||
enum
|
||||
{
|
||||
buf_count = 3
|
||||
};
|
||||
Blip_Buffer bufs[buf_count];
|
||||
channel_t chan;
|
||||
bool stereo_added;
|
||||
bool was_stereo;
|
||||
|
||||
void mix_stereo( blip_sample_t*, long );
|
||||
void mix_mono( blip_sample_t*, long );
|
||||
void mix_stereo(blip_sample_t *, long);
|
||||
void mix_mono(blip_sample_t *, long);
|
||||
|
||||
virtual void SaveAudioBufferState();
|
||||
virtual void RestoreAudioBufferState();
|
||||
virtual void SaveAudioBufferState();
|
||||
virtual void RestoreAudioBufferState();
|
||||
};
|
||||
|
||||
// Silent_Buffer generates no samples, useful where no sound is wanted
|
||||
class Silent_Buffer : public Multi_Buffer {
|
||||
channel_t chan;
|
||||
public:
|
||||
Silent_Buffer();
|
||||
class Silent_Buffer : public Multi_Buffer
|
||||
{
|
||||
channel_t chan;
|
||||
|
||||
const char* 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, bool unused = true ) { }
|
||||
long samples_avail() const { return 0; }
|
||||
long read_samples( blip_sample_t*, long ) { return 0; }
|
||||
public:
|
||||
Silent_Buffer();
|
||||
|
||||
virtual void SaveAudioBufferState();
|
||||
virtual void RestoreAudioBufferState();
|
||||
const char *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, bool unused = true) {}
|
||||
long samples_avail() const { return 0; }
|
||||
long read_samples(blip_sample_t *, long) { return 0; }
|
||||
|
||||
virtual void SaveAudioBufferState();
|
||||
virtual void RestoreAudioBufferState();
|
||||
};
|
||||
|
||||
|
||||
// End of public interface
|
||||
|
||||
inline const char* Multi_Buffer::set_sample_rate( long rate, int msec )
|
||||
inline const char *Multi_Buffer::set_sample_rate(long rate, int msec)
|
||||
{
|
||||
sample_rate_ = rate;
|
||||
length_ = msec;
|
||||
return 0;
|
||||
sample_rate_ = rate;
|
||||
length_ = msec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline const char* Silent_Buffer::set_sample_rate( long rate, int msec )
|
||||
inline const char *Silent_Buffer::set_sample_rate(long rate, int msec)
|
||||
{
|
||||
return Multi_Buffer::set_sample_rate( rate, msec );
|
||||
return Multi_Buffer::set_sample_rate(rate, msec);
|
||||
}
|
||||
|
||||
inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; }
|
||||
|
||||
inline long Stereo_Buffer::samples_avail() const { return bufs [0].samples_avail() * 2; }
|
||||
inline long Stereo_Buffer::samples_avail() const { return bufs[0].samples_avail() * 2; }
|
||||
|
||||
inline Stereo_Buffer::channel_t Stereo_Buffer::channel( int ) { return chan; }
|
||||
inline Stereo_Buffer::channel_t Stereo_Buffer::channel(int) { return chan; }
|
||||
|
||||
inline long Multi_Buffer::sample_rate() const { return sample_rate_; }
|
||||
|
||||
inline int Multi_Buffer::length() const { return length_; }
|
||||
|
||||
inline void Mono_Buffer::clock_rate( long rate ) { buf.clock_rate( rate ); }
|
||||
inline void Mono_Buffer::clock_rate(long rate) { buf.clock_rate(rate); }
|
||||
|
||||
inline void Mono_Buffer::clear() { buf.clear(); }
|
||||
|
||||
inline void Mono_Buffer::bass_freq( int freq ) { buf.bass_freq( freq ); }
|
||||
inline void Mono_Buffer::bass_freq(int freq) { buf.bass_freq(freq); }
|
||||
|
||||
inline long Mono_Buffer::read_samples( blip_sample_t* p, long s ) { return buf.read_samples( p, s ); }
|
||||
inline long Mono_Buffer::read_samples(blip_sample_t *p, long s) { return buf.read_samples(p, s); }
|
||||
|
||||
inline long Mono_Buffer::samples_avail() const { return buf.samples_avail(); }
|
||||
|
||||
|
|
|
@ -14,220 +14,218 @@ details. You should have received a copy of the GNU Lesser General Public
|
|||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
|
||||
// Nes_Buffer
|
||||
|
||||
Nes_Buffer::Nes_Buffer() : Multi_Buffer( 1 ) { }
|
||||
Nes_Buffer::Nes_Buffer() : Multi_Buffer(1) {}
|
||||
|
||||
Nes_Buffer::~Nes_Buffer() { }
|
||||
Nes_Buffer::~Nes_Buffer() {}
|
||||
|
||||
Multi_Buffer* set_apu( Nes_Buffer* buf, Nes_Apu* apu )
|
||||
Multi_Buffer *set_apu(Nes_Buffer *buf, Nes_Apu *apu)
|
||||
{
|
||||
buf->set_apu( apu );
|
||||
return buf;
|
||||
buf->set_apu(apu);
|
||||
return buf;
|
||||
}
|
||||
|
||||
void Nes_Buffer::enable_nonlinearity( bool b )
|
||||
void Nes_Buffer::enable_nonlinearity(bool b)
|
||||
{
|
||||
if ( b )
|
||||
clear();
|
||||
if (b)
|
||||
clear();
|
||||
|
||||
Nes_Apu* apu = nonlin.enable( b, &tnd );
|
||||
apu->osc_output( 0, &buf );
|
||||
apu->osc_output( 1, &buf );
|
||||
Nes_Apu *apu = nonlin.enable(b, &tnd);
|
||||
apu->osc_output(0, &buf);
|
||||
apu->osc_output(1, &buf);
|
||||
}
|
||||
|
||||
const char * Nes_Buffer::set_sample_rate( long rate, int msec )
|
||||
const char *Nes_Buffer::set_sample_rate(long rate, int msec)
|
||||
{
|
||||
enable_nonlinearity( nonlin.enabled ); // reapply
|
||||
buf.set_sample_rate( rate, msec );
|
||||
tnd.set_sample_rate( rate, msec );
|
||||
return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() );
|
||||
enable_nonlinearity(nonlin.enabled); // reapply
|
||||
buf.set_sample_rate(rate, msec);
|
||||
tnd.set_sample_rate(rate, msec);
|
||||
return Multi_Buffer::set_sample_rate(buf.sample_rate(), buf.length());
|
||||
}
|
||||
|
||||
void Nes_Buffer::clock_rate( long rate )
|
||||
void Nes_Buffer::clock_rate(long rate)
|
||||
{
|
||||
buf.clock_rate( rate );
|
||||
tnd.clock_rate( rate );
|
||||
buf.clock_rate(rate);
|
||||
tnd.clock_rate(rate);
|
||||
}
|
||||
|
||||
void Nes_Buffer::bass_freq( int freq )
|
||||
void Nes_Buffer::bass_freq(int freq)
|
||||
{
|
||||
buf.bass_freq( freq );
|
||||
tnd.bass_freq( freq );
|
||||
buf.bass_freq(freq);
|
||||
tnd.bass_freq(freq);
|
||||
}
|
||||
|
||||
void Nes_Buffer::clear()
|
||||
{
|
||||
nonlin.clear();
|
||||
buf.clear();
|
||||
tnd.clear();
|
||||
nonlin.clear();
|
||||
buf.clear();
|
||||
tnd.clear();
|
||||
}
|
||||
|
||||
Nes_Buffer::channel_t Nes_Buffer::channel( int i )
|
||||
Nes_Buffer::channel_t Nes_Buffer::channel(int i)
|
||||
{
|
||||
channel_t c;
|
||||
c.center = &buf;
|
||||
if ( 2 <= i && i <= 4 )
|
||||
c.center = &tnd; // only use for triangle, noise, and dmc
|
||||
c.left = c.center;
|
||||
c.right = c.center;
|
||||
return c;
|
||||
channel_t c;
|
||||
c.center = &buf;
|
||||
if (2 <= i && i <= 4)
|
||||
c.center = &tnd; // only use for triangle, noise, and dmc
|
||||
c.left = c.center;
|
||||
c.right = c.center;
|
||||
return c;
|
||||
}
|
||||
|
||||
void Nes_Buffer::end_frame( blip_time_t length, bool )
|
||||
void Nes_Buffer::end_frame(blip_time_t length, bool)
|
||||
{
|
||||
buf.end_frame( length );
|
||||
tnd.end_frame( length );
|
||||
buf.end_frame(length);
|
||||
tnd.end_frame(length);
|
||||
}
|
||||
|
||||
long Nes_Buffer::samples_avail() const
|
||||
{
|
||||
return buf.samples_avail();
|
||||
return buf.samples_avail();
|
||||
}
|
||||
|
||||
long Nes_Buffer::read_samples( blip_sample_t* out, long count )
|
||||
long Nes_Buffer::read_samples(blip_sample_t *out, long count)
|
||||
{
|
||||
count = nonlin.make_nonlinear( tnd, count );
|
||||
if ( count )
|
||||
{
|
||||
Blip_Reader lin;
|
||||
Blip_Reader nonlin;
|
||||
count = nonlin.make_nonlinear(tnd, count);
|
||||
if (count)
|
||||
{
|
||||
Blip_Reader lin;
|
||||
Blip_Reader nonlin;
|
||||
|
||||
int lin_bass = lin.begin( buf );
|
||||
int nonlin_bass = nonlin.begin( tnd );
|
||||
int lin_bass = lin.begin(buf);
|
||||
int nonlin_bass = nonlin.begin(tnd);
|
||||
|
||||
if (out != 0)
|
||||
{
|
||||
for ( int n = count; n--; )
|
||||
{
|
||||
int s = lin.read() + nonlin.read();
|
||||
lin.next( lin_bass );
|
||||
nonlin.next( nonlin_bass );
|
||||
*out++ = s;
|
||||
if (out != 0)
|
||||
{
|
||||
for (int n = count; n--;)
|
||||
{
|
||||
int s = lin.read() + nonlin.read();
|
||||
lin.next(lin_bass);
|
||||
nonlin.next(nonlin_bass);
|
||||
*out++ = s;
|
||||
|
||||
if ( (int16_t) s != s )
|
||||
out [-1] = 0x7FFF - (s >> 24);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//only run accumulators, do not output audio
|
||||
for (int n = count; n--; )
|
||||
{
|
||||
lin.next(lin_bass);
|
||||
nonlin.next(nonlin_bass);
|
||||
}
|
||||
}
|
||||
if ((int16_t)s != s)
|
||||
out[-1] = 0x7FFF - (s >> 24);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// only run accumulators, do not output audio
|
||||
for (int n = count; n--;)
|
||||
{
|
||||
lin.next(lin_bass);
|
||||
nonlin.next(nonlin_bass);
|
||||
}
|
||||
}
|
||||
|
||||
lin.end( buf );
|
||||
nonlin.end( tnd );
|
||||
lin.end(buf);
|
||||
nonlin.end(tnd);
|
||||
|
||||
buf.remove_samples( count );
|
||||
tnd.remove_samples( count );
|
||||
}
|
||||
buf.remove_samples(count);
|
||||
tnd.remove_samples(count);
|
||||
}
|
||||
|
||||
return count;
|
||||
return count;
|
||||
}
|
||||
|
||||
void Nes_Buffer::SaveAudioBufferState()
|
||||
{
|
||||
SaveAudioBufferStatePrivate();
|
||||
nonlin.SaveAudioBufferState();
|
||||
buf.SaveAudioBufferState();
|
||||
tnd.SaveAudioBufferState();
|
||||
SaveAudioBufferStatePrivate();
|
||||
nonlin.SaveAudioBufferState();
|
||||
buf.SaveAudioBufferState();
|
||||
tnd.SaveAudioBufferState();
|
||||
}
|
||||
|
||||
void Nes_Buffer::RestoreAudioBufferState()
|
||||
{
|
||||
RestoreAudioBufferStatePrivate();
|
||||
nonlin.RestoreAudioBufferState();
|
||||
buf.RestoreAudioBufferState();
|
||||
tnd.RestoreAudioBufferState();
|
||||
RestoreAudioBufferStatePrivate();
|
||||
nonlin.RestoreAudioBufferState();
|
||||
buf.RestoreAudioBufferState();
|
||||
tnd.RestoreAudioBufferState();
|
||||
}
|
||||
|
||||
// Nes_Nonlinearizer
|
||||
|
||||
Nes_Nonlinearizer::Nes_Nonlinearizer()
|
||||
{
|
||||
apu = nullptr;
|
||||
enabled = true;
|
||||
apu = nullptr;
|
||||
enabled = true;
|
||||
|
||||
float const gain = 0x7fff * 1.3f;
|
||||
// don't use entire range, so any overflow will stay within table
|
||||
int const range = (int) ( (double)table_size * Nes_Apu::nonlinear_tnd_gain());
|
||||
for ( int i = 0; i < table_size; i++ )
|
||||
{
|
||||
int const offset = table_size - range;
|
||||
int j = i - offset;
|
||||
float n = 202.0f / (range - 1) * j;
|
||||
float d = 0;
|
||||
// Prevent division by zero
|
||||
if ( n )
|
||||
d = gain * 163.67f / (24329.0f / n + 100.0f);
|
||||
int out = (int) d;
|
||||
table [j & (table_size - 1)] = out;
|
||||
}
|
||||
extra_accum = 0;
|
||||
extra_prev = 0;
|
||||
float const gain = 0x7fff * 1.3f;
|
||||
// don't use entire range, so any overflow will stay within table
|
||||
int const range = (int)((double)table_size * Nes_Apu::nonlinear_tnd_gain());
|
||||
for (int i = 0; i < table_size; i++)
|
||||
{
|
||||
int const offset = table_size - range;
|
||||
int j = i - offset;
|
||||
float n = 202.0f / (range - 1) * j;
|
||||
float d = 0;
|
||||
// Prevent division by zero
|
||||
if (n)
|
||||
d = gain * 163.67f / (24329.0f / n + 100.0f);
|
||||
int out = (int)d;
|
||||
table[j & (table_size - 1)] = out;
|
||||
}
|
||||
extra_accum = 0;
|
||||
extra_prev = 0;
|
||||
}
|
||||
|
||||
Nes_Apu* Nes_Nonlinearizer::enable( bool b, Blip_Buffer* buf )
|
||||
Nes_Apu *Nes_Nonlinearizer::enable(bool b, Blip_Buffer *buf)
|
||||
{
|
||||
apu->osc_output( 2, buf );
|
||||
apu->osc_output( 3, buf );
|
||||
apu->osc_output( 4, buf );
|
||||
enabled = b;
|
||||
if ( b )
|
||||
apu->enable_nonlinear( 1.0 );
|
||||
else
|
||||
apu->volume( 1.0 );
|
||||
return apu;
|
||||
apu->osc_output(2, buf);
|
||||
apu->osc_output(3, buf);
|
||||
apu->osc_output(4, buf);
|
||||
enabled = b;
|
||||
if (b)
|
||||
apu->enable_nonlinear(1.0);
|
||||
else
|
||||
apu->volume(1.0);
|
||||
return apu;
|
||||
}
|
||||
|
||||
#define ENTRY( s ) table [(s) >> (blip_sample_bits - table_bits - 1) & (table_size - 1)]
|
||||
#define ENTRY(s) table[(s) >> (blip_sample_bits - table_bits - 1) & (table_size - 1)]
|
||||
|
||||
long Nes_Nonlinearizer::make_nonlinear( Blip_Buffer& buf, long count )
|
||||
long Nes_Nonlinearizer::make_nonlinear(Blip_Buffer &buf, long count)
|
||||
{
|
||||
long avail = buf.samples_avail();
|
||||
if ( count > avail )
|
||||
count = avail;
|
||||
if ( count && enabled )
|
||||
{
|
||||
long avail = buf.samples_avail();
|
||||
if (count > avail)
|
||||
count = avail;
|
||||
if (count && enabled)
|
||||
{
|
||||
Blip_Buffer::buf_t_ *p = buf.buffer_;
|
||||
long accum = this->accum;
|
||||
long prev = this->prev;
|
||||
for (unsigned n = count; n; --n)
|
||||
{
|
||||
long entry = ENTRY(accum);
|
||||
accum += *p;
|
||||
*p++ = (entry - prev) << (blip_sample_bits - 16);
|
||||
prev = entry;
|
||||
}
|
||||
|
||||
Blip_Buffer::buf_t_* p = buf.buffer_;
|
||||
long accum = this->accum;
|
||||
long prev = this->prev;
|
||||
for ( unsigned n = count; n; --n )
|
||||
{
|
||||
long entry = ENTRY( accum );
|
||||
accum += *p;
|
||||
*p++ = (entry - prev) << (blip_sample_bits - 16);
|
||||
prev = entry;
|
||||
}
|
||||
this->prev = prev;
|
||||
this->accum = accum;
|
||||
}
|
||||
|
||||
this->prev = prev;
|
||||
this->accum = accum;
|
||||
}
|
||||
|
||||
return count;
|
||||
return count;
|
||||
}
|
||||
|
||||
void Nes_Nonlinearizer::clear()
|
||||
{
|
||||
accum = 0;
|
||||
prev = ENTRY( 86016000 ); // avoid thump due to APU's triangle dc bias
|
||||
// TODO: still results in slight clicks and thumps
|
||||
accum = 0;
|
||||
prev = ENTRY(86016000); // avoid thump due to APU's triangle dc bias
|
||||
// TODO: still results in slight clicks and thumps
|
||||
}
|
||||
|
||||
void Nes_Nonlinearizer::SaveAudioBufferState()
|
||||
{
|
||||
extra_accum = accum;
|
||||
extra_prev = prev;
|
||||
extra_accum = accum;
|
||||
extra_prev = prev;
|
||||
}
|
||||
|
||||
void Nes_Nonlinearizer::RestoreAudioBufferState()
|
||||
{
|
||||
accum = extra_accum;
|
||||
prev = extra_prev;
|
||||
accum = extra_accum;
|
||||
prev = extra_prev;
|
||||
}
|
||||
|
|
|
@ -8,60 +8,70 @@
|
|||
|
||||
class Nes_Apu;
|
||||
|
||||
class Nes_Nonlinearizer {
|
||||
private:
|
||||
enum { table_bits = 11 };
|
||||
enum { table_size = 1 << table_bits };
|
||||
int16_t table [table_size];
|
||||
Nes_Apu* apu;
|
||||
long accum;
|
||||
long prev;
|
||||
class Nes_Nonlinearizer
|
||||
{
|
||||
private:
|
||||
enum
|
||||
{
|
||||
table_bits = 11
|
||||
};
|
||||
enum
|
||||
{
|
||||
table_size = 1 << table_bits
|
||||
};
|
||||
int16_t table[table_size];
|
||||
Nes_Apu *apu;
|
||||
long accum;
|
||||
long prev;
|
||||
|
||||
long extra_accum;
|
||||
long extra_prev;
|
||||
public:
|
||||
Nes_Nonlinearizer();
|
||||
bool enabled;
|
||||
void clear();
|
||||
void set_apu( Nes_Apu* a ) { apu = a; }
|
||||
Nes_Apu* enable( bool, Blip_Buffer* tnd );
|
||||
long make_nonlinear( Blip_Buffer& buf, long count );
|
||||
void SaveAudioBufferState();
|
||||
void RestoreAudioBufferState();
|
||||
long extra_accum;
|
||||
long extra_prev;
|
||||
|
||||
public:
|
||||
Nes_Nonlinearizer();
|
||||
bool enabled;
|
||||
void clear();
|
||||
void set_apu(Nes_Apu *a) { apu = a; }
|
||||
Nes_Apu *enable(bool, Blip_Buffer *tnd);
|
||||
long make_nonlinear(Blip_Buffer &buf, long count);
|
||||
void SaveAudioBufferState();
|
||||
void RestoreAudioBufferState();
|
||||
};
|
||||
|
||||
class Nes_Buffer : public Multi_Buffer {
|
||||
public:
|
||||
Nes_Buffer();
|
||||
~Nes_Buffer();
|
||||
class Nes_Buffer : public Multi_Buffer
|
||||
{
|
||||
public:
|
||||
Nes_Buffer();
|
||||
~Nes_Buffer();
|
||||
|
||||
// Setup APU for use with buffer, including setting its output to this buffer.
|
||||
// If you're using Nes_Emu, this is automatically called for you.
|
||||
void set_apu( Nes_Apu* apu ) { nonlin.set_apu( apu ); }
|
||||
// Setup APU for use with buffer, including setting its output to this buffer.
|
||||
// If you're using Nes_Emu, this is automatically called for you.
|
||||
void set_apu(Nes_Apu *apu) { nonlin.set_apu(apu); }
|
||||
|
||||
// Enable/disable non-linear output
|
||||
void enable_nonlinearity( bool = true );
|
||||
// Enable/disable non-linear output
|
||||
void enable_nonlinearity(bool = true);
|
||||
|
||||
// Blip_Buffer to output other sound chips to
|
||||
Blip_Buffer* buffer() { return &buf; }
|
||||
// Blip_Buffer to output other sound chips to
|
||||
Blip_Buffer *buffer() { return &buf; }
|
||||
|
||||
// See Multi_Buffer.h
|
||||
const char *set_sample_rate( long rate, int msec = blip_default_length );
|
||||
// See Multi_Buffer.h
|
||||
const char *set_sample_rate(long rate, int msec = blip_default_length);
|
||||
|
||||
void clock_rate( long );
|
||||
void bass_freq( int );
|
||||
void clear();
|
||||
channel_t channel( int );
|
||||
void end_frame( blip_time_t, bool unused = true );
|
||||
long samples_avail() const;
|
||||
long read_samples( blip_sample_t*, long );
|
||||
void clock_rate(long);
|
||||
void bass_freq(int);
|
||||
void clear();
|
||||
channel_t channel(int);
|
||||
void end_frame(blip_time_t, bool unused = true);
|
||||
long samples_avail() const;
|
||||
long read_samples(blip_sample_t *, long);
|
||||
|
||||
private:
|
||||
Blip_Buffer buf;
|
||||
Blip_Buffer tnd;
|
||||
Nes_Nonlinearizer nonlin;
|
||||
friend Multi_Buffer* set_apu( Nes_Buffer*, Nes_Apu* );
|
||||
public:
|
||||
virtual void SaveAudioBufferState();
|
||||
virtual void RestoreAudioBufferState();
|
||||
private:
|
||||
Blip_Buffer buf;
|
||||
Blip_Buffer tnd;
|
||||
Nes_Nonlinearizer nonlin;
|
||||
friend Multi_Buffer *set_apu(Nes_Buffer *, Nes_Apu *);
|
||||
|
||||
public:
|
||||
virtual void SaveAudioBufferState();
|
||||
virtual void RestoreAudioBufferState();
|
||||
};
|
||||
|
|
|
@ -15,68 +15,67 @@ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
Nes_Effects_Buffer::Nes_Effects_Buffer() :
|
||||
Effects_Buffer( true ) // nes never uses stereo channels
|
||||
Nes_Effects_Buffer::Nes_Effects_Buffer() : Effects_Buffer(true) // nes never uses stereo channels
|
||||
{
|
||||
config_t c;
|
||||
c.effects_enabled = false;
|
||||
config( c );
|
||||
config_t c;
|
||||
c.effects_enabled = false;
|
||||
config(c);
|
||||
}
|
||||
|
||||
Nes_Effects_Buffer::~Nes_Effects_Buffer() { }
|
||||
Nes_Effects_Buffer::~Nes_Effects_Buffer() {}
|
||||
|
||||
Multi_Buffer* set_apu( Nes_Effects_Buffer* buf, Nes_Apu* apu )
|
||||
Multi_Buffer *set_apu(Nes_Effects_Buffer *buf, Nes_Apu *apu)
|
||||
{
|
||||
buf->set_apu( apu );
|
||||
return buf;
|
||||
buf->set_apu(apu);
|
||||
return buf;
|
||||
}
|
||||
|
||||
void Nes_Effects_Buffer::enable_nonlinearity( bool b )
|
||||
void Nes_Effects_Buffer::enable_nonlinearity(bool b)
|
||||
{
|
||||
if ( b )
|
||||
clear();
|
||||
Nes_Apu* apu = nonlin.enable( b, channel( 2 ).center );
|
||||
apu->osc_output( 0, channel( 0 ).center );
|
||||
apu->osc_output( 1, channel( 1 ).center );
|
||||
if (b)
|
||||
clear();
|
||||
Nes_Apu *apu = nonlin.enable(b, channel(2).center);
|
||||
apu->osc_output(0, channel(0).center);
|
||||
apu->osc_output(1, channel(1).center);
|
||||
}
|
||||
|
||||
void Nes_Effects_Buffer::config( const config_t& in )
|
||||
void Nes_Effects_Buffer::config(const config_t &in)
|
||||
{
|
||||
config_t c = in;
|
||||
if ( !c.effects_enabled )
|
||||
{
|
||||
// effects must always be enabled to keep separate buffers, so
|
||||
// set parameters to be equivalent to disabled
|
||||
c.pan_1 = 0;
|
||||
c.pan_2 = 0;
|
||||
c.echo_level = 0;
|
||||
c.reverb_level = 0;
|
||||
c.effects_enabled = true;
|
||||
}
|
||||
Effects_Buffer::config( c );
|
||||
config_t c = in;
|
||||
if (!c.effects_enabled)
|
||||
{
|
||||
// effects must always be enabled to keep separate buffers, so
|
||||
// set parameters to be equivalent to disabled
|
||||
c.pan_1 = 0;
|
||||
c.pan_2 = 0;
|
||||
c.echo_level = 0;
|
||||
c.reverb_level = 0;
|
||||
c.effects_enabled = true;
|
||||
}
|
||||
Effects_Buffer::config(c);
|
||||
}
|
||||
|
||||
const char * Nes_Effects_Buffer::set_sample_rate( long rate, int msec )
|
||||
const char *Nes_Effects_Buffer::set_sample_rate(long rate, int msec)
|
||||
{
|
||||
enable_nonlinearity( nonlin.enabled ); // reapply
|
||||
return Effects_Buffer::set_sample_rate( rate, msec );
|
||||
enable_nonlinearity(nonlin.enabled); // reapply
|
||||
return Effects_Buffer::set_sample_rate(rate, msec);
|
||||
}
|
||||
|
||||
void Nes_Effects_Buffer::clear()
|
||||
{
|
||||
nonlin.clear();
|
||||
Effects_Buffer::clear();
|
||||
nonlin.clear();
|
||||
Effects_Buffer::clear();
|
||||
}
|
||||
|
||||
Nes_Effects_Buffer::channel_t Nes_Effects_Buffer::channel( int i )
|
||||
Nes_Effects_Buffer::channel_t Nes_Effects_Buffer::channel(int i)
|
||||
{
|
||||
return Effects_Buffer::channel( (2 <= i && i <= 4) ? 2 : i & 1 );
|
||||
return Effects_Buffer::channel((2 <= i && i <= 4) ? 2 : i & 1);
|
||||
}
|
||||
|
||||
long Nes_Effects_Buffer::read_samples( blip_sample_t* out, long count )
|
||||
long Nes_Effects_Buffer::read_samples(blip_sample_t *out, long count)
|
||||
{
|
||||
count = 2 * nonlin.make_nonlinear( *channel( 2 ).center, count / 2 );
|
||||
return Effects_Buffer::read_samples( out, count );
|
||||
count = 2 * nonlin.make_nonlinear(*channel(2).center, count / 2);
|
||||
return Effects_Buffer::read_samples(out, count);
|
||||
}
|
||||
|
||||
void Nes_Effects_Buffer::SaveAudioBufferState()
|
||||
|
|
|
@ -3,33 +3,34 @@
|
|||
// Effects_Buffer with non-linear sound
|
||||
// Nes_Emu 0.7.0
|
||||
|
||||
#include "Nes_Buffer.hpp"
|
||||
#include "Effects_Buffer.hpp"
|
||||
#include "Nes_Buffer.hpp"
|
||||
|
||||
// Effects_Buffer uses several buffers and outputs stereo sample pairs.
|
||||
class Nes_Effects_Buffer : public Effects_Buffer {
|
||||
public:
|
||||
Nes_Effects_Buffer();
|
||||
~Nes_Effects_Buffer();
|
||||
class Nes_Effects_Buffer : public Effects_Buffer
|
||||
{
|
||||
public:
|
||||
Nes_Effects_Buffer();
|
||||
~Nes_Effects_Buffer();
|
||||
|
||||
// Setup APU for use with buffer, including setting its output to this buffer.
|
||||
// If you're using Nes_Emu, this is automatically called for you.
|
||||
void set_apu( Nes_Apu* apu ) { nonlin.set_apu( apu ); }
|
||||
// Setup APU for use with buffer, including setting its output to this buffer.
|
||||
// If you're using Nes_Emu, this is automatically called for you.
|
||||
void set_apu(Nes_Apu *apu) { nonlin.set_apu(apu); }
|
||||
|
||||
// Enable/disable non-linear output
|
||||
void enable_nonlinearity( bool = true );
|
||||
// Enable/disable non-linear output
|
||||
void enable_nonlinearity(bool = true);
|
||||
|
||||
// See Effects_Buffer.h for reference
|
||||
const char *set_sample_rate( long rate, int msec = blip_default_length );
|
||||
void config( const config_t& );
|
||||
void clear();
|
||||
channel_t channel( int );
|
||||
long read_samples( blip_sample_t*, long );
|
||||
// See Effects_Buffer.h for reference
|
||||
const char *set_sample_rate(long rate, int msec = blip_default_length);
|
||||
void config(const config_t &);
|
||||
void clear();
|
||||
channel_t channel(int);
|
||||
long read_samples(blip_sample_t *, long);
|
||||
|
||||
void SaveAudioBufferState();
|
||||
void RestoreAudioBufferState();
|
||||
void SaveAudioBufferState();
|
||||
void RestoreAudioBufferState();
|
||||
|
||||
private:
|
||||
Nes_Nonlinearizer nonlin;
|
||||
friend Multi_Buffer* set_apu( Nes_Effects_Buffer*, Nes_Apu* );
|
||||
private:
|
||||
Nes_Nonlinearizer nonlin;
|
||||
friend Multi_Buffer *set_apu(Nes_Effects_Buffer *, Nes_Apu *);
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -8,141 +8,160 @@
|
|||
|
||||
class Nes_Apu;
|
||||
|
||||
typedef long nes_time_t; // CPU clock cycle count
|
||||
typedef long nes_time_t; // CPU clock cycle count
|
||||
typedef unsigned nes_addr_t; // 16-bit memory address
|
||||
|
||||
struct Nes_Osc
|
||||
{
|
||||
unsigned char regs [4];
|
||||
bool reg_written [4];
|
||||
Blip_Buffer* output;
|
||||
int length_counter;// length counter (0 if unused by oscillator)
|
||||
int delay; // delay until next (potential) transition
|
||||
int last_amp; // last amplitude oscillator was outputting
|
||||
unsigned char regs[4];
|
||||
bool reg_written[4];
|
||||
Blip_Buffer *output;
|
||||
int length_counter; // length counter (0 if unused by oscillator)
|
||||
int delay; // delay until next (potential) transition
|
||||
int last_amp; // last amplitude oscillator was outputting
|
||||
|
||||
void clock_length( int halt_mask );
|
||||
int period() const {
|
||||
return (regs [3] & 7) * 0x100 + (regs [2] & 0xff);
|
||||
}
|
||||
void reset() {
|
||||
delay = 0;
|
||||
last_amp = 0;
|
||||
}
|
||||
int update_amp( int amp ) {
|
||||
int delta = amp - last_amp;
|
||||
last_amp = amp;
|
||||
return delta;
|
||||
}
|
||||
void clock_length(int halt_mask);
|
||||
int period() const
|
||||
{
|
||||
return (regs[3] & 7) * 0x100 + (regs[2] & 0xff);
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
delay = 0;
|
||||
last_amp = 0;
|
||||
}
|
||||
int update_amp(int amp)
|
||||
{
|
||||
int delta = amp - last_amp;
|
||||
last_amp = amp;
|
||||
return delta;
|
||||
}
|
||||
};
|
||||
|
||||
struct Nes_Envelope : Nes_Osc
|
||||
{
|
||||
int envelope;
|
||||
int env_delay;
|
||||
int envelope;
|
||||
int env_delay;
|
||||
|
||||
void clock_envelope();
|
||||
int volume() const;
|
||||
void reset() {
|
||||
envelope = 0;
|
||||
env_delay = 0;
|
||||
Nes_Osc::reset();
|
||||
}
|
||||
void clock_envelope();
|
||||
int volume() const;
|
||||
void reset()
|
||||
{
|
||||
envelope = 0;
|
||||
env_delay = 0;
|
||||
Nes_Osc::reset();
|
||||
}
|
||||
};
|
||||
|
||||
// Nes_Square
|
||||
struct Nes_Square : Nes_Envelope
|
||||
{
|
||||
enum { negate_flag = 0x08 };
|
||||
enum { shift_mask = 0x07 };
|
||||
enum { phase_range = 8 };
|
||||
int phase;
|
||||
int sweep_delay;
|
||||
enum
|
||||
{
|
||||
negate_flag = 0x08
|
||||
};
|
||||
enum
|
||||
{
|
||||
shift_mask = 0x07
|
||||
};
|
||||
enum
|
||||
{
|
||||
phase_range = 8
|
||||
};
|
||||
int phase;
|
||||
int sweep_delay;
|
||||
|
||||
typedef Blip_Synth<blip_good_quality,1> Synth;
|
||||
Synth const& synth; // shared between squares
|
||||
typedef Blip_Synth<blip_good_quality, 1> Synth;
|
||||
Synth const &synth; // shared between squares
|
||||
|
||||
Nes_Square( Synth const* s ) : synth( *s ) { }
|
||||
Nes_Square(Synth const *s) : synth(*s) {}
|
||||
|
||||
void clock_sweep( int adjust );
|
||||
void run( nes_time_t, nes_time_t );
|
||||
void reset() {
|
||||
sweep_delay = 0;
|
||||
Nes_Envelope::reset();
|
||||
}
|
||||
nes_time_t maintain_phase( nes_time_t time, nes_time_t end_time,
|
||||
nes_time_t timer_period );
|
||||
void clock_sweep(int adjust);
|
||||
void run(nes_time_t, nes_time_t);
|
||||
void reset()
|
||||
{
|
||||
sweep_delay = 0;
|
||||
Nes_Envelope::reset();
|
||||
}
|
||||
nes_time_t maintain_phase(nes_time_t time, nes_time_t end_time, nes_time_t timer_period);
|
||||
};
|
||||
|
||||
// Nes_Triangle
|
||||
struct Nes_Triangle : Nes_Osc
|
||||
{
|
||||
enum { phase_range = 16 };
|
||||
int phase;
|
||||
int linear_counter;
|
||||
Blip_Synth<blip_med_quality,1> synth;
|
||||
enum
|
||||
{
|
||||
phase_range = 16
|
||||
};
|
||||
int phase;
|
||||
int linear_counter;
|
||||
Blip_Synth<blip_med_quality, 1> synth;
|
||||
|
||||
int calc_amp() const;
|
||||
void run( nes_time_t, nes_time_t );
|
||||
void clock_linear_counter();
|
||||
void reset() {
|
||||
linear_counter = 0;
|
||||
phase = 1;
|
||||
Nes_Osc::reset();
|
||||
}
|
||||
nes_time_t maintain_phase( nes_time_t time, nes_time_t end_time,
|
||||
nes_time_t timer_period );
|
||||
int calc_amp() const;
|
||||
void run(nes_time_t, nes_time_t);
|
||||
void clock_linear_counter();
|
||||
void reset()
|
||||
{
|
||||
linear_counter = 0;
|
||||
phase = 1;
|
||||
Nes_Osc::reset();
|
||||
}
|
||||
nes_time_t maintain_phase(nes_time_t time, nes_time_t end_time, nes_time_t timer_period);
|
||||
};
|
||||
|
||||
// Nes_Noise
|
||||
struct Nes_Noise : Nes_Envelope
|
||||
{
|
||||
int noise;
|
||||
Blip_Synth<blip_med_quality,1> synth;
|
||||
int noise;
|
||||
Blip_Synth<blip_med_quality, 1> synth;
|
||||
|
||||
void run( nes_time_t, nes_time_t );
|
||||
void reset() {
|
||||
noise = 1 << 14;
|
||||
Nes_Envelope::reset();
|
||||
}
|
||||
void run(nes_time_t, nes_time_t);
|
||||
void reset()
|
||||
{
|
||||
noise = 1 << 14;
|
||||
Nes_Envelope::reset();
|
||||
}
|
||||
};
|
||||
|
||||
// Nes_Dmc
|
||||
struct Nes_Dmc : Nes_Osc
|
||||
{
|
||||
int address; // address of next byte to read
|
||||
int period;
|
||||
//int length_counter; // bytes remaining to play (already defined in Nes_Osc)
|
||||
int buf;
|
||||
int bits_remain;
|
||||
int bits;
|
||||
bool buf_full;
|
||||
bool silence;
|
||||
int address; // address of next byte to read
|
||||
int period;
|
||||
// int length_counter; // bytes remaining to play (already defined in Nes_Osc)
|
||||
int buf;
|
||||
int bits_remain;
|
||||
int bits;
|
||||
bool buf_full;
|
||||
bool silence;
|
||||
|
||||
enum { loop_flag = 0x40 };
|
||||
enum
|
||||
{
|
||||
loop_flag = 0x40
|
||||
};
|
||||
|
||||
int dac;
|
||||
int dac;
|
||||
|
||||
nes_time_t next_irq;
|
||||
bool irq_enabled;
|
||||
bool irq_flag;
|
||||
bool pal_mode;
|
||||
bool nonlinear;
|
||||
nes_time_t next_irq;
|
||||
bool irq_enabled;
|
||||
bool irq_flag;
|
||||
bool pal_mode;
|
||||
bool nonlinear;
|
||||
|
||||
int (*prg_reader)( void*, nes_addr_t ); // needs to be initialized to prg read function
|
||||
void* prg_reader_data;
|
||||
int (*prg_reader)(void *, nes_addr_t); // needs to be initialized to prg read function
|
||||
void *prg_reader_data;
|
||||
|
||||
Nes_Apu* apu;
|
||||
Nes_Apu *apu;
|
||||
|
||||
Blip_Synth<blip_med_quality,1> synth;
|
||||
Blip_Synth<blip_med_quality, 1> synth;
|
||||
|
||||
void start();
|
||||
void write_register( int, int );
|
||||
void run( nes_time_t, nes_time_t );
|
||||
void recalc_irq();
|
||||
void fill_buffer();
|
||||
void reload_sample();
|
||||
void reset();
|
||||
int count_reads( nes_time_t, nes_time_t* ) const;
|
||||
nes_time_t next_read_time() const;
|
||||
void start();
|
||||
void write_register(int, int);
|
||||
void run(nes_time_t, nes_time_t);
|
||||
void recalc_irq();
|
||||
void fill_buffer();
|
||||
void reload_sample();
|
||||
void reset();
|
||||
int count_reads(nes_time_t, nes_time_t *) const;
|
||||
nes_time_t next_read_time() const;
|
||||
};
|
||||
|
||||
|
|
|
@ -15,351 +15,356 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
int const amp_range = 15;
|
||||
|
||||
Nes_Apu::Nes_Apu() :
|
||||
square1( &square_synth ),
|
||||
square2( &square_synth )
|
||||
Nes_Apu::Nes_Apu() : square1(&square_synth),
|
||||
square2(&square_synth)
|
||||
{
|
||||
dmc.apu = this;
|
||||
dmc.prg_reader = 0;
|
||||
irq_notifier_ = 0;
|
||||
dmc.apu = this;
|
||||
dmc.prg_reader = 0;
|
||||
irq_notifier_ = 0;
|
||||
|
||||
oscs [0] = &square1;
|
||||
oscs [1] = &square2;
|
||||
oscs [2] = ▵
|
||||
oscs [3] = &noise;
|
||||
oscs [4] = &dmc;
|
||||
oscs[0] = &square1;
|
||||
oscs[1] = &square2;
|
||||
oscs[2] = ▵
|
||||
oscs[3] = &noise;
|
||||
oscs[4] = &dmc;
|
||||
|
||||
output( 0 );
|
||||
volume( 1.0 );
|
||||
reset( false );
|
||||
output(0);
|
||||
volume(1.0);
|
||||
reset(false);
|
||||
}
|
||||
|
||||
Nes_Apu::~Nes_Apu()
|
||||
{
|
||||
}
|
||||
|
||||
void Nes_Apu::treble_eq( const blip_eq_t& eq )
|
||||
void Nes_Apu::treble_eq(const blip_eq_t &eq)
|
||||
{
|
||||
square_synth.treble_eq( eq );
|
||||
triangle.synth.treble_eq( eq );
|
||||
noise.synth.treble_eq( eq );
|
||||
dmc.synth.treble_eq( eq );
|
||||
square_synth.treble_eq(eq);
|
||||
triangle.synth.treble_eq(eq);
|
||||
noise.synth.treble_eq(eq);
|
||||
dmc.synth.treble_eq(eq);
|
||||
}
|
||||
|
||||
void Nes_Apu::enable_nonlinear( double v )
|
||||
void Nes_Apu::enable_nonlinear(double v)
|
||||
{
|
||||
dmc.nonlinear = true;
|
||||
square_synth.volume( 1.3 * 0.25751258 / 0.742467605 * 0.25 / amp_range * v );
|
||||
dmc.nonlinear = true;
|
||||
square_synth.volume(1.3 * 0.25751258 / 0.742467605 * 0.25 / amp_range * v);
|
||||
|
||||
const double tnd = 0.48 / 202 * nonlinear_tnd_gain();
|
||||
triangle.synth.volume( 3.0 * tnd );
|
||||
noise.synth.volume( 2.0 * tnd );
|
||||
dmc.synth.volume( tnd );
|
||||
const double tnd = 0.48 / 202 * nonlinear_tnd_gain();
|
||||
triangle.synth.volume(3.0 * tnd);
|
||||
noise.synth.volume(2.0 * tnd);
|
||||
dmc.synth.volume(tnd);
|
||||
|
||||
square1 .last_amp = 0;
|
||||
square2 .last_amp = 0;
|
||||
triangle.last_amp = 0;
|
||||
noise .last_amp = 0;
|
||||
dmc .last_amp = 0;
|
||||
square1.last_amp = 0;
|
||||
square2.last_amp = 0;
|
||||
triangle.last_amp = 0;
|
||||
noise.last_amp = 0;
|
||||
dmc.last_amp = 0;
|
||||
}
|
||||
|
||||
void Nes_Apu::volume( double v )
|
||||
void Nes_Apu::volume(double v)
|
||||
{
|
||||
dmc.nonlinear = false;
|
||||
square_synth.volume( 0.1128 / amp_range * v );
|
||||
triangle.synth.volume( 0.12765 / amp_range * v );
|
||||
noise.synth.volume( 0.0741 / amp_range * v );
|
||||
dmc.synth.volume( 0.42545 / 127 * v );
|
||||
dmc.nonlinear = false;
|
||||
square_synth.volume(0.1128 / amp_range * v);
|
||||
triangle.synth.volume(0.12765 / amp_range * v);
|
||||
noise.synth.volume(0.0741 / amp_range * v);
|
||||
dmc.synth.volume(0.42545 / 127 * v);
|
||||
}
|
||||
|
||||
void Nes_Apu::output( Blip_Buffer* buffer )
|
||||
void Nes_Apu::output(Blip_Buffer *buffer)
|
||||
{
|
||||
for ( int i = 0; i < osc_count; i++ )
|
||||
osc_output( i, buffer );
|
||||
for (int i = 0; i < osc_count; i++)
|
||||
osc_output(i, buffer);
|
||||
}
|
||||
|
||||
void Nes_Apu::reset( bool pal_mode, int initial_dmc_dac )
|
||||
void Nes_Apu::reset(bool pal_mode, int initial_dmc_dac)
|
||||
{
|
||||
// to do: time pal frame periods exactly
|
||||
frame_period = pal_mode ? 8314 : 7458;
|
||||
dmc.pal_mode = pal_mode;
|
||||
// to do: time pal frame periods exactly
|
||||
frame_period = pal_mode ? 8314 : 7458;
|
||||
dmc.pal_mode = pal_mode;
|
||||
|
||||
square1.reset();
|
||||
square2.reset();
|
||||
triangle.reset();
|
||||
noise.reset();
|
||||
dmc.reset();
|
||||
square1.reset();
|
||||
square2.reset();
|
||||
triangle.reset();
|
||||
noise.reset();
|
||||
dmc.reset();
|
||||
|
||||
last_time = 0;
|
||||
last_dmc_time = 0;
|
||||
osc_enables = 0;
|
||||
irq_flag = false;
|
||||
earliest_irq_ = no_irq;
|
||||
frame_delay = 1;
|
||||
write_register( 0, 0x4017, 0x00 );
|
||||
write_register( 0, 0x4015, 0x00 );
|
||||
last_time = 0;
|
||||
last_dmc_time = 0;
|
||||
osc_enables = 0;
|
||||
irq_flag = false;
|
||||
earliest_irq_ = no_irq;
|
||||
frame_delay = 1;
|
||||
write_register(0, 0x4017, 0x00);
|
||||
write_register(0, 0x4015, 0x00);
|
||||
|
||||
for ( nes_addr_t addr = start_addr; addr <= 0x4013; addr++ )
|
||||
write_register( 0, addr, (addr & 3) ? 0x00 : 0x10 );
|
||||
for (nes_addr_t addr = start_addr; addr <= 0x4013; addr++)
|
||||
write_register(0, addr, (addr & 3) ? 0x00 : 0x10);
|
||||
|
||||
dmc.dac = initial_dmc_dac;
|
||||
if ( !dmc.nonlinear )
|
||||
triangle.last_amp = 15;
|
||||
//if ( !dmc.nonlinear ) // to do: remove?
|
||||
// dmc.last_amp = initial_dmc_dac; // prevent output transition
|
||||
dmc.dac = initial_dmc_dac;
|
||||
if (!dmc.nonlinear)
|
||||
triangle.last_amp = 15;
|
||||
// if ( !dmc.nonlinear ) // to do: remove?
|
||||
// dmc.last_amp = initial_dmc_dac; // prevent output transition
|
||||
}
|
||||
|
||||
void Nes_Apu::irq_changed()
|
||||
{
|
||||
nes_time_t new_irq = dmc.next_irq;
|
||||
if ( dmc.irq_flag | irq_flag ) {
|
||||
new_irq = 0;
|
||||
}
|
||||
else if ( new_irq > next_irq ) {
|
||||
new_irq = next_irq;
|
||||
}
|
||||
nes_time_t new_irq = dmc.next_irq;
|
||||
if (dmc.irq_flag | irq_flag)
|
||||
{
|
||||
new_irq = 0;
|
||||
}
|
||||
else if (new_irq > next_irq)
|
||||
{
|
||||
new_irq = next_irq;
|
||||
}
|
||||
|
||||
if ( new_irq != earliest_irq_ ) {
|
||||
earliest_irq_ = new_irq;
|
||||
if ( irq_notifier_ )
|
||||
irq_notifier_( irq_data );
|
||||
}
|
||||
if (new_irq != earliest_irq_)
|
||||
{
|
||||
earliest_irq_ = new_irq;
|
||||
if (irq_notifier_)
|
||||
irq_notifier_(irq_data);
|
||||
}
|
||||
}
|
||||
|
||||
// frames
|
||||
|
||||
void Nes_Apu::run_until( nes_time_t end_time )
|
||||
void Nes_Apu::run_until(nes_time_t end_time)
|
||||
{
|
||||
if ( end_time > next_dmc_read_time() )
|
||||
{
|
||||
nes_time_t start = last_dmc_time;
|
||||
last_dmc_time = end_time;
|
||||
dmc.run( start, end_time );
|
||||
}
|
||||
if (end_time > next_dmc_read_time())
|
||||
{
|
||||
nes_time_t start = last_dmc_time;
|
||||
last_dmc_time = end_time;
|
||||
dmc.run(start, end_time);
|
||||
}
|
||||
}
|
||||
|
||||
void Nes_Apu::run_until_( nes_time_t end_time )
|
||||
void Nes_Apu::run_until_(nes_time_t end_time)
|
||||
{
|
||||
if ( end_time == last_time )
|
||||
return;
|
||||
if (end_time == last_time)
|
||||
return;
|
||||
|
||||
if ( last_dmc_time < end_time )
|
||||
{
|
||||
nes_time_t start = last_dmc_time;
|
||||
last_dmc_time = end_time;
|
||||
dmc.run( start, end_time );
|
||||
}
|
||||
if (last_dmc_time < end_time)
|
||||
{
|
||||
nes_time_t start = last_dmc_time;
|
||||
last_dmc_time = end_time;
|
||||
dmc.run(start, end_time);
|
||||
}
|
||||
|
||||
while ( true )
|
||||
{
|
||||
// earlier of next frame time or end time
|
||||
nes_time_t time = last_time + frame_delay;
|
||||
if ( time > end_time )
|
||||
time = end_time;
|
||||
frame_delay -= time - last_time;
|
||||
while (true)
|
||||
{
|
||||
// earlier of next frame time or end time
|
||||
nes_time_t time = last_time + frame_delay;
|
||||
if (time > end_time)
|
||||
time = end_time;
|
||||
frame_delay -= time - last_time;
|
||||
|
||||
// run oscs to present
|
||||
square1.run( last_time, time );
|
||||
square2.run( last_time, time );
|
||||
triangle.run( last_time, time );
|
||||
noise.run( last_time, time );
|
||||
last_time = time;
|
||||
// run oscs to present
|
||||
square1.run(last_time, time);
|
||||
square2.run(last_time, time);
|
||||
triangle.run(last_time, time);
|
||||
noise.run(last_time, time);
|
||||
last_time = time;
|
||||
|
||||
if ( time == end_time )
|
||||
break; // no more frames to run
|
||||
if (time == end_time)
|
||||
break; // no more frames to run
|
||||
|
||||
// take frame-specific actions
|
||||
frame_delay = frame_period;
|
||||
switch ( frame++ )
|
||||
{
|
||||
case 0:
|
||||
if ( !(frame_mode & 0xc0) ) {
|
||||
next_irq = time + frame_period * 4 + 1;
|
||||
irq_flag = true;
|
||||
}
|
||||
// fall through
|
||||
case 2:
|
||||
// clock length and sweep on frames 0 and 2
|
||||
square1.clock_length( 0x20 );
|
||||
square2.clock_length( 0x20 );
|
||||
noise.clock_length( 0x20 );
|
||||
triangle.clock_length( 0x80 ); // different bit for halt flag on triangle
|
||||
// take frame-specific actions
|
||||
frame_delay = frame_period;
|
||||
switch (frame++)
|
||||
{
|
||||
case 0:
|
||||
if (!(frame_mode & 0xc0))
|
||||
{
|
||||
next_irq = time + frame_period * 4 + 1;
|
||||
irq_flag = true;
|
||||
}
|
||||
// fall through
|
||||
case 2:
|
||||
// clock length and sweep on frames 0 and 2
|
||||
square1.clock_length(0x20);
|
||||
square2.clock_length(0x20);
|
||||
noise.clock_length(0x20);
|
||||
triangle.clock_length(0x80); // different bit for halt flag on triangle
|
||||
|
||||
square1.clock_sweep( -1 );
|
||||
square2.clock_sweep( 0 );
|
||||
break;
|
||||
square1.clock_sweep(-1);
|
||||
square2.clock_sweep(0);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// frame 1 is slightly shorter
|
||||
frame_delay -= 2;
|
||||
break;
|
||||
case 1:
|
||||
// frame 1 is slightly shorter
|
||||
frame_delay -= 2;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
frame = 0;
|
||||
case 3:
|
||||
frame = 0;
|
||||
|
||||
// frame 3 is almost twice as long in mode 1
|
||||
if ( frame_mode & 0x80 )
|
||||
frame_delay += frame_period - 6;
|
||||
break;
|
||||
}
|
||||
// frame 3 is almost twice as long in mode 1
|
||||
if (frame_mode & 0x80)
|
||||
frame_delay += frame_period - 6;
|
||||
break;
|
||||
}
|
||||
|
||||
// clock envelopes and linear counter every frame
|
||||
triangle.clock_linear_counter();
|
||||
square1.clock_envelope();
|
||||
square2.clock_envelope();
|
||||
noise.clock_envelope();
|
||||
}
|
||||
// clock envelopes and linear counter every frame
|
||||
triangle.clock_linear_counter();
|
||||
square1.clock_envelope();
|
||||
square2.clock_envelope();
|
||||
noise.clock_envelope();
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline void zero_apu_osc( T* osc, nes_time_t time )
|
||||
template <class T>
|
||||
inline void zero_apu_osc(T *osc, nes_time_t time)
|
||||
{
|
||||
Blip_Buffer* output = osc->output;
|
||||
int last_amp = osc->last_amp;
|
||||
osc->last_amp = 0;
|
||||
if ( output && last_amp )
|
||||
osc->synth.offset( time, -last_amp, output );
|
||||
Blip_Buffer *output = osc->output;
|
||||
int last_amp = osc->last_amp;
|
||||
osc->last_amp = 0;
|
||||
if (output && last_amp)
|
||||
osc->synth.offset(time, -last_amp, output);
|
||||
}
|
||||
|
||||
void Nes_Apu::end_frame( nes_time_t end_time )
|
||||
void Nes_Apu::end_frame(nes_time_t end_time)
|
||||
{
|
||||
if ( end_time > last_time )
|
||||
run_until_( end_time );
|
||||
if (end_time > last_time)
|
||||
run_until_(end_time);
|
||||
|
||||
if ( dmc.nonlinear )
|
||||
{
|
||||
zero_apu_osc( &square1, last_time );
|
||||
zero_apu_osc( &square2, last_time );
|
||||
zero_apu_osc( &triangle, last_time );
|
||||
zero_apu_osc( &noise, last_time );
|
||||
zero_apu_osc( &dmc, last_time );
|
||||
}
|
||||
if (dmc.nonlinear)
|
||||
{
|
||||
zero_apu_osc(&square1, last_time);
|
||||
zero_apu_osc(&square2, last_time);
|
||||
zero_apu_osc(&triangle, last_time);
|
||||
zero_apu_osc(&noise, last_time);
|
||||
zero_apu_osc(&dmc, last_time);
|
||||
}
|
||||
|
||||
// make times relative to new frame
|
||||
last_time -= end_time;
|
||||
last_dmc_time -= end_time;
|
||||
// make times relative to new frame
|
||||
last_time -= end_time;
|
||||
last_dmc_time -= end_time;
|
||||
|
||||
if ( next_irq != no_irq ) {
|
||||
next_irq -= end_time;
|
||||
}
|
||||
if ( dmc.next_irq != no_irq ) {
|
||||
dmc.next_irq -= end_time;
|
||||
}
|
||||
if ( earliest_irq_ != no_irq ) {
|
||||
earliest_irq_ -= end_time;
|
||||
if ( earliest_irq_ < 0 )
|
||||
earliest_irq_ = 0;
|
||||
}
|
||||
if (next_irq != no_irq)
|
||||
{
|
||||
next_irq -= end_time;
|
||||
}
|
||||
if (dmc.next_irq != no_irq)
|
||||
{
|
||||
dmc.next_irq -= end_time;
|
||||
}
|
||||
if (earliest_irq_ != no_irq)
|
||||
{
|
||||
earliest_irq_ -= end_time;
|
||||
if (earliest_irq_ < 0)
|
||||
earliest_irq_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// registers
|
||||
|
||||
static const unsigned char length_table [0x20] = {
|
||||
0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06,
|
||||
0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E,
|
||||
0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16,
|
||||
0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E
|
||||
};
|
||||
static const unsigned char length_table[0x20] = {
|
||||
0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, 0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E, 0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16, 0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E};
|
||||
|
||||
void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data )
|
||||
void Nes_Apu::write_register(nes_time_t time, nes_addr_t addr, int data)
|
||||
{
|
||||
// Ignore addresses outside range
|
||||
if ( addr < start_addr || end_addr < addr )
|
||||
return;
|
||||
// Ignore addresses outside range
|
||||
if (addr < start_addr || end_addr < addr)
|
||||
return;
|
||||
|
||||
run_until_( time );
|
||||
run_until_(time);
|
||||
|
||||
if ( addr < 0x4014 )
|
||||
{
|
||||
// Write to channel
|
||||
int osc_index = (addr - start_addr) >> 2;
|
||||
Nes_Osc* osc = oscs [osc_index];
|
||||
if (addr < 0x4014)
|
||||
{
|
||||
// Write to channel
|
||||
int osc_index = (addr - start_addr) >> 2;
|
||||
Nes_Osc *osc = oscs[osc_index];
|
||||
|
||||
int reg = addr & 3;
|
||||
osc->regs [reg] = data;
|
||||
osc->reg_written [reg] = true;
|
||||
int reg = addr & 3;
|
||||
osc->regs[reg] = data;
|
||||
osc->reg_written[reg] = true;
|
||||
|
||||
if ( osc_index == 4 )
|
||||
{
|
||||
// handle DMC specially
|
||||
dmc.write_register( reg, data );
|
||||
}
|
||||
else if ( reg == 3 )
|
||||
{
|
||||
// load length counter
|
||||
if ( (osc_enables >> osc_index) & 1 )
|
||||
osc->length_counter = length_table [(data >> 3) & 0x1f];
|
||||
if (osc_index == 4)
|
||||
{
|
||||
// handle DMC specially
|
||||
dmc.write_register(reg, data);
|
||||
}
|
||||
else if (reg == 3)
|
||||
{
|
||||
// load length counter
|
||||
if ((osc_enables >> osc_index) & 1)
|
||||
osc->length_counter = length_table[(data >> 3) & 0x1f];
|
||||
|
||||
// reset square phase
|
||||
if ( osc_index < 2 )
|
||||
((Nes_Square*) osc)->phase = Nes_Square::phase_range - 1;
|
||||
}
|
||||
}
|
||||
else if ( addr == 0x4015 )
|
||||
{
|
||||
// Channel enables
|
||||
for ( int i = osc_count; i--; )
|
||||
if ( !((data >> i) & 1) )
|
||||
oscs [i]->length_counter = 0;
|
||||
// reset square phase
|
||||
if (osc_index < 2)
|
||||
((Nes_Square *)osc)->phase = Nes_Square::phase_range - 1;
|
||||
}
|
||||
}
|
||||
else if (addr == 0x4015)
|
||||
{
|
||||
// Channel enables
|
||||
for (int i = osc_count; i--;)
|
||||
if (!((data >> i) & 1))
|
||||
oscs[i]->length_counter = 0;
|
||||
|
||||
bool recalc_irq = dmc.irq_flag;
|
||||
dmc.irq_flag = false;
|
||||
bool recalc_irq = dmc.irq_flag;
|
||||
dmc.irq_flag = false;
|
||||
|
||||
int old_enables = osc_enables;
|
||||
osc_enables = data;
|
||||
if ( !(data & 0x10) ) {
|
||||
dmc.next_irq = no_irq;
|
||||
recalc_irq = true;
|
||||
}
|
||||
else if ( !(old_enables & 0x10) ) {
|
||||
dmc.start(); // dmc just enabled
|
||||
}
|
||||
int old_enables = osc_enables;
|
||||
osc_enables = data;
|
||||
if (!(data & 0x10))
|
||||
{
|
||||
dmc.next_irq = no_irq;
|
||||
recalc_irq = true;
|
||||
}
|
||||
else if (!(old_enables & 0x10))
|
||||
{
|
||||
dmc.start(); // dmc just enabled
|
||||
}
|
||||
|
||||
if ( recalc_irq )
|
||||
irq_changed();
|
||||
}
|
||||
else if ( addr == 0x4017 )
|
||||
{
|
||||
// Frame mode
|
||||
frame_mode = data;
|
||||
if (recalc_irq)
|
||||
irq_changed();
|
||||
}
|
||||
else if (addr == 0x4017)
|
||||
{
|
||||
// Frame mode
|
||||
frame_mode = data;
|
||||
|
||||
bool irq_enabled = !(data & 0x40);
|
||||
irq_flag &= irq_enabled;
|
||||
next_irq = no_irq;
|
||||
bool irq_enabled = !(data & 0x40);
|
||||
irq_flag &= irq_enabled;
|
||||
next_irq = no_irq;
|
||||
|
||||
// mode 1
|
||||
frame_delay = (frame_delay & 1);
|
||||
frame = 0;
|
||||
// mode 1
|
||||
frame_delay = (frame_delay & 1);
|
||||
frame = 0;
|
||||
|
||||
if ( !(data & 0x80) )
|
||||
{
|
||||
// mode 0
|
||||
frame = 1;
|
||||
frame_delay += frame_period;
|
||||
if ( irq_enabled )
|
||||
next_irq = time + frame_delay + frame_period * 3;
|
||||
}
|
||||
if (!(data & 0x80))
|
||||
{
|
||||
// mode 0
|
||||
frame = 1;
|
||||
frame_delay += frame_period;
|
||||
if (irq_enabled)
|
||||
next_irq = time + frame_delay + frame_period * 3;
|
||||
}
|
||||
|
||||
irq_changed();
|
||||
}
|
||||
irq_changed();
|
||||
}
|
||||
}
|
||||
|
||||
int Nes_Apu::read_status( nes_time_t time )
|
||||
int Nes_Apu::read_status(nes_time_t time)
|
||||
{
|
||||
run_until_( time - 1 );
|
||||
run_until_(time - 1);
|
||||
|
||||
int result = (dmc.irq_flag << 7) | (irq_flag << 6);
|
||||
int result = (dmc.irq_flag << 7) | (irq_flag << 6);
|
||||
|
||||
for ( int i = 0; i < osc_count; i++ )
|
||||
if ( oscs [i]->length_counter )
|
||||
result |= 1 << i;
|
||||
for (int i = 0; i < osc_count; i++)
|
||||
if (oscs[i]->length_counter)
|
||||
result |= 1 << i;
|
||||
|
||||
run_until_( time );
|
||||
run_until_(time);
|
||||
|
||||
if ( irq_flag ) {
|
||||
irq_flag = false;
|
||||
irq_changed();
|
||||
}
|
||||
if (irq_flag)
|
||||
{
|
||||
irq_flag = false;
|
||||
irq_changed();
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -3,353 +3,356 @@
|
|||
// NES 2A03 APU sound chip emulator
|
||||
// Nes_Snd_Emu 0.1.7
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdint>
|
||||
#include <climits>
|
||||
#include "Nes_Oscs.hpp"
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
|
||||
class Nes_Apu {
|
||||
public:
|
||||
class Nes_Apu
|
||||
{
|
||||
public:
|
||||
typedef uint8_t env_t[3];
|
||||
/*struct env_t {
|
||||
uint8_t delay;
|
||||
uint8_t env;
|
||||
uint8_t written;
|
||||
};*/
|
||||
|
||||
typedef uint8_t env_t [3];
|
||||
/*struct env_t {
|
||||
uint8_t delay;
|
||||
uint8_t env;
|
||||
uint8_t written;
|
||||
};*/
|
||||
struct apu_t
|
||||
{
|
||||
uint8_t w40xx[0x14]; // $4000-$4013
|
||||
uint8_t w4015; // enables
|
||||
uint8_t w4017; // mode
|
||||
uint16_t frame_delay;
|
||||
uint8_t frame_step;
|
||||
uint8_t irq_flag;
|
||||
};
|
||||
|
||||
struct apu_t {
|
||||
uint8_t w40xx [0x14]; // $4000-$4013
|
||||
uint8_t w4015; // enables
|
||||
uint8_t w4017; // mode
|
||||
uint16_t frame_delay;
|
||||
uint8_t frame_step;
|
||||
uint8_t irq_flag;
|
||||
};
|
||||
struct square_t
|
||||
{
|
||||
uint16_t delay;
|
||||
env_t env;
|
||||
uint8_t length_counter;
|
||||
uint8_t phase;
|
||||
uint8_t swp_delay;
|
||||
uint8_t swp_reset;
|
||||
uint8_t unused2[1];
|
||||
};
|
||||
|
||||
struct square_t {
|
||||
uint16_t delay;
|
||||
env_t env;
|
||||
uint8_t length_counter;
|
||||
uint8_t phase;
|
||||
uint8_t swp_delay;
|
||||
uint8_t swp_reset;
|
||||
uint8_t unused2 [1];
|
||||
};
|
||||
struct triangle_t
|
||||
{
|
||||
uint16_t delay;
|
||||
uint8_t length_counter;
|
||||
uint8_t phase;
|
||||
uint8_t linear_counter;
|
||||
uint8_t linear_mode;
|
||||
};
|
||||
|
||||
struct triangle_t {
|
||||
uint16_t delay;
|
||||
uint8_t length_counter;
|
||||
uint8_t phase;
|
||||
uint8_t linear_counter;
|
||||
uint8_t linear_mode;
|
||||
};
|
||||
struct noise_t
|
||||
{
|
||||
uint16_t delay;
|
||||
env_t env;
|
||||
uint8_t length_counter;
|
||||
uint16_t shift_reg;
|
||||
};
|
||||
|
||||
struct noise_t {
|
||||
uint16_t delay;
|
||||
env_t env;
|
||||
uint8_t length_counter;
|
||||
uint16_t shift_reg;
|
||||
};
|
||||
struct dmc_t
|
||||
{
|
||||
uint16_t delay;
|
||||
uint16_t remain;
|
||||
uint16_t addr;
|
||||
uint8_t buf;
|
||||
uint8_t bits_remain;
|
||||
uint8_t bits;
|
||||
uint8_t buf_full;
|
||||
uint8_t silence;
|
||||
uint8_t irq_flag;
|
||||
};
|
||||
|
||||
struct dmc_t {
|
||||
uint16_t delay;
|
||||
uint16_t remain;
|
||||
uint16_t addr;
|
||||
uint8_t buf;
|
||||
uint8_t bits_remain;
|
||||
uint8_t bits;
|
||||
uint8_t buf_full;
|
||||
uint8_t silence;
|
||||
uint8_t irq_flag;
|
||||
};
|
||||
struct apu_state_t
|
||||
{
|
||||
apu_t apu;
|
||||
square_t square1;
|
||||
square_t square2;
|
||||
triangle_t triangle;
|
||||
noise_t noise;
|
||||
dmc_t dmc;
|
||||
};
|
||||
static_assert(sizeof(apu_state_t) == 72);
|
||||
|
||||
struct apu_state_t
|
||||
{
|
||||
apu_t apu;
|
||||
square_t square1;
|
||||
square_t square2;
|
||||
triangle_t triangle;
|
||||
noise_t noise;
|
||||
dmc_t dmc;
|
||||
};
|
||||
static_assert( sizeof (apu_state_t) == 72 );
|
||||
Nes_Apu();
|
||||
~Nes_Apu();
|
||||
|
||||
Nes_Apu();
|
||||
~Nes_Apu();
|
||||
// Set buffer to generate all sound into, or disable sound if NULL
|
||||
void output(Blip_Buffer *);
|
||||
|
||||
// Set buffer to generate all sound into, or disable sound if NULL
|
||||
void output( Blip_Buffer* );
|
||||
// Set memory reader callback used by DMC oscillator to fetch samples.
|
||||
// When callback is invoked, 'user_data' is passed unchanged as the
|
||||
// first parameter.
|
||||
void dmc_reader(int (*callback)(void *user_data, nes_addr_t), void *user_data = nullptr);
|
||||
|
||||
// Set memory reader callback used by DMC oscillator to fetch samples.
|
||||
// When callback is invoked, 'user_data' is passed unchanged as the
|
||||
// first parameter.
|
||||
void dmc_reader( int (*callback)( void* user_data, nes_addr_t ), void* user_data = nullptr );
|
||||
// All time values are the number of CPU clock cycles relative to the
|
||||
// beginning of the current time frame. Before resetting the CPU clock
|
||||
// count, call end_frame( last_cpu_time ).
|
||||
|
||||
// All time values are the number of CPU clock cycles relative to the
|
||||
// beginning of the current time frame. Before resetting the CPU clock
|
||||
// count, call end_frame( last_cpu_time ).
|
||||
// Write to register (0x4000-0x4017, except 0x4014 and 0x4016)
|
||||
static const uint16_t start_addr = 0x4000;
|
||||
static const uint16_t end_addr = 0x4017;
|
||||
void write_register(nes_time_t, nes_addr_t, int data);
|
||||
|
||||
// Write to register (0x4000-0x4017, except 0x4014 and 0x4016)
|
||||
static const uint16_t start_addr = 0x4000;
|
||||
static const uint16_t end_addr = 0x4017;
|
||||
void write_register( nes_time_t, nes_addr_t, int data );
|
||||
// Read from status register at 0x4015
|
||||
static const uint16_t status_addr = 0x4015;
|
||||
int read_status(nes_time_t);
|
||||
|
||||
// Read from status register at 0x4015
|
||||
static const uint16_t status_addr = 0x4015;
|
||||
int read_status( nes_time_t );
|
||||
// Run all oscillators up to specified time, end current time frame, then
|
||||
// start a new time frame at time 0. Time frames have no effect on emulation
|
||||
// and each can be whatever length is convenient.
|
||||
void end_frame(nes_time_t);
|
||||
|
||||
// Run all oscillators up to specified time, end current time frame, then
|
||||
// start a new time frame at time 0. Time frames have no effect on emulation
|
||||
// and each can be whatever length is convenient.
|
||||
void end_frame( nes_time_t );
|
||||
// Additional optional features (can be ignored without any problem)
|
||||
|
||||
// Additional optional features (can be ignored without any problem)
|
||||
// Reset internal frame counter, registers, and all oscillators.
|
||||
// Use PAL timing if pal_timing is true, otherwise use NTSC timing.
|
||||
// Set the DMC oscillator's initial DAC value to initial_dmc_dac without
|
||||
// any audible click.
|
||||
void reset(bool pal_timing = false, int initial_dmc_dac = 0);
|
||||
|
||||
// Reset internal frame counter, registers, and all oscillators.
|
||||
// Use PAL timing if pal_timing is true, otherwise use NTSC timing.
|
||||
// Set the DMC oscillator's initial DAC value to initial_dmc_dac without
|
||||
// any audible click.
|
||||
void reset( bool pal_timing = false, int initial_dmc_dac = 0 );
|
||||
// Save/load exact emulation state
|
||||
void save_state(apu_state_t *out) const;
|
||||
void load_state(apu_state_t const &);
|
||||
|
||||
// Save/load exact emulation state
|
||||
void save_state( apu_state_t* out ) const;
|
||||
void load_state( apu_state_t const& );
|
||||
// Set overall volume (default is 1.0)
|
||||
void volume(double);
|
||||
|
||||
// Set overall volume (default is 1.0)
|
||||
void volume( double );
|
||||
// Set treble equalization (see notes.txt)
|
||||
void treble_eq(const blip_eq_t &);
|
||||
|
||||
// Set treble equalization (see notes.txt)
|
||||
void treble_eq( const blip_eq_t& );
|
||||
// Set sound output of specific oscillator to buffer. If buffer is NULL,
|
||||
// the specified oscillator is muted and emulation accuracy is reduced.
|
||||
// The oscillators are indexed as follows: 0) Square 1, 1) Square 2,
|
||||
// 2) Triangle, 3) Noise, 4) DMC.
|
||||
static const uint16_t osc_count = 5;
|
||||
void osc_output(int index, Blip_Buffer *buffer);
|
||||
|
||||
// Set sound output of specific oscillator to buffer. If buffer is NULL,
|
||||
// the specified oscillator is muted and emulation accuracy is reduced.
|
||||
// The oscillators are indexed as follows: 0) Square 1, 1) Square 2,
|
||||
// 2) Triangle, 3) Noise, 4) DMC.
|
||||
static const uint16_t osc_count = 5;
|
||||
void osc_output( int index, Blip_Buffer* buffer );
|
||||
// Set IRQ time callback that is invoked when the time of earliest IRQ
|
||||
// may have changed, or NULL to disable. When callback is invoked,
|
||||
// 'user_data' is passed unchanged as the first parameter.
|
||||
void irq_notifier(void (*callback)(void *user_data), void *user_data = nullptr);
|
||||
|
||||
// Set IRQ time callback that is invoked when the time of earliest IRQ
|
||||
// may have changed, or NULL to disable. When callback is invoked,
|
||||
// 'user_data' is passed unchanged as the first parameter.
|
||||
void irq_notifier( void (*callback)( void* user_data ), void* user_data = nullptr );
|
||||
// Get time that APU-generated IRQ will occur if no further register reads
|
||||
// or writes occur. If IRQ is already pending, returns irq_waiting. If no
|
||||
// IRQ will occur, returns no_irq.
|
||||
static const uint64_t no_irq = LONG_MAX / 2 + 1;
|
||||
static const uint16_t irq_waiting = 0;
|
||||
nes_time_t earliest_irq(nes_time_t) const;
|
||||
|
||||
// Get time that APU-generated IRQ will occur if no further register reads
|
||||
// or writes occur. If IRQ is already pending, returns irq_waiting. If no
|
||||
// IRQ will occur, returns no_irq.
|
||||
static const uint64_t no_irq = LONG_MAX / 2 + 1;
|
||||
static const uint16_t irq_waiting = 0;
|
||||
nes_time_t earliest_irq( nes_time_t ) const;
|
||||
// Count number of DMC reads that would occur if 'run_until( t )' were executed.
|
||||
// If last_read is not NULL, set *last_read to the earliest time that
|
||||
// 'count_dmc_reads( time )' would result in the same result.
|
||||
int count_dmc_reads(nes_time_t t, nes_time_t *last_read = nullptr) const;
|
||||
|
||||
// Count number of DMC reads that would occur if 'run_until( t )' were executed.
|
||||
// If last_read is not NULL, set *last_read to the earliest time that
|
||||
// 'count_dmc_reads( time )' would result in the same result.
|
||||
int count_dmc_reads( nes_time_t t, nes_time_t* last_read = nullptr ) const;
|
||||
// Time when next DMC memory read will occur
|
||||
nes_time_t next_dmc_read_time() const;
|
||||
|
||||
// Time when next DMC memory read will occur
|
||||
nes_time_t next_dmc_read_time() const;
|
||||
// Run DMC until specified time, so that any DMC memory reads can be
|
||||
// accounted for (i.e. inserting CPU wait states).
|
||||
void run_until(nes_time_t);
|
||||
|
||||
// Run DMC until specified time, so that any DMC memory reads can be
|
||||
// accounted for (i.e. inserting CPU wait states).
|
||||
void run_until( nes_time_t );
|
||||
// End of public interface.
|
||||
private:
|
||||
friend class Nes_Nonlinearizer;
|
||||
void enable_nonlinear(double volume);
|
||||
static double nonlinear_tnd_gain() { return 0.75; }
|
||||
|
||||
// End of public interface.
|
||||
private:
|
||||
friend class Nes_Nonlinearizer;
|
||||
void enable_nonlinear( double volume );
|
||||
static double nonlinear_tnd_gain() { return 0.75; }
|
||||
private:
|
||||
friend struct Nes_Dmc;
|
||||
private:
|
||||
friend struct Nes_Dmc;
|
||||
|
||||
// noncopyable
|
||||
Nes_Apu( const Nes_Apu& );
|
||||
Nes_Apu& operator = ( const Nes_Apu& );
|
||||
// noncopyable
|
||||
Nes_Apu(const Nes_Apu &);
|
||||
Nes_Apu &operator=(const Nes_Apu &);
|
||||
|
||||
Nes_Osc* oscs [osc_count];
|
||||
Nes_Square square1;
|
||||
Nes_Square square2;
|
||||
Nes_Noise noise;
|
||||
Nes_Triangle triangle;
|
||||
Nes_Dmc dmc;
|
||||
Nes_Osc *oscs[osc_count];
|
||||
Nes_Square square1;
|
||||
Nes_Square square2;
|
||||
Nes_Noise noise;
|
||||
Nes_Triangle triangle;
|
||||
Nes_Dmc dmc;
|
||||
|
||||
nes_time_t last_time; // has been run until this time in current frame
|
||||
nes_time_t last_dmc_time;
|
||||
nes_time_t earliest_irq_;
|
||||
nes_time_t next_irq;
|
||||
int frame_period;
|
||||
int frame_delay; // cycles until frame counter runs next
|
||||
int frame; // current frame (0-3)
|
||||
int osc_enables;
|
||||
int frame_mode;
|
||||
bool irq_flag;
|
||||
void (*irq_notifier_)( void* user_data );
|
||||
void* irq_data;
|
||||
Nes_Square::Synth square_synth; // shared by squares
|
||||
nes_time_t last_time; // has been run until this time in current frame
|
||||
nes_time_t last_dmc_time;
|
||||
nes_time_t earliest_irq_;
|
||||
nes_time_t next_irq;
|
||||
int frame_period;
|
||||
int frame_delay; // cycles until frame counter runs next
|
||||
int frame; // current frame (0-3)
|
||||
int osc_enables;
|
||||
int frame_mode;
|
||||
bool irq_flag;
|
||||
void (*irq_notifier_)(void *user_data);
|
||||
void *irq_data;
|
||||
Nes_Square::Synth square_synth; // shared by squares
|
||||
|
||||
void irq_changed();
|
||||
void state_restored();
|
||||
void run_until_( nes_time_t );
|
||||
void irq_changed();
|
||||
void state_restored();
|
||||
void run_until_(nes_time_t);
|
||||
|
||||
// TODO: remove
|
||||
friend class Nes_Core;
|
||||
// TODO: remove
|
||||
friend class Nes_Core;
|
||||
};
|
||||
|
||||
inline void Nes_Apu::osc_output( int osc, Blip_Buffer* buf )
|
||||
inline void Nes_Apu::osc_output(int osc, Blip_Buffer *buf)
|
||||
{
|
||||
oscs [osc]->output = buf;
|
||||
oscs[osc]->output = buf;
|
||||
}
|
||||
|
||||
inline nes_time_t Nes_Apu::earliest_irq( nes_time_t ) const
|
||||
inline nes_time_t Nes_Apu::earliest_irq(nes_time_t) const
|
||||
{
|
||||
return earliest_irq_;
|
||||
return earliest_irq_;
|
||||
}
|
||||
|
||||
inline void Nes_Apu::dmc_reader( int (*func)( void*, nes_addr_t ), void* user_data )
|
||||
inline void Nes_Apu::dmc_reader(int (*func)(void *, nes_addr_t), void *user_data)
|
||||
{
|
||||
dmc.prg_reader_data = user_data;
|
||||
dmc.prg_reader = func;
|
||||
dmc.prg_reader_data = user_data;
|
||||
dmc.prg_reader = func;
|
||||
}
|
||||
|
||||
inline void Nes_Apu::irq_notifier( void (*func)( void* user_data ), void* user_data )
|
||||
inline void Nes_Apu::irq_notifier(void (*func)(void *user_data), void *user_data)
|
||||
{
|
||||
irq_notifier_ = func;
|
||||
irq_data = user_data;
|
||||
irq_notifier_ = func;
|
||||
irq_data = user_data;
|
||||
}
|
||||
|
||||
inline int Nes_Apu::count_dmc_reads( nes_time_t time, nes_time_t* last_read ) const
|
||||
inline int Nes_Apu::count_dmc_reads(nes_time_t time, nes_time_t *last_read) const
|
||||
{
|
||||
return dmc.count_reads( time, last_read );
|
||||
return dmc.count_reads(time, last_read);
|
||||
}
|
||||
|
||||
inline nes_time_t Nes_Dmc::next_read_time() const
|
||||
{
|
||||
if ( length_counter == 0 )
|
||||
return Nes_Apu::no_irq; // not reading
|
||||
if (length_counter == 0)
|
||||
return Nes_Apu::no_irq; // not reading
|
||||
|
||||
return apu->last_dmc_time + delay + long (bits_remain - 1) * period;
|
||||
return apu->last_dmc_time + delay + long(bits_remain - 1) * period;
|
||||
}
|
||||
|
||||
inline nes_time_t Nes_Apu::next_dmc_read_time() const { return dmc.next_read_time(); }
|
||||
|
||||
|
||||
template<int mode>
|
||||
template <int mode>
|
||||
struct apu_reflection
|
||||
{
|
||||
#define REFLECT( apu, state ) (mode ? void (apu = state) : void (state = apu))
|
||||
#define REFLECT(apu, state) (mode ? void(apu = state) : void(state = apu))
|
||||
|
||||
static void reflect_env( Nes_Apu::env_t* state, Nes_Envelope& osc )
|
||||
{
|
||||
REFLECT( (*state) [0], osc.env_delay );
|
||||
REFLECT( (*state) [1], osc.envelope );
|
||||
REFLECT( (*state) [2], osc.reg_written [3] );
|
||||
}
|
||||
static void reflect_env(Nes_Apu::env_t *state, Nes_Envelope &osc)
|
||||
{
|
||||
REFLECT((*state)[0], osc.env_delay);
|
||||
REFLECT((*state)[1], osc.envelope);
|
||||
REFLECT((*state)[2], osc.reg_written[3]);
|
||||
}
|
||||
|
||||
static void reflect_square( Nes_Apu::square_t& state, Nes_Square& osc )
|
||||
{
|
||||
reflect_env( &state.env, osc );
|
||||
REFLECT( state.delay, osc.delay );
|
||||
REFLECT( state.length_counter, osc.length_counter );
|
||||
REFLECT( state.phase, osc.phase );
|
||||
REFLECT( state.swp_delay, osc.sweep_delay );
|
||||
REFLECT( state.swp_reset, osc.reg_written [1] );
|
||||
}
|
||||
static void reflect_square(Nes_Apu::square_t &state, Nes_Square &osc)
|
||||
{
|
||||
reflect_env(&state.env, osc);
|
||||
REFLECT(state.delay, osc.delay);
|
||||
REFLECT(state.length_counter, osc.length_counter);
|
||||
REFLECT(state.phase, osc.phase);
|
||||
REFLECT(state.swp_delay, osc.sweep_delay);
|
||||
REFLECT(state.swp_reset, osc.reg_written[1]);
|
||||
}
|
||||
|
||||
static void reflect_triangle( Nes_Apu::triangle_t& state, Nes_Triangle& osc )
|
||||
{
|
||||
REFLECT( state.delay, osc.delay );
|
||||
REFLECT( state.length_counter, osc.length_counter );
|
||||
REFLECT( state.linear_counter, osc.linear_counter );
|
||||
REFLECT( state.phase, osc.phase );
|
||||
REFLECT( state.linear_mode, osc.reg_written [3] );
|
||||
}
|
||||
static void reflect_triangle(Nes_Apu::triangle_t &state, Nes_Triangle &osc)
|
||||
{
|
||||
REFLECT(state.delay, osc.delay);
|
||||
REFLECT(state.length_counter, osc.length_counter);
|
||||
REFLECT(state.linear_counter, osc.linear_counter);
|
||||
REFLECT(state.phase, osc.phase);
|
||||
REFLECT(state.linear_mode, osc.reg_written[3]);
|
||||
}
|
||||
|
||||
static void reflect_noise( Nes_Apu::noise_t& state, Nes_Noise& osc )
|
||||
{
|
||||
reflect_env( &state.env, osc );
|
||||
REFLECT( state.delay, osc.delay );
|
||||
REFLECT( state.length_counter, osc.length_counter );
|
||||
REFLECT( state.shift_reg, osc.noise );
|
||||
}
|
||||
static void reflect_noise(Nes_Apu::noise_t &state, Nes_Noise &osc)
|
||||
{
|
||||
reflect_env(&state.env, osc);
|
||||
REFLECT(state.delay, osc.delay);
|
||||
REFLECT(state.length_counter, osc.length_counter);
|
||||
REFLECT(state.shift_reg, osc.noise);
|
||||
}
|
||||
|
||||
static void reflect_dmc( Nes_Apu::dmc_t& state, Nes_Dmc& osc )
|
||||
{
|
||||
REFLECT( state.delay, osc.delay );
|
||||
REFLECT( state.remain, osc.length_counter );
|
||||
REFLECT( state.buf, osc.buf );
|
||||
REFLECT( state.bits_remain, osc.bits_remain );
|
||||
REFLECT( state.bits, osc.bits );
|
||||
REFLECT( state.buf_full, osc.buf_full );
|
||||
REFLECT( state.silence, osc.silence );
|
||||
REFLECT( state.irq_flag, osc.irq_flag );
|
||||
if ( mode )
|
||||
state.addr = osc.address | 0x8000;
|
||||
else
|
||||
osc.address = state.addr & 0x7fff;
|
||||
}
|
||||
static void reflect_dmc(Nes_Apu::dmc_t &state, Nes_Dmc &osc)
|
||||
{
|
||||
REFLECT(state.delay, osc.delay);
|
||||
REFLECT(state.remain, osc.length_counter);
|
||||
REFLECT(state.buf, osc.buf);
|
||||
REFLECT(state.bits_remain, osc.bits_remain);
|
||||
REFLECT(state.bits, osc.bits);
|
||||
REFLECT(state.buf_full, osc.buf_full);
|
||||
REFLECT(state.silence, osc.silence);
|
||||
REFLECT(state.irq_flag, osc.irq_flag);
|
||||
if (mode)
|
||||
state.addr = osc.address | 0x8000;
|
||||
else
|
||||
osc.address = state.addr & 0x7fff;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
inline void Nes_Apu::save_state( apu_state_t* state ) const
|
||||
inline void Nes_Apu::save_state(apu_state_t *state) const
|
||||
{
|
||||
for ( int i = 0; i < osc_count * 4; i++ )
|
||||
{
|
||||
int index = i >> 2;
|
||||
state->apu.w40xx [i] = oscs [index]->regs [i & 3];
|
||||
//if ( index < 4 )
|
||||
// state->length_counters [index] = oscs [index]->length_counter;
|
||||
}
|
||||
state->apu.w40xx [0x11] = dmc.dac;
|
||||
for (int i = 0; i < osc_count * 4; i++)
|
||||
{
|
||||
int index = i >> 2;
|
||||
state->apu.w40xx[i] = oscs[index]->regs[i & 3];
|
||||
// if ( index < 4 )
|
||||
// state->length_counters [index] = oscs [index]->length_counter;
|
||||
}
|
||||
state->apu.w40xx[0x11] = dmc.dac;
|
||||
|
||||
state->apu.w4015 = osc_enables;
|
||||
state->apu.w4017 = frame_mode;
|
||||
state->apu.frame_delay = frame_delay;
|
||||
state->apu.frame_step = frame;
|
||||
state->apu.irq_flag = irq_flag;
|
||||
state->apu.w4015 = osc_enables;
|
||||
state->apu.w4017 = frame_mode;
|
||||
state->apu.frame_delay = frame_delay;
|
||||
state->apu.frame_step = frame;
|
||||
state->apu.irq_flag = irq_flag;
|
||||
|
||||
typedef apu_reflection<1> refl;
|
||||
Nes_Apu& apu = *(Nes_Apu*) this; // const_cast
|
||||
refl::reflect_square ( state->square1, apu.square1 );
|
||||
refl::reflect_square ( state->square2, apu.square2 );
|
||||
refl::reflect_triangle( state->triangle, apu.triangle );
|
||||
refl::reflect_noise ( state->noise, apu.noise );
|
||||
refl::reflect_dmc ( state->dmc, apu.dmc );
|
||||
typedef apu_reflection<1> refl;
|
||||
Nes_Apu &apu = *(Nes_Apu *)this; // const_cast
|
||||
refl::reflect_square(state->square1, apu.square1);
|
||||
refl::reflect_square(state->square2, apu.square2);
|
||||
refl::reflect_triangle(state->triangle, apu.triangle);
|
||||
refl::reflect_noise(state->noise, apu.noise);
|
||||
refl::reflect_dmc(state->dmc, apu.dmc);
|
||||
}
|
||||
|
||||
inline void Nes_Apu::load_state( apu_state_t const& state )
|
||||
inline void Nes_Apu::load_state(apu_state_t const &state)
|
||||
{
|
||||
reset();
|
||||
reset();
|
||||
|
||||
write_register( 0, 0x4017, state.apu.w4017 );
|
||||
write_register( 0, 0x4015, state.apu.w4015 );
|
||||
osc_enables = state.apu.w4015; // DMC clears bit 4
|
||||
write_register(0, 0x4017, state.apu.w4017);
|
||||
write_register(0, 0x4015, state.apu.w4015);
|
||||
osc_enables = state.apu.w4015; // DMC clears bit 4
|
||||
|
||||
for ( int i = 0; i < osc_count * 4; i++ )
|
||||
{
|
||||
int n = state.apu.w40xx [i];
|
||||
int index = i >> 2;
|
||||
oscs [index]->regs [i & 3] = n;
|
||||
write_register( 0, 0x4000 + i, n );
|
||||
//if ( index < 4 )
|
||||
// oscs [index]->length_counter = state.length_counters [index];
|
||||
}
|
||||
for (int i = 0; i < osc_count * 4; i++)
|
||||
{
|
||||
int n = state.apu.w40xx[i];
|
||||
int index = i >> 2;
|
||||
oscs[index]->regs[i & 3] = n;
|
||||
write_register(0, 0x4000 + i, n);
|
||||
// if ( index < 4 )
|
||||
// oscs [index]->length_counter = state.length_counters [index];
|
||||
}
|
||||
|
||||
frame_delay = state.apu.frame_delay;
|
||||
frame = state.apu.frame_step;
|
||||
irq_flag = state.apu.irq_flag;
|
||||
frame_delay = state.apu.frame_delay;
|
||||
frame = state.apu.frame_step;
|
||||
irq_flag = state.apu.irq_flag;
|
||||
|
||||
typedef apu_reflection<0> refl;
|
||||
apu_state_t& st = (apu_state_t&) state; // const_cast
|
||||
refl::reflect_square ( st.square1, square1 );
|
||||
refl::reflect_square ( st.square2, square2 );
|
||||
refl::reflect_triangle( st.triangle, triangle );
|
||||
refl::reflect_noise ( st.noise, noise );
|
||||
refl::reflect_dmc ( st.dmc, dmc );
|
||||
dmc.recalc_irq();
|
||||
typedef apu_reflection<0> refl;
|
||||
apu_state_t &st = (apu_state_t &)state; // const_cast
|
||||
refl::reflect_square(st.square1, square1);
|
||||
refl::reflect_square(st.square2, square2);
|
||||
refl::reflect_triangle(st.triangle, triangle);
|
||||
refl::reflect_noise(st.noise, noise);
|
||||
refl::reflect_dmc(st.dmc, dmc);
|
||||
dmc.recalc_irq();
|
||||
|
||||
//force channels to have correct last_amp levels after load state
|
||||
square1.run(last_time, last_time);
|
||||
square2.run(last_time, last_time);
|
||||
triangle.run(last_time, last_time);
|
||||
noise.run(last_time, last_time);
|
||||
dmc.run(last_time, last_time);
|
||||
// force channels to have correct last_amp levels after load state
|
||||
square1.run(last_time, last_time);
|
||||
square2.run(last_time, last_time);
|
||||
triangle.run(last_time, last_time);
|
||||
noise.run(last_time, last_time);
|
||||
dmc.run(last_time, last_time);
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
// Nes_Emu 0.7.0. http://www.slack.net/~ant/
|
||||
|
||||
#include <cstring>
|
||||
#include "apu/fme7/apu.hpp"
|
||||
#include <cstring>
|
||||
|
||||
/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
|
@ -17,91 +17,87 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
void Nes_Fme7_Apu::reset()
|
||||
{
|
||||
last_time = 0;
|
||||
last_time = 0;
|
||||
|
||||
for ( int i = 0; i < osc_count; i++ )
|
||||
oscs [i].last_amp = 0;
|
||||
for (int i = 0; i < osc_count; i++)
|
||||
oscs[i].last_amp = 0;
|
||||
|
||||
fme7_apu_state_t* state = this;
|
||||
memset( state, 0, sizeof *state );
|
||||
fme7_apu_state_t *state = this;
|
||||
memset(state, 0, sizeof *state);
|
||||
}
|
||||
|
||||
unsigned char Nes_Fme7_Apu::amp_table [16] =
|
||||
{
|
||||
#define ENTRY( n ) (unsigned char) (n * +amp_range + 0.5)
|
||||
ENTRY(0.0000), ENTRY(0.0078), ENTRY(0.0110), ENTRY(0.0156),
|
||||
ENTRY(0.0221), ENTRY(0.0312), ENTRY(0.0441), ENTRY(0.0624),
|
||||
ENTRY(0.0883), ENTRY(0.1249), ENTRY(0.1766), ENTRY(0.2498),
|
||||
ENTRY(0.3534), ENTRY(0.4998), ENTRY(0.7070), ENTRY(1.0000)
|
||||
#undef ENTRY
|
||||
unsigned char Nes_Fme7_Apu::amp_table[16] =
|
||||
{
|
||||
#define ENTRY(n) (unsigned char)(n * +amp_range + 0.5)
|
||||
ENTRY(0.0000), ENTRY(0.0078), ENTRY(0.0110), ENTRY(0.0156), ENTRY(0.0221), ENTRY(0.0312), ENTRY(0.0441), ENTRY(0.0624), ENTRY(0.0883), ENTRY(0.1249), ENTRY(0.1766), ENTRY(0.2498), ENTRY(0.3534), ENTRY(0.4998), ENTRY(0.7070), ENTRY(1.0000)
|
||||
#undef ENTRY
|
||||
};
|
||||
|
||||
void Nes_Fme7_Apu::run_until( blip_time_t end_time )
|
||||
void Nes_Fme7_Apu::run_until(blip_time_t end_time)
|
||||
{
|
||||
for ( int index = 0; index < osc_count; index++ )
|
||||
{
|
||||
int mode = regs [7] >> index;
|
||||
int vol_mode = regs [010 + index];
|
||||
int volume = amp_table [vol_mode & 0x0f];
|
||||
for (int index = 0; index < osc_count; index++)
|
||||
{
|
||||
int mode = regs[7] >> index;
|
||||
int vol_mode = regs[010 + index];
|
||||
int volume = amp_table[vol_mode & 0x0f];
|
||||
|
||||
if ( !oscs [index].output )
|
||||
continue;
|
||||
if (!oscs[index].output)
|
||||
continue;
|
||||
|
||||
if ( (mode & 001) | (vol_mode & 0x10) )
|
||||
volume = 0; // noise and envelope aren't supported
|
||||
if ((mode & 001) | (vol_mode & 0x10))
|
||||
volume = 0; // noise and envelope aren't supported
|
||||
|
||||
// period
|
||||
int const period_factor = 16;
|
||||
unsigned period = (regs [index * 2 + 1] & 0x0f) * 0x100 * period_factor +
|
||||
regs [index * 2] * period_factor;
|
||||
if ( period < 50 ) // around 22 kHz
|
||||
{
|
||||
volume = 0;
|
||||
if ( !period ) // on my AY-3-8910A, period doesn't have extra one added
|
||||
period = period_factor;
|
||||
}
|
||||
// period
|
||||
int const period_factor = 16;
|
||||
unsigned period = (regs[index * 2 + 1] & 0x0f) * 0x100 * period_factor +
|
||||
regs[index * 2] * period_factor;
|
||||
if (period < 50) // around 22 kHz
|
||||
{
|
||||
volume = 0;
|
||||
if (!period) // on my AY-3-8910A, period doesn't have extra one added
|
||||
period = period_factor;
|
||||
}
|
||||
|
||||
// current amplitude
|
||||
int amp = volume;
|
||||
if ( !phases [index] )
|
||||
amp = 0;
|
||||
int delta = amp - oscs [index].last_amp;
|
||||
if ( delta )
|
||||
{
|
||||
oscs [index].last_amp = amp;
|
||||
synth.offset( last_time, delta, oscs [index].output );
|
||||
}
|
||||
// current amplitude
|
||||
int amp = volume;
|
||||
if (!phases[index])
|
||||
amp = 0;
|
||||
int delta = amp - oscs[index].last_amp;
|
||||
if (delta)
|
||||
{
|
||||
oscs[index].last_amp = amp;
|
||||
synth.offset(last_time, delta, oscs[index].output);
|
||||
}
|
||||
|
||||
blip_time_t time = last_time + delays [index];
|
||||
if ( time < end_time )
|
||||
{
|
||||
Blip_Buffer* const osc_output = oscs [index].output;
|
||||
int delta = amp * 2 - volume;
|
||||
blip_time_t time = last_time + delays[index];
|
||||
if (time < end_time)
|
||||
{
|
||||
Blip_Buffer *const osc_output = oscs[index].output;
|
||||
int delta = amp * 2 - volume;
|
||||
|
||||
if ( volume )
|
||||
{
|
||||
do
|
||||
{
|
||||
delta = -delta;
|
||||
synth.offset_inline( time, delta, osc_output );
|
||||
time += period;
|
||||
}
|
||||
while ( time < end_time );
|
||||
if (volume)
|
||||
{
|
||||
do
|
||||
{
|
||||
delta = -delta;
|
||||
synth.offset_inline(time, delta, osc_output);
|
||||
time += period;
|
||||
} while (time < end_time);
|
||||
|
||||
oscs [index].last_amp = (delta + volume) >> 1;
|
||||
phases [index] = (delta > 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// maintain phase when silent
|
||||
int count = (end_time - time + period - 1) / period;
|
||||
phases [index] ^= count & 1;
|
||||
time += (long) count * period;
|
||||
}
|
||||
}
|
||||
oscs[index].last_amp = (delta + volume) >> 1;
|
||||
phases[index] = (delta > 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// maintain phase when silent
|
||||
int count = (end_time - time + period - 1) / period;
|
||||
phases[index] ^= count & 1;
|
||||
time += (long)count * period;
|
||||
}
|
||||
}
|
||||
|
||||
delays [index] = time - end_time;
|
||||
}
|
||||
delays[index] = time - end_time;
|
||||
}
|
||||
|
||||
last_time = end_time;
|
||||
last_time = end_time;
|
||||
}
|
||||
|
|
|
@ -3,129 +3,148 @@
|
|||
// Sunsoft FME-7 sound emulator
|
||||
// Nes_Emu 0.7.0
|
||||
|
||||
#include <cstdint>
|
||||
#include "apu/Blip_Buffer.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
struct fme7_apu_state_t
|
||||
{
|
||||
enum { reg_count = 14 };
|
||||
uint8_t regs [reg_count];
|
||||
uint8_t phases [3]; // 0 or 1
|
||||
uint8_t latch;
|
||||
uint16_t delays [3]; // a, b, c
|
||||
enum
|
||||
{
|
||||
reg_count = 14
|
||||
};
|
||||
uint8_t regs[reg_count];
|
||||
uint8_t phases[3]; // 0 or 1
|
||||
uint8_t latch;
|
||||
uint16_t delays[3]; // a, b, c
|
||||
};
|
||||
static_assert( sizeof (fme7_apu_state_t) == 24 );
|
||||
static_assert(sizeof(fme7_apu_state_t) == 24);
|
||||
|
||||
class Nes_Fme7_Apu : private fme7_apu_state_t {
|
||||
public:
|
||||
Nes_Fme7_Apu();
|
||||
class Nes_Fme7_Apu : private fme7_apu_state_t
|
||||
{
|
||||
public:
|
||||
Nes_Fme7_Apu();
|
||||
|
||||
// See Nes_Apu.h for reference
|
||||
void reset();
|
||||
void volume( double );
|
||||
void treble_eq( blip_eq_t const& );
|
||||
void output( Blip_Buffer* );
|
||||
enum { osc_count = 3 };
|
||||
void osc_output( int index, Blip_Buffer* );
|
||||
void end_frame( blip_time_t );
|
||||
void save_state( fme7_apu_state_t* ) const;
|
||||
void load_state( fme7_apu_state_t const& );
|
||||
// See Nes_Apu.h for reference
|
||||
void reset();
|
||||
void volume(double);
|
||||
void treble_eq(blip_eq_t const &);
|
||||
void output(Blip_Buffer *);
|
||||
enum
|
||||
{
|
||||
osc_count = 3
|
||||
};
|
||||
void osc_output(int index, Blip_Buffer *);
|
||||
void end_frame(blip_time_t);
|
||||
void save_state(fme7_apu_state_t *) const;
|
||||
void load_state(fme7_apu_state_t const &);
|
||||
|
||||
// Mask and addresses of registers
|
||||
enum { addr_mask = 0xe000 };
|
||||
enum { data_addr = 0xe000 };
|
||||
enum { latch_addr = 0xc000 };
|
||||
// Mask and addresses of registers
|
||||
enum
|
||||
{
|
||||
addr_mask = 0xe000
|
||||
};
|
||||
enum
|
||||
{
|
||||
data_addr = 0xe000
|
||||
};
|
||||
enum
|
||||
{
|
||||
latch_addr = 0xc000
|
||||
};
|
||||
|
||||
// (addr & addr_mask) == latch_addr
|
||||
void write_latch( int );
|
||||
// (addr & addr_mask) == latch_addr
|
||||
void write_latch(int);
|
||||
|
||||
// (addr & addr_mask) == data_addr
|
||||
void write_data( blip_time_t, int data );
|
||||
// (addr & addr_mask) == data_addr
|
||||
void write_data(blip_time_t, int data);
|
||||
|
||||
// End of public interface
|
||||
private:
|
||||
// noncopyable
|
||||
Nes_Fme7_Apu( const Nes_Fme7_Apu& );
|
||||
Nes_Fme7_Apu& operator = ( const Nes_Fme7_Apu& );
|
||||
// End of public interface
|
||||
private:
|
||||
// noncopyable
|
||||
Nes_Fme7_Apu(const Nes_Fme7_Apu &);
|
||||
Nes_Fme7_Apu &operator=(const Nes_Fme7_Apu &);
|
||||
|
||||
static unsigned char amp_table [16];
|
||||
static unsigned char amp_table[16];
|
||||
|
||||
struct {
|
||||
Blip_Buffer* output;
|
||||
int last_amp;
|
||||
} oscs [osc_count];
|
||||
blip_time_t last_time;
|
||||
struct
|
||||
{
|
||||
Blip_Buffer *output;
|
||||
int last_amp;
|
||||
} oscs[osc_count];
|
||||
blip_time_t last_time;
|
||||
|
||||
enum { amp_range = 192 }; // can be any value; this gives best error/quality tradeoff
|
||||
Blip_Synth<blip_good_quality,1> synth;
|
||||
enum
|
||||
{
|
||||
amp_range = 192
|
||||
}; // can be any value; this gives best error/quality tradeoff
|
||||
Blip_Synth<blip_good_quality, 1> synth;
|
||||
|
||||
void run_until( blip_time_t );
|
||||
void run_until(blip_time_t);
|
||||
};
|
||||
|
||||
inline void Nes_Fme7_Apu::volume( double v )
|
||||
inline void Nes_Fme7_Apu::volume(double v)
|
||||
{
|
||||
synth.volume( 0.38 / +amp_range * v ); // to do: fine-tune
|
||||
synth.volume(0.38 / +amp_range * v); // to do: fine-tune
|
||||
}
|
||||
|
||||
inline void Nes_Fme7_Apu::treble_eq( blip_eq_t const& eq )
|
||||
inline void Nes_Fme7_Apu::treble_eq(blip_eq_t const &eq)
|
||||
{
|
||||
synth.treble_eq( eq );
|
||||
synth.treble_eq(eq);
|
||||
}
|
||||
|
||||
inline void Nes_Fme7_Apu::osc_output( int i, Blip_Buffer* buf )
|
||||
inline void Nes_Fme7_Apu::osc_output(int i, Blip_Buffer *buf)
|
||||
{
|
||||
oscs [i].output = buf;
|
||||
oscs[i].output = buf;
|
||||
}
|
||||
|
||||
inline void Nes_Fme7_Apu::output( Blip_Buffer* buf )
|
||||
inline void Nes_Fme7_Apu::output(Blip_Buffer *buf)
|
||||
{
|
||||
for ( int i = 0; i < osc_count; i++ )
|
||||
osc_output( i, buf );
|
||||
for (int i = 0; i < osc_count; i++)
|
||||
osc_output(i, buf);
|
||||
}
|
||||
|
||||
inline Nes_Fme7_Apu::Nes_Fme7_Apu()
|
||||
{
|
||||
output( 0 );
|
||||
volume( 1.0 );
|
||||
reset();
|
||||
output(0);
|
||||
volume(1.0);
|
||||
reset();
|
||||
}
|
||||
|
||||
inline void Nes_Fme7_Apu::write_latch( int data ) { latch = data; }
|
||||
inline void Nes_Fme7_Apu::write_latch(int data) { latch = data; }
|
||||
|
||||
inline void Nes_Fme7_Apu::write_data( blip_time_t time, int data )
|
||||
inline void Nes_Fme7_Apu::write_data(blip_time_t time, int data)
|
||||
{
|
||||
if ( (unsigned) latch >= reg_count )
|
||||
{
|
||||
#ifdef dprintf
|
||||
dprintf( "FME7 write to %02X (past end of sound registers)\n", (int) latch );
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if ((unsigned)latch >= reg_count)
|
||||
{
|
||||
#ifdef dprintf
|
||||
dprintf("FME7 write to %02X (past end of sound registers)\n", (int)latch);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
run_until( time );
|
||||
regs [latch] = data;
|
||||
run_until(time);
|
||||
regs[latch] = data;
|
||||
}
|
||||
|
||||
inline void Nes_Fme7_Apu::end_frame( blip_time_t time )
|
||||
inline void Nes_Fme7_Apu::end_frame(blip_time_t time)
|
||||
{
|
||||
if ( time > last_time )
|
||||
run_until( time );
|
||||
if (time > last_time)
|
||||
run_until(time);
|
||||
|
||||
last_time -= time;
|
||||
last_time -= time;
|
||||
}
|
||||
|
||||
inline void Nes_Fme7_Apu::save_state( fme7_apu_state_t* out ) const
|
||||
inline void Nes_Fme7_Apu::save_state(fme7_apu_state_t *out) const
|
||||
{
|
||||
*out = *this;
|
||||
*out = *this;
|
||||
}
|
||||
|
||||
inline void Nes_Fme7_Apu::load_state( fme7_apu_state_t const& in )
|
||||
inline void Nes_Fme7_Apu::load_state(fme7_apu_state_t const &in)
|
||||
{
|
||||
reset();
|
||||
fme7_apu_state_t* state = this;
|
||||
*state = in;
|
||||
reset();
|
||||
fme7_apu_state_t *state = this;
|
||||
*state = in;
|
||||
|
||||
//Run sound channels for 0 cycles for clean audio after loading state
|
||||
run_until(last_time);
|
||||
// Run sound channels for 0 cycles for clean audio after loading state
|
||||
run_until(last_time);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/
|
||||
|
||||
#include "apu/Blip_Buffer.hpp"
|
||||
#include "apu/namco/apu.hpp"
|
||||
#include "apu/Blip_Buffer.hpp"
|
||||
|
||||
/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
|
@ -17,9 +17,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
Nes_Namco_Apu::Nes_Namco_Apu()
|
||||
{
|
||||
output( 0 );
|
||||
volume( 1.0 );
|
||||
reset();
|
||||
output(0);
|
||||
volume(1.0);
|
||||
reset();
|
||||
}
|
||||
|
||||
Nes_Namco_Apu::~Nes_Namco_Apu()
|
||||
|
@ -28,152 +28,151 @@ Nes_Namco_Apu::~Nes_Namco_Apu()
|
|||
|
||||
void Nes_Namco_Apu::reset()
|
||||
{
|
||||
last_time = 0;
|
||||
addr_reg = 0;
|
||||
last_time = 0;
|
||||
addr_reg = 0;
|
||||
|
||||
int i;
|
||||
for ( i = 0; i < reg_count; i++ )
|
||||
reg [i] = 0;
|
||||
int i;
|
||||
for (i = 0; i < reg_count; i++)
|
||||
reg[i] = 0;
|
||||
|
||||
for ( i = 0; i < osc_count; i++ )
|
||||
{
|
||||
Namco_Osc& osc = oscs [i];
|
||||
osc.delay = 0;
|
||||
osc.last_amp = 0;
|
||||
osc.wave_pos = 0;
|
||||
}
|
||||
for (i = 0; i < osc_count; i++)
|
||||
{
|
||||
Namco_Osc &osc = oscs[i];
|
||||
osc.delay = 0;
|
||||
osc.last_amp = 0;
|
||||
osc.wave_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Nes_Namco_Apu::output( Blip_Buffer* buf )
|
||||
void Nes_Namco_Apu::output(Blip_Buffer *buf)
|
||||
{
|
||||
for ( int i = 0; i < osc_count; i++ )
|
||||
osc_output( i, buf );
|
||||
for (int i = 0; i < osc_count; i++)
|
||||
osc_output(i, buf);
|
||||
}
|
||||
|
||||
/*
|
||||
void Nes_Namco_Apu::reflect_state( Tagged_Data& data )
|
||||
{
|
||||
reflect_int16( data, 'ADDR', &addr_reg );
|
||||
reflect_int16( data, 'ADDR', &addr_reg );
|
||||
|
||||
static const char hex [17] = "0123456789ABCDEF";
|
||||
int i;
|
||||
for ( i = 0; i < reg_count; i++ )
|
||||
reflect_int16( data, 'RG\0\0' + hex [i >> 4] * 0x100 + hex [i & 15], ® [i] );
|
||||
static const char hex [17] = "0123456789ABCDEF";
|
||||
int i;
|
||||
for ( i = 0; i < reg_count; i++ )
|
||||
reflect_int16( data, 'RG\0\0' + hex [i >> 4] * 0x100 + hex [i & 15], ® [i] );
|
||||
|
||||
for ( i = 0; i < osc_count; i++ )
|
||||
{
|
||||
reflect_int32( data, 'DLY0' + i, &oscs [i].delay );
|
||||
reflect_int16( data, 'POS0' + i, &oscs [i].wave_pos );
|
||||
}
|
||||
for ( i = 0; i < osc_count; i++ )
|
||||
{
|
||||
reflect_int32( data, 'DLY0' + i, &oscs [i].delay );
|
||||
reflect_int16( data, 'POS0' + i, &oscs [i].wave_pos );
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void Nes_Namco_Apu::end_frame( nes_time_t time )
|
||||
void Nes_Namco_Apu::end_frame(nes_time_t time)
|
||||
{
|
||||
if ( time > last_time )
|
||||
run_until( time );
|
||||
if (time > last_time)
|
||||
run_until(time);
|
||||
|
||||
last_time -= time;
|
||||
last_time -= time;
|
||||
}
|
||||
|
||||
void Nes_Namco_Apu::run_until( nes_time_t nes_end_time )
|
||||
void Nes_Namco_Apu::run_until(nes_time_t nes_end_time)
|
||||
{
|
||||
int active_oscs = (reg [0x7F] >> 4 & 7) + 1;
|
||||
for ( int i = osc_count - active_oscs; i < osc_count; i++ )
|
||||
{
|
||||
Namco_Osc& osc = oscs [i];
|
||||
Blip_Buffer* output = osc.output;
|
||||
if ( !output )
|
||||
continue;
|
||||
int active_oscs = (reg[0x7F] >> 4 & 7) + 1;
|
||||
for (int i = osc_count - active_oscs; i < osc_count; i++)
|
||||
{
|
||||
Namco_Osc &osc = oscs[i];
|
||||
Blip_Buffer *output = osc.output;
|
||||
if (!output)
|
||||
continue;
|
||||
|
||||
blip_resampled_time_t time =
|
||||
output->resampled_time( last_time ) + osc.delay;
|
||||
blip_resampled_time_t end_time = output->resampled_time( nes_end_time );
|
||||
osc.delay = 0;
|
||||
if ( time < end_time )
|
||||
{
|
||||
const uint8_t* osc_reg = ® [i * 8 + 0x40];
|
||||
if ( !(osc_reg [4] & 0xE0) )
|
||||
continue;
|
||||
blip_resampled_time_t time =
|
||||
output->resampled_time(last_time) + osc.delay;
|
||||
blip_resampled_time_t end_time = output->resampled_time(nes_end_time);
|
||||
osc.delay = 0;
|
||||
if (time < end_time)
|
||||
{
|
||||
const uint8_t *osc_reg = ®[i * 8 + 0x40];
|
||||
if (!(osc_reg[4] & 0xE0))
|
||||
continue;
|
||||
|
||||
int volume = osc_reg [7] & 15;
|
||||
if ( !volume )
|
||||
continue;
|
||||
int volume = osc_reg[7] & 15;
|
||||
if (!volume)
|
||||
continue;
|
||||
|
||||
long freq = (osc_reg [4] & 3) * 0x10000 + osc_reg [2] * 0x100L + osc_reg [0];
|
||||
if ( freq < 64 * active_oscs )
|
||||
continue; // prevent low frequencies from excessively delaying freq changes
|
||||
blip_resampled_time_t period =
|
||||
output->resampled_duration( 983040 ) / freq * active_oscs;
|
||||
long freq = (osc_reg[4] & 3) * 0x10000 + osc_reg[2] * 0x100L + osc_reg[0];
|
||||
if (freq < 64 * active_oscs)
|
||||
continue; // prevent low frequencies from excessively delaying freq changes
|
||||
blip_resampled_time_t period =
|
||||
output->resampled_duration(983040) / freq * active_oscs;
|
||||
|
||||
int wave_size = 32 - (osc_reg [4] >> 2 & 7) * 4;
|
||||
if ( !wave_size )
|
||||
continue;
|
||||
int wave_size = 32 - (osc_reg[4] >> 2 & 7) * 4;
|
||||
if (!wave_size)
|
||||
continue;
|
||||
|
||||
int last_amp = osc.last_amp;
|
||||
int wave_pos = osc.wave_pos;
|
||||
int last_amp = osc.last_amp;
|
||||
int wave_pos = osc.wave_pos;
|
||||
|
||||
do
|
||||
{
|
||||
// read wave sample
|
||||
int addr = wave_pos + osc_reg [6];
|
||||
int sample = reg [addr >> 1] >> (addr << 2 & 4);
|
||||
wave_pos++;
|
||||
sample = (sample & 15) * volume;
|
||||
do
|
||||
{
|
||||
// read wave sample
|
||||
int addr = wave_pos + osc_reg[6];
|
||||
int sample = reg[addr >> 1] >> (addr << 2 & 4);
|
||||
wave_pos++;
|
||||
sample = (sample & 15) * volume;
|
||||
|
||||
// output impulse if amplitude changed
|
||||
int delta = sample - last_amp;
|
||||
if ( delta )
|
||||
{
|
||||
last_amp = sample;
|
||||
synth.offset_resampled( time, delta, output );
|
||||
}
|
||||
// output impulse if amplitude changed
|
||||
int delta = sample - last_amp;
|
||||
if (delta)
|
||||
{
|
||||
last_amp = sample;
|
||||
synth.offset_resampled(time, delta, output);
|
||||
}
|
||||
|
||||
// next sample
|
||||
time += period;
|
||||
if ( wave_pos >= wave_size )
|
||||
wave_pos = 0;
|
||||
}
|
||||
while ( time < end_time );
|
||||
// next sample
|
||||
time += period;
|
||||
if (wave_pos >= wave_size)
|
||||
wave_pos = 0;
|
||||
} while (time < end_time);
|
||||
|
||||
osc.wave_pos = wave_pos;
|
||||
osc.last_amp = last_amp;
|
||||
}
|
||||
osc.delay = time - end_time;
|
||||
}
|
||||
osc.wave_pos = wave_pos;
|
||||
osc.last_amp = last_amp;
|
||||
}
|
||||
osc.delay = time - end_time;
|
||||
}
|
||||
|
||||
last_time = nes_end_time;
|
||||
last_time = nes_end_time;
|
||||
}
|
||||
|
||||
void Nes_Namco_Apu::save_state( namco_state_t* out ) const
|
||||
void Nes_Namco_Apu::save_state(namco_state_t *out) const
|
||||
{
|
||||
out->addr = addr_reg;
|
||||
for ( int r = 0; r < reg_count; r++ )
|
||||
out->regs [ r ] = reg [ r ];
|
||||
out->addr = addr_reg;
|
||||
for (int r = 0; r < reg_count; r++)
|
||||
out->regs[r] = reg[r];
|
||||
|
||||
for ( int i = 0; i < osc_count; i++ )
|
||||
{
|
||||
Namco_Osc const& osc = oscs [ i ];
|
||||
for (int i = 0; i < osc_count; i++)
|
||||
{
|
||||
Namco_Osc const &osc = oscs[i];
|
||||
|
||||
out->delays [ i ] = osc.delay;
|
||||
out->positions [ i ] = osc.wave_pos;
|
||||
}
|
||||
out->delays[i] = osc.delay;
|
||||
out->positions[i] = osc.wave_pos;
|
||||
}
|
||||
}
|
||||
|
||||
void Nes_Namco_Apu::load_state( namco_state_t const& in )
|
||||
void Nes_Namco_Apu::load_state(namco_state_t const &in)
|
||||
{
|
||||
reset();
|
||||
addr_reg = in.addr;
|
||||
for ( int r = 0; r < reg_count; r++ )
|
||||
reg [ r ] = in.regs [ r ];
|
||||
reset();
|
||||
addr_reg = in.addr;
|
||||
for (int r = 0; r < reg_count; r++)
|
||||
reg[r] = in.regs[r];
|
||||
|
||||
for ( int i = 0; i < osc_count; i++ )
|
||||
{
|
||||
Namco_Osc& osc = oscs [ i ];
|
||||
for (int i = 0; i < osc_count; i++)
|
||||
{
|
||||
Namco_Osc &osc = oscs[i];
|
||||
|
||||
osc.delay = in.delays [ i ];
|
||||
osc.wave_pos = in.positions [ i ];
|
||||
}
|
||||
osc.delay = in.delays[i];
|
||||
osc.wave_pos = in.positions[i];
|
||||
}
|
||||
|
||||
run_until( last_time );
|
||||
run_until(last_time);
|
||||
}
|
||||
|
|
|
@ -3,95 +3,108 @@
|
|||
// Namco 106 sound chip emulator
|
||||
// Nes_Snd_Emu 0.1.7
|
||||
|
||||
#include <cstdint>
|
||||
#include "apu/apu.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
struct namco_state_t
|
||||
{
|
||||
uint8_t regs [0x80];
|
||||
uint8_t addr;
|
||||
uint8_t unused;
|
||||
uint8_t positions [8];
|
||||
uint32_t delays [8];
|
||||
uint8_t regs[0x80];
|
||||
uint8_t addr;
|
||||
uint8_t unused;
|
||||
uint8_t positions[8];
|
||||
uint32_t delays[8];
|
||||
};
|
||||
static_assert( sizeof (namco_state_t) == 172 );
|
||||
static_assert(sizeof(namco_state_t) == 172);
|
||||
|
||||
class Nes_Namco_Apu {
|
||||
public:
|
||||
Nes_Namco_Apu();
|
||||
~Nes_Namco_Apu();
|
||||
|
||||
// See Nes_Apu.h for reference.
|
||||
void volume( double );
|
||||
void treble_eq( const blip_eq_t& );
|
||||
void output( Blip_Buffer* );
|
||||
enum { osc_count = 8 };
|
||||
void osc_output( int index, Blip_Buffer* );
|
||||
void reset();
|
||||
void end_frame( nes_time_t );
|
||||
|
||||
// Read/write data register is at 0x4800
|
||||
enum { data_reg_addr = 0x4800 };
|
||||
void write_data( nes_time_t, int );
|
||||
int read_data();
|
||||
|
||||
// Write-only address register is at 0xF800
|
||||
enum { addr_reg_addr = 0xF800 };
|
||||
void write_addr( int );
|
||||
|
||||
// to do: implement save/restore
|
||||
void save_state( namco_state_t* out ) const;
|
||||
void load_state( namco_state_t const& );
|
||||
|
||||
private:
|
||||
// noncopyable
|
||||
Nes_Namco_Apu( const Nes_Namco_Apu& );
|
||||
Nes_Namco_Apu& operator = ( const Nes_Namco_Apu& );
|
||||
|
||||
struct Namco_Osc {
|
||||
long delay;
|
||||
Blip_Buffer* output;
|
||||
short last_amp;
|
||||
short wave_pos;
|
||||
};
|
||||
|
||||
Namco_Osc oscs [osc_count];
|
||||
|
||||
nes_time_t last_time;
|
||||
int addr_reg;
|
||||
|
||||
enum { reg_count = 0x80 };
|
||||
uint8_t reg [reg_count];
|
||||
Blip_Synth<blip_good_quality,15> synth;
|
||||
|
||||
uint8_t& access();
|
||||
void run_until( nes_time_t );
|
||||
};
|
||||
|
||||
inline uint8_t& Nes_Namco_Apu::access()
|
||||
class Nes_Namco_Apu
|
||||
{
|
||||
int addr = addr_reg & 0x7f;
|
||||
if ( addr_reg & 0x80 )
|
||||
addr_reg = (addr + 1) | 0x80;
|
||||
return reg [addr];
|
||||
public:
|
||||
Nes_Namco_Apu();
|
||||
~Nes_Namco_Apu();
|
||||
|
||||
// See Nes_Apu.h for reference.
|
||||
void volume(double);
|
||||
void treble_eq(const blip_eq_t &);
|
||||
void output(Blip_Buffer *);
|
||||
enum
|
||||
{
|
||||
osc_count = 8
|
||||
};
|
||||
void osc_output(int index, Blip_Buffer *);
|
||||
void reset();
|
||||
void end_frame(nes_time_t);
|
||||
|
||||
// Read/write data register is at 0x4800
|
||||
enum
|
||||
{
|
||||
data_reg_addr = 0x4800
|
||||
};
|
||||
void write_data(nes_time_t, int);
|
||||
int read_data();
|
||||
|
||||
// Write-only address register is at 0xF800
|
||||
enum
|
||||
{
|
||||
addr_reg_addr = 0xF800
|
||||
};
|
||||
void write_addr(int);
|
||||
|
||||
// to do: implement save/restore
|
||||
void save_state(namco_state_t *out) const;
|
||||
void load_state(namco_state_t const &);
|
||||
|
||||
private:
|
||||
// noncopyable
|
||||
Nes_Namco_Apu(const Nes_Namco_Apu &);
|
||||
Nes_Namco_Apu &operator=(const Nes_Namco_Apu &);
|
||||
|
||||
struct Namco_Osc
|
||||
{
|
||||
long delay;
|
||||
Blip_Buffer *output;
|
||||
short last_amp;
|
||||
short wave_pos;
|
||||
};
|
||||
|
||||
Namco_Osc oscs[osc_count];
|
||||
|
||||
nes_time_t last_time;
|
||||
int addr_reg;
|
||||
|
||||
enum
|
||||
{
|
||||
reg_count = 0x80
|
||||
};
|
||||
uint8_t reg[reg_count];
|
||||
Blip_Synth<blip_good_quality, 15> synth;
|
||||
|
||||
uint8_t &access();
|
||||
void run_until(nes_time_t);
|
||||
};
|
||||
|
||||
inline uint8_t &Nes_Namco_Apu::access()
|
||||
{
|
||||
int addr = addr_reg & 0x7f;
|
||||
if (addr_reg & 0x80)
|
||||
addr_reg = (addr + 1) | 0x80;
|
||||
return reg[addr];
|
||||
}
|
||||
|
||||
inline void Nes_Namco_Apu::volume( double v ) { synth.volume( 0.10 / +osc_count * v ); }
|
||||
inline void Nes_Namco_Apu::volume(double v) { synth.volume(0.10 / +osc_count * v); }
|
||||
|
||||
inline void Nes_Namco_Apu::treble_eq( const blip_eq_t& eq ) { synth.treble_eq( eq ); }
|
||||
inline void Nes_Namco_Apu::treble_eq(const blip_eq_t &eq) { synth.treble_eq(eq); }
|
||||
|
||||
inline void Nes_Namco_Apu::write_addr( int v ) { addr_reg = v; }
|
||||
inline void Nes_Namco_Apu::write_addr(int v) { addr_reg = v; }
|
||||
|
||||
inline int Nes_Namco_Apu::read_data() { return access(); }
|
||||
|
||||
inline void Nes_Namco_Apu::osc_output( int i, Blip_Buffer* buf )
|
||||
inline void Nes_Namco_Apu::osc_output(int i, Blip_Buffer *buf)
|
||||
{
|
||||
oscs [i].output = buf;
|
||||
oscs[i].output = buf;
|
||||
}
|
||||
|
||||
inline void Nes_Namco_Apu::write_data( nes_time_t time, int data )
|
||||
inline void Nes_Namco_Apu::write_data(nes_time_t time, int data)
|
||||
{
|
||||
run_until( time );
|
||||
access() = data;
|
||||
run_until(time);
|
||||
access() = data;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,9 +16,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
Nes_Vrc6_Apu::Nes_Vrc6_Apu()
|
||||
{
|
||||
output( 0 );
|
||||
volume( 1.0 );
|
||||
reset();
|
||||
output(0);
|
||||
volume(1.0);
|
||||
reset();
|
||||
}
|
||||
|
||||
Nes_Vrc6_Apu::~Nes_Vrc6_Apu()
|
||||
|
@ -27,186 +27,184 @@ Nes_Vrc6_Apu::~Nes_Vrc6_Apu()
|
|||
|
||||
void Nes_Vrc6_Apu::reset()
|
||||
{
|
||||
last_time = 0;
|
||||
for ( int i = 0; i < osc_count; i++ )
|
||||
{
|
||||
Vrc6_Osc& osc = oscs [i];
|
||||
for ( int j = 0; j < reg_count; j++ )
|
||||
osc.regs [j] = 0;
|
||||
osc.delay = 0;
|
||||
osc.last_amp = 0;
|
||||
osc.phase = 1;
|
||||
osc.amp = 0;
|
||||
}
|
||||
last_time = 0;
|
||||
for (int i = 0; i < osc_count; i++)
|
||||
{
|
||||
Vrc6_Osc &osc = oscs[i];
|
||||
for (int j = 0; j < reg_count; j++)
|
||||
osc.regs[j] = 0;
|
||||
osc.delay = 0;
|
||||
osc.last_amp = 0;
|
||||
osc.phase = 1;
|
||||
osc.amp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Nes_Vrc6_Apu::output( Blip_Buffer* buf )
|
||||
void Nes_Vrc6_Apu::output(Blip_Buffer *buf)
|
||||
{
|
||||
for ( int i = 0; i < osc_count; i++ )
|
||||
osc_output( i, buf );
|
||||
for (int i = 0; i < osc_count; i++)
|
||||
osc_output(i, buf);
|
||||
}
|
||||
|
||||
void Nes_Vrc6_Apu::run_until( nes_time_t time )
|
||||
void Nes_Vrc6_Apu::run_until(nes_time_t time)
|
||||
{
|
||||
run_square( oscs [0], time );
|
||||
run_square( oscs [1], time );
|
||||
run_saw( time );
|
||||
last_time = time;
|
||||
run_square(oscs[0], time);
|
||||
run_square(oscs[1], time);
|
||||
run_saw(time);
|
||||
last_time = time;
|
||||
}
|
||||
|
||||
void Nes_Vrc6_Apu::write_osc( nes_time_t time, int osc_index, int reg, int data )
|
||||
void Nes_Vrc6_Apu::write_osc(nes_time_t time, int osc_index, int reg, int data)
|
||||
{
|
||||
run_until( time );
|
||||
oscs [osc_index].regs [reg] = data;
|
||||
run_until(time);
|
||||
oscs[osc_index].regs[reg] = data;
|
||||
}
|
||||
|
||||
void Nes_Vrc6_Apu::end_frame( nes_time_t time )
|
||||
void Nes_Vrc6_Apu::end_frame(nes_time_t time)
|
||||
{
|
||||
if ( time > last_time )
|
||||
run_until( time );
|
||||
if (time > last_time)
|
||||
run_until(time);
|
||||
|
||||
last_time -= time;
|
||||
last_time -= time;
|
||||
}
|
||||
|
||||
void Nes_Vrc6_Apu::save_state( vrc6_apu_state_t* out ) const
|
||||
void Nes_Vrc6_Apu::save_state(vrc6_apu_state_t *out) const
|
||||
{
|
||||
out->saw_amp = oscs [2].amp;
|
||||
for ( int i = 0; i < osc_count; i++ )
|
||||
{
|
||||
Vrc6_Osc const& osc = oscs [i];
|
||||
for ( int r = 0; r < reg_count; r++ )
|
||||
out->regs [i] [r] = osc.regs [r];
|
||||
out->saw_amp = oscs[2].amp;
|
||||
for (int i = 0; i < osc_count; i++)
|
||||
{
|
||||
Vrc6_Osc const &osc = oscs[i];
|
||||
for (int r = 0; r < reg_count; r++)
|
||||
out->regs[i][r] = osc.regs[r];
|
||||
|
||||
out->delays [i] = osc.delay;
|
||||
out->phases [i] = osc.phase;
|
||||
}
|
||||
out->delays[i] = osc.delay;
|
||||
out->phases[i] = osc.phase;
|
||||
}
|
||||
}
|
||||
|
||||
void Nes_Vrc6_Apu::load_state( vrc6_apu_state_t const& in )
|
||||
void Nes_Vrc6_Apu::load_state(vrc6_apu_state_t const &in)
|
||||
{
|
||||
reset();
|
||||
oscs [2].amp = in.saw_amp;
|
||||
for ( int i = 0; i < osc_count; i++ )
|
||||
{
|
||||
Vrc6_Osc& osc = oscs [i];
|
||||
for ( int r = 0; r < reg_count; r++ )
|
||||
osc.regs [r] = in.regs [i] [r];
|
||||
reset();
|
||||
oscs[2].amp = in.saw_amp;
|
||||
for (int i = 0; i < osc_count; i++)
|
||||
{
|
||||
Vrc6_Osc &osc = oscs[i];
|
||||
for (int r = 0; r < reg_count; r++)
|
||||
osc.regs[r] = in.regs[i][r];
|
||||
|
||||
osc.delay = in.delays [i];
|
||||
osc.phase = in.phases [i];
|
||||
}
|
||||
if ( !oscs [2].phase )
|
||||
oscs [2].phase = 1;
|
||||
osc.delay = in.delays[i];
|
||||
osc.phase = in.phases[i];
|
||||
}
|
||||
if (!oscs[2].phase)
|
||||
oscs[2].phase = 1;
|
||||
|
||||
//Run sound channels for 0 cycles for clean audio after loading state
|
||||
this->run_until(this->last_time);
|
||||
// Run sound channels for 0 cycles for clean audio after loading state
|
||||
this->run_until(this->last_time);
|
||||
}
|
||||
|
||||
void Nes_Vrc6_Apu::run_square( Vrc6_Osc& osc, nes_time_t end_time )
|
||||
void Nes_Vrc6_Apu::run_square(Vrc6_Osc &osc, nes_time_t end_time)
|
||||
{
|
||||
Blip_Buffer* output = osc.output;
|
||||
if ( !output )
|
||||
return;
|
||||
Blip_Buffer *output = osc.output;
|
||||
if (!output)
|
||||
return;
|
||||
|
||||
int volume = osc.regs [0] & 15;
|
||||
if ( !(osc.regs [2] & 0x80) )
|
||||
volume = 0;
|
||||
int volume = osc.regs[0] & 15;
|
||||
if (!(osc.regs[2] & 0x80))
|
||||
volume = 0;
|
||||
|
||||
int gate = osc.regs [0] & 0x80;
|
||||
int duty = ((osc.regs [0] >> 4) & 7) + 1;
|
||||
int delta = ((gate || osc.phase < duty) ? volume : 0) - osc.last_amp;
|
||||
nes_time_t time = last_time;
|
||||
if ( delta )
|
||||
{
|
||||
osc.last_amp += delta;
|
||||
square_synth.offset( time, delta, output );
|
||||
}
|
||||
int gate = osc.regs[0] & 0x80;
|
||||
int duty = ((osc.regs[0] >> 4) & 7) + 1;
|
||||
int delta = ((gate || osc.phase < duty) ? volume : 0) - osc.last_amp;
|
||||
nes_time_t time = last_time;
|
||||
if (delta)
|
||||
{
|
||||
osc.last_amp += delta;
|
||||
square_synth.offset(time, delta, output);
|
||||
}
|
||||
|
||||
time += osc.delay;
|
||||
osc.delay = 0;
|
||||
int period = osc.period();
|
||||
if ( volume && !gate && period > 4 )
|
||||
{
|
||||
if ( time < end_time )
|
||||
{
|
||||
int phase = osc.phase;
|
||||
time += osc.delay;
|
||||
osc.delay = 0;
|
||||
int period = osc.period();
|
||||
if (volume && !gate && period > 4)
|
||||
{
|
||||
if (time < end_time)
|
||||
{
|
||||
int phase = osc.phase;
|
||||
|
||||
do
|
||||
{
|
||||
phase++;
|
||||
if ( phase == 16 )
|
||||
{
|
||||
phase = 0;
|
||||
osc.last_amp = volume;
|
||||
square_synth.offset( time, volume, output );
|
||||
}
|
||||
if ( phase == duty )
|
||||
{
|
||||
osc.last_amp = 0;
|
||||
square_synth.offset( time, -volume, output );
|
||||
}
|
||||
time += period;
|
||||
}
|
||||
while ( time < end_time );
|
||||
do
|
||||
{
|
||||
phase++;
|
||||
if (phase == 16)
|
||||
{
|
||||
phase = 0;
|
||||
osc.last_amp = volume;
|
||||
square_synth.offset(time, volume, output);
|
||||
}
|
||||
if (phase == duty)
|
||||
{
|
||||
osc.last_amp = 0;
|
||||
square_synth.offset(time, -volume, output);
|
||||
}
|
||||
time += period;
|
||||
} while (time < end_time);
|
||||
|
||||
osc.phase = phase;
|
||||
}
|
||||
osc.delay = time - end_time;
|
||||
}
|
||||
osc.phase = phase;
|
||||
}
|
||||
osc.delay = time - end_time;
|
||||
}
|
||||
}
|
||||
|
||||
void Nes_Vrc6_Apu::run_saw( nes_time_t end_time )
|
||||
void Nes_Vrc6_Apu::run_saw(nes_time_t end_time)
|
||||
{
|
||||
Vrc6_Osc& osc = oscs [2];
|
||||
Blip_Buffer* output = osc.output;
|
||||
if ( !output )
|
||||
return;
|
||||
Vrc6_Osc &osc = oscs[2];
|
||||
Blip_Buffer *output = osc.output;
|
||||
if (!output)
|
||||
return;
|
||||
|
||||
int amp = osc.amp;
|
||||
int amp_step = osc.regs [0] & 0x3F;
|
||||
nes_time_t time = last_time;
|
||||
int last_amp = osc.last_amp;
|
||||
if ( !(osc.regs [2] & 0x80) || !(amp_step | amp) )
|
||||
{
|
||||
osc.delay = 0;
|
||||
int delta = (amp >> 3) - last_amp;
|
||||
last_amp = amp >> 3;
|
||||
saw_synth.offset( time, delta, output );
|
||||
}
|
||||
else
|
||||
{
|
||||
time += osc.delay;
|
||||
if ( time < end_time )
|
||||
{
|
||||
int period = osc.period() * 2;
|
||||
int phase = osc.phase;
|
||||
int amp = osc.amp;
|
||||
int amp_step = osc.regs[0] & 0x3F;
|
||||
nes_time_t time = last_time;
|
||||
int last_amp = osc.last_amp;
|
||||
if (!(osc.regs[2] & 0x80) || !(amp_step | amp))
|
||||
{
|
||||
osc.delay = 0;
|
||||
int delta = (amp >> 3) - last_amp;
|
||||
last_amp = amp >> 3;
|
||||
saw_synth.offset(time, delta, output);
|
||||
}
|
||||
else
|
||||
{
|
||||
time += osc.delay;
|
||||
if (time < end_time)
|
||||
{
|
||||
int period = osc.period() * 2;
|
||||
int phase = osc.phase;
|
||||
|
||||
do
|
||||
{
|
||||
if ( --phase == 0 )
|
||||
{
|
||||
phase = 7;
|
||||
amp = 0;
|
||||
}
|
||||
do
|
||||
{
|
||||
if (--phase == 0)
|
||||
{
|
||||
phase = 7;
|
||||
amp = 0;
|
||||
}
|
||||
|
||||
int delta = (amp >> 3) - last_amp;
|
||||
if ( delta )
|
||||
{
|
||||
last_amp = amp >> 3;
|
||||
saw_synth.offset( time, delta, output );
|
||||
}
|
||||
int delta = (amp >> 3) - last_amp;
|
||||
if (delta)
|
||||
{
|
||||
last_amp = amp >> 3;
|
||||
saw_synth.offset(time, delta, output);
|
||||
}
|
||||
|
||||
time += period;
|
||||
amp = (amp + amp_step) & 0xFF;
|
||||
}
|
||||
while ( time < end_time );
|
||||
time += period;
|
||||
amp = (amp + amp_step) & 0xFF;
|
||||
} while (time < end_time);
|
||||
|
||||
osc.phase = phase;
|
||||
osc.amp = amp;
|
||||
}
|
||||
osc.phase = phase;
|
||||
osc.amp = amp;
|
||||
}
|
||||
|
||||
osc.delay = time - end_time;
|
||||
}
|
||||
osc.delay = time - end_time;
|
||||
}
|
||||
|
||||
osc.last_amp = last_amp;
|
||||
osc.last_amp = last_amp;
|
||||
}
|
||||
|
|
|
@ -4,91 +4,104 @@
|
|||
// Konami VRC6 sound chip emulator
|
||||
// Nes_Snd_Emu 0.1.7
|
||||
|
||||
#include <cstdint>
|
||||
#include "apu/apu.hpp"
|
||||
#include "apu/Blip_Buffer.hpp"
|
||||
#include "apu/apu.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
struct vrc6_apu_state_t;
|
||||
|
||||
class Nes_Vrc6_Apu {
|
||||
public:
|
||||
Nes_Vrc6_Apu();
|
||||
~Nes_Vrc6_Apu();
|
||||
class Nes_Vrc6_Apu
|
||||
{
|
||||
public:
|
||||
Nes_Vrc6_Apu();
|
||||
~Nes_Vrc6_Apu();
|
||||
|
||||
// See Nes_Apu.h for reference
|
||||
void reset();
|
||||
void volume( double );
|
||||
void treble_eq( blip_eq_t const& );
|
||||
void output( Blip_Buffer* );
|
||||
enum { osc_count = 3 };
|
||||
void osc_output( int index, Blip_Buffer* );
|
||||
void end_frame( nes_time_t );
|
||||
void save_state( vrc6_apu_state_t* ) const;
|
||||
void load_state( vrc6_apu_state_t const& );
|
||||
// See Nes_Apu.h for reference
|
||||
void reset();
|
||||
void volume(double);
|
||||
void treble_eq(blip_eq_t const &);
|
||||
void output(Blip_Buffer *);
|
||||
enum
|
||||
{
|
||||
osc_count = 3
|
||||
};
|
||||
void osc_output(int index, Blip_Buffer *);
|
||||
void end_frame(nes_time_t);
|
||||
void save_state(vrc6_apu_state_t *) const;
|
||||
void load_state(vrc6_apu_state_t const &);
|
||||
|
||||
// Oscillator 0 write-only registers are at $9000-$9002
|
||||
// Oscillator 1 write-only registers are at $A000-$A002
|
||||
// Oscillator 2 write-only registers are at $B000-$B002
|
||||
enum { reg_count = 3 };
|
||||
enum { base_addr = 0x9000 };
|
||||
enum { addr_step = 0x1000 };
|
||||
void write_osc( nes_time_t, int osc, int reg, int data );
|
||||
// Oscillator 0 write-only registers are at $9000-$9002
|
||||
// Oscillator 1 write-only registers are at $A000-$A002
|
||||
// Oscillator 2 write-only registers are at $B000-$B002
|
||||
enum
|
||||
{
|
||||
reg_count = 3
|
||||
};
|
||||
enum
|
||||
{
|
||||
base_addr = 0x9000
|
||||
};
|
||||
enum
|
||||
{
|
||||
addr_step = 0x1000
|
||||
};
|
||||
void write_osc(nes_time_t, int osc, int reg, int data);
|
||||
|
||||
private:
|
||||
// noncopyable
|
||||
Nes_Vrc6_Apu( const Nes_Vrc6_Apu& );
|
||||
Nes_Vrc6_Apu& operator = ( const Nes_Vrc6_Apu& );
|
||||
private:
|
||||
// noncopyable
|
||||
Nes_Vrc6_Apu(const Nes_Vrc6_Apu &);
|
||||
Nes_Vrc6_Apu &operator=(const Nes_Vrc6_Apu &);
|
||||
|
||||
struct Vrc6_Osc
|
||||
{
|
||||
uint8_t regs [3];
|
||||
Blip_Buffer* output;
|
||||
int delay;
|
||||
int last_amp;
|
||||
int phase;
|
||||
int amp; // only used by saw
|
||||
struct Vrc6_Osc
|
||||
{
|
||||
uint8_t regs[3];
|
||||
Blip_Buffer *output;
|
||||
int delay;
|
||||
int last_amp;
|
||||
int phase;
|
||||
int amp; // only used by saw
|
||||
|
||||
int period() const
|
||||
{
|
||||
return (regs [2] & 0x0f) * 0x100L + regs [1] + 1;
|
||||
}
|
||||
};
|
||||
int period() const
|
||||
{
|
||||
return (regs[2] & 0x0f) * 0x100L + regs[1] + 1;
|
||||
}
|
||||
};
|
||||
|
||||
Vrc6_Osc oscs [osc_count];
|
||||
nes_time_t last_time;
|
||||
Vrc6_Osc oscs[osc_count];
|
||||
nes_time_t last_time;
|
||||
|
||||
Blip_Synth<blip_med_quality,1> saw_synth;
|
||||
Blip_Synth<blip_good_quality,1> square_synth;
|
||||
Blip_Synth<blip_med_quality, 1> saw_synth;
|
||||
Blip_Synth<blip_good_quality, 1> square_synth;
|
||||
|
||||
void run_until( nes_time_t );
|
||||
void run_square( Vrc6_Osc& osc, nes_time_t );
|
||||
void run_saw( nes_time_t );
|
||||
void run_until(nes_time_t);
|
||||
void run_square(Vrc6_Osc &osc, nes_time_t);
|
||||
void run_saw(nes_time_t);
|
||||
};
|
||||
|
||||
struct vrc6_apu_state_t
|
||||
{
|
||||
uint8_t regs [3] [3];
|
||||
uint8_t saw_amp;
|
||||
uint16_t delays [3];
|
||||
uint8_t phases [3];
|
||||
uint8_t unused;
|
||||
uint8_t regs[3][3];
|
||||
uint8_t saw_amp;
|
||||
uint16_t delays[3];
|
||||
uint8_t phases[3];
|
||||
uint8_t unused;
|
||||
};
|
||||
static_assert( sizeof (vrc6_apu_state_t) == 20 );
|
||||
static_assert(sizeof(vrc6_apu_state_t) == 20);
|
||||
|
||||
inline void Nes_Vrc6_Apu::osc_output( int i, Blip_Buffer* buf )
|
||||
inline void Nes_Vrc6_Apu::osc_output(int i, Blip_Buffer *buf)
|
||||
{
|
||||
oscs [i].output = buf;
|
||||
oscs[i].output = buf;
|
||||
}
|
||||
|
||||
inline void Nes_Vrc6_Apu::volume( double v )
|
||||
inline void Nes_Vrc6_Apu::volume(double v)
|
||||
{
|
||||
double const factor = 0.0967 * 2;
|
||||
saw_synth.volume( factor / 31 * v );
|
||||
square_synth.volume( factor * 0.5 / 15 * v );
|
||||
double const factor = 0.0967 * 2;
|
||||
saw_synth.volume(factor / 31 * v);
|
||||
square_synth.volume(factor * 0.5 / 15 * v);
|
||||
}
|
||||
|
||||
inline void Nes_Vrc6_Apu::treble_eq( blip_eq_t const& eq )
|
||||
inline void Nes_Vrc6_Apu::treble_eq(blip_eq_t const &eq)
|
||||
{
|
||||
saw_synth.treble_eq( eq );
|
||||
square_synth.treble_eq( eq );
|
||||
saw_synth.treble_eq(eq);
|
||||
square_synth.treble_eq(eq);
|
||||
}
|
||||
|
|
|
@ -2,203 +2,205 @@
|
|||
#include "apu/vrc7/emu2413.hpp"
|
||||
#include <cstring>
|
||||
|
||||
#define BYTESWAP(xxxx) {uint32_t _temp = (uint32_t)(xxxx);\
|
||||
((uint8_t*)&(xxxx))[0] = (uint8_t)((_temp) >> 24);\
|
||||
((uint8_t*)&(xxxx))[1] = (uint8_t)((_temp) >> 16);\
|
||||
((uint8_t*)&(xxxx))[2] = (uint8_t)((_temp) >> 8);\
|
||||
((uint8_t*)&(xxxx))[3] = (uint8_t)((_temp) >> 0);\
|
||||
}
|
||||
#define BYTESWAP(xxxx) \
|
||||
{ \
|
||||
uint32_t _temp = (uint32_t)(xxxx); \
|
||||
((uint8_t *)&(xxxx))[0] = (uint8_t)((_temp) >> 24); \
|
||||
((uint8_t *)&(xxxx))[1] = (uint8_t)((_temp) >> 16); \
|
||||
((uint8_t *)&(xxxx))[2] = (uint8_t)((_temp) >> 8); \
|
||||
((uint8_t *)&(xxxx))[3] = (uint8_t)((_temp) >> 0); \
|
||||
}
|
||||
|
||||
static bool IsLittleEndian()
|
||||
{
|
||||
int i = 42;
|
||||
if (((char*)&i)[0] == 42)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
int i = 42;
|
||||
if (((char *)&i)[0] == 42)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Nes_Vrc7::Nes_Vrc7()
|
||||
{
|
||||
opll = OPLL_new( 3579545 );
|
||||
output( NULL );
|
||||
volume( 1.0 );
|
||||
reset();
|
||||
opll = OPLL_new(3579545);
|
||||
output(NULL);
|
||||
volume(1.0);
|
||||
reset();
|
||||
}
|
||||
|
||||
Nes_Vrc7::~Nes_Vrc7()
|
||||
{
|
||||
OPLL_delete( ( OPLL * ) opll );
|
||||
OPLL_delete((OPLL *)opll);
|
||||
}
|
||||
|
||||
void Nes_Vrc7::reset()
|
||||
{
|
||||
last_time = 0;
|
||||
count = 0;
|
||||
last_time = 0;
|
||||
count = 0;
|
||||
|
||||
for ( int i = 0; i < osc_count; ++i )
|
||||
{
|
||||
Vrc7_Osc& osc = oscs [i];
|
||||
for ( int j = 0; j < 3; ++j )
|
||||
osc.regs [j] = 0;
|
||||
osc.last_amp = 0;
|
||||
}
|
||||
for (int i = 0; i < osc_count; ++i)
|
||||
{
|
||||
Vrc7_Osc &osc = oscs[i];
|
||||
for (int j = 0; j < 3; ++j)
|
||||
osc.regs[j] = 0;
|
||||
osc.last_amp = 0;
|
||||
}
|
||||
|
||||
OPLL_reset( ( OPLL * ) opll );
|
||||
OPLL_reset((OPLL *)opll);
|
||||
}
|
||||
|
||||
void Nes_Vrc7::volume( double v )
|
||||
void Nes_Vrc7::volume(double v)
|
||||
{
|
||||
synth.volume( v * 1. / 3. );
|
||||
synth.volume(v * 1. / 3.);
|
||||
}
|
||||
|
||||
void Nes_Vrc7::treble_eq( blip_eq_t const& eq )
|
||||
void Nes_Vrc7::treble_eq(blip_eq_t const &eq)
|
||||
{
|
||||
synth.treble_eq( eq );
|
||||
synth.treble_eq(eq);
|
||||
}
|
||||
|
||||
void Nes_Vrc7::output( Blip_Buffer* buf )
|
||||
void Nes_Vrc7::output(Blip_Buffer *buf)
|
||||
{
|
||||
for ( int i = 0; i < osc_count; i++ )
|
||||
osc_output( i, buf );
|
||||
for (int i = 0; i < osc_count; i++)
|
||||
osc_output(i, buf);
|
||||
}
|
||||
|
||||
void Nes_Vrc7::run_until( nes_time_t end_time )
|
||||
void Nes_Vrc7::run_until(nes_time_t end_time)
|
||||
{
|
||||
nes_time_t time = last_time;
|
||||
nes_time_t time = last_time;
|
||||
|
||||
while ( time < end_time )
|
||||
{
|
||||
if ( ++count == 36 )
|
||||
{
|
||||
count = 0;
|
||||
bool run = false;
|
||||
for ( unsigned i = 0; i < osc_count; ++i )
|
||||
{
|
||||
Vrc7_Osc & osc = oscs [i];
|
||||
if ( osc.output )
|
||||
{
|
||||
if ( ! run )
|
||||
{
|
||||
run = true;
|
||||
OPLL_run( ( OPLL * ) opll );
|
||||
}
|
||||
int amp = OPLL_calcCh( ( OPLL * ) opll, i );
|
||||
int delta = amp - osc.last_amp;
|
||||
if ( delta )
|
||||
{
|
||||
osc.last_amp = amp;
|
||||
synth.offset( time, delta, osc.output );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
++time;
|
||||
}
|
||||
while (time < end_time)
|
||||
{
|
||||
if (++count == 36)
|
||||
{
|
||||
count = 0;
|
||||
bool run = false;
|
||||
for (unsigned i = 0; i < osc_count; ++i)
|
||||
{
|
||||
Vrc7_Osc &osc = oscs[i];
|
||||
if (osc.output)
|
||||
{
|
||||
if (!run)
|
||||
{
|
||||
run = true;
|
||||
OPLL_run((OPLL *)opll);
|
||||
}
|
||||
int amp = OPLL_calcCh((OPLL *)opll, i);
|
||||
int delta = amp - osc.last_amp;
|
||||
if (delta)
|
||||
{
|
||||
osc.last_amp = amp;
|
||||
synth.offset(time, delta, osc.output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
++time;
|
||||
}
|
||||
|
||||
last_time = end_time;
|
||||
last_time = end_time;
|
||||
}
|
||||
|
||||
void Nes_Vrc7::write_reg( int data )
|
||||
void Nes_Vrc7::write_reg(int data)
|
||||
{
|
||||
OPLL_writeIO( ( OPLL * ) opll, 0, data );
|
||||
OPLL_writeIO((OPLL *)opll, 0, data);
|
||||
}
|
||||
|
||||
void Nes_Vrc7::write_data( nes_time_t time, int data )
|
||||
void Nes_Vrc7::write_data(nes_time_t time, int data)
|
||||
{
|
||||
if ( ( unsigned ) ( ( ( OPLL * ) opll )->adr - 0x10 ) < 0x36 )
|
||||
{
|
||||
int type = ( ( OPLL * ) opll )->adr >> 4;
|
||||
int chan = ( ( OPLL * ) opll )->adr & 15;
|
||||
if ((unsigned)(((OPLL *)opll)->adr - 0x10) < 0x36)
|
||||
{
|
||||
int type = ((OPLL *)opll)->adr >> 4;
|
||||
int chan = ((OPLL *)opll)->adr & 15;
|
||||
|
||||
if ( chan < 6 ) oscs [chan].regs [type-1] = data;
|
||||
}
|
||||
if (chan < 6) oscs[chan].regs[type - 1] = data;
|
||||
}
|
||||
|
||||
run_until( time );
|
||||
OPLL_writeIO( ( OPLL * ) opll, 1, data );
|
||||
run_until(time);
|
||||
OPLL_writeIO((OPLL *)opll, 1, data);
|
||||
}
|
||||
|
||||
void Nes_Vrc7::end_frame( nes_time_t time )
|
||||
void Nes_Vrc7::end_frame(nes_time_t time)
|
||||
{
|
||||
if ( time > last_time )
|
||||
run_until( time );
|
||||
last_time -= time;
|
||||
if (time > last_time)
|
||||
run_until(time);
|
||||
last_time -= time;
|
||||
}
|
||||
|
||||
void Nes_Vrc7::save_snapshot( vrc7_snapshot_t* out )
|
||||
void Nes_Vrc7::save_snapshot(vrc7_snapshot_t *out)
|
||||
{
|
||||
out->latch = ( ( OPLL * ) opll )->adr;
|
||||
memcpy( out->inst, ( ( OPLL * ) opll )->CustInst, 8 );
|
||||
for ( int i = 0; i < osc_count; ++i )
|
||||
{
|
||||
for ( int j = 0; j < 3; ++j )
|
||||
{
|
||||
out->regs [i] [j] = oscs [i].regs [j];
|
||||
}
|
||||
}
|
||||
out->count = count;
|
||||
out->internal_opl_state_size = sizeof(OPLL_STATE);
|
||||
if (!IsLittleEndian())
|
||||
{
|
||||
BYTESWAP(out->internal_opl_state_size);
|
||||
}
|
||||
OPLL_serialize((OPLL*)opll, &(out->internal_opl_state));
|
||||
OPLL_state_byteswap(&(out->internal_opl_state));
|
||||
out->latch = ((OPLL *)opll)->adr;
|
||||
memcpy(out->inst, ((OPLL *)opll)->CustInst, 8);
|
||||
for (int i = 0; i < osc_count; ++i)
|
||||
{
|
||||
for (int j = 0; j < 3; ++j)
|
||||
{
|
||||
out->regs[i][j] = oscs[i].regs[j];
|
||||
}
|
||||
}
|
||||
out->count = count;
|
||||
out->internal_opl_state_size = sizeof(OPLL_STATE);
|
||||
if (!IsLittleEndian())
|
||||
{
|
||||
BYTESWAP(out->internal_opl_state_size);
|
||||
}
|
||||
OPLL_serialize((OPLL *)opll, &(out->internal_opl_state));
|
||||
OPLL_state_byteswap(&(out->internal_opl_state));
|
||||
}
|
||||
|
||||
void Nes_Vrc7::load_snapshot( vrc7_snapshot_t & in, int dataSize )
|
||||
void Nes_Vrc7::load_snapshot(vrc7_snapshot_t &in, int dataSize)
|
||||
{
|
||||
reset();
|
||||
write_reg( in.latch );
|
||||
int i;
|
||||
for ( i = 0; i < osc_count; ++i )
|
||||
{
|
||||
for ( int j = 0; j < 3; ++j )
|
||||
{
|
||||
oscs [i].regs [j] = in.regs [i] [j];
|
||||
}
|
||||
}
|
||||
count = in.count;
|
||||
reset();
|
||||
write_reg(in.latch);
|
||||
int i;
|
||||
for (i = 0; i < osc_count; ++i)
|
||||
{
|
||||
for (int j = 0; j < 3; ++j)
|
||||
{
|
||||
oscs[i].regs[j] = in.regs[i][j];
|
||||
}
|
||||
}
|
||||
count = in.count;
|
||||
|
||||
for ( i = 0; i < 8; ++i )
|
||||
{
|
||||
OPLL_writeReg( ( OPLL * ) opll, i, in.inst [i] );
|
||||
}
|
||||
for (i = 0; i < 8; ++i)
|
||||
{
|
||||
OPLL_writeReg((OPLL *)opll, i, in.inst[i]);
|
||||
}
|
||||
|
||||
for ( i = 0; i < 3; ++i )
|
||||
{
|
||||
for ( int j = 0; j < 6; ++j )
|
||||
{
|
||||
OPLL_writeReg( ( OPLL * ) opll, 0x10 + i * 0x10 + j, oscs [j].regs [i] );
|
||||
}
|
||||
}
|
||||
if (!IsLittleEndian())
|
||||
{
|
||||
BYTESWAP(in.internal_opl_state_size);
|
||||
}
|
||||
if (in.internal_opl_state_size == sizeof(OPLL_STATE))
|
||||
{
|
||||
OPLL_state_byteswap(&(in.internal_opl_state));
|
||||
OPLL_deserialize((OPLL*)opll, &(in.internal_opl_state));
|
||||
}
|
||||
update_last_amp();
|
||||
for (i = 0; i < 3; ++i)
|
||||
{
|
||||
for (int j = 0; j < 6; ++j)
|
||||
{
|
||||
OPLL_writeReg((OPLL *)opll, 0x10 + i * 0x10 + j, oscs[j].regs[i]);
|
||||
}
|
||||
}
|
||||
if (!IsLittleEndian())
|
||||
{
|
||||
BYTESWAP(in.internal_opl_state_size);
|
||||
}
|
||||
if (in.internal_opl_state_size == sizeof(OPLL_STATE))
|
||||
{
|
||||
OPLL_state_byteswap(&(in.internal_opl_state));
|
||||
OPLL_deserialize((OPLL *)opll, &(in.internal_opl_state));
|
||||
}
|
||||
update_last_amp();
|
||||
}
|
||||
|
||||
void Nes_Vrc7::update_last_amp()
|
||||
{
|
||||
for (unsigned i = 0; i < osc_count; ++i)
|
||||
{
|
||||
Vrc7_Osc & osc = oscs[i];
|
||||
if (osc.output)
|
||||
{
|
||||
int amp = OPLL_calcCh((OPLL *)opll, i);
|
||||
int delta = amp - osc.last_amp;
|
||||
if (delta)
|
||||
{
|
||||
osc.last_amp = amp;
|
||||
synth.offset(last_time, delta, osc.output);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; i < osc_count; ++i)
|
||||
{
|
||||
Vrc7_Osc &osc = oscs[i];
|
||||
if (osc.output)
|
||||
{
|
||||
int amp = OPLL_calcCh((OPLL *)opll, i);
|
||||
int delta = amp - osc.last_amp;
|
||||
if (delta)
|
||||
{
|
||||
osc.last_amp = amp;
|
||||
synth.offset(last_time, delta, osc.output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,68 +4,71 @@
|
|||
// Konami VRC7 sound chip emulator
|
||||
// Nes_Snd_Emu 0.1.7. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
|
||||
|
||||
#include <cstdint>
|
||||
#include "apu/vrc7/emu2413_state.hpp"
|
||||
#include "apu/Blip_Buffer.hpp"
|
||||
#include "apu/vrc7/emu2413_state.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
struct vrc7_snapshot_t;
|
||||
typedef long nes_time_t;
|
||||
|
||||
class Nes_Vrc7 {
|
||||
public:
|
||||
Nes_Vrc7();
|
||||
~Nes_Vrc7();
|
||||
class Nes_Vrc7
|
||||
{
|
||||
public:
|
||||
Nes_Vrc7();
|
||||
~Nes_Vrc7();
|
||||
|
||||
// See Nes_Apu.h for reference
|
||||
void reset();
|
||||
void volume( double );
|
||||
void treble_eq( blip_eq_t const& );
|
||||
void output( Blip_Buffer* );
|
||||
enum { osc_count = 6 };
|
||||
void osc_output( int index, Blip_Buffer* );
|
||||
void end_frame( nes_time_t );
|
||||
void save_snapshot(vrc7_snapshot_t*);
|
||||
void load_snapshot(vrc7_snapshot_t &, int dataSize);
|
||||
void update_last_amp();
|
||||
// See Nes_Apu.h for reference
|
||||
void reset();
|
||||
void volume(double);
|
||||
void treble_eq(blip_eq_t const &);
|
||||
void output(Blip_Buffer *);
|
||||
enum
|
||||
{
|
||||
osc_count = 6
|
||||
};
|
||||
void osc_output(int index, Blip_Buffer *);
|
||||
void end_frame(nes_time_t);
|
||||
void save_snapshot(vrc7_snapshot_t *);
|
||||
void load_snapshot(vrc7_snapshot_t &, int dataSize);
|
||||
void update_last_amp();
|
||||
|
||||
void write_reg( int reg );
|
||||
void write_data( nes_time_t, int data );
|
||||
void write_reg(int reg);
|
||||
void write_data(nes_time_t, int data);
|
||||
|
||||
private:
|
||||
// noncopyable
|
||||
Nes_Vrc7( const Nes_Vrc7& );
|
||||
Nes_Vrc7& operator = ( const Nes_Vrc7& );
|
||||
private:
|
||||
// noncopyable
|
||||
Nes_Vrc7(const Nes_Vrc7 &);
|
||||
Nes_Vrc7 &operator=(const Nes_Vrc7 &);
|
||||
|
||||
struct Vrc7_Osc
|
||||
{
|
||||
uint8_t regs [3];
|
||||
Blip_Buffer* output;
|
||||
int last_amp;
|
||||
};
|
||||
struct Vrc7_Osc
|
||||
{
|
||||
uint8_t regs[3];
|
||||
Blip_Buffer *output;
|
||||
int last_amp;
|
||||
};
|
||||
|
||||
void * opll;
|
||||
nes_time_t last_time;
|
||||
void *opll;
|
||||
nes_time_t last_time;
|
||||
|
||||
Blip_Synth<blip_med_quality,2048*2> synth; // DB2LIN_AMP_BITS == 11, * 2
|
||||
int count;
|
||||
Vrc7_Osc oscs [osc_count];
|
||||
Blip_Synth<blip_med_quality, 2048 * 2> synth; // DB2LIN_AMP_BITS == 11, * 2
|
||||
int count;
|
||||
Vrc7_Osc oscs[osc_count];
|
||||
|
||||
void run_until( nes_time_t );
|
||||
void run_until(nes_time_t);
|
||||
};
|
||||
|
||||
struct vrc7_snapshot_t
|
||||
{
|
||||
uint8_t latch;
|
||||
uint8_t inst [8];
|
||||
uint8_t regs [6] [3];
|
||||
uint8_t count;
|
||||
int internal_opl_state_size;
|
||||
OPLL_STATE internal_opl_state;
|
||||
uint8_t latch;
|
||||
uint8_t inst[8];
|
||||
uint8_t regs[6][3];
|
||||
uint8_t count;
|
||||
int internal_opl_state_size;
|
||||
OPLL_STATE internal_opl_state;
|
||||
};
|
||||
static_assert( sizeof (vrc7_snapshot_t) == 28 + 440 + 4 );
|
||||
static_assert(sizeof(vrc7_snapshot_t) == 28 + 440 + 4);
|
||||
|
||||
inline void Nes_Vrc7::osc_output( int i, Blip_Buffer* buf )
|
||||
inline void Nes_Vrc7::osc_output(int i, Blip_Buffer *buf)
|
||||
{
|
||||
oscs [i].output = buf;
|
||||
oscs[i].output = buf;
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,56 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
typedef signed int e_int;
|
||||
typedef unsigned int e_uint;
|
||||
typedef signed char e_int8;
|
||||
typedef unsigned char e_uint8;
|
||||
typedef signed short e_int16;
|
||||
typedef signed int e_int;
|
||||
typedef unsigned int e_uint;
|
||||
typedef signed char e_int8;
|
||||
typedef unsigned char e_uint8;
|
||||
typedef signed short e_int16;
|
||||
typedef unsigned short e_uint16;
|
||||
typedef signed int e_int32;
|
||||
typedef unsigned int e_uint32;
|
||||
typedef signed int e_int32;
|
||||
typedef unsigned int e_uint32;
|
||||
|
||||
#define INLINE inline
|
||||
|
||||
/* Size of Sintable ( 8 -- 18 can be used. 9 recommended.)*/
|
||||
#define PG_BITS 9
|
||||
#define PG_WIDTH (1<<PG_BITS)
|
||||
#define PG_WIDTH (1 << PG_BITS)
|
||||
|
||||
/* Phase increment counter */
|
||||
#define DP_BITS 18
|
||||
#define DP_WIDTH (1<<DP_BITS)
|
||||
#define DP_WIDTH (1 << DP_BITS)
|
||||
#define DP_BASE_BITS (DP_BITS - PG_BITS)
|
||||
|
||||
/* Dynamic range (Accuracy of sin table) */
|
||||
#define DB_BITS 8
|
||||
#define DB_STEP (48.0/(1<<DB_BITS))
|
||||
#define DB_MUTE (1<<DB_BITS)
|
||||
#define DB_STEP (48.0 / (1 << DB_BITS))
|
||||
#define DB_MUTE (1 << DB_BITS)
|
||||
|
||||
/* Dynamic range of envelope */
|
||||
#define EG_STEP 0.375
|
||||
#define EG_BITS 7
|
||||
#define EG_MUTE (1<<EG_BITS)
|
||||
#define EG_MUTE (1 << EG_BITS)
|
||||
|
||||
/* Dynamic range of total level */
|
||||
#define TL_STEP 0.75
|
||||
#define TL_BITS 6
|
||||
#define TL_MUTE (1<<TL_BITS)
|
||||
#define TL_MUTE (1 << TL_BITS)
|
||||
|
||||
/* Dynamic range of sustine level */
|
||||
#define SL_STEP 3.0
|
||||
#define SL_BITS 4
|
||||
#define SL_MUTE (1<<SL_BITS)
|
||||
|
||||
#define SL_MUTE (1 << SL_BITS)
|
||||
|
||||
/* Bits for Pitch and Amp modulator */
|
||||
#define PM_PG_BITS 8
|
||||
#define PM_PG_WIDTH (1<<PM_PG_BITS)
|
||||
#define PM_PG_WIDTH (1 << PM_PG_BITS)
|
||||
#define PM_DP_BITS 16
|
||||
#define PM_DP_WIDTH (1<<PM_DP_BITS)
|
||||
#define PM_DP_WIDTH (1 << PM_DP_BITS)
|
||||
#define AM_PG_BITS 8
|
||||
#define AM_PG_WIDTH (1<<AM_PG_BITS)
|
||||
#define AM_PG_WIDTH (1 << AM_PG_BITS)
|
||||
#define AM_DP_BITS 16
|
||||
#define AM_DP_WIDTH (1<<AM_DP_BITS)
|
||||
|
||||
#define AM_DP_WIDTH (1 << AM_DP_BITS)
|
||||
|
||||
#ifdef EMU2413_DLL_EXPORTS
|
||||
#define EMU2413_API __declspec(dllexport)
|
||||
|
@ -61,156 +59,160 @@ typedef unsigned int e_uint32;
|
|||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#define PI 3.14159265358979323846
|
||||
|
||||
enum {OPLL_VRC7_TONE=0} ;
|
||||
enum
|
||||
{
|
||||
OPLL_VRC7_TONE = 0
|
||||
};
|
||||
|
||||
/* voice data */
|
||||
typedef struct {
|
||||
e_uint32 TL,FB,EG,ML,AR,DR,SL,RR,KR,KL,AM,PM,WF ;
|
||||
} OPLL_PATCH ;
|
||||
/* voice data */
|
||||
typedef struct
|
||||
{
|
||||
e_uint32 TL, FB, EG, ML, AR, DR, SL, RR, KR, KL, AM, PM, WF;
|
||||
} OPLL_PATCH;
|
||||
|
||||
/* slot */
|
||||
typedef struct {
|
||||
/* slot */
|
||||
typedef struct
|
||||
{
|
||||
OPLL_PATCH patch;
|
||||
|
||||
OPLL_PATCH patch;
|
||||
e_int32 type; /* 0 : modulator 1 : carrier */
|
||||
|
||||
e_int32 type ; /* 0 : modulator 1 : carrier */
|
||||
/* OUTPUT */
|
||||
e_int32 feedback;
|
||||
e_int32 output[2]; /* Output value of slot */
|
||||
|
||||
/* OUTPUT */
|
||||
e_int32 feedback ;
|
||||
e_int32 output[2] ; /* Output value of slot */
|
||||
/* for Phase Generator (PG) */
|
||||
e_uint16 *sintbl; /* Wavetable */
|
||||
e_uint32 phase; /* Phase */
|
||||
e_uint32 dphase; /* Phase increment amount */
|
||||
e_uint32 pgout; /* output */
|
||||
|
||||
/* for Phase Generator (PG) */
|
||||
e_uint16 *sintbl ; /* Wavetable */
|
||||
e_uint32 phase ; /* Phase */
|
||||
e_uint32 dphase ; /* Phase increment amount */
|
||||
e_uint32 pgout ; /* output */
|
||||
/* for Envelope Generator (EG) */
|
||||
e_int32 fnum; /* F-Number */
|
||||
e_int32 block; /* Block */
|
||||
e_int32 volume; /* Current volume */
|
||||
e_int32 sustine; /* Sustine 1 = ON, 0 = OFF */
|
||||
e_uint32 tll; /* Total Level + Key scale level*/
|
||||
e_uint32 rks; /* Key scale offset (Rks) */
|
||||
e_int32 eg_mode; /* Current state */
|
||||
e_uint32 eg_phase; /* Phase */
|
||||
e_uint32 eg_dphase; /* Phase increment amount */
|
||||
e_uint32 egout; /* output */
|
||||
|
||||
/* for Envelope Generator (EG) */
|
||||
e_int32 fnum ; /* F-Number */
|
||||
e_int32 block ; /* Block */
|
||||
e_int32 volume ; /* Current volume */
|
||||
e_int32 sustine ; /* Sustine 1 = ON, 0 = OFF */
|
||||
e_uint32 tll ; /* Total Level + Key scale level*/
|
||||
e_uint32 rks ; /* Key scale offset (Rks) */
|
||||
e_int32 eg_mode ; /* Current state */
|
||||
e_uint32 eg_phase ; /* Phase */
|
||||
e_uint32 eg_dphase ; /* Phase increment amount */
|
||||
e_uint32 egout ; /* output */
|
||||
|
||||
} OPLL_SLOT ;
|
||||
} OPLL_SLOT;
|
||||
|
||||
/* Mask */
|
||||
#define OPLL_MASK_CH(x) (1<<(x))
|
||||
#define OPLL_MASK_CH(x) (1 << (x))
|
||||
|
||||
/* opll */
|
||||
typedef struct {
|
||||
|
||||
e_uint32 adr ;
|
||||
e_int32 out ;
|
||||
/* opll */
|
||||
typedef struct
|
||||
{
|
||||
e_uint32 adr;
|
||||
e_int32 out;
|
||||
|
||||
#ifndef EMU2413_COMPACTION
|
||||
e_uint32 realstep ;
|
||||
e_uint32 oplltime ;
|
||||
e_uint32 opllstep ;
|
||||
e_int32 prev, next ;
|
||||
e_uint32 realstep;
|
||||
e_uint32 oplltime;
|
||||
e_uint32 opllstep;
|
||||
e_int32 prev, next;
|
||||
#endif
|
||||
|
||||
/* Register */
|
||||
e_uint8 LowFreq[6];
|
||||
e_uint8 HiFreq[6];
|
||||
e_uint8 InstVol[6];
|
||||
/* Register */
|
||||
e_uint8 LowFreq[6];
|
||||
e_uint8 HiFreq[6];
|
||||
e_uint8 InstVol[6];
|
||||
|
||||
e_uint8 CustInst[8];
|
||||
e_uint8 CustInst[8];
|
||||
|
||||
e_int32 slot_on_flag[6 * 2] ;
|
||||
e_int32 slot_on_flag[6 * 2];
|
||||
|
||||
/* Pitch Modulator */
|
||||
e_uint32 pm_phase ;
|
||||
e_int32 lfo_pm ;
|
||||
/* Pitch Modulator */
|
||||
e_uint32 pm_phase;
|
||||
e_int32 lfo_pm;
|
||||
|
||||
/* Amp Modulator */
|
||||
e_int32 am_phase ;
|
||||
e_int32 lfo_am ;
|
||||
/* Amp Modulator */
|
||||
e_int32 am_phase;
|
||||
e_int32 lfo_am;
|
||||
|
||||
e_uint32 quality;
|
||||
e_uint32 quality;
|
||||
|
||||
/* Channel Data */
|
||||
e_int32 patch_number[6];
|
||||
e_int32 key_status[6] ;
|
||||
/* Channel Data */
|
||||
e_int32 patch_number[6];
|
||||
e_int32 key_status[6];
|
||||
|
||||
/* Slot */
|
||||
OPLL_SLOT slot[6 * 2] ;
|
||||
/* Slot */
|
||||
OPLL_SLOT slot[6 * 2];
|
||||
|
||||
e_uint32 mask ;
|
||||
e_uint32 mask;
|
||||
|
||||
/* Input clock */
|
||||
e_uint32 clk;
|
||||
/* Input clock */
|
||||
e_uint32 clk;
|
||||
|
||||
/* WaveTable for each envelope amp */
|
||||
e_uint16 fullsintable[PG_WIDTH];
|
||||
e_uint16 halfsintable[PG_WIDTH];
|
||||
/* WaveTable for each envelope amp */
|
||||
e_uint16 fullsintable[PG_WIDTH];
|
||||
e_uint16 halfsintable[PG_WIDTH];
|
||||
|
||||
e_uint16 *waveform[2];
|
||||
e_uint16 *waveform[2];
|
||||
|
||||
/* LFO Table */
|
||||
e_int32 pmtable[PM_PG_WIDTH];
|
||||
e_int32 amtable[AM_PG_WIDTH];
|
||||
/* LFO Table */
|
||||
e_int32 pmtable[PM_PG_WIDTH];
|
||||
e_int32 amtable[AM_PG_WIDTH];
|
||||
|
||||
/* Phase delta for LFO */
|
||||
e_uint32 pm_dphase;
|
||||
e_uint32 am_dphase;
|
||||
/* Phase delta for LFO */
|
||||
e_uint32 pm_dphase;
|
||||
e_uint32 am_dphase;
|
||||
|
||||
/* dB to Liner table */
|
||||
e_int16 DB2LIN_TABLE[(DB_MUTE + DB_MUTE) * 2];
|
||||
/* dB to Liner table */
|
||||
e_int16 DB2LIN_TABLE[(DB_MUTE + DB_MUTE) * 2];
|
||||
|
||||
/* Liner to Log curve conversion table (for Attack rate). */
|
||||
e_uint16 AR_ADJUST_TABLE[1 << EG_BITS];
|
||||
/* Liner to Log curve conversion table (for Attack rate). */
|
||||
e_uint16 AR_ADJUST_TABLE[1 << EG_BITS];
|
||||
|
||||
/* Phase incr table for Attack */
|
||||
e_uint32 dphaseARTable[16][16];
|
||||
/* Phase incr table for Attack */
|
||||
e_uint32 dphaseARTable[16][16];
|
||||
|
||||
/* Phase incr table for Decay and Release */
|
||||
e_uint32 dphaseDRTable[16][16];
|
||||
/* Phase incr table for Decay and Release */
|
||||
e_uint32 dphaseDRTable[16][16];
|
||||
|
||||
/* KSL + TL Table */
|
||||
e_uint32 tllTable[16][8][1 << TL_BITS][4];
|
||||
e_int32 rksTable[2][8][2];
|
||||
/* KSL + TL Table */
|
||||
e_uint32 tllTable[16][8][1 << TL_BITS][4];
|
||||
e_int32 rksTable[2][8][2];
|
||||
|
||||
/* Phase incr table for PG */
|
||||
e_uint32 dphaseTable[512][8][16];
|
||||
} OPLL ;
|
||||
/* Phase incr table for PG */
|
||||
e_uint32 dphaseTable[512][8][16];
|
||||
} OPLL;
|
||||
|
||||
/* Create Object */
|
||||
EMU2413_API OPLL *OPLL_new(e_uint32 clk) ;
|
||||
EMU2413_API void OPLL_delete(OPLL *) ;
|
||||
/* Create Object */
|
||||
EMU2413_API OPLL *OPLL_new(e_uint32 clk);
|
||||
EMU2413_API void OPLL_delete(OPLL *);
|
||||
|
||||
/* Setup */
|
||||
EMU2413_API void OPLL_reset(OPLL *) ;
|
||||
//EMU2413_API void OPLL_set_rate(OPLL *opll, e_uint32 r) ;
|
||||
/* Setup */
|
||||
EMU2413_API void OPLL_reset(OPLL *);
|
||||
// EMU2413_API void OPLL_set_rate(OPLL *opll, e_uint32 r) ;
|
||||
|
||||
/* Port/Register access */
|
||||
EMU2413_API void OPLL_writeIO(OPLL *, e_uint32 reg, e_uint32 val) ;
|
||||
EMU2413_API void OPLL_writeReg(OPLL *, e_uint32 reg, e_uint32 val) ;
|
||||
/* Port/Register access */
|
||||
EMU2413_API void OPLL_writeIO(OPLL *, e_uint32 reg, e_uint32 val);
|
||||
EMU2413_API void OPLL_writeReg(OPLL *, e_uint32 reg, e_uint32 val);
|
||||
|
||||
/* Synthsize */
|
||||
EMU2413_API e_int16 OPLL_calc(OPLL *) ;
|
||||
/* or */
|
||||
EMU2413_API void OPLL_run(OPLL *) ;
|
||||
EMU2413_API e_uint32 OPLL_calcCh(OPLL *, e_uint32 ch) ;
|
||||
/* Synthsize */
|
||||
EMU2413_API e_int16 OPLL_calc(OPLL *);
|
||||
/* or */
|
||||
EMU2413_API void OPLL_run(OPLL *);
|
||||
EMU2413_API e_uint32 OPLL_calcCh(OPLL *, e_uint32 ch);
|
||||
|
||||
/* Misc */
|
||||
EMU2413_API void OPLL_forceRefresh(OPLL *) ;
|
||||
/* Misc */
|
||||
EMU2413_API void OPLL_forceRefresh(OPLL *);
|
||||
|
||||
/* Channel Mask */
|
||||
EMU2413_API e_uint32 OPLL_setMask(OPLL *, e_uint32 mask) ;
|
||||
EMU2413_API e_uint32 OPLL_toggleMask(OPLL *, e_uint32 mask) ;
|
||||
/* Channel Mask */
|
||||
EMU2413_API e_uint32 OPLL_setMask(OPLL *, e_uint32 mask);
|
||||
EMU2413_API e_uint32 OPLL_toggleMask(OPLL *, e_uint32 mask);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1,106 +1,110 @@
|
|||
#include <stdint.h>
|
||||
#include "emu2413_state.hpp"
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
int OPLL_serialize_size()
|
||||
{
|
||||
return sizeof(OPLL_STATE);
|
||||
}
|
||||
int OPLL_serialize_size()
|
||||
{
|
||||
return sizeof(OPLL_STATE);
|
||||
}
|
||||
|
||||
void OPLL_serialize(const OPLL * opll, OPLL_STATE* state)
|
||||
{
|
||||
int i;
|
||||
void OPLL_serialize(const OPLL *opll, OPLL_STATE *state)
|
||||
{
|
||||
int i;
|
||||
|
||||
state->pm_phase = opll->pm_phase;
|
||||
state->am_phase = opll->am_phase;
|
||||
state->pm_phase = opll->pm_phase;
|
||||
state->am_phase = opll->am_phase;
|
||||
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
OPLL_SLOT_STATE *slotState = &(state->slot[i]);
|
||||
const OPLL_SLOT *slot= &(opll->slot[i]);
|
||||
slotState->feedback = slot->feedback;
|
||||
slotState->output[0] = slot->output[0];
|
||||
slotState->output[1] = slot->output[1];
|
||||
slotState->phase = slot->phase;
|
||||
slotState->pgout = slot->pgout;
|
||||
slotState->eg_mode = slot->eg_mode;
|
||||
slotState->eg_phase = slot->eg_phase;
|
||||
slotState->eg_dphase = slot->eg_dphase;
|
||||
slotState->egout = slot->egout;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
OPLL_SLOT_STATE *slotState = &(state->slot[i]);
|
||||
const OPLL_SLOT *slot = &(opll->slot[i]);
|
||||
slotState->feedback = slot->feedback;
|
||||
slotState->output[0] = slot->output[0];
|
||||
slotState->output[1] = slot->output[1];
|
||||
slotState->phase = slot->phase;
|
||||
slotState->pgout = slot->pgout;
|
||||
slotState->eg_mode = slot->eg_mode;
|
||||
slotState->eg_phase = slot->eg_phase;
|
||||
slotState->eg_dphase = slot->eg_dphase;
|
||||
slotState->egout = slot->egout;
|
||||
}
|
||||
}
|
||||
|
||||
#define BYTESWAP(xxxx) {uint32_t _temp = (uint32_t)(xxxx);\
|
||||
((uint8_t*)&(xxxx))[0] = (uint8_t)((_temp) >> 24);\
|
||||
((uint8_t*)&(xxxx))[1] = (uint8_t)((_temp) >> 16);\
|
||||
((uint8_t*)&(xxxx))[2] = (uint8_t)((_temp) >> 8);\
|
||||
((uint8_t*)&(xxxx))[3] = (uint8_t)((_temp) >> 0);\
|
||||
}
|
||||
#define BYTESWAP(xxxx) \
|
||||
{ \
|
||||
uint32_t _temp = (uint32_t)(xxxx); \
|
||||
((uint8_t *)&(xxxx))[0] = (uint8_t)((_temp) >> 24); \
|
||||
((uint8_t *)&(xxxx))[1] = (uint8_t)((_temp) >> 16); \
|
||||
((uint8_t *)&(xxxx))[2] = (uint8_t)((_temp) >> 8); \
|
||||
((uint8_t *)&(xxxx))[3] = (uint8_t)((_temp) >> 0); \
|
||||
}
|
||||
|
||||
#define SET(xxxx, yyyy) \
|
||||
{ \
|
||||
if ((xxxx) != (yyyy)) \
|
||||
{ \
|
||||
(xxxx) = (yyyy); \
|
||||
}
|
||||
|
||||
#define SET(xxxx,yyyy) { if ((xxxx) != (yyyy)) {\
|
||||
(xxxx) = (yyyy);\
|
||||
}
|
||||
void OPLL_deserialize(OPLL *opll, const OPLL_STATE *state)
|
||||
{
|
||||
int i;
|
||||
|
||||
void OPLL_deserialize(OPLL * opll, const OPLL_STATE* state)
|
||||
{
|
||||
int i;
|
||||
opll->pm_phase = state->pm_phase;
|
||||
opll->am_phase = state->am_phase;
|
||||
|
||||
opll->pm_phase = state->pm_phase;
|
||||
opll->am_phase = state->am_phase;
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
const OPLL_SLOT_STATE *slotState = &(state->slot[i]);
|
||||
OPLL_SLOT *slot = &(opll->slot[i]);
|
||||
slot->feedback = slotState->feedback;
|
||||
slot->output[0] = slotState->output[0];
|
||||
slot->output[1] = slotState->output[1];
|
||||
slot->phase = slotState->phase;
|
||||
slot->pgout = slotState->pgout;
|
||||
slot->eg_mode = slotState->eg_mode;
|
||||
slot->eg_phase = slotState->eg_phase;
|
||||
slot->eg_dphase = slotState->eg_dphase;
|
||||
slot->egout = slotState->egout;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
const OPLL_SLOT_STATE *slotState = &(state->slot[i]);
|
||||
OPLL_SLOT *slot = &(opll->slot[i]);
|
||||
slot->feedback = slotState->feedback;
|
||||
slot->output[0] = slotState->output[0];
|
||||
slot->output[1] = slotState->output[1];
|
||||
slot->phase = slotState->phase;
|
||||
slot->pgout = slotState->pgout;
|
||||
slot->eg_mode = slotState->eg_mode;
|
||||
slot->eg_phase = slotState->eg_phase;
|
||||
slot->eg_dphase = slotState->eg_dphase;
|
||||
slot->egout = slotState->egout;
|
||||
}
|
||||
}
|
||||
static bool IsLittleEndian()
|
||||
{
|
||||
int i = 42;
|
||||
if (((char *)&i)[0] == 42)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsLittleEndian()
|
||||
{
|
||||
int i = 42;
|
||||
if (((char*)&i)[0] == 42)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void OPLL_state_byteswap(OPLL_STATE *state)
|
||||
{
|
||||
int i;
|
||||
if (IsLittleEndian()) return;
|
||||
|
||||
void OPLL_state_byteswap(OPLL_STATE *state)
|
||||
{
|
||||
int i;
|
||||
if (IsLittleEndian()) return;
|
||||
BYTESWAP(state->pm_phase);
|
||||
BYTESWAP(state->am_phase);
|
||||
|
||||
BYTESWAP(state->pm_phase);
|
||||
BYTESWAP(state->am_phase);
|
||||
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
OPLL_SLOT_STATE *slotState = &(state->slot[i]);
|
||||
BYTESWAP(slotState->feedback);
|
||||
BYTESWAP(slotState->output[0]);
|
||||
BYTESWAP(slotState->output[1]);
|
||||
BYTESWAP(slotState->phase);
|
||||
BYTESWAP(slotState->pgout);
|
||||
BYTESWAP(slotState->eg_mode);
|
||||
BYTESWAP(slotState->eg_phase);
|
||||
BYTESWAP(slotState->eg_dphase);
|
||||
BYTESWAP(slotState->egout);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
OPLL_SLOT_STATE *slotState = &(state->slot[i]);
|
||||
BYTESWAP(slotState->feedback);
|
||||
BYTESWAP(slotState->output[0]);
|
||||
BYTESWAP(slotState->output[1]);
|
||||
BYTESWAP(slotState->phase);
|
||||
BYTESWAP(slotState->pgout);
|
||||
BYTESWAP(slotState->eg_mode);
|
||||
BYTESWAP(slotState->eg_phase);
|
||||
BYTESWAP(slotState->eg_dphase);
|
||||
BYTESWAP(slotState->egout);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -2,21 +2,23 @@
|
|||
|
||||
#include "emu2413.hpp"
|
||||
|
||||
typedef struct {
|
||||
e_int32 feedback;
|
||||
e_int32 output[2];
|
||||
e_uint32 phase;
|
||||
e_uint32 pgout;
|
||||
e_int32 eg_mode;
|
||||
e_uint32 eg_phase;
|
||||
e_uint32 eg_dphase;
|
||||
e_uint32 egout;
|
||||
typedef struct
|
||||
{
|
||||
e_int32 feedback;
|
||||
e_int32 output[2];
|
||||
e_uint32 phase;
|
||||
e_uint32 pgout;
|
||||
e_int32 eg_mode;
|
||||
e_uint32 eg_phase;
|
||||
e_uint32 eg_dphase;
|
||||
e_uint32 egout;
|
||||
} OPLL_SLOT_STATE;
|
||||
|
||||
typedef struct {
|
||||
e_uint32 pm_phase;
|
||||
e_int32 am_phase;
|
||||
OPLL_SLOT_STATE slot[6 * 2];
|
||||
typedef struct
|
||||
{
|
||||
e_uint32 pm_phase;
|
||||
e_int32 am_phase;
|
||||
OPLL_SLOT_STATE slot[6 * 2];
|
||||
} OPLL_STATE;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -24,10 +26,10 @@ extern "C"
|
|||
{
|
||||
#endif
|
||||
|
||||
int OPLL_serialize_size();
|
||||
void OPLL_serialize(const OPLL * opll, OPLL_STATE* state);
|
||||
void OPLL_deserialize(OPLL * opll, const OPLL_STATE* state);
|
||||
void OPLL_state_byteswap(OPLL_STATE *state);
|
||||
int OPLL_serialize_size();
|
||||
void OPLL_serialize(const OPLL *opll, OPLL_STATE *state);
|
||||
void OPLL_deserialize(OPLL *opll, const OPLL_STATE *state);
|
||||
void OPLL_state_byteswap(OPLL_STATE *state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
|
||||
// Nes_Emu 0.7.0. http://www.slack.net/~ant/
|
||||
|
||||
#include <cstring>
|
||||
#include "mappers/mapper.hpp"
|
||||
#include "Nes_Core.hpp"
|
||||
#include <cstring>
|
||||
|
||||
/* Copyright (C) 2004-2006 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
|
@ -78,10 +78,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
Nes_Mapper::Nes_Mapper()
|
||||
{
|
||||
emu_ = NULL;
|
||||
static char c;
|
||||
state = &c; // TODO: state must not be null?
|
||||
state_size = 0;
|
||||
emu_ = NULL;
|
||||
static char c;
|
||||
state = &c; // TODO: state must not be null?
|
||||
state_size = 0;
|
||||
}
|
||||
|
||||
Nes_Mapper::~Nes_Mapper()
|
||||
|
@ -92,75 +92,75 @@ Nes_Mapper::~Nes_Mapper()
|
|||
// intercepts writes to upper half of memory, and clears registered state.
|
||||
void Nes_Mapper::default_reset_state()
|
||||
{
|
||||
int mirroring = cart_->mirroring();
|
||||
if ( mirroring & 8 )
|
||||
mirror_full();
|
||||
else if ( mirroring & 1 )
|
||||
mirror_vert();
|
||||
else
|
||||
mirror_horiz();
|
||||
int mirroring = cart_->mirroring();
|
||||
if (mirroring & 8)
|
||||
mirror_full();
|
||||
else if (mirroring & 1)
|
||||
mirror_vert();
|
||||
else
|
||||
mirror_horiz();
|
||||
|
||||
set_chr_bank( 0, bank_8k, 0 );
|
||||
set_chr_bank(0, bank_8k, 0);
|
||||
|
||||
set_prg_bank( 0x8000, bank_16k, 0 );
|
||||
set_prg_bank( 0xC000, bank_16k, last_bank );
|
||||
set_prg_bank(0x8000, bank_16k, 0);
|
||||
set_prg_bank(0xC000, bank_16k, last_bank);
|
||||
|
||||
intercept_writes( 0x8000, 0x8000 );
|
||||
intercept_writes(0x8000, 0x8000);
|
||||
|
||||
memset( state, 0, state_size );
|
||||
memset(state, 0, state_size);
|
||||
}
|
||||
|
||||
void Nes_Mapper::reset()
|
||||
{
|
||||
default_reset_state();
|
||||
reset_state();
|
||||
apply_mapping();
|
||||
default_reset_state();
|
||||
reset_state();
|
||||
apply_mapping();
|
||||
}
|
||||
|
||||
void mapper_state_t::write( const void* p, unsigned long s )
|
||||
void mapper_state_t::write(const void *p, unsigned long s)
|
||||
{
|
||||
size = s;
|
||||
memcpy( data, p, s );
|
||||
size = s;
|
||||
memcpy(data, p, s);
|
||||
}
|
||||
|
||||
int mapper_state_t::read( void* p, unsigned long s ) const
|
||||
int mapper_state_t::read(void *p, unsigned long s) const
|
||||
{
|
||||
if ( (long) s > size )
|
||||
s = size;
|
||||
memcpy( p, data, s );
|
||||
return s;
|
||||
if ((long)s > size)
|
||||
s = size;
|
||||
memcpy(p, data, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
void Nes_Mapper::save_state( mapper_state_t& out )
|
||||
void Nes_Mapper::save_state(mapper_state_t &out)
|
||||
{
|
||||
out.write( state, state_size );
|
||||
out.write(state, state_size);
|
||||
}
|
||||
|
||||
void Nes_Mapper::load_state( mapper_state_t const& in )
|
||||
void Nes_Mapper::load_state(mapper_state_t const &in)
|
||||
{
|
||||
default_reset_state();
|
||||
read_state( in );
|
||||
apply_mapping();
|
||||
default_reset_state();
|
||||
read_state(in);
|
||||
apply_mapping();
|
||||
}
|
||||
|
||||
void Nes_Mapper::read_state( mapper_state_t const& in )
|
||||
void Nes_Mapper::read_state(mapper_state_t const &in)
|
||||
{
|
||||
memset( state, 0, state_size );
|
||||
in.read( state, state_size );
|
||||
apply_mapping();
|
||||
memset(state, 0, state_size);
|
||||
in.read(state, state_size);
|
||||
apply_mapping();
|
||||
}
|
||||
|
||||
// Timing
|
||||
|
||||
void Nes_Mapper::irq_changed() { emu_->irq_changed(); }
|
||||
|
||||
nes_time_t Nes_Mapper::next_irq( nes_time_t ) { return no_irq; }
|
||||
nes_time_t Nes_Mapper::next_irq(nes_time_t) { return no_irq; }
|
||||
|
||||
void Nes_Mapper::a12_clocked() { }
|
||||
void Nes_Mapper::a12_clocked() {}
|
||||
|
||||
void Nes_Mapper::run_until( nes_time_t ) { }
|
||||
void Nes_Mapper::run_until(nes_time_t) {}
|
||||
|
||||
void Nes_Mapper::end_frame( nes_time_t ) { }
|
||||
void Nes_Mapper::end_frame(nes_time_t) {}
|
||||
|
||||
bool Nes_Mapper::ppu_enabled() const { return emu().ppu.w2001 & 0x08; }
|
||||
|
||||
|
@ -168,123 +168,122 @@ bool Nes_Mapper::ppu_enabled() const { return emu().ppu.w2001 & 0x08; }
|
|||
|
||||
int Nes_Mapper::channel_count() const { return 0; }
|
||||
|
||||
void Nes_Mapper::set_channel_buf( int, Blip_Buffer* ) { }
|
||||
void Nes_Mapper::set_channel_buf(int, Blip_Buffer *) {}
|
||||
|
||||
void Nes_Mapper::set_treble( blip_eq_t const& ) { }
|
||||
void Nes_Mapper::set_treble(blip_eq_t const &) {}
|
||||
|
||||
// Memory mapping
|
||||
|
||||
void Nes_Mapper::set_prg_bank( nes_addr_t addr, bank_size_t bs, int bank )
|
||||
void Nes_Mapper::set_prg_bank(nes_addr_t addr, bank_size_t bs, int bank)
|
||||
{
|
||||
int bank_size = 1 << bs;
|
||||
int bank_size = 1 << bs;
|
||||
|
||||
int bank_count = cart_->prg_size() >> bs;
|
||||
if ( bank < 0 )
|
||||
bank += bank_count;
|
||||
int bank_count = cart_->prg_size() >> bs;
|
||||
if (bank < 0)
|
||||
bank += bank_count;
|
||||
|
||||
if ( bank >= bank_count )
|
||||
bank %= bank_count;
|
||||
if (bank >= bank_count)
|
||||
bank %= bank_count;
|
||||
|
||||
emu().map_code( addr, bank_size, cart_->prg() + (bank << bs) );
|
||||
emu().map_code(addr, bank_size, cart_->prg() + (bank << bs));
|
||||
|
||||
if ( unsigned (addr - 0x6000) < 0x2000 )
|
||||
emu().enable_prg_6000();
|
||||
if (unsigned(addr - 0x6000) < 0x2000)
|
||||
emu().enable_prg_6000();
|
||||
}
|
||||
|
||||
void Nes_Mapper::set_chr_bank( nes_addr_t addr, bank_size_t bs, int bank )
|
||||
void Nes_Mapper::set_chr_bank(nes_addr_t addr, bank_size_t bs, int bank)
|
||||
{
|
||||
emu().ppu.render_until( emu().clock() );
|
||||
emu().ppu.set_chr_bank( addr, 1 << bs, bank << bs );
|
||||
emu().ppu.render_until(emu().clock());
|
||||
emu().ppu.set_chr_bank(addr, 1 << bs, bank << bs);
|
||||
}
|
||||
|
||||
void Nes_Mapper::set_chr_bank_ex( nes_addr_t addr, bank_size_t bs, int bank )
|
||||
void Nes_Mapper::set_chr_bank_ex(nes_addr_t addr, bank_size_t bs, int bank)
|
||||
{
|
||||
emu().ppu.render_until( emu().clock() );
|
||||
emu().ppu.set_chr_bank_ex( addr, 1 << bs, bank << bs );
|
||||
emu().ppu.render_until(emu().clock());
|
||||
emu().ppu.set_chr_bank_ex(addr, 1 << bs, bank << bs);
|
||||
}
|
||||
|
||||
void Nes_Mapper::mirror_manual( int page0, int page1, int page2, int page3 )
|
||||
void Nes_Mapper::mirror_manual(int page0, int page1, int page2, int page3)
|
||||
{
|
||||
emu().ppu.render_bg_until( emu().clock() );
|
||||
emu().ppu.set_nt_banks( page0, page1, page2, page3 );
|
||||
emu().ppu.render_bg_until(emu().clock());
|
||||
emu().ppu.set_nt_banks(page0, page1, page2, page3);
|
||||
}
|
||||
|
||||
|
||||
void Nes_Mapper::intercept_reads( nes_addr_t addr, unsigned size )
|
||||
void Nes_Mapper::intercept_reads(nes_addr_t addr, unsigned size)
|
||||
{
|
||||
emu().add_mapper_intercept( addr, size, true, false );
|
||||
emu().add_mapper_intercept(addr, size, true, false);
|
||||
}
|
||||
|
||||
void Nes_Mapper::intercept_writes( nes_addr_t addr, unsigned size )
|
||||
void Nes_Mapper::intercept_writes(nes_addr_t addr, unsigned size)
|
||||
{
|
||||
emu().add_mapper_intercept( addr, size, false, true );
|
||||
emu().add_mapper_intercept(addr, size, false, true);
|
||||
}
|
||||
|
||||
void Nes_Mapper::enable_sram( bool enabled, bool read_only )
|
||||
void Nes_Mapper::enable_sram(bool enabled, bool read_only)
|
||||
{
|
||||
emu_->enable_sram( enabled, read_only );
|
||||
emu_->enable_sram(enabled, read_only);
|
||||
}
|
||||
|
||||
Nes_Mapper* Nes_Mapper::getMapperFromCode(const int mapperCode)
|
||||
Nes_Mapper *Nes_Mapper::getMapperFromCode(const int mapperCode)
|
||||
{
|
||||
Nes_Mapper* mapper = nullptr;
|
||||
Nes_Mapper *mapper = nullptr;
|
||||
|
||||
// Now checking if the detected mapper code is supported
|
||||
if (mapperCode == 0) mapper = new Mapper000();
|
||||
if (mapperCode == 1) mapper = new Mapper001();
|
||||
if (mapperCode == 2) mapper = new Mapper002();
|
||||
if (mapperCode == 3) mapper = new Mapper003();
|
||||
if (mapperCode == 4) mapper = new Mapper004();
|
||||
if (mapperCode == 5) mapper = new Mapper005();
|
||||
if (mapperCode == 7) mapper = new Mapper007();
|
||||
if (mapperCode == 9) mapper = new Mapper009();
|
||||
if (mapperCode == 10) mapper = new Mapper010();
|
||||
if (mapperCode == 11) mapper = new Mapper011();
|
||||
if (mapperCode == 15) mapper = new Mapper015();
|
||||
if (mapperCode == 19) mapper = new Mapper019();
|
||||
if (mapperCode == 21) mapper = new Mapper021();
|
||||
if (mapperCode == 22) mapper = new Mapper022();
|
||||
if (mapperCode == 23) mapper = new Mapper023();
|
||||
if (mapperCode == 24) mapper = new Mapper024();
|
||||
if (mapperCode == 25) mapper = new Mapper025();
|
||||
if (mapperCode == 26) mapper = new Mapper026();
|
||||
if (mapperCode == 30) mapper = new Mapper030();
|
||||
if (mapperCode == 32) mapper = new Mapper032();
|
||||
if (mapperCode == 33) mapper = new Mapper033();
|
||||
if (mapperCode == 34) mapper = new Mapper034();
|
||||
if (mapperCode == 60) mapper = new Mapper060();
|
||||
if (mapperCode == 66) mapper = new Mapper066();
|
||||
if (mapperCode == 69) mapper = new Mapper069();
|
||||
if (mapperCode == 70) mapper = new Mapper070();
|
||||
if (mapperCode == 71) mapper = new Mapper071();
|
||||
if (mapperCode == 73) mapper = new Mapper073();
|
||||
if (mapperCode == 75) mapper = new Mapper075();
|
||||
if (mapperCode == 78) mapper = new Mapper078();
|
||||
if (mapperCode == 79) mapper = new Mapper079();
|
||||
if (mapperCode == 85) mapper = new Mapper085();
|
||||
if (mapperCode == 86) mapper = new Mapper086();
|
||||
if (mapperCode == 87) mapper = new Mapper087();
|
||||
if (mapperCode == 88) mapper = new Mapper088();
|
||||
if (mapperCode == 89) mapper = new Mapper089();
|
||||
if (mapperCode == 93) mapper = new Mapper093();
|
||||
if (mapperCode == 94) mapper = new Mapper094();
|
||||
if (mapperCode == 97) mapper = new Mapper097();
|
||||
if (mapperCode == 113) mapper = new Mapper113();
|
||||
if (mapperCode == 140) mapper = new Mapper140();
|
||||
if (mapperCode == 152) mapper = new Mapper152();
|
||||
if (mapperCode == 154) mapper = new Mapper154();
|
||||
if (mapperCode == 156) mapper = new Mapper156();
|
||||
if (mapperCode == 180) mapper = new Mapper180();
|
||||
if (mapperCode == 184) mapper = new Mapper184();
|
||||
if (mapperCode == 190) mapper = new Mapper190();
|
||||
if (mapperCode == 193) mapper = new Mapper193();
|
||||
if (mapperCode == 206) mapper = new Mapper206();
|
||||
if (mapperCode == 207) mapper = new Mapper207();
|
||||
if (mapperCode == 232) mapper = new Mapper232();
|
||||
if (mapperCode == 240) mapper = new Mapper240();
|
||||
if (mapperCode == 241) mapper = new Mapper241();
|
||||
if (mapperCode == 244) mapper = new Mapper244();
|
||||
if (mapperCode == 246) mapper = new Mapper246();
|
||||
// Now checking if the detected mapper code is supported
|
||||
if (mapperCode == 0) mapper = new Mapper000();
|
||||
if (mapperCode == 1) mapper = new Mapper001();
|
||||
if (mapperCode == 2) mapper = new Mapper002();
|
||||
if (mapperCode == 3) mapper = new Mapper003();
|
||||
if (mapperCode == 4) mapper = new Mapper004();
|
||||
if (mapperCode == 5) mapper = new Mapper005();
|
||||
if (mapperCode == 7) mapper = new Mapper007();
|
||||
if (mapperCode == 9) mapper = new Mapper009();
|
||||
if (mapperCode == 10) mapper = new Mapper010();
|
||||
if (mapperCode == 11) mapper = new Mapper011();
|
||||
if (mapperCode == 15) mapper = new Mapper015();
|
||||
if (mapperCode == 19) mapper = new Mapper019();
|
||||
if (mapperCode == 21) mapper = new Mapper021();
|
||||
if (mapperCode == 22) mapper = new Mapper022();
|
||||
if (mapperCode == 23) mapper = new Mapper023();
|
||||
if (mapperCode == 24) mapper = new Mapper024();
|
||||
if (mapperCode == 25) mapper = new Mapper025();
|
||||
if (mapperCode == 26) mapper = new Mapper026();
|
||||
if (mapperCode == 30) mapper = new Mapper030();
|
||||
if (mapperCode == 32) mapper = new Mapper032();
|
||||
if (mapperCode == 33) mapper = new Mapper033();
|
||||
if (mapperCode == 34) mapper = new Mapper034();
|
||||
if (mapperCode == 60) mapper = new Mapper060();
|
||||
if (mapperCode == 66) mapper = new Mapper066();
|
||||
if (mapperCode == 69) mapper = new Mapper069();
|
||||
if (mapperCode == 70) mapper = new Mapper070();
|
||||
if (mapperCode == 71) mapper = new Mapper071();
|
||||
if (mapperCode == 73) mapper = new Mapper073();
|
||||
if (mapperCode == 75) mapper = new Mapper075();
|
||||
if (mapperCode == 78) mapper = new Mapper078();
|
||||
if (mapperCode == 79) mapper = new Mapper079();
|
||||
if (mapperCode == 85) mapper = new Mapper085();
|
||||
if (mapperCode == 86) mapper = new Mapper086();
|
||||
if (mapperCode == 87) mapper = new Mapper087();
|
||||
if (mapperCode == 88) mapper = new Mapper088();
|
||||
if (mapperCode == 89) mapper = new Mapper089();
|
||||
if (mapperCode == 93) mapper = new Mapper093();
|
||||
if (mapperCode == 94) mapper = new Mapper094();
|
||||
if (mapperCode == 97) mapper = new Mapper097();
|
||||
if (mapperCode == 113) mapper = new Mapper113();
|
||||
if (mapperCode == 140) mapper = new Mapper140();
|
||||
if (mapperCode == 152) mapper = new Mapper152();
|
||||
if (mapperCode == 154) mapper = new Mapper154();
|
||||
if (mapperCode == 156) mapper = new Mapper156();
|
||||
if (mapperCode == 180) mapper = new Mapper180();
|
||||
if (mapperCode == 184) mapper = new Mapper184();
|
||||
if (mapperCode == 190) mapper = new Mapper190();
|
||||
if (mapperCode == 193) mapper = new Mapper193();
|
||||
if (mapperCode == 206) mapper = new Mapper206();
|
||||
if (mapperCode == 207) mapper = new Mapper207();
|
||||
if (mapperCode == 232) mapper = new Mapper232();
|
||||
if (mapperCode == 240) mapper = new Mapper240();
|
||||
if (mapperCode == 241) mapper = new Mapper241();
|
||||
if (mapperCode == 244) mapper = new Mapper244();
|
||||
if (mapperCode == 246) mapper = new Mapper246();
|
||||
|
||||
return mapper;
|
||||
return mapper;
|
||||
}
|
|
@ -3,9 +3,9 @@
|
|||
// NES mapper interface
|
||||
// Nes_Emu 0.7.0
|
||||
|
||||
#include <climits>
|
||||
#include "Nes_Cart.hpp"
|
||||
#include "Nes_Cpu.hpp"
|
||||
#include <climits>
|
||||
|
||||
class Blip_Buffer;
|
||||
class blip_eq_t;
|
||||
|
@ -13,190 +13,195 @@ class Nes_Core;
|
|||
|
||||
// Increase this (and let me know) if your mapper requires more state. This only
|
||||
// sets the size of the in-memory buffer; it doesn't affect the file format at all.
|
||||
static unsigned const max_mapper_state_size = 512; //was 256, needed more for VRC7 audio state
|
||||
static unsigned const max_mapper_state_size = 512; // was 256, needed more for VRC7 audio state
|
||||
struct mapper_state_t
|
||||
{
|
||||
int size;
|
||||
union {
|
||||
double align;
|
||||
uint8_t data [max_mapper_state_size];
|
||||
};
|
||||
int size;
|
||||
union
|
||||
{
|
||||
double align;
|
||||
uint8_t data[max_mapper_state_size];
|
||||
};
|
||||
|
||||
void write( const void* p, unsigned long s );
|
||||
int read( void* p, unsigned long s ) const;
|
||||
void write(const void *p, unsigned long s);
|
||||
int read(void *p, unsigned long s) const;
|
||||
};
|
||||
|
||||
class Nes_Mapper {
|
||||
public:
|
||||
virtual ~Nes_Mapper();
|
||||
|
||||
// Reset mapper to power-up state.
|
||||
virtual void reset();
|
||||
|
||||
// Save snapshot of mapper state. Default saves registered state.
|
||||
virtual void save_state( mapper_state_t& );
|
||||
|
||||
// Resets mapper, loads state, then applies it
|
||||
virtual void load_state( mapper_state_t const& );
|
||||
|
||||
// I/O
|
||||
|
||||
// Read from memory
|
||||
virtual int read( nes_time_t, nes_addr_t );
|
||||
|
||||
// Write to memory
|
||||
virtual void write( nes_time_t, nes_addr_t, int data ) = 0;
|
||||
|
||||
// Write to memory below 0x8000 (returns false if mapper didn't handle write)
|
||||
virtual bool write_intercepted( nes_time_t, nes_addr_t, int data );
|
||||
|
||||
// Timing
|
||||
|
||||
// Time returned when current mapper state won't ever cause an IRQ
|
||||
enum { no_irq = LONG_MAX / 2 };
|
||||
|
||||
// Time next IRQ will occur at
|
||||
virtual nes_time_t next_irq( nes_time_t present );
|
||||
|
||||
// Run mapper until given time
|
||||
virtual void run_until( nes_time_t );
|
||||
|
||||
// End video frame of given length
|
||||
virtual void end_frame( nes_time_t length );
|
||||
|
||||
// Sound
|
||||
|
||||
// Number of sound channels
|
||||
virtual int channel_count() const;
|
||||
|
||||
// Set sound buffer for channel to output to, or NULL to silence channel.
|
||||
virtual void set_channel_buf( int index, Blip_Buffer* );
|
||||
|
||||
// Set treble equalization
|
||||
virtual void set_treble( blip_eq_t const& );
|
||||
|
||||
// Misc
|
||||
|
||||
// Called when bit 12 of PPU's VRAM address changes from 0 to 1 due to
|
||||
// $2006 and $2007 accesses (but not due to PPU scanline rendering).
|
||||
virtual void a12_clocked();
|
||||
|
||||
void* state;
|
||||
unsigned state_size;
|
||||
|
||||
protected:
|
||||
// Services provided for derived mapper classes
|
||||
Nes_Mapper();
|
||||
|
||||
// Register state data to automatically save and load. Be sure the binary
|
||||
// layout is suitable for use in a file, including any byte-order issues.
|
||||
// Automatically cleared to zero by default reset().
|
||||
void register_state( void*, unsigned );
|
||||
|
||||
// Enable 8K of RAM at 0x6000-0x7FFF, optionally read-only.
|
||||
void enable_sram( bool enabled = true, bool read_only = false );
|
||||
|
||||
// Cause CPU writes within given address range to call mapper's write() function.
|
||||
// Might map a larger address range, which the mapper can ignore and pass to
|
||||
// Nes_Mapper::write(). The range 0x8000-0xffff is always intercepted by the mapper.
|
||||
void intercept_writes( nes_addr_t addr, unsigned size );
|
||||
|
||||
// Cause CPU reads within given address range to call mapper's read() function.
|
||||
// Might map a larger address range, which the mapper can ignore and pass to
|
||||
// Nes_Mapper::read(). CPU opcode/operand reads and low-memory reads always
|
||||
// go directly to memory and cannot be intercepted.
|
||||
void intercept_reads( nes_addr_t addr, unsigned size );
|
||||
|
||||
// Bank sizes for mapping
|
||||
enum bank_size_t { // 1 << bank_Xk = X * 1024
|
||||
bank_1k = 10,
|
||||
bank_2k = 11,
|
||||
bank_4k = 12,
|
||||
bank_8k = 13,
|
||||
bank_16k = 14,
|
||||
bank_32k = 15
|
||||
};
|
||||
|
||||
// Index of last PRG/CHR bank. Last_bank selects last bank, last_bank - 1
|
||||
// selects next-to-last bank, etc.
|
||||
enum { last_bank = -1 };
|
||||
|
||||
// Map 'size' bytes from 'PRG + bank * size' to CPU address space starting at 'addr'
|
||||
void set_prg_bank( nes_addr_t addr, bank_size_t size, int bank );
|
||||
|
||||
// Map 'size' bytes from 'CHR + bank * size' to PPU address space starting at 'addr'
|
||||
void set_chr_bank( nes_addr_t addr, bank_size_t size, int bank );
|
||||
void set_chr_bank_ex( nes_addr_t addr, bank_size_t size, int bank );
|
||||
|
||||
// Set PPU mirroring. All mappings implemented using mirror_manual().
|
||||
void mirror_manual( int page0, int page1, int page2, int page3 );
|
||||
void mirror_single( int page );
|
||||
void mirror_horiz( int page = 0 );
|
||||
void mirror_vert( int page = 0 );
|
||||
void mirror_full();
|
||||
|
||||
// True if PPU rendering is enabled. Some mappers watch PPU memory accesses to determine
|
||||
// when scanlines occur, and can only do this when rendering is enabled.
|
||||
bool ppu_enabled() const;
|
||||
|
||||
// Cartridge being emulated
|
||||
Nes_Cart const& cart() const { return *cart_; }
|
||||
|
||||
// Must be called when next_irq()'s return value is earlier than previous,
|
||||
// current CPU run can be stopped earlier. Best to call whenever time may
|
||||
// have changed (no performance impact if called even when time didn't change).
|
||||
void irq_changed();
|
||||
|
||||
// Handle data written to mapper that doesn't handle bus conflict arising due to
|
||||
// PRG also reading data. Returns data that mapper should act as if were
|
||||
// written. Currently always returns 'data' and just checks that data written is
|
||||
// the same as byte in PRG at same address and writes debug message if it doesn't.
|
||||
int handle_bus_conflict( nes_addr_t addr, int data );
|
||||
|
||||
// Reference to emulator that uses this mapper.
|
||||
Nes_Core& emu() const { return *emu_; }
|
||||
|
||||
protected:
|
||||
// Services derived classes provide
|
||||
|
||||
// Read state from snapshot. Default reads data into registered state, then calls
|
||||
// apply_mapping().
|
||||
virtual void read_state( mapper_state_t const& );
|
||||
|
||||
|
||||
|
||||
// Called by default reset() before apply_mapping() is called.
|
||||
virtual void reset_state() { }
|
||||
|
||||
// End of general interface
|
||||
|
||||
public:
|
||||
|
||||
Nes_Cart const* cart_;
|
||||
Nes_Core* emu_;
|
||||
|
||||
// Apply current mapping state to hardware. Called after reading mapper state
|
||||
// from a snapshot.
|
||||
virtual void apply_mapping() = 0;
|
||||
|
||||
void default_reset_state();
|
||||
|
||||
static Nes_Mapper* getMapperFromCode(const int mapperCode);
|
||||
};
|
||||
|
||||
|
||||
inline int Nes_Mapper::handle_bus_conflict( nes_addr_t addr, int data ) { return data; }
|
||||
inline void Nes_Mapper::mirror_horiz( int p ) { mirror_manual( p, p, p ^ 1, p ^ 1 ); }
|
||||
inline void Nes_Mapper::mirror_vert( int p ) { mirror_manual( p, p ^ 1, p, p ^ 1 ); }
|
||||
inline void Nes_Mapper::mirror_single( int p ) { mirror_manual( p, p, p, p ); }
|
||||
inline void Nes_Mapper::mirror_full() { mirror_manual( 0, 1, 2, 3 ); }
|
||||
|
||||
inline void Nes_Mapper::register_state( void* p, unsigned s )
|
||||
class Nes_Mapper
|
||||
{
|
||||
state = p;
|
||||
state_size = s;
|
||||
public:
|
||||
virtual ~Nes_Mapper();
|
||||
|
||||
// Reset mapper to power-up state.
|
||||
virtual void reset();
|
||||
|
||||
// Save snapshot of mapper state. Default saves registered state.
|
||||
virtual void save_state(mapper_state_t &);
|
||||
|
||||
// Resets mapper, loads state, then applies it
|
||||
virtual void load_state(mapper_state_t const &);
|
||||
|
||||
// I/O
|
||||
|
||||
// Read from memory
|
||||
virtual int read(nes_time_t, nes_addr_t);
|
||||
|
||||
// Write to memory
|
||||
virtual void write(nes_time_t, nes_addr_t, int data) = 0;
|
||||
|
||||
// Write to memory below 0x8000 (returns false if mapper didn't handle write)
|
||||
virtual bool write_intercepted(nes_time_t, nes_addr_t, int data);
|
||||
|
||||
// Timing
|
||||
|
||||
// Time returned when current mapper state won't ever cause an IRQ
|
||||
enum
|
||||
{
|
||||
no_irq = LONG_MAX / 2
|
||||
};
|
||||
|
||||
// Time next IRQ will occur at
|
||||
virtual nes_time_t next_irq(nes_time_t present);
|
||||
|
||||
// Run mapper until given time
|
||||
virtual void run_until(nes_time_t);
|
||||
|
||||
// End video frame of given length
|
||||
virtual void end_frame(nes_time_t length);
|
||||
|
||||
// Sound
|
||||
|
||||
// Number of sound channels
|
||||
virtual int channel_count() const;
|
||||
|
||||
// Set sound buffer for channel to output to, or NULL to silence channel.
|
||||
virtual void set_channel_buf(int index, Blip_Buffer *);
|
||||
|
||||
// Set treble equalization
|
||||
virtual void set_treble(blip_eq_t const &);
|
||||
|
||||
// Misc
|
||||
|
||||
// Called when bit 12 of PPU's VRAM address changes from 0 to 1 due to
|
||||
// $2006 and $2007 accesses (but not due to PPU scanline rendering).
|
||||
virtual void a12_clocked();
|
||||
|
||||
void *state;
|
||||
unsigned state_size;
|
||||
|
||||
protected:
|
||||
// Services provided for derived mapper classes
|
||||
Nes_Mapper();
|
||||
|
||||
// Register state data to automatically save and load. Be sure the binary
|
||||
// layout is suitable for use in a file, including any byte-order issues.
|
||||
// Automatically cleared to zero by default reset().
|
||||
void register_state(void *, unsigned);
|
||||
|
||||
// Enable 8K of RAM at 0x6000-0x7FFF, optionally read-only.
|
||||
void enable_sram(bool enabled = true, bool read_only = false);
|
||||
|
||||
// Cause CPU writes within given address range to call mapper's write() function.
|
||||
// Might map a larger address range, which the mapper can ignore and pass to
|
||||
// Nes_Mapper::write(). The range 0x8000-0xffff is always intercepted by the mapper.
|
||||
void intercept_writes(nes_addr_t addr, unsigned size);
|
||||
|
||||
// Cause CPU reads within given address range to call mapper's read() function.
|
||||
// Might map a larger address range, which the mapper can ignore and pass to
|
||||
// Nes_Mapper::read(). CPU opcode/operand reads and low-memory reads always
|
||||
// go directly to memory and cannot be intercepted.
|
||||
void intercept_reads(nes_addr_t addr, unsigned size);
|
||||
|
||||
// Bank sizes for mapping
|
||||
enum bank_size_t
|
||||
{ // 1 << bank_Xk = X * 1024
|
||||
bank_1k = 10,
|
||||
bank_2k = 11,
|
||||
bank_4k = 12,
|
||||
bank_8k = 13,
|
||||
bank_16k = 14,
|
||||
bank_32k = 15
|
||||
};
|
||||
|
||||
// Index of last PRG/CHR bank. Last_bank selects last bank, last_bank - 1
|
||||
// selects next-to-last bank, etc.
|
||||
enum
|
||||
{
|
||||
last_bank = -1
|
||||
};
|
||||
|
||||
// Map 'size' bytes from 'PRG + bank * size' to CPU address space starting at 'addr'
|
||||
void set_prg_bank(nes_addr_t addr, bank_size_t size, int bank);
|
||||
|
||||
// Map 'size' bytes from 'CHR + bank * size' to PPU address space starting at 'addr'
|
||||
void set_chr_bank(nes_addr_t addr, bank_size_t size, int bank);
|
||||
void set_chr_bank_ex(nes_addr_t addr, bank_size_t size, int bank);
|
||||
|
||||
// Set PPU mirroring. All mappings implemented using mirror_manual().
|
||||
void mirror_manual(int page0, int page1, int page2, int page3);
|
||||
void mirror_single(int page);
|
||||
void mirror_horiz(int page = 0);
|
||||
void mirror_vert(int page = 0);
|
||||
void mirror_full();
|
||||
|
||||
// True if PPU rendering is enabled. Some mappers watch PPU memory accesses to determine
|
||||
// when scanlines occur, and can only do this when rendering is enabled.
|
||||
bool ppu_enabled() const;
|
||||
|
||||
// Cartridge being emulated
|
||||
Nes_Cart const &cart() const { return *cart_; }
|
||||
|
||||
// Must be called when next_irq()'s return value is earlier than previous,
|
||||
// current CPU run can be stopped earlier. Best to call whenever time may
|
||||
// have changed (no performance impact if called even when time didn't change).
|
||||
void irq_changed();
|
||||
|
||||
// Handle data written to mapper that doesn't handle bus conflict arising due to
|
||||
// PRG also reading data. Returns data that mapper should act as if were
|
||||
// written. Currently always returns 'data' and just checks that data written is
|
||||
// the same as byte in PRG at same address and writes debug message if it doesn't.
|
||||
int handle_bus_conflict(nes_addr_t addr, int data);
|
||||
|
||||
// Reference to emulator that uses this mapper.
|
||||
Nes_Core &emu() const { return *emu_; }
|
||||
|
||||
protected:
|
||||
// Services derived classes provide
|
||||
|
||||
// Read state from snapshot. Default reads data into registered state, then calls
|
||||
// apply_mapping().
|
||||
virtual void read_state(mapper_state_t const &);
|
||||
|
||||
// Called by default reset() before apply_mapping() is called.
|
||||
virtual void reset_state() {}
|
||||
|
||||
// End of general interface
|
||||
|
||||
public:
|
||||
Nes_Cart const *cart_;
|
||||
Nes_Core *emu_;
|
||||
|
||||
// Apply current mapping state to hardware. Called after reading mapper state
|
||||
// from a snapshot.
|
||||
virtual void apply_mapping() = 0;
|
||||
|
||||
void default_reset_state();
|
||||
|
||||
static Nes_Mapper *getMapperFromCode(const int mapperCode);
|
||||
};
|
||||
|
||||
inline int Nes_Mapper::handle_bus_conflict(nes_addr_t addr, int data) { return data; }
|
||||
inline void Nes_Mapper::mirror_horiz(int p) { mirror_manual(p, p, p ^ 1, p ^ 1); }
|
||||
inline void Nes_Mapper::mirror_vert(int p) { mirror_manual(p, p ^ 1, p, p ^ 1); }
|
||||
inline void Nes_Mapper::mirror_single(int p) { mirror_manual(p, p, p, p); }
|
||||
inline void Nes_Mapper::mirror_full() { mirror_manual(0, 1, 2, 3); }
|
||||
|
||||
inline void Nes_Mapper::register_state(void *p, unsigned s)
|
||||
{
|
||||
state = p;
|
||||
state_size = s;
|
||||
}
|
||||
|
||||
inline bool Nes_Mapper::write_intercepted( nes_time_t, nes_addr_t, int ) { return false; }
|
||||
inline bool Nes_Mapper::write_intercepted(nes_time_t, nes_addr_t, int) { return false; }
|
||||
|
||||
inline int Nes_Mapper::read( nes_time_t, nes_addr_t ) { return -1; } // signal to caller
|
||||
inline int Nes_Mapper::read(nes_time_t, nes_addr_t) { return -1; } // signal to caller
|
||||
|
|
|
@ -19,14 +19,15 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
// NROM
|
||||
|
||||
class Mapper000 : public Nes_Mapper {
|
||||
public:
|
||||
Mapper000() { }
|
||||
class Mapper000 : public Nes_Mapper
|
||||
{
|
||||
public:
|
||||
Mapper000() {}
|
||||
|
||||
virtual void apply_mapping() { }
|
||||
virtual void apply_mapping() {}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t, int )
|
||||
{
|
||||
// empty
|
||||
}
|
||||
virtual void write(nes_time_t, nes_addr_t, int)
|
||||
{
|
||||
// empty
|
||||
}
|
||||
};
|
||||
|
|
|
@ -20,107 +20,105 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
struct mmc1_state_t
|
||||
{
|
||||
uint8_t regs [4]; // current registers (5 bits each)
|
||||
uint8_t bit; // number of bits in buffer (0 to 4)
|
||||
uint8_t buf; // currently buffered bits (new bits added to bottom)
|
||||
uint8_t regs[4]; // current registers (5 bits each)
|
||||
uint8_t bit; // number of bits in buffer (0 to 4)
|
||||
uint8_t buf; // currently buffered bits (new bits added to bottom)
|
||||
};
|
||||
static_assert( sizeof (mmc1_state_t) == 6 );
|
||||
static_assert(sizeof(mmc1_state_t) == 6);
|
||||
|
||||
class Mapper001 : public Nes_Mapper, mmc1_state_t {
|
||||
public:
|
||||
Mapper001()
|
||||
{
|
||||
mmc1_state_t* state = this;
|
||||
register_state( state, sizeof *state );
|
||||
}
|
||||
class Mapper001 : public Nes_Mapper, mmc1_state_t
|
||||
{
|
||||
public:
|
||||
Mapper001()
|
||||
{
|
||||
mmc1_state_t *state = this;
|
||||
register_state(state, sizeof *state);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{
|
||||
regs [0] = 0x0f;
|
||||
regs [1] = 0x00;
|
||||
regs [2] = 0x01;
|
||||
regs [3] = 0x00;
|
||||
}
|
||||
virtual void reset_state()
|
||||
{
|
||||
regs[0] = 0x0f;
|
||||
regs[1] = 0x00;
|
||||
regs[2] = 0x01;
|
||||
regs[3] = 0x00;
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
enable_sram(); // early MMC1 always had SRAM enabled
|
||||
register_changed( 0 );
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
enable_sram(); // early MMC1 always had SRAM enabled
|
||||
register_changed(0);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
if ( !(data & 0x80) )
|
||||
{
|
||||
buf |= (data & 1) << bit;
|
||||
bit++;
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
if (!(data & 0x80))
|
||||
{
|
||||
buf |= (data & 1) << bit;
|
||||
bit++;
|
||||
|
||||
if ( bit >= 5 )
|
||||
{
|
||||
int reg = addr >> 13 & 3;
|
||||
regs [reg] = buf & 0x1f;
|
||||
if (bit >= 5)
|
||||
{
|
||||
int reg = addr >> 13 & 3;
|
||||
regs[reg] = buf & 0x1f;
|
||||
|
||||
bit = 0;
|
||||
buf = 0;
|
||||
bit = 0;
|
||||
buf = 0;
|
||||
|
||||
register_changed( reg );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bit = 0;
|
||||
buf = 0;
|
||||
regs [0] |= 0x0c;
|
||||
register_changed( 0 );
|
||||
}
|
||||
}
|
||||
register_changed(reg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bit = 0;
|
||||
buf = 0;
|
||||
regs[0] |= 0x0c;
|
||||
register_changed(0);
|
||||
}
|
||||
}
|
||||
|
||||
void register_changed(int reg)
|
||||
{
|
||||
// Mirroring
|
||||
if (reg == 0)
|
||||
{
|
||||
int mode = regs[0] & 3;
|
||||
if (mode < 2)
|
||||
mirror_single(mode & 1);
|
||||
else if (mode == 2)
|
||||
mirror_vert();
|
||||
else
|
||||
mirror_horiz();
|
||||
}
|
||||
|
||||
void register_changed( int reg )
|
||||
{
|
||||
// Mirroring
|
||||
if ( reg == 0 )
|
||||
{
|
||||
int mode = regs [0] & 3;
|
||||
if ( mode < 2 )
|
||||
mirror_single( mode & 1 );
|
||||
else if ( mode == 2 )
|
||||
mirror_vert();
|
||||
else
|
||||
mirror_horiz();
|
||||
}
|
||||
// CHR
|
||||
if (reg < 3 && cart().chr_size() > 0)
|
||||
{
|
||||
if (regs[0] & 0x10)
|
||||
{
|
||||
set_chr_bank(0x0000, bank_4k, regs[1]);
|
||||
set_chr_bank(0x1000, bank_4k, regs[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
set_chr_bank(0, bank_8k, regs[1] >> 1);
|
||||
}
|
||||
}
|
||||
|
||||
// CHR
|
||||
if ( reg < 3 && cart().chr_size() > 0 )
|
||||
{
|
||||
if ( regs [0] & 0x10 )
|
||||
{
|
||||
set_chr_bank( 0x0000, bank_4k, regs [1] );
|
||||
set_chr_bank( 0x1000, bank_4k, regs [2] );
|
||||
}
|
||||
else
|
||||
{
|
||||
set_chr_bank( 0, bank_8k, regs [1] >> 1 );
|
||||
}
|
||||
}
|
||||
|
||||
// PRG
|
||||
int bank = (regs [1] & 0x10) | (regs [3] & 0x0f);
|
||||
if ( !(regs [0] & 0x08) )
|
||||
{
|
||||
set_prg_bank( 0x8000, bank_32k, bank >> 1 );
|
||||
}
|
||||
else if ( regs [0] & 0x04 )
|
||||
{
|
||||
set_prg_bank( 0x8000, bank_16k, bank );
|
||||
set_prg_bank( 0xC000, bank_16k, bank | 0x0f );
|
||||
}
|
||||
else
|
||||
{
|
||||
set_prg_bank( 0x8000, bank_16k, bank & ~0x0f );
|
||||
set_prg_bank( 0xC000, bank_16k, bank );
|
||||
}
|
||||
}
|
||||
// PRG
|
||||
int bank = (regs[1] & 0x10) | (regs[3] & 0x0f);
|
||||
if (!(regs[0] & 0x08))
|
||||
{
|
||||
set_prg_bank(0x8000, bank_32k, bank >> 1);
|
||||
}
|
||||
else if (regs[0] & 0x04)
|
||||
{
|
||||
set_prg_bank(0x8000, bank_16k, bank);
|
||||
set_prg_bank(0xC000, bank_16k, bank | 0x0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
set_prg_bank(0x8000, bank_16k, bank & ~0x0f);
|
||||
set_prg_bank(0xC000, bank_16k, bank);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -19,24 +19,25 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
// UNROM
|
||||
|
||||
class Mapper002 : public Nes_Mapper {
|
||||
uint8_t bank;
|
||||
public:
|
||||
Mapper002()
|
||||
{
|
||||
register_state( &bank, 1 );
|
||||
}
|
||||
class Mapper002 : public Nes_Mapper
|
||||
{
|
||||
uint8_t bank;
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
enable_sram(); // at least one UNROM game needs sram (Bomberman 2)
|
||||
set_prg_bank( 0x8000, bank_16k, bank );
|
||||
}
|
||||
public:
|
||||
Mapper002()
|
||||
{
|
||||
register_state(&bank, 1);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
bank = handle_bus_conflict( addr, data );
|
||||
set_prg_bank( 0x8000, bank_16k, data );
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
enable_sram(); // at least one UNROM game needs sram (Bomberman 2)
|
||||
set_prg_bank(0x8000, bank_16k, bank);
|
||||
}
|
||||
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
bank = handle_bus_conflict(addr, data);
|
||||
set_prg_bank(0x8000, bank_16k, data);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -19,23 +19,24 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
// CNROM
|
||||
|
||||
class Mapper003 : public Nes_Mapper {
|
||||
uint8_t bank;
|
||||
public:
|
||||
Mapper003()
|
||||
{
|
||||
register_state( &bank, 1 );
|
||||
}
|
||||
class Mapper003 : public Nes_Mapper
|
||||
{
|
||||
uint8_t bank;
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
set_chr_bank( 0, bank_8k, bank & 7 );
|
||||
}
|
||||
public:
|
||||
Mapper003()
|
||||
{
|
||||
register_state(&bank, 1);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
bank = handle_bus_conflict( addr, data );
|
||||
set_chr_bank( 0, bank_8k, bank & 7 );
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
set_chr_bank(0, bank_8k, bank & 7);
|
||||
}
|
||||
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
bank = handle_bus_conflict(addr, data);
|
||||
set_chr_bank(0, bank_8k, bank & 7);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
#include "mappers/mapper.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include "Nes_Core.hpp"
|
||||
#include <cstring>
|
||||
|
||||
/* Copyright (C) 2004-2006 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
|
@ -28,225 +28,226 @@ nes_time_t const last_scanline = first_scanline + 240 * Nes_Ppu::scanline_len;
|
|||
|
||||
struct mmc3_state_t
|
||||
{
|
||||
uint8_t banks [8]; // last writes to $8001 indexed by (mode & 7)
|
||||
uint8_t mode; // $8000
|
||||
uint8_t mirror; // $a000
|
||||
uint8_t sram_mode; // $a001
|
||||
uint8_t irq_ctr; // internal counter
|
||||
uint8_t irq_latch; // $c000
|
||||
uint8_t irq_enabled;// last write was to 0) $e000, 1) $e001
|
||||
uint8_t irq_flag;
|
||||
uint8_t banks[8]; // last writes to $8001 indexed by (mode & 7)
|
||||
uint8_t mode; // $8000
|
||||
uint8_t mirror; // $a000
|
||||
uint8_t sram_mode; // $a001
|
||||
uint8_t irq_ctr; // internal counter
|
||||
uint8_t irq_latch; // $c000
|
||||
uint8_t irq_enabled; // last write was to 0) $e000, 1) $e001
|
||||
uint8_t irq_flag;
|
||||
};
|
||||
static_assert( sizeof (mmc3_state_t) == 15 );
|
||||
static_assert(sizeof(mmc3_state_t) == 15);
|
||||
|
||||
class Mapper004 : public Nes_Mapper, mmc3_state_t {
|
||||
public:
|
||||
Mapper004()
|
||||
{
|
||||
mmc3_state_t* state = this;
|
||||
register_state( state, sizeof *state );
|
||||
}
|
||||
class Mapper004 : public Nes_Mapper, mmc3_state_t
|
||||
{
|
||||
public:
|
||||
Mapper004()
|
||||
{
|
||||
mmc3_state_t *state = this;
|
||||
register_state(state, sizeof *state);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{
|
||||
memcpy( banks, "\0\2\4\5\6\7\0\1", sizeof banks );
|
||||
virtual void reset_state()
|
||||
{
|
||||
memcpy(banks, "\0\2\4\5\6\7\0\1", sizeof banks);
|
||||
|
||||
counter_just_clocked = 0;
|
||||
next_time = 0;
|
||||
mirror = 1;
|
||||
counter_just_clocked = 0;
|
||||
next_time = 0;
|
||||
mirror = 1;
|
||||
|
||||
/* Cart specified vertical mirroring */
|
||||
if ( cart().mirroring() & 1 )
|
||||
mirror = 0;
|
||||
}
|
||||
/* Cart specified vertical mirroring */
|
||||
if (cart().mirroring() & 1)
|
||||
mirror = 0;
|
||||
}
|
||||
|
||||
void start_frame() { next_time = first_scanline; }
|
||||
void start_frame() { next_time = first_scanline; }
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
write( 0, 0xA000, mirror );
|
||||
write( 0, 0xA001, sram_mode );
|
||||
update_chr_banks();
|
||||
update_prg_banks();
|
||||
start_frame();
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
write(0, 0xA000, mirror);
|
||||
write(0, 0xA001, sram_mode);
|
||||
update_chr_banks();
|
||||
update_prg_banks();
|
||||
start_frame();
|
||||
}
|
||||
|
||||
void clock_counter()
|
||||
{
|
||||
if ( counter_just_clocked )
|
||||
counter_just_clocked--;
|
||||
void clock_counter()
|
||||
{
|
||||
if (counter_just_clocked)
|
||||
counter_just_clocked--;
|
||||
|
||||
if ( !irq_ctr-- )
|
||||
{
|
||||
irq_ctr = irq_latch;
|
||||
//if ( !irq_latch )
|
||||
//dprintf( "MMC3 IRQ counter reloaded with 0\n" );
|
||||
}
|
||||
if (!irq_ctr--)
|
||||
{
|
||||
irq_ctr = irq_latch;
|
||||
// if ( !irq_latch )
|
||||
// dprintf( "MMC3 IRQ counter reloaded with 0\n" );
|
||||
}
|
||||
|
||||
//dprintf( "%6d MMC3 IRQ clocked\n", time / ppu_overclock );
|
||||
if ( irq_ctr == 0 )
|
||||
{
|
||||
//if ( irq_enabled && !irq_flag )
|
||||
//dprintf( "%6d MMC3 IRQ triggered: %f\n", time / ppu_overclock, time / scanline_len.0 - 20 );
|
||||
irq_flag = irq_enabled;
|
||||
}
|
||||
}
|
||||
// dprintf( "%6d MMC3 IRQ clocked\n", time / ppu_overclock );
|
||||
if (irq_ctr == 0)
|
||||
{
|
||||
// if ( irq_enabled && !irq_flag )
|
||||
// dprintf( "%6d MMC3 IRQ triggered: %f\n", time / ppu_overclock, time / scanline_len.0 - 20 );
|
||||
irq_flag = irq_enabled;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void a12_clocked()
|
||||
{
|
||||
clock_counter();
|
||||
if (irq_enabled)
|
||||
irq_changed();
|
||||
}
|
||||
|
||||
virtual void a12_clocked()
|
||||
{
|
||||
clock_counter();
|
||||
if ( irq_enabled )
|
||||
irq_changed();
|
||||
}
|
||||
virtual void end_frame(nes_time_t end_time)
|
||||
{
|
||||
run_until(end_time);
|
||||
start_frame();
|
||||
}
|
||||
|
||||
virtual void end_frame( nes_time_t end_time )
|
||||
{
|
||||
run_until( end_time );
|
||||
start_frame();
|
||||
}
|
||||
virtual nes_time_t next_irq(nes_time_t present)
|
||||
{
|
||||
run_until(present);
|
||||
|
||||
virtual nes_time_t next_irq( nes_time_t present )
|
||||
{
|
||||
run_until( present );
|
||||
if (!irq_enabled)
|
||||
return no_irq;
|
||||
|
||||
if ( !irq_enabled )
|
||||
return no_irq;
|
||||
if (irq_flag)
|
||||
return 0;
|
||||
|
||||
if ( irq_flag )
|
||||
return 0;
|
||||
if (!ppu_enabled())
|
||||
return no_irq;
|
||||
|
||||
if ( !ppu_enabled() )
|
||||
return no_irq;
|
||||
int remain = irq_ctr - 1;
|
||||
if (remain < 0)
|
||||
remain = irq_latch;
|
||||
|
||||
int remain = irq_ctr - 1;
|
||||
if ( remain < 0 )
|
||||
remain = irq_latch;
|
||||
long time = remain * 341L + next_time;
|
||||
if (time > last_scanline)
|
||||
return no_irq;
|
||||
|
||||
long time = remain * 341L + next_time;
|
||||
if ( time > last_scanline )
|
||||
return no_irq;
|
||||
return time / ppu_overclock + 1;
|
||||
}
|
||||
|
||||
return time / ppu_overclock + 1;
|
||||
}
|
||||
void run_until(nes_time_t end_time)
|
||||
{
|
||||
bool bg_enabled = ppu_enabled();
|
||||
|
||||
void run_until( nes_time_t end_time )
|
||||
{
|
||||
bool bg_enabled = ppu_enabled();
|
||||
if (next_time < 0) next_time = 0;
|
||||
|
||||
if (next_time < 0) next_time = 0;
|
||||
end_time *= ppu_overclock;
|
||||
while (next_time < end_time && next_time <= last_scanline)
|
||||
{
|
||||
if (bg_enabled)
|
||||
clock_counter();
|
||||
next_time += Nes_Ppu::scanline_len;
|
||||
}
|
||||
}
|
||||
|
||||
end_time *= ppu_overclock;
|
||||
while ( next_time < end_time && next_time <= last_scanline )
|
||||
{
|
||||
if ( bg_enabled )
|
||||
clock_counter();
|
||||
next_time += Nes_Ppu::scanline_len;
|
||||
}
|
||||
}
|
||||
void update_chr_banks()
|
||||
{
|
||||
int chr_xor = (mode >> 7 & 1) * 0x1000;
|
||||
set_chr_bank(0x0000 ^ chr_xor, bank_2k, banks[0] >> 1);
|
||||
set_chr_bank(0x0800 ^ chr_xor, bank_2k, banks[1] >> 1);
|
||||
set_chr_bank(0x1000 ^ chr_xor, bank_1k, banks[2]);
|
||||
set_chr_bank(0x1400 ^ chr_xor, bank_1k, banks[3]);
|
||||
set_chr_bank(0x1800 ^ chr_xor, bank_1k, banks[4]);
|
||||
set_chr_bank(0x1c00 ^ chr_xor, bank_1k, banks[5]);
|
||||
}
|
||||
|
||||
void update_chr_banks()
|
||||
{
|
||||
int chr_xor = (mode >> 7 & 1) * 0x1000;
|
||||
set_chr_bank( 0x0000 ^ chr_xor, bank_2k, banks [0] >> 1 );
|
||||
set_chr_bank( 0x0800 ^ chr_xor, bank_2k, banks [1] >> 1 );
|
||||
set_chr_bank( 0x1000 ^ chr_xor, bank_1k, banks [2] );
|
||||
set_chr_bank( 0x1400 ^ chr_xor, bank_1k, banks [3] );
|
||||
set_chr_bank( 0x1800 ^ chr_xor, bank_1k, banks [4] );
|
||||
set_chr_bank( 0x1c00 ^ chr_xor, bank_1k, banks [5] );
|
||||
}
|
||||
void update_prg_banks()
|
||||
{
|
||||
set_prg_bank(0xA000, bank_8k, banks[7]);
|
||||
nes_addr_t addr = 0x8000 + 0x4000 * (mode >> 6 & 1);
|
||||
set_prg_bank(addr, bank_8k, banks[6]);
|
||||
set_prg_bank(addr ^ 0x4000, bank_8k, last_bank - 1);
|
||||
}
|
||||
|
||||
void update_prg_banks()
|
||||
{
|
||||
set_prg_bank( 0xA000, bank_8k, banks [7] );
|
||||
nes_addr_t addr = 0x8000 + 0x4000 * (mode >> 6 & 1);
|
||||
set_prg_bank( addr, bank_8k, banks [6] );
|
||||
set_prg_bank( addr ^ 0x4000, bank_8k, last_bank - 1 );
|
||||
}
|
||||
void write_irq(nes_addr_t addr, int data)
|
||||
{
|
||||
switch (addr & 0xE001)
|
||||
{
|
||||
case 0xC000:
|
||||
irq_latch = data;
|
||||
break;
|
||||
|
||||
void write_irq( nes_addr_t addr, int data )
|
||||
{
|
||||
switch ( addr & 0xE001 )
|
||||
{
|
||||
case 0xC000:
|
||||
irq_latch = data;
|
||||
break;
|
||||
case 0xC001:
|
||||
/* MMC3 IRQ counter pathological behavior triggered if
|
||||
* counter_just_clocked is 1 */
|
||||
counter_just_clocked = 2;
|
||||
irq_ctr = 0;
|
||||
break;
|
||||
|
||||
case 0xC001:
|
||||
/* MMC3 IRQ counter pathological behavior triggered if
|
||||
* counter_just_clocked is 1 */
|
||||
counter_just_clocked = 2;
|
||||
irq_ctr = 0;
|
||||
break;
|
||||
case 0xE000:
|
||||
irq_flag = false;
|
||||
irq_enabled = false;
|
||||
break;
|
||||
|
||||
case 0xE000:
|
||||
irq_flag = false;
|
||||
irq_enabled = false;
|
||||
break;
|
||||
case 0xE001:
|
||||
irq_enabled = true;
|
||||
break;
|
||||
}
|
||||
if (irq_enabled)
|
||||
irq_changed();
|
||||
}
|
||||
|
||||
case 0xE001:
|
||||
irq_enabled = true;
|
||||
break;
|
||||
}
|
||||
if ( irq_enabled )
|
||||
irq_changed();
|
||||
}
|
||||
void write(nes_time_t time, nes_addr_t addr, int data)
|
||||
{
|
||||
switch (addr & 0xE001)
|
||||
{
|
||||
case 0x8000:
|
||||
{
|
||||
int changed = mode ^ data;
|
||||
mode = data;
|
||||
// avoid unnecessary bank updates
|
||||
if (changed & 0x80)
|
||||
update_chr_banks();
|
||||
if (changed & 0x40)
|
||||
update_prg_banks();
|
||||
break;
|
||||
}
|
||||
|
||||
void write( nes_time_t time, nes_addr_t addr, int data )
|
||||
{
|
||||
switch ( addr & 0xE001 )
|
||||
{
|
||||
case 0x8000: {
|
||||
int changed = mode ^ data;
|
||||
mode = data;
|
||||
// avoid unnecessary bank updates
|
||||
if ( changed & 0x80 )
|
||||
update_chr_banks();
|
||||
if ( changed & 0x40 )
|
||||
update_prg_banks();
|
||||
break;
|
||||
}
|
||||
case 0x8001:
|
||||
{
|
||||
int bank = mode & 7;
|
||||
banks[bank] = data;
|
||||
if (bank < 6)
|
||||
update_chr_banks();
|
||||
else
|
||||
update_prg_banks();
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x8001: {
|
||||
int bank = mode & 7;
|
||||
banks [bank] = data;
|
||||
if ( bank < 6 )
|
||||
update_chr_banks();
|
||||
else
|
||||
update_prg_banks();
|
||||
break;
|
||||
}
|
||||
case 0xA000:
|
||||
mirror = data;
|
||||
if (!(cart().mirroring() & 0x08))
|
||||
{
|
||||
if (mirror & 1)
|
||||
mirror_horiz();
|
||||
else
|
||||
mirror_vert();
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xA000:
|
||||
mirror = data;
|
||||
if ( !(cart().mirroring() & 0x08) )
|
||||
{
|
||||
if ( mirror & 1 )
|
||||
mirror_horiz();
|
||||
else
|
||||
mirror_vert();
|
||||
}
|
||||
break;
|
||||
case 0xA001:
|
||||
sram_mode = data;
|
||||
// dprintf( "%02X->%04X\n", data, addr );
|
||||
|
||||
case 0xA001:
|
||||
sram_mode = data;
|
||||
//dprintf( "%02X->%04X\n", data, addr );
|
||||
// Startropics 1 & 2 use MMC6 and always enable low 512 bytes of SRAM
|
||||
if ((data & 0x3F) == 0x30)
|
||||
enable_sram(true);
|
||||
else
|
||||
enable_sram(data & 0x80, data & 0x40);
|
||||
break;
|
||||
|
||||
// Startropics 1 & 2 use MMC6 and always enable low 512 bytes of SRAM
|
||||
if ( (data & 0x3F) == 0x30 )
|
||||
enable_sram( true );
|
||||
else
|
||||
enable_sram( data & 0x80, data & 0x40 );
|
||||
break;
|
||||
default:
|
||||
run_until(time);
|
||||
write_irq(addr, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
run_until( time );
|
||||
write_irq( addr, data );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nes_time_t next_time;
|
||||
int counter_just_clocked; // used only for debugging
|
||||
nes_time_t next_time;
|
||||
int counter_just_clocked; // used only for debugging
|
||||
};
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
|
||||
// Nes_Emu 0.7.0. http://www.slack.net/~ant/
|
||||
|
||||
#include <cstring>
|
||||
#include "mappers/mapper.hpp"
|
||||
#include "Nes_Core.hpp"
|
||||
#include "mappers/mapper.hpp"
|
||||
#include <cstring>
|
||||
|
||||
/* Copyright (C) 2004-2006 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
|
@ -21,127 +21,127 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
struct mmc5_state_t
|
||||
{
|
||||
enum { reg_count = 0x30 };
|
||||
uint8_t regs [0x30];
|
||||
uint8_t irq_enabled;
|
||||
enum
|
||||
{
|
||||
reg_count = 0x30
|
||||
};
|
||||
uint8_t regs[0x30];
|
||||
uint8_t irq_enabled;
|
||||
};
|
||||
static_assert( sizeof (mmc5_state_t) == 0x31 );
|
||||
static_assert(sizeof(mmc5_state_t) == 0x31);
|
||||
|
||||
// MMC5
|
||||
|
||||
class Mapper005 : public Nes_Mapper, mmc5_state_t {
|
||||
public:
|
||||
Mapper005()
|
||||
{
|
||||
mmc5_state_t* state = this;
|
||||
register_state( state, sizeof *state );
|
||||
}
|
||||
class Mapper005 : public Nes_Mapper, mmc5_state_t
|
||||
{
|
||||
public:
|
||||
Mapper005()
|
||||
{
|
||||
mmc5_state_t *state = this;
|
||||
register_state(state, sizeof *state);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{
|
||||
irq_time = no_irq;
|
||||
regs [0x00] = 2;
|
||||
regs [0x01] = 3;
|
||||
regs [0x14] = 0x7f;
|
||||
regs [0x15] = 0x7f;
|
||||
regs [0x16] = 0x7f;
|
||||
regs [0x17] = 0x7f;
|
||||
}
|
||||
virtual void reset_state()
|
||||
{
|
||||
irq_time = no_irq;
|
||||
regs[0x00] = 2;
|
||||
regs[0x01] = 3;
|
||||
regs[0x14] = 0x7f;
|
||||
regs[0x15] = 0x7f;
|
||||
regs[0x16] = 0x7f;
|
||||
regs[0x17] = 0x7f;
|
||||
}
|
||||
|
||||
virtual void read_state( mapper_state_t const& in )
|
||||
{
|
||||
Nes_Mapper::read_state( in );
|
||||
irq_time = no_irq;
|
||||
}
|
||||
virtual void read_state(mapper_state_t const &in)
|
||||
{
|
||||
Nes_Mapper::read_state(in);
|
||||
irq_time = no_irq;
|
||||
}
|
||||
|
||||
enum { regs_addr = 0x5100 };
|
||||
enum
|
||||
{
|
||||
regs_addr = 0x5100
|
||||
};
|
||||
|
||||
virtual nes_time_t next_irq( nes_time_t )
|
||||
{
|
||||
if ( irq_enabled & 0x80 )
|
||||
return irq_time;
|
||||
virtual nes_time_t next_irq(nes_time_t)
|
||||
{
|
||||
if (irq_enabled & 0x80)
|
||||
return irq_time;
|
||||
|
||||
return no_irq;
|
||||
}
|
||||
return no_irq;
|
||||
}
|
||||
|
||||
virtual bool write_intercepted( nes_time_t time, nes_addr_t addr, int data )
|
||||
{
|
||||
int reg = addr - regs_addr;
|
||||
if ( (unsigned) reg < reg_count )
|
||||
{
|
||||
regs [reg] = data;
|
||||
switch ( reg )
|
||||
{
|
||||
case 0x05:
|
||||
mirror_manual( data & 3, data >> 2 & 3,
|
||||
data >> 4 & 3, data >> 6 & 3 );
|
||||
break;
|
||||
virtual bool write_intercepted(nes_time_t time, nes_addr_t addr, int data)
|
||||
{
|
||||
int reg = addr - regs_addr;
|
||||
if ((unsigned)reg < reg_count)
|
||||
{
|
||||
regs[reg] = data;
|
||||
switch (reg)
|
||||
{
|
||||
case 0x05:
|
||||
mirror_manual(data & 3, data >> 2 & 3, data >> 4 & 3, data >> 6 & 3);
|
||||
break;
|
||||
|
||||
case 0x15:
|
||||
set_prg_bank( 0x8000, bank_16k, data >> 1 & 0x3f );
|
||||
break;
|
||||
case 0x15:
|
||||
set_prg_bank(0x8000, bank_16k, data >> 1 & 0x3f);
|
||||
break;
|
||||
|
||||
case 0x16:
|
||||
set_prg_bank( 0xC000, bank_8k, data & 0x7f );
|
||||
break;
|
||||
case 0x16:
|
||||
set_prg_bank(0xC000, bank_8k, data & 0x7f);
|
||||
break;
|
||||
|
||||
case 0x17:
|
||||
set_prg_bank( 0xE000, bank_8k, data & 0x7f );
|
||||
break;
|
||||
case 0x17:
|
||||
set_prg_bank(0xE000, bank_8k, data & 0x7f);
|
||||
break;
|
||||
|
||||
case 0x20:
|
||||
case 0x21:
|
||||
case 0x22:
|
||||
case 0x23:
|
||||
case 0x28:
|
||||
case 0x29:
|
||||
case 0x2a:
|
||||
case 0x2b:
|
||||
set_chr_bank( ((reg >> 1 & 4) + (reg & 3)) * 0x400, bank_1k, data );
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ( addr == 0x5203 )
|
||||
{
|
||||
irq_time = no_irq;
|
||||
if ( data && data < 240 )
|
||||
{
|
||||
irq_time = (341 * 21 + 128 + (data * 341)) / 3;
|
||||
if ( irq_time < time )
|
||||
irq_time = no_irq;
|
||||
}
|
||||
irq_changed();
|
||||
}
|
||||
else if ( addr == 0x5204 )
|
||||
{
|
||||
irq_enabled = data;
|
||||
irq_changed();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case 0x20:
|
||||
case 0x21:
|
||||
case 0x22:
|
||||
case 0x23:
|
||||
case 0x28:
|
||||
case 0x29:
|
||||
case 0x2a:
|
||||
case 0x2b:
|
||||
set_chr_bank(((reg >> 1 & 4) + (reg & 3)) * 0x400, bank_1k, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (addr == 0x5203)
|
||||
{
|
||||
irq_time = no_irq;
|
||||
if (data && data < 240)
|
||||
{
|
||||
irq_time = (341 * 21 + 128 + (data * 341)) / 3;
|
||||
if (irq_time < time)
|
||||
irq_time = no_irq;
|
||||
}
|
||||
irq_changed();
|
||||
}
|
||||
else if (addr == 0x5204)
|
||||
{
|
||||
irq_enabled = data;
|
||||
irq_changed();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void apply_mapping()
|
||||
{
|
||||
static unsigned char list [] = {
|
||||
0x05, 0x15, 0x16, 0x17,
|
||||
0x20, 0x21, 0x22, 0x23,
|
||||
0x28, 0x29, 0x2a, 0x2b
|
||||
};
|
||||
void apply_mapping()
|
||||
{
|
||||
static unsigned char list[] = {
|
||||
0x05, 0x15, 0x16, 0x17, 0x20, 0x21, 0x22, 0x23, 0x28, 0x29, 0x2a, 0x2b};
|
||||
|
||||
for ( int i = 0; i < (int) sizeof list; i++ )
|
||||
write_intercepted( 0, regs_addr + list [i], regs [list [i]] );
|
||||
intercept_writes( 0x5100, 0x200 );
|
||||
}
|
||||
for (int i = 0; i < (int)sizeof list; i++)
|
||||
write_intercepted(0, regs_addr + list[i], regs[list[i]]);
|
||||
intercept_writes(0x5100, 0x200);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t, int ) { }
|
||||
virtual void write(nes_time_t, nes_addr_t, int) {}
|
||||
|
||||
nes_time_t irq_time;
|
||||
nes_time_t irq_time;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -19,32 +19,32 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
// AOROM
|
||||
|
||||
class Mapper007 : public Nes_Mapper {
|
||||
uint8_t bank;
|
||||
public:
|
||||
Mapper007()
|
||||
{
|
||||
register_state( &bank, 1 );
|
||||
}
|
||||
class Mapper007 : public Nes_Mapper
|
||||
{
|
||||
uint8_t bank;
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
int b = bank;
|
||||
bank = ~b; // force update
|
||||
write( 0, 0, b );
|
||||
}
|
||||
public:
|
||||
Mapper007()
|
||||
{
|
||||
register_state(&bank, 1);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t, int data )
|
||||
{
|
||||
int changed = bank ^ data;
|
||||
bank = data;
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
int b = bank;
|
||||
bank = ~b; // force update
|
||||
write(0, 0, b);
|
||||
}
|
||||
|
||||
if ( changed & 0x10 )
|
||||
mirror_single( bank >> 4 & 1 );
|
||||
virtual void write(nes_time_t, nes_addr_t, int data)
|
||||
{
|
||||
int changed = bank ^ data;
|
||||
bank = data;
|
||||
|
||||
if ( changed & 0x0f )
|
||||
set_prg_bank( 0x8000, bank_32k, bank & 7 );
|
||||
}
|
||||
if (changed & 0x10)
|
||||
mirror_single(bank >> 4 & 1);
|
||||
|
||||
if (changed & 0x0f)
|
||||
set_prg_bank(0x8000, bank_32k, bank & 7);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1,59 +1,76 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include "mappers/mapper.hpp"
|
||||
#include <cstring>
|
||||
|
||||
// MMC2
|
||||
|
||||
class Mapper009: public Nes_Mapper
|
||||
class Mapper009 : public Nes_Mapper
|
||||
{
|
||||
uint8_t regs[6]; // A,B,C,D,E,F
|
||||
uint8_t regs[6]; // A,B,C,D,E,F
|
||||
|
||||
void mirror(uint8_t val)
|
||||
{
|
||||
if (val & 1)
|
||||
mirror_horiz();
|
||||
else
|
||||
mirror_vert();
|
||||
}
|
||||
void mirror(uint8_t val)
|
||||
{
|
||||
if (val & 1)
|
||||
mirror_horiz();
|
||||
else
|
||||
mirror_vert();
|
||||
}
|
||||
|
||||
public:
|
||||
Mapper009()
|
||||
{
|
||||
register_state(regs, sizeof(regs));
|
||||
}
|
||||
public:
|
||||
Mapper009()
|
||||
{
|
||||
register_state(regs, sizeof(regs));
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{
|
||||
std::memset(regs, 0, sizeof(regs));
|
||||
}
|
||||
virtual void reset_state()
|
||||
{
|
||||
std::memset(regs, 0, sizeof(regs));
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
mirror(regs[5]);
|
||||
set_prg_bank(0x8000, bank_8k, regs[0]);
|
||||
set_prg_bank(0xa000, bank_8k, 13);
|
||||
set_prg_bank(0xc000, bank_8k, 14);
|
||||
set_prg_bank(0xe000, bank_8k, 15);
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
mirror(regs[5]);
|
||||
set_prg_bank(0x8000, bank_8k, regs[0]);
|
||||
set_prg_bank(0xa000, bank_8k, 13);
|
||||
set_prg_bank(0xc000, bank_8k, 14);
|
||||
set_prg_bank(0xe000, bank_8k, 15);
|
||||
|
||||
set_chr_bank(0x0000, bank_4k, regs[1]);
|
||||
set_chr_bank(0x1000, bank_4k, regs[3]);
|
||||
set_chr_bank(0x0000, bank_4k, regs[1]);
|
||||
set_chr_bank(0x1000, bank_4k, regs[3]);
|
||||
|
||||
set_chr_bank_ex(0x0000, bank_4k, regs[2]);
|
||||
set_chr_bank_ex(0x1000, bank_4k, regs[4]);
|
||||
}
|
||||
set_chr_bank_ex(0x0000, bank_4k, regs[2]);
|
||||
set_chr_bank_ex(0x1000, bank_4k, regs[4]);
|
||||
}
|
||||
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
switch (addr >> 12)
|
||||
{
|
||||
case 0xa: regs[0] = data; set_prg_bank(0x8000, bank_8k, data); break;
|
||||
case 0xb: regs[1] = data; set_chr_bank(0x0000, bank_4k, data); break;
|
||||
case 0xc: regs[2] = data; set_chr_bank_ex(0x0000, bank_4k, data); break;
|
||||
case 0xd: regs[3] = data; set_chr_bank(0x1000, bank_4k, data); break;
|
||||
case 0xe: regs[4] = data; set_chr_bank_ex(0x1000, bank_4k, data); break;
|
||||
case 0xf: regs[5] = data; mirror(data); break;
|
||||
}
|
||||
}
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
switch (addr >> 12)
|
||||
{
|
||||
case 0xa:
|
||||
regs[0] = data;
|
||||
set_prg_bank(0x8000, bank_8k, data);
|
||||
break;
|
||||
case 0xb:
|
||||
regs[1] = data;
|
||||
set_chr_bank(0x0000, bank_4k, data);
|
||||
break;
|
||||
case 0xc:
|
||||
regs[2] = data;
|
||||
set_chr_bank_ex(0x0000, bank_4k, data);
|
||||
break;
|
||||
case 0xd:
|
||||
regs[3] = data;
|
||||
set_chr_bank(0x1000, bank_4k, data);
|
||||
break;
|
||||
case 0xe:
|
||||
regs[4] = data;
|
||||
set_chr_bank_ex(0x1000, bank_4k, data);
|
||||
break;
|
||||
case 0xf:
|
||||
regs[5] = data;
|
||||
mirror(data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,56 +1,74 @@
|
|||
#pragma once
|
||||
#include <cstring>
|
||||
#include "mappers/mapper.hpp"
|
||||
#include <cstring>
|
||||
|
||||
// MMC4
|
||||
|
||||
class Mapper010: public Nes_Mapper
|
||||
class Mapper010 : public Nes_Mapper
|
||||
{
|
||||
uint8_t regs[6]; // A,B,C,D,E,F
|
||||
uint8_t regs[6]; // A,B,C,D,E,F
|
||||
|
||||
void mirror(uint8_t val)
|
||||
{
|
||||
if (val & 1)
|
||||
mirror_horiz();
|
||||
else
|
||||
mirror_vert();
|
||||
}
|
||||
void mirror(uint8_t val)
|
||||
{
|
||||
if (val & 1)
|
||||
mirror_horiz();
|
||||
else
|
||||
mirror_vert();
|
||||
}
|
||||
|
||||
public:
|
||||
Mapper010()
|
||||
{
|
||||
register_state(regs, sizeof(regs));
|
||||
}
|
||||
public:
|
||||
Mapper010()
|
||||
{
|
||||
register_state(regs, sizeof(regs));
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{
|
||||
std::memset(regs, 0, sizeof(regs));
|
||||
}
|
||||
virtual void reset_state()
|
||||
{
|
||||
std::memset(regs, 0, sizeof(regs));
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
enable_sram();
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
enable_sram();
|
||||
|
||||
mirror(regs[5]);
|
||||
set_prg_bank(0x8000, bank_16k, regs[0]);
|
||||
mirror(regs[5]);
|
||||
set_prg_bank(0x8000, bank_16k, regs[0]);
|
||||
|
||||
set_chr_bank(0x0000, bank_4k, regs[1]);
|
||||
set_chr_bank(0x1000, bank_4k, regs[3]);
|
||||
set_chr_bank(0x0000, bank_4k, regs[1]);
|
||||
set_chr_bank(0x1000, bank_4k, regs[3]);
|
||||
|
||||
set_chr_bank_ex(0x0000, bank_4k, regs[2]);
|
||||
set_chr_bank_ex(0x1000, bank_4k, regs[4]);
|
||||
}
|
||||
set_chr_bank_ex(0x0000, bank_4k, regs[2]);
|
||||
set_chr_bank_ex(0x1000, bank_4k, regs[4]);
|
||||
}
|
||||
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
switch (addr >> 12)
|
||||
{
|
||||
case 0xa: regs[0] = data; set_prg_bank(0x8000, bank_16k, data); break;
|
||||
case 0xb: regs[1] = data; set_chr_bank(0x0000, bank_4k, data); break;
|
||||
case 0xc: regs[2] = data; set_chr_bank_ex(0x0000, bank_4k, data); break;
|
||||
case 0xd: regs[3] = data; set_chr_bank(0x1000, bank_4k, data); break;
|
||||
case 0xe: regs[4] = data; set_chr_bank_ex(0x1000, bank_4k, data); break;
|
||||
case 0xf: regs[5] = data; mirror(data); break;
|
||||
}
|
||||
}
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
switch (addr >> 12)
|
||||
{
|
||||
case 0xa:
|
||||
regs[0] = data;
|
||||
set_prg_bank(0x8000, bank_16k, data);
|
||||
break;
|
||||
case 0xb:
|
||||
regs[1] = data;
|
||||
set_chr_bank(0x0000, bank_4k, data);
|
||||
break;
|
||||
case 0xc:
|
||||
regs[2] = data;
|
||||
set_chr_bank_ex(0x0000, bank_4k, data);
|
||||
break;
|
||||
case 0xd:
|
||||
regs[3] = data;
|
||||
set_chr_bank(0x1000, bank_4k, data);
|
||||
break;
|
||||
case 0xe:
|
||||
regs[4] = data;
|
||||
set_chr_bank_ex(0x1000, bank_4k, data);
|
||||
break;
|
||||
case 0xf:
|
||||
regs[5] = data;
|
||||
mirror(data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -19,30 +19,32 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
// Color Dreams
|
||||
|
||||
class Mapper011 : public Nes_Mapper {
|
||||
uint8_t bank;
|
||||
public:
|
||||
Mapper011()
|
||||
{
|
||||
register_state( &bank, 1 );
|
||||
}
|
||||
class Mapper011 : public Nes_Mapper
|
||||
{
|
||||
uint8_t bank;
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
int b = bank;
|
||||
bank = ~b;
|
||||
write( 0, 0, b );
|
||||
}
|
||||
public:
|
||||
Mapper011()
|
||||
{
|
||||
register_state(&bank, 1);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t, int data )
|
||||
{
|
||||
int changed = bank ^ data;
|
||||
bank = data;
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
int b = bank;
|
||||
bank = ~b;
|
||||
write(0, 0, b);
|
||||
}
|
||||
|
||||
if ( changed & 0x0f )
|
||||
set_prg_bank( 0x8000, bank_32k, bank & 0x0f );
|
||||
virtual void write(nes_time_t, nes_addr_t, int data)
|
||||
{
|
||||
int changed = bank ^ data;
|
||||
bank = data;
|
||||
|
||||
if ( changed & 0xf0 )
|
||||
set_chr_bank( 0, bank_8k, bank >> 4 );
|
||||
}
|
||||
if (changed & 0x0f)
|
||||
set_prg_bank(0x8000, bank_32k, bank & 0x0f);
|
||||
|
||||
if (changed & 0xf0)
|
||||
set_chr_bank(0, bank_8k, bank >> 4);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -21,72 +21,72 @@
|
|||
|
||||
struct Mapper015_state_t
|
||||
{
|
||||
uint8_t prg_bank [ 4 ];
|
||||
uint8_t mirroring;
|
||||
uint8_t prg_bank[4];
|
||||
uint8_t mirroring;
|
||||
};
|
||||
|
||||
static_assert( sizeof (Mapper015_state_t) == 5 );
|
||||
static_assert(sizeof(Mapper015_state_t) == 5);
|
||||
|
||||
// K-1029, K-1030P
|
||||
|
||||
class Mapper015 : public Nes_Mapper, Mapper015_state_t {
|
||||
public:
|
||||
Mapper015()
|
||||
{
|
||||
i = 0;
|
||||
Mapper015_state_t* state = this;
|
||||
register_state( state, sizeof *state );
|
||||
}
|
||||
class Mapper015 : public Nes_Mapper, Mapper015_state_t
|
||||
{
|
||||
public:
|
||||
Mapper015()
|
||||
{
|
||||
i = 0;
|
||||
Mapper015_state_t *state = this;
|
||||
register_state(state, sizeof *state);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{
|
||||
write( 0, 0x8000, 0 );
|
||||
}
|
||||
virtual void reset_state()
|
||||
{
|
||||
write(0, 0x8000, 0);
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
enable_sram();
|
||||
set_chr_bank ( 0, bank_8k, 0 );
|
||||
for ( i = 0; i < sizeof prg_bank; i++ )
|
||||
set_prg_bank ( 0x8000 + ( i * 0x2000 ), bank_8k, prg_bank [i] );
|
||||
switch ( mirroring )
|
||||
{
|
||||
case 0: mirror_vert(); break;
|
||||
case 1: mirror_horiz(); break;
|
||||
}
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
enable_sram();
|
||||
set_chr_bank(0, bank_8k, 0);
|
||||
for (i = 0; i < sizeof prg_bank; i++)
|
||||
set_prg_bank(0x8000 + (i * 0x2000), bank_8k, prg_bank[i]);
|
||||
switch (mirroring)
|
||||
{
|
||||
case 0: mirror_vert(); break;
|
||||
case 1: mirror_horiz(); break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
uint8_t bank = ( data & 0x3F ) << 1;
|
||||
uint8_t sbank = ( data >> 7 ) & 1;
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
uint8_t bank = (data & 0x3F) << 1;
|
||||
uint8_t sbank = (data >> 7) & 1;
|
||||
|
||||
mirroring = ( data >> 6 ) & 1;
|
||||
switch ( addr & 3 )
|
||||
{
|
||||
case 0:
|
||||
for ( i = 0; i < sizeof prg_bank; i++ )
|
||||
prg_bank [ i ] = bank + i;
|
||||
apply_mapping();
|
||||
break;
|
||||
case 2:
|
||||
for ( i = 0; i < sizeof prg_bank; i++ )
|
||||
prg_bank [ i ] = bank | sbank;
|
||||
apply_mapping();
|
||||
break;
|
||||
case 1:
|
||||
case 3:
|
||||
for ( i = 0; i < sizeof prg_bank; i++ )
|
||||
{
|
||||
if ( i >= 2 && !( addr & 2 ) )
|
||||
bank = 0x7E;
|
||||
prg_bank [ i ] = bank + ( i & 1 );
|
||||
}
|
||||
apply_mapping();
|
||||
break;
|
||||
}
|
||||
}
|
||||
mirroring = (data >> 6) & 1;
|
||||
switch (addr & 3)
|
||||
{
|
||||
case 0:
|
||||
for (i = 0; i < sizeof prg_bank; i++)
|
||||
prg_bank[i] = bank + i;
|
||||
apply_mapping();
|
||||
break;
|
||||
case 2:
|
||||
for (i = 0; i < sizeof prg_bank; i++)
|
||||
prg_bank[i] = bank | sbank;
|
||||
apply_mapping();
|
||||
break;
|
||||
case 1:
|
||||
case 3:
|
||||
for (i = 0; i < sizeof prg_bank; i++)
|
||||
{
|
||||
if (i >= 2 && !(addr & 2))
|
||||
bank = 0x7E;
|
||||
prg_bank[i] = bank + (i & 1);
|
||||
}
|
||||
apply_mapping();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long int i;
|
||||
unsigned long int i;
|
||||
};
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
// Nes_Emu 0.7.0. http://www.slack.net/~ant/
|
||||
|
||||
#include "mappers/mapper.hpp"
|
||||
#include "apu/namco/apu.hpp"
|
||||
#include "mappers/mapper.hpp"
|
||||
|
||||
/* Copyright (C) 2004-2006 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
|
@ -22,177 +22,176 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
struct namco106_state_t
|
||||
{
|
||||
uint8_t regs [16];
|
||||
uint16_t irq_ctr;
|
||||
uint8_t irq_pending;
|
||||
uint8_t unused1 [1];
|
||||
namco_state_t sound_state;
|
||||
uint8_t regs[16];
|
||||
uint16_t irq_ctr;
|
||||
uint8_t irq_pending;
|
||||
uint8_t unused1[1];
|
||||
namco_state_t sound_state;
|
||||
};
|
||||
static_assert( sizeof (namco106_state_t) == 20 + sizeof (namco_state_t) );
|
||||
static_assert(sizeof(namco106_state_t) == 20 + sizeof(namco_state_t));
|
||||
|
||||
// Namco106
|
||||
|
||||
class Mapper019 : public Nes_Mapper, namco106_state_t {
|
||||
public:
|
||||
Mapper019()
|
||||
{
|
||||
namco106_state_t* state = this;
|
||||
register_state( state, sizeof *state );
|
||||
}
|
||||
class Mapper019 : public Nes_Mapper, namco106_state_t
|
||||
{
|
||||
public:
|
||||
Mapper019()
|
||||
{
|
||||
namco106_state_t *state = this;
|
||||
register_state(state, sizeof *state);
|
||||
}
|
||||
|
||||
virtual int channel_count() const { return sound.osc_count; }
|
||||
virtual int channel_count() const { return sound.osc_count; }
|
||||
|
||||
virtual void set_channel_buf( int i, Blip_Buffer* b ) { sound.osc_output( i, b ); }
|
||||
virtual void set_channel_buf(int i, Blip_Buffer *b) { sound.osc_output(i, b); }
|
||||
|
||||
virtual void set_treble( blip_eq_t const& eq ) { sound.treble_eq( eq ); }
|
||||
virtual void set_treble(blip_eq_t const &eq) { sound.treble_eq(eq); }
|
||||
|
||||
void reset_state()
|
||||
{
|
||||
regs [12] = 0;
|
||||
regs [13] = 1;
|
||||
regs [14] = last_bank - 1;
|
||||
sound.reset();
|
||||
}
|
||||
void reset_state()
|
||||
{
|
||||
regs[12] = 0;
|
||||
regs[13] = 1;
|
||||
regs[14] = last_bank - 1;
|
||||
sound.reset();
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
last_time = 0;
|
||||
enable_sram();
|
||||
intercept_writes( 0x4800, 1 );
|
||||
intercept_reads ( 0x4800, 1 );
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
last_time = 0;
|
||||
enable_sram();
|
||||
intercept_writes(0x4800, 1);
|
||||
intercept_reads(0x4800, 1);
|
||||
|
||||
intercept_writes( 0x5000, 0x1000 );
|
||||
intercept_reads ( 0x5000, 0x1000 );
|
||||
intercept_writes(0x5000, 0x1000);
|
||||
intercept_reads(0x5000, 0x1000);
|
||||
|
||||
for ( int i = 0; i < (int) sizeof regs; i++ )
|
||||
write( 0, 0x8000 + i * 0x800, regs [i] );
|
||||
}
|
||||
for (int i = 0; i < (int)sizeof regs; i++)
|
||||
write(0, 0x8000 + i * 0x800, regs[i]);
|
||||
}
|
||||
|
||||
virtual nes_time_t next_irq( nes_time_t time )
|
||||
{
|
||||
if ( irq_pending )
|
||||
return time;
|
||||
virtual nes_time_t next_irq(nes_time_t time)
|
||||
{
|
||||
if (irq_pending)
|
||||
return time;
|
||||
|
||||
if ( !(irq_ctr & 0x8000) )
|
||||
return no_irq;
|
||||
if (!(irq_ctr & 0x8000))
|
||||
return no_irq;
|
||||
|
||||
return 0x10000 - irq_ctr + last_time;
|
||||
}
|
||||
return 0x10000 - irq_ctr + last_time;
|
||||
}
|
||||
|
||||
virtual void run_until( nes_time_t end_time )
|
||||
{
|
||||
long count = irq_ctr + (end_time - last_time);
|
||||
if ( irq_ctr & 0x8000 )
|
||||
{
|
||||
if ( count > 0xffff )
|
||||
{
|
||||
count = 0xffff;
|
||||
irq_pending = true;
|
||||
}
|
||||
}
|
||||
else if ( count > 0x7fff )
|
||||
{
|
||||
count = 0x7fff;
|
||||
}
|
||||
virtual void run_until(nes_time_t end_time)
|
||||
{
|
||||
long count = irq_ctr + (end_time - last_time);
|
||||
if (irq_ctr & 0x8000)
|
||||
{
|
||||
if (count > 0xffff)
|
||||
{
|
||||
count = 0xffff;
|
||||
irq_pending = true;
|
||||
}
|
||||
}
|
||||
else if (count > 0x7fff)
|
||||
{
|
||||
count = 0x7fff;
|
||||
}
|
||||
|
||||
irq_ctr = count;
|
||||
last_time = end_time;
|
||||
}
|
||||
irq_ctr = count;
|
||||
last_time = end_time;
|
||||
}
|
||||
|
||||
virtual void end_frame( nes_time_t end_time )
|
||||
{
|
||||
if ( end_time > last_time )
|
||||
run_until( end_time );
|
||||
last_time -= end_time;
|
||||
sound.end_frame( end_time );
|
||||
}
|
||||
virtual void end_frame(nes_time_t end_time)
|
||||
{
|
||||
if (end_time > last_time)
|
||||
run_until(end_time);
|
||||
last_time -= end_time;
|
||||
sound.end_frame(end_time);
|
||||
}
|
||||
|
||||
virtual int read( nes_time_t time, nes_addr_t addr )
|
||||
{
|
||||
if ( addr == 0x4800 )
|
||||
return sound.read_data();
|
||||
virtual int read(nes_time_t time, nes_addr_t addr)
|
||||
{
|
||||
if (addr == 0x4800)
|
||||
return sound.read_data();
|
||||
|
||||
if ( addr == 0x5000 )
|
||||
{
|
||||
irq_pending = false;
|
||||
return irq_ctr & 0xff;
|
||||
}
|
||||
if (addr == 0x5000)
|
||||
{
|
||||
irq_pending = false;
|
||||
return irq_ctr & 0xff;
|
||||
}
|
||||
|
||||
if ( addr == 0x5800 )
|
||||
{
|
||||
irq_pending = false;
|
||||
return irq_ctr >> 8;
|
||||
}
|
||||
if (addr == 0x5800)
|
||||
{
|
||||
irq_pending = false;
|
||||
return irq_ctr >> 8;
|
||||
}
|
||||
|
||||
return Nes_Mapper::read( time, addr );
|
||||
}
|
||||
return Nes_Mapper::read(time, addr);
|
||||
}
|
||||
|
||||
virtual bool write_intercepted( nes_time_t time, nes_addr_t addr, int data )
|
||||
{
|
||||
if ( addr == 0x4800 )
|
||||
{
|
||||
sound.write_data( time, data );
|
||||
}
|
||||
else if ( addr == 0x5000 )
|
||||
{
|
||||
irq_ctr = (irq_ctr & 0xff00) | data;
|
||||
irq_pending = false;
|
||||
irq_changed();
|
||||
}
|
||||
else if ( addr == 0x5800 )
|
||||
{
|
||||
irq_ctr = (data << 8) | (irq_ctr & 0xff);
|
||||
irq_pending = false;
|
||||
irq_changed();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool write_intercepted(nes_time_t time, nes_addr_t addr, int data)
|
||||
{
|
||||
if (addr == 0x4800)
|
||||
{
|
||||
sound.write_data(time, data);
|
||||
}
|
||||
else if (addr == 0x5000)
|
||||
{
|
||||
irq_ctr = (irq_ctr & 0xff00) | data;
|
||||
irq_pending = false;
|
||||
irq_changed();
|
||||
}
|
||||
else if (addr == 0x5800)
|
||||
{
|
||||
irq_ctr = (data << 8) | (irq_ctr & 0xff);
|
||||
irq_pending = false;
|
||||
irq_changed();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
int reg = addr >> 11 & 0x0F;
|
||||
regs [reg] = data;
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
int reg = addr >> 11 & 0x0F;
|
||||
regs[reg] = data;
|
||||
|
||||
int prg_bank = reg - 0x0c;
|
||||
if ( (unsigned) prg_bank < 3 )
|
||||
{
|
||||
if ( prg_bank == 0 && (data & 0x40) )
|
||||
mirror_vert();
|
||||
set_prg_bank( 0x8000 | (prg_bank << bank_8k), bank_8k, data & 0x3F );
|
||||
}
|
||||
else if ( reg < 8 )
|
||||
{
|
||||
set_chr_bank( reg * 0x400, bank_1k, data );
|
||||
}
|
||||
else if ( reg < 0x0c )
|
||||
{
|
||||
mirror_manual( regs [8] & 1, regs [9] & 1, regs [10] & 1, regs [11] & 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
sound.write_addr( data );
|
||||
}
|
||||
}
|
||||
int prg_bank = reg - 0x0c;
|
||||
if ((unsigned)prg_bank < 3)
|
||||
{
|
||||
if (prg_bank == 0 && (data & 0x40))
|
||||
mirror_vert();
|
||||
set_prg_bank(0x8000 | (prg_bank << bank_8k), bank_8k, data & 0x3F);
|
||||
}
|
||||
else if (reg < 8)
|
||||
{
|
||||
set_chr_bank(reg * 0x400, bank_1k, data);
|
||||
}
|
||||
else if (reg < 0x0c)
|
||||
{
|
||||
mirror_manual(regs[8] & 1, regs[9] & 1, regs[10] & 1, regs[11] & 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
sound.write_addr(data);
|
||||
}
|
||||
}
|
||||
|
||||
void save_state( mapper_state_t& out )
|
||||
{
|
||||
sound.save_state( &sound_state );
|
||||
Nes_Mapper::save_state( out );
|
||||
}
|
||||
void save_state(mapper_state_t &out)
|
||||
{
|
||||
sound.save_state(&sound_state);
|
||||
Nes_Mapper::save_state(out);
|
||||
}
|
||||
|
||||
void read_state( mapper_state_t const& in )
|
||||
{
|
||||
Nes_Mapper::read_state( in );
|
||||
sound.load_state( sound_state );
|
||||
}
|
||||
void read_state(mapper_state_t const &in)
|
||||
{
|
||||
Nes_Mapper::read_state(in);
|
||||
sound.load_state(sound_state);
|
||||
}
|
||||
|
||||
Nes_Namco_Apu sound;
|
||||
nes_time_t last_time;
|
||||
Nes_Namco_Apu sound;
|
||||
nes_time_t last_time;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -30,221 +30,225 @@
|
|||
|
||||
struct vrc2_state_t
|
||||
{
|
||||
uint8_t prg_banks [ 2 ];
|
||||
uint8_t chr_banks [ 8 ];
|
||||
uint8_t mirroring;
|
||||
uint8_t prg_swap;
|
||||
uint8_t irq_latch;
|
||||
uint8_t irq_control;
|
||||
uint8_t prg_banks[2];
|
||||
uint8_t chr_banks[8];
|
||||
uint8_t mirroring;
|
||||
uint8_t prg_swap;
|
||||
uint8_t irq_latch;
|
||||
uint8_t irq_control;
|
||||
|
||||
// internal state
|
||||
uint16_t next_time;
|
||||
uint8_t irq_pending;
|
||||
// internal state
|
||||
uint16_t next_time;
|
||||
uint8_t irq_pending;
|
||||
};
|
||||
static_assert( sizeof ( vrc2_state_t ) == 18 );
|
||||
static_assert(sizeof(vrc2_state_t) == 18);
|
||||
|
||||
template <bool type_a, bool type_b>
|
||||
class Mapper_VRC2_4 : public Nes_Mapper, vrc2_state_t {
|
||||
public:
|
||||
Mapper_VRC2_4()
|
||||
{
|
||||
if (type_a && type_b) // mapper 21
|
||||
{
|
||||
is22 = 0;
|
||||
reg1mask = 0x42;
|
||||
reg2mask = 0x84;
|
||||
}
|
||||
else if (!type_a && type_b) // mapper 22
|
||||
{
|
||||
is22 = 1;
|
||||
reg1mask = 2;
|
||||
reg2mask = 1;
|
||||
}
|
||||
else if (!type_a && !type_b) // mapper 23
|
||||
{
|
||||
is22 = 0;
|
||||
reg1mask = 0x15;
|
||||
reg2mask = 0x2a;
|
||||
}
|
||||
else if (type_a && !type_b) // mapper 25
|
||||
{
|
||||
is22 = 0;
|
||||
reg1mask = 0xa;
|
||||
reg2mask = 0x5;
|
||||
}
|
||||
vrc2_state_t * state = this;
|
||||
register_state( state, sizeof * state );
|
||||
}
|
||||
|
||||
void reset_state()
|
||||
{
|
||||
}
|
||||
|
||||
void apply_mapping()
|
||||
{
|
||||
if ( !is22 ) enable_sram();
|
||||
update_prg();
|
||||
update_chr();
|
||||
set_mirroring();
|
||||
}
|
||||
|
||||
void reset_timer( nes_time_t present )
|
||||
{
|
||||
next_time = present + unsigned ( ( 0x100 - irq_latch ) * timer_period ) / 4;
|
||||
}
|
||||
|
||||
virtual void run_until( nes_time_t end_time )
|
||||
{
|
||||
if ( irq_control & 2 )
|
||||
{
|
||||
while ( next_time < end_time )
|
||||
{
|
||||
// printf( "%d timer expired\n", next_time );
|
||||
irq_pending = true;
|
||||
reset_timer( next_time );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void end_frame( nes_time_t end_time )
|
||||
{
|
||||
run_until( end_time );
|
||||
|
||||
// to do: next_time might go negative if IRQ is disabled
|
||||
next_time -= end_time;
|
||||
}
|
||||
|
||||
virtual nes_time_t next_irq( nes_time_t present )
|
||||
{
|
||||
if ( irq_pending )
|
||||
return present;
|
||||
|
||||
if ( irq_control & 2 )
|
||||
return next_time + 1;
|
||||
|
||||
return no_irq;
|
||||
}
|
||||
|
||||
void write_irq( nes_time_t time, nes_addr_t addr, int data );
|
||||
|
||||
void write( nes_time_t time, nes_addr_t addr, int data )
|
||||
{
|
||||
addr = ( addr & 0xF000 ) | !!( addr & reg2mask ) << 1 | !!( addr & reg1mask );
|
||||
|
||||
if( addr >= 0xB000 && addr <= 0xE003)
|
||||
{
|
||||
unsigned banknumber = ( ( addr >> 1 ) & 1 ) | ( ( addr - 0xB000 ) >> 11 );
|
||||
unsigned offset = ( addr & 1 ) << 2;
|
||||
|
||||
chr_banks [ banknumber ] &= ( 0xF0 ) >> offset;
|
||||
chr_banks [ banknumber ] |= ( data & 0xF ) << offset;
|
||||
chr_banks [ banknumber ] |= ( offset ? ( ( data & 0x10 ) << 4 ) : 0 );
|
||||
update_chr();
|
||||
}
|
||||
else
|
||||
{
|
||||
switch ( addr & 0xF003 )
|
||||
{
|
||||
case 0x8000:
|
||||
case 0x8001:
|
||||
case 0x8002:
|
||||
case 0x8003:
|
||||
prg_banks [ 0 ] = data & 0x1F;
|
||||
update_prg();
|
||||
break;
|
||||
case 0xA000:
|
||||
case 0xA001:
|
||||
case 0xA002:
|
||||
case 0xA003:
|
||||
prg_banks [ 1 ] = data & 0x1F;
|
||||
update_prg();
|
||||
break;
|
||||
case 0x9000:
|
||||
case 0x9001:
|
||||
mirroring = data;
|
||||
set_mirroring();
|
||||
break;
|
||||
case 0x9002:
|
||||
case 0x9003:
|
||||
prg_swap = data;
|
||||
update_prg();
|
||||
break;
|
||||
case 0xF000:
|
||||
case 0xF001:
|
||||
case 0xF002:
|
||||
case 0xF003:
|
||||
write_irq( time, addr, data );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned is22, reg1mask, reg2mask;
|
||||
enum { timer_period = 113 * 4 + 3 };
|
||||
|
||||
private:
|
||||
void set_mirroring()
|
||||
{
|
||||
switch ( mirroring & 3 )
|
||||
{
|
||||
case 0: mirror_vert(); break;
|
||||
case 1: mirror_horiz(); break;
|
||||
case 2:
|
||||
case 3: mirror_single( mirroring & 1 ); break;
|
||||
}
|
||||
}
|
||||
|
||||
void update_prg()
|
||||
{
|
||||
if ( prg_swap & 2 )
|
||||
{
|
||||
set_prg_bank( 0x8000, bank_8k, ( 0xFE ) );
|
||||
set_prg_bank( 0xC000, bank_8k, prg_banks [ 0 ] );
|
||||
}
|
||||
else
|
||||
{
|
||||
set_prg_bank( 0x8000, bank_8k, prg_banks [ 0 ] );
|
||||
set_prg_bank( 0xC000, bank_8k, ( 0xFE ) );
|
||||
}
|
||||
|
||||
set_prg_bank( 0xA000, bank_8k, prg_banks [ 1 ] );
|
||||
set_prg_bank( 0xE000, bank_8k, ( 0xFF ) );
|
||||
}
|
||||
|
||||
void update_chr()
|
||||
{
|
||||
for ( int i = 0; i < (int) sizeof chr_banks; i++ )
|
||||
set_chr_bank( i * 0x400, bank_1k, chr_banks [ i ] >> is22 );
|
||||
}
|
||||
};
|
||||
|
||||
template <bool type_a, bool type_b>
|
||||
void Mapper_VRC2_4<type_a, type_b>::write_irq( nes_time_t time,
|
||||
nes_addr_t addr, int data )
|
||||
class Mapper_VRC2_4 : public Nes_Mapper, vrc2_state_t
|
||||
{
|
||||
// IRQ
|
||||
run_until( time );
|
||||
//printf("%6d VRC2_4 [%d] A:%04x V:%02x\n", time, addr & 3, addr, data);
|
||||
switch ( addr & 3 )
|
||||
{
|
||||
case 0:
|
||||
irq_latch = ( irq_latch & 0xF0 ) | ( data & 0xF );
|
||||
break;
|
||||
case 1:
|
||||
irq_latch = ( irq_latch & 0x0F ) | ( ( data & 0xF ) << 4 );
|
||||
break;
|
||||
case 2:
|
||||
irq_pending = false;
|
||||
irq_control = data & 3;
|
||||
if ( data & 2 ) reset_timer( time );
|
||||
break;
|
||||
case 3:
|
||||
irq_pending = false;
|
||||
irq_control = ( irq_control & ~2 ) | ( ( irq_control << 1 ) & 2 );
|
||||
break;
|
||||
}
|
||||
irq_changed();
|
||||
public:
|
||||
Mapper_VRC2_4()
|
||||
{
|
||||
if (type_a && type_b) // mapper 21
|
||||
{
|
||||
is22 = 0;
|
||||
reg1mask = 0x42;
|
||||
reg2mask = 0x84;
|
||||
}
|
||||
else if (!type_a && type_b) // mapper 22
|
||||
{
|
||||
is22 = 1;
|
||||
reg1mask = 2;
|
||||
reg2mask = 1;
|
||||
}
|
||||
else if (!type_a && !type_b) // mapper 23
|
||||
{
|
||||
is22 = 0;
|
||||
reg1mask = 0x15;
|
||||
reg2mask = 0x2a;
|
||||
}
|
||||
else if (type_a && !type_b) // mapper 25
|
||||
{
|
||||
is22 = 0;
|
||||
reg1mask = 0xa;
|
||||
reg2mask = 0x5;
|
||||
}
|
||||
vrc2_state_t *state = this;
|
||||
register_state(state, sizeof *state);
|
||||
}
|
||||
|
||||
void reset_state()
|
||||
{
|
||||
}
|
||||
|
||||
void apply_mapping()
|
||||
{
|
||||
if (!is22) enable_sram();
|
||||
update_prg();
|
||||
update_chr();
|
||||
set_mirroring();
|
||||
}
|
||||
|
||||
void reset_timer(nes_time_t present)
|
||||
{
|
||||
next_time = present + unsigned((0x100 - irq_latch) * timer_period) / 4;
|
||||
}
|
||||
|
||||
virtual void run_until(nes_time_t end_time)
|
||||
{
|
||||
if (irq_control & 2)
|
||||
{
|
||||
while (next_time < end_time)
|
||||
{
|
||||
// printf( "%d timer expired\n", next_time );
|
||||
irq_pending = true;
|
||||
reset_timer(next_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void end_frame(nes_time_t end_time)
|
||||
{
|
||||
run_until(end_time);
|
||||
|
||||
// to do: next_time might go negative if IRQ is disabled
|
||||
next_time -= end_time;
|
||||
}
|
||||
|
||||
virtual nes_time_t next_irq(nes_time_t present)
|
||||
{
|
||||
if (irq_pending)
|
||||
return present;
|
||||
|
||||
if (irq_control & 2)
|
||||
return next_time + 1;
|
||||
|
||||
return no_irq;
|
||||
}
|
||||
|
||||
void write_irq(nes_time_t time, nes_addr_t addr, int data);
|
||||
|
||||
void write(nes_time_t time, nes_addr_t addr, int data)
|
||||
{
|
||||
addr = (addr & 0xF000) | !!(addr & reg2mask) << 1 | !!(addr & reg1mask);
|
||||
|
||||
if (addr >= 0xB000 && addr <= 0xE003)
|
||||
{
|
||||
unsigned banknumber = ((addr >> 1) & 1) | ((addr - 0xB000) >> 11);
|
||||
unsigned offset = (addr & 1) << 2;
|
||||
|
||||
chr_banks[banknumber] &= (0xF0) >> offset;
|
||||
chr_banks[banknumber] |= (data & 0xF) << offset;
|
||||
chr_banks[banknumber] |= (offset ? ((data & 0x10) << 4) : 0);
|
||||
update_chr();
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (addr & 0xF003)
|
||||
{
|
||||
case 0x8000:
|
||||
case 0x8001:
|
||||
case 0x8002:
|
||||
case 0x8003:
|
||||
prg_banks[0] = data & 0x1F;
|
||||
update_prg();
|
||||
break;
|
||||
case 0xA000:
|
||||
case 0xA001:
|
||||
case 0xA002:
|
||||
case 0xA003:
|
||||
prg_banks[1] = data & 0x1F;
|
||||
update_prg();
|
||||
break;
|
||||
case 0x9000:
|
||||
case 0x9001:
|
||||
mirroring = data;
|
||||
set_mirroring();
|
||||
break;
|
||||
case 0x9002:
|
||||
case 0x9003:
|
||||
prg_swap = data;
|
||||
update_prg();
|
||||
break;
|
||||
case 0xF000:
|
||||
case 0xF001:
|
||||
case 0xF002:
|
||||
case 0xF003:
|
||||
write_irq(time, addr, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned is22, reg1mask, reg2mask;
|
||||
enum
|
||||
{
|
||||
timer_period = 113 * 4 + 3
|
||||
};
|
||||
|
||||
private:
|
||||
void set_mirroring()
|
||||
{
|
||||
switch (mirroring & 3)
|
||||
{
|
||||
case 0: mirror_vert(); break;
|
||||
case 1: mirror_horiz(); break;
|
||||
case 2:
|
||||
case 3: mirror_single(mirroring & 1); break;
|
||||
}
|
||||
}
|
||||
|
||||
void update_prg()
|
||||
{
|
||||
if (prg_swap & 2)
|
||||
{
|
||||
set_prg_bank(0x8000, bank_8k, (0xFE));
|
||||
set_prg_bank(0xC000, bank_8k, prg_banks[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
set_prg_bank(0x8000, bank_8k, prg_banks[0]);
|
||||
set_prg_bank(0xC000, bank_8k, (0xFE));
|
||||
}
|
||||
|
||||
set_prg_bank(0xA000, bank_8k, prg_banks[1]);
|
||||
set_prg_bank(0xE000, bank_8k, (0xFF));
|
||||
}
|
||||
|
||||
void update_chr()
|
||||
{
|
||||
for (int i = 0; i < (int)sizeof chr_banks; i++)
|
||||
set_chr_bank(i * 0x400, bank_1k, chr_banks[i] >> is22);
|
||||
}
|
||||
};
|
||||
|
||||
template <bool type_a, bool type_b>
|
||||
void Mapper_VRC2_4<type_a, type_b>::write_irq(nes_time_t time,
|
||||
nes_addr_t addr,
|
||||
int data)
|
||||
{
|
||||
// IRQ
|
||||
run_until(time);
|
||||
// printf("%6d VRC2_4 [%d] A:%04x V:%02x\n", time, addr & 3, addr, data);
|
||||
switch (addr & 3)
|
||||
{
|
||||
case 0:
|
||||
irq_latch = (irq_latch & 0xF0) | (data & 0xF);
|
||||
break;
|
||||
case 1:
|
||||
irq_latch = (irq_latch & 0x0F) | ((data & 0xF) << 4);
|
||||
break;
|
||||
case 2:
|
||||
irq_pending = false;
|
||||
irq_control = data & 3;
|
||||
if (data & 2) reset_timer(time);
|
||||
break;
|
||||
case 3:
|
||||
irq_pending = false;
|
||||
irq_control = (irq_control & ~2) | ((irq_control << 1) & 2);
|
||||
break;
|
||||
}
|
||||
irq_changed();
|
||||
}
|
||||
|
||||
|
||||
typedef Mapper_VRC2_4<true,true> Mapper021;
|
||||
typedef Mapper_VRC2_4<true, true> Mapper021;
|
||||
|
|
|
@ -29,4 +29,4 @@
|
|||
#include "mappers/mapper.hpp"
|
||||
#include "mappers/mapper021.hpp"
|
||||
|
||||
typedef Mapper_VRC2_4<false,true> Mapper022;
|
||||
typedef Mapper_VRC2_4<false, true> Mapper022;
|
|
@ -3,9 +3,9 @@
|
|||
// Konami VRC6 mapper
|
||||
// Nes_Emu 0.7.0. http://www.slack.net/~ant/
|
||||
|
||||
#include <cstring>
|
||||
#include "mappers/mapper.hpp"
|
||||
#include "apu/vrc6/apu.hpp"
|
||||
#include "mappers/mapper.hpp"
|
||||
#include <cstring>
|
||||
|
||||
/* Copyright (C) 2004-2006 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
|
@ -20,209 +20,212 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
struct vrc6_state_t
|
||||
{
|
||||
// written registers
|
||||
uint8_t prg_16k_bank;
|
||||
// could move sound regs int and out of vrc6_apu_state_t for state saving,
|
||||
// allowing them to be stored here
|
||||
uint8_t old_sound_regs [3] [3]; // to do: eliminate this duplicate
|
||||
uint8_t mirroring;
|
||||
uint8_t prg_8k_bank;
|
||||
uint8_t chr_banks [8];
|
||||
uint8_t irq_reload;
|
||||
uint8_t irq_mode;
|
||||
// written registers
|
||||
uint8_t prg_16k_bank;
|
||||
// could move sound regs int and out of vrc6_apu_state_t for state saving,
|
||||
// allowing them to be stored here
|
||||
uint8_t old_sound_regs[3][3]; // to do: eliminate this duplicate
|
||||
uint8_t mirroring;
|
||||
uint8_t prg_8k_bank;
|
||||
uint8_t chr_banks[8];
|
||||
uint8_t irq_reload;
|
||||
uint8_t irq_mode;
|
||||
|
||||
// internal state
|
||||
uint16_t next_time;
|
||||
uint8_t irq_pending;
|
||||
uint8_t unused;
|
||||
// internal state
|
||||
uint16_t next_time;
|
||||
uint8_t irq_pending;
|
||||
uint8_t unused;
|
||||
|
||||
vrc6_apu_state_t sound_state;
|
||||
vrc6_apu_state_t sound_state;
|
||||
};
|
||||
static_assert( sizeof (vrc6_state_t) == 26 + sizeof (vrc6_apu_state_t) );
|
||||
static_assert(sizeof(vrc6_state_t) == 26 + sizeof(vrc6_apu_state_t));
|
||||
|
||||
template <int swapMask>
|
||||
class Mapper_Vrc6 : public Nes_Mapper, vrc6_state_t {
|
||||
public:
|
||||
Mapper_Vrc6( )
|
||||
{
|
||||
swap_mask = swapMask;
|
||||
vrc6_state_t* state = this;
|
||||
register_state( state, sizeof *state );
|
||||
}
|
||||
|
||||
virtual int channel_count() const { return sound.osc_count; }
|
||||
|
||||
virtual void set_channel_buf( int i, Blip_Buffer* b ) { sound.osc_output( i, b ); }
|
||||
|
||||
virtual void set_treble( blip_eq_t const& eq ) { sound.treble_eq( eq ); }
|
||||
|
||||
virtual void reset_state()
|
||||
{
|
||||
prg_8k_bank = last_bank - 1;
|
||||
sound.reset();
|
||||
}
|
||||
|
||||
virtual void save_state( mapper_state_t& out )
|
||||
{
|
||||
sound.save_state( &sound_state );
|
||||
Nes_Mapper::save_state( out );
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
enable_sram();
|
||||
set_prg_bank( 0x8000, bank_16k, prg_16k_bank );
|
||||
set_prg_bank( 0xC000, bank_8k, prg_8k_bank );
|
||||
|
||||
for ( int i = 0; i < (int) sizeof chr_banks; i++ )
|
||||
set_chr_bank( i * 0x400, bank_1k, chr_banks [i] );
|
||||
|
||||
write_bank( 0xb003, mirroring );
|
||||
}
|
||||
|
||||
void reset_timer( nes_time_t present )
|
||||
{
|
||||
next_time = present + unsigned ((0x100 - irq_reload) * timer_period) / 4;
|
||||
}
|
||||
|
||||
virtual void run_until( nes_time_t end_time )
|
||||
{
|
||||
if ( irq_mode & 2 )
|
||||
{
|
||||
while ( next_time < end_time )
|
||||
{
|
||||
//dprintf( "%d timer expired\n", next_time );
|
||||
irq_pending = true;
|
||||
reset_timer( next_time );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void end_frame( nes_time_t end_time )
|
||||
{
|
||||
run_until( end_time );
|
||||
|
||||
// to do: next_time might go negative if IRQ is disabled
|
||||
next_time -= end_time;
|
||||
|
||||
sound.end_frame( end_time );
|
||||
}
|
||||
|
||||
virtual nes_time_t next_irq( nes_time_t present )
|
||||
{
|
||||
if ( irq_pending )
|
||||
return present;
|
||||
|
||||
if ( irq_mode & 2 )
|
||||
return next_time + 1;
|
||||
|
||||
return no_irq;
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t time, nes_addr_t addr, int data )
|
||||
{
|
||||
int osc = unsigned (addr - sound.base_addr) / sound.addr_step;
|
||||
|
||||
if ( (addr + 1) & 2 ) // optionally swap 1 and 2
|
||||
addr ^= swap_mask;
|
||||
|
||||
int reg = addr & 3;
|
||||
if ( (unsigned) osc < sound.osc_count && reg < sound.reg_count )
|
||||
sound.write_osc( time, osc, reg, data );
|
||||
else if ( addr < 0xf000 )
|
||||
write_bank( addr, data );
|
||||
else
|
||||
write_irq( time, addr, data );
|
||||
}
|
||||
int swap_mask;
|
||||
Nes_Vrc6_Apu sound;
|
||||
enum { timer_period = 113 * 4 + 3 };
|
||||
|
||||
|
||||
void read_state( mapper_state_t const& in )
|
||||
class Mapper_Vrc6 : public Nes_Mapper, vrc6_state_t
|
||||
{
|
||||
Nes_Mapper::read_state( in );
|
||||
public:
|
||||
Mapper_Vrc6()
|
||||
{
|
||||
swap_mask = swapMask;
|
||||
vrc6_state_t *state = this;
|
||||
register_state(state, sizeof *state);
|
||||
}
|
||||
|
||||
// to do: eliminate when format is updated
|
||||
// old-style registers
|
||||
static char zero [sizeof old_sound_regs] = { 0 };
|
||||
if ( 0 != memcmp( old_sound_regs, zero, sizeof zero ) )
|
||||
{
|
||||
/* Using old VRC6 sound register format */
|
||||
memcpy( sound_state.regs, old_sound_regs, sizeof sound_state.regs );
|
||||
memset( old_sound_regs, 0, sizeof old_sound_regs );
|
||||
}
|
||||
virtual int channel_count() const { return sound.osc_count; }
|
||||
|
||||
sound.load_state( sound_state );
|
||||
}
|
||||
virtual void set_channel_buf(int i, Blip_Buffer *b) { sound.osc_output(i, b); }
|
||||
|
||||
void write_irq( nes_time_t time, nes_addr_t addr, int data )
|
||||
{
|
||||
// IRQ
|
||||
run_until( time );
|
||||
//dprintf( "%d VRC6 IRQ [%d] = %02X\n", time, addr & 3, data );
|
||||
switch ( addr & 3 )
|
||||
{
|
||||
case 0:
|
||||
irq_reload = data;
|
||||
break;
|
||||
virtual void set_treble(blip_eq_t const &eq) { sound.treble_eq(eq); }
|
||||
|
||||
case 1:
|
||||
irq_pending = false;
|
||||
irq_mode = data;
|
||||
if ( data & 2 )
|
||||
reset_timer( time );
|
||||
break;
|
||||
virtual void reset_state()
|
||||
{
|
||||
prg_8k_bank = last_bank - 1;
|
||||
sound.reset();
|
||||
}
|
||||
|
||||
case 2:
|
||||
irq_pending = false;
|
||||
irq_mode = (irq_mode & ~2) | ((irq_mode << 1) & 2);
|
||||
break;
|
||||
}
|
||||
irq_changed();
|
||||
}
|
||||
virtual void save_state(mapper_state_t &out)
|
||||
{
|
||||
sound.save_state(&sound_state);
|
||||
Nes_Mapper::save_state(out);
|
||||
}
|
||||
|
||||
void write_bank( nes_addr_t addr, int data )
|
||||
{
|
||||
switch ( addr & 0xf003 )
|
||||
{
|
||||
case 0x8000:
|
||||
prg_16k_bank = data;
|
||||
set_prg_bank( 0x8000, bank_16k, data );
|
||||
break;
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
enable_sram();
|
||||
set_prg_bank(0x8000, bank_16k, prg_16k_bank);
|
||||
set_prg_bank(0xC000, bank_8k, prg_8k_bank);
|
||||
|
||||
case 0xb003: {
|
||||
mirroring = data;
|
||||
for (int i = 0; i < (int)sizeof chr_banks; i++)
|
||||
set_chr_bank(i * 0x400, bank_1k, chr_banks[i]);
|
||||
|
||||
//dprintf( "Change mirroring %d\n", data );
|
||||
// emu()->enable_sram( data & 0x80 ); // to do: needed?
|
||||
int page = data >> 5 & 1;
|
||||
if ( data & 8 )
|
||||
mirror_single( ((data >> 2) ^ page) & 1 );
|
||||
else if ( data & 4 )
|
||||
mirror_horiz( page );
|
||||
else
|
||||
mirror_vert( page );
|
||||
break;
|
||||
}
|
||||
write_bank(0xb003, mirroring);
|
||||
}
|
||||
|
||||
case 0xc000:
|
||||
prg_8k_bank = data;
|
||||
set_prg_bank( 0xC000, bank_8k, data );
|
||||
break;
|
||||
void reset_timer(nes_time_t present)
|
||||
{
|
||||
next_time = present + unsigned((0x100 - irq_reload) * timer_period) / 4;
|
||||
}
|
||||
|
||||
default:
|
||||
int bank = (addr >> 11 & 4) | (addr & 3);
|
||||
if ( addr >= 0xd000 )
|
||||
{
|
||||
//dprintf( "change chr bank %d\n", bank );
|
||||
chr_banks [bank] = data;
|
||||
set_chr_bank( bank * 0x400, bank_1k, data );
|
||||
}
|
||||
break;
|
||||
}
|
||||
virtual void run_until(nes_time_t end_time)
|
||||
{
|
||||
if (irq_mode & 2)
|
||||
{
|
||||
while (next_time < end_time)
|
||||
{
|
||||
// dprintf( "%d timer expired\n", next_time );
|
||||
irq_pending = true;
|
||||
reset_timer(next_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void end_frame(nes_time_t end_time)
|
||||
{
|
||||
run_until(end_time);
|
||||
|
||||
// to do: next_time might go negative if IRQ is disabled
|
||||
next_time -= end_time;
|
||||
|
||||
sound.end_frame(end_time);
|
||||
}
|
||||
|
||||
virtual nes_time_t next_irq(nes_time_t present)
|
||||
{
|
||||
if (irq_pending)
|
||||
return present;
|
||||
|
||||
if (irq_mode & 2)
|
||||
return next_time + 1;
|
||||
|
||||
return no_irq;
|
||||
}
|
||||
|
||||
virtual void write(nes_time_t time, nes_addr_t addr, int data)
|
||||
{
|
||||
int osc = unsigned(addr - sound.base_addr) / sound.addr_step;
|
||||
|
||||
if ((addr + 1) & 2) // optionally swap 1 and 2
|
||||
addr ^= swap_mask;
|
||||
|
||||
int reg = addr & 3;
|
||||
if ((unsigned)osc < sound.osc_count && reg < sound.reg_count)
|
||||
sound.write_osc(time, osc, reg, data);
|
||||
else if (addr < 0xf000)
|
||||
write_bank(addr, data);
|
||||
else
|
||||
write_irq(time, addr, data);
|
||||
}
|
||||
int swap_mask;
|
||||
Nes_Vrc6_Apu sound;
|
||||
enum
|
||||
{
|
||||
timer_period = 113 * 4 + 3
|
||||
};
|
||||
|
||||
void read_state(mapper_state_t const &in)
|
||||
{
|
||||
Nes_Mapper::read_state(in);
|
||||
|
||||
// to do: eliminate when format is updated
|
||||
// old-style registers
|
||||
static char zero[sizeof old_sound_regs] = {0};
|
||||
if (0 != memcmp(old_sound_regs, zero, sizeof zero))
|
||||
{
|
||||
/* Using old VRC6 sound register format */
|
||||
memcpy(sound_state.regs, old_sound_regs, sizeof sound_state.regs);
|
||||
memset(old_sound_regs, 0, sizeof old_sound_regs);
|
||||
}
|
||||
|
||||
sound.load_state(sound_state);
|
||||
}
|
||||
|
||||
void write_irq(nes_time_t time, nes_addr_t addr, int data)
|
||||
{
|
||||
// IRQ
|
||||
run_until(time);
|
||||
// dprintf( "%d VRC6 IRQ [%d] = %02X\n", time, addr & 3, data );
|
||||
switch (addr & 3)
|
||||
{
|
||||
case 0:
|
||||
irq_reload = data;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
irq_pending = false;
|
||||
irq_mode = data;
|
||||
if (data & 2)
|
||||
reset_timer(time);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
irq_pending = false;
|
||||
irq_mode = (irq_mode & ~2) | ((irq_mode << 1) & 2);
|
||||
break;
|
||||
}
|
||||
irq_changed();
|
||||
}
|
||||
|
||||
void write_bank(nes_addr_t addr, int data)
|
||||
{
|
||||
switch (addr & 0xf003)
|
||||
{
|
||||
case 0x8000:
|
||||
prg_16k_bank = data;
|
||||
set_prg_bank(0x8000, bank_16k, data);
|
||||
break;
|
||||
|
||||
case 0xb003:
|
||||
{
|
||||
mirroring = data;
|
||||
|
||||
// dprintf( "Change mirroring %d\n", data );
|
||||
// emu()->enable_sram( data & 0x80 ); // to do: needed?
|
||||
int page = data >> 5 & 1;
|
||||
if (data & 8)
|
||||
mirror_single(((data >> 2) ^ page) & 1);
|
||||
else if (data & 4)
|
||||
mirror_horiz(page);
|
||||
else
|
||||
mirror_vert(page);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0xc000:
|
||||
prg_8k_bank = data;
|
||||
set_prg_bank(0xC000, bank_8k, data);
|
||||
break;
|
||||
|
||||
default:
|
||||
int bank = (addr >> 11 & 4) | (addr & 3);
|
||||
if (addr >= 0xd000)
|
||||
{
|
||||
// dprintf( "change chr bank %d\n", bank );
|
||||
chr_banks[bank] = data;
|
||||
set_chr_bank(bank * 0x400, bank_1k, data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
typedef Mapper_Vrc6<0> Mapper024;
|
|
@ -28,4 +28,4 @@
|
|||
|
||||
#include "mappers/mapper.hpp"
|
||||
|
||||
typedef Mapper_VRC2_4<true,false> Mapper025;
|
||||
typedef Mapper_VRC2_4<true, false> Mapper025;
|
|
@ -31,21 +31,21 @@
|
|||
|
||||
// Unrom512
|
||||
|
||||
class Mapper030 : public Nes_Mapper {
|
||||
public:
|
||||
Mapper030() { }
|
||||
class Mapper030 : public Nes_Mapper
|
||||
{
|
||||
public:
|
||||
Mapper030() {}
|
||||
|
||||
void reset_state() { }
|
||||
void reset_state() {}
|
||||
|
||||
void apply_mapping() { }
|
||||
void apply_mapping() {}
|
||||
|
||||
void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
if ( ( addr & 0xF000 ) >= 0x8000 )
|
||||
{
|
||||
set_prg_bank(0x8000, bank_16k, data & 0x1F);
|
||||
set_chr_bank(0x0000, bank_8k, (data >> 5) & 0x3);
|
||||
}
|
||||
}
|
||||
void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
if ((addr & 0xF000) >= 0x8000)
|
||||
{
|
||||
set_prg_bank(0x8000, bank_16k, data & 0x1F);
|
||||
set_chr_bank(0x0000, bank_8k, (data >> 5) & 0x3);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -27,92 +27,98 @@
|
|||
|
||||
struct mapper32_state_t
|
||||
{
|
||||
uint8_t chr_bank [ 8 ];
|
||||
uint8_t prg_bank [ 2 ];
|
||||
uint8_t prg_mode;
|
||||
uint8_t mirr;
|
||||
uint8_t chr_bank[8];
|
||||
uint8_t prg_bank[2];
|
||||
uint8_t prg_mode;
|
||||
uint8_t mirr;
|
||||
};
|
||||
static_assert( sizeof ( mapper32_state_t ) == 12 );
|
||||
static_assert(sizeof(mapper32_state_t) == 12);
|
||||
|
||||
// Irem_G101
|
||||
|
||||
class Mapper032 : public Nes_Mapper, mapper32_state_t {
|
||||
public:
|
||||
Mapper032()
|
||||
{
|
||||
mapper32_state_t * state = this;
|
||||
register_state( state, sizeof * state );
|
||||
}
|
||||
class Mapper032 : public Nes_Mapper, mapper32_state_t
|
||||
{
|
||||
public:
|
||||
Mapper032()
|
||||
{
|
||||
mapper32_state_t *state = this;
|
||||
register_state(state, sizeof *state);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{
|
||||
prg_bank [ 0 ] = ~1;
|
||||
prg_bank [ 1 ] = ~0;
|
||||
enable_sram();
|
||||
}
|
||||
virtual void reset_state()
|
||||
{
|
||||
prg_bank[0] = ~1;
|
||||
prg_bank[1] = ~0;
|
||||
enable_sram();
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
if ( prg_mode == 0 )
|
||||
{
|
||||
set_prg_bank ( 0x8000, bank_8k, prg_bank [ 0 ] );
|
||||
set_prg_bank ( 0xA000, bank_8k, prg_bank [ 1 ] );
|
||||
set_prg_bank ( 0xC000, bank_8k, ~1 );
|
||||
set_prg_bank ( 0xE000, bank_8k, ~0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
set_prg_bank ( 0xC000, bank_8k, prg_bank [ 0 ] );
|
||||
set_prg_bank ( 0xA000, bank_8k, prg_bank [ 1 ] );
|
||||
set_prg_bank ( 0x8000, bank_8k, ~1 );
|
||||
set_prg_bank ( 0xE000, bank_8k, ~0 );
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
if (prg_mode == 0)
|
||||
{
|
||||
set_prg_bank(0x8000, bank_8k, prg_bank[0]);
|
||||
set_prg_bank(0xA000, bank_8k, prg_bank[1]);
|
||||
set_prg_bank(0xC000, bank_8k, ~1);
|
||||
set_prg_bank(0xE000, bank_8k, ~0);
|
||||
}
|
||||
else
|
||||
{
|
||||
set_prg_bank(0xC000, bank_8k, prg_bank[0]);
|
||||
set_prg_bank(0xA000, bank_8k, prg_bank[1]);
|
||||
set_prg_bank(0x8000, bank_8k, ~1);
|
||||
set_prg_bank(0xE000, bank_8k, ~0);
|
||||
}
|
||||
|
||||
for ( unsigned long int i = 0; i < sizeof chr_bank; i++)
|
||||
set_chr_bank( ( i << 10 ), bank_1k, chr_bank [ i ] );
|
||||
for (unsigned long int i = 0; i < sizeof chr_bank; i++)
|
||||
set_chr_bank((i << 10), bank_1k, chr_bank[i]);
|
||||
|
||||
switch ( mirr )
|
||||
{
|
||||
case 0: mirror_vert(); break;
|
||||
case 1: mirror_horiz(); break;
|
||||
}
|
||||
}
|
||||
switch (mirr)
|
||||
{
|
||||
case 0: mirror_vert(); break;
|
||||
case 1: mirror_horiz(); break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
switch ( addr & 0xF000 )
|
||||
{
|
||||
case 0x8000:
|
||||
prg_bank [ 0 ] = data;
|
||||
switch ( prg_mode )
|
||||
{
|
||||
case 0: set_prg_bank ( 0x8000, bank_8k, data ); break;
|
||||
case 1: set_prg_bank ( 0xC000, bank_8k, data ); break;
|
||||
}
|
||||
break;
|
||||
case 0x9000:
|
||||
mirr = data & 1;
|
||||
prg_mode = ( data >> 1 ) & 1;
|
||||
switch ( data & 1 )
|
||||
{
|
||||
case 0: mirror_vert(); break;
|
||||
case 1: mirror_horiz(); break;
|
||||
}
|
||||
break;
|
||||
case 0xA000:
|
||||
prg_bank [ 1 ] = data;
|
||||
set_prg_bank ( 0xA000, bank_8k, data );
|
||||
break;
|
||||
}
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
switch (addr & 0xF000)
|
||||
{
|
||||
case 0x8000:
|
||||
prg_bank[0] = data;
|
||||
switch (prg_mode)
|
||||
{
|
||||
case 0: set_prg_bank(0x8000, bank_8k, data); break;
|
||||
case 1: set_prg_bank(0xC000, bank_8k, data); break;
|
||||
}
|
||||
break;
|
||||
case 0x9000:
|
||||
mirr = data & 1;
|
||||
prg_mode = (data >> 1) & 1;
|
||||
switch (data & 1)
|
||||
{
|
||||
case 0: mirror_vert(); break;
|
||||
case 1: mirror_horiz(); break;
|
||||
}
|
||||
break;
|
||||
case 0xA000:
|
||||
prg_bank[1] = data;
|
||||
set_prg_bank(0xA000, bank_8k, data);
|
||||
break;
|
||||
}
|
||||
|
||||
switch ( addr & 0xF007 )
|
||||
{
|
||||
case 0xB000: case 0xB001: case 0xB002: case 0xB003:
|
||||
case 0xB004: case 0xB005: case 0xB006: case 0xB007:
|
||||
chr_bank [ addr & 0x07 ] = data;
|
||||
set_chr_bank( ( addr & 0x07 ) << 10, bank_1k, data );
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (addr & 0xF007)
|
||||
{
|
||||
case 0xB000:
|
||||
case 0xB001:
|
||||
case 0xB002:
|
||||
case 0xB003:
|
||||
case 0xB004:
|
||||
case 0xB005:
|
||||
case 0xB006:
|
||||
case 0xB007:
|
||||
chr_bank[addr & 0x07] = data;
|
||||
set_chr_bank((addr & 0x07) << 10, bank_1k, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -27,66 +27,75 @@
|
|||
|
||||
struct tc0190_state_t
|
||||
{
|
||||
uint8_t preg [ 2 ];
|
||||
uint8_t creg [ 6 ];
|
||||
uint8_t mirr;
|
||||
uint8_t preg[2];
|
||||
uint8_t creg[6];
|
||||
uint8_t mirr;
|
||||
};
|
||||
static_assert( sizeof ( tc0190_state_t ) == 9 );
|
||||
static_assert(sizeof(tc0190_state_t) == 9);
|
||||
|
||||
// TaitoTC0190
|
||||
|
||||
class Mapper033 : public Nes_Mapper, tc0190_state_t {
|
||||
public:
|
||||
Mapper033()
|
||||
{
|
||||
tc0190_state_t *state = this;
|
||||
register_state( state, sizeof *state );
|
||||
}
|
||||
class Mapper033 : public Nes_Mapper, tc0190_state_t
|
||||
{
|
||||
public:
|
||||
Mapper033()
|
||||
{
|
||||
tc0190_state_t *state = this;
|
||||
register_state(state, sizeof *state);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{ }
|
||||
virtual void reset_state()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
for ( int i = 0; i < 2; i++ )
|
||||
{
|
||||
set_prg_bank ( 0x8000 + ( i << 13 ), bank_8k, preg [ i ] );
|
||||
set_chr_bank ( 0x0000 + ( i << 11 ), bank_2k, creg [ i ] );
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
set_prg_bank(0x8000 + (i << 13), bank_8k, preg[i]);
|
||||
set_chr_bank(0x0000 + (i << 11), bank_2k, creg[i]);
|
||||
}
|
||||
|
||||
for ( int i = 0; i < 4; i++ )
|
||||
set_chr_bank ( 0x1000 + ( i << 10 ), bank_1k, creg [ 2 + i ] );
|
||||
for (int i = 0; i < 4; i++)
|
||||
set_chr_bank(0x1000 + (i << 10), bank_1k, creg[2 + i]);
|
||||
|
||||
if ( mirr ) mirror_horiz();
|
||||
else mirror_vert();
|
||||
}
|
||||
if (mirr)
|
||||
mirror_horiz();
|
||||
else
|
||||
mirror_vert();
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
switch ( addr & 0xA003 )
|
||||
{
|
||||
case 0x8000:
|
||||
preg [ 0 ] = data & 0x3F;
|
||||
mirr = data >> 6;
|
||||
set_prg_bank ( 0x8000, bank_8k, preg [ 0 ] );
|
||||
if ( mirr ) mirror_horiz();
|
||||
else mirror_vert();
|
||||
break;
|
||||
case 0x8001:
|
||||
preg [ 1 ] = data & 0x3F;
|
||||
set_prg_bank ( 0xA000, bank_8k, preg [ 1 ] );
|
||||
break;
|
||||
case 0x8002: case 0x8003:
|
||||
addr &= 0x01;
|
||||
creg [ addr ] = data;
|
||||
set_chr_bank ( addr << 11, bank_2k, creg [ addr ] );
|
||||
break;
|
||||
case 0xA000: case 0xA001:
|
||||
case 0xA002: case 0xA003:
|
||||
addr &= 0x03;
|
||||
creg [ 2 + addr ] = data;
|
||||
set_chr_bank ( 0x1000 | ( addr << 10 ), bank_1k, creg [ 2 + addr ] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
switch (addr & 0xA003)
|
||||
{
|
||||
case 0x8000:
|
||||
preg[0] = data & 0x3F;
|
||||
mirr = data >> 6;
|
||||
set_prg_bank(0x8000, bank_8k, preg[0]);
|
||||
if (mirr)
|
||||
mirror_horiz();
|
||||
else
|
||||
mirror_vert();
|
||||
break;
|
||||
case 0x8001:
|
||||
preg[1] = data & 0x3F;
|
||||
set_prg_bank(0xA000, bank_8k, preg[1]);
|
||||
break;
|
||||
case 0x8002:
|
||||
case 0x8003:
|
||||
addr &= 0x01;
|
||||
creg[addr] = data;
|
||||
set_chr_bank(addr << 11, bank_2k, creg[addr]);
|
||||
break;
|
||||
case 0xA000:
|
||||
case 0xA001:
|
||||
case 0xA002:
|
||||
case 0xA003:
|
||||
addr &= 0x03;
|
||||
creg[2 + addr] = data;
|
||||
set_chr_bank(0x1000 | (addr << 10), bank_1k, creg[2 + addr]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -19,23 +19,24 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
// Nina-1 (Deadly Towers only)
|
||||
|
||||
class Mapper034 : public Nes_Mapper {
|
||||
uint8_t bank;
|
||||
public:
|
||||
Mapper034()
|
||||
{
|
||||
register_state( &bank, 1 );
|
||||
}
|
||||
class Mapper034 : public Nes_Mapper
|
||||
{
|
||||
uint8_t bank;
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
write( 0, 0, bank );
|
||||
}
|
||||
public:
|
||||
Mapper034()
|
||||
{
|
||||
register_state(&bank, 1);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t, int data )
|
||||
{
|
||||
bank = data;
|
||||
set_prg_bank( 0x8000, bank_32k, bank );
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
write(0, 0, bank);
|
||||
}
|
||||
|
||||
virtual void write(nes_time_t, nes_addr_t, int data)
|
||||
{
|
||||
bank = data;
|
||||
set_prg_bank(0x8000, bank_32k, bank);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -21,30 +21,31 @@
|
|||
|
||||
// NROM-128 4-in-1 multicart
|
||||
|
||||
class Mapper060 : public Nes_Mapper {
|
||||
public:
|
||||
Mapper060()
|
||||
{
|
||||
last_game = 2;
|
||||
register_state( &game_sel, 1 );
|
||||
}
|
||||
class Mapper060 : public Nes_Mapper
|
||||
{
|
||||
public:
|
||||
Mapper060()
|
||||
{
|
||||
last_game = 2;
|
||||
register_state(&game_sel, 1);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{
|
||||
game_sel = last_game;
|
||||
game_sel++;
|
||||
game_sel &= 3;
|
||||
}
|
||||
virtual void reset_state()
|
||||
{
|
||||
game_sel = last_game;
|
||||
game_sel++;
|
||||
game_sel &= 3;
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
set_prg_bank ( 0x8000, bank_16k, game_sel );
|
||||
set_prg_bank ( 0xC000, bank_16k, game_sel );
|
||||
set_chr_bank ( 0, bank_8k, game_sel );
|
||||
last_game = game_sel;
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
set_prg_bank(0x8000, bank_16k, game_sel);
|
||||
set_prg_bank(0xC000, bank_16k, game_sel);
|
||||
set_chr_bank(0, bank_8k, game_sel);
|
||||
last_game = game_sel;
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data ) { }
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data) {}
|
||||
|
||||
uint8_t game_sel, last_game;
|
||||
uint8_t game_sel, last_game;
|
||||
};
|
||||
|
|
|
@ -19,31 +19,32 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
// GNROM
|
||||
|
||||
class Mapper066 : public Nes_Mapper {
|
||||
uint8_t bank;
|
||||
public:
|
||||
Mapper066()
|
||||
{
|
||||
register_state( &bank, 1 );
|
||||
}
|
||||
class Mapper066 : public Nes_Mapper
|
||||
{
|
||||
uint8_t bank;
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
int b = bank;
|
||||
bank = ~b;
|
||||
write( 0, 0, b );
|
||||
}
|
||||
public:
|
||||
Mapper066()
|
||||
{
|
||||
register_state(&bank, 1);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t, int data )
|
||||
{
|
||||
int changed = bank ^ data;
|
||||
bank = data;
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
int b = bank;
|
||||
bank = ~b;
|
||||
write(0, 0, b);
|
||||
}
|
||||
|
||||
if ( changed & 0x30 )
|
||||
set_prg_bank( 0x8000, bank_32k, bank >> 4 & 3 );
|
||||
virtual void write(nes_time_t, nes_addr_t, int data)
|
||||
{
|
||||
int changed = bank ^ data;
|
||||
bank = data;
|
||||
|
||||
if ( changed & 0x03 )
|
||||
set_chr_bank( 0, bank_8k, bank & 3 );
|
||||
}
|
||||
if (changed & 0x30)
|
||||
set_prg_bank(0x8000, bank_32k, bank >> 4 & 3);
|
||||
|
||||
if (changed & 0x03)
|
||||
set_chr_bank(0, bank_8k, bank & 3);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
// Nes_Emu 0.7.0. http://www.slack.net/~ant/libs/
|
||||
|
||||
#include "mappers/mapper.hpp"
|
||||
#include "apu/fme7/apu.hpp"
|
||||
#include "mappers/mapper.hpp"
|
||||
|
||||
/* Copyright (C) 2005 Chris Moeller */
|
||||
/* Copyright (C) 2005-2006 Shay Green. This module is free software; you
|
||||
|
@ -21,170 +21,168 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
struct fme7_state_t
|
||||
{
|
||||
// first 16 bytes in register order
|
||||
uint8_t regs [13];
|
||||
uint8_t irq_mode;
|
||||
uint16_t irq_count;
|
||||
// first 16 bytes in register order
|
||||
uint8_t regs[13];
|
||||
uint8_t irq_mode;
|
||||
uint16_t irq_count;
|
||||
|
||||
uint8_t command;
|
||||
uint8_t irq_pending;
|
||||
fme7_apu_state_t sound_state; // only used when saving/restoring state
|
||||
uint8_t command;
|
||||
uint8_t irq_pending;
|
||||
fme7_apu_state_t sound_state; // only used when saving/restoring state
|
||||
};
|
||||
static_assert( sizeof (fme7_state_t) == 18 + sizeof (fme7_apu_state_t) );
|
||||
static_assert(sizeof(fme7_state_t) == 18 + sizeof(fme7_apu_state_t));
|
||||
|
||||
// Fme7
|
||||
|
||||
class Mapper069 : public Nes_Mapper, fme7_state_t {
|
||||
public:
|
||||
Mapper069()
|
||||
{
|
||||
fme7_state_t* state = this;
|
||||
register_state( state, sizeof *state );
|
||||
}
|
||||
class Mapper069 : public Nes_Mapper, fme7_state_t
|
||||
{
|
||||
public:
|
||||
Mapper069()
|
||||
{
|
||||
fme7_state_t *state = this;
|
||||
register_state(state, sizeof *state);
|
||||
}
|
||||
|
||||
virtual int channel_count() const { return sound.osc_count; }
|
||||
virtual int channel_count() const { return sound.osc_count; }
|
||||
|
||||
virtual void set_channel_buf( int i, Blip_Buffer* b ) { sound.osc_output( i, b ); }
|
||||
virtual void set_channel_buf(int i, Blip_Buffer *b) { sound.osc_output(i, b); }
|
||||
|
||||
virtual void set_treble( blip_eq_t const& eq ) { sound.treble_eq( eq ); }
|
||||
virtual void set_treble(blip_eq_t const &eq) { sound.treble_eq(eq); }
|
||||
|
||||
virtual void reset_state()
|
||||
{
|
||||
regs [8] = 0x40; // wram disabled
|
||||
irq_count = 0xFFFF;
|
||||
sound.reset();
|
||||
}
|
||||
virtual void reset_state()
|
||||
{
|
||||
regs[8] = 0x40; // wram disabled
|
||||
irq_count = 0xFFFF;
|
||||
sound.reset();
|
||||
}
|
||||
|
||||
virtual void save_state( mapper_state_t& out )
|
||||
{
|
||||
sound.save_state( &sound_state );
|
||||
Nes_Mapper::save_state( out );
|
||||
}
|
||||
virtual void save_state(mapper_state_t &out)
|
||||
{
|
||||
sound.save_state(&sound_state);
|
||||
Nes_Mapper::save_state(out);
|
||||
}
|
||||
|
||||
virtual void read_state( mapper_state_t const& in )
|
||||
{
|
||||
Nes_Mapper::read_state( in );
|
||||
sound.load_state( sound_state );
|
||||
}
|
||||
virtual void read_state(mapper_state_t const &in)
|
||||
{
|
||||
Nes_Mapper::read_state(in);
|
||||
sound.load_state(sound_state);
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
last_time = 0;
|
||||
for ( int i = 0; i < (int) sizeof regs; i++ )
|
||||
write_register( i, regs [i] );
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
last_time = 0;
|
||||
for (int i = 0; i < (int)sizeof regs; i++)
|
||||
write_register(i, regs[i]);
|
||||
}
|
||||
|
||||
virtual void run_until( nes_time_t end_time )
|
||||
{
|
||||
int new_count = irq_count - (end_time - last_time);
|
||||
last_time = end_time;
|
||||
virtual void run_until(nes_time_t end_time)
|
||||
{
|
||||
int new_count = irq_count - (end_time - last_time);
|
||||
last_time = end_time;
|
||||
|
||||
if ( new_count <= 0 && (irq_mode & 0x81) == 0x81 )
|
||||
irq_pending = true;
|
||||
if (new_count <= 0 && (irq_mode & 0x81) == 0x81)
|
||||
irq_pending = true;
|
||||
|
||||
if ( irq_mode & 0x01 )
|
||||
irq_count = new_count & 0xFFFF;
|
||||
}
|
||||
if (irq_mode & 0x01)
|
||||
irq_count = new_count & 0xFFFF;
|
||||
}
|
||||
|
||||
virtual nes_time_t next_irq( nes_time_t )
|
||||
{
|
||||
if ( irq_pending )
|
||||
return 0;
|
||||
virtual nes_time_t next_irq(nes_time_t)
|
||||
{
|
||||
if (irq_pending)
|
||||
return 0;
|
||||
|
||||
if ( (irq_mode & 0x81) == 0x81 )
|
||||
return last_time + irq_count + 1;
|
||||
if ((irq_mode & 0x81) == 0x81)
|
||||
return last_time + irq_count + 1;
|
||||
|
||||
return no_irq;
|
||||
}
|
||||
return no_irq;
|
||||
}
|
||||
|
||||
virtual void end_frame( nes_time_t end_time )
|
||||
{
|
||||
if ( end_time > last_time )
|
||||
run_until( end_time );
|
||||
virtual void end_frame(nes_time_t end_time)
|
||||
{
|
||||
if (end_time > last_time)
|
||||
run_until(end_time);
|
||||
|
||||
last_time -= end_time;
|
||||
last_time -= end_time;
|
||||
|
||||
sound.end_frame( end_time );
|
||||
}
|
||||
sound.end_frame(end_time);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t time, nes_addr_t addr, int data )
|
||||
{
|
||||
switch ( addr & 0xE000 )
|
||||
{
|
||||
case 0x8000:
|
||||
command = data & 0x0F;
|
||||
break;
|
||||
virtual void write(nes_time_t time, nes_addr_t addr, int data)
|
||||
{
|
||||
switch (addr & 0xE000)
|
||||
{
|
||||
case 0x8000:
|
||||
command = data & 0x0F;
|
||||
break;
|
||||
|
||||
case 0xA000:
|
||||
if ( command < 0x0D )
|
||||
write_register( command, data );
|
||||
else
|
||||
write_irq( time, command, data );
|
||||
break;
|
||||
case 0xA000:
|
||||
if (command < 0x0D)
|
||||
write_register(command, data);
|
||||
else
|
||||
write_irq(time, command, data);
|
||||
break;
|
||||
|
||||
case 0xC000:
|
||||
sound.write_latch( data );
|
||||
break;
|
||||
case 0xC000:
|
||||
sound.write_latch(data);
|
||||
break;
|
||||
|
||||
case 0xE000:
|
||||
sound.write_data( time, data );
|
||||
break;
|
||||
}
|
||||
}
|
||||
case 0xE000:
|
||||
sound.write_data(time, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void write_irq(nes_time_t time, int index, int data)
|
||||
{
|
||||
run_until(time);
|
||||
switch (index)
|
||||
{
|
||||
case 0x0D:
|
||||
irq_mode = data;
|
||||
irq_pending = false;
|
||||
irq_changed();
|
||||
break;
|
||||
|
||||
void write_irq( nes_time_t time, int index, int data )
|
||||
{
|
||||
run_until( time );
|
||||
switch ( index )
|
||||
{
|
||||
case 0x0D:
|
||||
irq_mode = data;
|
||||
irq_pending = false;
|
||||
irq_changed();
|
||||
break;
|
||||
case 0x0E:
|
||||
irq_count = (irq_count & 0xFF00) | data;
|
||||
break;
|
||||
|
||||
case 0x0E:
|
||||
irq_count = (irq_count & 0xFF00) | data;
|
||||
break;
|
||||
case 0x0F:
|
||||
irq_count = data << 8 | (irq_count & 0xFF);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
case 0x0F:
|
||||
irq_count = data << 8 | (irq_count & 0xFF);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void write_register(int index, int data)
|
||||
{
|
||||
regs[index] = data;
|
||||
int prg_bank = index - 0x09;
|
||||
if ((unsigned)prg_bank < 3) // most common
|
||||
{
|
||||
set_prg_bank(0x8000 | (prg_bank << bank_8k), bank_8k, data);
|
||||
}
|
||||
else if (index == 0x08)
|
||||
{
|
||||
enable_sram((data & 0xC0) == 0xC0);
|
||||
if (!(data & 0xC0))
|
||||
set_prg_bank(0x6000, bank_8k, data & 0x3F);
|
||||
}
|
||||
else if (index < 0x08)
|
||||
{
|
||||
set_chr_bank(index * 0x400, bank_1k, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (data & 2)
|
||||
mirror_single(data & 1);
|
||||
else if (data & 1)
|
||||
mirror_horiz();
|
||||
else
|
||||
mirror_vert();
|
||||
}
|
||||
}
|
||||
|
||||
void write_register( int index, int data )
|
||||
{
|
||||
regs [index] = data;
|
||||
int prg_bank = index - 0x09;
|
||||
if ( (unsigned) prg_bank < 3 ) // most common
|
||||
{
|
||||
set_prg_bank( 0x8000 | (prg_bank << bank_8k), bank_8k, data );
|
||||
}
|
||||
else if ( index == 0x08 )
|
||||
{
|
||||
enable_sram( (data & 0xC0) == 0xC0 );
|
||||
if ( !(data & 0xC0) )
|
||||
set_prg_bank( 0x6000, bank_8k, data & 0x3F );
|
||||
}
|
||||
else if ( index < 0x08 )
|
||||
{
|
||||
set_chr_bank( index * 0x400, bank_1k, data );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( data & 2 )
|
||||
mirror_single( data & 1 );
|
||||
else if ( data & 1 )
|
||||
mirror_horiz();
|
||||
else
|
||||
mirror_vert();
|
||||
}
|
||||
}
|
||||
|
||||
nes_time_t last_time;
|
||||
Nes_Fme7_Apu sound;
|
||||
nes_time_t last_time;
|
||||
Nes_Fme7_Apu sound;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -27,55 +27,56 @@
|
|||
|
||||
// Mapper_74x161x162x32
|
||||
|
||||
template < int mapperId >
|
||||
class Mapper_74x161x162x32 : public Nes_Mapper {
|
||||
public:
|
||||
Mapper_74x161x162x32()
|
||||
{
|
||||
register_state( &bank, 1 );
|
||||
}
|
||||
template <int mapperId>
|
||||
class Mapper_74x161x162x32 : public Nes_Mapper
|
||||
{
|
||||
public:
|
||||
Mapper_74x161x162x32()
|
||||
{
|
||||
register_state(&bank, 1);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{
|
||||
if ( mapperId == 86 )
|
||||
bank = ~0;
|
||||
}
|
||||
virtual void reset_state()
|
||||
{
|
||||
if (mapperId == 86)
|
||||
bank = ~0;
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
if ( mapperId == 152 ) write( 0, 0, bank );
|
||||
if ( mapperId == 70 ) write( 0, 0, bank );
|
||||
if ( mapperId == 86 )
|
||||
{
|
||||
intercept_writes( 0x6000, 1 );
|
||||
write_intercepted( 0, 0x6000, bank );
|
||||
}
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
if (mapperId == 152) write(0, 0, bank);
|
||||
if (mapperId == 70) write(0, 0, bank);
|
||||
if (mapperId == 86)
|
||||
{
|
||||
intercept_writes(0x6000, 1);
|
||||
write_intercepted(0, 0x6000, bank);
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool write_intercepted( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
if ( addr != 0x6000 ) return false;
|
||||
if ( mapperId == 152 ) return false;
|
||||
if ( mapperId == 70 ) return false;
|
||||
virtual bool write_intercepted(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
if (addr != 0x6000) return false;
|
||||
if (mapperId == 152) return false;
|
||||
if (mapperId == 70) return false;
|
||||
|
||||
bank = data;
|
||||
set_prg_bank( 0x8000, bank_32k, ( bank >> 4 ) & 0x03 );
|
||||
set_chr_bank( 0x0000, bank_8k, ( ( bank >> 4 ) & 0x04 ) | ( bank & 0x03 ) );
|
||||
bank = data;
|
||||
set_prg_bank(0x8000, bank_32k, (bank >> 4) & 0x03);
|
||||
set_chr_bank(0x0000, bank_8k, ((bank >> 4) & 0x04) | (bank & 0x03));
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
if ( mapperId == 86) return;
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
if (mapperId == 86) return;
|
||||
|
||||
bank = handle_bus_conflict (addr, data );
|
||||
set_prg_bank( 0x8000, bank_16k, ( bank >> 4 ) & 0x07 );
|
||||
set_chr_bank( 0x0000, bank_8k, bank & 0x0F );
|
||||
mirror_single( ( bank >> 7) & 0x01 );
|
||||
}
|
||||
bank = handle_bus_conflict(addr, data);
|
||||
set_prg_bank(0x8000, bank_16k, (bank >> 4) & 0x07);
|
||||
set_chr_bank(0x0000, bank_8k, bank & 0x0F);
|
||||
mirror_single((bank >> 7) & 0x01);
|
||||
}
|
||||
|
||||
uint8_t bank;
|
||||
uint8_t bank;
|
||||
};
|
||||
|
||||
typedef Mapper_74x161x162x32<70> Mapper070;
|
|
@ -19,36 +19,34 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
// Camerica
|
||||
|
||||
class Mapper071 : public Nes_Mapper {
|
||||
uint8_t regs [3];
|
||||
public:
|
||||
Mapper071()
|
||||
{
|
||||
register_state( regs, sizeof regs );
|
||||
}
|
||||
class Mapper071 : public Nes_Mapper
|
||||
{
|
||||
uint8_t regs[3];
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
write( 0, 0xc000, regs [0] );
|
||||
if ( regs [1] & 0x80 )
|
||||
write( 0, 0x9000, regs [1] );
|
||||
}
|
||||
public:
|
||||
Mapper071()
|
||||
{
|
||||
register_state(regs, sizeof regs);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
if ( addr >= 0xc000 )
|
||||
{
|
||||
regs [0] = data;
|
||||
set_prg_bank( 0x8000, bank_16k, data );
|
||||
}
|
||||
else if ( (addr & 0xf000) == 0x9000 )
|
||||
{
|
||||
regs [1] = 0x80 | data;
|
||||
mirror_single( (data >> 4) & 1 );
|
||||
}
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
write(0, 0xc000, regs[0]);
|
||||
if (regs[1] & 0x80)
|
||||
write(0, 0x9000, regs[1]);
|
||||
}
|
||||
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
if (addr >= 0xc000)
|
||||
{
|
||||
regs[0] = data;
|
||||
set_prg_bank(0x8000, bank_16k, data);
|
||||
}
|
||||
else if ((addr & 0xf000) == 0x9000)
|
||||
{
|
||||
regs[1] = 0x80 | data;
|
||||
mirror_single((data >> 4) & 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -27,107 +27,108 @@
|
|||
|
||||
struct vrc3_state_t
|
||||
{
|
||||
bool irq_enable;
|
||||
bool irq_awk;
|
||||
uint16_t irq_latch;
|
||||
uint16_t irq_counter;
|
||||
bool irq_enable;
|
||||
bool irq_awk;
|
||||
uint16_t irq_latch;
|
||||
uint16_t irq_counter;
|
||||
|
||||
uint8_t irq_pending;
|
||||
uint16_t next_time;
|
||||
uint8_t irq_pending;
|
||||
uint16_t next_time;
|
||||
};
|
||||
|
||||
// VRC3
|
||||
|
||||
class Mapper073 : public Nes_Mapper, vrc3_state_t {
|
||||
public:
|
||||
Mapper073()
|
||||
{
|
||||
vrc3_state_t * state = this;
|
||||
register_state( state, sizeof * state );
|
||||
}
|
||||
class Mapper073 : public Nes_Mapper, vrc3_state_t
|
||||
{
|
||||
public:
|
||||
Mapper073()
|
||||
{
|
||||
vrc3_state_t *state = this;
|
||||
register_state(state, sizeof *state);
|
||||
}
|
||||
|
||||
void reset_state()
|
||||
{
|
||||
}
|
||||
void reset_state()
|
||||
{
|
||||
}
|
||||
|
||||
void apply_mapping()
|
||||
{
|
||||
enable_sram();
|
||||
mirror_vert();
|
||||
}
|
||||
void apply_mapping()
|
||||
{
|
||||
enable_sram();
|
||||
mirror_vert();
|
||||
}
|
||||
|
||||
virtual void run_until( nes_time_t end_time )
|
||||
{
|
||||
if ( irq_enable )
|
||||
{
|
||||
long counter = irq_counter + ( end_time - next_time );
|
||||
virtual void run_until(nes_time_t end_time)
|
||||
{
|
||||
if (irq_enable)
|
||||
{
|
||||
long counter = irq_counter + (end_time - next_time);
|
||||
|
||||
if ( counter > 0xFFFF )
|
||||
{
|
||||
irq_pending = true;
|
||||
irq_enable = irq_awk;
|
||||
irq_counter = irq_latch;
|
||||
}
|
||||
else
|
||||
irq_counter = counter;
|
||||
}
|
||||
if (counter > 0xFFFF)
|
||||
{
|
||||
irq_pending = true;
|
||||
irq_enable = irq_awk;
|
||||
irq_counter = irq_latch;
|
||||
}
|
||||
else
|
||||
irq_counter = counter;
|
||||
}
|
||||
|
||||
next_time = end_time;
|
||||
}
|
||||
next_time = end_time;
|
||||
}
|
||||
|
||||
virtual void end_frame( nes_time_t end_time )
|
||||
{
|
||||
if ( end_time > next_time )
|
||||
run_until( end_time );
|
||||
virtual void end_frame(nes_time_t end_time)
|
||||
{
|
||||
if (end_time > next_time)
|
||||
run_until(end_time);
|
||||
|
||||
next_time -= end_time;
|
||||
}
|
||||
next_time -= end_time;
|
||||
}
|
||||
|
||||
virtual nes_time_t next_irq( nes_time_t present )
|
||||
{
|
||||
if ( irq_pending )
|
||||
return present;
|
||||
virtual nes_time_t next_irq(nes_time_t present)
|
||||
{
|
||||
if (irq_pending)
|
||||
return present;
|
||||
|
||||
if ( !irq_enable )
|
||||
return no_irq;
|
||||
if (!irq_enable)
|
||||
return no_irq;
|
||||
|
||||
return 0x10000 - irq_counter + next_time;
|
||||
}
|
||||
return 0x10000 - irq_counter + next_time;
|
||||
}
|
||||
|
||||
void write_irq_counter( int shift, int data )
|
||||
{
|
||||
irq_latch &= ~( 0xF << shift );
|
||||
irq_latch |= data << shift;
|
||||
}
|
||||
void write_irq_counter(int shift, int data)
|
||||
{
|
||||
irq_latch &= ~(0xF << shift);
|
||||
irq_latch |= data << shift;
|
||||
}
|
||||
|
||||
void write( nes_time_t time, nes_addr_t addr, int data )
|
||||
{
|
||||
data &= 0xF;
|
||||
void write(nes_time_t time, nes_addr_t addr, int data)
|
||||
{
|
||||
data &= 0xF;
|
||||
|
||||
switch ( addr >> 12 )
|
||||
{
|
||||
case 0xF: set_prg_bank( 0x8000, bank_16k, data ); break;
|
||||
case 0x8: write_irq_counter( 0, data ); break;
|
||||
case 0x9: write_irq_counter( 4, data ); break;
|
||||
case 0xA: write_irq_counter( 8, data ); break;
|
||||
case 0xB: write_irq_counter( 12, data ); break;
|
||||
case 0xC:
|
||||
irq_pending = false;
|
||||
irq_awk = data & 1;
|
||||
irq_enable = data & 2;
|
||||
switch (addr >> 12)
|
||||
{
|
||||
case 0xF: set_prg_bank(0x8000, bank_16k, data); break;
|
||||
case 0x8: write_irq_counter(0, data); break;
|
||||
case 0x9: write_irq_counter(4, data); break;
|
||||
case 0xA: write_irq_counter(8, data); break;
|
||||
case 0xB: write_irq_counter(12, data); break;
|
||||
case 0xC:
|
||||
irq_pending = false;
|
||||
irq_awk = data & 1;
|
||||
irq_enable = data & 2;
|
||||
|
||||
if ( irq_enable )
|
||||
irq_counter = irq_latch;
|
||||
if (irq_enable)
|
||||
irq_counter = irq_latch;
|
||||
|
||||
break;
|
||||
case 0xD:
|
||||
irq_pending = false;
|
||||
irq_enable = irq_awk;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0xD:
|
||||
irq_pending = false;
|
||||
irq_enable = irq_awk;
|
||||
break;
|
||||
}
|
||||
|
||||
irq_changed();
|
||||
}
|
||||
irq_changed();
|
||||
}
|
||||
};
|
||||
|
||||
// void register_vrc3_mapper();
|
||||
|
|
|
@ -21,89 +21,88 @@
|
|||
|
||||
struct vrc1_state_t
|
||||
{
|
||||
uint8_t prg_banks [ 3 ];
|
||||
uint8_t chr_banks [ 2 ];
|
||||
uint8_t chr_banks_hi [ 2 ];
|
||||
uint8_t mirroring;
|
||||
uint8_t prg_banks[3];
|
||||
uint8_t chr_banks[2];
|
||||
uint8_t chr_banks_hi[2];
|
||||
uint8_t mirroring;
|
||||
};
|
||||
static_assert( sizeof ( vrc1_state_t ) == 8 );
|
||||
static_assert(sizeof(vrc1_state_t) == 8);
|
||||
|
||||
// VRC1
|
||||
|
||||
class Mapper075 : public Nes_Mapper, vrc1_state_t {
|
||||
public:
|
||||
Mapper075()
|
||||
{
|
||||
vrc1_state_t * state = this;
|
||||
register_state( state, sizeof * state );
|
||||
}
|
||||
class Mapper075 : public Nes_Mapper, vrc1_state_t
|
||||
{
|
||||
public:
|
||||
Mapper075()
|
||||
{
|
||||
vrc1_state_t *state = this;
|
||||
register_state(state, sizeof *state);
|
||||
}
|
||||
|
||||
void reset_state()
|
||||
{
|
||||
}
|
||||
void reset_state()
|
||||
{
|
||||
}
|
||||
|
||||
void apply_mapping()
|
||||
{
|
||||
update_prg_banks();
|
||||
update_chr_banks();
|
||||
update_mirroring();
|
||||
}
|
||||
void apply_mapping()
|
||||
{
|
||||
update_prg_banks();
|
||||
update_chr_banks();
|
||||
update_mirroring();
|
||||
}
|
||||
|
||||
void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
switch ( addr & 0xF000 )
|
||||
{
|
||||
case 0x8000:
|
||||
prg_banks [ 0 ] = data & 0xF;
|
||||
update_prg_banks();
|
||||
break;
|
||||
case 0x9000:
|
||||
mirroring = data & 1;
|
||||
chr_banks_hi [ 0 ] = ( data & 2 ) << 3;
|
||||
chr_banks_hi [ 1 ] = ( data & 4 ) << 2;
|
||||
update_chr_banks();
|
||||
update_mirroring();
|
||||
break;
|
||||
case 0xa000:
|
||||
prg_banks [ 1 ] = data & 0xF;
|
||||
update_prg_banks();
|
||||
break;
|
||||
case 0xc000:
|
||||
prg_banks [ 2 ] = data & 0xF;
|
||||
update_prg_banks();
|
||||
break;
|
||||
case 0xe000:
|
||||
chr_banks [ 0 ] = data & 0xF;
|
||||
update_chr_banks();
|
||||
break;
|
||||
case 0xf000:
|
||||
chr_banks [ 1 ] = data & 0xF;
|
||||
update_chr_banks();
|
||||
break;
|
||||
}
|
||||
}
|
||||
void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
switch (addr & 0xF000)
|
||||
{
|
||||
case 0x8000:
|
||||
prg_banks[0] = data & 0xF;
|
||||
update_prg_banks();
|
||||
break;
|
||||
case 0x9000:
|
||||
mirroring = data & 1;
|
||||
chr_banks_hi[0] = (data & 2) << 3;
|
||||
chr_banks_hi[1] = (data & 4) << 2;
|
||||
update_chr_banks();
|
||||
update_mirroring();
|
||||
break;
|
||||
case 0xa000:
|
||||
prg_banks[1] = data & 0xF;
|
||||
update_prg_banks();
|
||||
break;
|
||||
case 0xc000:
|
||||
prg_banks[2] = data & 0xF;
|
||||
update_prg_banks();
|
||||
break;
|
||||
case 0xe000:
|
||||
chr_banks[0] = data & 0xF;
|
||||
update_chr_banks();
|
||||
break;
|
||||
case 0xf000:
|
||||
chr_banks[1] = data & 0xF;
|
||||
update_chr_banks();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void update_prg_banks()
|
||||
{
|
||||
set_prg_bank( 0x8000, bank_8k, prg_banks [ 0 ] );
|
||||
set_prg_bank( 0xa000, bank_8k, prg_banks [ 1 ] );
|
||||
set_prg_bank( 0xc000, bank_8k, prg_banks [ 2 ] );
|
||||
}
|
||||
void update_prg_banks()
|
||||
{
|
||||
set_prg_bank(0x8000, bank_8k, prg_banks[0]);
|
||||
set_prg_bank(0xa000, bank_8k, prg_banks[1]);
|
||||
set_prg_bank(0xc000, bank_8k, prg_banks[2]);
|
||||
}
|
||||
|
||||
void update_chr_banks()
|
||||
{
|
||||
set_chr_bank( 0x0000, bank_4k, chr_banks [ 0 ] | chr_banks_hi [ 0 ] );
|
||||
set_chr_bank( 0x1000, bank_4k, chr_banks [ 1 ] | chr_banks_hi [ 1 ] );
|
||||
}
|
||||
void update_chr_banks()
|
||||
{
|
||||
set_chr_bank(0x0000, bank_4k, chr_banks[0] | chr_banks_hi[0]);
|
||||
set_chr_bank(0x1000, bank_4k, chr_banks[1] | chr_banks_hi[1]);
|
||||
}
|
||||
|
||||
void update_mirroring()
|
||||
{
|
||||
switch ( mirroring & 1 )
|
||||
{
|
||||
case 1: mirror_horiz(); break;
|
||||
case 0: mirror_vert(); break;
|
||||
}
|
||||
}
|
||||
void update_mirroring()
|
||||
{
|
||||
switch (mirroring & 1)
|
||||
{
|
||||
case 1: mirror_horiz(); break;
|
||||
case 0: mirror_vert(); break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -4,71 +4,72 @@
|
|||
|
||||
// Holy Diver and Uchuusen - Cosmo Carrier.
|
||||
|
||||
class Mapper078 : public Nes_Mapper {
|
||||
// lower 8 bits are the reg at 8000:ffff
|
||||
// next two bits are autodetecting type
|
||||
// 0 = unknown 1 = cosmo carrier 2 = holy diver
|
||||
int reg;
|
||||
void writeinternal(int data, int changed)
|
||||
{
|
||||
reg &= 0x300;
|
||||
reg |= data;
|
||||
class Mapper078 : public Nes_Mapper
|
||||
{
|
||||
// lower 8 bits are the reg at 8000:ffff
|
||||
// next two bits are autodetecting type
|
||||
// 0 = unknown 1 = cosmo carrier 2 = holy diver
|
||||
int reg;
|
||||
void writeinternal(int data, int changed)
|
||||
{
|
||||
reg &= 0x300;
|
||||
reg |= data;
|
||||
|
||||
if (changed & 0x07)
|
||||
set_prg_bank(0x8000, bank_16k, reg & 0x07);
|
||||
if (changed & 0xf0)
|
||||
set_chr_bank(0x0000, bank_8k, (reg >> 4) & 0x0f);
|
||||
if (changed & 0x08)
|
||||
{
|
||||
// set mirroring based on memorized board type
|
||||
if (reg & 0x100)
|
||||
{
|
||||
mirror_single((reg >> 3) & 1);
|
||||
}
|
||||
else if (reg & 0x200)
|
||||
{
|
||||
if (reg & 0x08)
|
||||
mirror_vert();
|
||||
else
|
||||
mirror_horiz();
|
||||
}
|
||||
else
|
||||
{
|
||||
// if you don't set something here, holy diver dumps with 4sc set will
|
||||
// savestate as 4k NTRAM. then when you later set H\V mapping, state size mismatch.
|
||||
mirror_single(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (changed & 0x07)
|
||||
set_prg_bank(0x8000, bank_16k, reg & 0x07);
|
||||
if (changed & 0xf0)
|
||||
set_chr_bank(0x0000, bank_8k, (reg >> 4) & 0x0f);
|
||||
if (changed & 0x08)
|
||||
{
|
||||
// set mirroring based on memorized board type
|
||||
if (reg & 0x100)
|
||||
{
|
||||
mirror_single((reg >> 3) & 1);
|
||||
}
|
||||
else if (reg & 0x200)
|
||||
{
|
||||
if (reg & 0x08)
|
||||
mirror_vert();
|
||||
else
|
||||
mirror_horiz();
|
||||
}
|
||||
else
|
||||
{
|
||||
// if you don't set something here, holy diver dumps with 4sc set will
|
||||
// savestate as 4k NTRAM. then when you later set H\V mapping, state size mismatch.
|
||||
mirror_single(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
Mapper078()
|
||||
{
|
||||
register_state(®, 4);
|
||||
}
|
||||
public:
|
||||
Mapper078()
|
||||
{
|
||||
register_state(®, 4);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{
|
||||
reg = 0;
|
||||
}
|
||||
virtual void reset_state()
|
||||
{
|
||||
reg = 0;
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
writeinternal(reg, 0xff);
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
writeinternal(reg, 0xff);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
// heuristic: if the first write ever to the register is 0,
|
||||
// we're on holy diver, otherwise, carrier. it works for these two games...
|
||||
if (!(reg & 0x300))
|
||||
{
|
||||
reg |= data ? 0x100 : 0x200;
|
||||
writeinternal(data, 0xff);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeinternal(data, reg ^ data);
|
||||
}
|
||||
}
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
// heuristic: if the first write ever to the register is 0,
|
||||
// we're on holy diver, otherwise, carrier. it works for these two games...
|
||||
if (!(reg & 0x300))
|
||||
{
|
||||
reg |= data ? 0x100 : 0x200;
|
||||
writeinternal(data, 0xff);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeinternal(data, reg ^ data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -27,64 +27,67 @@
|
|||
|
||||
#include "mappers/mapper.hpp"
|
||||
|
||||
template < bool multicart >
|
||||
class Mapper_AveNina : public Nes_Mapper {
|
||||
public:
|
||||
Mapper_AveNina()
|
||||
{
|
||||
register_state( ®s, 1 );
|
||||
}
|
||||
template <bool multicart>
|
||||
class Mapper_AveNina : public Nes_Mapper
|
||||
{
|
||||
public:
|
||||
Mapper_AveNina()
|
||||
{
|
||||
register_state(®s, 1);
|
||||
}
|
||||
|
||||
void write_regs();
|
||||
void write_regs();
|
||||
|
||||
virtual void reset_state()
|
||||
{
|
||||
intercept_writes( 0x4000, 0x1000 );
|
||||
intercept_writes( 0x5000, 0x1000 );
|
||||
}
|
||||
virtual void reset_state()
|
||||
{
|
||||
intercept_writes(0x4000, 0x1000);
|
||||
intercept_writes(0x5000, 0x1000);
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
write_intercepted( 0, 0x4100, regs );
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
write_intercepted(0, 0x4100, regs);
|
||||
}
|
||||
|
||||
virtual bool write_intercepted( nes_time_t, nes_addr_t addr , int data )
|
||||
{
|
||||
if ( addr < 0x4100 || addr > 0x5FFF )
|
||||
return false;
|
||||
virtual bool write_intercepted(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
if (addr < 0x4100 || addr > 0x5FFF)
|
||||
return false;
|
||||
|
||||
if ( addr & 0x100 )
|
||||
regs = data;
|
||||
if (addr & 0x100)
|
||||
regs = data;
|
||||
|
||||
write_regs();
|
||||
return true;
|
||||
}
|
||||
write_regs();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
if ( multicart == 0 &&
|
||||
( ( addr == 0x8000 ) || ( addr & 0xFCB0 ) == 0xFCB0 ) )
|
||||
set_chr_bank( 0, bank_8k, data & 0x07 );
|
||||
}
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
if (multicart == 0 &&
|
||||
((addr == 0x8000) || (addr & 0xFCB0) == 0xFCB0))
|
||||
set_chr_bank(0, bank_8k, data & 0x07);
|
||||
}
|
||||
|
||||
uint8_t regs;
|
||||
uint8_t regs;
|
||||
};
|
||||
|
||||
template < bool multicart >
|
||||
void Mapper_AveNina< multicart >::write_regs()
|
||||
template <bool multicart>
|
||||
void Mapper_AveNina<multicart>::write_regs()
|
||||
{
|
||||
if ( multicart == 0 )
|
||||
{
|
||||
set_prg_bank ( 0x8000, bank_32k, ( regs >> 3 ) & 0x01 );
|
||||
set_chr_bank ( 0, bank_8k, regs & 0x07 );
|
||||
}
|
||||
else
|
||||
{
|
||||
set_prg_bank ( 0x8000, bank_32k, ( regs >> 3 ) & 0x07 );
|
||||
set_chr_bank ( 0x0000, bank_8k, ( ( regs >> 3 ) & 0x08 ) | ( regs & 0x07 ) );
|
||||
if ( regs & 0x80 ) mirror_vert();
|
||||
else mirror_horiz();
|
||||
}
|
||||
if (multicart == 0)
|
||||
{
|
||||
set_prg_bank(0x8000, bank_32k, (regs >> 3) & 0x01);
|
||||
set_chr_bank(0, bank_8k, regs & 0x07);
|
||||
}
|
||||
else
|
||||
{
|
||||
set_prg_bank(0x8000, bank_32k, (regs >> 3) & 0x07);
|
||||
set_chr_bank(0x0000, bank_8k, ((regs >> 3) & 0x08) | (regs & 0x07));
|
||||
if (regs & 0x80)
|
||||
mirror_vert();
|
||||
else
|
||||
mirror_horiz();
|
||||
}
|
||||
}
|
||||
|
||||
typedef Mapper_AveNina<false> Mapper079;
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
// Nes_Emu 0.5.4. http://www.slack.net/~ant/
|
||||
|
||||
#include <cstring>
|
||||
#include "mappers/mapper.hpp"
|
||||
#include "apu/vrc7/apu.hpp"
|
||||
#include "mappers/mapper.hpp"
|
||||
#include <cstring>
|
||||
|
||||
/* Copyright (C) 2004-2005 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
|
@ -19,199 +19,205 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
struct vrc7_state_t
|
||||
{
|
||||
// written registers
|
||||
uint8_t mirroring;
|
||||
uint8_t prg_banks [3];
|
||||
uint8_t chr_banks [8];
|
||||
uint8_t irq_reload;
|
||||
uint8_t irq_mode;
|
||||
// written registers
|
||||
uint8_t mirroring;
|
||||
uint8_t prg_banks[3];
|
||||
uint8_t chr_banks[8];
|
||||
uint8_t irq_reload;
|
||||
uint8_t irq_mode;
|
||||
|
||||
// internal state
|
||||
uint16_t next_time;
|
||||
uint8_t irq_pending;
|
||||
uint8_t unused;
|
||||
// internal state
|
||||
uint16_t next_time;
|
||||
uint8_t irq_pending;
|
||||
uint8_t unused;
|
||||
|
||||
vrc7_snapshot_t sound_state;
|
||||
vrc7_snapshot_t sound_state;
|
||||
};
|
||||
static_assert( sizeof (vrc7_state_t) == 20 + sizeof (vrc7_snapshot_t) );
|
||||
static_assert(sizeof(vrc7_state_t) == 20 + sizeof(vrc7_snapshot_t));
|
||||
|
||||
// Vrc7
|
||||
|
||||
class Mapper085 : public Nes_Mapper, vrc7_state_t {
|
||||
public:
|
||||
Mapper085()
|
||||
{
|
||||
vrc7_state_t* state = this;
|
||||
register_state( state, sizeof *state );
|
||||
}
|
||||
class Mapper085 : public Nes_Mapper, vrc7_state_t
|
||||
{
|
||||
public:
|
||||
Mapper085()
|
||||
{
|
||||
vrc7_state_t *state = this;
|
||||
register_state(state, sizeof *state);
|
||||
}
|
||||
|
||||
virtual int channel_count() const { return sound.osc_count; }
|
||||
virtual int channel_count() const { return sound.osc_count; }
|
||||
|
||||
virtual void set_channel_buf( int i, Blip_Buffer* b ) { sound.osc_output( i, b ); }
|
||||
virtual void set_channel_buf(int i, Blip_Buffer *b) { sound.osc_output(i, b); }
|
||||
|
||||
virtual void set_treble( blip_eq_t const& eq ) { sound.treble_eq( eq ); }
|
||||
virtual void set_treble(blip_eq_t const &eq) { sound.treble_eq(eq); }
|
||||
|
||||
virtual void save_state( mapper_state_t & out )
|
||||
{
|
||||
sound.save_snapshot( &sound_state );
|
||||
Nes_Mapper::save_state( out );
|
||||
}
|
||||
virtual void save_state(mapper_state_t &out)
|
||||
{
|
||||
sound.save_snapshot(&sound_state);
|
||||
Nes_Mapper::save_state(out);
|
||||
}
|
||||
|
||||
virtual void load_state( mapper_state_t const& in )
|
||||
{
|
||||
Nes_Mapper::load_state( in );
|
||||
sound.load_snapshot( sound_state, in.size );
|
||||
}
|
||||
virtual void load_state(mapper_state_t const &in)
|
||||
{
|
||||
Nes_Mapper::load_state(in);
|
||||
sound.load_snapshot(sound_state, in.size);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{
|
||||
mirroring = 0;
|
||||
virtual void reset_state()
|
||||
{
|
||||
mirroring = 0;
|
||||
|
||||
memset( prg_banks, 0, sizeof prg_banks );
|
||||
memset( chr_banks, 0, sizeof chr_banks );
|
||||
memset( &sound_state, 0, sizeof sound_state );
|
||||
memset(prg_banks, 0, sizeof prg_banks);
|
||||
memset(chr_banks, 0, sizeof chr_banks);
|
||||
memset(&sound_state, 0, sizeof sound_state);
|
||||
|
||||
irq_reload = 0;
|
||||
irq_mode = 0;
|
||||
irq_pending = false;
|
||||
irq_reload = 0;
|
||||
irq_mode = 0;
|
||||
irq_pending = false;
|
||||
|
||||
next_time = 0;
|
||||
sound.reset();
|
||||
next_time = 0;
|
||||
sound.reset();
|
||||
|
||||
set_prg_bank( 0xE000, bank_8k, last_bank );
|
||||
apply_mapping();
|
||||
}
|
||||
set_prg_bank(0xE000, bank_8k, last_bank);
|
||||
apply_mapping();
|
||||
}
|
||||
|
||||
void write_prg_bank( int bank, int data )
|
||||
{
|
||||
prg_banks [bank] = data;
|
||||
set_prg_bank( 0x8000 | ( bank << bank_8k ), bank_8k, data );
|
||||
}
|
||||
void write_prg_bank(int bank, int data)
|
||||
{
|
||||
prg_banks[bank] = data;
|
||||
set_prg_bank(0x8000 | (bank << bank_8k), bank_8k, data);
|
||||
}
|
||||
|
||||
void write_chr_bank( int bank, int data )
|
||||
{
|
||||
//dprintf( "change chr bank %d\n", bank );
|
||||
chr_banks [bank] = data;
|
||||
set_chr_bank( bank * 0x400, bank_1k, data );
|
||||
}
|
||||
void write_chr_bank(int bank, int data)
|
||||
{
|
||||
// dprintf( "change chr bank %d\n", bank );
|
||||
chr_banks[bank] = data;
|
||||
set_chr_bank(bank * 0x400, bank_1k, data);
|
||||
}
|
||||
|
||||
void write_mirroring( int data )
|
||||
{
|
||||
mirroring = data;
|
||||
void write_mirroring(int data)
|
||||
{
|
||||
mirroring = data;
|
||||
|
||||
//dprintf( "Change mirroring %d\n", data );
|
||||
enable_sram( data & 128, data & 64 );
|
||||
// dprintf( "Change mirroring %d\n", data );
|
||||
enable_sram(data & 128, data & 64);
|
||||
|
||||
if ( data & 2 )
|
||||
mirror_single( data & 1 );
|
||||
else if ( data & 1 )
|
||||
mirror_horiz();
|
||||
else
|
||||
mirror_vert();
|
||||
}
|
||||
if (data & 2)
|
||||
mirror_single(data & 1);
|
||||
else if (data & 1)
|
||||
mirror_horiz();
|
||||
else
|
||||
mirror_vert();
|
||||
}
|
||||
|
||||
void apply_mapping()
|
||||
{
|
||||
size_t i;
|
||||
void apply_mapping()
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for ( i = 0; i < sizeof prg_banks; i++ )
|
||||
write_prg_bank( i, prg_banks [i] );
|
||||
for (i = 0; i < sizeof prg_banks; i++)
|
||||
write_prg_bank(i, prg_banks[i]);
|
||||
|
||||
for ( i = 0; i < sizeof chr_banks; i++ )
|
||||
write_chr_bank( i, chr_banks [i] );
|
||||
for (i = 0; i < sizeof chr_banks; i++)
|
||||
write_chr_bank(i, chr_banks[i]);
|
||||
|
||||
write_mirroring( mirroring );
|
||||
}
|
||||
write_mirroring(mirroring);
|
||||
}
|
||||
|
||||
void reset_timer( nes_time_t present )
|
||||
{
|
||||
next_time = present + unsigned ((0x100 - irq_reload) * timer_period) / 4;
|
||||
}
|
||||
void reset_timer(nes_time_t present)
|
||||
{
|
||||
next_time = present + unsigned((0x100 - irq_reload) * timer_period) / 4;
|
||||
}
|
||||
|
||||
virtual void run_until( nes_time_t end_time )
|
||||
{
|
||||
if ( irq_mode & 2 )
|
||||
{
|
||||
while ( next_time < end_time )
|
||||
{
|
||||
//dprintf( "%d timer expired\n", next_time );
|
||||
irq_pending = true;
|
||||
reset_timer( next_time );
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void run_until(nes_time_t end_time)
|
||||
{
|
||||
if (irq_mode & 2)
|
||||
{
|
||||
while (next_time < end_time)
|
||||
{
|
||||
// dprintf( "%d timer expired\n", next_time );
|
||||
irq_pending = true;
|
||||
reset_timer(next_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void end_frame( nes_time_t end_time )
|
||||
{
|
||||
run_until( end_time );
|
||||
virtual void end_frame(nes_time_t end_time)
|
||||
{
|
||||
run_until(end_time);
|
||||
|
||||
next_time -= end_time;
|
||||
next_time -= end_time;
|
||||
|
||||
sound.end_frame( end_time );
|
||||
}
|
||||
sound.end_frame(end_time);
|
||||
}
|
||||
|
||||
virtual nes_time_t next_irq( nes_time_t present )
|
||||
{
|
||||
if ( irq_pending )
|
||||
return present;
|
||||
virtual nes_time_t next_irq(nes_time_t present)
|
||||
{
|
||||
if (irq_pending)
|
||||
return present;
|
||||
|
||||
if ( irq_mode & 2 )
|
||||
return next_time + 1;
|
||||
if (irq_mode & 2)
|
||||
return next_time + 1;
|
||||
|
||||
return no_irq;
|
||||
}
|
||||
return no_irq;
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t time, nes_addr_t addr, int data )
|
||||
{
|
||||
addr |= ( addr & 8 ) << 1;
|
||||
virtual void write(nes_time_t time, nes_addr_t addr, int data)
|
||||
{
|
||||
addr |= (addr & 8) << 1;
|
||||
|
||||
if ( addr >= 0xe010 )
|
||||
{
|
||||
// IRQ
|
||||
run_until( time );
|
||||
//dprintf( "%d VRC6 IRQ [%d] = %02X\n", time, addr & 3, data );
|
||||
switch ( addr & 0xf010 )
|
||||
{
|
||||
case 0xe010:
|
||||
irq_reload = data;
|
||||
break;
|
||||
if (addr >= 0xe010)
|
||||
{
|
||||
// IRQ
|
||||
run_until(time);
|
||||
// dprintf( "%d VRC6 IRQ [%d] = %02X\n", time, addr & 3, data );
|
||||
switch (addr & 0xf010)
|
||||
{
|
||||
case 0xe010:
|
||||
irq_reload = data;
|
||||
break;
|
||||
|
||||
case 0xf000:
|
||||
irq_pending = false;
|
||||
irq_mode = data;
|
||||
if ( data & 2 )
|
||||
reset_timer( time );
|
||||
break;
|
||||
case 0xf000:
|
||||
irq_pending = false;
|
||||
irq_mode = data;
|
||||
if (data & 2)
|
||||
reset_timer(time);
|
||||
break;
|
||||
|
||||
case 0xf010:
|
||||
irq_pending = false;
|
||||
irq_mode = (irq_mode & ~2) | ((irq_mode << 1) & 2);
|
||||
break;
|
||||
}
|
||||
irq_changed();
|
||||
}
|
||||
else if ( ( unsigned ) ( addr - 0xa000 ) < 0x4000 )
|
||||
{
|
||||
write_chr_bank( ((addr >> 4) & 1) | (((addr - 0xa000) >> 11)&~1), data );
|
||||
}
|
||||
else switch ( addr & 0xf010 )
|
||||
{
|
||||
case 0x8000: write_prg_bank( 0, data ); break;
|
||||
case 0x8010: write_prg_bank( 1, data ); break;
|
||||
case 0x9000: write_prg_bank( 2, data ); break;
|
||||
case 0xf010:
|
||||
irq_pending = false;
|
||||
irq_mode = (irq_mode & ~2) | ((irq_mode << 1) & 2);
|
||||
break;
|
||||
}
|
||||
irq_changed();
|
||||
}
|
||||
else if ((unsigned)(addr - 0xa000) < 0x4000)
|
||||
{
|
||||
write_chr_bank(((addr >> 4) & 1) | (((addr - 0xa000) >> 11) & ~1), data);
|
||||
}
|
||||
else
|
||||
switch (addr & 0xf010)
|
||||
{
|
||||
case 0x8000: write_prg_bank(0, data); break;
|
||||
case 0x8010: write_prg_bank(1, data); break;
|
||||
case 0x9000: write_prg_bank(2, data); break;
|
||||
|
||||
case 0xe000:
|
||||
write_mirroring( data );
|
||||
break;
|
||||
case 0xe000:
|
||||
write_mirroring(data);
|
||||
break;
|
||||
|
||||
case 0x9010:
|
||||
if ( addr & 0x20 ) sound.write_data( time, data );
|
||||
else sound.write_reg( data );
|
||||
break;
|
||||
}
|
||||
}
|
||||
case 0x9010:
|
||||
if (addr & 0x20)
|
||||
sound.write_data(time, data);
|
||||
else
|
||||
sound.write_reg(data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Nes_Vrc7 sound;
|
||||
enum { timer_period = 113 * 4 + 3 };
|
||||
Nes_Vrc7 sound;
|
||||
enum
|
||||
{
|
||||
timer_period = 113 * 4 + 3
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -18,30 +18,31 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
// Jaleco/Konami/Taito
|
||||
|
||||
class Mapper087 : public Nes_Mapper {
|
||||
uint8_t bank;
|
||||
public:
|
||||
Mapper087()
|
||||
{
|
||||
register_state( &bank, 1 );
|
||||
}
|
||||
class Mapper087 : public Nes_Mapper
|
||||
{
|
||||
uint8_t bank;
|
||||
|
||||
void apply_mapping()
|
||||
{
|
||||
intercept_writes( 0x6000, 1 );
|
||||
write( 0, 0x6000, bank );
|
||||
}
|
||||
public:
|
||||
Mapper087()
|
||||
{
|
||||
register_state(&bank, 1);
|
||||
}
|
||||
|
||||
bool write_intercepted( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
if ( addr != 0x6000 )
|
||||
return false;
|
||||
void apply_mapping()
|
||||
{
|
||||
intercept_writes(0x6000, 1);
|
||||
write(0, 0x6000, bank);
|
||||
}
|
||||
|
||||
bank = data;
|
||||
set_chr_bank( 0, bank_8k, data >> 1 );
|
||||
return true;
|
||||
}
|
||||
bool write_intercepted(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
if (addr != 0x6000)
|
||||
return false;
|
||||
|
||||
void write( nes_time_t, nes_addr_t, int ) { }
|
||||
bank = data;
|
||||
set_chr_bank(0, bank_8k, data >> 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
void write(nes_time_t, nes_addr_t, int) {}
|
||||
};
|
||||
|
||||
|
|
|
@ -29,79 +29,85 @@
|
|||
|
||||
struct namco_34x3_state_t
|
||||
{
|
||||
uint8_t bank [ 8 ];
|
||||
uint8_t mirr;
|
||||
uint8_t mode;
|
||||
uint8_t bank[8];
|
||||
uint8_t mirr;
|
||||
uint8_t mode;
|
||||
};
|
||||
static_assert( sizeof (namco_34x3_state_t) == 10 );
|
||||
static_assert(sizeof(namco_34x3_state_t) == 10);
|
||||
|
||||
template < bool _is154 >
|
||||
class Mapper_Namco_34x3 : public Nes_Mapper, namco_34x3_state_t {
|
||||
public:
|
||||
Mapper_Namco_34x3()
|
||||
{
|
||||
namco_34x3_state_t *state = this;
|
||||
register_state( state, sizeof *state );
|
||||
}
|
||||
template <bool _is154>
|
||||
class Mapper_Namco_34x3 : public Nes_Mapper, namco_34x3_state_t
|
||||
{
|
||||
public:
|
||||
Mapper_Namco_34x3()
|
||||
{
|
||||
namco_34x3_state_t *state = this;
|
||||
register_state(state, sizeof *state);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{ }
|
||||
virtual void reset_state()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
set_chr_bank( 0x0000, bank_2k, bank [ 0 ] );
|
||||
set_chr_bank( 0x0800, bank_2k, bank [ 1 ] );
|
||||
for ( int i = 0; i < 4; i++ )
|
||||
set_chr_bank( 0x1000 + ( i << 10 ), bank_1k, bank [ i + 2 ] );
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
set_chr_bank(0x0000, bank_2k, bank[0]);
|
||||
set_chr_bank(0x0800, bank_2k, bank[1]);
|
||||
for (int i = 0; i < 4; i++)
|
||||
set_chr_bank(0x1000 + (i << 10), bank_1k, bank[i + 2]);
|
||||
|
||||
set_prg_bank( 0x8000, bank_8k, bank [ 6 ] );
|
||||
set_prg_bank( 0xA000, bank_8k, bank [ 7 ] );
|
||||
set_prg_bank( 0xC000, bank_8k, ~1 );
|
||||
set_prg_bank( 0xE000, bank_8k, ~0 );
|
||||
set_prg_bank(0x8000, bank_8k, bank[6]);
|
||||
set_prg_bank(0xA000, bank_8k, bank[7]);
|
||||
set_prg_bank(0xC000, bank_8k, ~1);
|
||||
set_prg_bank(0xE000, bank_8k, ~0);
|
||||
|
||||
if ( _is154 )
|
||||
mirror_single( mirr );
|
||||
}
|
||||
if (_is154)
|
||||
mirror_single(mirr);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
switch ( addr & 0xE001 )
|
||||
{
|
||||
case 0x8000:
|
||||
mode = data;
|
||||
mirr = ( data >> 6 ) & 0x01;
|
||||
if ( _is154 )
|
||||
mirror_single( mirr );
|
||||
break;
|
||||
case 0x8001:
|
||||
mode &= 0x07;
|
||||
switch ( mode )
|
||||
{
|
||||
case 0: case 1:
|
||||
bank [ mode ] = data >> 1;
|
||||
set_chr_bank( 0x0000 + ( mode << 11 ), bank_2k, bank [ mode ] );
|
||||
break;
|
||||
case 2: case 3: case 4: case 5:
|
||||
bank [ mode ] = data | 0x40;
|
||||
set_chr_bank( 0x1000 + ( ( mode - 2 ) << 10 ), bank_1k, bank [ mode ] );
|
||||
break;
|
||||
case 6: case 7:
|
||||
bank [ mode ] = data;
|
||||
set_prg_bank( 0x8000 + ( ( mode - 6 ) << 13 ), bank_8k, bank [ mode ] );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0xC000:
|
||||
mirr = ( data >> 6 ) & 0x01;
|
||||
if ( _is154 )
|
||||
mirror_single( mirr );
|
||||
}
|
||||
}
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
switch (addr & 0xE001)
|
||||
{
|
||||
case 0x8000:
|
||||
mode = data;
|
||||
mirr = (data >> 6) & 0x01;
|
||||
if (_is154)
|
||||
mirror_single(mirr);
|
||||
break;
|
||||
case 0x8001:
|
||||
mode &= 0x07;
|
||||
switch (mode)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
bank[mode] = data >> 1;
|
||||
set_chr_bank(0x0000 + (mode << 11), bank_2k, bank[mode]);
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
bank[mode] = data | 0x40;
|
||||
set_chr_bank(0x1000 + ((mode - 2) << 10), bank_1k, bank[mode]);
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
bank[mode] = data;
|
||||
set_prg_bank(0x8000 + ((mode - 6) << 13), bank_8k, bank[mode]);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0xC000:
|
||||
mirr = (data >> 6) & 0x01;
|
||||
if (_is154)
|
||||
mirror_single(mirr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef Mapper_Namco_34x3<false> Mapper088;
|
||||
|
||||
|
||||
// void register_mapper_namco_34xx();
|
||||
// void register_mapper_namco_34xx()
|
||||
// {
|
||||
|
|
|
@ -26,30 +26,32 @@
|
|||
|
||||
// Sunsoft2b
|
||||
|
||||
class Mapper089 : public Nes_Mapper {
|
||||
public:
|
||||
Mapper089()
|
||||
{
|
||||
register_state( ®s, 1 );
|
||||
}
|
||||
class Mapper089 : public Nes_Mapper
|
||||
{
|
||||
public:
|
||||
Mapper089()
|
||||
{
|
||||
register_state(®s, 1);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{}
|
||||
virtual void reset_state()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
set_prg_bank( 0xC000, bank_16k, last_bank );
|
||||
write( 0, 0x8000, regs );
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
set_prg_bank(0xC000, bank_16k, last_bank);
|
||||
write(0, 0x8000, regs);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
regs = handle_bus_conflict( addr, data );
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
regs = handle_bus_conflict(addr, data);
|
||||
|
||||
set_chr_bank( 0x0000, bank_8k, ( ( data >> 4 ) & 0x08 ) | ( data & 0x07 ) );
|
||||
set_prg_bank( 0x8000, bank_16k, ( data >> 4 ) & 0x07 );
|
||||
mirror_single( ( data >> 3 ) & 1 );
|
||||
}
|
||||
set_chr_bank(0x0000, bank_8k, ((data >> 4) & 0x08) | (data & 0x07));
|
||||
set_prg_bank(0x8000, bank_16k, (data >> 4) & 0x07);
|
||||
mirror_single((data >> 3) & 1);
|
||||
}
|
||||
|
||||
uint8_t regs;
|
||||
uint8_t regs;
|
||||
};
|
||||
|
|
|
@ -26,29 +26,31 @@
|
|||
|
||||
// Sunsoft2a
|
||||
|
||||
class Mapper093 : public Nes_Mapper {
|
||||
public:
|
||||
Mapper093()
|
||||
{
|
||||
register_state( ®s, 1 );
|
||||
}
|
||||
class Mapper093 : public Nes_Mapper
|
||||
{
|
||||
public:
|
||||
Mapper093()
|
||||
{
|
||||
register_state(®s, 1);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{}
|
||||
virtual void reset_state()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
set_prg_bank( 0xC000, bank_16k, last_bank );
|
||||
write( 0, 0x8000, regs );
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
set_prg_bank(0xC000, bank_16k, last_bank);
|
||||
write(0, 0x8000, regs);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
regs = handle_bus_conflict( addr, data );
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
regs = handle_bus_conflict(addr, data);
|
||||
|
||||
set_chr_bank( 0x0000, bank_8k, data & 0x0F );
|
||||
set_prg_bank( 0x8000, bank_16k, ( data >> 4 ) & 0x07 );
|
||||
}
|
||||
set_chr_bank(0x0000, bank_8k, data & 0x0F);
|
||||
set_prg_bank(0x8000, bank_16k, (data >> 4) & 0x07);
|
||||
}
|
||||
|
||||
uint8_t regs;
|
||||
uint8_t regs;
|
||||
};
|
||||
|
|
|
@ -28,26 +28,28 @@
|
|||
|
||||
// Un1rom
|
||||
|
||||
class Mapper094 : public Nes_Mapper {
|
||||
public:
|
||||
Mapper094()
|
||||
{
|
||||
register_state( &bank, 1 );
|
||||
}
|
||||
class Mapper094 : public Nes_Mapper
|
||||
{
|
||||
public:
|
||||
Mapper094()
|
||||
{
|
||||
register_state(&bank, 1);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{ }
|
||||
virtual void reset_state()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
write( 0, 0, bank );
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
write(0, 0, bank);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t, int data )
|
||||
{
|
||||
bank = data;
|
||||
set_prg_bank( 0x8000, bank_16k, bank >> 2 );
|
||||
}
|
||||
virtual void write(nes_time_t, nes_addr_t, int data)
|
||||
{
|
||||
bank = data;
|
||||
set_prg_bank(0x8000, bank_16k, bank >> 2);
|
||||
}
|
||||
|
||||
uint8_t bank;
|
||||
uint8_t bank;
|
||||
};
|
||||
|
|
|
@ -27,37 +27,38 @@
|
|||
|
||||
// Irem_Tam_S1
|
||||
|
||||
class Mapper097 : public Nes_Mapper {
|
||||
public:
|
||||
Mapper097()
|
||||
{
|
||||
register_state( &bank, 1 );
|
||||
}
|
||||
class Mapper097 : public Nes_Mapper
|
||||
{
|
||||
public:
|
||||
Mapper097()
|
||||
{
|
||||
register_state(&bank, 1);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{
|
||||
bank = ~0;
|
||||
}
|
||||
virtual void reset_state()
|
||||
{
|
||||
bank = ~0;
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
write( 0, 0, bank );
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
write(0, 0, bank);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t, int data )
|
||||
{
|
||||
bank = data;
|
||||
set_prg_bank( 0x8000, bank_16k, ~0 );
|
||||
set_prg_bank( 0xC000, bank_16k, bank & 0x0F );
|
||||
virtual void write(nes_time_t, nes_addr_t, int data)
|
||||
{
|
||||
bank = data;
|
||||
set_prg_bank(0x8000, bank_16k, ~0);
|
||||
set_prg_bank(0xC000, bank_16k, bank & 0x0F);
|
||||
|
||||
switch ( ( bank >> 6 ) & 0x03 )
|
||||
{
|
||||
case 1: mirror_horiz(); break;
|
||||
case 2: mirror_vert(); break;
|
||||
case 0:
|
||||
case 3: mirror_single( bank & 0x01 ); break;
|
||||
}
|
||||
}
|
||||
switch ((bank >> 6) & 0x03)
|
||||
{
|
||||
case 1: mirror_horiz(); break;
|
||||
case 2: mirror_vert(); break;
|
||||
case 0:
|
||||
case 3: mirror_single(bank & 0x01); break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t bank;
|
||||
uint8_t bank;
|
||||
};
|
||||
|
|
|
@ -28,36 +28,37 @@
|
|||
|
||||
// Jaleco_JF11
|
||||
|
||||
class Mapper140 : public Nes_Mapper {
|
||||
public:
|
||||
Mapper140()
|
||||
{
|
||||
register_state( ®s, 1 );
|
||||
}
|
||||
class Mapper140 : public Nes_Mapper
|
||||
{
|
||||
public:
|
||||
Mapper140()
|
||||
{
|
||||
register_state(®s, 1);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{
|
||||
intercept_writes( 0x6000, 1 );
|
||||
}
|
||||
virtual void reset_state()
|
||||
{
|
||||
intercept_writes(0x6000, 1);
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
write_intercepted(0, 0x6000, regs );
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
write_intercepted(0, 0x6000, regs);
|
||||
}
|
||||
|
||||
bool write_intercepted( nes_time_t time, nes_addr_t addr, int data )
|
||||
{
|
||||
if ( addr < 0x6000 || addr > 0x7FFF )
|
||||
return false;
|
||||
bool write_intercepted(nes_time_t time, nes_addr_t addr, int data)
|
||||
{
|
||||
if (addr < 0x6000 || addr > 0x7FFF)
|
||||
return false;
|
||||
|
||||
regs = data;
|
||||
set_prg_bank( 0x8000, bank_32k, data >> 4);
|
||||
set_chr_bank( 0, bank_8k, data );
|
||||
regs = data;
|
||||
set_prg_bank(0x8000, bank_32k, data >> 4);
|
||||
set_chr_bank(0, bank_8k, data);
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data ) { }
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data) {}
|
||||
|
||||
uint8_t regs;
|
||||
uint8_t regs;
|
||||
};
|
||||
|
|
|
@ -6,54 +6,55 @@
|
|||
|
||||
struct m156_state_t
|
||||
{
|
||||
uint8_t prg_bank;
|
||||
uint8_t chr_banks [8];
|
||||
uint8_t prg_bank;
|
||||
uint8_t chr_banks[8];
|
||||
};
|
||||
static_assert( sizeof (m156_state_t) == 9 );
|
||||
static_assert(sizeof(m156_state_t) == 9);
|
||||
|
||||
class Mapper156 : public Nes_Mapper, m156_state_t {
|
||||
public:
|
||||
Mapper156()
|
||||
{
|
||||
m156_state_t * state = this;
|
||||
register_state( state, sizeof * state );
|
||||
}
|
||||
class Mapper156 : public Nes_Mapper, m156_state_t
|
||||
{
|
||||
public:
|
||||
Mapper156()
|
||||
{
|
||||
m156_state_t *state = this;
|
||||
register_state(state, sizeof *state);
|
||||
}
|
||||
|
||||
void reset_state()
|
||||
{
|
||||
prg_bank = 0;
|
||||
for ( unsigned i = 0; i < 8; i++ ) chr_banks [i] = i;
|
||||
enable_sram();
|
||||
apply_mapping();
|
||||
}
|
||||
void reset_state()
|
||||
{
|
||||
prg_bank = 0;
|
||||
for (unsigned i = 0; i < 8; i++) chr_banks[i] = i;
|
||||
enable_sram();
|
||||
apply_mapping();
|
||||
}
|
||||
|
||||
void apply_mapping()
|
||||
{
|
||||
mirror_single( 0 );
|
||||
set_prg_bank( 0x8000, bank_16k, prg_bank );
|
||||
void apply_mapping()
|
||||
{
|
||||
mirror_single(0);
|
||||
set_prg_bank(0x8000, bank_16k, prg_bank);
|
||||
|
||||
for ( int i = 0; i < (int) sizeof chr_banks; i++ )
|
||||
set_chr_bank( i * 0x400, bank_1k, chr_banks [i] );
|
||||
}
|
||||
for (int i = 0; i < (int)sizeof chr_banks; i++)
|
||||
set_chr_bank(i * 0x400, bank_1k, chr_banks[i]);
|
||||
}
|
||||
|
||||
void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
unsigned int reg = addr - 0xC000;
|
||||
if ( addr == 0xC010 )
|
||||
{
|
||||
prg_bank = data;
|
||||
set_prg_bank( 0x8000, bank_16k, data );
|
||||
}
|
||||
else if ( reg < 4 )
|
||||
{
|
||||
chr_banks [reg] = data;
|
||||
set_chr_bank( reg * 0x400, bank_1k, data );
|
||||
}
|
||||
else if ( ( reg - 8 ) < 4 )
|
||||
{
|
||||
reg -= 4;
|
||||
chr_banks [reg] = data;
|
||||
set_chr_bank( reg * 0x400, bank_1k, data );
|
||||
}
|
||||
}
|
||||
void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
unsigned int reg = addr - 0xC000;
|
||||
if (addr == 0xC010)
|
||||
{
|
||||
prg_bank = data;
|
||||
set_prg_bank(0x8000, bank_16k, data);
|
||||
}
|
||||
else if (reg < 4)
|
||||
{
|
||||
chr_banks[reg] = data;
|
||||
set_chr_bank(reg * 0x400, bank_1k, data);
|
||||
}
|
||||
else if ((reg - 8) < 4)
|
||||
{
|
||||
reg -= 4;
|
||||
chr_banks[reg] = data;
|
||||
set_chr_bank(reg * 0x400, bank_1k, data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -27,27 +27,29 @@
|
|||
|
||||
// UxROM (inverted)
|
||||
|
||||
class Mapper180 : public Nes_Mapper {
|
||||
public:
|
||||
Mapper180()
|
||||
{
|
||||
register_state( &bank, 1 );
|
||||
}
|
||||
class Mapper180 : public Nes_Mapper
|
||||
{
|
||||
public:
|
||||
Mapper180()
|
||||
{
|
||||
register_state(&bank, 1);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{ }
|
||||
virtual void reset_state()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
set_prg_bank( 0x8000, bank_16k, 0 );
|
||||
write( 0, 0, bank );
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
set_prg_bank(0x8000, bank_16k, 0);
|
||||
write(0, 0, bank);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t, int data )
|
||||
{
|
||||
bank = data;
|
||||
set_prg_bank( 0xC000, bank_16k, data );
|
||||
}
|
||||
virtual void write(nes_time_t, nes_addr_t, int data)
|
||||
{
|
||||
bank = data;
|
||||
set_prg_bank(0xC000, bank_16k, data);
|
||||
}
|
||||
|
||||
uint8_t bank;
|
||||
uint8_t bank;
|
||||
};
|
||||
|
|
|
@ -26,38 +26,40 @@
|
|||
|
||||
// Sunsoft1
|
||||
|
||||
class Mapper184 : public Nes_Mapper {
|
||||
public:
|
||||
Mapper184()
|
||||
{
|
||||
register_state( ®s, 1 );
|
||||
}
|
||||
class Mapper184 : public Nes_Mapper
|
||||
{
|
||||
public:
|
||||
Mapper184()
|
||||
{
|
||||
register_state(®s, 1);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{}
|
||||
virtual void reset_state()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
set_prg_bank( 0x8000, bank_32k, 0 );
|
||||
intercept_writes( 0x6000, 1 );
|
||||
write_intercepted( 0, 0x6000, regs );
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
set_prg_bank(0x8000, bank_32k, 0);
|
||||
intercept_writes(0x6000, 1);
|
||||
write_intercepted(0, 0x6000, regs);
|
||||
}
|
||||
|
||||
virtual bool write_intercepted( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
if ( addr != 0x6000 )
|
||||
return false;
|
||||
virtual bool write_intercepted(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
if (addr != 0x6000)
|
||||
return false;
|
||||
|
||||
regs = data;
|
||||
set_chr_bank( 0x0000, bank_4k, data & 0x07 );
|
||||
set_chr_bank( 0x1000, bank_4k, ( data >> 4 ) & 0x07 );
|
||||
regs = data;
|
||||
set_chr_bank(0x0000, bank_4k, data & 0x07);
|
||||
set_chr_bank(0x1000, bank_4k, (data >> 4) & 0x07);
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{}
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t regs;
|
||||
uint8_t regs;
|
||||
};
|
||||
|
||||
|
|
|
@ -4,52 +4,52 @@
|
|||
|
||||
// Magic Kid Googoo
|
||||
|
||||
class Mapper190: public Nes_Mapper {
|
||||
public:
|
||||
Mapper190()
|
||||
{
|
||||
}
|
||||
class Mapper190 : public Nes_Mapper
|
||||
{
|
||||
public:
|
||||
Mapper190()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{
|
||||
}
|
||||
virtual void reset_state()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
mirror_vert();
|
||||
enable_sram();
|
||||
set_prg_bank( 0xc000, bank_16k, 0);
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
mirror_vert();
|
||||
enable_sram();
|
||||
set_prg_bank(0xc000, bank_16k, 0);
|
||||
}
|
||||
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
switch ( addr >> 12 )
|
||||
{
|
||||
case 0x8:
|
||||
case 0x9:
|
||||
case 0xc:
|
||||
case 0xd:
|
||||
set_prg_bank( 0x8000, bank_16k, ( ( ( addr >> 11 ) & 8 ) | ( data & 7 ) ) );
|
||||
break;
|
||||
case 0xa:
|
||||
case 0xb:
|
||||
switch ( addr & 3 )
|
||||
{
|
||||
case 0:
|
||||
set_chr_bank( 0x0000, bank_2k, data );
|
||||
break;
|
||||
case 1:
|
||||
set_chr_bank( 0x0800, bank_2k, data );
|
||||
break;
|
||||
case 2:
|
||||
set_chr_bank( 0x1000, bank_2k, data );
|
||||
break;
|
||||
case 3:
|
||||
set_chr_bank( 0x1800, bank_2k, data );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
switch (addr >> 12)
|
||||
{
|
||||
case 0x8:
|
||||
case 0x9:
|
||||
case 0xc:
|
||||
case 0xd:
|
||||
set_prg_bank(0x8000, bank_16k, (((addr >> 11) & 8) | (data & 7)));
|
||||
break;
|
||||
case 0xa:
|
||||
case 0xb:
|
||||
switch (addr & 3)
|
||||
{
|
||||
case 0:
|
||||
set_chr_bank(0x0000, bank_2k, data);
|
||||
break;
|
||||
case 1:
|
||||
set_chr_bank(0x0800, bank_2k, data);
|
||||
break;
|
||||
case 2:
|
||||
set_chr_bank(0x1000, bank_2k, data);
|
||||
break;
|
||||
case 3:
|
||||
set_chr_bank(0x1800, bank_2k, data);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -29,46 +29,48 @@
|
|||
|
||||
// NTDEC's TC-112 mapper IC.
|
||||
|
||||
class Mapper193 : public Nes_Mapper {
|
||||
public:
|
||||
Mapper193()
|
||||
{
|
||||
register_state( regs, sizeof regs );
|
||||
}
|
||||
class Mapper193 : public Nes_Mapper
|
||||
{
|
||||
public:
|
||||
Mapper193()
|
||||
{
|
||||
register_state(regs, sizeof regs);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{ }
|
||||
virtual void reset_state()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
for ( size_t i = 0; i < sizeof regs; i++ )
|
||||
write_intercepted( 0, 0x6000 + i, regs [ i ] );
|
||||
set_prg_bank( 0xA000, bank_8k, ~2 );
|
||||
set_prg_bank( 0xC000, bank_8k, ~1 );
|
||||
set_prg_bank( 0xE000, bank_8k, ~0 );
|
||||
intercept_writes( 0x6000, 0x03 );
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
for (size_t i = 0; i < sizeof regs; i++)
|
||||
write_intercepted(0, 0x6000 + i, regs[i]);
|
||||
set_prg_bank(0xA000, bank_8k, ~2);
|
||||
set_prg_bank(0xC000, bank_8k, ~1);
|
||||
set_prg_bank(0xE000, bank_8k, ~0);
|
||||
intercept_writes(0x6000, 0x03);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{ }
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool write_intercepted( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
if ( addr < 0x6000 || addr > 0x6003 )
|
||||
return false;
|
||||
virtual bool write_intercepted(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
if (addr < 0x6000 || addr > 0x6003)
|
||||
return false;
|
||||
|
||||
regs [ addr & 0x03 ] = data;
|
||||
switch ( addr & 0x03 )
|
||||
{
|
||||
case 0: set_chr_bank( 0x0000, bank_4k, regs [ 0 ] >> 2 ); break;
|
||||
case 1: set_chr_bank( 0x1000, bank_2k, regs [ 1 ] >> 1 ); break;
|
||||
case 2: set_chr_bank( 0x1800, bank_2k, regs [ 2 ] >> 1 ); break;
|
||||
case 3: set_prg_bank( 0x8000, bank_8k, regs [ 3 ] ); break;
|
||||
}
|
||||
regs[addr & 0x03] = data;
|
||||
switch (addr & 0x03)
|
||||
{
|
||||
case 0: set_chr_bank(0x0000, bank_4k, regs[0] >> 2); break;
|
||||
case 1: set_chr_bank(0x1000, bank_2k, regs[1] >> 1); break;
|
||||
case 2: set_chr_bank(0x1800, bank_2k, regs[2] >> 1); break;
|
||||
case 3: set_prg_bank(0x8000, bank_8k, regs[3]); break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t regs [ 4 ];
|
||||
uint8_t regs[4];
|
||||
};
|
||||
|
||||
|
|
|
@ -29,64 +29,70 @@
|
|||
|
||||
struct namco_34xx_state_t
|
||||
{
|
||||
uint8_t bank [ 8 ];
|
||||
uint8_t mirr;
|
||||
uint8_t mode;
|
||||
uint8_t bank[8];
|
||||
uint8_t mirr;
|
||||
uint8_t mode;
|
||||
};
|
||||
static_assert( sizeof (namco_34xx_state_t) == 10 );
|
||||
static_assert(sizeof(namco_34xx_state_t) == 10);
|
||||
|
||||
// Namco_34xx
|
||||
|
||||
class Mapper206 : public Nes_Mapper, namco_34xx_state_t {
|
||||
public:
|
||||
Mapper206()
|
||||
{
|
||||
namco_34xx_state_t *state = this;
|
||||
register_state( state, sizeof *state );
|
||||
}
|
||||
class Mapper206 : public Nes_Mapper, namco_34xx_state_t
|
||||
{
|
||||
public:
|
||||
Mapper206()
|
||||
{
|
||||
namco_34xx_state_t *state = this;
|
||||
register_state(state, sizeof *state);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{ }
|
||||
virtual void reset_state()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
set_chr_bank( 0x0000, bank_2k, bank [ 0 ] );
|
||||
set_chr_bank( 0x0800, bank_2k, bank [ 1 ] );
|
||||
for ( int i = 0; i < 4; i++ )
|
||||
set_chr_bank( 0x1000 + ( i << 10 ), bank_1k, bank [ i + 2 ] );
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
set_chr_bank(0x0000, bank_2k, bank[0]);
|
||||
set_chr_bank(0x0800, bank_2k, bank[1]);
|
||||
for (int i = 0; i < 4; i++)
|
||||
set_chr_bank(0x1000 + (i << 10), bank_1k, bank[i + 2]);
|
||||
|
||||
set_prg_bank( 0x8000, bank_8k, bank [ 6 ] );
|
||||
set_prg_bank( 0xA000, bank_8k, bank [ 7 ] );
|
||||
set_prg_bank( 0xC000, bank_8k, ~1 );
|
||||
set_prg_bank( 0xE000, bank_8k, ~0 );
|
||||
}
|
||||
set_prg_bank(0x8000, bank_8k, bank[6]);
|
||||
set_prg_bank(0xA000, bank_8k, bank[7]);
|
||||
set_prg_bank(0xC000, bank_8k, ~1);
|
||||
set_prg_bank(0xE000, bank_8k, ~0);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
switch ( addr & 0xE001 )
|
||||
{
|
||||
case 0x8000:
|
||||
mode = data;
|
||||
break;
|
||||
case 0x8001:
|
||||
mode &= 0x07;
|
||||
switch ( mode )
|
||||
{
|
||||
case 0: case 1:
|
||||
bank [ mode ] = data >> 1;
|
||||
set_chr_bank( 0x0000 + ( mode << 11 ), bank_2k, bank [ mode ] );
|
||||
break;
|
||||
case 2: case 3: case 4: case 5:
|
||||
bank [ mode ] = data;
|
||||
set_chr_bank( 0x1000 + ( ( mode - 2 ) << 10 ), bank_1k, bank [ mode ] );
|
||||
break;
|
||||
case 6: case 7:
|
||||
bank [ mode ] = data;
|
||||
set_prg_bank( 0x8000 + ( ( mode - 6 ) << 13 ), bank_8k, bank [ mode ] );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
switch (addr & 0xE001)
|
||||
{
|
||||
case 0x8000:
|
||||
mode = data;
|
||||
break;
|
||||
case 0x8001:
|
||||
mode &= 0x07;
|
||||
switch (mode)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
bank[mode] = data >> 1;
|
||||
set_chr_bank(0x0000 + (mode << 11), bank_2k, bank[mode]);
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
bank[mode] = data;
|
||||
set_chr_bank(0x1000 + ((mode - 2) << 10), bank_1k, bank[mode]);
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
bank[mode] = data;
|
||||
set_prg_bank(0x8000 + ((mode - 6) << 13), bank_8k, bank[mode]);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -27,64 +27,65 @@
|
|||
|
||||
struct taito_x1005_state_t
|
||||
{
|
||||
uint8_t preg [ 3 ];
|
||||
uint8_t creg [ 6 ];
|
||||
uint8_t nametable [ 2 ];
|
||||
uint8_t preg[3];
|
||||
uint8_t creg[6];
|
||||
uint8_t nametable[2];
|
||||
};
|
||||
static_assert( sizeof (taito_x1005_state_t) == 11 );
|
||||
static_assert(sizeof(taito_x1005_state_t) == 11);
|
||||
|
||||
// TaitoX1005
|
||||
|
||||
class Mapper207 : public Nes_Mapper, taito_x1005_state_t {
|
||||
public:
|
||||
Mapper207()
|
||||
{
|
||||
taito_x1005_state_t *state = this;
|
||||
register_state( state, sizeof *state );
|
||||
}
|
||||
class Mapper207 : public Nes_Mapper, taito_x1005_state_t
|
||||
{
|
||||
public:
|
||||
Mapper207()
|
||||
{
|
||||
taito_x1005_state_t *state = this;
|
||||
register_state(state, sizeof *state);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{ }
|
||||
virtual void reset_state()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
int i;
|
||||
intercept_writes( 0x7EF0, 1 );
|
||||
for ( i = 0; i < 3; i++ )
|
||||
set_prg_bank( 0x8000 + ( i << 13 ), bank_8k, preg [ i ] );
|
||||
for ( i = 0; i < 2; i++ )
|
||||
set_chr_bank( 0x0000 + ( i << 11 ), bank_2k, creg [ i ] >> 1);
|
||||
for ( i = 0; i < 4; i++ )
|
||||
set_chr_bank( 0x1000 + ( i << 10 ), bank_1k, creg [ 2 + i ] );
|
||||
mirror_manual( nametable [ 0 ], nametable [ 0 ], nametable [ 1 ], nametable [ 1 ] );
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
int i;
|
||||
intercept_writes(0x7EF0, 1);
|
||||
for (i = 0; i < 3; i++)
|
||||
set_prg_bank(0x8000 + (i << 13), bank_8k, preg[i]);
|
||||
for (i = 0; i < 2; i++)
|
||||
set_chr_bank(0x0000 + (i << 11), bank_2k, creg[i] >> 1);
|
||||
for (i = 0; i < 4; i++)
|
||||
set_chr_bank(0x1000 + (i << 10), bank_1k, creg[2 + i]);
|
||||
mirror_manual(nametable[0], nametable[0], nametable[1], nametable[1]);
|
||||
}
|
||||
|
||||
virtual bool write_intercepted( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
if ( addr < 0x7EF0 || addr > 0x7EFF )
|
||||
return false;
|
||||
virtual bool write_intercepted(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
if (addr < 0x7EF0 || addr > 0x7EFF)
|
||||
return false;
|
||||
|
||||
if ( ( addr & 0x0F ) < 6 )
|
||||
{
|
||||
creg [ addr & 0x07 ] = data;
|
||||
if ( ( addr & 0x0F ) < 2 )
|
||||
{
|
||||
nametable [ addr & 0x01 ] = data >> 7;
|
||||
mirror_manual( nametable [ 0 ], nametable [ 0 ], nametable [ 1 ], nametable [ 1 ] );
|
||||
set_chr_bank( ( addr << 11 ) & 0x800, bank_2k, creg [ addr & 0x01 ] >> 1 );
|
||||
return true;
|
||||
}
|
||||
if ((addr & 0x0F) < 6)
|
||||
{
|
||||
creg[addr & 0x07] = data;
|
||||
if ((addr & 0x0F) < 2)
|
||||
{
|
||||
nametable[addr & 0x01] = data >> 7;
|
||||
mirror_manual(nametable[0], nametable[0], nametable[1], nametable[1]);
|
||||
set_chr_bank((addr << 11) & 0x800, bank_2k, creg[addr & 0x01] >> 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
set_chr_bank( 0x1000 | ( ( addr - 0x7EF2 ) << 10 ), bank_1k, creg [ addr & 0x07 ] );
|
||||
return true;
|
||||
}
|
||||
set_chr_bank(0x1000 | ((addr - 0x7EF2) << 10), bank_1k, creg[addr & 0x07]);
|
||||
return true;
|
||||
}
|
||||
|
||||
addr = ( addr - 0x7EFA ) >> 1;
|
||||
preg [ addr ] = data;
|
||||
set_prg_bank( 0x8000 | ( addr << 13 ), bank_8k, preg [ addr ] );
|
||||
return true;
|
||||
}
|
||||
addr = (addr - 0x7EFA) >> 1;
|
||||
preg[addr] = data;
|
||||
set_prg_bank(0x8000 | (addr << 13), bank_8k, preg[addr]);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data ) { }
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data) {}
|
||||
};
|
||||
|
||||
|
|
|
@ -20,34 +20,35 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
// Quattro
|
||||
|
||||
class Mapper232 : public Nes_Mapper {
|
||||
uint8_t regs [2];
|
||||
public:
|
||||
Mapper232()
|
||||
{
|
||||
register_state( regs, sizeof regs );
|
||||
}
|
||||
class Mapper232 : public Nes_Mapper
|
||||
{
|
||||
uint8_t regs[2];
|
||||
|
||||
virtual void reset_state()
|
||||
{
|
||||
regs [0] = 0;
|
||||
regs [1] = 3;
|
||||
}
|
||||
public:
|
||||
Mapper232()
|
||||
{
|
||||
register_state(regs, sizeof regs);
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
int bank = regs [0] >> 1 & 0x0c;
|
||||
set_prg_bank( 0x8000, bank_16k, bank + (regs [1] & 3) );
|
||||
set_prg_bank( 0xC000, bank_16k, bank + 3 );
|
||||
}
|
||||
virtual void reset_state()
|
||||
{
|
||||
regs[0] = 0;
|
||||
regs[1] = 3;
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
if ( addr < 0xc000 )
|
||||
regs [0] = data;
|
||||
else
|
||||
regs [1] = data;
|
||||
Mapper232::apply_mapping();
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
int bank = regs[0] >> 1 & 0x0c;
|
||||
set_prg_bank(0x8000, bank_16k, bank + (regs[1] & 3));
|
||||
set_prg_bank(0xC000, bank_16k, bank + 3);
|
||||
}
|
||||
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
if (addr < 0xc000)
|
||||
regs[0] = data;
|
||||
else
|
||||
regs[1] = data;
|
||||
Mapper232::apply_mapping();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -25,38 +25,40 @@
|
|||
|
||||
// https://www.nesdev.org/wiki/INES_Mapper240
|
||||
|
||||
class Mapper240 : public Nes_Mapper {
|
||||
public:
|
||||
Mapper240()
|
||||
{
|
||||
register_state( ®s, 1 );
|
||||
}
|
||||
class Mapper240 : public Nes_Mapper
|
||||
{
|
||||
public:
|
||||
Mapper240()
|
||||
{
|
||||
register_state(®s, 1);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{
|
||||
}
|
||||
virtual void reset_state()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
enable_sram();
|
||||
intercept_writes( 0x4020, 1 );
|
||||
write_intercepted( 0, 0x4120, regs );
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
enable_sram();
|
||||
intercept_writes(0x4020, 1);
|
||||
write_intercepted(0, 0x4120, regs);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t, int data )
|
||||
{ }
|
||||
virtual void write(nes_time_t, nes_addr_t, int data)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool write_intercepted( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
if ( addr < 0x4020 || addr > 0x5FFF )
|
||||
return false;
|
||||
virtual bool write_intercepted(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
if (addr < 0x4020 || addr > 0x5FFF)
|
||||
return false;
|
||||
|
||||
regs = data;
|
||||
set_chr_bank( 0x0000, bank_8k, data & 0x0F );
|
||||
set_prg_bank( 0x8000, bank_32k, data >> 4 );
|
||||
regs = data;
|
||||
set_chr_bank(0x0000, bank_8k, data & 0x0F);
|
||||
set_prg_bank(0x8000, bank_32k, data >> 4);
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t regs;
|
||||
uint8_t regs;
|
||||
};
|
||||
|
|
|
@ -25,27 +25,29 @@
|
|||
|
||||
// https://www.nesdev.org/wiki/INES_Mapper241
|
||||
|
||||
class Mapper241 : public Nes_Mapper {
|
||||
public:
|
||||
Mapper241()
|
||||
{
|
||||
register_state( &bank, 1 );
|
||||
}
|
||||
class Mapper241 : public Nes_Mapper
|
||||
{
|
||||
public:
|
||||
Mapper241()
|
||||
{
|
||||
register_state(&bank, 1);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{ }
|
||||
virtual void reset_state()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
enable_sram();
|
||||
write( 0, 0, bank );
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
enable_sram();
|
||||
write(0, 0, bank);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t, int data )
|
||||
{
|
||||
bank = data;
|
||||
set_prg_bank( 0x8000, bank_32k, bank );
|
||||
}
|
||||
virtual void write(nes_time_t, nes_addr_t, int data)
|
||||
{
|
||||
bank = data;
|
||||
set_prg_bank(0x8000, bank_32k, bank);
|
||||
}
|
||||
|
||||
uint8_t bank;
|
||||
uint8_t bank;
|
||||
};
|
||||
|
|
|
@ -29,40 +29,42 @@
|
|||
|
||||
struct mapper244_state_t
|
||||
{
|
||||
uint8_t preg;
|
||||
uint8_t creg;
|
||||
uint8_t preg;
|
||||
uint8_t creg;
|
||||
};
|
||||
static_assert( sizeof (mapper244_state_t) == 2 );
|
||||
static_assert(sizeof(mapper244_state_t) == 2);
|
||||
|
||||
class Mapper244 : public Nes_Mapper, mapper244_state_t {
|
||||
public:
|
||||
Mapper244()
|
||||
{
|
||||
mapper244_state_t *state = this;
|
||||
register_state( state, sizeof *state );
|
||||
}
|
||||
class Mapper244 : public Nes_Mapper, mapper244_state_t
|
||||
{
|
||||
public:
|
||||
Mapper244()
|
||||
{
|
||||
mapper244_state_t *state = this;
|
||||
register_state(state, sizeof *state);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{ }
|
||||
virtual void reset_state()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
set_prg_bank( 0x8000, bank_32k, preg );
|
||||
set_chr_bank( 0x0000, bank_8k, creg );
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
set_prg_bank(0x8000, bank_32k, preg);
|
||||
set_chr_bank(0x0000, bank_8k, creg);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
if ( addr >= 0x8065 && addr <= 0x80A4 )
|
||||
{
|
||||
preg = ( addr - 0x8065 ) & 0x03;
|
||||
set_prg_bank( 0x8000, bank_32k, preg );
|
||||
}
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
if (addr >= 0x8065 && addr <= 0x80A4)
|
||||
{
|
||||
preg = (addr - 0x8065) & 0x03;
|
||||
set_prg_bank(0x8000, bank_32k, preg);
|
||||
}
|
||||
|
||||
if ( addr >= 0x80A5 && addr <= 0x80E4 )
|
||||
{
|
||||
creg = (addr - 0x80A5 ) & 0x07;
|
||||
set_chr_bank( 0x0000, bank_8k, creg );
|
||||
}
|
||||
}
|
||||
if (addr >= 0x80A5 && addr <= 0x80E4)
|
||||
{
|
||||
creg = (addr - 0x80A5) & 0x07;
|
||||
set_chr_bank(0x0000, bank_8k, creg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -27,48 +27,49 @@
|
|||
|
||||
// https://www.nesdev.org/wiki/INES_Mapper246
|
||||
|
||||
class Mapper246 : public Nes_Mapper {
|
||||
public:
|
||||
Mapper246()
|
||||
{
|
||||
register_state( regs, sizeof regs );
|
||||
}
|
||||
class Mapper246 : public Nes_Mapper
|
||||
{
|
||||
public:
|
||||
Mapper246()
|
||||
{
|
||||
register_state(regs, sizeof regs);
|
||||
}
|
||||
|
||||
virtual void reset_state()
|
||||
{
|
||||
regs [ 3 ] = ~0;
|
||||
}
|
||||
virtual void reset_state()
|
||||
{
|
||||
regs[3] = ~0;
|
||||
}
|
||||
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
enable_sram();
|
||||
intercept_writes( 0x6000, 0x07 );
|
||||
for ( size_t i = 0; i < sizeof regs; i++ )
|
||||
write_intercepted( 0, 0x6000 + i, regs [ i ] );
|
||||
}
|
||||
virtual void apply_mapping()
|
||||
{
|
||||
enable_sram();
|
||||
intercept_writes(0x6000, 0x07);
|
||||
for (size_t i = 0; i < sizeof regs; i++)
|
||||
write_intercepted(0, 0x6000 + i, regs[i]);
|
||||
}
|
||||
|
||||
virtual void write( nes_time_t, nes_addr_t addr, int data )
|
||||
{ }
|
||||
virtual void write(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool write_intercepted( nes_time_t, nes_addr_t addr, int data )
|
||||
{
|
||||
int bank = addr & 0x07;
|
||||
virtual bool write_intercepted(nes_time_t, nes_addr_t addr, int data)
|
||||
{
|
||||
int bank = addr & 0x07;
|
||||
|
||||
if ( addr < 0x6000 || addr > 0x67FF )
|
||||
return false;
|
||||
if (addr < 0x6000 || addr > 0x67FF)
|
||||
return false;
|
||||
|
||||
regs [ bank ] = data;
|
||||
if ( bank < 4 )
|
||||
{
|
||||
set_prg_bank( 0x8000 + ( bank << 13 ), bank_8k, data );
|
||||
return true;
|
||||
}
|
||||
regs[bank] = data;
|
||||
if (bank < 4)
|
||||
{
|
||||
set_prg_bank(0x8000 + (bank << 13), bank_8k, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
set_chr_bank( 0x0000 + ( ( bank & 0x03 ) << 11 ) , bank_2k, data );
|
||||
set_chr_bank(0x0000 + ((bank & 0x03) << 11), bank_2k, data);
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t regs [ 8 ];
|
||||
uint8_t regs[8];
|
||||
};
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -14,124 +14,128 @@ typedef long ppu_time_t; // ppu_time_t = nes_time_t * ppu_overclock
|
|||
|
||||
ppu_time_t const ppu_overclock = 3; // PPU clocks for each CPU clock
|
||||
|
||||
class Nes_Ppu : public Nes_Ppu_Rendering {
|
||||
typedef Nes_Ppu_Rendering base;
|
||||
public:
|
||||
Nes_Ppu( Nes_Core* );
|
||||
class Nes_Ppu : public Nes_Ppu_Rendering
|
||||
{
|
||||
typedef Nes_Ppu_Rendering base;
|
||||
|
||||
// Begin PPU frame and return beginning CPU timestamp
|
||||
nes_time_t begin_frame( ppu_time_t );
|
||||
public:
|
||||
Nes_Ppu(Nes_Core *);
|
||||
|
||||
nes_time_t nmi_time() { return nmi_time_; }
|
||||
void acknowledge_nmi() { nmi_time_ = LONG_MAX / 2 + 1; }
|
||||
// Begin PPU frame and return beginning CPU timestamp
|
||||
nes_time_t begin_frame(ppu_time_t);
|
||||
|
||||
int read_2002( nes_time_t );
|
||||
int read( unsigned addr, nes_time_t );
|
||||
void write( nes_time_t, unsigned addr, int );
|
||||
nes_time_t nmi_time() { return nmi_time_; }
|
||||
void acknowledge_nmi() { nmi_time_ = LONG_MAX / 2 + 1; }
|
||||
|
||||
void render_bg_until( nes_time_t );
|
||||
void render_until( nes_time_t );
|
||||
int read_2002(nes_time_t);
|
||||
int read(unsigned addr, nes_time_t);
|
||||
void write(nes_time_t, unsigned addr, int);
|
||||
|
||||
// CPU time that frame will have ended by
|
||||
int frame_length() const { return frame_length_; }
|
||||
void render_bg_until(nes_time_t);
|
||||
void render_until(nes_time_t);
|
||||
|
||||
// End frame rendering and return PPU timestamp for next frame
|
||||
ppu_time_t end_frame( nes_time_t );
|
||||
// CPU time that frame will have ended by
|
||||
int frame_length() const { return frame_length_; }
|
||||
|
||||
// Do direct memory copy to sprite RAM
|
||||
void dma_sprites( nes_time_t, void const* in );
|
||||
// End frame rendering and return PPU timestamp for next frame
|
||||
ppu_time_t end_frame(nes_time_t);
|
||||
|
||||
int burst_phase;
|
||||
// Do direct memory copy to sprite RAM
|
||||
void dma_sprites(nes_time_t, void const *in);
|
||||
|
||||
private:
|
||||
int burst_phase;
|
||||
|
||||
Nes_Core& emu;
|
||||
private:
|
||||
Nes_Core &emu;
|
||||
|
||||
enum { indefinite_time = LONG_MAX / 2 + 1 };
|
||||
enum
|
||||
{
|
||||
indefinite_time = LONG_MAX / 2 + 1
|
||||
};
|
||||
|
||||
void suspend_rendering();
|
||||
int read_( unsigned addr, nes_time_t ); // note swapped arguments!
|
||||
void suspend_rendering();
|
||||
int read_(unsigned addr, nes_time_t); // note swapped arguments!
|
||||
|
||||
// NES<->PPU time conversion
|
||||
int extra_clocks;
|
||||
ppu_time_t ppu_time( nes_time_t t ) const { return t * ppu_overclock + extra_clocks; }
|
||||
nes_time_t nes_time( ppu_time_t t ) const { return (t - extra_clocks) / ppu_overclock; }
|
||||
// NES<->PPU time conversion
|
||||
int extra_clocks;
|
||||
ppu_time_t ppu_time(nes_time_t t) const { return t * ppu_overclock + extra_clocks; }
|
||||
nes_time_t nes_time(ppu_time_t t) const { return (t - extra_clocks) / ppu_overclock; }
|
||||
|
||||
// frame
|
||||
nes_time_t nmi_time_;
|
||||
int end_vbl_mask;
|
||||
int frame_length_;
|
||||
int frame_length_extra;
|
||||
bool frame_ended;
|
||||
void end_vblank();
|
||||
void run_end_frame( nes_time_t );
|
||||
// frame
|
||||
nes_time_t nmi_time_;
|
||||
int end_vbl_mask;
|
||||
int frame_length_;
|
||||
int frame_length_extra;
|
||||
bool frame_ended;
|
||||
void end_vblank();
|
||||
void run_end_frame(nes_time_t);
|
||||
|
||||
// bg rendering
|
||||
nes_time_t next_bg_time;
|
||||
ppu_time_t scanline_time;
|
||||
ppu_time_t hblank_time;
|
||||
int scanline_count;
|
||||
int frame_phase;
|
||||
void render_bg_until_( nes_time_t );
|
||||
void run_scanlines( int count );
|
||||
// bg rendering
|
||||
nes_time_t next_bg_time;
|
||||
ppu_time_t scanline_time;
|
||||
ppu_time_t hblank_time;
|
||||
int scanline_count;
|
||||
int frame_phase;
|
||||
void render_bg_until_(nes_time_t);
|
||||
void run_scanlines(int count);
|
||||
|
||||
// sprite rendering
|
||||
ppu_time_t next_sprites_time;
|
||||
int next_sprites_scanline;
|
||||
void render_until_( nes_time_t );
|
||||
// sprite rendering
|
||||
ppu_time_t next_sprites_time;
|
||||
int next_sprites_scanline;
|
||||
void render_until_(nes_time_t);
|
||||
|
||||
// $2002 status register
|
||||
nes_time_t next_status_event;
|
||||
void query_until( nes_time_t );
|
||||
// $2002 status register
|
||||
nes_time_t next_status_event;
|
||||
void query_until(nes_time_t);
|
||||
|
||||
// sprite hit
|
||||
nes_time_t next_sprite_hit_check;
|
||||
void update_sprite_hit( nes_time_t );
|
||||
// sprite hit
|
||||
nes_time_t next_sprite_hit_check;
|
||||
void update_sprite_hit(nes_time_t);
|
||||
|
||||
// open bus decay
|
||||
void update_open_bus( nes_time_t );
|
||||
void poke_open_bus( nes_time_t, int, int mask );
|
||||
nes_time_t earliest_open_bus_decay();
|
||||
// open bus decay
|
||||
void update_open_bus(nes_time_t);
|
||||
void poke_open_bus(nes_time_t, int, int mask);
|
||||
nes_time_t earliest_open_bus_decay();
|
||||
|
||||
// sprite max
|
||||
nes_time_t next_sprite_max_run; // doesn't need to run until this time
|
||||
nes_time_t sprite_max_set_time; // if 0, needs to be recalculated
|
||||
int next_sprite_max_scanline;
|
||||
void run_sprite_max_( nes_time_t );
|
||||
void run_sprite_max( nes_time_t );
|
||||
void invalidate_sprite_max_();
|
||||
void invalidate_sprite_max( nes_time_t );
|
||||
// sprite max
|
||||
nes_time_t next_sprite_max_run; // doesn't need to run until this time
|
||||
nes_time_t sprite_max_set_time; // if 0, needs to be recalculated
|
||||
int next_sprite_max_scanline;
|
||||
void run_sprite_max_(nes_time_t);
|
||||
void run_sprite_max(nes_time_t);
|
||||
void invalidate_sprite_max_();
|
||||
void invalidate_sprite_max(nes_time_t);
|
||||
|
||||
friend int nes_cpu_read_likely_ppu( class Nes_Core*, unsigned, nes_time_t );
|
||||
friend int nes_cpu_read_likely_ppu(class Nes_Core *, unsigned, nes_time_t);
|
||||
};
|
||||
|
||||
inline void Nes_Ppu::suspend_rendering()
|
||||
{
|
||||
next_bg_time = indefinite_time;
|
||||
next_sprites_time = indefinite_time;
|
||||
extra_clocks = 0;
|
||||
next_bg_time = indefinite_time;
|
||||
next_sprites_time = indefinite_time;
|
||||
extra_clocks = 0;
|
||||
}
|
||||
|
||||
inline Nes_Ppu::Nes_Ppu( Nes_Core* e ) : emu( *e )
|
||||
inline Nes_Ppu::Nes_Ppu(Nes_Core *e) : emu(*e)
|
||||
{
|
||||
burst_phase = 0;
|
||||
suspend_rendering();
|
||||
burst_phase = 0;
|
||||
suspend_rendering();
|
||||
}
|
||||
|
||||
inline void Nes_Ppu::render_until( nes_time_t t )
|
||||
inline void Nes_Ppu::render_until(nes_time_t t)
|
||||
{
|
||||
if ( t > next_sprites_time )
|
||||
render_until_( t );
|
||||
if (t > next_sprites_time)
|
||||
render_until_(t);
|
||||
}
|
||||
|
||||
inline void Nes_Ppu::render_bg_until( nes_time_t t )
|
||||
inline void Nes_Ppu::render_bg_until(nes_time_t t)
|
||||
{
|
||||
if ( t > next_bg_time )
|
||||
render_bg_until_( t );
|
||||
if (t > next_bg_time)
|
||||
render_bg_until_(t);
|
||||
}
|
||||
|
||||
inline void Nes_Ppu::update_open_bus( nes_time_t time )
|
||||
inline void Nes_Ppu::update_open_bus(nes_time_t time)
|
||||
{
|
||||
if ( time >= decay_low ) open_bus &= ~0x1F;
|
||||
if ( time >= decay_high ) open_bus &= ~0xE0;
|
||||
if (time >= decay_low) open_bus &= ~0x1F;
|
||||
if (time >= decay_high) open_bus &= ~0xE0;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// Nes_Emu 0.7.0. http://www.slack.net/~ant/
|
||||
|
||||
#include <cstring>
|
||||
#include "Nes_Ppu_Impl.hpp"
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include "Nes_Ppu_Impl.hpp"
|
||||
#include <cstring>
|
||||
|
||||
/* Copyright (C) 2004-2006 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
|
@ -18,420 +18,414 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
int const cache_line_size = 128; // tile cache is kept aligned to this boundary
|
||||
|
||||
inline void set_be32( void* p, unsigned long n ) {
|
||||
((unsigned char*) p) [0] = (unsigned char) (n >> 24);
|
||||
((unsigned char*) p) [1] = (unsigned char) (n >> 16);
|
||||
((unsigned char*) p) [2] = (unsigned char) (n >> 8);
|
||||
((unsigned char*) p) [3] = (unsigned char) n;
|
||||
inline void set_be32(void *p, unsigned long n)
|
||||
{
|
||||
((unsigned char *)p)[0] = (unsigned char)(n >> 24);
|
||||
((unsigned char *)p)[1] = (unsigned char)(n >> 16);
|
||||
((unsigned char *)p)[2] = (unsigned char)(n >> 8);
|
||||
((unsigned char *)p)[3] = (unsigned char)n;
|
||||
}
|
||||
|
||||
#define SET_BE32( addr, data ) set_be32( addr, data )
|
||||
#define SET_BE32(addr, data) set_be32(addr, data)
|
||||
|
||||
Nes_Ppu_Impl::Nes_Ppu_Impl()
|
||||
{
|
||||
impl = NULL;
|
||||
chr_data = NULL;
|
||||
chr_size = 0;
|
||||
tile_cache = NULL;
|
||||
host_palette = NULL;
|
||||
max_palette_size = 0;
|
||||
tile_cache_mem = NULL;
|
||||
ppu_state_t::unused = 0;
|
||||
|
||||
mmc24_enabled = false;
|
||||
mmc24_latched[0] = 0;
|
||||
mmc24_latched[1] = 0;
|
||||
impl = NULL;
|
||||
chr_data = NULL;
|
||||
chr_size = 0;
|
||||
tile_cache = NULL;
|
||||
host_palette = NULL;
|
||||
max_palette_size = 0;
|
||||
tile_cache_mem = NULL;
|
||||
ppu_state_t::unused = 0;
|
||||
|
||||
mmc24_enabled = false;
|
||||
mmc24_latched[0] = 0;
|
||||
mmc24_latched[1] = 0;
|
||||
}
|
||||
|
||||
Nes_Ppu_Impl::~Nes_Ppu_Impl()
|
||||
{
|
||||
close_chr();
|
||||
delete impl;
|
||||
close_chr();
|
||||
delete impl;
|
||||
}
|
||||
|
||||
void Nes_Ppu_Impl::all_tiles_modified()
|
||||
{
|
||||
any_tiles_modified = true;
|
||||
memset( modified_tiles, ~0, sizeof modified_tiles );
|
||||
any_tiles_modified = true;
|
||||
memset(modified_tiles, ~0, sizeof modified_tiles);
|
||||
}
|
||||
|
||||
const char *Nes_Ppu_Impl::open_chr( uint8_t const* new_chr, long chr_data_size )
|
||||
const char *Nes_Ppu_Impl::open_chr(uint8_t const *new_chr, long chr_data_size)
|
||||
{
|
||||
close_chr();
|
||||
close_chr();
|
||||
|
||||
if ( !impl )
|
||||
{
|
||||
impl = new impl_t;
|
||||
chr_ram = impl->chr_ram;
|
||||
}
|
||||
if (!impl)
|
||||
{
|
||||
impl = new impl_t;
|
||||
chr_ram = impl->chr_ram;
|
||||
}
|
||||
|
||||
chr_data = new_chr;
|
||||
chr_size = chr_data_size;
|
||||
chr_is_writable = false;
|
||||
chr_data = new_chr;
|
||||
chr_size = chr_data_size;
|
||||
chr_is_writable = false;
|
||||
|
||||
if ( chr_data_size == 0 )
|
||||
{
|
||||
// CHR RAM
|
||||
chr_data = impl->chr_ram;
|
||||
chr_size = sizeof impl->chr_ram;
|
||||
chr_is_writable = true;
|
||||
}
|
||||
if (chr_data_size == 0)
|
||||
{
|
||||
// CHR RAM
|
||||
chr_data = impl->chr_ram;
|
||||
chr_size = sizeof impl->chr_ram;
|
||||
chr_is_writable = true;
|
||||
}
|
||||
|
||||
// allocate aligned memory for cache
|
||||
long tile_count = chr_size / bytes_per_tile;
|
||||
tile_cache_mem = new uint8_t [tile_count * sizeof (cached_tile_t) * 2 + cache_line_size];
|
||||
tile_cache = (cached_tile_t*) (tile_cache_mem + cache_line_size -
|
||||
(uintptr_t) tile_cache_mem % cache_line_size);
|
||||
flipped_tiles = tile_cache + tile_count;
|
||||
// allocate aligned memory for cache
|
||||
long tile_count = chr_size / bytes_per_tile;
|
||||
tile_cache_mem = new uint8_t[tile_count * sizeof(cached_tile_t) * 2 + cache_line_size];
|
||||
tile_cache = (cached_tile_t *)(tile_cache_mem + cache_line_size -
|
||||
(uintptr_t)tile_cache_mem % cache_line_size);
|
||||
flipped_tiles = tile_cache + tile_count;
|
||||
|
||||
// rebuild cache
|
||||
all_tiles_modified();
|
||||
if ( !chr_is_writable )
|
||||
{
|
||||
any_tiles_modified = false;
|
||||
rebuild_chr( 0, chr_size );
|
||||
}
|
||||
// rebuild cache
|
||||
all_tiles_modified();
|
||||
if (!chr_is_writable)
|
||||
{
|
||||
any_tiles_modified = false;
|
||||
rebuild_chr(0, chr_size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Nes_Ppu_Impl::close_chr()
|
||||
{
|
||||
delete [] tile_cache_mem;
|
||||
tile_cache_mem = NULL;
|
||||
delete[] tile_cache_mem;
|
||||
tile_cache_mem = NULL;
|
||||
}
|
||||
|
||||
void Nes_Ppu_Impl::set_chr_bank( int addr, int size, long data )
|
||||
void Nes_Ppu_Impl::set_chr_bank(int addr, int size, long data)
|
||||
{
|
||||
if ( data + size > chr_size )
|
||||
data %= chr_size;
|
||||
if (data + size > chr_size)
|
||||
data %= chr_size;
|
||||
|
||||
int count = (unsigned) size / chr_page_size;
|
||||
int count = (unsigned)size / chr_page_size;
|
||||
|
||||
int page = (unsigned) addr / chr_page_size;
|
||||
while ( count-- )
|
||||
{
|
||||
chr_pages [page] = data - page * chr_page_size;
|
||||
page++;
|
||||
data += chr_page_size;
|
||||
}
|
||||
int page = (unsigned)addr / chr_page_size;
|
||||
while (count--)
|
||||
{
|
||||
chr_pages[page] = data - page * chr_page_size;
|
||||
page++;
|
||||
data += chr_page_size;
|
||||
}
|
||||
}
|
||||
|
||||
void Nes_Ppu_Impl::set_chr_bank_ex( int addr, int size, long data )
|
||||
void Nes_Ppu_Impl::set_chr_bank_ex(int addr, int size, long data)
|
||||
{
|
||||
mmc24_enabled = true;
|
||||
mmc24_enabled = true;
|
||||
|
||||
//check( !chr_is_writable || addr == data ); // to do: is CHR RAM ever bank-switched?
|
||||
//dprintf( "Tried to set CHR RAM bank at %04X to CHR+%04X\n", addr, data );
|
||||
// check( !chr_is_writable || addr == data ); // to do: is CHR RAM ever bank-switched?
|
||||
// dprintf( "Tried to set CHR RAM bank at %04X to CHR+%04X\n", addr, data );
|
||||
|
||||
if ( data + size > chr_size )
|
||||
data %= chr_size;
|
||||
if (data + size > chr_size)
|
||||
data %= chr_size;
|
||||
|
||||
int count = (unsigned) size / chr_page_size;
|
||||
//assert( chr_page_size * count == size );
|
||||
//assert( addr + size <= chr_addr_size );
|
||||
int count = (unsigned)size / chr_page_size;
|
||||
// assert( chr_page_size * count == size );
|
||||
// assert( addr + size <= chr_addr_size );
|
||||
|
||||
int page = (unsigned) addr / chr_page_size;
|
||||
while ( count-- )
|
||||
{
|
||||
chr_pages_ex [page] = data - page * chr_page_size;
|
||||
page++;
|
||||
data += chr_page_size;
|
||||
}
|
||||
int page = (unsigned)addr / chr_page_size;
|
||||
while (count--)
|
||||
{
|
||||
chr_pages_ex[page] = data - page * chr_page_size;
|
||||
page++;
|
||||
data += chr_page_size;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t const initial_palette [0x20] =
|
||||
static uint8_t const initial_palette[0x20] =
|
||||
{
|
||||
0x0f, 0x01, 0x00, 0x01, 0x00, 0x02, 0x02, 0x0D, 0x08, 0x10, 0x08, 0x24, 0x00, 0x00, 0x04, 0x2C, 0x00, 0x01, 0x34, 0x03, 0x00, 0x04, 0x00, 0x14, 0x00, 0x3A, 0x00, 0x02, 0x00, 0x20, 0x2C, 0x08};
|
||||
|
||||
void Nes_Ppu_Impl::reset(bool full_reset)
|
||||
{
|
||||
0x0f,0x01,0x00,0x01,0x00,0x02,0x02,0x0D,0x08,0x10,0x08,0x24,0x00,0x00,0x04,0x2C,
|
||||
0x00,0x01,0x34,0x03,0x00,0x04,0x00,0x14,0x00,0x3A,0x00,0x02,0x00,0x20,0x2C,0x08
|
||||
};
|
||||
w2000 = 0;
|
||||
w2001 = 0;
|
||||
r2002 = 0x80;
|
||||
r2007 = 0;
|
||||
open_bus = 0;
|
||||
decay_low = 0;
|
||||
decay_high = 0;
|
||||
second_write = false;
|
||||
vram_temp = 0;
|
||||
pixel_x = 0;
|
||||
|
||||
void Nes_Ppu_Impl::reset( bool full_reset )
|
||||
{
|
||||
w2000 = 0;
|
||||
w2001 = 0;
|
||||
r2002 = 0x80;
|
||||
r2007 = 0;
|
||||
open_bus = 0;
|
||||
decay_low = 0;
|
||||
decay_high = 0;
|
||||
second_write = false;
|
||||
vram_temp = 0;
|
||||
pixel_x = 0;
|
||||
if (full_reset)
|
||||
{
|
||||
vram_addr = 0;
|
||||
w2003 = 0;
|
||||
memset(impl->chr_ram, 0xff, sizeof impl->chr_ram);
|
||||
memset(impl->nt_ram, 0xff, sizeof impl->nt_ram);
|
||||
memcpy(palette, initial_palette, sizeof palette);
|
||||
}
|
||||
|
||||
if ( full_reset )
|
||||
{
|
||||
vram_addr = 0;
|
||||
w2003 = 0;
|
||||
memset( impl->chr_ram, 0xff, sizeof impl->chr_ram );
|
||||
memset( impl->nt_ram, 0xff, sizeof impl->nt_ram );
|
||||
memcpy( palette, initial_palette, sizeof palette );
|
||||
}
|
||||
|
||||
set_nt_banks( 0, 0, 0, 0 );
|
||||
set_chr_bank( 0, chr_addr_size, 0 );
|
||||
memset( spr_ram, 0xff, sizeof spr_ram );
|
||||
all_tiles_modified();
|
||||
if ( max_palette_size > 0 )
|
||||
memset( host_palette, 0, max_palette_size * sizeof *host_palette );
|
||||
set_nt_banks(0, 0, 0, 0);
|
||||
set_chr_bank(0, chr_addr_size, 0);
|
||||
memset(spr_ram, 0xff, sizeof spr_ram);
|
||||
all_tiles_modified();
|
||||
if (max_palette_size > 0)
|
||||
memset(host_palette, 0, max_palette_size * sizeof *host_palette);
|
||||
}
|
||||
|
||||
void Nes_Ppu_Impl::capture_palette()
|
||||
{
|
||||
if ( palette_size + palette_increment <= max_palette_size )
|
||||
{
|
||||
palette_offset = (palette_begin + palette_size) * 0x01010101;
|
||||
if (palette_size + palette_increment <= max_palette_size)
|
||||
{
|
||||
palette_offset = (palette_begin + palette_size) * 0x01010101;
|
||||
|
||||
short* out = host_palette + palette_size;
|
||||
palette_size += palette_increment;
|
||||
short *out = host_palette + palette_size;
|
||||
palette_size += palette_increment;
|
||||
|
||||
int i;
|
||||
int i;
|
||||
|
||||
int emph = w2001 << 1 & 0x1C0;
|
||||
int mono = (w2001 & 1 ? 0x30 : 0x3F);
|
||||
int emph = w2001 << 1 & 0x1C0;
|
||||
int mono = (w2001 & 1 ? 0x30 : 0x3F);
|
||||
|
||||
for ( i = 0; i < 32; i++ )
|
||||
out [i] = (palette [i] & mono) | emph;
|
||||
for (i = 0; i < 32; i++)
|
||||
out[i] = (palette[i] & mono) | emph;
|
||||
|
||||
int bg = out [0];
|
||||
for ( i = 4; i < 32; i += 4 )
|
||||
out [i] = bg;
|
||||
int bg = out[0];
|
||||
for (i = 4; i < 32; i += 4)
|
||||
out[i] = bg;
|
||||
|
||||
memcpy( out + 32, out, 32 * sizeof *out );
|
||||
}
|
||||
memcpy(out + 32, out, 32 * sizeof *out);
|
||||
}
|
||||
}
|
||||
|
||||
void Nes_Ppu_Impl::run_hblank( int count )
|
||||
void Nes_Ppu_Impl::run_hblank(int count)
|
||||
{
|
||||
long addr = (vram_addr & 0x7be0) + (vram_temp & 0x41f) + (count * 0x1000);
|
||||
if ( w2001 & 0x08 )
|
||||
{
|
||||
while ( addr >= 0x8000 )
|
||||
{
|
||||
int y = (addr + 0x20) & 0x3e0;
|
||||
addr = (addr - 0x8000) & ~0x3e0;
|
||||
if ( y == 30 * 0x20 )
|
||||
y = 0x800;
|
||||
addr ^= y;
|
||||
}
|
||||
vram_addr = addr;
|
||||
}
|
||||
long addr = (vram_addr & 0x7be0) + (vram_temp & 0x41f) + (count * 0x1000);
|
||||
if (w2001 & 0x08)
|
||||
{
|
||||
while (addr >= 0x8000)
|
||||
{
|
||||
int y = (addr + 0x20) & 0x3e0;
|
||||
addr = (addr - 0x8000) & ~0x3e0;
|
||||
if (y == 30 * 0x20)
|
||||
y = 0x800;
|
||||
addr ^= y;
|
||||
}
|
||||
vram_addr = addr;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __MWERKS__
|
||||
#pragma ppc_unroll_factor_limit 1 // messes up calc_sprite_max_scanlines loop
|
||||
static int zero = 0;
|
||||
#pragma ppc_unroll_factor_limit 1 // messes up calc_sprite_max_scanlines loop
|
||||
static int zero = 0;
|
||||
#else
|
||||
const int zero = 0;
|
||||
const int zero = 0;
|
||||
#endif
|
||||
|
||||
// Tile cache
|
||||
|
||||
inline unsigned long reorder( unsigned long n )
|
||||
inline unsigned long reorder(unsigned long n)
|
||||
{
|
||||
n |= n << 7;
|
||||
return ((n << 14) | n);
|
||||
n |= n << 7;
|
||||
return ((n << 14) | n);
|
||||
}
|
||||
|
||||
inline void Nes_Ppu_Impl::update_tile( int index )
|
||||
inline void Nes_Ppu_Impl::update_tile(int index)
|
||||
{
|
||||
const uint8_t* in = chr_data + (index) * bytes_per_tile;
|
||||
uint8_t* out = (uint8_t*) tile_cache [index];
|
||||
uint8_t* flipped_out = (uint8_t*) flipped_tiles [index];
|
||||
const uint8_t *in = chr_data + (index)*bytes_per_tile;
|
||||
uint8_t *out = (uint8_t *)tile_cache[index];
|
||||
uint8_t *flipped_out = (uint8_t *)flipped_tiles[index];
|
||||
|
||||
unsigned long bit_mask = 0x11111111 + zero;
|
||||
unsigned long bit_mask = 0x11111111 + zero;
|
||||
|
||||
for ( int n = 4; n--; )
|
||||
{
|
||||
// Reorder two lines of two-bit pixels. No bits are wasted, so
|
||||
// reordered version is also four bytes.
|
||||
//
|
||||
// 12345678 to A0E4B1F5C2G6D3H7
|
||||
// ABCDEFGH
|
||||
unsigned long c =
|
||||
((reorder( in [0] ) & bit_mask) << 0) |
|
||||
((reorder( in [8] ) & bit_mask) << 1) |
|
||||
((reorder( in [1] ) & bit_mask) << 2) |
|
||||
((reorder( in [9] ) & bit_mask) << 3);
|
||||
in += 2;
|
||||
for (int n = 4; n--;)
|
||||
{
|
||||
// Reorder two lines of two-bit pixels. No bits are wasted, so
|
||||
// reordered version is also four bytes.
|
||||
//
|
||||
// 12345678 to A0E4B1F5C2G6D3H7
|
||||
// ABCDEFGH
|
||||
unsigned long c =
|
||||
((reorder(in[0]) & bit_mask) << 0) |
|
||||
((reorder(in[8]) & bit_mask) << 1) |
|
||||
((reorder(in[1]) & bit_mask) << 2) |
|
||||
((reorder(in[9]) & bit_mask) << 3);
|
||||
in += 2;
|
||||
|
||||
SET_BE32( out, c );
|
||||
out += 4;
|
||||
SET_BE32(out, c);
|
||||
out += 4;
|
||||
|
||||
// make horizontally-flipped version
|
||||
c = ((c >> 28) & 0x000f) |
|
||||
((c >> 20) & 0x00f0) |
|
||||
((c >> 12) & 0x0f00) |
|
||||
((c >> 4) & 0xf000) |
|
||||
((c & 0xf000) << 4) |
|
||||
((c & 0x0f00) << 12) |
|
||||
((c & 0x00f0) << 20) |
|
||||
((c & 0x000f) << 28);
|
||||
SET_BE32( flipped_out, c );
|
||||
flipped_out += 4;
|
||||
}
|
||||
// make horizontally-flipped version
|
||||
c = ((c >> 28) & 0x000f) |
|
||||
((c >> 20) & 0x00f0) |
|
||||
((c >> 12) & 0x0f00) |
|
||||
((c >> 4) & 0xf000) |
|
||||
((c & 0xf000) << 4) |
|
||||
((c & 0x0f00) << 12) |
|
||||
((c & 0x00f0) << 20) |
|
||||
((c & 0x000f) << 28);
|
||||
SET_BE32(flipped_out, c);
|
||||
flipped_out += 4;
|
||||
}
|
||||
}
|
||||
|
||||
void Nes_Ppu_Impl::rebuild_chr( unsigned long begin, unsigned long end )
|
||||
void Nes_Ppu_Impl::rebuild_chr(unsigned long begin, unsigned long end)
|
||||
{
|
||||
unsigned end_index = (end + bytes_per_tile - 1) / bytes_per_tile;
|
||||
for ( unsigned index = begin / bytes_per_tile; index < end_index; index++ )
|
||||
update_tile( index );
|
||||
unsigned end_index = (end + bytes_per_tile - 1) / bytes_per_tile;
|
||||
for (unsigned index = begin / bytes_per_tile; index < end_index; index++)
|
||||
update_tile(index);
|
||||
}
|
||||
|
||||
void Nes_Ppu_Impl::update_tiles( int first_tile )
|
||||
void Nes_Ppu_Impl::update_tiles(int first_tile)
|
||||
{
|
||||
int chunk = 0;
|
||||
do
|
||||
{
|
||||
if ( !(uint32_t&) modified_tiles [chunk] )
|
||||
{
|
||||
chunk += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
int modified = modified_tiles [chunk];
|
||||
if ( modified )
|
||||
{
|
||||
modified_tiles [chunk] = 0;
|
||||
int chunk = 0;
|
||||
do
|
||||
{
|
||||
if (!(uint32_t &)modified_tiles[chunk])
|
||||
{
|
||||
chunk += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
int modified = modified_tiles[chunk];
|
||||
if (modified)
|
||||
{
|
||||
modified_tiles[chunk] = 0;
|
||||
|
||||
int index = first_tile + chunk * 8;
|
||||
do
|
||||
{
|
||||
if ( modified & 1 )
|
||||
update_tile( index );
|
||||
index++;
|
||||
}
|
||||
while ( (modified >>= 1) != 0 );
|
||||
}
|
||||
}
|
||||
while ( ++chunk & 3 );
|
||||
}
|
||||
}
|
||||
while ( chunk < chr_tile_count / 8 );
|
||||
int index = first_tile + chunk * 8;
|
||||
do
|
||||
{
|
||||
if (modified & 1)
|
||||
update_tile(index);
|
||||
index++;
|
||||
} while ((modified >>= 1) != 0);
|
||||
}
|
||||
} while (++chunk & 3);
|
||||
}
|
||||
} while (chunk < chr_tile_count / 8);
|
||||
}
|
||||
|
||||
// Sprite max
|
||||
|
||||
template<int height>
|
||||
template <int height>
|
||||
struct calc_sprite_max_scanlines
|
||||
{
|
||||
static unsigned long func( uint8_t const* sprites, uint8_t* scanlines, int begin )
|
||||
{
|
||||
unsigned long any_hits = 0;
|
||||
unsigned long const offset = 0x01010101 + zero;
|
||||
unsigned limit = 239 + height - begin;
|
||||
for ( int n = 64; n; --n )
|
||||
{
|
||||
int top = *sprites;
|
||||
sprites += 4;
|
||||
uint8_t* p = scanlines + top;
|
||||
if ( (unsigned) (239 - top) < limit )
|
||||
{
|
||||
unsigned long p0 = ((uint32_t*)p) [0] + offset;
|
||||
unsigned long p4 = ((uint32_t*)p) [1] + offset;
|
||||
((uint32_t*)p) [0] = p0;
|
||||
any_hits |= p0;
|
||||
((uint32_t*)p) [1] = p4;
|
||||
any_hits |= p4;
|
||||
if ( height > 8 )
|
||||
{
|
||||
unsigned long p0 = ((uint32_t*)p) [2] + offset;
|
||||
unsigned long p4 = ((uint32_t*)p) [3] + offset;
|
||||
((uint32_t*)p) [2] = p0;
|
||||
any_hits |= p0;
|
||||
((uint32_t*)p) [3] = p4;
|
||||
any_hits |= p4;
|
||||
}
|
||||
}
|
||||
}
|
||||
static unsigned long func(uint8_t const *sprites, uint8_t *scanlines, int begin)
|
||||
{
|
||||
unsigned long any_hits = 0;
|
||||
unsigned long const offset = 0x01010101 + zero;
|
||||
unsigned limit = 239 + height - begin;
|
||||
for (int n = 64; n; --n)
|
||||
{
|
||||
int top = *sprites;
|
||||
sprites += 4;
|
||||
uint8_t *p = scanlines + top;
|
||||
if ((unsigned)(239 - top) < limit)
|
||||
{
|
||||
unsigned long p0 = ((uint32_t *)p)[0] + offset;
|
||||
unsigned long p4 = ((uint32_t *)p)[1] + offset;
|
||||
((uint32_t *)p)[0] = p0;
|
||||
any_hits |= p0;
|
||||
((uint32_t *)p)[1] = p4;
|
||||
any_hits |= p4;
|
||||
if (height > 8)
|
||||
{
|
||||
unsigned long p0 = ((uint32_t *)p)[2] + offset;
|
||||
unsigned long p4 = ((uint32_t *)p)[3] + offset;
|
||||
((uint32_t *)p)[2] = p0;
|
||||
any_hits |= p0;
|
||||
((uint32_t *)p)[3] = p4;
|
||||
any_hits |= p4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return any_hits;
|
||||
}
|
||||
return any_hits;
|
||||
}
|
||||
};
|
||||
|
||||
long Nes_Ppu_Impl::recalc_sprite_max( int scanline )
|
||||
long Nes_Ppu_Impl::recalc_sprite_max(int scanline)
|
||||
{
|
||||
int const max_scanline_count = image_height;
|
||||
int const max_scanline_count = image_height;
|
||||
|
||||
uint8_t sprite_max_scanlines [256 + 16];
|
||||
uint8_t sprite_max_scanlines[256 + 16];
|
||||
|
||||
// recalculate sprites per scanline
|
||||
memset( sprite_max_scanlines + scanline, 0x78, last_sprite_max_scanline - scanline );
|
||||
unsigned long any_hits;
|
||||
if ( w2000 & 0x20 )
|
||||
any_hits = calc_sprite_max_scanlines<16>::func( spr_ram, sprite_max_scanlines, scanline );
|
||||
else
|
||||
any_hits = calc_sprite_max_scanlines<8 >::func( spr_ram, sprite_max_scanlines, scanline );
|
||||
// recalculate sprites per scanline
|
||||
memset(sprite_max_scanlines + scanline, 0x78, last_sprite_max_scanline - scanline);
|
||||
unsigned long any_hits;
|
||||
if (w2000 & 0x20)
|
||||
any_hits = calc_sprite_max_scanlines<16>::func(spr_ram, sprite_max_scanlines, scanline);
|
||||
else
|
||||
any_hits = calc_sprite_max_scanlines<8>::func(spr_ram, sprite_max_scanlines, scanline);
|
||||
|
||||
// cause search to terminate past max_scanline_count if none have 8 or more sprites
|
||||
(uint32_t&) sprite_max_scanlines [max_scanline_count] = 0;
|
||||
sprite_max_scanlines [max_scanline_count + 3] = 0x80;
|
||||
// cause search to terminate past max_scanline_count if none have 8 or more sprites
|
||||
(uint32_t &)sprite_max_scanlines[max_scanline_count] = 0;
|
||||
sprite_max_scanlines[max_scanline_count + 3] = 0x80;
|
||||
|
||||
// avoid scan if no possible hits
|
||||
if ( !(any_hits & 0x80808080) )
|
||||
return 0;
|
||||
// avoid scan if no possible hits
|
||||
if (!(any_hits & 0x80808080))
|
||||
return 0;
|
||||
|
||||
// find soonest scanline with 8 or more sprites
|
||||
while ( true )
|
||||
{
|
||||
unsigned long const mask = 0x80808080 + zero;
|
||||
// find soonest scanline with 8 or more sprites
|
||||
while (true)
|
||||
{
|
||||
unsigned long const mask = 0x80808080 + zero;
|
||||
|
||||
// check four at a time
|
||||
uint8_t* pos = &sprite_max_scanlines [scanline];
|
||||
unsigned long n = *((uint32_t*)pos);
|
||||
while ( 1 )
|
||||
{
|
||||
unsigned long x = n & mask;
|
||||
pos += 4;
|
||||
n = *((uint32_t*)pos);
|
||||
if ( x )
|
||||
break;
|
||||
}
|
||||
// check four at a time
|
||||
uint8_t *pos = &sprite_max_scanlines[scanline];
|
||||
unsigned long n = *((uint32_t *)pos);
|
||||
while (1)
|
||||
{
|
||||
unsigned long x = n & mask;
|
||||
pos += 4;
|
||||
n = *((uint32_t *)pos);
|
||||
if (x)
|
||||
break;
|
||||
}
|
||||
|
||||
int height = sprite_height();
|
||||
int remain = 8;
|
||||
int i = 0;
|
||||
int height = sprite_height();
|
||||
int remain = 8;
|
||||
int i = 0;
|
||||
|
||||
// find which of the four
|
||||
pos -= 3 + (pos [-4] >> 7 & 1);
|
||||
pos += 1 - (*pos >> 7 & 1);
|
||||
pos += 1 - (*pos >> 7 & 1);
|
||||
// find which of the four
|
||||
pos -= 3 + (pos[-4] >> 7 & 1);
|
||||
pos += 1 - (*pos >> 7 & 1);
|
||||
pos += 1 - (*pos >> 7 & 1);
|
||||
|
||||
scanline = pos - sprite_max_scanlines;
|
||||
if ( scanline >= max_scanline_count )
|
||||
break;
|
||||
scanline = pos - sprite_max_scanlines;
|
||||
if (scanline >= max_scanline_count)
|
||||
break;
|
||||
|
||||
// find time that max sprites flag is set (or that it won't be set)
|
||||
do
|
||||
{
|
||||
int relative = scanline - spr_ram [i];
|
||||
i += 4;
|
||||
if ( (unsigned) relative < (unsigned) height && !--remain )
|
||||
{
|
||||
// now use screwey search for 9th sprite
|
||||
int offset = 0;
|
||||
while ( i < 0x100 )
|
||||
{
|
||||
int relative = scanline - spr_ram [i + offset];
|
||||
//dprintf( "Checking sprite %d [%d]\n", i / 4, offset );
|
||||
i += 4;
|
||||
offset = (offset + 1) & 3;
|
||||
if ( (unsigned) relative < (unsigned) height )
|
||||
{
|
||||
//dprintf( "sprite max on scanline %d\n", scanline );
|
||||
return scanline * scanline_len + (unsigned) i / 2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
while ( i < 0x100 );
|
||||
scanline++;
|
||||
}
|
||||
// find time that max sprites flag is set (or that it won't be set)
|
||||
do
|
||||
{
|
||||
int relative = scanline - spr_ram[i];
|
||||
i += 4;
|
||||
if ((unsigned)relative < (unsigned)height && !--remain)
|
||||
{
|
||||
// now use screwey search for 9th sprite
|
||||
int offset = 0;
|
||||
while (i < 0x100)
|
||||
{
|
||||
int relative = scanline - spr_ram[i + offset];
|
||||
// dprintf( "Checking sprite %d [%d]\n", i / 4, offset );
|
||||
i += 4;
|
||||
offset = (offset + 1) & 3;
|
||||
if ((unsigned)relative < (unsigned)height)
|
||||
{
|
||||
// dprintf( "sprite max on scanline %d\n", scanline );
|
||||
return scanline * scanline_len + (unsigned)i / 2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
} while (i < 0x100);
|
||||
scanline++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -5,236 +5,237 @@
|
|||
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
struct ppu_state_t
|
||||
{
|
||||
uint8_t w2000; // control
|
||||
uint8_t w2001; // control
|
||||
uint8_t r2002; // status
|
||||
uint8_t w2003; // sprite ram addr
|
||||
uint8_t r2007; // vram read buffer
|
||||
uint8_t second_write; // next write to $2005/$2006 is second since last $2002 read
|
||||
uint16_t vram_addr; // loopy_v
|
||||
uint16_t vram_temp; // loopy_t
|
||||
uint8_t pixel_x; // fine-scroll (0-7)
|
||||
uint8_t unused;
|
||||
uint8_t palette [0x20]; // entries $10, $14, $18, $1c should be ignored
|
||||
uint16_t decay_low;
|
||||
uint16_t decay_high;
|
||||
uint8_t open_bus;
|
||||
uint8_t unused2[3];
|
||||
uint8_t w2000; // control
|
||||
uint8_t w2001; // control
|
||||
uint8_t r2002; // status
|
||||
uint8_t w2003; // sprite ram addr
|
||||
uint8_t r2007; // vram read buffer
|
||||
uint8_t second_write; // next write to $2005/$2006 is second since last $2002 read
|
||||
uint16_t vram_addr; // loopy_v
|
||||
uint16_t vram_temp; // loopy_t
|
||||
uint8_t pixel_x; // fine-scroll (0-7)
|
||||
uint8_t unused;
|
||||
uint8_t palette[0x20]; // entries $10, $14, $18, $1c should be ignored
|
||||
uint16_t decay_low;
|
||||
uint16_t decay_high;
|
||||
uint8_t open_bus;
|
||||
uint8_t unused2[3];
|
||||
};
|
||||
static_assert( sizeof (ppu_state_t) == 20 + 0x20 );
|
||||
static_assert(sizeof(ppu_state_t) == 20 + 0x20);
|
||||
|
||||
class Nes_Ppu_Impl : public ppu_state_t {
|
||||
public:
|
||||
Nes_Ppu_Impl();
|
||||
~Nes_Ppu_Impl();
|
||||
class Nes_Ppu_Impl : public ppu_state_t
|
||||
{
|
||||
public:
|
||||
Nes_Ppu_Impl();
|
||||
~Nes_Ppu_Impl();
|
||||
|
||||
void reset( bool full_reset );
|
||||
void reset(bool full_reset);
|
||||
|
||||
// Setup
|
||||
const char * open_chr( const uint8_t*, long size );
|
||||
void rebuild_chr( unsigned long begin, unsigned long end );
|
||||
void close_chr();
|
||||
// Setup
|
||||
const char *open_chr(const uint8_t *, long size);
|
||||
void rebuild_chr(unsigned long begin, unsigned long end);
|
||||
void close_chr();
|
||||
|
||||
static const uint16_t image_width = 256;
|
||||
static const uint16_t image_height = 240;
|
||||
static const uint16_t image_left = 8;
|
||||
static const uint16_t buffer_width = image_width + 16;
|
||||
static const uint16_t buffer_height = image_height;
|
||||
enum { spr_ram_size = 0x100 };
|
||||
static const uint16_t image_width = 256;
|
||||
static const uint16_t image_height = 240;
|
||||
static const uint16_t image_left = 8;
|
||||
static const uint16_t buffer_width = image_width + 16;
|
||||
static const uint16_t buffer_height = image_height;
|
||||
enum
|
||||
{
|
||||
spr_ram_size = 0x100
|
||||
};
|
||||
|
||||
uint8_t* nt_banks [4];
|
||||
bool chr_is_writable;
|
||||
long chr_size;
|
||||
uint8_t *nt_banks[4];
|
||||
bool chr_is_writable;
|
||||
long chr_size;
|
||||
|
||||
int write_2007( int );
|
||||
int write_2007(int);
|
||||
|
||||
// Host palette
|
||||
static const uint8_t palette_increment = 64;
|
||||
short* host_palette;
|
||||
int palette_begin;
|
||||
int max_palette_size;
|
||||
int palette_size; // set after frame is rendered
|
||||
// Host palette
|
||||
static const uint8_t palette_increment = 64;
|
||||
short *host_palette;
|
||||
int palette_begin;
|
||||
int max_palette_size;
|
||||
int palette_size; // set after frame is rendered
|
||||
|
||||
// Mapping
|
||||
static const uint16_t vaddr_clock_mask = 0x1000;
|
||||
void set_nt_banks( int bank0, int bank1, int bank2, int bank3 );
|
||||
void set_chr_bank( int addr, int size, long data );
|
||||
void set_chr_bank_ex( int addr, int size, long data );
|
||||
// Mapping
|
||||
static const uint16_t vaddr_clock_mask = 0x1000;
|
||||
void set_nt_banks(int bank0, int bank1, int bank2, int bank3);
|
||||
void set_chr_bank(int addr, int size, long data);
|
||||
void set_chr_bank_ex(int addr, int size, long data);
|
||||
|
||||
// Nametable and CHR RAM
|
||||
static const uint16_t nt_ram_size = 0x1000;
|
||||
static const uint16_t chr_addr_size = 0x2000;
|
||||
static const uint8_t bytes_per_tile = 16;
|
||||
static const uint16_t chr_tile_count = chr_addr_size / bytes_per_tile;
|
||||
static const uint8_t mini_offscreen_height = 16; // double-height sprite
|
||||
// Nametable and CHR RAM
|
||||
static const uint16_t nt_ram_size = 0x1000;
|
||||
static const uint16_t chr_addr_size = 0x2000;
|
||||
static const uint8_t bytes_per_tile = 16;
|
||||
static const uint16_t chr_tile_count = chr_addr_size / bytes_per_tile;
|
||||
static const uint8_t mini_offscreen_height = 16; // double-height sprite
|
||||
|
||||
struct impl_t
|
||||
{
|
||||
uint8_t nt_ram [nt_ram_size];
|
||||
uint8_t chr_ram [chr_addr_size];
|
||||
union {
|
||||
uint32_t clip_buf [256 * 2];
|
||||
uint8_t mini_offscreen [buffer_width * mini_offscreen_height];
|
||||
};
|
||||
};
|
||||
impl_t* impl;
|
||||
struct impl_t
|
||||
{
|
||||
uint8_t nt_ram[nt_ram_size];
|
||||
uint8_t chr_ram[chr_addr_size];
|
||||
union
|
||||
{
|
||||
uint32_t clip_buf[256 * 2];
|
||||
uint8_t mini_offscreen[buffer_width * mini_offscreen_height];
|
||||
};
|
||||
};
|
||||
impl_t *impl;
|
||||
|
||||
static const uint16_t scanline_len = 341;
|
||||
static const uint16_t scanline_len = 341;
|
||||
|
||||
uint8_t* getSpriteRAM () { return spr_ram; }
|
||||
uint16_t getSpriteRAMSize () { return spr_ram_size; }
|
||||
uint8_t spr_ram [spr_ram_size];
|
||||
void all_tiles_modified();
|
||||
protected:
|
||||
uint8_t *getSpriteRAM() { return spr_ram; }
|
||||
uint16_t getSpriteRAMSize() { return spr_ram_size; }
|
||||
uint8_t spr_ram[spr_ram_size];
|
||||
void all_tiles_modified();
|
||||
|
||||
void begin_frame();
|
||||
void run_hblank( int );
|
||||
int sprite_height() const { return (w2000 >> 2 & 8) + 8; }
|
||||
protected:
|
||||
void begin_frame();
|
||||
void run_hblank(int);
|
||||
int sprite_height() const { return (w2000 >> 2 & 8) + 8; }
|
||||
|
||||
protected: //friend class Nes_Ppu; private:
|
||||
protected: // friend class Nes_Ppu; private:
|
||||
int addr_inc; // pre-calculated $2007 increment (based on w2001 & 0x04)
|
||||
int read_2007(int addr);
|
||||
|
||||
int addr_inc; // pre-calculated $2007 increment (based on w2001 & 0x04)
|
||||
int read_2007( int addr );
|
||||
static const uint16_t last_sprite_max_scanline = 240;
|
||||
long recalc_sprite_max(int scanline);
|
||||
int first_opaque_sprite_line();
|
||||
|
||||
static const uint16_t last_sprite_max_scanline = 240;
|
||||
long recalc_sprite_max( int scanline );
|
||||
int first_opaque_sprite_line();
|
||||
protected: // friend class Nes_Ppu_Rendering; private:
|
||||
unsigned long palette_offset;
|
||||
int palette_changed;
|
||||
void capture_palette();
|
||||
|
||||
protected: //friend class Nes_Ppu_Rendering; private:
|
||||
bool any_tiles_modified;
|
||||
void update_tiles(int first_tile);
|
||||
|
||||
unsigned long palette_offset;
|
||||
int palette_changed;
|
||||
void capture_palette();
|
||||
typedef uint32_t cache_t;
|
||||
typedef cache_t cached_tile_t[4];
|
||||
cached_tile_t const &get_bg_tile(int index);
|
||||
cached_tile_t const &get_sprite_tile(uint8_t const *sprite);
|
||||
uint8_t *get_nametable(int addr) { return nt_banks[addr >> 10 & 3]; };
|
||||
|
||||
bool any_tiles_modified;
|
||||
void update_tiles( int first_tile );
|
||||
private:
|
||||
static int map_palette(int addr);
|
||||
int sprite_tile_index(uint8_t const *sprite) const;
|
||||
|
||||
typedef uint32_t cache_t;
|
||||
typedef cache_t cached_tile_t [4];
|
||||
cached_tile_t const& get_bg_tile( int index );
|
||||
cached_tile_t const& get_sprite_tile( uint8_t const* sprite );
|
||||
uint8_t* get_nametable( int addr ) { return nt_banks [addr >> 10 & 3]; };
|
||||
// Mapping
|
||||
static const uint16_t chr_page_size = 0x400;
|
||||
long chr_pages[chr_addr_size / chr_page_size];
|
||||
long chr_pages_ex[chr_addr_size / chr_page_size];
|
||||
long map_chr_addr(unsigned a) /*const*/
|
||||
{
|
||||
if (!mmc24_enabled)
|
||||
return chr_pages[a / chr_page_size] + a;
|
||||
|
||||
private:
|
||||
int page = a >> 12 & 1;
|
||||
int newval0 = (a & 0xff0) != 0xfd0;
|
||||
int newval1 = (a & 0xff0) == 0xfe0;
|
||||
|
||||
static int map_palette( int addr );
|
||||
int sprite_tile_index( uint8_t const* sprite ) const;
|
||||
long ret;
|
||||
if (mmc24_latched[page])
|
||||
ret = chr_pages_ex[a / chr_page_size] + a;
|
||||
else
|
||||
ret = chr_pages[a / chr_page_size] + a;
|
||||
|
||||
// Mapping
|
||||
static const uint16_t chr_page_size = 0x400;
|
||||
long chr_pages [chr_addr_size / chr_page_size];
|
||||
long chr_pages_ex [chr_addr_size / chr_page_size];
|
||||
long map_chr_addr( unsigned a ) /*const*/
|
||||
{
|
||||
if (!mmc24_enabled)
|
||||
return chr_pages [a / chr_page_size] + a;
|
||||
mmc24_latched[page] &= newval0;
|
||||
mmc24_latched[page] |= newval1;
|
||||
|
||||
int page = a >> 12 & 1;
|
||||
int newval0 = (a & 0xff0) != 0xfd0;
|
||||
int newval1 = (a & 0xff0) == 0xfe0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
long ret;
|
||||
if (mmc24_latched[page])
|
||||
ret = chr_pages_ex [a / chr_page_size] + a;
|
||||
else
|
||||
ret = chr_pages [a / chr_page_size] + a;
|
||||
bool mmc24_enabled;
|
||||
uint8_t mmc24_latched[2];
|
||||
|
||||
mmc24_latched[page] &= newval0;
|
||||
mmc24_latched[page] |= newval1;
|
||||
// CHR data
|
||||
uint8_t const *chr_data; // points to chr ram when there is no read-only data
|
||||
uint8_t *chr_ram; // always points to impl->chr_ram; makes write_2007() faster
|
||||
uint8_t const *map_chr(int addr) { return &chr_data[map_chr_addr(addr)]; }
|
||||
|
||||
return ret;
|
||||
}
|
||||
// CHR cache
|
||||
cached_tile_t *tile_cache;
|
||||
cached_tile_t *flipped_tiles;
|
||||
uint8_t *tile_cache_mem;
|
||||
union
|
||||
{
|
||||
uint8_t modified_tiles[chr_tile_count / 8];
|
||||
uint32_t align_;
|
||||
};
|
||||
|
||||
|
||||
bool mmc24_enabled;
|
||||
uint8_t mmc24_latched [2];
|
||||
|
||||
// CHR data
|
||||
uint8_t const* chr_data; // points to chr ram when there is no read-only data
|
||||
uint8_t* chr_ram; // always points to impl->chr_ram; makes write_2007() faster
|
||||
uint8_t const* map_chr( int addr ) { return &chr_data [map_chr_addr( addr )]; }
|
||||
|
||||
// CHR cache
|
||||
cached_tile_t* tile_cache;
|
||||
cached_tile_t* flipped_tiles;
|
||||
uint8_t* tile_cache_mem;
|
||||
union {
|
||||
uint8_t modified_tiles [chr_tile_count / 8];
|
||||
uint32_t align_;
|
||||
};
|
||||
|
||||
void update_tile( int index );
|
||||
void update_tile(int index);
|
||||
};
|
||||
|
||||
inline void Nes_Ppu_Impl::set_nt_banks( int bank0, int bank1, int bank2, int bank3 )
|
||||
inline void Nes_Ppu_Impl::set_nt_banks(int bank0, int bank1, int bank2, int bank3)
|
||||
{
|
||||
uint8_t* nt_ram = impl->nt_ram;
|
||||
nt_banks [0] = &nt_ram [bank0 * 0x400];
|
||||
nt_banks [1] = &nt_ram [bank1 * 0x400];
|
||||
nt_banks [2] = &nt_ram [bank2 * 0x400];
|
||||
nt_banks [3] = &nt_ram [bank3 * 0x400];
|
||||
uint8_t *nt_ram = impl->nt_ram;
|
||||
nt_banks[0] = &nt_ram[bank0 * 0x400];
|
||||
nt_banks[1] = &nt_ram[bank1 * 0x400];
|
||||
nt_banks[2] = &nt_ram[bank2 * 0x400];
|
||||
nt_banks[3] = &nt_ram[bank3 * 0x400];
|
||||
}
|
||||
|
||||
inline int Nes_Ppu_Impl::map_palette( int addr )
|
||||
inline int Nes_Ppu_Impl::map_palette(int addr)
|
||||
{
|
||||
if ( (addr & 3) == 0 )
|
||||
addr &= 0x0f; // 0x10, 0x14, 0x18, 0x1c map to 0x00, 0x04, 0x08, 0x0c
|
||||
return addr & 0x1f;
|
||||
if ((addr & 3) == 0)
|
||||
addr &= 0x0f; // 0x10, 0x14, 0x18, 0x1c map to 0x00, 0x04, 0x08, 0x0c
|
||||
return addr & 0x1f;
|
||||
}
|
||||
|
||||
inline int Nes_Ppu_Impl::sprite_tile_index( uint8_t const* sprite ) const
|
||||
inline int Nes_Ppu_Impl::sprite_tile_index(uint8_t const *sprite) const
|
||||
{
|
||||
int tile = sprite [1] + (w2000 << 5 & 0x100);
|
||||
if ( w2000 & 0x20 )
|
||||
tile = (tile & 1) * 0x100 + (tile & 0xfe);
|
||||
return tile;
|
||||
int tile = sprite[1] + (w2000 << 5 & 0x100);
|
||||
if (w2000 & 0x20)
|
||||
tile = (tile & 1) * 0x100 + (tile & 0xfe);
|
||||
return tile;
|
||||
}
|
||||
|
||||
inline int Nes_Ppu_Impl::write_2007( int data )
|
||||
inline int Nes_Ppu_Impl::write_2007(int data)
|
||||
{
|
||||
int addr = vram_addr;
|
||||
uint8_t * chr_ram = this->chr_ram; // pre-read
|
||||
int changed = addr + addr_inc;
|
||||
unsigned const divisor = bytes_per_tile * 8;
|
||||
int mod_index = (unsigned) addr / divisor % (0x4000 / divisor);
|
||||
vram_addr = changed;
|
||||
changed ^= addr;
|
||||
addr &= 0x3fff;
|
||||
int addr = vram_addr;
|
||||
uint8_t *chr_ram = this->chr_ram; // pre-read
|
||||
int changed = addr + addr_inc;
|
||||
unsigned const divisor = bytes_per_tile * 8;
|
||||
int mod_index = (unsigned)addr / divisor % (0x4000 / divisor);
|
||||
vram_addr = changed;
|
||||
changed ^= addr;
|
||||
addr &= 0x3fff;
|
||||
|
||||
// use index into modified_tiles [] since it's calculated sooner than addr is masked
|
||||
if ( (unsigned) mod_index < 0x2000 / divisor )
|
||||
{
|
||||
// Avoid overhead of checking for read-only CHR; if that is the case,
|
||||
// this modification will be ignored.
|
||||
int mod = modified_tiles [mod_index];
|
||||
chr_ram [addr] = data;
|
||||
any_tiles_modified = true;
|
||||
modified_tiles [mod_index] = mod | (1 << ((unsigned) addr / bytes_per_tile % 8));
|
||||
}
|
||||
else if ( addr < 0x3f00 )
|
||||
{
|
||||
get_nametable( addr ) [addr & 0x3ff] = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
data &= 0x3f;
|
||||
uint8_t& entry = palette [map_palette( addr )];
|
||||
int changed = entry ^ data;
|
||||
entry = data;
|
||||
if ( changed )
|
||||
palette_changed = 0x18;
|
||||
}
|
||||
// use index into modified_tiles [] since it's calculated sooner than addr is masked
|
||||
if ((unsigned)mod_index < 0x2000 / divisor)
|
||||
{
|
||||
// Avoid overhead of checking for read-only CHR; if that is the case,
|
||||
// this modification will be ignored.
|
||||
int mod = modified_tiles[mod_index];
|
||||
chr_ram[addr] = data;
|
||||
any_tiles_modified = true;
|
||||
modified_tiles[mod_index] = mod | (1 << ((unsigned)addr / bytes_per_tile % 8));
|
||||
}
|
||||
else if (addr < 0x3f00)
|
||||
{
|
||||
get_nametable(addr)[addr & 0x3ff] = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
data &= 0x3f;
|
||||
uint8_t &entry = palette[map_palette(addr)];
|
||||
int changed = entry ^ data;
|
||||
entry = data;
|
||||
if (changed)
|
||||
palette_changed = 0x18;
|
||||
}
|
||||
|
||||
return changed;
|
||||
return changed;
|
||||
}
|
||||
|
||||
inline void Nes_Ppu_Impl::begin_frame()
|
||||
{
|
||||
palette_changed = 0x18;
|
||||
palette_size = 0;
|
||||
palette_offset = palette_begin * 0x01010101;
|
||||
addr_inc = w2000 & 4 ? 32 : 1;
|
||||
palette_changed = 0x18;
|
||||
palette_size = 0;
|
||||
palette_offset = palette_begin * 0x01010101;
|
||||
addr_inc = w2000 & 4 ? 32 : 1;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// Nes_Emu 0.7.0. http://www.slack.net/~ant/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
#include "Nes_Ppu_Rendering.hpp"
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
|
||||
/* Copyright (C) 2004-2006 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
|
@ -20,491 +20,484 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|||
|
||||
static unsigned zero = 0; // helps CodeWarrior optimizer when added to constants
|
||||
|
||||
inline Nes_Ppu_Impl::cached_tile_t const&
|
||||
Nes_Ppu_Impl::get_sprite_tile( uint8_t const* sprite )
|
||||
inline Nes_Ppu_Impl::cached_tile_t const &
|
||||
Nes_Ppu_Impl::get_sprite_tile(uint8_t const *sprite)
|
||||
{
|
||||
cached_tile_t* tiles = tile_cache;
|
||||
if ( sprite [2] & 0x40 )
|
||||
tiles = flipped_tiles;
|
||||
int index = sprite_tile_index( sprite );
|
||||
cached_tile_t *tiles = tile_cache;
|
||||
if (sprite[2] & 0x40)
|
||||
tiles = flipped_tiles;
|
||||
int index = sprite_tile_index(sprite);
|
||||
|
||||
// use index directly, since cached tile is same size as native tile
|
||||
static_assert( sizeof (cached_tile_t) == bytes_per_tile );
|
||||
return *(Nes_Ppu_Impl::cached_tile_t*)
|
||||
((uint8_t*) tiles + map_chr_addr( index * bytes_per_tile ));
|
||||
// use index directly, since cached tile is same size as native tile
|
||||
static_assert(sizeof(cached_tile_t) == bytes_per_tile);
|
||||
return *(Nes_Ppu_Impl::cached_tile_t *)((uint8_t *)tiles + map_chr_addr(index * bytes_per_tile));
|
||||
}
|
||||
|
||||
inline Nes_Ppu_Impl::cached_tile_t const& Nes_Ppu_Impl::get_bg_tile( int index )
|
||||
inline Nes_Ppu_Impl::cached_tile_t const &Nes_Ppu_Impl::get_bg_tile(int index)
|
||||
{
|
||||
// use index directly, since cached tile is same size as native tile
|
||||
static_assert( sizeof (cached_tile_t) == bytes_per_tile );
|
||||
return *(Nes_Ppu_Impl::cached_tile_t*)
|
||||
((uint8_t*) tile_cache + map_chr_addr( index * bytes_per_tile ));
|
||||
// use index directly, since cached tile is same size as native tile
|
||||
static_assert(sizeof(cached_tile_t) == bytes_per_tile);
|
||||
return *(Nes_Ppu_Impl::cached_tile_t *)((uint8_t *)tile_cache + map_chr_addr(index * bytes_per_tile));
|
||||
}
|
||||
|
||||
// Fill
|
||||
|
||||
void Nes_Ppu_Rendering::fill_background( int count )
|
||||
void Nes_Ppu_Rendering::fill_background(int count)
|
||||
{
|
||||
ptrdiff_t const next_line = scanline_row_bytes - image_width;
|
||||
uint32_t* pixels = (uint32_t*) scanline_pixels;
|
||||
ptrdiff_t const next_line = scanline_row_bytes - image_width;
|
||||
uint32_t *pixels = (uint32_t *)scanline_pixels;
|
||||
|
||||
unsigned long fill = palette_offset;
|
||||
if ( (vram_addr & 0x3f00) == 0x3f00 )
|
||||
{
|
||||
// PPU uses current palette entry if addr is within palette ram
|
||||
int color = vram_addr & 0x1f;
|
||||
if ( !(color & 3) )
|
||||
color &= 0x0f;
|
||||
fill += color * 0x01010101;
|
||||
}
|
||||
unsigned long fill = palette_offset;
|
||||
if ((vram_addr & 0x3f00) == 0x3f00)
|
||||
{
|
||||
// PPU uses current palette entry if addr is within palette ram
|
||||
int color = vram_addr & 0x1f;
|
||||
if (!(color & 3))
|
||||
color &= 0x0f;
|
||||
fill += color * 0x01010101;
|
||||
}
|
||||
|
||||
for ( int n = count; n--; )
|
||||
{
|
||||
for ( int n = image_width / 16; n--; )
|
||||
{
|
||||
pixels [0] = fill;
|
||||
pixels [1] = fill;
|
||||
pixels [2] = fill;
|
||||
pixels [3] = fill;
|
||||
pixels += 4;
|
||||
}
|
||||
pixels = (uint32_t*) ((uint8_t*) pixels + next_line);
|
||||
}
|
||||
for (int n = count; n--;)
|
||||
{
|
||||
for (int n = image_width / 16; n--;)
|
||||
{
|
||||
pixels[0] = fill;
|
||||
pixels[1] = fill;
|
||||
pixels[2] = fill;
|
||||
pixels[3] = fill;
|
||||
pixels += 4;
|
||||
}
|
||||
pixels = (uint32_t *)((uint8_t *)pixels + next_line);
|
||||
}
|
||||
}
|
||||
|
||||
void Nes_Ppu_Rendering::clip_left( int count )
|
||||
void Nes_Ppu_Rendering::clip_left(int count)
|
||||
{
|
||||
ptrdiff_t next_line = scanline_row_bytes;
|
||||
uint8_t* p = scanline_pixels;
|
||||
unsigned long fill = palette_offset;
|
||||
ptrdiff_t next_line = scanline_row_bytes;
|
||||
uint8_t *p = scanline_pixels;
|
||||
unsigned long fill = palette_offset;
|
||||
|
||||
for ( int n = count; n--; )
|
||||
{
|
||||
((uint32_t*) p) [0] = fill;
|
||||
((uint32_t*) p) [1] = fill;
|
||||
p += next_line;
|
||||
}
|
||||
for (int n = count; n--;)
|
||||
{
|
||||
((uint32_t *)p)[0] = fill;
|
||||
((uint32_t *)p)[1] = fill;
|
||||
p += next_line;
|
||||
}
|
||||
}
|
||||
|
||||
void Nes_Ppu_Rendering::save_left( int count )
|
||||
void Nes_Ppu_Rendering::save_left(int count)
|
||||
{
|
||||
ptrdiff_t next_line = scanline_row_bytes;
|
||||
uint8_t* in = scanline_pixels;
|
||||
uint32_t* out = impl->clip_buf;
|
||||
ptrdiff_t next_line = scanline_row_bytes;
|
||||
uint8_t *in = scanline_pixels;
|
||||
uint32_t *out = impl->clip_buf;
|
||||
|
||||
for ( int n = count; n--; )
|
||||
{
|
||||
unsigned long in0 = ((uint32_t*) in) [0];
|
||||
unsigned long in1 = ((uint32_t*) in) [1];
|
||||
in += next_line;
|
||||
out [0] = in0;
|
||||
out [1] = in1;
|
||||
out += 2;
|
||||
}
|
||||
for (int n = count; n--;)
|
||||
{
|
||||
unsigned long in0 = ((uint32_t *)in)[0];
|
||||
unsigned long in1 = ((uint32_t *)in)[1];
|
||||
in += next_line;
|
||||
out[0] = in0;
|
||||
out[1] = in1;
|
||||
out += 2;
|
||||
}
|
||||
}
|
||||
|
||||
void Nes_Ppu_Rendering::restore_left( int count )
|
||||
void Nes_Ppu_Rendering::restore_left(int count)
|
||||
{
|
||||
ptrdiff_t next_line = scanline_row_bytes;
|
||||
uint8_t* out = scanline_pixels;
|
||||
uint32_t* in = impl->clip_buf;
|
||||
ptrdiff_t next_line = scanline_row_bytes;
|
||||
uint8_t *out = scanline_pixels;
|
||||
uint32_t *in = impl->clip_buf;
|
||||
|
||||
for ( int n = count; n--; )
|
||||
{
|
||||
unsigned long in0 = in [0];
|
||||
unsigned long in1 = in [1];
|
||||
in += 2;
|
||||
((uint32_t*) out) [0] = in0;
|
||||
((uint32_t*) out) [1] = in1;
|
||||
out += next_line;
|
||||
}
|
||||
for (int n = count; n--;)
|
||||
{
|
||||
unsigned long in0 = in[0];
|
||||
unsigned long in1 = in[1];
|
||||
in += 2;
|
||||
((uint32_t *)out)[0] = in0;
|
||||
((uint32_t *)out)[1] = in1;
|
||||
out += next_line;
|
||||
}
|
||||
}
|
||||
|
||||
// Background
|
||||
|
||||
void Nes_Ppu_Rendering::draw_background_( int remain )
|
||||
void Nes_Ppu_Rendering::draw_background_(int remain)
|
||||
{
|
||||
// Draws 'remain' background scanlines. Does not modify vram_addr.
|
||||
// Draws 'remain' background scanlines. Does not modify vram_addr.
|
||||
|
||||
int vram_addr = this->vram_addr & 0x7fff;
|
||||
uint8_t* row_pixels = scanline_pixels - pixel_x;
|
||||
int left_clip = (w2001 >> 1 & 1) ^ 1;
|
||||
row_pixels += left_clip * 8;
|
||||
do
|
||||
{
|
||||
// scanlines until next row
|
||||
int height = 8 - (vram_addr >> 12);
|
||||
if ( height > remain )
|
||||
height = remain;
|
||||
int vram_addr = this->vram_addr & 0x7fff;
|
||||
uint8_t *row_pixels = scanline_pixels - pixel_x;
|
||||
int left_clip = (w2001 >> 1 & 1) ^ 1;
|
||||
row_pixels += left_clip * 8;
|
||||
do
|
||||
{
|
||||
// scanlines until next row
|
||||
int height = 8 - (vram_addr >> 12);
|
||||
if (height > remain)
|
||||
height = remain;
|
||||
|
||||
// handle hscroll change before next scanline
|
||||
int hscroll_changed = (vram_addr ^ vram_temp) & 0x41f;
|
||||
int addr = vram_addr;
|
||||
if ( hscroll_changed )
|
||||
{
|
||||
vram_addr ^= hscroll_changed;
|
||||
height = 1; // hscroll will change after first line
|
||||
}
|
||||
remain -= height;
|
||||
// handle hscroll change before next scanline
|
||||
int hscroll_changed = (vram_addr ^ vram_temp) & 0x41f;
|
||||
int addr = vram_addr;
|
||||
if (hscroll_changed)
|
||||
{
|
||||
vram_addr ^= hscroll_changed;
|
||||
height = 1; // hscroll will change after first line
|
||||
}
|
||||
remain -= height;
|
||||
|
||||
// increment address for next row
|
||||
vram_addr += height << 12;
|
||||
if ( vram_addr & 0x8000 )
|
||||
{
|
||||
int y = (vram_addr + 0x20) & 0x3e0;
|
||||
vram_addr &= 0x7fff & ~0x3e0;
|
||||
if ( y == 30 * 0x20 )
|
||||
y = 0x800; // toggle vertical nametable
|
||||
vram_addr ^= y;
|
||||
}
|
||||
// increment address for next row
|
||||
vram_addr += height << 12;
|
||||
if (vram_addr & 0x8000)
|
||||
{
|
||||
int y = (vram_addr + 0x20) & 0x3e0;
|
||||
vram_addr &= 0x7fff & ~0x3e0;
|
||||
if (y == 30 * 0x20)
|
||||
y = 0x800; // toggle vertical nametable
|
||||
vram_addr ^= y;
|
||||
}
|
||||
|
||||
// nametable change usually occurs in middle of row
|
||||
uint8_t const* nametable = get_nametable( addr );
|
||||
uint8_t const* nametable2 = get_nametable( addr ^ 0x400 );
|
||||
int count2 = addr & 31;
|
||||
int count = 32 - count2 - left_clip;
|
||||
//if ( pixel_x )
|
||||
count2++;
|
||||
// nametable change usually occurs in middle of row
|
||||
uint8_t const *nametable = get_nametable(addr);
|
||||
uint8_t const *nametable2 = get_nametable(addr ^ 0x400);
|
||||
int count2 = addr & 31;
|
||||
int count = 32 - count2 - left_clip;
|
||||
// if ( pixel_x )
|
||||
count2++;
|
||||
|
||||
uint8_t const* attr_table = &nametable [0x3c0 | (addr >> 4 & 0x38)];
|
||||
int bg_bank = (w2000 << 4) & 0x100;
|
||||
addr += left_clip;
|
||||
uint8_t const *attr_table = &nametable[0x3c0 | (addr >> 4 & 0x38)];
|
||||
int bg_bank = (w2000 << 4) & 0x100;
|
||||
addr += left_clip;
|
||||
|
||||
// output pixels
|
||||
ptrdiff_t const row_bytes = scanline_row_bytes;
|
||||
uint8_t* pixels = row_pixels;
|
||||
row_pixels += height * row_bytes;
|
||||
// output pixels
|
||||
ptrdiff_t const row_bytes = scanline_row_bytes;
|
||||
uint8_t *pixels = row_pixels;
|
||||
row_pixels += height * row_bytes;
|
||||
|
||||
unsigned long const mask = 0x03030303 + zero;
|
||||
unsigned long const attrib_factor = 0x04040404 + zero;
|
||||
unsigned long const mask = 0x03030303 + zero;
|
||||
unsigned long const attrib_factor = 0x04040404 + zero;
|
||||
|
||||
const int fine_y = (height == 8) ? 0 : addr >> 12;
|
||||
const int clipped = (height == 8) ? false : true;
|
||||
addr &= 0x03ff;
|
||||
if (height == 8) height -= fine_y & 1;
|
||||
const int fine_y = (height == 8) ? 0 : addr >> 12;
|
||||
const int clipped = (height == 8) ? false : true;
|
||||
addr &= 0x03ff;
|
||||
if (height == 8) height -= fine_y & 1;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
while ( count-- )
|
||||
{
|
||||
int attrib = attr_table [addr >> 2 & 0x07];
|
||||
attrib >>= (addr >> 4 & 4) | (addr & 2);
|
||||
unsigned long offset = (attrib & 3) * attrib_factor + this->palette_offset;
|
||||
while (true)
|
||||
{
|
||||
while (count--)
|
||||
{
|
||||
int attrib = attr_table[addr >> 2 & 0x07];
|
||||
attrib >>= (addr >> 4 & 4) | (addr & 2);
|
||||
unsigned long offset = (attrib & 3) * attrib_factor + this->palette_offset;
|
||||
|
||||
// draw one tile
|
||||
cache_t const* lines = this->get_bg_tile( nametable [addr] + bg_bank );
|
||||
uint8_t* p = pixels;
|
||||
addr++;
|
||||
pixels += 8; // next tile
|
||||
// draw one tile
|
||||
cache_t const *lines = this->get_bg_tile(nametable[addr] + bg_bank);
|
||||
uint8_t *p = pixels;
|
||||
addr++;
|
||||
pixels += 8; // next tile
|
||||
|
||||
if ( !clipped )
|
||||
{
|
||||
// optimal case: no clipping
|
||||
for ( int n = 4; n--; )
|
||||
{
|
||||
unsigned long line = *lines++;
|
||||
((uint32_t*) p) [0] = (line >> 4 & mask) + offset;
|
||||
((uint32_t*) p) [1] = (line & mask) + offset;
|
||||
p += row_bytes;
|
||||
((uint32_t*) p) [0] = (line >> 6 & mask) + offset;
|
||||
((uint32_t*) p) [1] = (line >> 2 & mask) + offset;
|
||||
p += row_bytes;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lines += fine_y >> 1;
|
||||
if (!clipped)
|
||||
{
|
||||
// optimal case: no clipping
|
||||
for (int n = 4; n--;)
|
||||
{
|
||||
unsigned long line = *lines++;
|
||||
((uint32_t *)p)[0] = (line >> 4 & mask) + offset;
|
||||
((uint32_t *)p)[1] = (line & mask) + offset;
|
||||
p += row_bytes;
|
||||
((uint32_t *)p)[0] = (line >> 6 & mask) + offset;
|
||||
((uint32_t *)p)[1] = (line >> 2 & mask) + offset;
|
||||
p += row_bytes;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lines += fine_y >> 1;
|
||||
|
||||
if ( fine_y & 1 )
|
||||
{
|
||||
unsigned long line = *lines++;
|
||||
((uint32_t*) p) [0] = (line >> 6 & mask) + offset;
|
||||
((uint32_t*) p) [1] = (line >> 2 & mask) + offset;
|
||||
p += row_bytes;
|
||||
}
|
||||
if (fine_y & 1)
|
||||
{
|
||||
unsigned long line = *lines++;
|
||||
((uint32_t *)p)[0] = (line >> 6 & mask) + offset;
|
||||
((uint32_t *)p)[1] = (line >> 2 & mask) + offset;
|
||||
p += row_bytes;
|
||||
}
|
||||
|
||||
for ( int n = height >> 1; n--; )
|
||||
{
|
||||
unsigned long line = *lines++;
|
||||
((uint32_t*) p) [0] = (line >> 4 & mask) + offset;
|
||||
((uint32_t*) p) [1] = (line & mask) + offset;
|
||||
p += row_bytes;
|
||||
((uint32_t*) p) [0] = (line >> 6 & mask) + offset;
|
||||
((uint32_t*) p) [1] = (line >> 2 & mask) + offset;
|
||||
p += row_bytes;
|
||||
}
|
||||
for (int n = height >> 1; n--;)
|
||||
{
|
||||
unsigned long line = *lines++;
|
||||
((uint32_t *)p)[0] = (line >> 4 & mask) + offset;
|
||||
((uint32_t *)p)[1] = (line & mask) + offset;
|
||||
p += row_bytes;
|
||||
((uint32_t *)p)[0] = (line >> 6 & mask) + offset;
|
||||
((uint32_t *)p)[1] = (line >> 2 & mask) + offset;
|
||||
p += row_bytes;
|
||||
}
|
||||
|
||||
if ( height & 1 )
|
||||
{
|
||||
unsigned long line = *lines;
|
||||
((uint32_t*) p) [0] = (line >> 4 & mask) + offset;
|
||||
((uint32_t*) p) [1] = (line & mask) + offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (height & 1)
|
||||
{
|
||||
unsigned long line = *lines;
|
||||
((uint32_t *)p)[0] = (line >> 4 & mask) + offset;
|
||||
((uint32_t *)p)[1] = (line & mask) + offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
count = count2;
|
||||
count2 = 0;
|
||||
addr -= 32;
|
||||
attr_table = attr_table - nametable + nametable2;
|
||||
nametable = nametable2;
|
||||
if ( !count )
|
||||
break;
|
||||
}
|
||||
count = count2;
|
||||
count2 = 0;
|
||||
addr -= 32;
|
||||
attr_table = attr_table - nametable + nametable2;
|
||||
nametable = nametable2;
|
||||
if (!count)
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
while ( remain );
|
||||
} while (remain);
|
||||
}
|
||||
|
||||
// Sprites
|
||||
|
||||
void Nes_Ppu_Rendering::draw_sprites_( int begin, int end )
|
||||
void Nes_Ppu_Rendering::draw_sprites_(int begin, int end)
|
||||
{
|
||||
// Draws sprites on scanlines begin through end - 1. Handles clipping.
|
||||
// Draws sprites on scanlines begin through end - 1. Handles clipping.
|
||||
|
||||
int const sprite_height = this->sprite_height();
|
||||
int end_minus_one = end - 1;
|
||||
int begin_minus_one = begin - 1;
|
||||
int index = 0;
|
||||
do
|
||||
{
|
||||
uint8_t const* sprite = &spr_ram [index];
|
||||
index += 4;
|
||||
int const sprite_height = this->sprite_height();
|
||||
int end_minus_one = end - 1;
|
||||
int begin_minus_one = begin - 1;
|
||||
int index = 0;
|
||||
do
|
||||
{
|
||||
uint8_t const *sprite = &spr_ram[index];
|
||||
index += 4;
|
||||
|
||||
// find if sprite is visible
|
||||
int top_minus_one = sprite [0];
|
||||
int visible = end_minus_one - top_minus_one;
|
||||
if ( visible <= 0 )
|
||||
continue; // off bottom
|
||||
// find if sprite is visible
|
||||
int top_minus_one = sprite[0];
|
||||
int visible = end_minus_one - top_minus_one;
|
||||
if (visible <= 0)
|
||||
continue; // off bottom
|
||||
|
||||
// quickly determine whether sprite is unclipped
|
||||
int neg_vis = visible - sprite_height;
|
||||
int neg_skip = top_minus_one - begin_minus_one;
|
||||
if ( (neg_skip | neg_vis) >= 0 ) // neg_skip >= 0 && neg_vis >= 0
|
||||
{
|
||||
int const skip = 0;
|
||||
int visible = sprite_height;
|
||||
// quickly determine whether sprite is unclipped
|
||||
int neg_vis = visible - sprite_height;
|
||||
int neg_skip = top_minus_one - begin_minus_one;
|
||||
if ((neg_skip | neg_vis) >= 0) // neg_skip >= 0 && neg_vis >= 0
|
||||
{
|
||||
int const skip = 0;
|
||||
int visible = sprite_height;
|
||||
|
||||
#define CLIPPED 0
|
||||
#include "Nes_Ppu_Sprites.hpp"
|
||||
}
|
||||
else
|
||||
{
|
||||
// clipped
|
||||
if ( neg_vis > 0 )
|
||||
visible -= neg_vis;
|
||||
#define CLIPPED 0
|
||||
#include "Nes_Ppu_Sprites.hpp"
|
||||
}
|
||||
else
|
||||
{
|
||||
// clipped
|
||||
if (neg_vis > 0)
|
||||
visible -= neg_vis;
|
||||
|
||||
if ( neg_skip > 0 )
|
||||
neg_skip = 0;
|
||||
visible += neg_skip;
|
||||
if (neg_skip > 0)
|
||||
neg_skip = 0;
|
||||
visible += neg_skip;
|
||||
|
||||
if ( visible <= 0 )
|
||||
continue; // off top
|
||||
if (visible <= 0)
|
||||
continue; // off top
|
||||
|
||||
int skip = -neg_skip;
|
||||
int skip = -neg_skip;
|
||||
|
||||
//dprintf( "begin: %d, end: %d, top: %d, skip: %d, visible: %d\n",
|
||||
// begin, end, top_minus_one + 1, skip, visible );
|
||||
// dprintf( "begin: %d, end: %d, top: %d, skip: %d, visible: %d\n",
|
||||
// begin, end, top_minus_one + 1, skip, visible );
|
||||
|
||||
#define CLIPPED 1
|
||||
#include "Nes_Ppu_Sprites.hpp"
|
||||
}
|
||||
}
|
||||
while ( index < 0x100 );
|
||||
#define CLIPPED 1
|
||||
#include "Nes_Ppu_Sprites.hpp"
|
||||
}
|
||||
} while (index < 0x100);
|
||||
}
|
||||
|
||||
void Nes_Ppu_Rendering::check_sprite_hit( int begin, int end )
|
||||
void Nes_Ppu_Rendering::check_sprite_hit(int begin, int end)
|
||||
{
|
||||
// Checks for sprite 0 hit on scanlines begin through end - 1.
|
||||
// Updates sprite_hit_found. Background (but not sprites) must have
|
||||
// already been rendered for the scanlines.
|
||||
// Checks for sprite 0 hit on scanlines begin through end - 1.
|
||||
// Updates sprite_hit_found. Background (but not sprites) must have
|
||||
// already been rendered for the scanlines.
|
||||
|
||||
// clip
|
||||
int top = spr_ram [0] + 1;
|
||||
int skip = begin - top;
|
||||
if ( skip < 0 )
|
||||
skip = 0;
|
||||
// clip
|
||||
int top = spr_ram[0] + 1;
|
||||
int skip = begin - top;
|
||||
if (skip < 0)
|
||||
skip = 0;
|
||||
|
||||
top += skip;
|
||||
int visible = end - top;
|
||||
if ( visible <= 0 )
|
||||
return; // not visible
|
||||
top += skip;
|
||||
int visible = end - top;
|
||||
if (visible <= 0)
|
||||
return; // not visible
|
||||
|
||||
int height = sprite_height();
|
||||
if ( visible >= height )
|
||||
{
|
||||
visible = height;
|
||||
sprite_hit_found = -1; // signal that no more hit checking will take place
|
||||
}
|
||||
int height = sprite_height();
|
||||
if (visible >= height)
|
||||
{
|
||||
visible = height;
|
||||
sprite_hit_found = -1; // signal that no more hit checking will take place
|
||||
}
|
||||
|
||||
// pixels
|
||||
ptrdiff_t next_row = this->scanline_row_bytes;
|
||||
uint8_t const* bg = this->scanline_pixels + spr_ram [3] + (top - begin) * next_row;
|
||||
cache_t const* lines = get_sprite_tile( spr_ram );
|
||||
// pixels
|
||||
ptrdiff_t next_row = this->scanline_row_bytes;
|
||||
uint8_t const *bg = this->scanline_pixels + spr_ram[3] + (top - begin) * next_row;
|
||||
cache_t const *lines = get_sprite_tile(spr_ram);
|
||||
|
||||
// left edge clipping
|
||||
int start_x = 0;
|
||||
if ( spr_ram [3] < 8 && (w2001 & 0x01e) != 0x1e )
|
||||
{
|
||||
if ( spr_ram [3] == 0 )
|
||||
return; // won't hit
|
||||
start_x = 8 - spr_ram [3];
|
||||
}
|
||||
// left edge clipping
|
||||
int start_x = 0;
|
||||
if (spr_ram[3] < 8 && (w2001 & 0x01e) != 0x1e)
|
||||
{
|
||||
if (spr_ram[3] == 0)
|
||||
return; // won't hit
|
||||
start_x = 8 - spr_ram[3];
|
||||
}
|
||||
|
||||
// vertical flip
|
||||
int final = skip + visible;
|
||||
if ( spr_ram [2] & 0x80 )
|
||||
{
|
||||
skip += height - 1;
|
||||
final = skip - visible;
|
||||
}
|
||||
// vertical flip
|
||||
int final = skip + visible;
|
||||
if (spr_ram[2] & 0x80)
|
||||
{
|
||||
skip += height - 1;
|
||||
final = skip - visible;
|
||||
}
|
||||
|
||||
// check each line
|
||||
unsigned long const mask = 0x01010101 + zero;
|
||||
do
|
||||
{
|
||||
// get pixels for line
|
||||
unsigned long line = lines [skip >> 1];
|
||||
unsigned long hit0 = ((uint32_t*) bg) [0];
|
||||
unsigned long hit1 = ((uint32_t*) bg) [1];
|
||||
bg += next_row;
|
||||
line >>= skip << 1 & 2;
|
||||
line |= line >> 1;
|
||||
// check each line
|
||||
unsigned long const mask = 0x01010101 + zero;
|
||||
do
|
||||
{
|
||||
// get pixels for line
|
||||
unsigned long line = lines[skip >> 1];
|
||||
unsigned long hit0 = ((uint32_t *)bg)[0];
|
||||
unsigned long hit1 = ((uint32_t *)bg)[1];
|
||||
bg += next_row;
|
||||
line >>= skip << 1 & 2;
|
||||
line |= line >> 1;
|
||||
|
||||
// check for hits
|
||||
hit0 = ((hit0 >> 1) | hit0) & (line >> 4);
|
||||
hit1 = ((hit1 >> 1) | hit1) & line;
|
||||
if ( (hit0 | hit1) & mask )
|
||||
{
|
||||
// write to memory to avoid endian issues
|
||||
uint32_t quads [3];
|
||||
quads [0] = hit0;
|
||||
quads [1] = hit1;
|
||||
// check for hits
|
||||
hit0 = ((hit0 >> 1) | hit0) & (line >> 4);
|
||||
hit1 = ((hit1 >> 1) | hit1) & line;
|
||||
if ((hit0 | hit1) & mask)
|
||||
{
|
||||
// write to memory to avoid endian issues
|
||||
uint32_t quads[3];
|
||||
quads[0] = hit0;
|
||||
quads[1] = hit1;
|
||||
|
||||
// find which pixel hit
|
||||
int x = start_x;
|
||||
do
|
||||
{
|
||||
if ( ((uint8_t*) quads) [x] & 1 )
|
||||
{
|
||||
x += spr_ram [3];
|
||||
if ( x >= 255 )
|
||||
break; // ignore right edge
|
||||
// find which pixel hit
|
||||
int x = start_x;
|
||||
do
|
||||
{
|
||||
if (((uint8_t *)quads)[x] & 1)
|
||||
{
|
||||
x += spr_ram[3];
|
||||
if (x >= 255)
|
||||
break; // ignore right edge
|
||||
|
||||
if ( spr_ram [2] & 0x80 )
|
||||
skip = height - 1 - skip; // vertical flip
|
||||
int y = spr_ram [0] + 1 + skip;
|
||||
sprite_hit_found = y * scanline_len + x;
|
||||
if (spr_ram[2] & 0x80)
|
||||
skip = height - 1 - skip; // vertical flip
|
||||
int y = spr_ram[0] + 1 + skip;
|
||||
sprite_hit_found = y * scanline_len + x;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
while ( x++ < 7 );
|
||||
}
|
||||
if ( skip > final )
|
||||
skip -= 2;
|
||||
skip++;
|
||||
}
|
||||
while ( skip != final );
|
||||
return;
|
||||
}
|
||||
} while (x++ < 7);
|
||||
}
|
||||
if (skip > final)
|
||||
skip -= 2;
|
||||
skip++;
|
||||
} while (skip != final);
|
||||
}
|
||||
|
||||
// Draw scanlines
|
||||
|
||||
inline bool Nes_Ppu_Rendering::sprite_hit_possible( int scanline ) const
|
||||
inline bool Nes_Ppu_Rendering::sprite_hit_possible(int scanline) const
|
||||
{
|
||||
return !sprite_hit_found && spr_ram [0] <= scanline && (w2001 & 0x18) == 0x18;
|
||||
return !sprite_hit_found && spr_ram[0] <= scanline && (w2001 & 0x18) == 0x18;
|
||||
}
|
||||
|
||||
void Nes_Ppu_Rendering::draw_scanlines( int start, int count,
|
||||
uint8_t* pixels, long pitch, int mode )
|
||||
void Nes_Ppu_Rendering::draw_scanlines(int start, int count, uint8_t *pixels, long pitch, int mode)
|
||||
{
|
||||
scanline_pixels = pixels + image_left;
|
||||
scanline_row_bytes = pitch;
|
||||
scanline_pixels = pixels + image_left;
|
||||
scanline_row_bytes = pitch;
|
||||
|
||||
int const obj_mask = 2;
|
||||
int const bg_mask = 1;
|
||||
int draw_mode = (w2001 >> 3) & 3;
|
||||
int clip_mode = (~w2001 >> 1) & draw_mode;
|
||||
int const obj_mask = 2;
|
||||
int const bg_mask = 1;
|
||||
int draw_mode = (w2001 >> 3) & 3;
|
||||
int clip_mode = (~w2001 >> 1) & draw_mode;
|
||||
|
||||
if ( !(draw_mode & bg_mask) )
|
||||
{
|
||||
// no background
|
||||
clip_mode |= bg_mask; // avoid unnecessary save/restore
|
||||
if ( mode & bg_mask )
|
||||
fill_background( count );
|
||||
}
|
||||
if (!(draw_mode & bg_mask))
|
||||
{
|
||||
// no background
|
||||
clip_mode |= bg_mask; // avoid unnecessary save/restore
|
||||
if (mode & bg_mask)
|
||||
fill_background(count);
|
||||
}
|
||||
|
||||
if ( start == 0 && mode & 1 )
|
||||
memset( sprite_scanlines, max_sprites - sprite_limit, 240 );
|
||||
if (start == 0 && mode & 1)
|
||||
memset(sprite_scanlines, max_sprites - sprite_limit, 240);
|
||||
|
||||
if ( (draw_mode &= mode) )
|
||||
{
|
||||
// sprites and/or background are being rendered
|
||||
if ((draw_mode &= mode))
|
||||
{
|
||||
// sprites and/or background are being rendered
|
||||
|
||||
if ( any_tiles_modified && chr_is_writable )
|
||||
{
|
||||
any_tiles_modified = false;
|
||||
update_tiles( 0 );
|
||||
}
|
||||
if (any_tiles_modified && chr_is_writable)
|
||||
{
|
||||
any_tiles_modified = false;
|
||||
update_tiles(0);
|
||||
}
|
||||
|
||||
if ( draw_mode & bg_mask )
|
||||
{
|
||||
//dprintf( "bg %3d-%3d\n", start, start + count - 1 );
|
||||
draw_background_( count );
|
||||
if (draw_mode & bg_mask)
|
||||
{
|
||||
// dprintf( "bg %3d-%3d\n", start, start + count - 1 );
|
||||
draw_background_(count);
|
||||
|
||||
if ( clip_mode == bg_mask )
|
||||
clip_left( count );
|
||||
if (clip_mode == bg_mask)
|
||||
clip_left(count);
|
||||
|
||||
if ( sprite_hit_possible( start + count ) )
|
||||
check_sprite_hit( start, start + count );
|
||||
}
|
||||
if (sprite_hit_possible(start + count))
|
||||
check_sprite_hit(start, start + count);
|
||||
}
|
||||
|
||||
if ( draw_mode & obj_mask )
|
||||
{
|
||||
// when clipping just sprites, save left strip then restore after drawing them
|
||||
if ( clip_mode == obj_mask )
|
||||
save_left( count );
|
||||
if (draw_mode & obj_mask)
|
||||
{
|
||||
// when clipping just sprites, save left strip then restore after drawing them
|
||||
if (clip_mode == obj_mask)
|
||||
save_left(count);
|
||||
|
||||
//dprintf( "obj %3d-%3d\n", start, start + count - 1 );
|
||||
// dprintf( "obj %3d-%3d\n", start, start + count - 1 );
|
||||
|
||||
draw_sprites_( start, start + count );
|
||||
draw_sprites_(start, start + count);
|
||||
|
||||
if ( clip_mode == obj_mask )
|
||||
restore_left( count );
|
||||
if (clip_mode == obj_mask)
|
||||
restore_left(count);
|
||||
|
||||
if ( clip_mode == (obj_mask | bg_mask) )
|
||||
clip_left( count );
|
||||
}
|
||||
}
|
||||
if (clip_mode == (obj_mask | bg_mask))
|
||||
clip_left(count);
|
||||
}
|
||||
}
|
||||
|
||||
scanline_pixels = NULL;
|
||||
scanline_pixels = NULL;
|
||||
}
|
||||
|
||||
void Nes_Ppu_Rendering::draw_background( int start, int count )
|
||||
void Nes_Ppu_Rendering::draw_background(int start, int count)
|
||||
{
|
||||
// always capture palette at least once per frame
|
||||
if ( (start + count >= 240 && !palette_size) || (w2001 & palette_changed) )
|
||||
{
|
||||
palette_changed = false;
|
||||
capture_palette();
|
||||
}
|
||||
// always capture palette at least once per frame
|
||||
if ((start + count >= 240 && !palette_size) || (w2001 & palette_changed))
|
||||
{
|
||||
palette_changed = false;
|
||||
capture_palette();
|
||||
}
|
||||
|
||||
if ( host_pixels )
|
||||
{
|
||||
draw_scanlines( start, count, host_pixels + host_row_bytes * start, host_row_bytes, 1 );
|
||||
}
|
||||
else if ( sprite_hit_possible( start + count ) )
|
||||
{
|
||||
// not rendering, but still handle sprite hit using mini graphics buffer
|
||||
int y = spr_ram [0] + 1;
|
||||
int skip = std::min( count, std::max( y - start, 0 ) );
|
||||
int visible = std::min( count - skip, sprite_height() );
|
||||
if (host_pixels)
|
||||
{
|
||||
draw_scanlines(start, count, host_pixels + host_row_bytes * start, host_row_bytes, 1);
|
||||
}
|
||||
else if (sprite_hit_possible(start + count))
|
||||
{
|
||||
// not rendering, but still handle sprite hit using mini graphics buffer
|
||||
int y = spr_ram[0] + 1;
|
||||
int skip = std::min(count, std::max(y - start, 0));
|
||||
int visible = std::min(count - skip, sprite_height());
|
||||
|
||||
if ( visible > 0 )
|
||||
{
|
||||
run_hblank( skip );
|
||||
draw_scanlines( start + skip, visible, impl->mini_offscreen, buffer_width, 3 );
|
||||
}
|
||||
}
|
||||
if (visible > 0)
|
||||
{
|
||||
run_hblank(skip);
|
||||
draw_scanlines(start + skip, visible, impl->mini_offscreen, buffer_width, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,53 +5,55 @@
|
|||
|
||||
#include "Nes_Ppu_Impl.hpp"
|
||||
|
||||
class Nes_Ppu_Rendering : public Nes_Ppu_Impl {
|
||||
typedef Nes_Ppu_Impl base;
|
||||
public:
|
||||
Nes_Ppu_Rendering();
|
||||
class Nes_Ppu_Rendering : public Nes_Ppu_Impl
|
||||
{
|
||||
typedef Nes_Ppu_Impl base;
|
||||
|
||||
int sprite_limit;
|
||||
public:
|
||||
Nes_Ppu_Rendering();
|
||||
|
||||
uint8_t* host_pixels;
|
||||
long host_row_bytes;
|
||||
int sprite_limit;
|
||||
|
||||
protected:
|
||||
uint8_t *host_pixels;
|
||||
long host_row_bytes;
|
||||
|
||||
long sprite_hit_found; // -1: sprite 0 didn't hit, 0: no hit so far, > 0: y * 341 + x
|
||||
void draw_background( int start, int count );
|
||||
void draw_sprites( int start, int count );
|
||||
protected:
|
||||
long sprite_hit_found; // -1: sprite 0 didn't hit, 0: no hit so far, > 0: y * 341 + x
|
||||
void draw_background(int start, int count);
|
||||
void draw_sprites(int start, int count);
|
||||
|
||||
private:
|
||||
private:
|
||||
void draw_scanlines(int start, int count, uint8_t *pixels, long pitch, int mode);
|
||||
void draw_background_(int count);
|
||||
|
||||
void draw_scanlines( int start, int count, uint8_t* pixels, long pitch, int mode );
|
||||
void draw_background_( int count );
|
||||
// destination for draw functions; avoids extra parameters
|
||||
uint8_t *scanline_pixels;
|
||||
long scanline_row_bytes;
|
||||
|
||||
// destination for draw functions; avoids extra parameters
|
||||
uint8_t* scanline_pixels;
|
||||
long scanline_row_bytes;
|
||||
// fill/copy
|
||||
void fill_background(int count);
|
||||
void clip_left(int count);
|
||||
void save_left(int count);
|
||||
void restore_left(int count);
|
||||
|
||||
// fill/copy
|
||||
void fill_background( int count );
|
||||
void clip_left( int count );
|
||||
void save_left( int count );
|
||||
void restore_left( int count );
|
||||
|
||||
// sprites
|
||||
enum { max_sprites = 64 };
|
||||
uint8_t sprite_scanlines [image_height]; // number of sprites on each scanline
|
||||
void draw_sprites_( int start, int count );
|
||||
bool sprite_hit_possible( int scanline ) const;
|
||||
void check_sprite_hit( int begin, int end );
|
||||
// sprites
|
||||
enum
|
||||
{
|
||||
max_sprites = 64
|
||||
};
|
||||
uint8_t sprite_scanlines[image_height]; // number of sprites on each scanline
|
||||
void draw_sprites_(int start, int count);
|
||||
bool sprite_hit_possible(int scanline) const;
|
||||
void check_sprite_hit(int begin, int end);
|
||||
};
|
||||
|
||||
inline Nes_Ppu_Rendering::Nes_Ppu_Rendering()
|
||||
{
|
||||
sprite_limit = 8;
|
||||
host_pixels = nullptr;
|
||||
sprite_limit = 8;
|
||||
host_pixels = nullptr;
|
||||
}
|
||||
|
||||
inline void Nes_Ppu_Rendering::draw_sprites( int start, int count )
|
||||
inline void Nes_Ppu_Rendering::draw_sprites(int start, int count)
|
||||
{
|
||||
draw_scanlines( start, count, host_pixels + host_row_bytes * start, host_row_bytes, 2 );
|
||||
draw_scanlines(start, count, host_pixels + host_row_bytes * start, host_row_bytes, 2);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,129 +1,131 @@
|
|||
int sprite_2 = sprite [2];
|
||||
int sprite_2 = sprite[2];
|
||||
|
||||
// pixels
|
||||
ptrdiff_t next_row = this->scanline_row_bytes;
|
||||
uint8_t* out = this->scanline_pixels + sprite [3] +
|
||||
(top_minus_one + skip - begin_minus_one) * next_row;
|
||||
cache_t const* lines = get_sprite_tile( sprite );
|
||||
uint8_t *out = this->scanline_pixels + sprite[3] +
|
||||
(top_minus_one + skip - begin_minus_one) * next_row;
|
||||
cache_t const *lines = get_sprite_tile(sprite);
|
||||
|
||||
int dir = 1;
|
||||
uint8_t* scanlines = this->sprite_scanlines + 1 + top_minus_one + skip;
|
||||
uint8_t *scanlines = this->sprite_scanlines + 1 + top_minus_one + skip;
|
||||
|
||||
if ( sprite_2 & 0x80 )
|
||||
if (sprite_2 & 0x80)
|
||||
{
|
||||
// vertical flip
|
||||
out -= next_row;
|
||||
out += visible * next_row;
|
||||
next_row = -next_row;
|
||||
dir = -1;
|
||||
scanlines += visible - 1;
|
||||
#if CLIPPED
|
||||
int height = this->sprite_height();
|
||||
skip = height - skip - visible;
|
||||
#endif
|
||||
// vertical flip
|
||||
out -= next_row;
|
||||
out += visible * next_row;
|
||||
next_row = -next_row;
|
||||
dir = -1;
|
||||
scanlines += visible - 1;
|
||||
#if CLIPPED
|
||||
int height = this->sprite_height();
|
||||
skip = height - skip - visible;
|
||||
#endif
|
||||
}
|
||||
|
||||
// attributes
|
||||
unsigned long offset = (sprite_2 & 3) * 0x04040404 + (this->palette_offset + 0x10101010);
|
||||
|
||||
unsigned long const mask = 0x03030303 + zero;
|
||||
unsigned long const mask = 0x03030303 + zero;
|
||||
unsigned long const maskgen = 0x80808080 + zero;
|
||||
|
||||
#define DRAW_PAIR( shift ) { \
|
||||
int sprite_count = *scanlines; \
|
||||
CALC_FOUR( ((uint32_t*) out) [0], (line >> (shift + 4)), out0 ) \
|
||||
CALC_FOUR( ((uint32_t*) out) [1], (line >> shift), out1 ) \
|
||||
if ( sprite_count < this->max_sprites ) { \
|
||||
((uint32_t*) out) [0] = out0; \
|
||||
((uint32_t*) out) [1] = out1; \
|
||||
} \
|
||||
if ( CLIPPED ) visible--; \
|
||||
out += next_row; \
|
||||
*scanlines = sprite_count + 1; \
|
||||
scanlines += dir; \
|
||||
if ( CLIPPED && !visible ) break; \
|
||||
}
|
||||
#define DRAW_PAIR(shift) \
|
||||
{ \
|
||||
int sprite_count = *scanlines; \
|
||||
CALC_FOUR(((uint32_t *)out)[0], (line >> (shift + 4)), out0) \
|
||||
CALC_FOUR(((uint32_t *)out)[1], (line >> shift), out1) \
|
||||
if (sprite_count < this->max_sprites) \
|
||||
{ \
|
||||
((uint32_t *)out)[0] = out0; \
|
||||
((uint32_t *)out)[1] = out1; \
|
||||
} \
|
||||
if (CLIPPED) visible--; \
|
||||
out += next_row; \
|
||||
*scanlines = sprite_count + 1; \
|
||||
scanlines += dir; \
|
||||
if (CLIPPED && !visible) break; \
|
||||
}
|
||||
|
||||
if ( !(sprite_2 & 0x20) )
|
||||
if (!(sprite_2 & 0x20))
|
||||
{
|
||||
// front
|
||||
unsigned long const maskgen2 = 0x7f7f7f7f + zero;
|
||||
// front
|
||||
unsigned long const maskgen2 = 0x7f7f7f7f + zero;
|
||||
|
||||
#define CALC_FOUR( in, line, out ) \
|
||||
unsigned long out; \
|
||||
{ \
|
||||
unsigned long bg = in; \
|
||||
unsigned long sp = line & mask; \
|
||||
unsigned long bgm = maskgen2 + ((bg >> 4) & mask); \
|
||||
unsigned long spm = (maskgen - sp) & maskgen2; \
|
||||
unsigned long m = (bgm & spm) >> 2; \
|
||||
out = (bg & ~m) | ((sp + offset) & m); \
|
||||
}
|
||||
#define CALC_FOUR(in, line, out) \
|
||||
unsigned long out; \
|
||||
{ \
|
||||
unsigned long bg = in; \
|
||||
unsigned long sp = line & mask; \
|
||||
unsigned long bgm = maskgen2 + ((bg >> 4) & mask); \
|
||||
unsigned long spm = (maskgen - sp) & maskgen2; \
|
||||
unsigned long m = (bgm & spm) >> 2; \
|
||||
out = (bg & ~m) | ((sp + offset) & m); \
|
||||
}
|
||||
|
||||
#if CLIPPED
|
||||
lines += skip >> 1;
|
||||
unsigned long line = *lines++;
|
||||
if ( skip & 1 )
|
||||
goto front_skip;
|
||||
#if CLIPPED
|
||||
lines += skip >> 1;
|
||||
unsigned long line = *lines++;
|
||||
if (skip & 1)
|
||||
goto front_skip;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
DRAW_PAIR( 0 )
|
||||
front_skip:
|
||||
DRAW_PAIR( 2 )
|
||||
line = *lines++;
|
||||
}
|
||||
#else
|
||||
for ( int n = visible >> 1; n--; )
|
||||
{
|
||||
unsigned long line = *lines++;
|
||||
DRAW_PAIR( 0 )
|
||||
DRAW_PAIR( 2 )
|
||||
}
|
||||
#endif
|
||||
while (true)
|
||||
{
|
||||
DRAW_PAIR(0)
|
||||
front_skip:
|
||||
DRAW_PAIR(2)
|
||||
line = *lines++;
|
||||
}
|
||||
#else
|
||||
for (int n = visible >> 1; n--;)
|
||||
{
|
||||
unsigned long line = *lines++;
|
||||
DRAW_PAIR(0)
|
||||
DRAW_PAIR(2)
|
||||
}
|
||||
#endif
|
||||
|
||||
#undef CALC_FOUR
|
||||
#undef CALC_FOUR
|
||||
}
|
||||
else
|
||||
{
|
||||
// behind
|
||||
unsigned long const omask = 0x20202020 + zero;
|
||||
unsigned long const bg_or = 0xc3c3c3c3 + zero;
|
||||
// behind
|
||||
unsigned long const omask = 0x20202020 + zero;
|
||||
unsigned long const bg_or = 0xc3c3c3c3 + zero;
|
||||
|
||||
#define CALC_FOUR( in, line, out ) \
|
||||
unsigned long out; \
|
||||
{ \
|
||||
unsigned long bg = in; \
|
||||
unsigned long sp = line & mask; \
|
||||
unsigned long bgm = maskgen - (bg & mask); \
|
||||
unsigned long spm = maskgen - sp; \
|
||||
out = (bg & (bgm | bg_or)) | (spm & omask) | \
|
||||
(((offset & spm) + sp) & ~(bgm >> 2)); \
|
||||
}
|
||||
#define CALC_FOUR(in, line, out) \
|
||||
unsigned long out; \
|
||||
{ \
|
||||
unsigned long bg = in; \
|
||||
unsigned long sp = line & mask; \
|
||||
unsigned long bgm = maskgen - (bg & mask); \
|
||||
unsigned long spm = maskgen - sp; \
|
||||
out = (bg & (bgm | bg_or)) | (spm & omask) | \
|
||||
(((offset & spm) + sp) & ~(bgm >> 2)); \
|
||||
}
|
||||
|
||||
#if CLIPPED
|
||||
lines += skip >> 1;
|
||||
unsigned long line = *lines++;
|
||||
if ( skip & 1 )
|
||||
goto back_skip;
|
||||
#if CLIPPED
|
||||
lines += skip >> 1;
|
||||
unsigned long line = *lines++;
|
||||
if (skip & 1)
|
||||
goto back_skip;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
DRAW_PAIR( 0 )
|
||||
back_skip:
|
||||
DRAW_PAIR( 2 )
|
||||
line = *lines++;
|
||||
}
|
||||
#else
|
||||
for ( int n = visible >> 1; n--; )
|
||||
{
|
||||
unsigned long line = *lines++;
|
||||
DRAW_PAIR( 0 )
|
||||
DRAW_PAIR( 2 )
|
||||
}
|
||||
#endif
|
||||
while (true)
|
||||
{
|
||||
DRAW_PAIR(0)
|
||||
back_skip:
|
||||
DRAW_PAIR(2)
|
||||
line = *lines++;
|
||||
}
|
||||
#else
|
||||
for (int n = visible >> 1; n--;)
|
||||
{
|
||||
unsigned long line = *lines++;
|
||||
DRAW_PAIR(0)
|
||||
DRAW_PAIR(2)
|
||||
}
|
||||
#endif
|
||||
|
||||
#undef CALC_FOUR
|
||||
#undef CALC_FOUR
|
||||
}
|
||||
|
||||
#undef CLIPPED
|
||||
|
|
|
@ -5,72 +5,70 @@
|
|||
|
||||
class QuickerNESInstance : public EmuInstance
|
||||
{
|
||||
public:
|
||||
public:
|
||||
QuickerNESInstance() : EmuInstance()
|
||||
{
|
||||
// Creating new emulator
|
||||
_nes = new Nes_Emu;
|
||||
|
||||
QuickerNESInstance() : EmuInstance()
|
||||
{
|
||||
// Creating new emulator
|
||||
_nes = new Nes_Emu;
|
||||
// Allocating video buffer
|
||||
video_buffer = (uint8_t *)malloc(image_width * image_height);
|
||||
|
||||
// Allocating video buffer
|
||||
video_buffer = (uint8_t*) malloc(image_width * image_height);
|
||||
// Setting video buffer
|
||||
_nes->set_pixels(video_buffer, image_width + 8);
|
||||
}
|
||||
|
||||
// Setting video buffer
|
||||
_nes->set_pixels(video_buffer, image_width+8);
|
||||
}
|
||||
~QuickerNESInstance() = default;
|
||||
|
||||
~QuickerNESInstance() = default;
|
||||
virtual bool loadROMFileImpl(const std::string &romData) override
|
||||
{
|
||||
// Loading rom data
|
||||
_nes->load_ines((uint8_t *)romData.data());
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool loadROMFileImpl(const std::string& romData) override
|
||||
{
|
||||
// Loading rom data
|
||||
_nes->load_ines((uint8_t*)romData.data());
|
||||
return true;
|
||||
}
|
||||
uint8_t *getLowMem() const override { return _nes->low_mem(); };
|
||||
uint8_t *getNametableMem() const override { return _nes->nametable_mem(); };
|
||||
uint8_t *getHighMem() const override { return _nes->high_mem(); };
|
||||
const uint8_t *getChrMem() const override { return _nes->chr_mem(); };
|
||||
size_t getChrMemSize() const override { return _nes->chr_size(); };
|
||||
|
||||
uint8_t* getLowMem() const override { return _nes->low_mem(); };
|
||||
uint8_t* getNametableMem() const override { return _nes->nametable_mem(); };
|
||||
uint8_t* getHighMem() const override { return _nes->high_mem();};
|
||||
const uint8_t* getChrMem() const override { return _nes->chr_mem();};
|
||||
size_t getChrMemSize() const override { return _nes->chr_size();};
|
||||
|
||||
void serializeState(uint8_t* state) const override
|
||||
{
|
||||
void serializeState(uint8_t *state) const override
|
||||
{
|
||||
_nes->serializeState(state);
|
||||
}
|
||||
}
|
||||
|
||||
void deserializeState(const uint8_t* state) override
|
||||
{
|
||||
_nes->deserializeState(state);
|
||||
}
|
||||
void deserializeState(const uint8_t *state) override
|
||||
{
|
||||
_nes->deserializeState(state);
|
||||
}
|
||||
|
||||
void advanceStateImpl(const inputType controller1, const inputType controller2) override
|
||||
{
|
||||
if (_doRendering == true) _nes->emulate_frame(controller1, controller2);
|
||||
if (_doRendering == false) _nes->emulate_skip_frame(controller1, controller2);
|
||||
}
|
||||
void advanceStateImpl(const inputType controller1, const inputType controller2) override
|
||||
{
|
||||
if (_doRendering == true) _nes->emulate_frame(controller1, controller2);
|
||||
if (_doRendering == false) _nes->emulate_skip_frame(controller1, controller2);
|
||||
}
|
||||
|
||||
std::string getCoreName() const override { return "QuickerNES"; }
|
||||
void doSoftReset() override { _nes->reset(false); }
|
||||
void doHardReset() override { _nes->reset(true); }
|
||||
std::string getCoreName() const override { return "QuickerNES"; }
|
||||
void doSoftReset() override { _nes->reset(false); }
|
||||
void doHardReset() override { _nes->reset(true); }
|
||||
|
||||
void* getInternalEmulatorPointer() const override { return _nes; }
|
||||
void *getInternalEmulatorPointer() const override { return _nes; }
|
||||
|
||||
private:
|
||||
|
||||
inline size_t getStateSizeImpl() const override
|
||||
{
|
||||
private:
|
||||
inline size_t getStateSizeImpl() const override
|
||||
{
|
||||
return _nes->getStateSize();
|
||||
}
|
||||
}
|
||||
|
||||
inline size_t getLiteStateSizeImpl() const override
|
||||
{
|
||||
inline size_t getLiteStateSizeImpl() const override
|
||||
{
|
||||
return _nes->getLiteStateSize();
|
||||
}
|
||||
}
|
||||
|
||||
// Video buffer
|
||||
uint8_t* video_buffer;
|
||||
// Video buffer
|
||||
uint8_t *video_buffer;
|
||||
|
||||
// Emulator instance
|
||||
Nes_Emu* _nes;
|
||||
// Emulator instance
|
||||
Nes_Emu *_nes;
|
||||
};
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
#include <sstream>
|
||||
#include <chrono>
|
||||
#include "argparse/argparse.hpp"
|
||||
#include "nlohmann/json.hpp"
|
||||
#include "sha1/sha1.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "nlohmann/json.hpp"
|
||||
#include <chrono>
|
||||
#include <sstream>
|
||||
|
||||
#ifdef _USE_QUICKNES
|
||||
#include "quickNESInstance.hpp"
|
||||
#include "quickNESInstance.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef _USE_QUICKERNES
|
||||
#include "quickerNESInstance.hpp"
|
||||
#include "quickerNESInstance.hpp"
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Parsing command line arguments
|
||||
// Parsing command line arguments
|
||||
argparse::ArgumentParser program("tester", "1.0");
|
||||
|
||||
program.add_argument("scriptFile")
|
||||
|
@ -31,8 +31,14 @@ int main(int argc, char *argv[])
|
|||
.default_value(std::string(""));
|
||||
|
||||
// Try to parse arguments
|
||||
try { program.parse_args(argc, argv); }
|
||||
catch (const std::runtime_error &err) { EXIT_WITH_ERROR("%s\n%s", err.what(), program.help().str().c_str()); }
|
||||
try
|
||||
{
|
||||
program.parse_args(argc, argv);
|
||||
}
|
||||
catch (const std::runtime_error &err)
|
||||
{
|
||||
EXIT_WITH_ERROR("%s\n%s", err.what(), program.help().str().c_str());
|
||||
}
|
||||
|
||||
// Getting test script file path
|
||||
std::string scriptFilePath = program.get<std::string>("scriptFile");
|
||||
|
@ -45,7 +51,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
// Loading script file
|
||||
std::string scriptJsonRaw;
|
||||
if (loadStringFromFile(scriptJsonRaw, scriptFilePath) == false) EXIT_WITH_ERROR("Could not find/read script file: %s\n", scriptFilePath.c_str());
|
||||
if (loadStringFromFile(scriptJsonRaw, scriptFilePath) == false) EXIT_WITH_ERROR("Could not find/read script file: %s\n", scriptFilePath.c_str());
|
||||
|
||||
// Parsing script
|
||||
const auto scriptJson = nlohmann::json::parse(scriptJsonRaw);
|
||||
|
@ -70,14 +76,14 @@ int main(int argc, char *argv[])
|
|||
if (scriptJson["Expected ROM SHA1"].is_string() == false) EXIT_WITH_ERROR("Script file 'Expected ROM SHA1' entry is not a string\n");
|
||||
std::string expectedROMSHA1 = scriptJson["Expected ROM SHA1"].get<std::string>();
|
||||
|
||||
// Creating emulator instance
|
||||
#ifdef _USE_QUICKNES
|
||||
// Creating emulator instance
|
||||
#ifdef _USE_QUICKNES
|
||||
auto e = QuickNESInstance();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _USE_QUICKERNES
|
||||
#ifdef _USE_QUICKERNES
|
||||
auto e = QuickerNESInstance();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Loading ROM File
|
||||
e.loadROMFile(romFilePath);
|
||||
|
@ -133,7 +139,7 @@ int main(int argc, char *argv[])
|
|||
fflush(stdout);
|
||||
|
||||
// Serializing initial state
|
||||
uint8_t* currentState = (uint8_t*) malloc (stateSize);
|
||||
uint8_t *currentState = (uint8_t *)malloc(stateSize);
|
||||
e.serializeState(currentState);
|
||||
|
||||
// Check whether to perform each action
|
||||
|
@ -143,7 +149,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
// Actually running the sequence
|
||||
auto t0 = std::chrono::high_resolution_clock::now();
|
||||
for (const std::string& input : sequence)
|
||||
for (const std::string &input : sequence)
|
||||
{
|
||||
if (doPreAdvance == true) e.advanceState(input);
|
||||
if (doDeserialize == true) e.deserializeState(currentState);
|
||||
|
@ -174,4 +180,3 @@ int main(int argc, char *argv[])
|
|||
// If reached this point, everything ran ok
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
114
source/utils.hpp
114
source/utils.hpp
|
@ -1,21 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <metrohash128/metrohash128.h>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <stdarg.h>
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
#include <metrohash128/metrohash128.h>
|
||||
#include <vector>
|
||||
|
||||
// If we use NCurses, we need to use the appropriate printing function
|
||||
#ifdef NCURSES
|
||||
#include <ncurses.h>
|
||||
#define LOG printw
|
||||
#include <ncurses.h>
|
||||
#define LOG printw
|
||||
#else
|
||||
#define LOG printf
|
||||
#define LOG printf
|
||||
#endif
|
||||
|
||||
// If we use NCurses, define the following useful functions
|
||||
|
@ -82,12 +82,10 @@ void refreshTerminal()
|
|||
refresh();
|
||||
}
|
||||
|
||||
|
||||
#endif // NCURSES
|
||||
|
||||
|
||||
typedef _uint128_t hash_t;
|
||||
inline hash_t calculateMetroHash(uint8_t* data, size_t size)
|
||||
inline hash_t calculateMetroHash(uint8_t *data, size_t size)
|
||||
{
|
||||
MetroHash128 hash;
|
||||
hash.Update(data, size);
|
||||
|
@ -100,7 +98,8 @@ inline hash_t calculateMetroHash(uint8_t* data, size_t size)
|
|||
// Taken from stack overflow answer to https://stackoverflow.com/questions/236129/how-do-i-iterate-over-the-words-of-a-string
|
||||
// By Evan Teran
|
||||
|
||||
template <typename Out> inline void split(const std::string &s, char delim, Out result)
|
||||
template <typename Out>
|
||||
inline void split(const std::string &s, char delim, Out result)
|
||||
{
|
||||
std::istringstream iss(s);
|
||||
std::string item;
|
||||
|
@ -112,11 +111,11 @@ template <typename Out> inline void split(const std::string &s, char delim, Out
|
|||
|
||||
inline std::vector<std::string> split(const std::string &s, char delim)
|
||||
{
|
||||
std::string newString = s;
|
||||
std::replace(newString.begin(), newString.end(), '\n', ' ');
|
||||
std::vector<std::string> elems;
|
||||
split(newString, delim, std::back_inserter(elems));
|
||||
return elems;
|
||||
std::string newString = s;
|
||||
std::replace(newString.begin(), newString.end(), '\n', ' ');
|
||||
std::vector<std::string> elems;
|
||||
split(newString, delim, std::back_inserter(elems));
|
||||
return elems;
|
||||
}
|
||||
|
||||
// Taken from https://stackoverflow.com/questions/116038/how-do-i-read-an-entire-file-into-a-stdstring-in-c/116220#116220
|
||||
|
@ -127,8 +126,8 @@ inline std::string slurp(std::ifstream &in)
|
|||
return sstr.str();
|
||||
}
|
||||
|
||||
#define EXIT_WITH_ERROR(...) exitWithError(__FILE__, __LINE__, __VA_ARGS__)
|
||||
inline void exitWithError [[noreturn]] (const char *fileName, const int lineNumber, const char *format, ...)
|
||||
#define EXIT_WITH_ERROR(...) exitWithError(__FILE__, __LINE__, __VA_ARGS__)
|
||||
inline void exitWithError [[noreturn]] (const char *fileName, const int lineNumber, const char *format, ...)
|
||||
{
|
||||
char *outstr = 0;
|
||||
va_list ap;
|
||||
|
@ -178,7 +177,8 @@ inline bool saveStringToFile(const std::string &src, const char *fileName)
|
|||
}
|
||||
|
||||
// Function to split a vector into n mostly fair chunks
|
||||
template <typename T> inline std::vector<T> splitVector(const T size, const T n)
|
||||
template <typename T>
|
||||
inline std::vector<T> splitVector(const T size, const T n)
|
||||
{
|
||||
std::vector<T> subSizes(n);
|
||||
|
||||
|
@ -191,14 +191,19 @@ template <typename T> inline std::vector<T> splitVector(const T size, const T n)
|
|||
return subSizes;
|
||||
}
|
||||
|
||||
inline std::string simplifyMove(const std::string& move)
|
||||
inline std::string simplifyMove(const std::string &move)
|
||||
{
|
||||
std::string simpleMove;
|
||||
std::string simpleMove;
|
||||
|
||||
bool isEmptyMove = true;
|
||||
for (size_t i = 0; i < move.size(); i++) if (move[i] != '.' && move[i] != '|') { simpleMove += move[i]; isEmptyMove = false; }
|
||||
if (isEmptyMove) return ".";
|
||||
return simpleMove;
|
||||
bool isEmptyMove = true;
|
||||
for (size_t i = 0; i < move.size(); i++)
|
||||
if (move[i] != '.' && move[i] != '|')
|
||||
{
|
||||
simpleMove += move[i];
|
||||
isEmptyMove = false;
|
||||
}
|
||||
if (isEmptyMove) return ".";
|
||||
return simpleMove;
|
||||
}
|
||||
|
||||
inline bool getBitFlag(const uint8_t value, const uint8_t idx)
|
||||
|
@ -214,29 +219,42 @@ inline bool getBitFlag(const uint8_t value, const uint8_t idx)
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
inline size_t countButtonsPressedString(const std::string& input) { size_t count = 0; for (size_t i = 0; i < input.size(); i++) if (input[i] != '.') count++; return count; };
|
||||
|
||||
template<typename T> inline uint16_t countButtonsPressedNumber(const T& input) {
|
||||
uint16_t count = 0;
|
||||
if (input & 0b0000000000000001) count++;
|
||||
if (input & 0b0000000000000010) count++;
|
||||
if (input & 0b0000000000000100) count++;
|
||||
if (input & 0b0000000000001000) count++;
|
||||
if (input & 0b0000000000010000) count++;
|
||||
if (input & 0b0000000000100000) count++;
|
||||
if (input & 0b0000000001000000) count++;
|
||||
if (input & 0b0000000010000000) count++;
|
||||
if (input & 0b0000000100000000) count++;
|
||||
if (input & 0b0000001000000000) count++;
|
||||
if (input & 0b0000010000000000) count++;
|
||||
if (input & 0b0000100000000000) count++;
|
||||
if (input & 0b0001000000000000) count++;
|
||||
if (input & 0b0010000000000000) count++;
|
||||
if (input & 0b0100000000000000) count++;
|
||||
if (input & 0b1000000000000000) count++;
|
||||
return count;
|
||||
inline size_t countButtonsPressedString(const std::string &input)
|
||||
{
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < input.size(); i++)
|
||||
if (input[i] != '.') count++;
|
||||
return count;
|
||||
};
|
||||
|
||||
static auto moveCountComparerString = [](const std::string& a, const std::string& b) { return countButtonsPressedString(a) < countButtonsPressedString(b); };
|
||||
static auto moveCountComparerNumber = [](const uint8_t a, const uint8_t b) { return countButtonsPressedNumber(a) < countButtonsPressedNumber(b); };
|
||||
template <typename T>
|
||||
inline uint16_t countButtonsPressedNumber(const T &input)
|
||||
{
|
||||
uint16_t count = 0;
|
||||
if (input & 0b0000000000000001) count++;
|
||||
if (input & 0b0000000000000010) count++;
|
||||
if (input & 0b0000000000000100) count++;
|
||||
if (input & 0b0000000000001000) count++;
|
||||
if (input & 0b0000000000010000) count++;
|
||||
if (input & 0b0000000000100000) count++;
|
||||
if (input & 0b0000000001000000) count++;
|
||||
if (input & 0b0000000010000000) count++;
|
||||
if (input & 0b0000000100000000) count++;
|
||||
if (input & 0b0000001000000000) count++;
|
||||
if (input & 0b0000010000000000) count++;
|
||||
if (input & 0b0000100000000000) count++;
|
||||
if (input & 0b0001000000000000) count++;
|
||||
if (input & 0b0010000000000000) count++;
|
||||
if (input & 0b0100000000000000) count++;
|
||||
if (input & 0b1000000000000000) count++;
|
||||
return count;
|
||||
};
|
||||
|
||||
static auto moveCountComparerString = [](const std::string &a, const std::string &b)
|
||||
{
|
||||
return countButtonsPressedString(a) < countButtonsPressedString(b);
|
||||
};
|
||||
static auto moveCountComparerNumber = [](const uint8_t a, const uint8_t b)
|
||||
{
|
||||
return countButtonsPressedNumber(a) < countButtonsPressedNumber(b);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue