Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
257 lines
7.6 KiB
C
257 lines
7.6 KiB
C
// ____________________________
|
|
// ██▀▀█▀▀██▀▀▀▀▀▀▀█▀▀█ │ ▄▄▄ ▄▄▄ ▄▄▄
|
|
// ██ ▀ █▄ ▀██▄ ▀ ▄█ ▄▀▀ █ │ ██▄▀ ▀█▄ ██
|
|
// █ █ █ ▀▀ ▄█ █ █ ▀▄█ █▄ │ ██ ▄▄█▀ ▀█▄█
|
|
// ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀────────┘
|
|
// by Guillaume 'Aoineko' Blanchard under CC BY-SA license
|
|
//─────────────────────────────────────────────────────────────────────────────
|
|
// PSG handler module
|
|
//
|
|
// References:
|
|
// - AY-3-8910 Programmable Sound Generator Data Manual
|
|
// http://map.grauw.nl/resources/sound/generalinstrument_ay-3-8910.pdf
|
|
// - YM2149 Programmable Software-Controlled Sound Generator Manual
|
|
// http://map.grauw.nl/resources/sound/yamaha_ym2149.pdf
|
|
// - The Revised MSX Red Book - PSG
|
|
// https://www.angelfire.com/art2/unicorndreams/msx/RR-PSG.html
|
|
//─────────────────────────────────────────────────────────────────────────────
|
|
#pragma once
|
|
|
|
//=============================================================================
|
|
// INCLUDES
|
|
//=============================================================================
|
|
|
|
#include "core.h"
|
|
#include "system_port.h"
|
|
#include "psg_reg.h"
|
|
|
|
//=============================================================================
|
|
// DEFINES
|
|
//=============================================================================
|
|
|
|
#define PSG_REG(a) ((a) & 0x0F)
|
|
#define PSG_CHAN(a) ((a) & 0x03)
|
|
|
|
// Ports
|
|
#if (PSG_CHIP == PSG_INTERNAL)
|
|
#define PSG_PORT_REG P_PSG_REGS //< Used to select a specific register by writing its number (0 to 15)
|
|
#define PSG_PORT_WRITE P_PSG_DATA //< Used to write to any register once it has been selected by the Address Port.
|
|
#define PSG_PORT_READ P_PSG_STAT //< Used to read any register once it has been selected by the Address Port.
|
|
#define PSG_VAR_REG g_PSG_RegPort
|
|
#define PSG_VAR_WRITE g_PSG_DataPort
|
|
#define PSG_VAR_READ g_PSG_StatPort
|
|
|
|
#elif (PSG_CHIP == PSG_EXTERNAL)
|
|
#define PSG_PORT_REG P_PSG_EXT_REGS //< Used to select a specific register by writing its number (0 to 15)
|
|
#define PSG_PORT_WRITE P_PSG_EXT_DATA //< Used to write to any register once it has been selected by the Address Port.
|
|
#define PSG_PORT_READ P_PSG_EXT_STAT //< Used to read any register once it has been selected by the Address Port.
|
|
#define PSG_VAR_REG g_PSG_Ext_RegPort
|
|
#define PSG_VAR_WRITE g_PSG_Ext_DataPort
|
|
#define PSG_VAR_READ g_PSG_Ext_StatPort
|
|
|
|
#elif (PSG_CHIP == PSG_BOTH)
|
|
// Primary PSG
|
|
#define PSG_PORT_REG P_PSG_REGS
|
|
#define PSG_PORT_WRITE P_PSG_DATA
|
|
#define PSG_PORT_READ P_PSG_STAT
|
|
// Secondary PSG
|
|
#define PSG2_PORT_REG P_PSG_EXT_REGS
|
|
#define PSG2_PORT_WRITE P_PSG_EXT_DATA
|
|
#define PSG2_PORT_READ P_PSG_EXT_STAT
|
|
#define PSG2_REG(a) (PSG_REG(a) | 0x80)
|
|
#define PSG2_CHAN(a) (PSG_CHAN(a) | 0x80)
|
|
#endif
|
|
|
|
// Channels
|
|
#define PSG_CHANNEL_A 0
|
|
#define PSG_CHANNEL_B 1
|
|
#define PSG_CHANNEL_C 2
|
|
|
|
// Mixer
|
|
#define PSG_TONE_A_ON 0b00000001
|
|
#define PSG_TONE_B_ON 0b00000010
|
|
#define PSG_TONE_C_ON 0b00000100
|
|
#define PSG_NOISE_A_ON 0b00001000
|
|
#define PSG_NOISE_B_ON 0b00010000
|
|
#define PSG_NOISE_C_ON 0b00100000
|
|
#define PSG_TONE_A_OFF 0
|
|
#define PSG_TONE_B_OFF 0
|
|
#define PSG_TONE_C_OFF 0
|
|
#define PSG_NOISE_A_OFF 0
|
|
#define PSG_NOISE_B_OFF 0
|
|
#define PSG_NOISE_C_OFF 0
|
|
|
|
#define PSG_USE_ENVELOPE PSG_F_ENV
|
|
|
|
#if (PSG_USE_NOTES)
|
|
|
|
enum NOTE
|
|
{
|
|
// Latin notation
|
|
NOTE_DO = 0, // Do
|
|
NOTE_DOd, // Do#
|
|
NOTE_RE, // Re
|
|
NOTE_REd, // Re#
|
|
NOTE_MI, // Mi
|
|
NOTE_FA, // Fa
|
|
NOTE_FAd, // Fa#
|
|
NOTE_SOL, // Sol
|
|
NOTE_SOLd, // Sol#
|
|
NOTE_LA, // La
|
|
NOTE_LAd, // La#
|
|
NOTE_SI, // Si
|
|
// Anglo-Germanic notation
|
|
NOTE_C = 0, // C
|
|
NOTE_Cd, // C#
|
|
NOTE_D, // D
|
|
NOTE_Dd, // D#
|
|
NOTE_E, // E
|
|
NOTE_F, // F
|
|
NOTE_Fd, // F#
|
|
NOTE_G, // G
|
|
NOTE_Gd, // G#
|
|
NOTE_A, // A
|
|
NOTE_Ad, // A#
|
|
NOTE_B // B
|
|
};
|
|
|
|
#endif // (PSG_USE_NOTES)
|
|
|
|
//-----------------------------------------------------------------------------
|
|
typedef struct PSG_Data
|
|
{
|
|
u16 Tone[3]; // 6
|
|
u8 Noise; // 1
|
|
u8 Mixer; // 1
|
|
u8 Volume[3]; // 3
|
|
u16 Envelope; // 2
|
|
u8 Shape; // 1
|
|
u8 IOPortA; // 1
|
|
u8 IOPortB; // 1
|
|
} PSG_Data;
|
|
|
|
#if (PSG_ACCESS == PSG_INDIRECT)
|
|
extern PSG_Data g_PSG_Regs;
|
|
#if (PSG_CHIP == PSG_BOTH)
|
|
extern PSG_Data g_PSG2_Regs;
|
|
#endif
|
|
#endif
|
|
|
|
//=============================================================================
|
|
// FUNCTIONS
|
|
//=============================================================================
|
|
|
|
// Group: Register
|
|
|
|
// Function: PSG_SetRegister
|
|
// Set the value of a given register
|
|
//
|
|
// Parameters:
|
|
// reg - Register to set
|
|
// value - Value to set
|
|
void PSG_SetRegister(u8 reg, u8 value);
|
|
|
|
// Function: PSG_GetRegister
|
|
// Get the value of a given register
|
|
//
|
|
// Parameters:
|
|
// reg - Register to get
|
|
//
|
|
// Return:
|
|
// Value of the register
|
|
u8 PSG_GetRegister(u8 reg);
|
|
|
|
// Group: Helper
|
|
#if (PSG_USE_EXTRA)
|
|
// Function: PSG_SetTone
|
|
// Set the tone period of a given channel (tone generator control register)
|
|
//
|
|
// Parameters:
|
|
// chan - Channel to set (0 to 2; or PSG_CHANNEL_A, PSG_CHANNEL_B, PSG_CHANNEL_C)
|
|
// period - Period to set (12-bits value)
|
|
void PSG_SetTone(u8 chan, u16 period);
|
|
|
|
// Function: PSG_SetNoise
|
|
// Set the noise period (noise generator control register)
|
|
//
|
|
// Parameters:
|
|
// period - Period to set (5-bits value)
|
|
void PSG_SetNoise(u8 period);
|
|
|
|
// Function: PSG_SetMixer
|
|
// Setup mixer by enabling tune and noise generators for each channel (mixer control enable register)
|
|
//
|
|
// Parameters:
|
|
// mix - Mixer value to set
|
|
void PSG_SetMixer(u8 mix);
|
|
|
|
// Function: PSG_SetVolume
|
|
// Set the volume of a given channel (Amplitude control register)
|
|
//
|
|
// Parameters:
|
|
// chan - Channel to set (0 to 2; or PSG_CHANNEL_A, PSG_CHANNEL_B, PSG_CHANNEL_C)
|
|
// vol - Volume to set (4-bits value)
|
|
void PSG_SetVolume(u8 chan, u8 vol);
|
|
|
|
// Function: PSG_SetEnvelope
|
|
// Set the envelope period (Envelope priod control register)
|
|
//
|
|
// Parameters:
|
|
// period - Period to set (16-bits value)
|
|
void PSG_SetEnvelope(u16 period);
|
|
|
|
// Function: PSG_SetShape
|
|
// Set the envelope shape (Envelope shape control register)
|
|
//
|
|
// Parameters:
|
|
// shape - Shape to set (4-bits value)
|
|
void PSG_SetShape(u8 shape);
|
|
|
|
// Function: PSG_EnableTone
|
|
// Enable/disable tone on the given channel
|
|
//
|
|
// Parameters:
|
|
// chan - Channel to set (0 to 2; or PSG_CHANNEL_A, PSG_CHANNEL_B, PSG_CHANNEL_C)
|
|
// val - TRUE to enable, FALSE to disable
|
|
void PSG_EnableTone(u8 chan, bool val);
|
|
|
|
// Function: PSG_EnableNoise
|
|
// Enable/disable noise on the given channel
|
|
//
|
|
// Parameters:
|
|
// chan - Channel to set (0 to 2; or PSG_CHANNEL_A, PSG_CHANNEL_B, PSG_CHANNEL_C)
|
|
// val - TRUE to enable, FALSE to disable
|
|
void PSG_EnableNoise(u8 chan, bool val);
|
|
|
|
// Function: PSG_EnableEnvelope
|
|
// Enable/disable envelope on the given channel
|
|
//
|
|
// Parameters:
|
|
// chan - Channel to set (0 to 2; or PSG_CHANNEL_A, PSG_CHANNEL_B, PSG_CHANNEL_C)
|
|
|
|
void PSG_EnableEnvelope(u8 chan, bool val);
|
|
|
|
#endif //(PSG_USE_EXTRA)
|
|
|
|
// Function: PSG_Mute
|
|
// Silent the PSG (set according registers)
|
|
void PSG_Mute();
|
|
|
|
#if (PSG_USE_RESUME)
|
|
// Function: PSG_Resume
|
|
// Resume PSG sound
|
|
void PSG_Resume();
|
|
#endif
|
|
|
|
#if (PSG_ACCESS == PSG_INDIRECT)
|
|
|
|
// Group: Indirect
|
|
|
|
// Function: PSG_Apply
|
|
// Send data to PSG registers #0 to #13
|
|
void PSG_Apply();
|
|
|
|
#endif
|
|
|
|
|