// ____________________________ // ██▀▀█▀▀██▀▀▀▀▀▀▀█▀▀█ │ ▄▄▄ ▄▄▄ ▄▄▄ // ██ ▀ █▄ ▀██▄ ▀ ▄█ ▄▀▀ █ │ ██▄▀ ▀█▄ ██ // █ █ █ ▀▀ ▄█ █ █ ▀▄█ █▄ │ ██ ▄▄█▀ ▀█▄█ // ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀────────┘ // 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