Add web build, unify screen update and frame wait function, update CI

This commit is contained in:
OctoSpacc 2023-11-04 16:49:35 +01:00
parent ee19b03b1b
commit 2026d954ee
8 changed files with 251 additions and 122 deletions

View File

@ -1,20 +1,31 @@
image: debian:latest image: debian:latest
before_script: | before_script: |
apt update sudo apt update
apt install \ sudo apt install -y \
make gcc cc65 python3 python3-pil \ make gcc mingw-w64 wine wine32 cc65 emscripten curl python3 python3-pil \
libsdl1.2-dev libsdl-image1.2-dev libsdl-ttf2.0-dev libsdl-mixer1.2-dev \ libsdl1.2-dev libsdl-image1.2-dev libsdl-ttf2.0-dev libsdl-mixer1.2-dev \
libsdl2-dev libsdl2-image-dev libsdl2-ttf-dev libsdl2-mixer-dev libsdl2-dev libsdl2-image-dev libsdl2-ttf-dev libsdl2-mixer-dev \
;
curl https://apt.devkitpro.org/install-devkitpro-pacman | sudo bash
sudo dkp-pacman -Sy nds-dev
Tests: Tests:
stage: Tests
script: | script: |
cd ./LibMultiSpacc/Examples cd ./LibMultiSpacc/Examples
for Example in */ for Example in */
do do
cd ${Example} cd ${Example}
for Build in "Target=LinuxPC MultiSpacc_Target=SDL12" "Target=LinuxPC MultiSpacc_Target=SDL20" "Target=NES" for Build in \
do "Target=LinuxPC MultiSpacc_Target=SDL12"
"Target=LinuxPC MultiSpacc_Target=SDL20"
"Target=WindowsPC MultiSpacc_Target=SDL12"
"Target=WindowsPC MultiSpacc_Target=SDL20"
"Target=Windows9x"
"Target=NDS"
"Target=NES"
; do
make clean make clean
make -j$(nproc --all) ${Build} make -j$(nproc --all) ${Build}
done done

View File

@ -5,51 +5,72 @@ SpaccSources = $(wildcard ../../LibMultiSpacc/*.c)
SpaccHeaders = $(wildcard ../../LibMultiSpacc/*.h) SpaccHeaders = $(wildcard ../../LibMultiSpacc/*.h)
CFlags = -Os -Werror -Wpedantic -Wdeclaration-after-statement CFlags = -Os -Werror -Wpedantic -Wdeclaration-after-statement
# Default build is always for the host system # TODO: handle other unixes
ifeq ($(shell uname --operating-system), Msys)
Host = Windows
else
Host = Linux
endif
# When no user-specified target, build for the host system
ifndef Target ifndef Target
ifeq ($(shell uname --operating-system), Msys) ifeq ($(Host), Windows)
Target = WindowsPC Target = WindowsPC
else else ifeq ($(Host), Linux)
Target = LinuxPC Target = LinuxPC
endif endif
endif endif
ifdef Target # TODO: handle building for Windows targets from Linux hosts
ifeq ($(Target), LinuxPC)
ExeSuffix = .run ifeq ($(Target), LinuxPC)
Defines += -DTarget_LinuxPC ExeSuffix = .run
MultiSpacc_Target = SDL20 Defines += -DTarget_LinuxPC
else ifeq ($(Target), WindowsPC) MultiSpacc_Target = SDL20
ExeSuffix = .exe else ifeq ($(Target), WindowsPC)
Defines += -DTarget_WindowsPC ExeSuffix = .exe
MultiSpacc_Target = SDL20 Defines += -DTarget_WindowsPC
else ifeq ($(Target), Windows9x) MultiSpacc_Target = SDL20
ExeSuffix = .exe ifneq ($(Host), Windows)
Defines += -DTarget_Windows9x ToolsSuffix = -mingw-w64
MultiSpacc_Target = SDL12
LdFlags += -lmingw32 -static-libgcc
ToolsSyspath=/c/Files/Sdk/mingw32/bin
ToolsPrefix=$(ToolsSyspath)/
export PATH=$$PATH:$(ToolsSyspath)
else ifeq ($(Target), NDS)
Defines += -DTarget_NDS
MultiSpacc_Target = NDS
else ifeq ($(Target), NES)
Defines += -DTarget_NES
MultiSpacc_Target = NES
endif endif
else ifeq ($(Target), Windows9x)
ExeSuffix = .exe
Defines += -DTarget_Windows9x
MultiSpacc_Target = SDL12
LdFlags += -lmingw32 -static-libgcc
ifeq ($(Host), Windows)
ToolsSyspath = /c/Files/Sdk/mingw32/bin
export PATH=$$PATH:$(ToolsSyspath)
else
ToolsSyspath = /opt/Sdk/mingw32/bin
ToolsWrapper = wine
endif
ToolsPrefix = $(ToolsSyspath)/
else ifeq ($(Target), Web)
Defines += -DTarget_Web
MultiSpacc_Target = Web
else ifeq ($(Target), NDS)
Defines += -DTarget_NDS
MultiSpacc_Target = NDS
else ifeq ($(Target), NES)
Defines += -DTarget_NES
MultiSpacc_Target = NES
endif endif
ifeq ($(MultiSpacc_Target), SDL12) ifeq ($(MultiSpacc_Target), SDL12)
Defines += -DMultiSpacc_Target_SDL12 -DMultiSpacc_Target_SDLCom Defines += -DMultiSpacc_Target_SDL12 -DMultiSpacc_Target_SDLCom -DMultiSpacc_Target_SDLStandard
CFlags += $(shell sdl-config --cflags) CFlags += $(shell sdl-config --cflags)
LdFlags += $(shell sdl-config --libs) -lSDLmain -lSDL -lSDL_image -lSDL_mixer -lSDL_ttf LdFlags += $(shell sdl-config --libs) -lSDLmain -lSDL -lSDL_image -lSDL_mixer -lSDL_ttf
BuildProcess = __Normal__ BuildProcess = __Normal__
else ifeq ($(MultiSpacc_Target), SDL20) else ifeq ($(MultiSpacc_Target), SDL20)
Defines += -DMultiSpacc_Target_SDL20 -DMultiSpacc_Target_SDLCom Defines += -DMultiSpacc_Target_SDL20 -DMultiSpacc_Target_SDLCom -DMultiSpacc_Target_SDLStandard
CFlags += $(shell sdl2-config --cflags) CFlags += $(shell sdl2-config --cflags)
LdFlags += $(shell sdl2-config --libs) -lSDL2main -lSDL2 -lSDL2_image -lSDL2_mixer -lSDL2_ttf LdFlags += $(shell sdl2-config --libs) -lSDL2main -lSDL2 -lSDL2_image -lSDL2_mixer -lSDL2_ttf
BuildProcess = __Normal__ BuildProcess = __Normal__
else ifeq ($(MultiSpacc_Target), Web)
Defines += -DMultiSpacc_Target_Web -DMultiSpacc_Target_SDL20 -DMultiSpacc_Target_SDLCom
BuildProcess = __Web__
else ifeq ($(MultiSpacc_Target), NDS) else ifeq ($(MultiSpacc_Target), NDS)
Defines += -DMultiSpacc_Target_NDS Defines += -DMultiSpacc_Target_NDS
BuildProcess = __NDS__ BuildProcess = __NDS__
@ -58,16 +79,24 @@ else ifeq ($(MultiSpacc_Target), NES)
BuildProcess = __NES__ BuildProcess = __NES__
endif endif
CC = $(ToolsPrefix)gcc $(CFlags) $(Defines)
BuildSources = $(AppSources) $(SpaccSources) BuildSources = $(AppSources) $(SpaccSources)
BuildObjects = $(BuildSources:.c=.o) BuildObjects = $(BuildSources:.c=.o)
ifeq ($(BuildProcess), __Normal__)
CC = $(ToolsWrapper) $(ToolsPrefix)gcc$(ToolsSuffix) $(CFlags) $(Defines)
endif
All all: $(BuildProcess) All all: $(BuildProcess)
# TODO: use virtual build dirs even for normals to allow linking against different libraries without recleaning # TODO: use virtual build dirs even for normals to allow linking against different libraries without recleaning
__Normal__: $(BuildObjects) __Normal__: $(BuildObjects)
$(CC) $^ $(LdFlags) -o $(AppName)$(ExeSuffix) $(CC) $^ $(LdFlags) -o $(AppName)$(ExeSuffix)
__Web__:
emcc $(BuildSources) -sWASM=1 -sUSE_SDL=2 -sUSE_SDL_IMAGE=2 -sSDL2_IMAGE_FORMATS='["png"]' -sUSE_SDL_TTF=2 -sUSE_SDL_MIXER=2 --preload-file Emscripten -o Emscripten.js
cp ../Emscripten.html ./$(AppName.html)
# TODO: bundle JS, WASM, and assets package in HTML file
# TODO: Fix include substitutions properly in non-standard build processes # TODO: Fix include substitutions properly in non-standard build processes
__NDS__: __NDS__:

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body>
<canvas id="canvas" oncontextmenu="event.preventDefault()"></canvas>
<script> var Module = { canvas: (function(){ return document.getElementById('canvas') })() } </script>
<script src="Emscripten.js"></script>
</body>
</html>

View File

@ -2,6 +2,17 @@
#define AppName "Hello World" #define AppName "Hello World"
typedef struct MainArgs {
int spriteX;
int spriteY;
int accelX;
int accelY;
MultiSpacc_SurfaceConfig *WindowConfig;
MultiSpacc_Window *Window;
MultiSpacc_Surface *Screen;
MultiSpacc_Surface *TilesImg;
} MainArgs;
/*{pal:"nes",layout:"nes"}*/ /*{pal:"nes",layout:"nes"}*/
const char PALETTE[32] = { const char PALETTE[32] = {
0x03, // screen 0x03, // screen
@ -15,67 +26,72 @@ const char PALETTE[32] = {
0x0d,0x27,0x2a, // sprite 3 0x0d,0x27,0x2a, // sprite 3
}; };
Uint32 nextTick;
bool MainLoop( void *args )
{
MainArgs *margs = (MainArgs*)args;
MultiSpacc_Sprite( 0, margs->spriteX, margs->spriteY, 1, margs->TilesImg, margs->Screen );
//scroll(spriteX,0);
margs->spriteX += margs->accelX;
margs->spriteY += margs->accelY;
if( margs->spriteX >= margs->WindowConfig->Width )
{
margs->spriteX = 0;
}
if( margs->spriteY == 0 || margs->spriteY == ( margs->WindowConfig->Height - 8 ) )
{
margs->accelY *= -1;
}
// check keys
// if ESC pressed exit
// ...
if( !MultiSpacc_WaitUpdateDisplay( margs->Window, &nextTick ) )
{
MultiSpacc_PrintDebug("[E] Error Updating Screen.\n");
return false;
};
return true;
}
int main( int argc, char *argv[] ) int main( int argc, char *argv[] )
{ {
int spriteX = 0; MainArgs margs = {0};
int spriteY = 0;
int accelX = +1;
int accelY = +2;
MultiSpacc_SurfaceConfig WindowConfig = {0}; MultiSpacc_SurfaceConfig WindowConfig = {0};
MultiSpacc_Window *Window;
MultiSpacc_Surface *Screen; margs.WindowConfig = &WindowConfig;
MultiSpacc_Surface *TilesImg; margs.accelX = +1;
margs.accelY = +2;
WindowConfig.Width = 320; WindowConfig.Width = 320;
WindowConfig.Height = 240; WindowConfig.Height = 240;
WindowConfig.Bits = 16; WindowConfig.Bits = 16;
memcpy( WindowConfig.Palette, PALETTE, 32 ); memcpy( WindowConfig.Palette, PALETTE, 32 );
//WindowConfig.Frequency = 50; //WindowConfig.Frequency = 50;
Window = MultiSpacc_SetWindow( &WindowConfig /*, &PALETTE*/ );
Screen = MultiSpacc_GetWindowSurface( Window );
if( Screen == NULL ) margs.Window = MultiSpacc_SetWindow( &WindowConfig );
margs.Screen = MultiSpacc_GetWindowSurface( margs.Window );
if( margs.Screen == NULL )
{ {
MultiSpacc_PrintDebug("[E] Error Initializing Video System.\n"); MultiSpacc_PrintDebug("[E] Error Initializing Video System.\n");
return -1; return -1;
}; };
MultiSpacc_SetAppTitle( Window, AppName ); MultiSpacc_SetAppTitle( margs.Window, AppName );
MultiSpacc_PrintDebug("[I] Ready!\n");
// Bitmap font borrowed from: <https://github.com/nesdoug/01_Hello/blob/master/Alpha.chr> // Bitmap font borrowed from: <https://github.com/nesdoug/01_Hello/blob/master/Alpha.chr>
// Copyright (c) 2018 Doug Fraker www.nesdoug.com (MIT) // Copyright (c) 2018 Doug Fraker www.nesdoug.com (MIT)
TilesImg = MultiSpacc_LoadImage( "CHARS.png", Screen, NULL ); margs.TilesImg = MultiSpacc_LoadImage( "CHARS.png", margs.Screen, NULL );
MultiSpacc_PrintText( "Hello, World!", Screen, &WindowConfig, 2, 2, TilesImg ); MultiSpacc_PrintText( "Hello, World!", margs.Screen, &WindowConfig, 2, 2, margs.TilesImg );
MultiSpacc_PrintDebug("[I] Ready!\n");
while(true) return MultiSpacc_SetMainLoop( MainLoop, &margs );
{
MultiSpacc_Sprite( 0, spriteX, spriteY, 1, TilesImg, Screen );
//scroll(spriteX,0);
spriteX += accelX;
spriteY += accelY;
if( spriteX >= WindowConfig.Width )
{
spriteX = 0;
}
if( spriteY == 0 || spriteY == (WindowConfig.Height - 8) )
{
accelY *= -1;
}
if( MultiSpacc_UpdateWindowSurface(Window) != 0 )
{
MultiSpacc_PrintDebug("[E] Error Updating Screen.\n");
return -1;
};
// TODO: Implement cross-platform vblank-wait
MultiSpacc_Sleep(16);
}
return 0;
} }

View File

@ -1,30 +1,88 @@
#include "./MultiSpacc.h" #include "./MultiSpacc.h"
#ifdef MultiSpacc_Target_SDL12 bool MultiSpacc_SetMainLoop( bool function( void *args ), void *args )
int MultiSpacc_SetColorKey( MultiSpacc_Surface *Surface, bool Flag, Uint32 Key ) { {
if( Flag ) #ifdef MultiSpacc_Target_Web
return SDL_SetColorKey( Surface, SDL_SRCCOLORKEY, Key ); emscripten_set_main_loop_arg( function, args, -1, true );
else #else
return SDL_SetColorKey( Surface, 0, Key ); while(true){
} if( !function(args) ){
#endif return false;
}
#ifdef MultiSpacc_Target_SDL20
int MultiSpacc_SetColorKey( MultiSpacc_Surface *Surface, bool Flag, Uint32 Key ) {
if ( Flag ) {
return SDL_SetColorKey( Surface, SDL_TRUE, Key );
} else {
return SDL_SetColorKey( Surface, SDL_FALSE, Key );
} }
#endif
return true;
}
bool MultiSpacc_UpdateDisplay( MultiSpacc_Window *window )
{
#if defined(MultiSpacc_Target_SDL12)
return !SDL_Flip(window);
#elif defined(MultiSpacc_Target_SDL20)
return !SDL_UpdateWindowSurface(window);
#endif
return true;
}
bool MultiSpacc_WaitFrame( Uint32 *nextTick )
{
#if defined(MultiSpacc_Target_SDLStandard)
// TODO: check if this actually works with any framerate or it caps the game speed
Uint32 now;
now = SDL_GetTicks();
if ( *nextTick <= now ) {
MultiSpacc_Sleep(0);
} else {
MultiSpacc_Sleep( *nextTick - now );
}
*nextTick += 1000/60; // TODO: specified framerate and variable framerate
*nextTick = SDL_GetTicks() + 1000/60; // this last part should execute at the start of a loop maybe ???
#elif defined(MultiSpacc_Target_NES)
ppu_wait_frame();
#endif
return true;
}
bool MultiSpacc_WaitUpdateDisplay( MultiSpacc_Window *window, Uint32 *nextTick )
{
if( !MultiSpacc_UpdateDisplay(window) || !MultiSpacc_WaitFrame(nextTick) ){
return false;
} }
#endif return true;
}
int MultiSpacc_SetColorKey( MultiSpacc_Surface *Surface, bool Flag, Uint32 Key )
{
#ifdef MultiSpacc_Target_SDLCom
Uint32 useKey;
if(Flag)
{
#if defined(MultiSpacc_Target_SDL12)
useKey = SDL_SRCCOLORKEY;
#elif defined(MultiSpacc_Target_SDL20)
useKey = SDL_TRUE;
#endif
}
else
{
#if defined(MultiSpacc_Target_SDL12)
useKey = 0;
#elif defined(MultiSpacc_Target_SDL20)
useKey = SDL_FALSE;
#endif
}
return SDL_SetColorKey( Surface, useKey, Key );
#endif
}
#ifdef MultiSpacc_Target_SDLCom #ifdef MultiSpacc_Target_SDLCom
int MultiSpacc_PollEvent( MultiSpacc_Event *Event ) int MultiSpacc_PollEvent( MultiSpacc_Event *Event )
{ {
SDL_Event FromEvent; SDL_Event FromEvent;
int Result = SDL_PollEvent( &FromEvent ); int Result = SDL_PollEvent( &FromEvent );
*Event = (MultiSpacc_Event) { *Event = (MultiSpacc_Event){
.Type = FromEvent.type, .Type = FromEvent.type,
.Key = FromEvent.key.keysym.sym, .Key = FromEvent.key.keysym.sym,
}; };

View File

@ -44,6 +44,10 @@
#define MultiSpacc_Rect SDL_Rect #define MultiSpacc_Rect SDL_Rect
#endif #endif
#ifdef MultiSpacc_Target_Web
#include <emscripten.h>
#endif
#ifdef MultiSpacc_Target_NDS #ifdef MultiSpacc_Target_NDS
#include <nds.h> #include <nds.h>
#define Uint32 int32 #define Uint32 int32
@ -81,6 +85,10 @@ MultiSpacc_Surface *MultiSpacc_GetWindowSurface( MultiSpacc_Window *Window );
void MultiSpacc_SetAppTitle( MultiSpacc_Window *Window, const char Title[] ); void MultiSpacc_SetAppTitle( MultiSpacc_Window *Window, const char Title[] );
void MultiSpacc_SetAppIcon( MultiSpacc_Window *Window, MultiSpacc_Surface *Icon ); void MultiSpacc_SetAppIcon( MultiSpacc_Window *Window, MultiSpacc_Surface *Icon );
bool MultiSpacc_SetMainLoop( bool function( void *args ), void *args );
bool MultiSpacc_WaitUpdateDisplay( MultiSpacc_Window *window, Uint32 *nextTick );
MultiSpacc_Surface *MultiSpacc_LoadImage( char FilePath[], MultiSpacc_Surface *Screen, Uint32 *ColorKey ); MultiSpacc_Surface *MultiSpacc_LoadImage( char FilePath[], MultiSpacc_Surface *Screen, Uint32 *ColorKey );
int MultiSpacc_SetColorKey( MultiSpacc_Surface *Surface, bool Flag, Uint32 Key ); int MultiSpacc_SetColorKey( MultiSpacc_Surface *Surface, bool Flag, Uint32 Key );

View File

@ -6,13 +6,13 @@ The idea is simple: to build an universal abstraction layer on top of other exis
The list of supported (or planned) backend libraries follows: The list of supported (or planned) backend libraries follows:
- SDL 1.2 (WIP) - Current PC platforms, modern mobile platforms and game consoles, the Web, ... via SDL 2.0 (WIP) (and 3.0?[^1])
- SDL 2.0/3.0 (WIP/?)[^1] - Old PC and embedded platforms, ... via SDL 1.2 (WIP)
- MS-DOS (?)
- NDS (WIP) - NDS (WIP)
- GBA (Planned) - GBA (Planned)
- NES (WIP) - NES (WIP)
SDL is used as the main cross-platform library (covering many old systems with v1.2, and all modern PC, embedded, and virtualized systems with v2.0). SDL is used as the main cross-platform library everywhere possible as stated above, for convenience. Specific platform that require special code are handled separately via other base abstraction layers.
Specific platform that require special code are handled separately via other base abstraction layers.
[^1]: I just discovered that SDL 3.0 exists, I'm so tired, why would they make a new major, now I have to support 3 versions... or just drop 2.0 and only support 1.2 + 3.0. [^1]: I just discovered that SDL 3.0 exists, I'm so tired, why would they make a new major, now I have to support 3 versions... or just drop 2.0 and only support 1.2 + 3.0.

View File

@ -8,34 +8,28 @@ MEMORY {
ZP: start = $00, size = $100, type = rw, define = yes; ZP: start = $00, size = $100, type = rw, define = yes;
# INES Cartridge Header # INES Cartridge Header
HEADER: start = $0, size = $10, file = %O, fill = yes;
HEADER: start = $0, size = $10, file = %O ,fill = yes;
# 2 16K ROM Banks # 2 16K ROM Banks
# - startup # - startup
# - code # - code
# - rodata # - rodata
# - data (load) # - data (load)
PRG: start = $8000, size = $7f00, file = %O, fill = yes, define = yes;
PRG: start = $8000, size = $7f00, file = %O ,fill = yes, define = yes;
# DPCM Samples at end of the ROM # DPCM Samples at end of the ROM
DMC: start = $ff00, size = $fa, file = %O, fill = yes; DMC: start = $ff00, size = $fa, file = %O, fill = yes;
# Hardware Vectors at end of the ROM # Hardware Vectors at end of the ROM
VECTORS: start = $fffa, size = $6, file = %O, fill = yes; VECTORS: start = $fffa, size = $6, file = %O, fill = yes;
# 1 8K CHR Bank # 1 8K CHR Bank
CHR: start = $0000, size = $2000, file = %O, fill = yes; CHR: start = $0000, size = $2000, file = %O, fill = yes;
# standard 2K SRAM (-zeropage) # standard 2K SRAM (-zeropage)
# $0100 famitone, palette, cpu stack # $0100 famitone, palette, cpu stack
# $0200 oam buffer # $0200 oam buffer
# $0300..$800 ca65 stack # $0300..$800 ca65 stack
RAM: start = $0300, size = $0500, define = yes; RAM: start = $0300, size = $0500, define = yes;
# Use this definition instead if you going to use extra 8K RAM # Use this definition instead if you going to use extra 8K RAM
@ -43,20 +37,20 @@ MEMORY {
} }
SEGMENTS { SEGMENTS {
HEADER: load = HEADER, type = ro; HEADER: load = HEADER, type = ro;
STARTUP: load = PRG, type = ro, define = yes; STARTUP: load = PRG, type = ro, define = yes;
LOWCODE: load = PRG, type = ro, optional = yes; LOWCODE: load = PRG, type = ro, optional = yes;
ONCE: load = PRG, type = ro, optional = yes; ONCE: load = PRG, type = ro, optional = yes;
INIT: load = PRG, type = ro, define = yes, optional = yes; INIT: load = PRG, type = ro, define = yes, optional = yes;
CODE: load = PRG, type = ro, define = yes; CODE: load = PRG, type = ro, define = yes;
RODATA: load = PRG, type = ro, define = yes; RODATA: load = PRG, type = ro, define = yes;
DATA: load = PRG, run = RAM, type = rw, define = yes; DATA: load = PRG, run = RAM, type = rw, define = yes;
VECTORS: load = VECTORS, type = rw; VECTORS: load = VECTORS, type = rw;
SAMPLES: load = DMC, type = rw; SAMPLES: load = DMC, type = rw;
CHARS: load = CHR, type = rw; CHARS: load = CHR, type = rw;
BSS: load = RAM, type = bss, define = yes; BSS: load = RAM, type = bss, define = yes;
HEAP: load = RAM, type = bss, optional = yes; HEAP: load = RAM, type = bss, optional = yes;
ZEROPAGE: load = ZP, type = zp; ZEROPAGE: load = ZP, type = zp;
} }
FEATURES { FEATURES {