From 9be90d762943c42437ce78f607029d775fbde794 Mon Sep 17 00:00:00 2001 From: OctoSpacc Date: Wed, 1 Nov 2023 13:58:22 +0100 Subject: [PATCH] Add NES building and adapt example functions --- .gitignore | 2 + LibMultiSpacc/Examples/Common.mk | 43 +- .../Examples/HelloWorld/HelloWorld.c | 46 +- LibMultiSpacc/Examples/NES.mk.sh | 14 + LibMultiSpacc/LibMultiSpacc/MultiSpacc.c | 12 + LibMultiSpacc/LibMultiSpacc/MultiSpacc.h | 21 +- LibMultiSpacc/LibMultiSpacc/Print.c | 9 +- LibMultiSpacc/LibMultiSpacc/Setup.c | 12 +- LibMultiSpacc/neslib/.gitignore | 5 + LibMultiSpacc/neslib/COPYING | 18 + LibMultiSpacc/neslib/Makefile | 12 + LibMultiSpacc/neslib/NROM256.cfg | 76 + LibMultiSpacc/neslib/README.asciidoc | 13 + LibMultiSpacc/neslib/crt0.s | 208 +++ LibMultiSpacc/neslib/famitone2.sinc | 1243 +++++++++++++++++ LibMultiSpacc/neslib/lz4vram.s | 349 +++++ LibMultiSpacc/neslib/memfill.s | 49 + LibMultiSpacc/neslib/nes.inc | 16 + LibMultiSpacc/neslib/neslib.h | 339 +++++ LibMultiSpacc/neslib/neslib.sinc | 791 +++++++++++ LibMultiSpacc/neslib/oam_clear_fast.s | 20 + LibMultiSpacc/neslib/oam_meta_spr.s | 64 + LibMultiSpacc/neslib/oam_meta_spr_clip.s | 107 ++ LibMultiSpacc/neslib/oam_meta_spr_pal.s | 71 + LibMultiSpacc/neslib/oam_spr.s | 42 + LibMultiSpacc/neslib/pad.s | 89 ++ LibMultiSpacc/neslib/rand.s | 71 + LibMultiSpacc/neslib/split.s | 41 + LibMultiSpacc/neslib/splitxy.s | 81 ++ LibMultiSpacc/neslib/vram_read.s | 47 + LibMultiSpacc/neslib/vram_unrle.s | 60 + LibMultiSpacc/neslib/zpvars.inc | 14 + Transpiler/README.md | 1 + 33 files changed, 3951 insertions(+), 35 deletions(-) create mode 100644 LibMultiSpacc/Examples/NES.mk.sh create mode 100644 LibMultiSpacc/neslib/.gitignore create mode 100644 LibMultiSpacc/neslib/COPYING create mode 100644 LibMultiSpacc/neslib/Makefile create mode 100644 LibMultiSpacc/neslib/NROM256.cfg create mode 100644 LibMultiSpacc/neslib/README.asciidoc create mode 100644 LibMultiSpacc/neslib/crt0.s create mode 100644 LibMultiSpacc/neslib/famitone2.sinc create mode 100644 LibMultiSpacc/neslib/lz4vram.s create mode 100644 LibMultiSpacc/neslib/memfill.s create mode 100644 LibMultiSpacc/neslib/nes.inc create mode 100644 LibMultiSpacc/neslib/neslib.h create mode 100644 LibMultiSpacc/neslib/neslib.sinc create mode 100644 LibMultiSpacc/neslib/oam_clear_fast.s create mode 100644 LibMultiSpacc/neslib/oam_meta_spr.s create mode 100644 LibMultiSpacc/neslib/oam_meta_spr_clip.s create mode 100644 LibMultiSpacc/neslib/oam_meta_spr_pal.s create mode 100644 LibMultiSpacc/neslib/oam_spr.s create mode 100644 LibMultiSpacc/neslib/pad.s create mode 100644 LibMultiSpacc/neslib/rand.s create mode 100644 LibMultiSpacc/neslib/split.s create mode 100644 LibMultiSpacc/neslib/splitxy.s create mode 100644 LibMultiSpacc/neslib/vram_read.s create mode 100644 LibMultiSpacc/neslib/vram_unrle.s create mode 100644 LibMultiSpacc/neslib/zpvars.inc create mode 100644 Transpiler/README.md diff --git a/.gitignore b/.gitignore index 37515ed..7c381c9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ *.o +*.lib *.exe *.run *.tmp +.tmp/ Build/ diff --git a/LibMultiSpacc/Examples/Common.mk b/LibMultiSpacc/Examples/Common.mk index cb0fe42..0d0460b 100644 --- a/LibMultiSpacc/Examples/Common.mk +++ b/LibMultiSpacc/Examples/Common.mk @@ -3,7 +3,7 @@ AppSources = $(wildcard *.c) AppHeaders = $(wildcard *.h) SpaccSources = $(wildcard ../../LibMultiSpacc/*.c) SpaccHeaders = $(wildcard ../../LibMultiSpacc/*.h) -CFlags = -Os -Wpedantic -Werror +CFlags = -Os -Werror -Wpedantic -Wdeclaration-after-statement # Default build is always for the host system ifndef Target @@ -36,30 +36,37 @@ ifeq ($(MultiSpacc_Target), SDL12) Defines += -DMultiSpacc_Target_SDL12 -DMultiSpacc_Target_SDLCom CFlags += $(shell sdl-config --cflags) LdFlags += $(shell sdl-config --libs) -lSDL -lSDL_image -lSDL_mixer -lSDL_ttf - BuildProcess = Normal + BuildProcess = __Normal__ else ifeq ($(MultiSpacc_Target), SDL20) Defines += -DMultiSpacc_Target_SDL20 -DMultiSpacc_Target_SDLCom CFlags += $(shell sdl2-config --cflags) LdFlags += $(shell sdl2-config --libs) -lSDL2 -lSDL2_image -lSDL2_mixer -lSDL2_ttf - BuildProcess = Normal + BuildProcess = __Normal__ else ifeq ($(MultiSpacc_Target), NDS) Defines += -DMultiSpacc_Target_NDS - BuildProcess = NDS + BuildProcess = __NDS__ else ifeq ($(MultiSpacc_Target), NES) Defines += -DMultiSpacc_Target_NES - BuildProcess = NES + BuildProcess = __NES__ endif CC = $(ToolsPrefix)gcc $(CFlags) $(Defines) -BuildSources = $(AppSources) $(SpaccSources) -Objects = $(BuildSources:.c=.o) +Shell = $(shell echo $$SHELL) +AppObjects = $(AppSources:.c=.o) +SpaccObjects = $(SpaccSources:.c=.o) +BuildObjects = $(AppObjects) $(SpaccObjects) +#BuildSources = $(AppSources) $(SpaccSources) +#Objects = $(BuildSources:.c=.o) All all: $(BuildProcess) -Normal: $(Objects) +# TODO: use virtual build dirs even for normals to allow linking against different wrapped libraries +__Normal__: $(BuildObjects) $(CC) $^ $(LdFlags) -o $(AppName)$(ExeSuffix) -NDS: +# TODO: Fix include substitutions properly in non-standard build processes + +__NDS__: $(eval VirtualBuildDir = ./Build/NDS) mkdir -p $(VirtualBuildDir)/source/.tmp cp ../NDS.mk $(VirtualBuildDir)/Makefile @@ -71,8 +78,20 @@ NDS: for i in $(VirtualBuildDir)/source/*; do sed -i 's|#include[ \t]"./|#include "./LibMultiSpacc_|g' $$i; done cd $(VirtualBuildDir); make -NES: - # +__NES__: + cd ../../neslib; make + $(eval VirtualBuildDir = ./Build/NES) + mkdir -p $(VirtualBuildDir)/.tmp + cp $(SpaccSources) $(SpaccHeaders) $(VirtualBuildDir)/.tmp/ + cd $(VirtualBuildDir)/.tmp; for i in *; do mv ./$$i ../LibMultiSpacc_$$i; done + cp $(AppSources) $(AppHeaders) $(VirtualBuildDir)/ + for i in $(VirtualBuildDir)/*; do sed -i 's|#include[ \t]"../../LibMultiSpacc/|#include "LibMultiSpacc_|g' $$i; done + for i in $(VirtualBuildDir)/*; do sed -i 's|#include[ \t]"../MultiSpacc|#include "LibMultiSpacc_MultiSpacc|g' $$i; done + for i in $(VirtualBuildDir)/*; do sed -i 's|#include[ \t]"./|#include "./LibMultiSpacc_|g' $$i; done + cp ../../neslib/*.cfg ../../neslib/crt0.o ../../neslib/chr_generic.o ../../neslib/*.lib ../../neslib/*.h $(VirtualBuildDir)/ + echo "AppName='$(AppName)'; Defines='$(Defines)'; AppSources='$(AppSources)'; SpaccSources='$(SpaccSources)'; AppObjects='$(AppObjects)'; BuildObjects='$(BuildObjects)';" > $(VirtualBuildDir)/Make.sh + cat ../NES.mk.sh >> $(VirtualBuildDir)/Make.sh + cd $(VirtualBuildDir); $(Shell) ./Make.sh Run run: All ./$(AppName)$(ExeSuffix) @@ -80,5 +99,5 @@ Run run: All Clean clean Clear clear: find -L . -name "*.o" -type f -delete find -L ../../LibMultiSpacc -name "*.o" -type f -delete - rm -f ./$(AppName)$(ExeSuffix) ./$(AppName).*$(ExeSuffix) + rm -f ./$(AppName)$(ExeSuffix) rm -rf ./Build diff --git a/LibMultiSpacc/Examples/HelloWorld/HelloWorld.c b/LibMultiSpacc/Examples/HelloWorld/HelloWorld.c index bba4f0c..ac30547 100644 --- a/LibMultiSpacc/Examples/HelloWorld/HelloWorld.c +++ b/LibMultiSpacc/Examples/HelloWorld/HelloWorld.c @@ -2,16 +2,38 @@ #define AppName "Hello World" +/*{pal:"nes",layout:"nes"}*/ +const char PALETTE[32] = { + 0x03, // screen + 0x11,0x30,0x27,0x00, // background 0 + 0x1c,0x20,0x2c,0x00, // background 1 + 0x00,0x10,0x20,0x00, // background 2 + 0x06,0x16,0x26,0x00, // background 3 + 0x16,0x35,0x24,0x00, // sprite 0 + 0x00,0x37,0x25,0x00, // sprite 1 + 0x0d,0x2d,0x3a,0x00, // sprite 2 + 0x0d,0x27,0x2a, // sprite 3 +}; + int main( int argc, char *argv[] ) { int spriteX = 0; int spriteY = 0; - int accelX = +2; + int accelX = +1; int accelY = +2; - MultiSpacc_SurfaceConfig WindowConfig = { .Width = 320, .Height = 240, .Bits = 16 }; - MultiSpacc_Window *Window = MultiSpacc_SetWindow( WindowConfig ); - MultiSpacc_Surface *Screen = MultiSpacc_GetWindowSurface( Window ); + MultiSpacc_SurfaceConfig WindowConfig = {0}; + MultiSpacc_Window *Window; + MultiSpacc_Surface *Screen; + MultiSpacc_Surface *TilesImg; + + WindowConfig.Width = 256; + 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 ) { @@ -23,19 +45,14 @@ int main( int argc, char *argv[] ) // Bitmap font borrowed from: // Copyright (c) 2018 Doug Fraker www.nesdoug.com (MIT) - MultiSpacc_Surface *TilesImg = MultiSpacc_LoadImage( "Tiles.png", Screen, NULL ); - MultiSpacc_PrintText( "Hello, World!", Screen, WindowConfig, 0, 0, TilesImg ); + //TilesImg = MultiSpacc_LoadImage( "Tiles.png", Screen, NULL ); + MultiSpacc_PrintText( "Hello, World!", Screen, &WindowConfig, 2, 2, TilesImg ); MultiSpacc_PrintDebug("[I] Ready!\n"); - // if( MultiSpacc_UpdateWindowSurface(Window) != 0 ) - // { - // MultiSpacc_PrintDebug("[E] Error Updating Screen.\n"); - // return -1; - // }; - while(true) { - MultiSpacc_Sprite( 0, spriteX, spriteY, 0x80, TilesImg, Screen ); + MultiSpacc_Sprite( 0, spriteX, spriteY, 20/*128*/, TilesImg, Screen ); + //scroll(spriteX,0); spriteX += accelX; spriteY += accelY; @@ -57,9 +74,8 @@ int main( int argc, char *argv[] ) }; // TODO: Implement cross-platform vblank-wait - MultiSpacc_Sleep(16); + //MultiSpacc_Sleep(16); } - //MultiSpacc_Sleep(4000); return 0; } diff --git a/LibMultiSpacc/Examples/NES.mk.sh b/LibMultiSpacc/Examples/NES.mk.sh new file mode 100644 index 0000000..2419f85 --- /dev/null +++ b/LibMultiSpacc/Examples/NES.mk.sh @@ -0,0 +1,14 @@ +#!/dev/null +set -e + +# ${SpaccSources} ${AppSources} ${SpaccObjects} ${AppObjects} chr_generic.s chr_generic.o crt0.o + +for File in *.c +do cc65 -Oirs --target nes ${File} ${Defines} +done + +for File in *.s +do ca65 --target nes ${File} +done + +ld65 -v -o ${AppName}.nes *.o *.lib nes.lib --config NROM256.cfg -DNES_MAPPER=0 -DNES_PRG_BANKS=2 -DNES_CHR_BANKS=1 -DNES_MIRRORING=0 diff --git a/LibMultiSpacc/LibMultiSpacc/MultiSpacc.c b/LibMultiSpacc/LibMultiSpacc/MultiSpacc.c index d0b7611..b1fea4e 100644 --- a/LibMultiSpacc/LibMultiSpacc/MultiSpacc.c +++ b/LibMultiSpacc/LibMultiSpacc/MultiSpacc.c @@ -86,4 +86,16 @@ void MultiSpacc_Sprite( int id, int x, int y, int sprite, MultiSpacc_Surface *Ti }; SDL_BlitSurface( Tiles, &Clip, Surface, &Offset ); #endif + + #ifdef MultiSpacc_Target_NES + oam_spr(x, y, sprite, 0, id); + #endif } + +#ifdef MultiSpacc_Target_NES + int MultiSpacc_UpdateWindowSurface( MultiSpacc_Window *Window ) + { + ppu_wait_frame(); + return 0; + } +#endif diff --git a/LibMultiSpacc/LibMultiSpacc/MultiSpacc.h b/LibMultiSpacc/LibMultiSpacc/MultiSpacc.h index 7196fb9..bb1d84b 100644 --- a/LibMultiSpacc/LibMultiSpacc/MultiSpacc.h +++ b/LibMultiSpacc/LibMultiSpacc/MultiSpacc.h @@ -1,9 +1,14 @@ -#pragma once +#ifndef _MultiSpacc_h_ +#define _MultiSpacc_h_ + #include -#include +#ifndef MultiSpacc_Target_NES + #include +#endif #include #include #include +#include #ifdef MultiSpacc_Target_SDL12 #include @@ -54,16 +59,22 @@ #ifdef MultiSpacc_Target_NES #include #include "neslib.h" + #define Uint32 int + #define MultiSpacc_Window int + #define MultiSpacc_Surface int + #define MultiSpacc_Event int + int MultiSpacc_UpdateWindowSurface( MultiSpacc_Window *Window ); #endif typedef struct MultiSpacc_SurfaceConfig { int Width; int Height; int Bits; + char Palette[32]; Uint32 Flags; } MultiSpacc_SurfaceConfig; -MultiSpacc_Window *MultiSpacc_SetWindow( MultiSpacc_SurfaceConfig WindowConfig ); +MultiSpacc_Window *MultiSpacc_SetWindow( MultiSpacc_SurfaceConfig *WindowConfig ); MultiSpacc_Surface *MultiSpacc_GetWindowSurface( MultiSpacc_Window *Window ); void MultiSpacc_SetAppTitle( MultiSpacc_Window *Window, const char Title[] ); @@ -75,6 +86,8 @@ int MultiSpacc_SetColorKey( MultiSpacc_Surface *Surface, bool Flag, Uint32 Key ) int MultiSpacc_PollEvent( MultiSpacc_Event *Event ); void MultiSpacc_PrintDebug( const char *format, ... ); -void MultiSpacc_PrintText( char Text[], MultiSpacc_Surface *Surface, MultiSpacc_SurfaceConfig WindowConfig, int x, int y, MultiSpacc_Surface *Tiles /*, int FontSize, int Color */ ); // WIP +void MultiSpacc_PrintText( char Text[], MultiSpacc_Surface *Surface, MultiSpacc_SurfaceConfig *WindowConfig, int x, int y, MultiSpacc_Surface *Tiles /*, int FontSize, int Color */ ); // WIP void MultiSpacc_Sprite( int id, int x, int y, int sprite, MultiSpacc_Surface *Tiles, MultiSpacc_Surface *Surface ); + +#endif // _MultiSpacc_h_ \ No newline at end of file diff --git a/LibMultiSpacc/LibMultiSpacc/Print.c b/LibMultiSpacc/LibMultiSpacc/Print.c index 480f0d0..4552720 100644 --- a/LibMultiSpacc/LibMultiSpacc/Print.c +++ b/LibMultiSpacc/LibMultiSpacc/Print.c @@ -1,6 +1,6 @@ #include "./MultiSpacc.h" -void MultiSpacc_PrintText( char Text[], MultiSpacc_Surface *Surface, MultiSpacc_SurfaceConfig WindowConfig, int x, int y, MultiSpacc_Surface *Tiles /*, int FontSize, int Color */ ) +void MultiSpacc_PrintText( char Text[], MultiSpacc_Surface *Surface, MultiSpacc_SurfaceConfig *WindowConfig, int x, int y, MultiSpacc_Surface *Tiles /*, int FontSize, int Color */ ) { #ifdef MultiSpacc_Target_SDLCom for( int i = 0; i < strlen(Text); i++ ) @@ -22,6 +22,13 @@ void MultiSpacc_PrintText( char Text[], MultiSpacc_Surface *Surface, MultiSpacc_ #ifdef MultiSpacc_Target_NDS iprintf("%s", Text); #endif + + #ifdef MultiSpacc_Target_NES + ppu_off(); + vram_adr(NTADR_A( x, y )); + vram_write( Text, strlen(Text) ); + ppu_on_all(); + #endif } void MultiSpacc_PrintDebug( const char *format, ... ) diff --git a/LibMultiSpacc/LibMultiSpacc/Setup.c b/LibMultiSpacc/LibMultiSpacc/Setup.c index 36d347b..e018e5c 100644 --- a/LibMultiSpacc/LibMultiSpacc/Setup.c +++ b/LibMultiSpacc/LibMultiSpacc/Setup.c @@ -1,13 +1,13 @@ #include "./MultiSpacc.h" -MultiSpacc_Window *MultiSpacc_SetWindow( MultiSpacc_SurfaceConfig WindowConfig ) +MultiSpacc_Window *MultiSpacc_SetWindow( MultiSpacc_SurfaceConfig *WindowConfig ) { #ifdef MultiSpacc_Target_SDL12 - return SDL_SetVideoMode( WindowConfig.Width, WindowConfig.Height, WindowConfig.Bits, WindowConfig.Flags ); + return SDL_SetVideoMode( WindowConfig->Width, WindowConfig->Height, WindowConfig->Bits, WindowConfig->Flags ); #endif #ifdef MultiSpacc_Target_SDL20 - return SDL_CreateWindow( NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WindowConfig.Width, WindowConfig.Height, WindowConfig.Flags ); + return SDL_CreateWindow( NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WindowConfig->Width, WindowConfig->Height, WindowConfig->Flags ); #endif #ifdef MultiSpacc_Target_NDS @@ -20,6 +20,12 @@ MultiSpacc_Window *MultiSpacc_SetWindow( MultiSpacc_SurfaceConfig WindowConfig ) consoleSelect(bottomScreen); return bottomScreen; #endif + + #ifdef MultiSpacc_Target_NES + oam_clear(); + pal_all(WindowConfig->Palette); + ppu_on_all(); + #endif } MultiSpacc_Surface *MultiSpacc_GetWindowSurface( MultiSpacc_Window *Window ) diff --git a/LibMultiSpacc/neslib/.gitignore b/LibMultiSpacc/neslib/.gitignore new file mode 100644 index 0000000..4c1aeeb --- /dev/null +++ b/LibMultiSpacc/neslib/.gitignore @@ -0,0 +1,5 @@ +*.o +*.lst +*~ +crt0.lst +neslib2.lib diff --git a/LibMultiSpacc/neslib/COPYING b/LibMultiSpacc/neslib/COPYING new file mode 100644 index 0000000..4806cef --- /dev/null +++ b/LibMultiSpacc/neslib/COPYING @@ -0,0 +1,18 @@ + (C) 2015 Alex Semenov (Shiru) + (C) 2016 Lauri Kasanen + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. diff --git a/LibMultiSpacc/neslib/Makefile b/LibMultiSpacc/neslib/Makefile new file mode 100644 index 0000000..e29aadf --- /dev/null +++ b/LibMultiSpacc/neslib/Makefile @@ -0,0 +1,12 @@ +.PHONY: all clean + +all: crt0.o neslib2.lib + +neslib2.lib: lz4vram.o pad.o rand.o memfill.o vram_read.o vram_unrle.o oam_meta_spr_clip.o oam_meta_spr_pal.o oam_meta_spr.o oam_spr.o oam_clear_fast.o split.o splitxy.o + ar65 a neslib2.lib $^ + +%.o: %.s $(wildcard *.s *.sinc) + cl65 -t nes -Oisr -g -c $*.s + +clean: + rm -f *.o *.lst neslib2.lib diff --git a/LibMultiSpacc/neslib/NROM256.cfg b/LibMultiSpacc/neslib/NROM256.cfg new file mode 100644 index 0000000..72ff3c0 --- /dev/null +++ b/LibMultiSpacc/neslib/NROM256.cfg @@ -0,0 +1,76 @@ +SYMBOLS { + __STACKSIZE__: type = weak, value = $0500; # 5 pages stack +} + +# NROM256 (32 KB PRG ROM) + +MEMORY { + ZP: start = $00, size = $100, type = rw, define = yes; + + # INES Cartridge Header + + 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; + + # 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 + # RAM: start = $6000, size = $2000, define = yes; +} + +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; +} + +FEATURES { + CONDES: segment = INIT, + type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__; + CONDES: segment = RODATA, + type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__; + CONDES: type = interruptor, + segment = RODATA, + label = __INTERRUPTOR_TABLE__, + count = __INTERRUPTOR_COUNT__; +} + diff --git a/LibMultiSpacc/neslib/README.asciidoc b/LibMultiSpacc/neslib/README.asciidoc new file mode 100644 index 0000000..e38a85b --- /dev/null +++ b/LibMultiSpacc/neslib/README.asciidoc @@ -0,0 +1,13 @@ +Neslib +====== + +This is a ca65 asm library consisting of useful NES functions. It's designed to be +used with cc65 git master, no need for old runtimes or the like. + +Credit for making this goes to Shiru, who published the original in public domain. + +@clbr made some changes and fixed some things. + +@sehugg made some more changes to integrate with http://8bitworkshop.com/ + +This is under the zlib license. diff --git a/LibMultiSpacc/neslib/crt0.s b/LibMultiSpacc/neslib/crt0.s new file mode 100644 index 0000000..65bb09a --- /dev/null +++ b/LibMultiSpacc/neslib/crt0.s @@ -0,0 +1,208 @@ +; Startup code for cc65 and Shiru's NES library +; based on code by Groepaz/Hitmen , Ullrich von Bassewitz +; edited by Steven Hugg (remove integrated Famitone2 library, add NMICallback) + + .export _exit,__STARTUP__:absolute=1 + .import initlib,push0,popa,popax,_main,zerobss,copydata + + ; Linker generated symbols + .import __RAM_START__ ,__RAM_SIZE__ + .import __ROM0_START__ ,__ROM0_SIZE__ + .import __STARTUP_LOAD__,__STARTUP_RUN__,__STARTUP_SIZE__ + .import __CODE_LOAD__ ,__CODE_RUN__ ,__CODE_SIZE__ + .import __RODATA_LOAD__ ,__RODATA_RUN__ ,__RODATA_SIZE__ + .import NES_MAPPER,NES_PRG_BANKS,NES_CHR_BANKS,NES_MIRRORING + + .include "zeropage.inc" + .include "nes.inc" + +FT_BASE_ADR =$0100 ;page in RAM, should be $xx00 + +.define FT_THREAD 1 ;undefine if you call sound effects in the same thread as sound update +.define FT_PAL_SUPPORT 1 ;undefine to exclude PAL support +.define FT_NTSC_SUPPORT 1 ;undefine to exclude NTSC support + +.segment "ZEROPAGE" + +NTSC_MODE: .res 1 +FRAME_CNT1: .res 1 +FRAME_CNT2: .res 1 +VRAM_UPDATE: .res 1 +NAME_UPD_ADR: .res 2 +NAME_UPD_ENABLE: .res 1 +PAL_UPDATE: .res 1 +PAL_BG_PTR: .res 2 +PAL_SPR_PTR: .res 2 +SCROLL_X: .res 1 +SCROLL_Y: .res 1 +SCROLL_X1: .res 1 +SCROLL_Y1: .res 1 +PPU_CTRL_VAR: .res 1 +PPU_CTRL_VAR1: .res 1 +PPU_MASK_VAR: .res 1 +;;FT_TEMP: .res 3 +_oam_off: .res 1 +NMICallback: .res 3 + +TEMP: .res 11 + +.exportzp NTSC_MODE, FRAME_CNT1, FRAME_CNT2, VRAM_UPDATE +.exportzp NAME_UPD_ADR, NAME_UPD_ENABLE +.exportzp PAL_UPDATE, PAL_BG_PTR, PAL_SPR_PTR +.exportzp SCROLL_X, SCROLL_Y, SCROLL_X1, SCROLL_Y1 +.exportzp PPU_CTRL_VAR, PPU_CTRL_VAR1, PPU_MASK_VAR +.exportzp _oam_off, NMICallback +.exportzp TEMP + +.include "zpvars.inc" + +.segment "HEADER" + + .byte $4e,$45,$53,$1a + .byte (__RAM_START__+__RAM_SIZE__) + sta sp+1 ; Set argument stack ptr + + jsr initlib + +; setup NMICallback trampoline to NOP + lda #$4C ;JMP xxxx + sta NMICallback + lda #HandyRTS + sta NMICallback+2 + + lda #%10000000 + sta >6 + + +;zero page variables + +FT_TEMP_PTR = FT_TEMP ;word +FT_TEMP_PTR_L = FT_TEMP_PTR+0 +FT_TEMP_PTR_H = FT_TEMP_PTR+1 +FT_TEMP_VAR1 = FT_TEMP+2 + + +;envelope structure offsets, 5 bytes per envelope, grouped by variable type + +FT_ENVELOPES_ALL = 3+3+3+2 ;3 for the pulse and triangle channels, 2 for the noise channel +FT_ENV_STRUCT_SIZE = 5 + +FT_ENV_VALUE = FT_BASE_ADR+0*FT_ENVELOPES_ALL +FT_ENV_REPEAT = FT_BASE_ADR+1*FT_ENVELOPES_ALL +FT_ENV_ADR_L = FT_BASE_ADR+2*FT_ENVELOPES_ALL +FT_ENV_ADR_H = FT_BASE_ADR+3*FT_ENVELOPES_ALL +FT_ENV_PTR = FT_BASE_ADR+4*FT_ENVELOPES_ALL + + +;channel structure offsets, 7 bytes per channel + +FT_CHANNELS_ALL = 5 +FT_CHN_STRUCT_SIZE = 9 + +FT_CHN_PTR_L = FT_BASE_ADR+0*FT_CHANNELS_ALL +FT_CHN_PTR_H = FT_BASE_ADR+1*FT_CHANNELS_ALL +FT_CHN_NOTE = FT_BASE_ADR+2*FT_CHANNELS_ALL +FT_CHN_INSTRUMENT = FT_BASE_ADR+3*FT_CHANNELS_ALL +FT_CHN_REPEAT = FT_BASE_ADR+4*FT_CHANNELS_ALL +FT_CHN_RETURN_L = FT_BASE_ADR+5*FT_CHANNELS_ALL +FT_CHN_RETURN_H = FT_BASE_ADR+6*FT_CHANNELS_ALL +FT_CHN_REF_LEN = FT_BASE_ADR+7*FT_CHANNELS_ALL +FT_CHN_DUTY = FT_BASE_ADR+8*FT_CHANNELS_ALL + + +;variables and aliases + +FT_ENVELOPES = FT_BASE_ADR +FT_CH1_ENVS = FT_ENVELOPES+0 +FT_CH2_ENVS = FT_ENVELOPES+3 +FT_CH3_ENVS = FT_ENVELOPES+6 +FT_CH4_ENVS = FT_ENVELOPES+9 + +FT_CHANNELS = FT_ENVELOPES+FT_ENVELOPES_ALL*FT_ENV_STRUCT_SIZE +FT_CH1_VARS = FT_CHANNELS+0 +FT_CH2_VARS = FT_CHANNELS+1 +FT_CH3_VARS = FT_CHANNELS+2 +FT_CH4_VARS = FT_CHANNELS+3 +FT_CH5_VARS = FT_CHANNELS+4 + + +FT_CH1_NOTE = FT_CH1_VARS+.lobyte(FT_CHN_NOTE) +FT_CH2_NOTE = FT_CH2_VARS+.lobyte(FT_CHN_NOTE) +FT_CH3_NOTE = FT_CH3_VARS+.lobyte(FT_CHN_NOTE) +FT_CH4_NOTE = FT_CH4_VARS+.lobyte(FT_CHN_NOTE) +FT_CH5_NOTE = FT_CH5_VARS+.lobyte(FT_CHN_NOTE) + +FT_CH1_INSTRUMENT = FT_CH1_VARS+.lobyte(FT_CHN_INSTRUMENT) +FT_CH2_INSTRUMENT = FT_CH2_VARS+.lobyte(FT_CHN_INSTRUMENT) +FT_CH3_INSTRUMENT = FT_CH3_VARS+.lobyte(FT_CHN_INSTRUMENT) +FT_CH4_INSTRUMENT = FT_CH4_VARS+.lobyte(FT_CHN_INSTRUMENT) +FT_CH5_INSTRUMENT = FT_CH5_VARS+.lobyte(FT_CHN_INSTRUMENT) + +FT_CH1_DUTY = FT_CH1_VARS+.lobyte(FT_CHN_DUTY) +FT_CH2_DUTY = FT_CH2_VARS+.lobyte(FT_CHN_DUTY) +FT_CH3_DUTY = FT_CH3_VARS+.lobyte(FT_CHN_DUTY) +FT_CH4_DUTY = FT_CH4_VARS+.lobyte(FT_CHN_DUTY) +FT_CH5_DUTY = FT_CH5_VARS+.lobyte(FT_CHN_DUTY) + +FT_CH1_VOLUME = FT_CH1_ENVS+.lobyte(FT_ENV_VALUE)+0 +FT_CH2_VOLUME = FT_CH2_ENVS+.lobyte(FT_ENV_VALUE)+0 +FT_CH3_VOLUME = FT_CH3_ENVS+.lobyte(FT_ENV_VALUE)+0 +FT_CH4_VOLUME = FT_CH4_ENVS+.lobyte(FT_ENV_VALUE)+0 + +FT_CH1_NOTE_OFF = FT_CH1_ENVS+.lobyte(FT_ENV_VALUE)+1 +FT_CH2_NOTE_OFF = FT_CH2_ENVS+.lobyte(FT_ENV_VALUE)+1 +FT_CH3_NOTE_OFF = FT_CH3_ENVS+.lobyte(FT_ENV_VALUE)+1 +FT_CH4_NOTE_OFF = FT_CH4_ENVS+.lobyte(FT_ENV_VALUE)+1 + +FT_CH1_PITCH_OFF = FT_CH1_ENVS+.lobyte(FT_ENV_VALUE)+2 +FT_CH2_PITCH_OFF = FT_CH2_ENVS+.lobyte(FT_ENV_VALUE)+2 +FT_CH3_PITCH_OFF = FT_CH3_ENVS+.lobyte(FT_ENV_VALUE)+2 + + +FT_VARS = FT_CHANNELS+FT_CHANNELS_ALL*FT_CHN_STRUCT_SIZE + +FT_PAL_ADJUST = FT_VARS+0 +FT_SONG_LIST_L = FT_VARS+1 +FT_SONG_LIST_H = FT_VARS+2 +FT_INSTRUMENT_L = FT_VARS+3 +FT_INSTRUMENT_H = FT_VARS+4 +FT_TEMPO_STEP_L = FT_VARS+5 +FT_TEMPO_STEP_H = FT_VARS+6 +FT_TEMPO_ACC_L = FT_VARS+7 +FT_TEMPO_ACC_H = FT_VARS+8 +FT_SONG_SPEED = FT_CH5_INSTRUMENT +FT_PULSE1_PREV = FT_CH3_DUTY +FT_PULSE2_PREV = FT_CH5_DUTY +FT_DPCM_LIST_L = FT_VARS+9 +FT_DPCM_LIST_H = FT_VARS+10 +FT_DPCM_EFFECT = FT_VARS+11 +FT_OUT_BUF = FT_VARS+12 ;11 bytes + + +;sound effect stream variables, 2 bytes and 15 bytes per stream +;when sound effects are disabled, this memory is not used + +FT_SFX_ADR_L = FT_VARS+23 +FT_SFX_ADR_H = FT_VARS+24 +FT_SFX_BASE_ADR = FT_VARS+25 + +FT_SFX_STRUCT_SIZE = 15 +FT_SFX_REPEAT = FT_SFX_BASE_ADR+0 +FT_SFX_PTR_L = FT_SFX_BASE_ADR+1 +FT_SFX_PTR_H = FT_SFX_BASE_ADR+2 +FT_SFX_OFF = FT_SFX_BASE_ADR+3 +FT_SFX_BUF = FT_SFX_BASE_ADR+4 ;11 bytes + + +;aliases for sound effect channels to use in user calls + +FT_SFX_CH0 = FT_SFX_STRUCT_SIZE*0 +FT_SFX_CH1 = FT_SFX_STRUCT_SIZE*1 +FT_SFX_CH2 = FT_SFX_STRUCT_SIZE*2 +FT_SFX_CH3 = FT_SFX_STRUCT_SIZE*3 + + +;aliases for the APU registers + +APU_PL1_VOL = $4000 +APU_PL1_SWEEP = $4001 +APU_PL1_LO = $4002 +APU_PL1_HI = $4003 +APU_PL2_VOL = $4004 +APU_PL2_SWEEP = $4005 +APU_PL2_LO = $4006 +APU_PL2_HI = $4007 +APU_TRI_LINEAR = $4008 +APU_TRI_LO = $400a +APU_TRI_HI = $400b +APU_NOISE_VOL = $400c +APU_NOISE_LO = $400e +APU_NOISE_HI = $400f +APU_DMC_FREQ = $4010 +APU_DMC_RAW = $4011 +APU_DMC_START = $4012 +APU_DMC_LEN = $4013 +APU_SND_CHN = $4015 + + +;aliases for the APU registers in the output buffer + + .if(!FT_SFX_ENABLE) ;if sound effects are disabled, write to the APU directly +FT_MR_PULSE1_V = APU_PL1_VOL +FT_MR_PULSE1_L = APU_PL1_LO +FT_MR_PULSE1_H = APU_PL1_HI +FT_MR_PULSE2_V = APU_PL2_VOL +FT_MR_PULSE2_L = APU_PL2_LO +FT_MR_PULSE2_H = APU_PL2_HI +FT_MR_TRI_V = APU_TRI_LINEAR +FT_MR_TRI_L = APU_TRI_LO +FT_MR_TRI_H = APU_TRI_HI +FT_MR_NOISE_V = APU_NOISE_VOL +FT_MR_NOISE_F = APU_NOISE_LO + .else ;otherwise write to the output buffer +FT_MR_PULSE1_V = FT_OUT_BUF +FT_MR_PULSE1_L = FT_OUT_BUF+1 +FT_MR_PULSE1_H = FT_OUT_BUF+2 +FT_MR_PULSE2_V = FT_OUT_BUF+3 +FT_MR_PULSE2_L = FT_OUT_BUF+4 +FT_MR_PULSE2_H = FT_OUT_BUF+5 +FT_MR_TRI_V = FT_OUT_BUF+6 +FT_MR_TRI_L = FT_OUT_BUF+7 +FT_MR_TRI_H = FT_OUT_BUF+8 +FT_MR_NOISE_V = FT_OUT_BUF+9 +FT_MR_NOISE_F = FT_OUT_BUF+10 + .endif + + + +;------------------------------------------------------------------------------ +; reset APU, initialize FamiTone +; in: A 0 for PAL, not 0 for NTSC +; X,Y pointer to music data +;------------------------------------------------------------------------------ + +FamiToneInit: + + stx FT_SONG_LIST_L ;store music data pointer for further use + sty FT_SONG_LIST_H + stx 0 + ldx #FT_SFX_CH0 + jsr _FT2SfxUpdate + .endif + .if FT_SFX_STREAMS>1 + ldx #FT_SFX_CH1 + jsr _FT2SfxUpdate + .endif + .if FT_SFX_STREAMS>2 + ldx #FT_SFX_CH2 + jsr _FT2SfxUpdate + .endif + .if FT_SFX_STREAMS>3 + ldx #FT_SFX_CH3 + jsr _FT2SfxUpdate + .endif + + + ;send data from the output buffer to the APU + + lda FT_OUT_BUF ;pulse 1 volume + sta APU_PL1_VOL + lda FT_OUT_BUF+1 ;pulse 1 period LSB + sta APU_PL1_LO + lda FT_OUT_BUF+2 ;pulse 1 period MSB, only applied when changed + cmp FT_PULSE1_PREV + beq @no_pulse1_upd + sta FT_PULSE1_PREV + sta APU_PL1_HI +@no_pulse1_upd: + + lda FT_OUT_BUF+3 ;pulse 2 volume + sta APU_PL2_VOL + lda FT_OUT_BUF+4 ;pulse 2 period LSB + sta APU_PL2_LO + lda FT_OUT_BUF+5 ;pulse 2 period MSB, only applied when changed + cmp FT_PULSE2_PREV + beq @no_pulse2_upd + sta FT_PULSE2_PREV + sta APU_PL2_HI +@no_pulse2_upd: + + lda FT_OUT_BUF+6 ;triangle volume (plays or not) + sta APU_TRI_LINEAR + lda FT_OUT_BUF+7 ;triangle period LSB + sta APU_TRI_LO + lda FT_OUT_BUF+8 ;triangle period MSB + sta APU_TRI_HI + + lda FT_OUT_BUF+9 ;noise volume + sta APU_NOISE_VOL + lda FT_OUT_BUF+10 ;noise period + sta APU_NOISE_LO + + .endif + + .if(FT_THREAD) + pla + sta FT_TEMP_PTR_H + pla + sta FT_TEMP_PTR_L + .endif + + rts + + +;internal routine, sets up envelopes of a channel according to current instrument +;in X envelope group offset, A instrument number + +_FT2SetInstrument: + asl a ;instrument number is pre multiplied by 4 + tay + lda FT_INSTRUMENT_H + adc #0 ;use carry to extend range for 64 instruments + sta > 4; +; + ldx #$00 + lsr a + lsr a + lsr a + lsr a + sta _offset + stx _offset+1 +; +; token &= 0xf; +; token += 4; // Minmatch +; + lda _token + and #$0F + clc + adc #4 + sta _token +; +; if (offset == 15) { +; + lda _offset + cmp #$0F +L0013: bne L001A +; +; tmp = *in++; +; + ldy #0 + lda (_in),y + sta _tmp + + inc _in + bne L0017 + inc _in+1 +L0017: +; +; offset += tmp; +; + clc + adc _offset + sta _offset + lda #$00 + adc _offset+1 + sta _offset+1 +; +; if (tmp == 255) +; + lda _tmp + cmp #$FF +; +; goto moreliterals; +; + jmp L0013 +; +; if (offset) { +; +L001A: lda _offset + ora _offset+1 + beq L001C +; +; memcpy(&out[written], in, offset); +; + lda _out + clc + adc _written + sta ptr2 + lda _out+1 + adc _written+1 + tax + stx ptr2+1 + lda _in + ldx _in+1 + sta ptr1 + stx ptr1+1 + jsr memcpy_r2v +; +; written += offset; +; + lda _offset + clc + adc _written + sta _written + lda _offset+1 + adc _written+1 + sta _written+1 +; +; in += offset; +; + lda _offset + clc + adc _in + sta _in + lda _offset+1 + adc _in+1 + sta _in+1 +; +; if (written >= outlen) +; +L001C: lda _written + cmp _outlen + lda _written+1 + sbc _outlen+1 +; +; return; +; + bcc L0047 + rts +; +; memcpy(&offset, in, 2); +; +L0047: ldy #0 + lda (_in),y + sta _offset + iny + lda (_in),y + sta _offset+1 +; +; in += 2; +; + lda #$02 + clc + adc _in + sta _in + bcc L002F + inc _in+1 +; +; copysrc = out + written - offset; +; +L002F: lda _out + clc + adc _written + pha + lda _out+1 + adc _written+1 + tax + pla + sec + sbc _offset + sta ptr1 + txa + sbc _offset+1 + sta ptr1+1 +; +; offset = token; +; + lda #$00 + sta _offset+1 + lda _token + sta _offset +; +; if (token == 19) { +; + cmp #$13 +L0045: bne L003C +; +; tmp = *in++; +; + ldy #0 + lda (_in),y + sta _tmp + + inc _in + bne L0039 + inc _in+1 +L0039: +; +; offset += tmp; +; + clc + adc _offset + sta _offset + tya + adc _offset+1 + sta _offset+1 +; +; if (tmp == 255) +; + lda _tmp + cmp #$FF +; +; goto morematches; +; + jmp L0045 +; +; memcpy(&out[written], copysrc, offset); +; +L003C: lda _out + clc + adc _written + sta ptr2 + lda _out+1 + adc _written+1 + tax + stx ptr2+1 + jsr memcpy_v2v +; +; written += offset; +; + lda _offset + clc + adc _written + sta _written + lda _offset+1 + adc _written+1 +L0046: sta _written+1 +; +; while (written < outlen) { +; + lda _written + cmp _outlen + lda _written+1 + sbc _outlen+1 + jcc L0004 + + rts + +.endproc + diff --git a/LibMultiSpacc/neslib/memfill.s b/LibMultiSpacc/neslib/memfill.s new file mode 100644 index 0000000..231ac4f --- /dev/null +++ b/LibMultiSpacc/neslib/memfill.s @@ -0,0 +1,49 @@ + +.importzp TEMP +.import popa, popax +.include "zpvars.inc" +.export _memfill + +;void __fastcall__ memfill(void *dst,unsigned char value,unsigned int len); + +_memfill: + + sta