Add NES building and adapt example functions

This commit is contained in:
OctoSpacc 2023-11-01 13:58:22 +01:00
parent 5c404b5faf
commit 9be90d7629
33 changed files with 3951 additions and 35 deletions

2
.gitignore vendored
View File

@ -1,5 +1,7 @@
*.o
*.lib
*.exe
*.run
*.tmp
.tmp/
Build/

View File

@ -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

View File

@ -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: <https://github.com/nesdoug/01_Hello/blob/master/Alpha.chr>
// 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;
}

View File

@ -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

View File

@ -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

View File

@ -1,9 +1,14 @@
#pragma once
#ifndef _MultiSpacc_h_
#define _MultiSpacc_h_
#include <stdarg.h>
#include <stdbool.h>
#ifndef MultiSpacc_Target_NES
#include <stdbool.h>
#endif
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#ifdef MultiSpacc_Target_SDL12
#include <SDL/SDL.h>
@ -54,16 +59,22 @@
#ifdef MultiSpacc_Target_NES
#include <nes.h>
#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_

View File

@ -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, ... )

View File

@ -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 )

5
LibMultiSpacc/neslib/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
*.o
*.lst
*~
crt0.lst
neslib2.lib

View File

@ -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.

View File

@ -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

View File

@ -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__;
}

View File

@ -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.

208
LibMultiSpacc/neslib/crt0.s Normal file
View File

@ -0,0 +1,208 @@
; Startup code for cc65 and Shiru's NES library
; based on code by Groepaz/Hitmen <groepaz@gmx.net>, Ullrich von Bassewitz <uz@cc65.org>
; 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 <NES_PRG_BANKS
.byte <NES_CHR_BANKS
.byte <NES_MIRRORING|(<NES_MAPPER<<4)
.byte <NES_MAPPER&$f0
.res 8,0
.segment "STARTUP"
start:
_exit:
sei
ldx #$ff
txs
inx
stx PPU_MASK
stx DMC_FREQ
stx PPU_CTRL ;no NMI
initPPU:
bit PPU_STATUS
@1:
bit PPU_STATUS
bpl @1
@2:
bit PPU_STATUS
bpl @2
; no APU frame counter IRQs
lda #$40
sta PPU_FRAMECNT
clearPalette:
lda #$3f
sta PPU_ADDR
stx PPU_ADDR
lda #$0f
ldx #$20
@1:
sta PPU_DATA
dex
bne @1
clearVRAM:
txa
ldy #$20
sty PPU_ADDR
sta PPU_ADDR
ldy #$10
@1:
sta PPU_DATA
inx
bne @1
dey
bne @1
clearRAM:
txa
@1:
sta $000,x
sta $100,x
sta $200,x
sta $300,x
sta $400,x
sta $500,x
sta $600,x
sta $700,x
inx
bne @1
lda #4
jsr _pal_bright
jsr _pal_clear
jsr _oam_clear
jsr zerobss
jsr copydata
lda #<(__RAM_START__+__RAM_SIZE__)
sta sp
lda #>(__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+1
lda #>HandyRTS
sta NMICallback+2
lda #%10000000
sta <PPU_CTRL_VAR
sta PPU_CTRL ;enable NMI
lda #%00000110
sta <PPU_MASK_VAR
waitSync3:
lda <FRAME_CNT1
@1:
cmp <FRAME_CNT1
beq @1
detectNTSC:
ldx #52 ;blargg's code
ldy #24
@1:
dex
bne @1
dey
bne @1
lda PPU_STATUS
and #$80
sta <NTSC_MODE
jsr _ppu_off
lda #0
sta PPU_SCROLL
sta PPU_SCROLL
sta PPU_OAM_ADDR
jmp _main ;no parameters
.include "neslib.sinc"
.segment "CHARS"
;;
.segment "SAMPLES"
;;
.segment "VECTORS"
.word nmi ;$fffa vblank nmi
.word start ;$fffc reset
.word irq ;$fffe irq / brk

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,349 @@
;
; Lauri Kasanen, 6 Jun 2017
; (C) Mega Cat Studios
; An optimized LZ4 decompressor
;
.importzp sp, sreg, regsave, regbank
.importzp tmp1, tmp2, tmp3, tmp4, ptr1, ptr2, ptr3, ptr4
.macpack longbranch
.import pushax,popax,_vram_write
.export _vram_unlz4
_out = regsave
_written = regsave + 2
_tmp = tmp1
_token = tmp2
_offset = ptr3
_in = sreg
_outlen = ptr4
PPU_ADDR = $2006
PPU_DATA = $2007
; ---------------------------------------------------------------
; void decompress_lz4 (const u8 *in, u8 * const out, const u16 outlen)
; ---------------------------------------------------------------
.segment "CODE"
; size in ptr3, src in ptr1, dest in ptr2
; don't touch size
memcpy_r2v:
lda ptr2+1
sta PPU_ADDR
lda ptr2
sta PPU_ADDR
lda ptr1
ldx ptr1+1
jsr pushax
lda ptr3
ldx ptr3+1
jmp _vram_write
memcpy_v2v:
lda #0
sta tmp3
sta tmp4
jmp @check
@loop:
; read source byte
lda ptr1+1
sta PPU_ADDR
lda ptr1
sta PPU_ADDR
ldx PPU_DATA
ldx PPU_DATA
inc ptr1
bne @nosrcinc
inc ptr1+1
@nosrcinc:
; write dst byte
lda ptr2+1
sta PPU_ADDR
lda ptr2
sta PPU_ADDR
stx PPU_DATA
inc ptr2
bne @nodstinc
inc ptr2+1
@nodstinc:
; increase counter
inc tmp3
bne @noinc
inc tmp4
@noinc:
@check:
lda tmp3
cmp ptr3
lda tmp4
sbc ptr3+1
bcc @loop
rts
.proc _vram_unlz4: near
sta _outlen
stx _outlen+1
jsr popax
sta _out
stx _out+1
jsr popax
sta _in
stx _in+1
;
; written = 0;
;
lda #$00
sta _written
;
; while (written < outlen) {
;
jmp L0046
;
; token = *in++;
;
L0004: ldy #0
lda (_in),y
sta _token
inc _in
bne L000A
inc _in+1
L000A:
;
; offset = token >> 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

View File

@ -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 <LEN
stx <LEN+1
jsr popa
sta <TEMP
jsr popax
sta <DST
stx <DST+1
ldx #0
@1:
lda <LEN+1
beq @2
jsr @3
dec <LEN+1
inc <DST+1
jmp @1
@2:
ldx <LEN
beq @5
@3:
ldy #0
lda <TEMP
@4:
sta (DST),y
iny
dex
bne @4
@5:
rts

View File

@ -0,0 +1,16 @@
PPU_CTRL =$2000
PPU_MASK =$2001
PPU_STATUS =$2002
PPU_OAM_ADDR =$2003
PPU_OAM_DATA =$2004
PPU_SCROLL =$2005
PPU_ADDR =$2006
PPU_DATA =$2007
PPU_OAM_DMA =$4014
PPU_FRAMECNT =$4017
DMC_FREQ =$4010
CTRL_PORT1 =$4016
CTRL_PORT2 =$4017
OAM_BUF =$0200
PAL_BUF =$01c0

View File

@ -0,0 +1,339 @@
#ifndef _NESLIB_H
#define _NESLIB_H
/*
(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.
*/
// NES hardware-dependent functions by Shiru (shiru@mail.ru)
// Feel free to do anything you want with this code, consider it Public Domain
// Versions history:
// 280215 - fixed palette glitch caused with the active DMC DMA glitch
// 030914 - minor fixes in the vram update system
// 310814 - added vram_flush_update
// 120414 - removed adr argument from vram_write and vram_read,
// unrle_vram renamed to vram_unrle, with adr argument removed
// 060414 - many fixes and improvements, including sequental VRAM updates
// previous versions were created since mid-2011, there were many updates
// xxxx19 - updated by sehugg@8bitworkshop
// define basic types for convenience
typedef unsigned char byte; // 8-bit unsigned
typedef signed char sbyte; // 8-bit signed
typedef unsigned short word; // 16-bit signed
typedef enum { false, true } bool; // boolean
// set bg and spr palettes, data is 32 bytes array
void __fastcall__ pal_all(const char *data);
// set bg palette only, data is 16 bytes array
void __fastcall__ pal_bg(const char *data);
// set spr palette only, data is 16 bytes array
void __fastcall__ pal_spr(const char *data);
// set a palette entry, index is 0..31
void __fastcall__ pal_col(unsigned char index, unsigned char color);
// reset palette to $0f
void __fastcall__ pal_clear(void);
// set virtual bright both for sprites and background, 0 is black, 4 is normal, 8 is white
void __fastcall__ pal_bright(unsigned char bright);
// set virtual bright for sprites only
void __fastcall__ pal_spr_bright(unsigned char bright);
// set virtual bright for sprites background only
void __fastcall__ pal_bg_bright(unsigned char bright);
// wait actual TV frame, 50hz for PAL, 60hz for NTSC
void __fastcall__ ppu_wait_nmi(void);
// wait virtual frame, it is always 50hz, frame-to-frame in PAL, frameskip in NTSC
void __fastcall__ ppu_wait_frame(void);
// turn off rendering, nmi still enabled when rendering is disabled
void __fastcall__ ppu_off(void);
// turn on bg, spr
void __fastcall__ ppu_on_all(void);
// turn on bg only
void __fastcall__ ppu_on_bg(void);
// turn on spr only
void __fastcall__ ppu_on_spr(void);
// set PPU_MASK directly
void __fastcall__ ppu_mask(unsigned char mask);
// get current video system, 0 for PAL, not 0 for NTSC
unsigned char __fastcall__ ppu_system(void);
// Return an 8-bit counter incremented at each vblank
unsigned char __fastcall__ nesclock(void);
// get/set the internal ppu ctrl cache var for manual writing
unsigned char __fastcall__ get_ppu_ctrl_var(void);
void __fastcall__ set_ppu_ctrl_var(unsigned char var);
// clear OAM buffer, all the sprites are hidden
void __fastcall__ oam_clear(void);
// set sprite display mode, 0 for 8x8 sprites, 1 for 8x16 sprites
void __fastcall__ oam_size(unsigned char size);
// set sprite in OAM buffer, chrnum is tile, attr is attribute, sprid is offset in OAM in bytes
// returns sprid+4, which is offset for a next sprite
unsigned char __fastcall__ oam_spr(unsigned char x, unsigned char y,
unsigned char chrnum, unsigned char attr,
unsigned char sprid);
// set metasprite in OAM buffer
// meta sprite is a const unsigned char array, it contains four bytes per sprite
// in order x offset, y offset, tile, attribute
// x=128 is end of a meta sprite
// returns sprid+4, which is offset for a next sprite
unsigned char __fastcall__ oam_meta_spr(unsigned char x, unsigned char y,
unsigned char sprid, const unsigned char *data);
// hide all remaining sprites from given offset
void __fastcall__ oam_hide_rest(unsigned char sprid);
// initialize the FamiTone system
void __fastcall__ famitone_init(void* music_data);
// initialize the FamiTone SFX system
void __fastcall__ sfx_init(void* sounds_data);
// play a music in FamiTone format
void __fastcall__ music_play(unsigned char song);
// stop music
void __fastcall__ music_stop(void);
// pause and unpause music
void __fastcall__ music_pause(unsigned char pause);
// play FamiTone sound effect on channel 0..3
void __fastcall__ sfx_play(unsigned char sound, unsigned char channel);
// play a DPCM sample, 1..63
void __fastcall__ sample_play(unsigned char sample);
// call from NMI once per frame
void __fastcall__ famitone_update(void);
// poll controller and return flags like PAD_LEFT etc, input is pad number (0 or 1)
unsigned char __fastcall__ pad_poll(unsigned char pad);
// poll controller in trigger mode, a flag is set only on button down, not hold
// if you need to poll the pad in both normal and trigger mode, poll it in the
// trigger mode for first, then use pad_state
unsigned char __fastcall__ pad_trigger(unsigned char pad);
// get previous pad state without polling ports
unsigned char __fastcall__ pad_state(unsigned char pad);
// set scroll, including the top bits
// it is always applied at beginning of a TV frame, not at the function call
void __fastcall__ scroll(unsigned int x, unsigned int y);
// set scroll after screen split invoked by the sprite 0 hit
// warning: all CPU time between the function call and the actual split point will be wasted!
// warning: the program loop has to fit into the frame time, ppu_wait_frame should not be used
// otherwise empty frames without split will be inserted, resulting in jumpy screen
// warning: only X scroll could be changed in this version
void __fastcall__ split(unsigned int x, unsigned int y);
// set scroll after screen split invoked by the sprite 0 hit
// sets both X and Y, but timing might be iffy depending
// on exact sprite 0 position
void __fastcall__ splitxy(unsigned int x, unsigned int y);
// select current chr bank for sprites, 0..1
void __fastcall__ bank_spr(unsigned char n);
// select current chr bank for background, 0..1
void __fastcall__ bank_bg(unsigned char n);
// get random number 0..255 or 0..65535
unsigned char __fastcall__ rand8(void);
unsigned int __fastcall__ rand16(void);
// set random seed
void __fastcall__ set_rand(unsigned int seed);
// when display is enabled, vram access could only be done with this vram update system
// the function sets a pointer to the update buffer that contains data and addresses
// in a special format. It allows to write non-sequental bytes, as well as horizontal or
// vertical nametable sequences.
// buffer pointer could be changed during rendering, but it only takes effect on a new frame
// number of transferred bytes is limited by vblank time
// to disable updates, call this function with NULL pointer
// the update data format:
// MSB, LSB, byte for a non-sequental write
// MSB|NT_UPD_HORZ, LSB, LEN, [bytes] for a horizontal sequence
// MSB|NT_UPD_VERT, LSB, LEN, [bytes] for a vertical sequence
// NT_UPD_EOF to mark end of the buffer
// length of this data should be under 256 bytes
void __fastcall__ set_vram_update(unsigned char *buf);
// all following vram functions only work when display is disabled
// do a series of VRAM writes, the same format as for set_vram_update, but writes done right away
void __fastcall__ flush_vram_update(unsigned char *buf);
// set vram pointer to write operations if you need to write some data to vram
void __fastcall__ vram_adr(unsigned int adr);
// put a byte at current vram address, works only when rendering is turned off
void __fastcall__ vram_put(unsigned char n);
// fill a block with a byte at current vram address, works only when rendering is turned off
void __fastcall__ vram_fill(unsigned char n, unsigned int len);
// set vram autoincrement, 0 for +1 and not 0 for +32
void __fastcall__ vram_inc(unsigned char n);
// read a block from current address of vram, works only when rendering is turned off
void __fastcall__ vram_read(unsigned char *dst, unsigned int size);
// write a block to current address of vram, works only when rendering is turned off
void __fastcall__ vram_write(const unsigned char *src, unsigned int size);
// unpack RLE data to current address of vram, mostly used for nametables
void __fastcall__ vram_unrle(const unsigned char *data);
// unpack LZ4 data to this address
void __fastcall__ vram_unlz4(const unsigned char *in, unsigned char *out,
const unsigned uncompressed_size);
/*
Rough speeds for a full 1024 nametable:
- rle takes 0.5 frames
- uncompressed takes 1.3 frames
- lz4 takes 2.8 frames
*/
// like memset, but does not return anything
void __fastcall__ memfill(void *dst, unsigned char value, unsigned int len);
// delay for N frames
void __fastcall__ delay(unsigned char frames);
// display.sinc functions
void __fastcall__ oam_clear_fast(void);
void __fastcall__ oam_meta_spr_pal(unsigned char x,unsigned char y,unsigned char pal,const unsigned char *metasprite);
void __fastcall__ oam_meta_spr_clip(signed int x,unsigned char y,const unsigned char *metasprite);
// set NMI/IRQ callback
void __fastcall__ nmi_set_callback(void (*callback)(void));
#define PAD_A 0x01
#define PAD_B 0x02
#define PAD_SELECT 0x04
#define PAD_START 0x08
#define PAD_UP 0x10
#define PAD_DOWN 0x20
#define PAD_LEFT 0x40
#define PAD_RIGHT 0x80
#define OAM_FLIP_V 0x80
#define OAM_FLIP_H 0x40
#define OAM_BEHIND 0x20
#define MAX(x1,x2) ((x1)<(x2)?(x2):(x1))
#define MIN(x1,x2) ((x1)<(x2)?(x1):(x2))
#define MASK_SPR 0x10
#define MASK_BG 0x08
#define MASK_EDGE_SPR 0x04
#define MASK_EDGE_BG 0x02
#define MASK_TINT_RED 0x20
#define MASK_TINT_BLUE 0x40
#define MASK_TINT_GREEN 0x80
#define MASK_MONO 0x01
#define NAMETABLE_A 0x2000
#define NAMETABLE_B 0x2400
#define NAMETABLE_C 0x2800
#define NAMETABLE_D 0x2c00
#ifndef NULL
#define NULL 0
#endif
#define TRUE 1
#define FALSE 0
#define NT_UPD_HORZ 0x40
#define NT_UPD_VERT 0x80
#define NT_UPD_EOF 0xff
// macro to calculate nametable address from X,Y in compile time
#define NTADR_A(x,y) (NAMETABLE_A|(((y)<<5)|(x)))
#define NTADR_B(x,y) (NAMETABLE_B|(((y)<<5)|(x)))
#define NTADR_C(x,y) (NAMETABLE_C|(((y)<<5)|(x)))
#define NTADR_D(x,y) (NAMETABLE_D|(((y)<<5)|(x)))
// macro to get MSB and LSB
#define MSB(x) (((x)>>8))
#define LSB(x) (((x)&0xff))
// OAM buffer @ $200-$2FF
typedef struct OAMSprite {
byte y; // Y coordinate
byte name; // tile index in name table
byte attr; // attribute flags
byte x; // X coordinate
} OAMSprite;
#define OAMBUF ((OAMSprite*) 0x200)
// OAM offset for spr_pal and spr_clip
extern byte oam_off;
#pragma zpsym ("oam_off")
#endif /* neslib.h */

View File

@ -0,0 +1,791 @@
;NES hardware-dependent functions by Shiru (shiru@mail.ru)
;with improvements by VEG
;Feel free to do anything you want with this code, consider it Public Domain
.export _pal_all,_pal_bg,_pal_spr,_pal_col,_pal_clear
.export _pal_bright,_pal_spr_bright,_pal_bg_bright
.export _ppu_off,_ppu_on_all,_ppu_on_bg,_ppu_on_spr,_ppu_mask,_ppu_system
.export _oam_clear,_oam_size,_oam_hide_rest
.export _ppu_wait_frame,_ppu_wait_nmi
.export _scroll
.export _bank_spr,_bank_bg
.export _vram_write
.export _vram_adr,_vram_put,_vram_fill,_vram_inc
.export _set_vram_update,_flush_vram_update
.export _delay
.export _get_ppu_ctrl_var,_set_ppu_ctrl_var
.export _nesclock
.export _nmi_set_callback
;NMI handler
nmi:
pha
txa
pha
tya
pha
lda <PPU_MASK_VAR ;if rendering is disabled, do not access the VRAM at all
and #%00011000
bne @doUpdate
jmp @skipAll
@doUpdate:
lda #>OAM_BUF ;update OAM
sta PPU_OAM_DMA
lda <PAL_UPDATE ;update palette if needed
bne @updPal
jmp @updVRAM
@updPal:
ldx #0
stx <PAL_UPDATE
lda #$3f
sta PPU_ADDR
stx PPU_ADDR
ldy PAL_BUF ;background color, remember it in X
lda (PAL_BG_PTR),y
sta PPU_DATA
tax
.repeat 3,I
ldy PAL_BUF+1+I
lda (PAL_BG_PTR),y
sta PPU_DATA
.endrepeat
.repeat 3,J
stx PPU_DATA ;background color
.repeat 3,I
ldy PAL_BUF+5+(J*4)+I
lda (PAL_BG_PTR),y
sta PPU_DATA
.endrepeat
.endrepeat
.repeat 4,J
stx PPU_DATA ;background color
.repeat 3,I
ldy PAL_BUF+17+(J*4)+I
lda (PAL_SPR_PTR),y
sta PPU_DATA
.endrepeat
.endrepeat
@updVRAM:
lda <VRAM_UPDATE
beq @skipUpd
lda #0
sta <VRAM_UPDATE
lda <NAME_UPD_ENABLE
beq @skipUpd
jsr _flush_vram_update_nmi
@skipUpd:
lda #0
sta PPU_ADDR
sta PPU_ADDR
lda <SCROLL_X
sta PPU_SCROLL
lda <SCROLL_Y
sta PPU_SCROLL
lda <PPU_CTRL_VAR
sta PPU_CTRL
@skipAll:
lda <PPU_MASK_VAR
sta PPU_MASK
inc <FRAME_CNT1
inc <FRAME_CNT2
lda <FRAME_CNT2
cmp #6
bne skipNtsc
lda #0
sta <FRAME_CNT2
skipNtsc:
jsr NMICallback
pla
tay
pla
tax
pla
rti
; IRQ: jumps to NMICallback, passing -1 as argument
irq:
pha
txa
pha
tya
pha
lda #$ff
jmp skipNtsc
;void __fastcall__ nmi_set_callback(void (*callback)());
_nmi_set_callback:
sta NMICallback+1
stx NMICallback+2
HandyRTS:
rts
;void __fastcall__ pal_all(const char *data);
_pal_all:
sta <PTR
stx <PTR+1
ldx #$00
lda #$20
pal_copy:
sta <LEN
ldy #$00
@0:
lda (PTR),y
sta PAL_BUF,x
inx
iny
dec <LEN
bne @0
inc <PAL_UPDATE
rts
;void __fastcall__ pal_bg(const char *data);
_pal_bg:
sta <PTR
stx <PTR+1
ldx #$00
lda #$10
bne pal_copy ;bra
;void __fastcall__ pal_spr(const char *data);
_pal_spr:
sta <PTR
stx <PTR+1
ldx #$10
txa
bne pal_copy ;bra
;void __fastcall__ pal_col(unsigned char index,unsigned char color);
_pal_col:
sta <PTR
jsr popa
and #$1f
tax
lda <PTR
sta PAL_BUF,x
inc <PAL_UPDATE
rts
;void __fastcall__ pal_clear(void);
_pal_clear:
lda #$0f
ldx #0
@1:
sta PAL_BUF,x
inx
cpx #$20
bne @1
stx <PAL_UPDATE
rts
;void __fastcall__ pal_spr_bright(unsigned char bright);
_pal_spr_bright:
tax
lda palBrightTableL,x
sta <PAL_SPR_PTR
lda palBrightTableH,x ;MSB is never zero
sta <PAL_SPR_PTR+1
sta <PAL_UPDATE
rts
;void __fastcall__ pal_bg_bright(unsigned char bright);
_pal_bg_bright:
tax
lda palBrightTableL,x
sta <PAL_BG_PTR
lda palBrightTableH,x ;MSB is never zero
sta <PAL_BG_PTR+1
sta <PAL_UPDATE
rts
;void __fastcall__ pal_bright(unsigned char bright);
_pal_bright:
jsr _pal_spr_bright
txa
jmp _pal_bg_bright
;void __fastcall__ ppu_off(void);
_ppu_off:
lda <PPU_MASK_VAR
and #%11100111
sta <PPU_MASK_VAR
jmp _ppu_wait_nmi
;void __fastcall__ ppu_on_all(void);
_ppu_on_all:
lda <PPU_MASK_VAR
ora #%00011000
ppu_onoff:
sta <PPU_MASK_VAR
jmp _ppu_wait_nmi
;void __fastcall__ ppu_on_bg(void);
_ppu_on_bg:
lda <PPU_MASK_VAR
ora #%00001000
bne ppu_onoff ;bra
;void __fastcall__ ppu_on_spr(void);
_ppu_on_spr:
lda <PPU_MASK_VAR
ora #%00010000
bne ppu_onoff ;bra
;void __fastcall__ ppu_mask(unsigned char mask);
_ppu_mask:
sta <PPU_MASK_VAR
rts
;unsigned char __fastcall__ ppu_system(void);
_ppu_system:
lda <NTSC_MODE
ldx #$00
rts
;unsigned char __fastcall__ get_ppu_ctrl_var(void);
_get_ppu_ctrl_var:
lda <PPU_CTRL_VAR
ldx #$00
rts
;void __fastcall__ set_ppu_ctrl_var(unsigned char var);
_set_ppu_ctrl_var:
sta <PPU_CTRL_VAR
rts
;void __fastcall__ oam_clear(void);
_oam_clear:
ldx #0
lda #$ff
@1:
sta OAM_BUF,x
inx
inx
inx
inx
bne @1
rts
;void __fastcall__ oam_size(unsigned char size);
_oam_size:
asl a
asl a
asl a
asl a
asl a
and #$20
sta <TEMP
lda <PPU_CTRL_VAR
and #$df
ora <TEMP
sta <PPU_CTRL_VAR
rts
;void __fastcall__ oam_hide_rest(unsigned char sprid);
_oam_hide_rest:
tax
lda #240
@1:
sta OAM_BUF,x
inx
inx
inx
inx
bne @1
rts
;void __fastcall__ ppu_wait_frame(void);
_ppu_wait_frame:
lda #1
sta <VRAM_UPDATE
lda <FRAME_CNT1
@1:
cmp <FRAME_CNT1
beq @1
lda <NTSC_MODE
beq @3
@2:
lda <FRAME_CNT2
cmp #5
beq @2
@3:
rts
;void __fastcall__ ppu_wait_nmi(void);
_ppu_wait_nmi:
lda #1
sta <VRAM_UPDATE
lda <FRAME_CNT1
@1:
cmp <FRAME_CNT1
beq @1
rts
;void __fastcall__ scroll(unsigned int x,unsigned int y);
_scroll:
sta <TEMP
txa
bne @1
lda <TEMP
cmp #240
bcs @1
sta <SCROLL_Y
lda #0
sta <TEMP
beq @2 ;bra
@1:
sec
lda <TEMP
sbc #240
sta <SCROLL_Y
lda #2
sta <TEMP
@2:
jsr popax
sta <SCROLL_X
txa
and #$01
ora <TEMP
sta <TEMP
lda <PPU_CTRL_VAR
and #$fc
ora <TEMP
sta <PPU_CTRL_VAR
rts
;void __fastcall__ bank_spr(unsigned char n);
_bank_spr:
and #$01
asl a
asl a
asl a
sta <TEMP
lda <PPU_CTRL_VAR
and #%11110111
ora <TEMP
sta <PPU_CTRL_VAR
rts
;void __fastcall__ bank_bg(unsigned char n);
_bank_bg:
and #$01
asl a
asl a
asl a
asl a
sta <TEMP
lda <PPU_CTRL_VAR
and #%11101111
ora <TEMP
sta <PPU_CTRL_VAR
rts
;void __fastcall__ vram_write(unsigned char *src,unsigned int size);
_vram_write:
sta <TEMP
stx <TEMP+1
jsr popax
sta <TEMP+2
stx <TEMP+3
ldy #0
@1:
lda (TEMP+2),y
sta PPU_DATA
inc <TEMP+2
bne @2
inc <TEMP+3
@2:
lda <TEMP
bne @3
dec <TEMP+1
@3:
dec <TEMP
lda <TEMP
ora <TEMP+1
bne @1
rts
;void __fastcall__ set_vram_update(unsigned char *buf);
_set_vram_update:
sta <NAME_UPD_ADR+0
stx <NAME_UPD_ADR+1
ora <NAME_UPD_ADR+1
sta <NAME_UPD_ENABLE
rts
;void __fastcall__ flush_vram_update(unsigned char *buf);
_flush_vram_update:
sta <NAME_UPD_ADR+0
stx <NAME_UPD_ADR+1
_flush_vram_update_nmi:
ldy #0
@updName:
lda (NAME_UPD_ADR),y
iny
cmp #$40 ;is it a non-sequental write?
bcs @updNotSeq
sta PPU_ADDR
lda (NAME_UPD_ADR),y
iny
sta PPU_ADDR
lda (NAME_UPD_ADR),y
iny
sta PPU_DATA
jmp @updName
@updNotSeq:
tax
lda <PPU_CTRL_VAR
cpx #$80 ;is it a horizontal or vertical sequence?
bcc @updHorzSeq
cpx #$ff ;is it end of the update?
beq @updDone
@updVertSeq:
ora #$04
bne @updNameSeq ;bra
@updHorzSeq:
and #$fb
@updNameSeq:
sta PPU_CTRL
txa
and #$3f
sta PPU_ADDR
lda (NAME_UPD_ADR),y
iny
sta PPU_ADDR
lda (NAME_UPD_ADR),y
iny
tax
@updNameLoop:
lda (NAME_UPD_ADR),y
iny
sta PPU_DATA
dex
bne @updNameLoop
lda <PPU_CTRL_VAR
sta PPU_CTRL
jmp @updName
@updDone:
rts
;void __fastcall__ vram_adr(unsigned int adr);
_vram_adr:
stx PPU_ADDR
sta PPU_ADDR
rts
;void __fastcall__ vram_put(unsigned char n);
_vram_put:
sta PPU_DATA
rts
;void __fastcall__ vram_fill(unsigned char n,unsigned int len);
_vram_fill:
sta <LEN
stx <LEN+1
jsr popa
ldx <LEN+1
beq @2
ldx #0
@1:
sta PPU_DATA
dex
bne @1
dec <LEN+1
bne @1
@2:
ldx <LEN
beq @4
@3:
sta PPU_DATA
dex
bne @3
@4:
rts
;void __fastcall__ vram_inc(unsigned char n);
_vram_inc:
ora #0
beq @1
lda #$04
@1:
sta <TEMP
lda <PPU_CTRL_VAR
and #$fb
ora <TEMP
sta <PPU_CTRL_VAR
sta PPU_CTRL
rts
;unsigned char __fastcall__ nesclock(void);
_nesclock:
lda <FRAME_CNT1
ldx #$00
rts
;void __fastcall__ delay(unsigned char frames);
_delay:
tax
@1:
jsr _ppu_wait_nmi
dex
bne @1
rts
palBrightTableL:
.byte <palBrightTable0,<palBrightTable1,<palBrightTable2
.byte <palBrightTable3,<palBrightTable4,<palBrightTable5
.byte <palBrightTable6,<palBrightTable7,<palBrightTable8
palBrightTableH:
.byte >palBrightTable0,>palBrightTable1,>palBrightTable2
.byte >palBrightTable3,>palBrightTable4,>palBrightTable5
.byte >palBrightTable6,>palBrightTable7,>palBrightTable8
palBrightTable0:
.byte $0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f ;black
palBrightTable1:
.byte $0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f
palBrightTable2:
.byte $0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f
palBrightTable3:
.byte $0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f
palBrightTable4:
.byte $00,$01,$02,$03,$04,$05,$06,$07,$08,$09,$0a,$0b,$0c,$0f,$0f,$0f ;normal colors
palBrightTable5:
.byte $10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$1a,$1b,$1c,$00,$00,$00
palBrightTable6:
.byte $10,$21,$22,$23,$24,$25,$26,$27,$28,$29,$2a,$2b,$2c,$10,$10,$10 ;$10 because $20 is the same as $30
palBrightTable7:
.byte $30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$3a,$3b,$3c,$20,$20,$20
palBrightTable8:
.byte $30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30 ;white
.byte $30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30
.byte $30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30
.byte $30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30

View File

@ -0,0 +1,20 @@
.include "nes.inc"
.segment "CODE"
;void __fastcall__ oam_clear_fast(void);
.export _oam_clear_fast ;make this function accessible to C code
_oam_clear_fast:
lda #240
.repeat 64,I
sta OAM_BUF+I*4
.endrepeat
rts

View File

@ -0,0 +1,64 @@
.importzp TEMP, _oam_off, sp
.import popa, popax
.include "nes.inc"
.include "zpvars.inc"
;unsigned char __fastcall__ oam_meta_spr(unsigned char x,unsigned char y,unsigned char sprid,const unsigned char *data);
.export _oam_meta_spr
_oam_meta_spr:
sta <PTR
stx <PTR+1
ldy #2 ;three popa calls replacement, performed in reversed order
lda (sp),y
dey
sta <SCRX
lda (sp),y
dey
sta <SCRY
lda (sp),y
tax
@1:
lda (PTR),y ;x offset
cmp #$80
beq @2
iny
clc
adc <SCRX
sta OAM_BUF+3,x
lda (PTR),y ;y offset
iny
clc
adc <SCRY
sta OAM_BUF+0,x
lda (PTR),y ;tile
iny
sta OAM_BUF+1,x
lda (PTR),y ;attribute
iny
sta OAM_BUF+2,x
inx
inx
inx
inx
jmp @1
@2:
lda <sp
adc #2 ;carry is always set here, so it adds 3
sta <sp
bcc @3
inc <sp+1
@3:
txa
ldx #$00
rts

View File

@ -0,0 +1,107 @@
.importzp TEMP, _oam_off
.import popa, popax
.include "nes.inc"
.include "zpvars.inc"
;void __fastcall__ oam_meta_spr_clip(signed int x,unsigned char y,const unsigned char *metasprite);
.segment "ZEROPAGE"
SPR_XOFF: .res 1
SPR_YOFF: .res 1
.segment "CODE"
.export _oam_meta_spr_clip
_oam_meta_spr_clip:
sta <PTR
stx <PTR+1
jsr popa ;y
sta <SCRY
jsr popax ;x
sta <SCRX
stx <DST ;this is X MSB, repurposing RAM variable name
ldx _oam_off
ldy #0
@1:
lda (PTR),y ;x offset
cmp #$80
beq @2
iny
sta <SPR_XOFF+0
clc
adc <SCRX
sta <SPR_XOFF+1
lda <SPR_XOFF+0
ora #$7f
bmi :+
lda #0
:
adc <DST
bne @skip
lda <SPR_XOFF+1
sta OAM_BUF+3,x
lda (PTR),y ;y offset
iny
clc
adc <SCRY
sta OAM_BUF+0,x
lda (PTR),y ;tile
iny
sta OAM_BUF+1,x
lda (PTR),y ;attribute
iny
sta OAM_BUF+2,x
inx
inx
inx
inx
jmp @1
@skip:
iny
iny
iny
jmp @1
@2:
stx _oam_off
rts
; static unsigned char sx,sy,tile,pal,off;
; static signed int sxx;
; off=0;
; while(1)
; {
; sx=metasprite[off++];
; if(sx==128) return;
; sxx =x+(signed char)sx;
; sy =metasprite[off++]+y;
; tile=metasprite[off++];
; pal =metasprite[off++];
; if(!(sxx&0xff00))
; {
; oam_spr(sxx,sy,tile,pal,oam_off);
; oam_off+=4;
; }
; }

View File

@ -0,0 +1,71 @@
.importzp TEMP, _oam_off, sp
.import popa, popax
.include "nes.inc"
.include "zpvars.inc"
;void __fastcall__ oam_meta_spr_pal(unsigned char x,unsigned char y,unsigned char pal,const unsigned char *metasprite);
.export _oam_meta_spr_pal
_oam_meta_spr_pal:
sta <PTR
stx <PTR+1
ldy #2 ;three popa calls replacement, performed in reversed order
lda (sp),y
dey
sta <SCRX
lda (sp),y
dey
sta <SCRY
lda (sp),y
sta <DST ;this is palette, just repurposing RAM variable name
ldx _oam_off
@1:
lda (PTR),y ;x offset
cmp #$80
beq @2
iny
clc
adc <SCRX
sta OAM_BUF+3,x
lda (PTR),y ;y offset
iny
clc
adc <SCRY
sta OAM_BUF+0,x
lda (PTR),y ;tile
iny
sta OAM_BUF+1,x
lda (PTR),y ;attribute
iny
and #$fc ;remove palette bits
ora <DST ;add new palette bits
sta OAM_BUF+2,x
inx
inx
inx
inx
jmp @1
@2:
stx _oam_off
lda <sp
adc #2 ;carry is always set here, so it adds 3
sta <sp
bcc @3
inc <sp+1
@3:
rts

View File

@ -0,0 +1,42 @@
.importzp TEMP, _oam_off, sp
.import popa, popax
.include "nes.inc"
.include "zpvars.inc"
;unsigned char __fastcall__ oam_spr(unsigned char x,unsigned char y,unsigned char chrnum,unsigned char attr,unsigned char sprid);
.export _oam_spr
_oam_spr:
tax
ldy #0 ;four popa calls replacement
lda (sp),y
iny
sta OAM_BUF+2,x
lda (sp),y
iny
sta OAM_BUF+1,x
lda (sp),y
iny
sta OAM_BUF+0,x
lda (sp),y
sta OAM_BUF+3,x
lda <sp
clc
adc #4
sta <sp
bcc @1
inc <sp+1
@1:
txa
clc
adc #4
ldx #$00
rts

View File

@ -0,0 +1,89 @@
.segment "ZEROPAGE"
PAD_STATE: .res 2 ;one byte per controller
PAD_STATEP: .res 2
PAD_STATET: .res 2
.importzp TEMP
PAD_BUF =TEMP+1
.include "nes.inc"
.segment "CODE"
.export _pad_poll, _pad_trigger, _pad_state
;unsigned char __fastcall__ pad_poll(unsigned char pad);
_pad_poll:
tay
ldx #0
@padPollPort:
lda #1
sta CTRL_PORT1
lda #0
sta CTRL_PORT1
lda #8
sta <TEMP
@padPollLoop:
lda CTRL_PORT1,y
lsr a
ror <PAD_BUF,x
dec <TEMP
bne @padPollLoop
inx
cpx #3
bne @padPollPort
lda <PAD_BUF
cmp <PAD_BUF+1
beq @done
cmp <PAD_BUF+2
beq @done
lda <PAD_BUF+1
@done:
sta <PAD_STATE,y
tax
eor <PAD_STATEP,y
and <PAD_STATE ,y
sta <PAD_STATET,y
txa
sta <PAD_STATEP,y
ldx #$00
rts
;unsigned char __fastcall__ pad_trigger(unsigned char pad);
_pad_trigger:
pha
jsr _pad_poll
pla
tax
lda <PAD_STATET,x
ldx #$00
rts
;unsigned char __fastcall__ pad_state(unsigned char pad);
_pad_state:
tax
lda <PAD_STATE,x
ldx #$00
rts

View File

@ -0,0 +1,71 @@
.segment "ZEROPAGE"
RAND_SEED: .res 2
.segment "CODE"
.export _rand8,_rand16,_set_rand
.exportzp RAND_SEED
;unsigned char __fastcall__ rand8(void);
;Galois random generator, found somewhere
;out: A random number 0..255
rand1:
lda <RAND_SEED
beq @z1
asl a
bcc @1
@z1: eor #$cf
@1:
sta <RAND_SEED
rts
rand2:
lda <RAND_SEED+1
beq @z2
asl a
bcc @1
@z2: eor #$d7
@1:
sta <RAND_SEED+1
rts
_rand8:
jsr rand1
jsr rand2
adc <RAND_SEED
ldx #$00
rts
;unsigned int __fastcall__ rand16(void);
_rand16:
jsr rand1
tax
jsr rand2
rts
;void __fastcall__ set_rand(unsigned char seed);
_set_rand:
ora #1
sta <RAND_SEED
stx <RAND_SEED+1
rts

View File

@ -0,0 +1,41 @@
.importzp TEMP, PPU_CTRL_VAR, PPU_CTRL_VAR1, SCROLL_X1
.import popax
.include "nes.inc"
.include "zpvars.inc"
.export _split
;;void __fastcall__ split(unsigned int x,unsigned int y);
_split:
jsr popax
sta <SCROLL_X1
txa
and #$01
sta <TEMP
lda <PPU_CTRL_VAR
and #$fc
ora <TEMP
sta <PPU_CTRL_VAR1
@3:
bit PPU_STATUS
bvs @3
@4:
bit PPU_STATUS
bvc @4
lda <SCROLL_X1
sta PPU_SCROLL
lda #0
sta PPU_SCROLL
lda <PPU_CTRL_VAR1
sta PPU_CTRL
rts

View File

@ -0,0 +1,81 @@
.importzp TEMP, PPU_CTRL_VAR, PPU_CTRL_VAR1, SCROLL_X1, SCROLL_Y1
.import popax
.include "nes.inc"
.include "zpvars.inc"
.export _splitxy
;;void __fastcall__ splitxy(unsigned int x,unsigned int y);
WRITE1=TEMP+1
WRITE2=TEMP+2
_splitxy:
; Extract SCROLL_Y1, SCROLL_X1, WRITE1 from parameters.
sta <TEMP
txa
bne @1
lda <TEMP
cmp #240
bcs @1
sta <SCROLL_Y1
lda #0
sta <TEMP
beq @2 ;bra
@1:
sec
lda <TEMP
sbc #240
sta <SCROLL_Y1
lda #8 ;; Bit 3
sta <TEMP
@2:
jsr popax
sta <SCROLL_X1
txa
and #$01
asl a
asl a ;; Bit 2
ora <TEMP ;; From Y
sta <WRITE1 ;; Store!
; Calculate WRITE2 = ((Y & $F8) << 2) | (X >> 3)
lda <SCROLL_Y1
and #$F8
asl a
asl a
sta <TEMP ;; TEMP = (Y & $F8) << 2
lda <SCROLL_X1
lsr a
lsr a
lsr a ;; A = (X >> 3)
ora <TEMP ;; A = (X >> 3) | ((Y & $F8) << 2)
sta <WRITE2 ;; Store!
; Wait for sprite 0 hit
@3:
bit PPU_STATUS
bvs @3
@4:
bit PPU_STATUS
bvc @4
; Set scroll value
lda PPU_STATUS
lda <WRITE1
sta PPU_ADDR
lda <SCROLL_Y1
sta PPU_SCROLL
lda <SCROLL_X1
ldx <WRITE2
sta PPU_SCROLL
stx PPU_ADDR
rts

View File

@ -0,0 +1,47 @@
.importzp TEMP
.import popa, popax
.include "nes.inc"
.include "zpvars.inc"
.export _vram_read
;void __fastcall__ vram_read(unsigned char *dst,unsigned int size);
_vram_read:
sta <TEMP
stx <TEMP+1
jsr popax
sta <TEMP+2
stx <TEMP+3
lda PPU_DATA
ldy #0
@1:
lda PPU_DATA
sta (TEMP+2),y
inc <TEMP+2
bne @2
inc <TEMP+3
@2:
lda <TEMP
bne @3
dec <TEMP+1
@3:
dec <TEMP
lda <TEMP
ora <TEMP+1
bne @1
rts

View File

@ -0,0 +1,60 @@
.importzp TEMP
.include "nes.inc"
.include "zpvars.inc"
.export _vram_unrle
;void __fastcall__ vram_unrle(const unsigned char *data);
_vram_unrle:
tay
stx <RLE_HIGH
lda #0
sta <RLE_LOW
lda (RLE_LOW),y
sta <RLE_TAG
iny
bne @1
inc <RLE_HIGH
@1:
lda (RLE_LOW),y
iny
bne @11
inc <RLE_HIGH
@11:
cmp <RLE_TAG
beq @2
sta PPU_DATA
sta <RLE_BYTE
bne @1
@2:
lda (RLE_LOW),y
beq @4
iny
bne @21
inc <RLE_HIGH
@21:
tax
lda <RLE_BYTE
@3:
sta PPU_DATA
dex
bne @3
beq @1
@4:
rts

View File

@ -0,0 +1,14 @@
PTR =TEMP ;word
LEN =TEMP+2 ;word
NEXTSPR =TEMP+4
SCRX =TEMP+5
SCRY =TEMP+6
SRC =TEMP+7 ;word
DST =TEMP+9 ;word
RLE_LOW =TEMP
RLE_HIGH =TEMP+1
RLE_TAG =TEMP+2
RLE_BYTE =TEMP+3

1
Transpiler/README.md Normal file
View File

@ -0,0 +1 @@
This component of the MultiSpaccSDK is abandoned until further notice.