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
before_script: |
apt update
apt install \
make gcc cc65 python3 python3-pil \
sudo apt update
sudo apt install -y \
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 \
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:
stage: Tests
script: |
cd ./LibMultiSpacc/Examples
for Example in */
do
cd ${Example}
for Build in "Target=LinuxPC MultiSpacc_Target=SDL12" "Target=LinuxPC MultiSpacc_Target=SDL20" "Target=NES"
do
for Build in \
"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 -j$(nproc --all) ${Build}
done

View File

@ -5,51 +5,72 @@ SpaccSources = $(wildcard ../../LibMultiSpacc/*.c)
SpaccHeaders = $(wildcard ../../LibMultiSpacc/*.h)
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
ifeq ($(shell uname --operating-system), Msys)
ifeq ($(Host), Windows)
Target = WindowsPC
else
else ifeq ($(Host), Linux)
Target = LinuxPC
endif
endif
ifdef Target
ifeq ($(Target), LinuxPC)
ExeSuffix = .run
Defines += -DTarget_LinuxPC
MultiSpacc_Target = SDL20
else ifeq ($(Target), WindowsPC)
ExeSuffix = .exe
Defines += -DTarget_WindowsPC
MultiSpacc_Target = SDL20
else ifeq ($(Target), Windows9x)
ExeSuffix = .exe
Defines += -DTarget_Windows9x
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
# TODO: handle building for Windows targets from Linux hosts
ifeq ($(Target), LinuxPC)
ExeSuffix = .run
Defines += -DTarget_LinuxPC
MultiSpacc_Target = SDL20
else ifeq ($(Target), WindowsPC)
ExeSuffix = .exe
Defines += -DTarget_WindowsPC
MultiSpacc_Target = SDL20
ifneq ($(Host), Windows)
ToolsSuffix = -mingw-w64
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
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)
LdFlags += $(shell sdl-config --libs) -lSDLmain -lSDL -lSDL_image -lSDL_mixer -lSDL_ttf
BuildProcess = __Normal__
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)
LdFlags += $(shell sdl2-config --libs) -lSDL2main -lSDL2 -lSDL2_image -lSDL2_mixer -lSDL2_ttf
BuildProcess = __Normal__
else ifeq ($(MultiSpacc_Target), Web)
Defines += -DMultiSpacc_Target_Web -DMultiSpacc_Target_SDL20 -DMultiSpacc_Target_SDLCom
BuildProcess = __Web__
else ifeq ($(MultiSpacc_Target), NDS)
Defines += -DMultiSpacc_Target_NDS
BuildProcess = __NDS__
@ -58,16 +79,24 @@ else ifeq ($(MultiSpacc_Target), NES)
BuildProcess = __NES__
endif
CC = $(ToolsPrefix)gcc $(CFlags) $(Defines)
BuildSources = $(AppSources) $(SpaccSources)
BuildObjects = $(BuildSources:.c=.o)
ifeq ($(BuildProcess), __Normal__)
CC = $(ToolsWrapper) $(ToolsPrefix)gcc$(ToolsSuffix) $(CFlags) $(Defines)
endif
All all: $(BuildProcess)
# TODO: use virtual build dirs even for normals to allow linking against different libraries without recleaning
__Normal__: $(BuildObjects)
$(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
__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"
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"}*/
const char PALETTE[32] = {
0x03, // screen
@ -15,67 +26,72 @@ const char PALETTE[32] = {
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 spriteX = 0;
int spriteY = 0;
int accelX = +1;
int accelY = +2;
MainArgs margs = {0};
MultiSpacc_SurfaceConfig WindowConfig = {0};
MultiSpacc_Window *Window;
MultiSpacc_Surface *Screen;
MultiSpacc_Surface *TilesImg;
margs.WindowConfig = &WindowConfig;
margs.accelX = +1;
margs.accelY = +2;
WindowConfig.Width = 320;
WindowConfig.Height = 240;
WindowConfig.Bits = 16;
memcpy( WindowConfig.Palette, PALETTE, 32 );
//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");
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>
// Copyright (c) 2018 Doug Fraker www.nesdoug.com (MIT)
TilesImg = MultiSpacc_LoadImage( "CHARS.png", Screen, NULL );
MultiSpacc_PrintText( "Hello, World!", Screen, &WindowConfig, 2, 2, TilesImg );
MultiSpacc_PrintDebug("[I] Ready!\n");
margs.TilesImg = MultiSpacc_LoadImage( "CHARS.png", margs.Screen, NULL );
MultiSpacc_PrintText( "Hello, World!", margs.Screen, &WindowConfig, 2, 2, margs.TilesImg );
while(true)
{
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;
return MultiSpacc_SetMainLoop( MainLoop, &margs );
}

View File

@ -1,30 +1,88 @@
#include "./MultiSpacc.h"
#ifdef MultiSpacc_Target_SDL12
int MultiSpacc_SetColorKey( MultiSpacc_Surface *Surface, bool Flag, Uint32 Key ) {
if( Flag )
return SDL_SetColorKey( Surface, SDL_SRCCOLORKEY, Key );
else
return SDL_SetColorKey( Surface, 0, Key );
}
#endif
#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 );
bool MultiSpacc_SetMainLoop( bool function( void *args ), void *args )
{
#ifdef MultiSpacc_Target_Web
emscripten_set_main_loop_arg( function, args, -1, true );
#else
while(true){
if( !function(args) ){
return false;
}
}
#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
int MultiSpacc_PollEvent( MultiSpacc_Event *Event )
{
SDL_Event FromEvent;
int Result = SDL_PollEvent( &FromEvent );
*Event = (MultiSpacc_Event) {
*Event = (MultiSpacc_Event){
.Type = FromEvent.type,
.Key = FromEvent.key.keysym.sym,
};

View File

@ -44,6 +44,10 @@
#define MultiSpacc_Rect SDL_Rect
#endif
#ifdef MultiSpacc_Target_Web
#include <emscripten.h>
#endif
#ifdef MultiSpacc_Target_NDS
#include <nds.h>
#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_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 );
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:
- SDL 1.2 (WIP)
- SDL 2.0/3.0 (WIP/?)[^1]
- Current PC platforms, modern mobile platforms and game consoles, the Web, ... via SDL 2.0 (WIP) (and 3.0?[^1])
- Old PC and embedded platforms, ... via SDL 1.2 (WIP)
- MS-DOS (?)
- NDS (WIP)
- GBA (Planned)
- 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).
Specific platform that require special code are handled separately via other base abstraction layers.
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.
[^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;
# 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
# - startup
# - code
# - rodata
# - 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
DMC: start = $ff00, size = $fa, file = %O, fill = yes;
# Hardware Vectors at end of the ROM
VECTORS: start = $fffa, size = $6, file = %O, fill = yes;
# 1 8K CHR Bank
CHR: start = $0000, size = $2000, file = %O, fill = yes;
# standard 2K SRAM (-zeropage)
# $0100 famitone, palette, cpu stack
# $0200 oam buffer
# $0300..$800 ca65 stack
RAM: start = $0300, size = $0500, define = yes;
# Use this definition instead if you going to use extra 8K RAM
@ -43,20 +37,20 @@ MEMORY {
}
SEGMENTS {
HEADER: load = HEADER, type = ro;
STARTUP: load = PRG, type = ro, define = yes;
LOWCODE: load = PRG, type = ro, optional = yes;
ONCE: load = PRG, type = ro, optional = yes;
INIT: load = PRG, type = ro, define = yes, optional = yes;
CODE: load = PRG, type = ro, define = yes;
RODATA: load = PRG, type = ro, define = yes;
DATA: load = PRG, run = RAM, type = rw, define = yes;
VECTORS: load = VECTORS, type = rw;
SAMPLES: load = DMC, type = rw;
CHARS: load = CHR, type = rw;
BSS: load = RAM, type = bss, define = yes;
HEAP: load = RAM, type = bss, optional = yes;
ZEROPAGE: load = ZP, type = zp;
HEADER: load = HEADER, type = ro;
STARTUP: load = PRG, type = ro, define = yes;
LOWCODE: load = PRG, type = ro, optional = yes;
ONCE: load = PRG, type = ro, optional = yes;
INIT: load = PRG, type = ro, define = yes, optional = yes;
CODE: load = PRG, type = ro, define = yes;
RODATA: load = PRG, type = ro, define = yes;
DATA: load = PRG, run = RAM, type = rw, define = yes;
VECTORS: load = VECTORS, type = rw;
SAMPLES: load = DMC, type = rw;
CHARS: load = CHR, type = rw;
BSS: load = RAM, type = bss, define = yes;
HEAP: load = RAM, type = bss, optional = yes;
ZEROPAGE: load = ZP, type = zp;
}
FEATURES {