Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
370 lines
11 KiB
C
370 lines
11 KiB
C
// ____________________________
|
|
// ██▀▀█▀▀██▀▀▀▀▀▀▀█▀▀█ │ ▄▄▄ ▄▄
|
|
// ██ ▀ █▄ ▀██▄ ▀ ▄█ ▄▀▀ █ │ ▀█▄ ▄▀██ ▄█▄█ ██▀▄ ██ ▄███
|
|
// █ █ █ ▀▀ ▄█ █ █ ▀▄█ █▄ │ ▄▄█▀ ▀▄██ ██ █ ██▀ ▀█▄ ▀█▄▄
|
|
// ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀────────┘ ▀▀
|
|
// Sprite mode 2 sample
|
|
//─────────────────────────────────────────────────────────────────────────────
|
|
|
|
//=============================================================================
|
|
// INCLUDES
|
|
//=============================================================================
|
|
#include "msxgl.h"
|
|
#include "bios.h"
|
|
|
|
//=============================================================================
|
|
// DEFINES
|
|
//=============================================================================
|
|
|
|
// Pattern enum
|
|
#define PATTERN_8_1ST (u8)0
|
|
#define PATTERN_8_NUM (u8)64
|
|
#define PATTERN_16_1ST (u8)(PATTERN_8_1ST+PATTERN_8_NUM)
|
|
#define PATTERN_16_NUM (u8)(6*2*4)
|
|
#define PATTERN_16OR_1ST (u8)(PATTERN_16_1ST+PATTERN_16_NUM)
|
|
#define PATTERN_16OR_NUM (u8)(6*2*4)
|
|
|
|
// Sprite enum
|
|
#define SPRITE_8_1ST (u8)0
|
|
#define SPRITE_8_NUM (u8)25
|
|
#define SPRITE_16_1ST (u8)(SPRITE_8_1ST+SPRITE_8_NUM)
|
|
#define SPRITE_16_NUM (u8)2
|
|
#define SPRITE_16OR_1ST (u8)(SPRITE_16_1ST+SPRITE_16_NUM)
|
|
#define SPRITE_16OR_NUM (u8)4
|
|
|
|
// Line interruption
|
|
#if (TARGET_TYPE == TYPE_BIN)
|
|
#define SPRITE_2X_LINE 63-9
|
|
#define SPRITE_8_LINE 113-9
|
|
#elif (TARGET_TYPE == TYPE_ROM)
|
|
#define SPRITE_2X_LINE 63-4
|
|
#define SPRITE_8_LINE 113-4
|
|
#elif (TARGET_TYPE == TYPE_DOS)
|
|
#define SPRITE_2X_LINE 63-10
|
|
#define SPRITE_8_LINE 113-10
|
|
#endif
|
|
|
|
// Vector structure
|
|
struct Vector
|
|
{
|
|
i8 x, y;
|
|
};
|
|
|
|
/// Sprite data
|
|
struct SpriteData
|
|
{
|
|
u8 Y;
|
|
u8 X;
|
|
u8 Shape;
|
|
};
|
|
|
|
//=============================================================================
|
|
// READ-ONLY DATA
|
|
//=============================================================================
|
|
|
|
// Fonts
|
|
#include "font/font_mgl_sample6.h"
|
|
#include "font/font_mgl_symbol1.h"
|
|
|
|
// Sprite by GrafxKid (https://opengameart.org/content/super-random-sprites)
|
|
#include "content/data_sprt_16.h"
|
|
#include "content/data_sprt_16or.h"
|
|
|
|
// Character animation
|
|
const u8 chrAnim[] = { '|', '\\', '-', '/' };
|
|
|
|
// Sprite movement
|
|
const struct Vector DirMove[] =
|
|
{
|
|
{ (i8)1, (i8)1 },
|
|
{ (i8)1, (i8)0 },
|
|
{ (i8)1, (i8)-1 },
|
|
{ (i8)0, (i8)-1 },
|
|
{ (i8)-1, (i8)-1 },
|
|
{ (i8)-1, (i8)0 },
|
|
{ (i8)-1, (i8)1 },
|
|
{ (i8)0, (i8)1 },
|
|
};
|
|
|
|
// Color table
|
|
const u8 ColorTab[] =
|
|
{
|
|
15, 5, 6, 7, 1, 7, 6, 5,
|
|
15, 5, 6, 7, 1, 7, 6, 5,
|
|
15, 5, 6, 7, 1, 7, 6, 5,
|
|
};
|
|
|
|
//=============================================================================
|
|
// MEMORY DATA
|
|
//=============================================================================
|
|
|
|
// Screen mode setting index
|
|
u8 g_VBlank = 0;
|
|
u8 g_Frame = 0;
|
|
u8 g_Phase = 0;
|
|
|
|
// Sprite data
|
|
struct SpriteData g_Sprite[32];
|
|
|
|
struct SpriteData* g_Data1;
|
|
struct SpriteData* g_Data2;
|
|
struct SpriteData* g_Data3;
|
|
|
|
//=============================================================================
|
|
// HELPER FUNCTIONS
|
|
//=============================================================================
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// H-Blank interrupt hook
|
|
void HBlankHook()
|
|
{
|
|
// VDP_SetColor(COLOR_BLACK);
|
|
if (g_Phase == 0)
|
|
{
|
|
VDP_SetSpriteFlag(VDP_SPRITE_SIZE_16 + VDP_SPRITE_SCALE_2);
|
|
VDP_SetHBlankLine(SPRITE_8_LINE);
|
|
g_Phase++;
|
|
// VDP_SetColor(COLOR_DARK_GREEN);
|
|
}
|
|
else
|
|
{
|
|
VDP_SetSpriteFlag(VDP_SPRITE_SIZE_8);
|
|
// VDP_SetColor(COLOR_DARK_RED);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// H_KEYI interrupt hook
|
|
void InterruptHook()
|
|
{
|
|
__asm
|
|
// Get S#1
|
|
ld a, #1
|
|
out (P_VDP_ADDR), a
|
|
ld a, #(0x80 + 15)
|
|
out (P_VDP_ADDR), a
|
|
in a, (P_VDP_STAT)
|
|
// Call H-Blank if bit #0 of S#1 is set
|
|
rrca
|
|
jp nc, _no_hblank
|
|
call _HBlankHook // call to C function HBlankHook()
|
|
_no_hblank:
|
|
// Reset R#15 to S#0
|
|
xor a
|
|
out (P_VDP_ADDR), a
|
|
ld a, #(0x80 + 15)
|
|
out (P_VDP_ADDR),a
|
|
__endasm;
|
|
|
|
// Call((u16)HookBackup_KEYI);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// H_TIMI interrupt hook
|
|
void VBlankHook()
|
|
{
|
|
g_VBlank = 1;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Wait for V-Blank period
|
|
void WaitVBlank()
|
|
{
|
|
while (g_VBlank == 0) {}
|
|
g_VBlank = 0;
|
|
g_Frame++;
|
|
g_Phase = 0;
|
|
VDP_SetSpriteFlag(VDP_SPRITE_SIZE_16);
|
|
VDP_SetHBlankLine(SPRITE_2X_LINE);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Update sprites attributes
|
|
void UpdateSprites()
|
|
{
|
|
struct SpriteData* sprt = &g_Sprite[0];
|
|
u8 sprtIdx = SPRITE_8_1ST;
|
|
for (u8 i = 0; i < SPRITE_8_NUM; i++)
|
|
{
|
|
const struct Vector* mov = &DirMove[i & 0x7];
|
|
sprt->X += mov->x;
|
|
sprt->Y += mov->y;
|
|
if (sprt->Y > 212)
|
|
sprt->Y -= 98;
|
|
if (sprt->Y < 114)
|
|
sprt->Y += 98;
|
|
|
|
VDP_SetSpriteData(sprtIdx++, (u8*)sprt++);
|
|
}
|
|
|
|
u8 frame = (g_Frame >> 2) % 6;
|
|
g_Data1->X++;
|
|
if (g_Data1->X > 128-16)
|
|
g_Data1->X = 0;
|
|
g_Data1->Shape = PATTERN_16_1ST + (0 + frame) * 4;
|
|
VDP_SetSpriteData(SPRITE_16_1ST + 0, (u8*)g_Data1);
|
|
g_Data1->Shape = PATTERN_16_1ST + (6 + frame) * 4;
|
|
VDP_SetSpriteData(SPRITE_16_1ST + 1, (u8*)g_Data1);
|
|
|
|
g_Data2->X = g_Data1->X + 128;
|
|
g_Data2->Shape = PATTERN_16OR_1ST + (0 + frame) * 4;
|
|
VDP_SetSpriteData(SPRITE_16OR_1ST + 0, (u8*)g_Data2);
|
|
g_Data2->Shape = PATTERN_16OR_1ST + (6 + frame) * 4;
|
|
VDP_SetSpriteData(SPRITE_16OR_1ST + 1, (u8*)g_Data2);
|
|
|
|
g_Data3->X += 2;
|
|
g_Data3->Shape = PATTERN_16OR_1ST + (0 + frame) * 4;
|
|
VDP_SetSpriteData(SPRITE_16OR_1ST + 2, (u8*)g_Data3);
|
|
g_Data3->Shape = PATTERN_16OR_1ST + (6 + frame) * 4;
|
|
VDP_SetSpriteData(SPRITE_16OR_1ST + 3, (u8*)g_Data3);
|
|
|
|
VDP_SetSpriteMultiColor(SPRITE_16OR_1ST + 4, ColorTab + ((g_Frame >> 2) & 0x07));
|
|
}
|
|
|
|
//=============================================================================
|
|
// MAIN LOOP
|
|
//=============================================================================
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Program entry point
|
|
void main()
|
|
{
|
|
BIOS_SetKeyClick(FALSE);
|
|
if (Sys_GetMSXVersion() == MSXVER_1)
|
|
{
|
|
BIOS_ClearScreen();
|
|
BIOS_TextPrint("This sample need MSX2 or above");
|
|
BIOS_GetCharacter();
|
|
return;
|
|
}
|
|
|
|
// Setup screen
|
|
VDP_SetMode(VDP_MODE_SCREEN5);
|
|
VDP_SetColor(COLOR_DARK_BLUE);
|
|
VDP_CommandHMMV(0, 0, 256, 1024, COLOR_MERGE(COLOR_DARK_BLUE, COLOR_DARK_BLUE)); // Clear VRAM
|
|
VDP_CommandWait();
|
|
|
|
// Setup sprite
|
|
VDP_EnableSprite(TRUE);
|
|
VDP_SetSpritePatternTable(0x17000);
|
|
VDP_SetSpriteAttributeTable(0x17A00);
|
|
VDP_SetSpriteFlag(VDP_SPRITE_SIZE_16 /*+ VDP_SPRITE_SCALE_2*/);
|
|
|
|
// Load 8x8 sprites (Pattern 0~95)
|
|
VDP_LoadSpritePattern(g_Font_MGL_Symbol1 + 4 + (16 * 10 * 8), PATTERN_8_1ST, PATTERN_8_NUM);
|
|
// Load 16x16 sprites (Pattern 96~143)
|
|
u8 chrSprt = PATTERN_16_1ST;
|
|
for (u8 i5 = 0; i5 < 6; i5++)
|
|
{
|
|
VDP_LoadSpritePattern(g_DataSprt16 + (i5 * 2 + 0) * 8, chrSprt++, 1);
|
|
VDP_LoadSpritePattern(g_DataSprt16 + (i5 * 2 + 12) * 8, chrSprt++, 1);
|
|
VDP_LoadSpritePattern(g_DataSprt16 + (i5 * 2 + 1) * 8, chrSprt++, 1);
|
|
VDP_LoadSpritePattern(g_DataSprt16 + (i5 * 2 + 13) * 8, chrSprt++, 1);
|
|
}
|
|
for (u8 i6 = 0; i6 < 6; i6++)
|
|
{
|
|
VDP_LoadSpritePattern(g_DataSprt16 + (i6 * 2 + 24) * 8, chrSprt++, 1);
|
|
VDP_LoadSpritePattern(g_DataSprt16 + (i6 * 2 + 36) * 8, chrSprt++, 1);
|
|
VDP_LoadSpritePattern(g_DataSprt16 + (i6 * 2 + 25) * 8, chrSprt++, 1);
|
|
VDP_LoadSpritePattern(g_DataSprt16 + (i6 * 2 + 37) * 8, chrSprt++, 1);
|
|
}
|
|
chrSprt = PATTERN_16OR_1ST;
|
|
for (u8 i5 = 0; i5 < 6; i5++)
|
|
{
|
|
VDP_LoadSpritePattern(g_DataSprt16or + (i5 * 2 + 0) * 8, chrSprt++, 1);
|
|
VDP_LoadSpritePattern(g_DataSprt16or + (i5 * 2 + 12) * 8, chrSprt++, 1);
|
|
VDP_LoadSpritePattern(g_DataSprt16or + (i5 * 2 + 1) * 8, chrSprt++, 1);
|
|
VDP_LoadSpritePattern(g_DataSprt16or + (i5 * 2 + 13) * 8, chrSprt++, 1);
|
|
}
|
|
for (u8 i6 = 0; i6 < 6; i6++)
|
|
{
|
|
VDP_LoadSpritePattern(g_DataSprt16or + (i6 * 2 + 24) * 8, chrSprt++, 1);
|
|
VDP_LoadSpritePattern(g_DataSprt16or + (i6 * 2 + 36) * 8, chrSprt++, 1);
|
|
VDP_LoadSpritePattern(g_DataSprt16or + (i6 * 2 + 25) * 8, chrSprt++, 1);
|
|
VDP_LoadSpritePattern(g_DataSprt16or + (i6 * 2 + 37) * 8, chrSprt++, 1);
|
|
}
|
|
// Initialize 8x8 sprites
|
|
for (u8 i = 0; i < SPRITE_8_NUM; i++)
|
|
{
|
|
u16 rnd = Math_GetRandom16();
|
|
g_Sprite[i].X = rnd >> 8;
|
|
g_Sprite[i].Y = (rnd & 0x007F) + 114;
|
|
g_Sprite[i].Shape = PATTERN_8_1ST + ((rnd >> 4) & 0x3F);
|
|
VDP_SetSpriteExUniColor(SPRITE_8_1ST + i, g_Sprite[i].X, g_Sprite[i].Y, g_Sprite[i].Shape, ((rnd >> 12) & 0x7) + 0x08);
|
|
}
|
|
// Initialize 16x16 sprites
|
|
VDP_SetSpriteExUniColor(SPRITE_16_1ST + 0, 0, 32, PATTERN_16_1ST + 0 * 4, 0x0F);
|
|
VDP_SetSpriteExUniColor(SPRITE_16_1ST + 1, 0, 32, PATTERN_16_1ST + 6 * 4, 0x01);
|
|
// Initialize 16x16 OR sprites
|
|
VDP_SetPaletteEntry(2, RGB16(7, 7, 7));
|
|
VDP_SetPaletteEntry(3, RGB16(6, 4, 1));
|
|
VDP_SetSpriteExUniColor(SPRITE_16OR_1ST + 0, (u8)128, 32, PATTERN_16OR_1ST + 0 * 4, 0x02);
|
|
VDP_SetSpriteExUniColor(SPRITE_16OR_1ST + 1, (u8)128, 32, PATTERN_16OR_1ST + 6 * 4, VDP_SPRITE_CC + 0x01);
|
|
VDP_SetSpriteExUniColor(SPRITE_16OR_1ST + 2, (u8)128, 64+8, PATTERN_16OR_1ST + 0 * 4, 0x02);
|
|
VDP_SetSpriteExUniColor(SPRITE_16OR_1ST + 3, (u8)128, 64+8, PATTERN_16OR_1ST + 6 * 4, VDP_SPRITE_CC + 0x01);
|
|
|
|
VDP_SetPaletteEntry(5, RGB16(7, 5, 5));
|
|
VDP_SetPaletteEntry(6, RGB16(5, 3, 3));
|
|
VDP_SetPaletteEntry(7, RGB16(3, 1, 1));
|
|
VDP_SetSpriteExMultiColor(SPRITE_16OR_1ST + 4, (u8)(256-32), 64+8, PATTERN_8_1ST + 16, ColorTab);
|
|
|
|
VDP_DisableSpritesFrom(SPRITE_16_1ST + 7);
|
|
|
|
// Setup print
|
|
Print_SetBitmapFont(g_Font_MGL_Sample6);
|
|
Print_SetColor(0xF, 0x4);
|
|
Print_SetPosition(0, 2);
|
|
Print_DrawText("\x1\x2\x3\x4\x5\x6 SPRITE MODE 2 (G4)");
|
|
Draw_LineH(0, 255, 12, 0xFF, 0);
|
|
Draw_LineH(0, 255, 64, 0xFF, 0);
|
|
Draw_LineH(0, 255, 114, 0xFF, 0);
|
|
Draw_LineV(128, 16, 60, 0xFF, 0);
|
|
|
|
Print_SetPosition(8, 20);
|
|
Print_DrawText("16x16");
|
|
Print_SetPosition(8+128, 20);
|
|
Print_DrawText("16x16 OR Color");
|
|
Print_SetPosition(8, 64 + 8);
|
|
Print_DrawText("16x16 OR x2");
|
|
Print_SetPosition(8+128, 64 + 8);
|
|
Print_DrawText("Multi-color");
|
|
Print_SetPosition(8, 24 + 98);
|
|
Print_DrawText("8x8");
|
|
|
|
g_Data1 = &g_Sprite[SPRITE_16_1ST];
|
|
g_Data1->X = 0;
|
|
g_Data1->Y = 32;
|
|
g_Data2 = &g_Sprite[SPRITE_16_1ST+1];
|
|
g_Data2->X = 128;
|
|
g_Data2->Y = 32;
|
|
g_Data3 = &g_Sprite[SPRITE_16_1ST+2];
|
|
g_Data3->X = 0;
|
|
g_Data3->Y = 64+8;
|
|
|
|
g_Phase = 0;
|
|
VDP_SetSpriteFlag(VDP_SPRITE_SIZE_16);
|
|
VDP_SetHBlankLine(SPRITE_2X_LINE);
|
|
VDP_EnableHBlank(TRUE);
|
|
BIOS_SetHookCallback(H_KEYI, InterruptHook);
|
|
BIOS_SetHookCallback(H_TIMI, VBlankHook);
|
|
|
|
bool bContinue = TRUE;
|
|
while (bContinue)
|
|
{
|
|
// VDP_SetColor(COLOR_LIGHT_BLUE);
|
|
WaitVBlank();
|
|
// VDP_SetColor(COLOR_DARK_BLUE);
|
|
|
|
UpdateSprites();
|
|
|
|
if (Keyboard_IsKeyPressed(KEY_ESC))
|
|
bContinue = FALSE;
|
|
}
|
|
|
|
BIOS_ClearHook(H_TIMI);
|
|
BIOS_ClearHook(H_KEYI);
|
|
BIOS_Exit(0);
|
|
}
|
|
|
|
|