Files
mazegame/projects/samples/s_lvgm.c
T

482 lines
13 KiB
C

// ____________________________
// ██▀▀█▀▀██▀▀▀▀▀▀▀█▀▀█ │ ▄▄▄ ▄▄
// ██ ▀ █▄ ▀██▄ ▀ ▄█ ▄▀▀ █ │ ▀█▄ ▄▀██ ▄█▄█ ██▀▄ ██ ▄███
// █ █ █ ▀▀ ▄█ █ █ ▀▄█ █▄ │ ▄▄█▀ ▀▄██ ██ █ ██▀ ▀█▄ ▀█▄▄
// ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀────────┘ ▀▀
// VGM format sample
//─────────────────────────────────────────────────────────────────────────────
#include "msxgl.h"
#include "vgm/lvgm_player.h"
#include "dos.h"
#include "dos_mapper.h"
#include "memory.h"
#include "psg.h"
#if (LVGM_USE_SCC)
#include "scc.h"
#endif
#if (LVGM_USE_MSXMUSIC)
#include "msx-music.h"
#endif
#if (LVGM_USE_MSXAUDIO)
#include "msx-audio.h"
#endif
//=============================================================================
// DEFINES
//=============================================================================
// Library's logo
#define MSX_GL "\x02\x03\x04\x05"
//
#define LOG_SIZE 16
// VGM music entry
struct MusicEntry
{
const c8* Name;
const c8* File;
};
// Player button entry
struct ButtonEntry
{
u8 Char;
callback Func;
};
// Button callbacks
void ButtonPlay();
void ButtonPause();
void ButtonStop();
void ButtonPrev();
void ButtonNext();
void ButtonLoop();
//=============================================================================
// READ-ONLY DATA
//=============================================================================
// Fonts
#include "font/font_mgl_sample8.h"
// Animation characters
const u8 g_ChrAnim[] = { '|', '\\', '-', '/' };
// Music list
const struct MusicEntry g_MusicEntry[] =
{
{ "Dragon Slayer 4", "ds4.lvm" },
{ "Maze of Galious", "galious.lvm" },
{ "Gambare Goemon", "goemon.lvm" },
{ "Metal Gear", "mg.lvm" },
{ "Hi no Tori", "honotori.lvm" },
{ "Penguin Adventure", "penguin.lvm" },
{ "Yurei Kun", "yureikun.lvm" },
{ "Final Fantasy (OPLL)", "ff.lvm" },
{ "Laydock 2 (OPLL)", "laydock2.lvm" },
{ "Undeadline (OPLL)", "undeadli.lvm" },
{ "F1 Spirit (SCC)", "f1spirit.lvm" },
{ "Salamander (SCC)", "salamand.lvm" },
{ "Space Manbow (SCC)", "manbow.lvm" },
{ "Metal Gear 2 (SCC)", "mg2.lvm" },
{ "Pro Yakyuu (OPL1)", "proyakyu.lvm" },
{ "Xevious (OPL1)", "xevious.lvm" },
};
// Player button list
const struct ButtonEntry g_ButtonEntry[] =
{
{ 0x80, ButtonPlay },
{ 0xB8, ButtonPause },
{ 0xB9, ButtonStop },
{ 0xBB, ButtonPrev },
{ 0xBA, ButtonNext },
{ 0xBE, ButtonLoop },
};
// Color shade
const u8 g_ColorBlink[4] = { COLOR_LIGHT_RED, COLOR_MEDIUM_RED, COLOR_DARK_RED, COLOR_MEDIUM_RED };
//=============================================================================
// MEMORY DATA
//=============================================================================
u8 g_CurrentMusic = 0;
u8 g_CurrentButton;
// Last segment
DOS_Segment g_SegList[4];
u8 g_SegIdx;
u8 g_SegLoop;
//=============================================================================
// HELPER FUNCTIONS
//=============================================================================
//-----------------------------------------------------------------------------
//
void DrawVGM(const u8* ptr)
{
Print_SetPosition(32-6, 4);
Print_DrawFormat("%i %4x", g_SegIdx, ptr);
}
//-----------------------------------------------------------------------------
//
void UpdatePlayer()
{
Print_SetPosition(0, 11);
Print_DrawText("Player:\n");
Print_DrawFormat("\x07" "Freq %s\n", (g_LVGM_State & LVGM_STATE_50HZ) ? "50Hz" : "60Hz");
Print_DrawFormat("\x07" "DoLoop %c\n", (g_LVGM_State & LVGM_STATE_LOOP) ? '\x0C' : '\x0B');
Print_DrawFormat("\x07" "DoPlay %c\n", (g_LVGM_State & LVGM_STATE_PLAY) ? '\x0C' : '\x0B');
}
//-----------------------------------------------------------------------------
//
bool MusicNotification(u8 id)
{
switch (id)
{
case 0: // End of data segment
g_SegIdx++;
DOSMapper_SetPage2(g_SegList[g_SegIdx].Number); // Select the next segment
LVGM_SetPointer((const u8*)0x8000); // Set playback address to the start of the next segment
return FALSE;
case 0xFE: // Reach loop marker
g_SegLoop = g_SegIdx; // Backup the segment that contain the loop marker
break;
case 0xFF: // Jump to loop marker
g_SegIdx = g_SegLoop; // Restore the segment that contain the loop marker
DOSMapper_SetPage2(g_SegList[g_SegIdx].Number);
}
return TRUE;
}
//-----------------------------------------------------------------------------
//
void SetMusic(u8 idx)
{
g_CurrentMusic = idx;
LVGM_Stop();
LVGM_Decode();
#if (PSG_ACCESS == PSG_INDIRECT)
PSG_Apply();
#endif
//.........................................................................
// Init mapper
g_SegIdx = 0;
g_SegLoop = 0;
DOSMapper_SetPage2(g_SegList[g_SegIdx].Number);
//.........................................................................
// Load file
u8 err;
Print_SetPosition(0, 2);
Print_DrawCharX(' ', 32);
Print_SetPosition(0, 2);
Print_DrawFormat("Load \"%s\"...", g_MusicEntry[idx].File);
// open
u8 file = DOS_FOpen(g_MusicEntry[idx].File, O_RDONLY);
if (file == 0xFF)
return;
// get size
u32 size = DOS_FSeek(file, 0, SEEK_END);
err = DOS_GetLastError();
if (err != DOS_ERR_NONE)
return;
// rewind
DOS_FSeek(file, 0, SEEK_SET);
// load
while (size > 16 * 1024)
{
DOS_FRead(file, (void*)0x8000, 16 * 1024);
err = DOS_GetLastError();
if (err != DOS_ERR_NONE)
return;
size -= 16 * 1024;
DOSMapper_SetPage2(g_SegList[++g_SegIdx].Number);
}
DOS_FRead(file, (void*)0x8000, size);
err = DOS_GetLastError();
if (err != DOS_ERR_NONE)
return;
Print_DrawText(" OK!");
//.........................................................................
// Start playback
g_SegIdx = 0;
DOSMapper_SetPage2(g_SegList[g_SegIdx].Number);
bool ok = LVGM_Play((const void*)0x8000, TRUE);
//.........................................................................
// Display music info
Print_SetPosition(0, 2);
Print_DrawCharX(' ', 32);
Print_SetPosition(0, 2);
Print_DrawFormat("%i/%i %s", 1 + idx, numberof(g_MusicEntry), g_MusicEntry[idx].Name);
Print_SetPosition(0, 4);
Print_DrawText("Data:\n");
Print_DrawFormat("\x07" "Ident %c\n", ok ? '\x0C' : '\x0B');
Print_DrawFormat("\x07" "Freq %i Hz\n", (g_LVGM_Header->Option & LVGM_OPTION_50HZ) ? 50 : 60);
Print_DrawFormat("\x07" "Loop %c\n", (g_LVGM_Header->Option & LVGM_OPTION_LOOP) ? '\x0C' : '\x0B');
Print_DrawCharX(' ', 32);
Print_SetPosition(0, 8);
Print_DrawFormat("\x07" "Chips %c", (g_LVGM_Header->Option & LVGM_OPTION_DEVICE) ? '\x0C' : '\x0B');
if (LVGM_GetDevices() & LVGM_CHIP_PSG)
Print_DrawText(" PSG");
if (LVGM_GetDevices() & LVGM_CHIP_OPLL)
Print_DrawText(" OPLL");
if (LVGM_GetDevices() & LVGM_CHIP_OPL1)
Print_DrawText(" OPL1");
if (LVGM_GetDevices() & LVGM_CHIP_SCC)
Print_DrawText(" SCC");
Print_SetPosition(0, 9);
Print_DrawFormat("\x07" "Default %c %2xh", (LVGM_GetDevices() & LVGM_CHIP_PSG) ? '\x0C' : '\x0B', LVGM_GetDefaultPSGValue());
UpdatePlayer();
}
//-----------------------------------------------------------------------------
//
void ButtonPlay()
{
LVGM_Resume();
UpdatePlayer();
}
//-----------------------------------------------------------------------------
//
void ButtonPause()
{
LVGM_Pause();
UpdatePlayer();
}
//-----------------------------------------------------------------------------
//
void ButtonStop()
{
LVGM_Stop();
UpdatePlayer();
}
//-----------------------------------------------------------------------------
//
void ButtonPrev()
{
if (g_CurrentMusic > 0)
SetMusic(g_CurrentMusic - 1);
else
SetMusic(numberof(g_MusicEntry) - 1);
}
//-----------------------------------------------------------------------------
//
void ButtonNext()
{
if (g_CurrentMusic < numberof(g_MusicEntry) - 1)
SetMusic(g_CurrentMusic + 1);
else
SetMusic(0);
}
//-----------------------------------------------------------------------------
//
void ButtonLoop()
{
if (g_LVGM_State & LVGM_STATE_LOOP)
g_LVGM_State &= ~LVGM_STATE_LOOP;
else
g_LVGM_State |= LVGM_STATE_LOOP;
UpdatePlayer();
}
//-----------------------------------------------------------------------------
//
void SetCursor(u8 id)
{
g_CurrentButton = id % 6;
VDP_SetSpriteSM1(0, 8 + 16*g_CurrentButton, (u8)(128 + 8 - 1), g_ButtonEntry[g_CurrentButton].Char, COLOR_LIGHT_RED);
}
//-----------------------------------------------------------------------------
//
void Print_DrawSlot(u8 slot)
{
if (slot == 0xFF)
{
Print_DrawText("No!");
return;
}
Print_DrawInt(Sys_SlotGetPrimary(slot));
if (Sys_SlotIsExpended(slot))
{
Print_DrawChar('-');
Print_DrawInt(Sys_SlotGetSecondary(slot));
}
}
//-----------------------------------------------------------------------------
//
void VDP_InterruptHandler()
{
VDP_SetColor(0xF4);
LVGM_Decode();
VDP_SetColor(0xF6);
#if (PSG_ACCESS == PSG_INDIRECT)
PSG_Apply();
#endif
VDP_SetColor(0xF0);
}
//=============================================================================
// MAIN LOOP
//=============================================================================
//-----------------------------------------------------------------------------
// Program entry point
void main()
{
// Initialize and alloc memory segment
if (!DOSMapper_Init())
{
DOS_StringOutput("Error: Unable to init mapper\n$");
DOS_Exit(1);
}
loop (i, 4)
{
if (!DOSMapper_Alloc(DOS_ALLOC_USER, DOS_SEGSLOT_PRIM, &g_SegList[i]))
{
DOS_StringOutput("Error: Can't allocate 4 segments of 16 KB\n$");
DOS_Exit(1);
}
}
// Initialize screen
VDP_SetMode(VDP_MODE_SCREEN1);
VDP_ClearVRAM();
VDP_EnableVBlank(TRUE);
// Initialize audio chip
LVGM_SetNotifyCallback(MusicNotification);
#if (LVGM_USE_SCC)
SCC_Initialize();
#endif
#if (LVGM_USE_MSXMUSIC)
MSXMusic_Initialize();
#endif
#if (LVGM_USE_MSXAUDIO)
MSXAudio_Initialize();
#endif
// Initialize font
Print_SetTextFont(g_Font_MGL_Sample8, 1); // Initialize font
Print_DrawText(MSX_GL " LVGM SAMPLE");
Print_DrawLineH(0, 1, 32);
Print_SetPosition(20, 11);
Print_DrawText("Main-ROM:");
Print_SetPosition(20, 12);
Print_DrawFormat("\x07" "Freq %s", Sys_Is50Hz() ? "50Hz" : "60Hz");
u8 Y = 17;
Print_SetPosition(20, Y++);
Print_DrawText("Slots: ");
#if (LVGM_USE_MSXMUSIC)
Print_SetPosition(20, Y++);
Print_DrawText("\x07" "YM2413: ");
Print_DrawSlot(g_MSXMusic_SlotId);
#endif
#if (LVGM_USE_MSXAUDIO)
Print_SetPosition(20, Y++);
Print_DrawText("\x07" "Y8950: ");
Print_DrawChar(MSXAudio_Detect() ? '\x0C' : '\x0B');
#endif
#if (LVGM_USE_SCC)
Print_SetPosition(20, Y++);
Print_DrawText("\x07" "SCC: ");
Print_DrawSlot(SCC_SLOT);
#endif
// Decode VGM header
SetMusic(0);
Print_DrawBox(0, 16, numberof(g_ButtonEntry) * 2 + 1, 3);
for (u8 i = 0; i < numberof(g_ButtonEntry); ++i)
{
Print_SetPosition(1 + 2 * i, 17);
Print_DrawChar(g_ButtonEntry[i].Char);
}
// Footer
Print_DrawLineH(0, 22, 32);
Print_SetPosition(0, 23);
Print_DrawText("\x8D:Button \x83:Action \x82:Freq");
//-------------------------------------------------------------------------
// 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]);
// BIOS_SetHookCallback(H_TIMI, VDP_InterruptHandler);
SetCursor(4);
u8 prevRow8 = 0xFF;
u8 count = 0;
while (1)
{
Halt();
VDP_InterruptHandler();
VDP_SetSpriteColorSM1(0, g_ColorBlink[(count >> 2) & 0x03]);
DrawVGM(g_LVGM_Pointer);
Print_SetPosition(31, 0);
u8 chr = count++ & 0x03;
Print_DrawChar(g_ChrAnim[chr]);
// Handle input
u8 row8 = Keyboard_Read(8);
// Change button
if (IS_KEY_PRESSED(row8, KEY_RIGHT) && !IS_KEY_PRESSED(prevRow8, KEY_RIGHT))
{
SetCursor((g_CurrentButton < numberof(g_ButtonEntry) - 1) ? g_CurrentButton + 1 : 0);
}
else if (IS_KEY_PRESSED(row8, KEY_LEFT) && !IS_KEY_PRESSED(prevRow8, KEY_LEFT))
{
SetCursor((g_CurrentButton > 0) ? g_CurrentButton - 1 : numberof(g_ButtonEntry) - 1);
}
// Activate button
if (IS_KEY_PRESSED(row8, KEY_SPACE) && !IS_KEY_PRESSED(prevRow8, KEY_SPACE))
{
g_ButtonEntry[g_CurrentButton].Func();
}
// Change frequency
if ((IS_KEY_PRESSED(row8, KEY_UP) && !IS_KEY_PRESSED(prevRow8, KEY_UP))
|| (IS_KEY_PRESSED(row8, KEY_DOWN) && !IS_KEY_PRESSED(prevRow8, KEY_DOWN)))
{
if (g_LVGM_State & LVGM_STATE_50HZ)
g_LVGM_State &= ~LVGM_STATE_50HZ;
else
g_LVGM_State |= LVGM_STATE_50HZ;
UpdatePlayer();
}
// Exit
if (IS_KEY_PRESSED(row8, KEY_ESC))
DOS_Exit(0);
prevRow8 = row8;
}
}