Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1005 lines
25 KiB
C
1005 lines
25 KiB
C
// ____________________________
|
|
// ██▀▀█▀▀██▀▀▀▀▀▀▀█▀▀█ │ ▄▄▄ ▄▄
|
|
// ██ ▀ █▄ ▀██▄ ▀ ▄█ ▄▀▀ █ │ ▀█▄ ▄▀██ ▄█▄█ ██▀▄ ██ ▄███
|
|
// █ █ █ ▀▀ ▄█ █ █ ▀▄█ █▄ │ ▄▄█▀ ▀▄██ ██ █ ██▀ ▀█▄ ▀█▄▄
|
|
// ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀────────┘ ▀▀
|
|
// Hello world sample
|
|
//─────────────────────────────────────────────────────────────────────────────
|
|
#include "msxgl.h"
|
|
#include "v9990.h"
|
|
#include "math.h"
|
|
#include "memory.h"
|
|
|
|
//=============================================================================
|
|
// DEFINES
|
|
//=============================================================================
|
|
|
|
#define CUSTOM_ISR 0
|
|
|
|
// Library's logo
|
|
#define MSX_GL "\x01\x02\x03\x04\x05\x06"
|
|
|
|
// Display constants
|
|
#define V9_GROUND_Y 20
|
|
#define V9_HORIZON_Y 14
|
|
#define BMP_TILES_Y 256
|
|
#define BG_COLOR 14
|
|
|
|
// Bits per pixel
|
|
#define BPP_2 0x02
|
|
#define BPP_4 0x04
|
|
#define BPP_8 0x08
|
|
#define BPP_16 0x10
|
|
|
|
// Screen mode index (match the g_ScreenMode table)
|
|
enum
|
|
{
|
|
#if (V9_USE_MODE_P1)
|
|
MODE_P1,
|
|
#endif
|
|
#if (V9_USE_MODE_P2)
|
|
MODE_P2,
|
|
#endif
|
|
#if ((V9_USE_MODE_B0) || (V9_USE_MODE_B1) || (V9_USE_MODE_B2) || (V9_USE_MODE_B3) || (V9_USE_MODE_B4) || (V9_USE_MODE_B5) || (V9_USE_MODE_B6) || (V9_USE_MODE_B7))
|
|
MODE_B0,
|
|
MODE_B1,
|
|
MODE_B2,
|
|
MODE_B3,
|
|
MODE_B4,
|
|
MODE_B5,
|
|
MODE_B6,
|
|
MODE_B7,
|
|
#endif
|
|
MODE_MAX,
|
|
};
|
|
|
|
// Screen mode structure
|
|
struct ScreenMode
|
|
{
|
|
const c8* Name;
|
|
u8 Mode;
|
|
callback Init;
|
|
callback Tick;
|
|
callback Hblank;
|
|
u8 BPP;
|
|
u16 Width;
|
|
u16 Height;
|
|
};
|
|
|
|
// Color mode structure
|
|
struct ColorMode
|
|
{
|
|
const c8* Name;
|
|
u8 Mode;
|
|
u8 BPP;
|
|
u16 ScaneLine;
|
|
};
|
|
|
|
// Function prototypes
|
|
void InitP1(); void TickP1(); void HblankP1();
|
|
void InitP2(); void TickP2(); void HblankP2();
|
|
void InitBmp(); void TickBmp(); void HblankBmp();
|
|
|
|
//=============================================================================
|
|
// READ-ONLY DATA
|
|
//=============================================================================
|
|
|
|
// 1-bit font (VDP)
|
|
#include "font/font_mgl_sample6.h"
|
|
|
|
// 4-bits font (V9990)
|
|
#include "content/v9990/data_v9_font.h"
|
|
|
|
// Sprite
|
|
#include "content/v9990/data_v9_chr.h"
|
|
|
|
// Tiles
|
|
#include "content/v9990/data_v9_bg.h"
|
|
|
|
// Trigo table
|
|
#include "mathtable/mt_sin_64.inc"
|
|
|
|
// Animation characters
|
|
const u8 g_ChrAnim[4] = { '|', '\\', '-', '/' };
|
|
|
|
// Black color
|
|
const u8 g_ColorBlack[] = { 0, 0, 0 };
|
|
const u8 g_ColorBG[] = { 0x07, 0x12, 0x1C };
|
|
|
|
// Screen mode configuration
|
|
const struct ScreenMode g_ScreenMode[MODE_MAX] =
|
|
{
|
|
#if (V9_USE_MODE_P1)
|
|
{ "P1", V9_MODE_P1, InitP1, TickP1, HblankP1, BPP_4, 256, 212 },
|
|
#endif
|
|
#if (V9_USE_MODE_P2)
|
|
{ "P2", V9_MODE_P2, InitP2, TickP2, HblankP2, BPP_4, 512, 212 },
|
|
#endif
|
|
#if ((V9_USE_MODE_B0) || (V9_USE_MODE_B1) || (V9_USE_MODE_B2) || (V9_USE_MODE_B3) || (V9_USE_MODE_B4) || (V9_USE_MODE_B5) || (V9_USE_MODE_B6) || (V9_USE_MODE_B7))
|
|
{ "B0", V9_MODE_B0, InitBmp, TickBmp, HblankBmp, BPP_2|BPP_4|BPP_8|BPP_16, 192, 240 },
|
|
{ "B1", V9_MODE_B1, InitBmp, TickBmp, HblankBmp, BPP_2|BPP_4|BPP_8|BPP_16, 256, 212 },
|
|
{ "B2", V9_MODE_B2, InitBmp, TickBmp, HblankBmp, BPP_2|BPP_4|BPP_8|BPP_16, 384, 240 },
|
|
{ "B3", V9_MODE_B3, InitBmp, TickBmp, HblankBmp, BPP_2|BPP_4|BPP_8|BPP_16, 512, 212 },
|
|
{ "B4", V9_MODE_B4, InitBmp, TickBmp, HblankBmp, BPP_2|BPP_4, 768, 240 },
|
|
{ "B5", V9_MODE_B5, InitBmp, TickBmp, HblankBmp, BPP_2|BPP_4, 640, 400 },
|
|
{ "B6", V9_MODE_B6, InitBmp, TickBmp, HblankBmp, BPP_2|BPP_4, 640, 480 },
|
|
{ "B7", V9_MODE_B7, InitBmp, TickBmp, HblankBmp, BPP_2|BPP_4, 1024, 212 },
|
|
#endif
|
|
};
|
|
|
|
// Color mode configuration for bitmap modes
|
|
const struct ColorMode g_ColorMode[V9_COLOR_BMP_MAX] =
|
|
{
|
|
{ "BP2", V9_COLOR_BP2, BPP_2, 256 }, // Color palette (4 colors out of 32768 colors)
|
|
{ "BP4", V9_COLOR_BP4, BPP_4, 512 }, // Color palette (16 colors out of 32768 colors)
|
|
{ "BP6", V9_COLOR_BP6, BPP_8, 1024 }, // Color palette (64 colors out of 32768 colors)
|
|
{ "BD8", V9_COLOR_BD8, BPP_8, 1024 }, // Direct RGB [G:3|R:3|B:2] (256 colors)
|
|
{ "BYJK", V9_COLOR_BYJK, BPP_8, 1024 }, // YJK Decoder (19268 colors)
|
|
{ "BYJKP", V9_COLOR_BYJKP, BPP_8, 1024 }, // YJK Decoder + Color palette (12599 colors + 16 colors out of 32768 colors)
|
|
{ "BYUV", V9_COLOR_BYUV, BPP_8, 1024 }, // YUV Decoder (19268 colors)
|
|
{ "BYUVP", V9_COLOR_BYUVP, BPP_8, 1024 }, // YUV Decoder + Color palette (12599 colors + 16 colors out of 32768 colors)
|
|
{ "BD16", V9_COLOR_BD16, BPP_16, 2048 }, // Direct RGB [YS|G:5|R:2][R:3|B:5] (32768 colors)
|
|
};
|
|
|
|
//=============================================================================
|
|
// MEMORY DATA
|
|
//=============================================================================
|
|
|
|
u8 g_CurrentScreen = MODE_P1; // Current screen mode index in the g_ScreenMode table
|
|
u8 g_CurrentColor = V9_COLOR_BP4; // Current color mode index in the g_ColorMode table
|
|
|
|
u16 g_Frame = 0;
|
|
bool g_Interlaced = false;
|
|
u16 g_TextX;
|
|
u16 g_TextY;
|
|
bool g_HideSprite = FALSE;
|
|
bool g_KeyPressed[256];
|
|
|
|
//=============================================================================
|
|
// CODE
|
|
//=============================================================================
|
|
|
|
void InitPalette()
|
|
{
|
|
switch (g_ScreenMode[g_CurrentScreen].Mode)
|
|
{
|
|
case V9_MODE_P1:
|
|
case V9_MODE_P2:
|
|
V9_SetPaletteEntry(0, g_ColorBlack);
|
|
V9_SetPalette(1, 15, g_DataV9BG_palette);
|
|
V9_SetPaletteEntry(16, g_ColorBlack);
|
|
V9_SetPalette(17, 15, g_DataV9Chr_palette);
|
|
V9_SetPaletteEntry(32, g_ColorBlack);
|
|
V9_SetPalette(33, 15, g_DataV9Font_palette);
|
|
break;
|
|
case V9_MODE_B0:
|
|
case V9_MODE_B1:
|
|
case V9_MODE_B2:
|
|
case V9_MODE_B3:
|
|
V9_SetPaletteEntry(0, g_ColorBlack);
|
|
V9_SetPalette(1, 15, g_DataV9BG_palette);
|
|
V9_SetPaletteEntry(16, g_ColorBlack);
|
|
V9_SetPalette(17, 15, g_DataV9Font_palette);
|
|
V9_SetPaletteEntry(16 + 6, g_ColorBG);
|
|
break;
|
|
case V9_MODE_B4:
|
|
case V9_MODE_B5:
|
|
case V9_MODE_B6:
|
|
case V9_MODE_B7:
|
|
loop (i, 2)
|
|
{
|
|
V9_SetPaletteEntry(i * 32 + 0, g_ColorBlack);
|
|
V9_SetPalette(i * 32 + 1, 15, g_DataV9BG_palette);
|
|
V9_SetPaletteEntry(i * 32 + 16, g_ColorBlack);
|
|
V9_SetPalette(i * 32 + 17, 15, g_DataV9Font_palette);
|
|
V9_SetPaletteEntry(i * 32 + 16 + 6, g_ColorBG);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// P1 MODE (256x212, 2 layers)
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if (V9_USE_MODE_P1)
|
|
|
|
void V9_Print(u32 addr, const c8* str)
|
|
{
|
|
while (*str != 0)
|
|
V9_Poke16(addr += 2, 512 + *(str++) - ' ');
|
|
}
|
|
|
|
//
|
|
void InitP1()
|
|
{
|
|
// Initialize screen mode
|
|
V9_SetScreenMode(V9_MODE_P1);
|
|
V9_SetDisplayEnable(FALSE);
|
|
V9_SetInterrupt(V9_INT_NONE);
|
|
V9_SetBackgroundColor(BG_COLOR);
|
|
V9_SetSpriteEnable(TRUE);
|
|
|
|
InitPalette();
|
|
|
|
// Pattern generator table
|
|
V9_WriteVRAM(V9_P1_PGT_A, g_DataV9BG, sizeof(g_DataV9BG));
|
|
V9_WriteVRAM(V9_P1_PGT_A + 0x04000, g_DataV9Font, sizeof(g_DataV9Font));
|
|
V9_WriteVRAM(V9_P1_PGT_B, g_DataV9BG, sizeof(g_DataV9BG));
|
|
|
|
// Pattern name table
|
|
V9_FillVRAM16(V9_P1_PNT_A, 0x0000, 64*64); // Init layer A
|
|
V9_Print(V9_P1_PNT_A, "MSXgl V9990 - P1 Mode (PP)"); // Draw title
|
|
for (u8 i = 0; i < 8; ++i) // Draw plateform
|
|
{
|
|
u8 x = i * 8;
|
|
u8 y = Math_GetRandom8() % 8 + 10;
|
|
for (u8 j = y; j < V9_GROUND_Y; j++)
|
|
{
|
|
u8 cell;
|
|
if (j == y)
|
|
cell = 32;
|
|
else if (((j - y) & 1))
|
|
cell = 64;
|
|
else
|
|
cell = 96;
|
|
|
|
u32 addr = V9_CellAddrP1A(x, j);
|
|
V9_Poke16(addr += 2, cell++);
|
|
V9_Poke16(addr += 2, cell++);
|
|
V9_Poke16(addr += 2, cell++);
|
|
V9_Poke16(addr += 2, cell++);
|
|
}
|
|
}
|
|
for (u16 i = 0; i < 64; ++i) // Draw ground
|
|
{
|
|
for (u8 j = V9_GROUND_Y; j < 27; j++)
|
|
{
|
|
u8 cell;
|
|
if (j == V9_GROUND_Y)
|
|
cell = 33;
|
|
else
|
|
cell = (j & 1) ? 65 : 97;
|
|
V9_Poke16(V9_CellAddrP1A(0, j) + i*2, cell + i%2);
|
|
}
|
|
}
|
|
|
|
V9_FillVRAM16(V9_P1_PNT_B, 160, 64*27); // Init layer B
|
|
for (u16 i = 0; i < 64; ++i) // Draw horizon
|
|
V9_Poke16(V9_CellAddrP1B(0, V9_HORIZON_Y) + i*2, 128 + i%4);
|
|
V9_FillVRAM16(V9_P1_PNT_B, 135, 64*V9_HORIZON_Y); // Draw sky
|
|
for (u8 i = 0; i < 6; ++i) // Draw big cloud
|
|
{
|
|
u8 x = Math_GetRandom8() % 8 + (i * 10);
|
|
u8 y = (i == 0) ? 0 : Math_GetRandom8() % 8;
|
|
u32 addr = V9_CellAddrP1B(x, y);
|
|
V9_Poke16(addr, 132); addr += 2;
|
|
V9_Poke16(addr, 133); addr += 2;
|
|
V9_Poke16(addr, 134); addr += 2;
|
|
V9_Poke16(addr, 135);
|
|
addr = V9_CellAddrP1B(x, y +1);
|
|
V9_Poke16(addr, 164); addr += 2;
|
|
V9_Poke16(addr, 165); addr += 2;
|
|
V9_Poke16(addr, 166); addr += 2;
|
|
V9_Poke16(addr, 167);
|
|
}
|
|
for (u8 i = 0; i < 6; ++i) // Draw small cloud
|
|
{
|
|
u8 x = Math_GetRandom8() % 8 + (i * 10);
|
|
u8 y = Math_GetRandom8() % 4 + 9;
|
|
u32 addr = V9_CellAddrP1B(x, y);
|
|
V9_Poke16(addr, 136); addr += 2;
|
|
V9_Poke16(addr, 137);
|
|
addr = V9_CellAddrP1B(x, y +1);
|
|
V9_Poke16(addr, 168); addr += 2;
|
|
V9_Poke16(addr, 169);
|
|
}
|
|
|
|
// Sprite pattern generator table
|
|
V9_SetSpritePatternAddr(V9_P1_SGT_08000);
|
|
V9_WriteVRAM(0x08000, g_DataV9Chr, sizeof(g_DataV9Chr));
|
|
|
|
// Sprite attribute table
|
|
struct V9_Sprite attr;
|
|
for (u16 i = 0; i < 125; ++i)
|
|
{
|
|
attr.Y = (i / 13) * 20 + 12;
|
|
attr.Pattern = 0;
|
|
attr.X = (i % 13) * 20;
|
|
attr.P = 0;
|
|
attr.D = 0;
|
|
attr.SC = 1;
|
|
V9_SetSpriteP1(i, &attr);
|
|
}
|
|
|
|
V9_SetInterrupt(V9_INT_VBLANK | V9_INT_HBLANK);
|
|
V9_SetInterruptLine(71);
|
|
V9_SetDisplayEnable(TRUE);
|
|
}
|
|
|
|
//
|
|
void TickP1()
|
|
{
|
|
V9_SelectPaletteP1(2, 0);
|
|
V9_SetScrollingX(0);
|
|
V9_SetScrollingBX(g_Frame >> 1);
|
|
|
|
u8 frame = (g_Frame >> 2) % 6;
|
|
for (u16 i = 0; i < 125; ++i)
|
|
V9_SetSpritePatternP1(i, frame);
|
|
|
|
g_Frame++;
|
|
}
|
|
|
|
//
|
|
void HblankP1()
|
|
{
|
|
V9_SelectPaletteP1(0, 0);
|
|
V9_SetScrollingX(g_Frame >> 0);
|
|
V9_SetScrollingBX(g_Frame >> 2);
|
|
}
|
|
|
|
#endif // (V9_USE_MODE_P1)
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// P1 MODE (512x212, 1 layer)
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if (V9_USE_MODE_P2)
|
|
|
|
void V9_WriteVRAM_256to512(u32 addr, const u8* src, u8 line)
|
|
{
|
|
for (u8 i = 0; i < line; ++i)
|
|
{
|
|
u8 line = i % 8;
|
|
u8 row = i / 2 / 8 * 8;
|
|
u8 odd = (i / 8) & 1;
|
|
V9_WriteVRAM(addr + (256 * (row + line)) + (128 * odd), src + (128 * i), 128);
|
|
}
|
|
}
|
|
|
|
//
|
|
void InitP2()
|
|
{
|
|
// Initialize screen mode
|
|
V9_SetScreenMode(V9_MODE_P2);
|
|
V9_SetDisplayEnable(FALSE);
|
|
V9_SetInterrupt(V9_INT_NONE);
|
|
V9_SetBackgroundColor(BG_COLOR);
|
|
V9_SetSpriteEnable(TRUE);
|
|
|
|
InitPalette();
|
|
|
|
// Pattern generator table
|
|
V9_WriteVRAM_256to512(V9_P2_PGT + 0x00000, g_DataV9BG, 48);
|
|
V9_WriteVRAM_256to512(V9_P2_PGT + 0x04000, g_DataV9Font, 24);
|
|
|
|
// Pattern name table
|
|
// for (u16 i = 0; i < 64*27; ++i)
|
|
// {
|
|
// u16 addr = (((i / 64) * 128) + (i % 64)) * 2;
|
|
// V9_Poke16(V9_P2_PNT + addr, i);
|
|
// addr += 2;
|
|
// }
|
|
V9_FillVRAM16(V9_P2_PNT, 0, 128*2);
|
|
V9_Print(V9_P2_PNT, "MSXgl V9990 - P2 Mode (PP)"); // Draw title
|
|
V9_FillVRAM16(V9_P2_PNT + 512, 135, 128*V9_HORIZON_Y); // Draw sky
|
|
for (u16 i = 0; i < 128; ++i) // Draw horizon
|
|
V9_Poke16(V9_CellAddrP2(0, V9_HORIZON_Y) + i*2, 128 + i%4);
|
|
V9_FillVRAM16(V9_CellAddrP2(0, V9_HORIZON_Y+1), 160, (27-V9_HORIZON_Y)*128); // Draw sea
|
|
for (u8 i = 0; i < 12; ++i) // Draw big cloud
|
|
{
|
|
u8 x = Math_GetRandom8() % 8 + (i * 10);
|
|
u8 y = Math_GetRandom8() % 4 + 4;
|
|
u32 addr = V9_CellAddrP2(x, y);
|
|
V9_Poke16(addr, 132); addr += 2;
|
|
V9_Poke16(addr, 133); addr += 2;
|
|
V9_Poke16(addr, 134); addr += 2;
|
|
V9_Poke16(addr, 135);
|
|
addr = V9_CellAddrP2(x, y +1);
|
|
V9_Poke16(addr, 164); addr += 2;
|
|
V9_Poke16(addr, 165); addr += 2;
|
|
V9_Poke16(addr, 166); addr += 2;
|
|
V9_Poke16(addr, 167);
|
|
}
|
|
for (u8 i = 0; i < 12; ++i) // Draw small cloud
|
|
{
|
|
u8 x = Math_GetRandom8() % 8 + (i * 10);
|
|
u8 y = Math_GetRandom8() % 4 + 9;
|
|
u32 addr = V9_CellAddrP2(x, y);
|
|
V9_Poke16(addr, 136); addr += 2;
|
|
V9_Poke16(addr, 137);
|
|
addr = V9_CellAddrP2(x, y +1);
|
|
V9_Poke16(addr, 168); addr += 2;
|
|
V9_Poke16(addr, 169);
|
|
}
|
|
for (u8 i = 0; i < 8; ++i) // Draw plateform
|
|
{
|
|
u8 x = i * 16;
|
|
u8 y = Math_GetRandom8() % 8 + 10;
|
|
for (u8 j = y; j < V9_GROUND_Y; j++)
|
|
{
|
|
u8 cell;
|
|
if (j == y)
|
|
cell = 32;
|
|
else if (((j - y) & 1))
|
|
cell = 64;
|
|
else
|
|
cell = 96;
|
|
|
|
u32 addr = V9_CellAddrP2(x, j);
|
|
V9_Poke16(addr += 2, cell++);
|
|
V9_Poke16(addr += 2, cell++);
|
|
V9_Poke16(addr += 2, cell++); cell -= 2;
|
|
V9_Poke16(addr += 2, cell++);
|
|
V9_Poke16(addr += 2, cell++); cell -= 2;
|
|
V9_Poke16(addr += 2, cell++);
|
|
V9_Poke16(addr += 2, cell++);
|
|
V9_Poke16(addr += 2, cell++);
|
|
}
|
|
}
|
|
for (u16 i = 0; i < 128; ++i) // Draw ground
|
|
{
|
|
for (u8 j = V9_GROUND_Y; j < 27; j++)
|
|
{
|
|
u8 cell;
|
|
if (j == V9_GROUND_Y)
|
|
cell = 33;
|
|
else
|
|
cell = (j & 1) ? 65 : 97;
|
|
V9_Poke16(V9_CellAddrP2(0, j) + i*2, cell + i%2);
|
|
}
|
|
}
|
|
|
|
// Sprite pattern generator table
|
|
V9_SetSpritePatternAddr(V9_P2_SGT_08000);
|
|
for (u8 i = 0; i < 16; ++i)
|
|
V9_WriteVRAM(V9_P2_PGT + 0x08000 + (256 * i), g_DataV9Chr + (128 * i), 128);
|
|
|
|
// Sprite attribute table
|
|
struct V9_Sprite attr;
|
|
for (u8 i = 0; i < 125; ++i)
|
|
{
|
|
attr.Y = (i / 13) * 20 + 12;
|
|
attr.Pattern = 0;
|
|
attr.X = (i % 13) * 40 + 8;
|
|
attr.P = 0;
|
|
attr.D = 0;
|
|
attr.SC = 1;
|
|
V9_SetSpriteP2(i, &attr);
|
|
}
|
|
|
|
V9_SetInterrupt(V9_INT_VBLANK | V9_INT_HBLANK);
|
|
V9_SetInterruptLine(6);
|
|
V9_SetDisplayEnable(TRUE);
|
|
}
|
|
|
|
//
|
|
void TickP2()
|
|
{
|
|
V9_SelectPaletteP2(2);
|
|
V9_SetScrollingX(0);
|
|
|
|
u8 frame = (g_Frame >> 2) % 6;
|
|
for (u16 i = 0; i < 125; ++i)
|
|
V9_SetSpritePatternP2(i, frame);
|
|
|
|
g_Frame++;
|
|
}
|
|
|
|
//
|
|
void HblankP2()
|
|
{
|
|
V9_SelectPaletteP2(0);
|
|
V9_SetScrollingX(g_Frame >> 0);
|
|
V9_SetScrollingBX(g_Frame >> 2);
|
|
}
|
|
|
|
#endif // (V9_USE_MODE_P1)
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// BITMAP MODE
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if ((V9_USE_MODE_B0) || (V9_USE_MODE_B1) || (V9_USE_MODE_B2) || (V9_USE_MODE_B3) || (V9_USE_MODE_B4) || (V9_USE_MODE_B5) || (V9_USE_MODE_B6) || (V9_USE_MODE_B7))
|
|
|
|
//
|
|
void V9_SetPrintPositionBmp(u16 x, u16 y)
|
|
{
|
|
g_TextX = x;
|
|
g_TextY = y;
|
|
}
|
|
|
|
//
|
|
void V9_PrintBmpChar(c8 chr)
|
|
{
|
|
u8 idx = chr - ' ';
|
|
|
|
V9_WaitCmdEnd();
|
|
V9_CommandLMMM(idx % 32 * 8, BMP_TILES_Y + 48 + (idx / 32) * 8, g_TextX, g_TextY, 8, 8, 0);
|
|
g_TextX += 8;
|
|
}
|
|
|
|
//
|
|
void V9_PrintBmpString(const c8* str)
|
|
{
|
|
while (*str)
|
|
V9_PrintBmpChar(*str++);
|
|
}
|
|
|
|
//
|
|
void V9_PrintBmpStringAt(u16 x, u16 y, const c8* str)
|
|
{
|
|
V9_SetPrintPositionBmp(x, y);
|
|
V9_PrintBmpString(str);
|
|
}
|
|
|
|
//
|
|
void V9_DrawTileBmp(u8 x, u8 y, u8 tile)
|
|
{
|
|
V9_WaitCmdEnd();
|
|
V9_CommandLMMM((tile % 32) * 8, BMP_TILES_Y + (tile / 32) * 8, x * 8, y * 8, 8, 8, 0);
|
|
}
|
|
|
|
const u8 g_BP6_Palette[] =
|
|
{
|
|
0x00, 0x00, 0x00, // [ 1] Black
|
|
0x0A, 0x0A, 0x0A, // [ 2] Dark gray
|
|
0x16, 0x16, 0x16, // [ 3] Light gray
|
|
0x1F, 0x1F, 0x1F, // [ 4] White
|
|
};
|
|
|
|
// 15-bit RGB => 8-bit GRB
|
|
const u8 g_BD8_PaletteBG[] =
|
|
{
|
|
0b00000000, // [ 0] #000000
|
|
0b00000000, // [ 1] #000000
|
|
0b01000110, // [ 2] #3953AB : 010'001'10
|
|
0b11001111, // [ 3] #71DCE9 : 110'011'11
|
|
0b00110001, // [ 4] #903F43 : 001'100'01
|
|
0b00000100, // [ 5] #2E142E : 000'001'00
|
|
0b10000111, // [ 6] #3B95E2 : 100'001'11
|
|
0b10111101, // [ 7] #EDB774 : 101'111'01
|
|
0b10101110, // [ 8] #79A790 : 101'011'10
|
|
0b11111111, // [ 9] #F6F3F3 : 111'111'11
|
|
0b10010110, // [10] #A89E99 : 100'101'10
|
|
0b00101001, // [11] #542F54 : 001'010'01
|
|
0b01101111, // [12] #6D60E7 : 011'011'11
|
|
0b01101001, // [13] #4E636B : 011'010'01
|
|
0b11011011, // [14] #CEC9CC : 110'110'11
|
|
0b01111001, // [15] #D86E61 : 011'110'01
|
|
};
|
|
|
|
//
|
|
void InitBmp()
|
|
{
|
|
// Initialize screen mode
|
|
V9_SetScreenMode(g_ScreenMode[g_CurrentScreen].Mode);
|
|
V9_SetDisplayEnable(FALSE);
|
|
V9_SetInterrupt(V9_INT_NONE);
|
|
V9_SetImageSpaceWidth(V9_R06_WIDH_1024);
|
|
V9_SetColorMode(g_ColorMode[g_CurrentColor].Mode);
|
|
V9_SetBackgroundColor(BG_COLOR);
|
|
V9_SetScrollingX(0);
|
|
|
|
u16 bgColor = 0;
|
|
|
|
if (g_CurrentColor == V9_COLOR_BP2)
|
|
{
|
|
bgColor = 0x5555;
|
|
V9_SetPalette(0, 4, g_BP6_Palette);
|
|
V9_SelectPaletteBP2(0);
|
|
|
|
// Load graphics data to VRAM
|
|
// for (u8 i = 0; i < 48; ++i)
|
|
// V9_WriteVRAM((u32)(0 + (u32)(512 * (u32)(512 + 0 + i))), g_DataV9BG + (128 * i), 128);
|
|
// for (u8 i = 0; i < 24; ++i)
|
|
// V9_WriteVRAM((u32)(0 + (u32)(512 * (u32)(512 + 48 + i))), g_DataV9Font + (128 * i), 128);
|
|
|
|
// loop (i, 48+24)
|
|
// loop (j, 64)
|
|
// V9_Poke((u32)(0 + (u32)(512 * (u32)(256 + 0 + i)) + j), (j & 1) ? 0b11100100 : 0b00011011);
|
|
V9_FillVRAM(BMP_TILES_Y * 256, 0b00000000, 256 * 72);
|
|
}
|
|
else if (g_CurrentColor == V9_COLOR_BP4)
|
|
{
|
|
bgColor = 0xEEEE;
|
|
InitPalette();
|
|
V9_SelectPaletteBP4(0);
|
|
|
|
// Load graphics data to VRAM
|
|
for (u8 i = 0; i < 48; ++i)
|
|
V9_WriteVRAM((u32)(0 + (u32)(512 * (u32)(BMP_TILES_Y + 0 + i))), g_DataV9BG + (128 * i), 128);
|
|
for (u8 i = 0; i < 24; ++i)
|
|
{
|
|
u8* buf = (u8*)Mem_GetHeapAddress();
|
|
loop (j, 128)
|
|
{
|
|
u8 v = *(const u8*)(g_DataV9Font + (128 * i) + j);
|
|
if ((v & 0xF0) == 0x00)
|
|
v |= 0x60;
|
|
else if ((v & 0xF0) == 0x60)
|
|
v &= 0x0F;
|
|
if ((v & 0x0F) == 0x00)
|
|
v |= 0x06;
|
|
else if ((v & 0x0F) == 0x06)
|
|
v &= 0xF0;
|
|
buf[j] = v;
|
|
}
|
|
V9_WriteVRAM((u32)(0 + (u32)(512 * (u32)(BMP_TILES_Y + 48 + i))), buf, 128);
|
|
}
|
|
}
|
|
else if (g_CurrentColor == V9_COLOR_BD8)
|
|
{
|
|
bgColor = 0x8787;
|
|
// Load graphics data to VRAM
|
|
const u8* data = g_DataV9BG;
|
|
loop (i, 48)
|
|
{
|
|
u32 addr = BMP_TILES_Y * 1024 + i * 1024;
|
|
loop (j, 128)
|
|
{
|
|
V9_Poke(addr++, g_BD8_PaletteBG[*data >> 4]);
|
|
V9_Poke(addr++, g_BD8_PaletteBG[*data & 0x0F]);
|
|
data++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Initialize background color
|
|
V9_SetCommandLogicalOp(V9_R45_LOP_SET);//| V9_R45_TP);
|
|
V9_SetCommandWriteMask(0xFFFF);
|
|
V9_WaitCmdEnd();
|
|
V9_CommandLMMV(0, 0, 1024, 256, 0, bgColor);
|
|
|
|
for (u16 i = 0; i < 128; ++i) // Draw ground
|
|
{
|
|
for (u16 j = V9_HORIZON_Y; j < 32; j++)
|
|
{
|
|
u8 cell;
|
|
if (j == V9_HORIZON_Y)
|
|
cell = 128 + i % 4;
|
|
else if (j < V9_GROUND_Y)
|
|
cell = 160;
|
|
else if (j == V9_GROUND_Y)
|
|
cell = 33 + i % 2;
|
|
else
|
|
{
|
|
cell = (j & 1) ? 65 : 97;
|
|
cell += i % 2;
|
|
}
|
|
V9_DrawTileBmp(i, j, cell);
|
|
}
|
|
}
|
|
|
|
for (u8 i = 0; i < 16; ++i) // Draw plateform
|
|
{
|
|
u16 x = i * 8;
|
|
u8 y = Math_GetRandom8() % 8 + 10;
|
|
for (u8 j = y; j < V9_GROUND_Y; j++)
|
|
{
|
|
u8 cell;
|
|
if (j == y)
|
|
cell = 32;
|
|
else if (((j - y) & 1))
|
|
cell = 64;
|
|
else
|
|
cell = 96;
|
|
V9_DrawTileBmp(x, j, cell++);
|
|
V9_DrawTileBmp(x + 1, j, cell++);
|
|
V9_DrawTileBmp(x + 2, j, cell++);
|
|
V9_DrawTileBmp(x + 3, j, cell++);
|
|
}
|
|
}
|
|
|
|
// Draw title
|
|
V9_PrintBmpStringAt(g_CurrentScreen > MODE_B1 ? 16 : 8, 1, "MSXgl V9990 - ");
|
|
V9_PrintBmpString(g_ScreenMode[g_CurrentScreen].Name);
|
|
V9_PrintBmpString(" (");
|
|
V9_PrintBmpString(g_ColorMode[g_CurrentColor].Name);
|
|
V9_PrintBmpString(")");
|
|
|
|
// Setup cursors
|
|
V9_SetCursorEnable(true);
|
|
V9_SetCursorPalette(2); // entry 8
|
|
u8* buf = (u8*)Mem_GetHeapAddress();
|
|
loop (i, 128)
|
|
{
|
|
buf[i] = i & 0x04 ? 0xAA : 0x55; // Initialize 'check' pattern
|
|
}
|
|
loop (i, 2)
|
|
V9_SetCursorPattern(i, buf);
|
|
|
|
V9_SetInterrupt(V9_INT_VBLANK | V9_INT_HBLANK);
|
|
V9_SetInterruptLine(9);
|
|
V9_SetDisplayEnable(TRUE);
|
|
}
|
|
|
|
//
|
|
void TickBmp()
|
|
{
|
|
if (g_CurrentColor == V9_COLOR_BP4)
|
|
V9_SelectPaletteBP4(1);
|
|
|
|
V9_SetScrollingX(0);
|
|
|
|
if (Keyboard_IsKeyPressed(KEY_H))
|
|
{
|
|
if (g_CurrentColor == V9_COLOR_BP4)
|
|
V9_SelectPaletteBP4(0);
|
|
|
|
V9_SetScrollingX(0);
|
|
V9_SetScrollingY(BMP_TILES_Y);
|
|
V9_SetInterrupt(V9_INT_VBLANK);
|
|
}
|
|
else
|
|
{
|
|
V9_SetScrollingY(0);
|
|
V9_SetInterrupt(V9_INT_VBLANK | V9_INT_HBLANK);
|
|
}
|
|
|
|
loop (i, 2)
|
|
{
|
|
u8 idx = ((g_Frame >> 2) + (i * 32)) % 64;
|
|
V9_SetCursorAttribute(i, 64 + i * 96, 64 + ((i16)g_Sinus64[idx] / 8), i + 1);
|
|
}
|
|
|
|
g_Frame++;
|
|
}
|
|
|
|
//
|
|
void HblankBmp()
|
|
{
|
|
if (g_CurrentColor == V9_COLOR_BP4)
|
|
V9_SelectPaletteBP4(0);
|
|
|
|
V9_SetScrollingX(g_Frame >> 0);
|
|
}
|
|
|
|
#endif // ((V9_USE_MODE_B0) || (V9_USE_MODE_B1) || (V9_USE_MODE_B2) || (V9_USE_MODE_B3) || (V9_USE_MODE_B4) || (V9_USE_MODE_B5) || (V9_USE_MODE_B6) || (V9_USE_MODE_B7))
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Helper function
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
void DisplayMSX()
|
|
{
|
|
Print_SetTextFont(g_Font_MGL_Sample6, 1); // Initialize font
|
|
Print_SetColor(0xF, 0x0);
|
|
Print_DrawText(MSX_GL " V9990 SAMPLE");
|
|
Print_DrawLineH(0, 1, 40);
|
|
|
|
const c8* str = "V9990 not found!";
|
|
if (V9_Detect())
|
|
str = "V9990 found!";
|
|
Print_SetPosition(0, 3);
|
|
Print_DrawText(str);
|
|
|
|
Print_SetPosition(0, 5);
|
|
Print_DrawText("Ports:\n\n 0-7\n 8-F");
|
|
for (u8 i = 0; i < 16; ++i)
|
|
{
|
|
u8 val = V9_GetPort(V9_P00 + i);
|
|
u8 x = 9 + (i % 8) * 3;
|
|
u8 y = 7 + (i / 8);
|
|
Print_SetPosition(x, y);
|
|
Print_DrawHex8(val);
|
|
}
|
|
|
|
Print_SetPosition(0, 10);
|
|
Print_DrawText("Registers:\n\n 00-07\n 08-0F\n 10-17\n 18-1F\n 20-27\n 28-2F\n 30-37\n 38-3F");
|
|
for (u8 i = 0; i < 64; ++i)
|
|
{
|
|
u8 val = V9_GetRegister(i);
|
|
u8 x = 9 + (i % 8) * 3;
|
|
u8 y = 12 + (i / 8);
|
|
Print_SetPosition(x, y);
|
|
Print_DrawHex8(val);
|
|
}
|
|
|
|
Print_DrawLineH(0, 22, 40);
|
|
Print_SetPosition(0, 23);
|
|
Print_DrawText("R:Refresh C:Color");
|
|
|
|
}
|
|
|
|
bool g_VSynch = FALSE;
|
|
|
|
#if (CUSTOM_ISR == 0)
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
void InterruptHook()
|
|
{
|
|
__asm
|
|
// Flush VDP interruption signal
|
|
in a, (P_VDP_STAT)
|
|
|
|
// Call VDP interruption handler
|
|
in a, (V9_P06)
|
|
out (V9_P06), a
|
|
// V-Blank interruption
|
|
rra
|
|
call c, _V9_InterruptVBlank
|
|
// H-Blank interruption
|
|
rra
|
|
call c, _V9_InterruptHBlank
|
|
// Command end interruption
|
|
rra
|
|
call c, _V9_InterruptCommand
|
|
__endasm;
|
|
}
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
void V9_InterruptVBlank()
|
|
{
|
|
g_VSynch = TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
void V9_InterruptHBlank()
|
|
{
|
|
g_ScreenMode[g_CurrentScreen].Hblank();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
void V9_InterruptCommand()
|
|
{
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
void VDP_InterruptVBlank()
|
|
{
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Set current screen mode
|
|
void SetScreenMode(u8 mode)
|
|
{
|
|
g_CurrentScreen = mode;
|
|
g_ScreenMode[g_CurrentScreen].Init();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Set current bitmap color mode
|
|
void SetColorMode(u8 mode)
|
|
{
|
|
#if (V9_USE_MODE_P1)
|
|
if (g_CurrentScreen == MODE_P1)
|
|
return;
|
|
#endif
|
|
#if (V9_USE_MODE_P2)
|
|
if (g_CurrentScreen == MODE_P2)
|
|
return;
|
|
#endif
|
|
|
|
g_CurrentColor = mode;
|
|
g_ScreenMode[g_CurrentScreen].Init();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Program entry point
|
|
void main()
|
|
{
|
|
V9_SetPort(V9_P15, 0); // Disactivate Video9000 if present (recommanded)
|
|
|
|
// Initialize screen mode 0 (text)
|
|
VDP_SetMode(VDP_MODE_SCREEN0);
|
|
VDP_EnableVBlank(FALSE);
|
|
VDP_ClearVRAM();
|
|
|
|
DisplayMSX();
|
|
|
|
#if (CUSTOM_ISR == 0)
|
|
BIOS_SetHookDirectCallback(H_KEYI, InterruptHook);
|
|
BIOS_ClearHook(H_TIMI);
|
|
#endif
|
|
|
|
//---- CURRENT SCREEN MODE INIT ----
|
|
g_ScreenMode[g_CurrentScreen].Init();
|
|
|
|
loop (i, 80)
|
|
g_KeyPressed[i] = FALSE;
|
|
|
|
u16 count = 0;
|
|
u8 clr = 0;
|
|
while (1)
|
|
{
|
|
// V9_SetRegister(15, 2);
|
|
while (g_VSynch == FALSE) {}
|
|
// V9_SetRegister(15, 1);
|
|
g_VSynch = FALSE;
|
|
|
|
//---- CURRENT SCREEN MODE UPDATE ----
|
|
g_ScreenMode[g_CurrentScreen].Tick();
|
|
|
|
// Sign of life
|
|
Print_SetPosition(39, 0);
|
|
Print_DrawChar(g_ChrAnim[count++ & 0x03]);
|
|
|
|
// Handle inputs
|
|
u8 row0 = Keyboard_Read(0);
|
|
u8 row1 = Keyboard_Read(1);
|
|
u8 row8 = Keyboard_Read(8);
|
|
|
|
// Select next/previous screen mode
|
|
if (IS_KEY_PRESSED(row8, KEY_RIGHT))
|
|
{
|
|
if (g_CurrentScreen < numberof(g_ScreenMode) - 1)
|
|
SetScreenMode(g_CurrentScreen + 1);
|
|
}
|
|
else if (IS_KEY_PRESSED(row8, KEY_LEFT))
|
|
{
|
|
if (g_CurrentScreen > 0)
|
|
SetScreenMode(g_CurrentScreen - 1);
|
|
}
|
|
|
|
// Direction selection of a screen mode
|
|
#if ((V9_USE_MODE_B0) || (V9_USE_MODE_B1) || (V9_USE_MODE_B2) || (V9_USE_MODE_B3) || (V9_USE_MODE_B4) || (V9_USE_MODE_B5) || (V9_USE_MODE_B6) || (V9_USE_MODE_B7))
|
|
if (IS_KEY_PRESSED(row0, KEY_0))
|
|
SetScreenMode(MODE_B0);
|
|
else if (IS_KEY_PRESSED(row0, KEY_1))
|
|
SetScreenMode(MODE_B1);
|
|
else if (IS_KEY_PRESSED(row0, KEY_2))
|
|
SetScreenMode(MODE_B2);
|
|
else if (IS_KEY_PRESSED(row0, KEY_3))
|
|
SetScreenMode(MODE_B3);
|
|
else if (IS_KEY_PRESSED(row0, KEY_4))
|
|
SetScreenMode(MODE_B4);
|
|
else if (IS_KEY_PRESSED(row0, KEY_5))
|
|
SetScreenMode(MODE_B5);
|
|
else if (IS_KEY_PRESSED(row0, KEY_6))
|
|
SetScreenMode(MODE_B6);
|
|
else if (IS_KEY_PRESSED(row0, KEY_7))
|
|
SetScreenMode(MODE_B7);
|
|
#endif
|
|
#if (V9_USE_MODE_P1)
|
|
if (IS_KEY_PRESSED(row1, KEY_8))
|
|
SetScreenMode(MODE_P1);
|
|
#endif
|
|
#if (V9_USE_MODE_P2)
|
|
if (IS_KEY_PRESSED(row1, KEY_9))
|
|
SetScreenMode(MODE_P2);
|
|
#endif
|
|
|
|
// Select color mode
|
|
if (IS_KEY_PRESSED(row8, KEY_DOWN))
|
|
{
|
|
if (g_CurrentColor < numberof(g_ColorMode) - 1)
|
|
SetColorMode(g_CurrentColor + 1);
|
|
}
|
|
else if (IS_KEY_PRESSED(row8, KEY_UP))
|
|
{
|
|
if (g_CurrentColor > 0)
|
|
SetColorMode(g_CurrentColor - 1);
|
|
}
|
|
|
|
// Hide/display sprite/cursor
|
|
if (Keyboard_IsKeyPressed(KEY_G))
|
|
{
|
|
if (!g_KeyPressed[KEY_G])
|
|
{
|
|
g_KeyPressed[KEY_G] = TRUE;
|
|
g_HideSprite = !g_HideSprite;
|
|
if (g_HideSprite)
|
|
V9_SetFlag(8, V9_R08_SPD_OFF, V9_R08_SPD_OFF);
|
|
else
|
|
V9_SetFlag(8, V9_R08_SPD_OFF, 0);
|
|
}
|
|
}
|
|
else
|
|
g_KeyPressed[KEY_G] = FALSE;
|
|
|
|
// Refresh MSX display
|
|
if (Keyboard_IsKeyPressed(KEY_R))
|
|
DisplayMSX();
|
|
}
|
|
|
|
V9_SetPort(V9_P15, 0x10); // Reactivate Video9000 if present (recommanded)
|
|
} |