Files
mazegame/projects/samples/s_psg.c
T

490 lines
14 KiB
C

// ____________________________
// ██▀▀█▀▀██▀▀▀▀▀▀▀█▀▀█ │ ▄▄▄ ▄▄
// ██ ▀ █▄ ▀██▄ ▀ ▄█ ▄▀▀ █ │ ▀█▄ ▄▀██ ▄█▄█ ██▀▄ ██ ▄███
// █ █ █ ▀▀ ▄█ █ █ ▀▄█ █▄ │ ▄▄█▀ ▀▄██ ██ █ ██▀ ▀█▄ ▀█▄▄
// ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀────────┘ ▀▀
// Real-time clock module sample
//─────────────────────────────────────────────────────────────────────────────
//=============================================================================
// INCLUDES
//=============================================================================
#include "msxgl.h"
#include "psg.h"
//=============================================================================
// DEFINES
//=============================================================================
// Library's logo
#define MSX_GL "\x02\x03\x04\x05"
//
typedef void (*UICall)(u8 chan, u8 action);
//
struct UIEntry
{
u8 PosX;
u8 PosY;
u8 Chan;
UICall Func;
};
// Move directions
#define MOVE_UP -3
#define MOVE_RIGHT 1
#define MOVE_DOWN 3
#define MOVE_LEFT -1
// Action
#define ACTION_NONE 0
#define ACTION_PRESS_UP 1
#define ACTION_HOLD_UP 2
#define ACTION_PRESS_DOWN 3
#define ACTION_HOLD_DOWN 4
//
#define CURSOR_ID 25
#define TONE_FREQ_OFFSET 0x20
#define TONE_FREQ_MAX 0x0FFF
#define TONE_VOL_OFFSET 0x01
#define TONE_VOL_MAX 0x0F
#define ENV_FREQ_OFFSET 0x0200
#define ENV_FREQ_MAX 0xFFFF
#define ENV_SHAPE_OFFSET 0x01
#define ENV_SHAPE_MAX 0x0F
#define NOISE_FREQ_OFFSET 0x02
#define NOISE_FREQ_MAX 0x1F
//
void SetToneEnable(u8 chan, u8 action);
void SetTonePeriod(u8 chan, u8 action);
void SetToneVolume(u8 chan, u8 action);
void SetEnvEnable(u8 chan, u8 action);
void SetEnvPeriod(u8 chan, u8 action);
void SetEnvShape(u8 chan, u8 action);
void SetNoiseEnable(u8 chan, u8 action);
void SetNoisePeriod(u8 chan, u8 action);
//=============================================================================
// READ-ONLY DATA
//=============================================================================
// Fonts
#include "font/font_mgl_sample8.h"
//
const u8 g_ShapesId[16] = { 0, 0, 0, 0, 1, 1, 1, 1, 2, 0, 3, 4, 5, 6, 7, 1 };
//
const c8* g_ShapesName[8] =
{ // _ _
"\x1F\xAC\xAC\xAC\xAC\xAC\xAC\xAC", // 0-3, 9 _ \__________________ _
// _ _
"\xAA\xAC\xAC\xAC\xAC\xAC\xAC\xAC", // 4-7, 15 _ /|_________________ _
// _ _
"\xAB\xAB\xAB\xAB\xAB\xAB\xAB\xAB", // 8 _ \|\|\|\|\|\|\|\|\|\ _
// _ _
"\x1F\x1E\x1F\x1E\x1F\x1E\x1F\x1E", // 10 _ \/\/\/\/\/\/\/\/\/\ _
// _ _________________ _
"\xAB\xAD\xAD\xAD\xAD\xAD\xAD\xAD", // 11 _ \| _
// _ _
"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA", // 12 _ /|/|/|/|/|/|/|/|/|/ _
// _ __________________ _
"\x1E\xAD\xAD\xAD\xAD\xAD\xAD\xAD", // 13 _ / _
// _ _
"\x1E\x1F\x1E\x1F\x1E\x1F\x1E\x1F", // 14 _ /\/\/\/\/\/\/\/\/\/ _
};
// Color shade
const u8 g_ColorBlink[4] = { COLOR_LIGHT_RED, COLOR_MEDIUM_RED, COLOR_DARK_RED, COLOR_MEDIUM_RED };
//
const struct UIEntry g_UIEntries[24] =
{
{ 64, 56, 0, SetToneEnable }, { 128, 56, 1, SetToneEnable }, { 192, 56, 2, SetToneEnable },
{ 64, 64, 0, SetTonePeriod }, { 128, 64, 1, SetTonePeriod }, { 192, 64, 2, SetTonePeriod },
{ 64, 72, 0, SetToneVolume }, { 128, 72, 1, SetToneVolume }, { 192, 72, 2, SetToneVolume },
{ 64, 88, 0, SetEnvEnable }, { 128, 88, 1, SetEnvEnable }, { 192, 88, 2, SetEnvEnable },
{ 64, 96, 0, SetEnvPeriod }, { 128, 96, 1, NULL }, { 192, 96, 2, NULL },
{ 64, 104, 0, SetEnvShape }, { 128, 104, 1, NULL }, { 192, 104, 2, NULL },
{ 64, 120, 0, SetNoiseEnable }, { 128, 120, 1, SetNoiseEnable }, { 192, 120, 2, SetNoiseEnable },
{ 64, 128, 0, SetNoisePeriod }, { 128, 128, 1, NULL }, { 192, 128, 2, NULL },
};
//=============================================================================
// MEMORY DATA
//=============================================================================
u8 g_CursorPos = 0;
//=============================================================================
// HELPER FUNCTIONS
//=============================================================================
//-----------------------------------------------------------------------------
//
void SetToneEnable(u8 chan, u8 action)
{
u8 enable = (g_PSG_Regs.Mixer & (1 << chan)) ? 0 : 1;
if (action == ACTION_HOLD_UP)
enable = 1;
else if (action == ACTION_HOLD_DOWN)
enable = 0;
PSG_EnableTone(chan, enable);
VDP_SetSpritePattern(chan, enable ? 0x0C : 0x0B);
}
//-----------------------------------------------------------------------------
//
void SetTonePeriod(u8 chan, u8 action)
{
u16 val = g_PSG_Regs.Tone[chan];
if (action == ACTION_HOLD_UP)
{
if (val < TONE_FREQ_MAX - TONE_FREQ_OFFSET)
val += TONE_FREQ_OFFSET;
else
val = TONE_FREQ_MAX;
}
else if (action == ACTION_HOLD_DOWN)
{
if (val > TONE_FREQ_OFFSET)
val -= TONE_FREQ_OFFSET;
else
val = 0;
}
PSG_SetTone(chan, val);
Print_SetPosition(10 + chan * 8, 8);
Print_DrawFormat("%3x", val);
VDP_SetSpritePattern(3 + chan, (u8)(0xB0 + (val >> 9)));
}
//-----------------------------------------------------------------------------
//
void SetToneVolume(u8 chan, u8 action)
{
u8 val = g_PSG_Regs.Volume[chan];
u8 env = val & PSG_F_ENV;
val &= 0x0F;
if (action == ACTION_HOLD_UP)
{
if (val < TONE_VOL_MAX - TONE_VOL_OFFSET)
val += TONE_VOL_OFFSET;
else
val = TONE_VOL_MAX;
}
else if (action == ACTION_HOLD_DOWN)
{
if (val > TONE_VOL_OFFSET)
val -= TONE_VOL_OFFSET;
else
val = 0;
}
PSG_SetVolume(chan, val | env);
Print_SetPosition(10 + chan * 8, 9);
Print_DrawFormat("%1x", val);
VDP_SetSpritePattern(6 + chan, (u8)(0xB0 + (val >> 1)));
}
//-----------------------------------------------------------------------------
//
void SetEnvEnable(u8 chan, u8 action)
{
u8 enable = (g_PSG_Regs.Volume[chan] & PSG_F_ENV) ? 1 : 0;
if (action == ACTION_HOLD_UP)
enable = 1;
else if (action == ACTION_HOLD_DOWN)
enable = 0;
PSG_EnableEnvelope(chan, enable);
VDP_SetSpritePattern(9 + chan, enable ? 0x0C : 0x0B);
}
//-----------------------------------------------------------------------------
//
void SetEnvPeriod(u8 chan, u8 action)
{
u16 val = g_PSG_Regs.Envelope;
if (action == ACTION_HOLD_UP)
{
if (val < ENV_FREQ_MAX - ENV_FREQ_OFFSET)
val += ENV_FREQ_OFFSET;
else
val = ENV_FREQ_MAX;
}
else if (action == ACTION_HOLD_DOWN)
{
if (val > ENV_FREQ_OFFSET)
val -= ENV_FREQ_OFFSET;
else
val = 0;
}
PSG_SetEnvelope(val);
Print_SetPosition(10 + chan * 8, 12);
Print_DrawFormat("%4x", val);
VDP_SetSpritePattern(12 + chan, (u8)(0xB0 + (val >> 13)));
}
//-----------------------------------------------------------------------------
//
void SetEnvShape(u8 chan, u8 action)
{
u8 val = g_PSG_Regs.Shape;
val &= 0x0F;
if (action == ACTION_PRESS_UP)
{
if (val < ENV_SHAPE_MAX - ENV_SHAPE_OFFSET)
val += ENV_SHAPE_OFFSET;
else
val = ENV_SHAPE_MAX;
}
else if (action == ACTION_PRESS_DOWN)
{
if (val > ENV_SHAPE_OFFSET)
val -= ENV_SHAPE_OFFSET;
else
val = 0;
}
PSG_SetShape(val);
Print_SetPosition(10 + chan * 8, 13);
Print_DrawFormat("%1x %s", g_PSG_Regs.Shape & 0x0F, g_ShapesName[g_ShapesId[g_PSG_Regs.Shape & 0x0F]]);
VDP_SetSpritePattern(15 + chan, 0x82);
}
//-----------------------------------------------------------------------------
//
void SetNoiseEnable(u8 chan, u8 action)
{
u8 enable = (g_PSG_Regs.Mixer & (8 << chan)) ? 0 : 1;
if (action == ACTION_HOLD_UP)
enable = 1;
else if (action == ACTION_HOLD_DOWN)
enable = 0;
PSG_EnableNoise(chan, enable);
VDP_SetSpritePattern(18 + chan, enable ? 0x0C : 0x0B);
}
//-----------------------------------------------------------------------------
//
void SetNoisePeriod(u8 chan, u8 action)
{
u8 val = g_PSG_Regs.Noise;
if (action == ACTION_HOLD_UP)
{
if (val < NOISE_FREQ_MAX - NOISE_FREQ_OFFSET)
val += NOISE_FREQ_OFFSET;
else
val = NOISE_FREQ_MAX;
}
else if (action == ACTION_HOLD_DOWN)
{
if (val > NOISE_FREQ_OFFSET)
val -= NOISE_FREQ_OFFSET;
else
val = 0;
}
PSG_SetNoise(val);
Print_SetPosition(10 + chan * 8, 16);
Print_DrawFormat("%2x", val);
VDP_SetSpritePattern(21 + chan, (u8)(0xB0 + (val >> 2)));
}
//-----------------------------------------------------------------------------
// Set cursor to new position
void MoveCursor(i8 offset)
{
u8 newPos = g_CursorPos;
while (1)
{
newPos = (24 + newPos + offset) % 24;
if (g_UIEntries[newPos].Func != 0)
break;
}
VDP_SetSpriteColorSM1(g_CursorPos, COLOR_WHITE);
VDP_SetSpritePosition(CURSOR_ID, g_UIEntries[newPos].PosX - 8, g_UIEntries[newPos].PosY - 1);
VDP_SetSpriteColorSM1(newPos, COLOR_LIGHT_RED);
g_CursorPos = newPos;
}
//=============================================================================
// MAIN LOOP
//=============================================================================
//-----------------------------------------------------------------------------
// Program entry point
void main()
{
VDP_SetMode(VDP_MODE_SCREEN1); // Initialize screen mode 1 (G1)
VDP_SetColor2(COLOR_BLACK, COLOR_WHITE);
VDP_ClearVRAM();
VDP_EnableVBlank(TRUE);
Print_SetTextFont(g_Font_MGL_Sample8, 0); // Initialize font
VDP_FillVRAM_16K(0, g_ScreenPatternLow, 8);
Print_DrawText(MSX_GL " PSG SAMPLE");
Print_DrawLineH(0, 1, 32);
#if (PSG_CHIP == PSG_INTERNAL)
Print_DrawFormat("Chip: Intern (%2x)\n", PSG_PORT_REG);
#elif (PSG_CHIP == PSG_EXTERNAL)
Print_DrawFormat("Chip: Extern (%2x)\n", PSG_PORT_REG);
#elif (PSG_CHIP == PSG_BOTH)
Print_DrawFormat("Chip: Both (%2x+%2x)\n", PSG_PORT_REG, PSG2_PORT_REG);
#endif
#if (PSG_ACCESS == PSG_DIRECT)
Print_DrawText("Access:Direct\n");
#else
Print_DrawText("Access:Indirect\n");
#endif
//-------------------------------------------------------------------------
// Sprite
VDP_SetSpriteFlag(VDP_SPRITE_SIZE_8 + VDP_SPRITE_SCALE_1);
VDP_LoadSpritePattern(g_Font_MGL_Sample8 + 4, 0, g_Font_MGL_Sample8[3] - g_Font_MGL_Sample8[2]);
VDP_SetSpriteSM1(CURSOR_ID, 100, 100, 0x8A, COLOR_LIGHT_RED);
MoveCursor(0);
//-------------------------------------------------------------------------
// Setup PSG
PSG_SetTone(0, 0x400);
PSG_SetTone(1, 0x800);
PSG_SetTone(2, 0xA00);
PSG_SetNoise(0x10);
PSG_SetMixer(0);
PSG_SetVolume(0, 0xF);
PSG_SetVolume(1, 0xF);
PSG_SetVolume(2, 0xF);
PSG_SetEnvelope(0x8000);
PSG_SetShape(8);
//-------------------------------------------------------------------------
// Draw page
Print_SetPosition(8, 5);
Print_DrawText("Chan A Chan B Chan C\n");
Print_DrawCharX(0x1C, 32);
Print_DrawText("Tone\n");
Print_DrawText("Freq\n");
Print_DrawText("Vol\n");
Print_DrawCharX(0x1C, 32);
Print_DrawText("Env\n");
Print_DrawText("Freq\n");
Print_DrawText("Shape\n");
Print_DrawCharX(0x1C, 32);
Print_DrawText("Noise\n");
Print_DrawText("Freq\n");
Print_DrawCharX(0x1C, 32);
Print_SetPosition(4, 19);
for (u8 i = 0; i < 14; ++i)
Print_DrawFormat("%1x ", i);
Print_DrawText("\nRAW:");
// Footer
Print_DrawLineH(0, 22, 32);
Print_SetPosition(0, 23);
Print_DrawText("\x81\x82\x80:Move H:Up N:Down");
// Sprite
for (u8 i = 0; i < 24; ++i)
{
const struct UIEntry* entry = &g_UIEntries[i];
if (entry->Func)
VDP_SetSpriteSM1(i, entry->PosX, entry->PosY - 1, 0, COLOR_WHITE);
else
VDP_SetSpriteSM1(i, 0, 213, 0, 0);
}
SetToneEnable(0, ACTION_NONE);
SetToneEnable(1, ACTION_NONE);
SetToneEnable(2, ACTION_NONE);
SetTonePeriod(0, ACTION_NONE);
SetTonePeriod(1, ACTION_NONE);
SetTonePeriod(2, ACTION_NONE);
SetToneVolume(0, ACTION_NONE);
SetToneVolume(1, ACTION_NONE);
SetToneVolume(2, ACTION_NONE);
SetEnvEnable(0, ACTION_NONE);
SetEnvEnable(1, ACTION_NONE);
SetEnvEnable(2, ACTION_NONE);
SetEnvPeriod(0, ACTION_NONE);
SetEnvShape(0, ACTION_NONE);
SetNoiseEnable(0, ACTION_NONE);
SetNoiseEnable(1, ACTION_NONE);
SetNoiseEnable(2, ACTION_NONE);
SetNoisePeriod(0, ACTION_NONE);
u8 count = 0;
u8 prevRow8 = 0xFF;
u8 prevUp = 0;
u8 prevDown = 0;
while (1)
{
VDP_SetSpriteColorSM1(CURSOR_ID, g_ColorBlink[(count >> 2) & 0x03]);
VDP_SetSpriteColorSM1(g_CursorPos, g_ColorBlink[(count >> 2) & 0x03]);
Print_SetPosition(31, 0);
Print_DrawChar(0xB0 + ((count) & 0x07));
// Move cursor
u8 row8 = Keyboard_Read(8);
if (IS_KEY_PRESSED(row8, KEY_UP) && !IS_KEY_PRESSED(prevRow8, KEY_UP))
MoveCursor(MOVE_UP);
else if (IS_KEY_PRESSED(row8, KEY_RIGHT) && !IS_KEY_PRESSED(prevRow8, KEY_RIGHT))
MoveCursor(MOVE_RIGHT);
else if (IS_KEY_PRESSED(row8, KEY_DOWN) && !IS_KEY_PRESSED(prevRow8, KEY_DOWN))
MoveCursor(MOVE_DOWN);
else if (IS_KEY_PRESSED(row8, KEY_LEFT) && !IS_KEY_PRESSED(prevRow8, KEY_LEFT))
MoveCursor(MOVE_LEFT);
prevRow8 = row8;
// Increase value
u8 newUp = Keyboard_IsKeyPressed(KEY_H);
if (newUp)
{
const struct UIEntry* entry = &g_UIEntries[g_CursorPos];
if (!prevUp)
entry->Func(entry->Chan, ACTION_PRESS_UP);
else
entry->Func(entry->Chan, ACTION_HOLD_UP);
}
prevUp = newUp;
u8 newDown = Keyboard_IsKeyPressed(KEY_N);
if (newDown)
{
const struct UIEntry* entry = &g_UIEntries[g_CursorPos];
if (!prevDown)
entry->Func(entry->Chan, ACTION_PRESS_DOWN);
else
entry->Func(entry->Chan, ACTION_HOLD_DOWN);
}
prevDown = newDown;
Halt();
count++;
Print_SetPosition(4, 20);
i8* ptr = (u8*)g_PSG_Regs;
for (u8 i = 0; i < 14; ++i)
Print_DrawHex8(*ptr++);
PSG_Apply();
}
}