mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-12-24 20:32:39 +00:00
Attempting to port lazyNES API to KickC.
This commit is contained in:
parent
6b2d9d6522
commit
a74d02203e
@ -55,7 +55,7 @@ inline void initNES() {
|
||||
// which may be accomplished by waiting for 2 (two) vertical blank intervals.
|
||||
clearVBlankFlag();
|
||||
waitForVBlank();
|
||||
// Clear RAM - since it has all variables and the stack it is necesary to do it inline
|
||||
// Clear RAM - since it has all variables and the stack it is necessary to do it inline
|
||||
char i=0;
|
||||
do {
|
||||
(MEMORY+0x000)[i] = 0;
|
||||
|
BIN
src/test/kc/complex/lazynes/example.chr
Normal file
BIN
src/test/kc/complex/lazynes/example.chr
Normal file
Binary file not shown.
16
src/test/kc/complex/lazynes/lazyhello.c
Normal file
16
src/test/kc/complex/lazynes/lazyhello.c
Normal file
@ -0,0 +1,16 @@
|
||||
// lazyNES lazyhello demo
|
||||
|
||||
// Ported to KickC 2020 by Jesper Gravgaard
|
||||
// Original Source VBCC alpha 2 http://www.ibaug.de/vbcc/vbcc6502_2.zip
|
||||
|
||||
#include "lazynes.h"
|
||||
|
||||
int lnMain() {
|
||||
static const ubyte bgColors[] = { 0x02, 0x33 };
|
||||
static const char text[]="HELLO LAZYNES!";
|
||||
lnSync(lfBlank); // blank screen to enable lnPush()
|
||||
lnPush(lnBackCol, sizeof(bgColors), bgColors); // set colors, always directly after lnSync()
|
||||
lnPush(lnNameTab0+32, sizeof(text)-1, text); // draw text in 2nd line
|
||||
lnSync(0); // sync with vblank, unblank screen
|
||||
return 0;
|
||||
}
|
61
src/test/kc/complex/lazynes/lazymain.c
Normal file
61
src/test/kc/complex/lazynes/lazymain.c
Normal file
@ -0,0 +1,61 @@
|
||||
// lazyNES lazyhello demo
|
||||
// Main file
|
||||
|
||||
// Ported to KickC 2020 by Jesper Gravgaard
|
||||
// Original Source VBCC aplha 2 http://www.ibaug.de/vbcc/vbcc6502_2.zip
|
||||
|
||||
#pragma target(nes)
|
||||
#include <nes.h>
|
||||
#include <string.h>
|
||||
#include "lazynes.c"
|
||||
#include "lazyhello.c"
|
||||
|
||||
// RESET Called when the NES is reset, including when it is turned on.
|
||||
void main() {
|
||||
// Initialize NES after RESET
|
||||
initNES();
|
||||
// Clear the name table
|
||||
ppuDataFill(PPU_NAME_TABLE_0, 0, 0x3c0);
|
||||
// Fill the PPU attribute table
|
||||
ppuDataFill(PPU_ATTRIBUTE_TABLE_0, 0, 0x40);
|
||||
// Enable screen rendering and vblank
|
||||
enableVideoOutput();
|
||||
// Execute main code
|
||||
lnMain();
|
||||
// Infinite loop
|
||||
while(1) ;
|
||||
}
|
||||
|
||||
// NMI Called when the PPU refreshes the screen (also known as the V-Blank period)
|
||||
interrupt(hardware_stack) void vblank() {
|
||||
// DMA transfer the entire sprite buffer to the PPU
|
||||
ppuSpriteBufferDmaTransfer(SPRITE_BUFFER);
|
||||
// Set scroll
|
||||
PPU->PPUSCROLL = 0;
|
||||
PPU->PPUSCROLL = 0;
|
||||
}
|
||||
|
||||
// Data (in PRG ROM)
|
||||
#pragma data_seg(Data)
|
||||
|
||||
// Tile Set (in CHR ROM)
|
||||
#pragma data_seg(Tiles)
|
||||
export char TILES[] = kickasm(resource "example.chr") {{
|
||||
.import binary "example.chr"
|
||||
}};
|
||||
|
||||
// Sprite Buffer (in GAME RAM)
|
||||
// Will be transferred to the PPU via DMA during vblank
|
||||
#pragma data_seg(GameRam)
|
||||
struct SpriteData align(0x100) SPRITE_BUFFER[0x40];
|
||||
|
||||
// Interrupt Vectors (in PRG ROM)
|
||||
#pragma data_seg(Vectors)
|
||||
export void()* const VECTORS[] = {
|
||||
// NMI Called when the PPU refreshes the screen (also known as the V-Blank period)
|
||||
&vblank,
|
||||
// RESET Called when the NES is reset, including when it is turned on.
|
||||
&main,
|
||||
// IRQ Called when a BRK instruction is executed.
|
||||
0
|
||||
};
|
105
src/test/kc/complex/lazynes/lazynes.c
Normal file
105
src/test/kc/complex/lazynes/lazynes.c
Normal file
@ -0,0 +1,105 @@
|
||||
// lazyNES - As lazy as possible NES hardware support library for vbcc6502
|
||||
// (happily cooperates with Shiru's famitone2 replay code)
|
||||
// V1.0, 'Lazycow 2020
|
||||
// Ported to KickC 2020 by Jesper Gravgaard
|
||||
// Original Source VBCC aplha 2 http://www.ibaug.de/vbcc/vbcc6502_2.zip
|
||||
|
||||
#include "lazynes.h"
|
||||
#include <nes.h>
|
||||
|
||||
// Wait for next vblank
|
||||
// flags: 0, lfBlank or lfSplit (see below)
|
||||
// result: Amount of frames since last sync [0..31], 128 is added on NTSC
|
||||
//
|
||||
ubyte lnSync(ubyte flags) {
|
||||
// Enable video output if lfBlank not set
|
||||
if(!(flags&lfBlank))
|
||||
enableVideoOutput();
|
||||
// Wait for V-Blank
|
||||
waitForVBlank();
|
||||
// Disable video output if lfBlank set
|
||||
if(flags&lfBlank)
|
||||
// lfBlank = 1, activates blank mode, blanks screen and allows lnPush() calls
|
||||
disableVideoOutput();
|
||||
|
||||
// TODO: Handle lfSplit = 2 : activates split mode, NMI waits for SPR0HIT and sets registers
|
||||
// TODO: Count frames
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Write data into nametables, palettes, CHRRAM, etc.
|
||||
// (System must be in blank mode!)
|
||||
// o: Destination address offset in vram
|
||||
// a: Amount of bytes that should be written
|
||||
// p: Pointer to data
|
||||
//
|
||||
void lnPush(uword o, ubyte a, void* s) {
|
||||
ppuDataTransfer(o, s, a);
|
||||
}
|
||||
|
||||
// Write data into nametables, palettes, CHRRAM, etc.
|
||||
// (Screen has to be visible, doesn't work in blank mode!)
|
||||
// updateList: Pointer to update list
|
||||
//
|
||||
// TODO: void lnList(void* updateList);
|
||||
// TODO: enum { lfHor=64, lfVer=128, lfEnd=255 };
|
||||
|
||||
// remarks:
|
||||
// - The format of the update list is an array of unsigned bytes.
|
||||
// - There can be 3 different commands in the update list:
|
||||
// a) addressHi, addressLo, value
|
||||
// b) addressHi|lfHor, addressLo, amountOfBytes, byte1, byte2, byte3, ...
|
||||
// c) addressHi|lfVer, addressLo, amountOfBytes, byte1, byte2, byte3, ...
|
||||
// - Multiple commands can be queued in one list,
|
||||
// but there can only be one activated updatelist at a time.
|
||||
// - The end of the list is marked by lfEnd! (important!)
|
||||
// - It's the same format that's used in set_vram_update() of Shiru's neslib
|
||||
|
||||
|
||||
// Scroll background
|
||||
// x: New horizotnal scrolling offset in pixels, allowed range: [0..511]
|
||||
// y: New vertical scrolling offset in pixels, allowed range: [0..479]
|
||||
//
|
||||
// TODO: void lnScroll(uword x, uword y);
|
||||
//
|
||||
// remarks:
|
||||
// - If a SPR0HIT based splitscreen is used, the 1st call of lnScroll() sets
|
||||
// the scrolling offsets of the area above the split and the 2nd call of
|
||||
// lnScroll() sets the scrolling offsets of the area below the split.
|
||||
|
||||
|
||||
// Add meta-sprite to display list
|
||||
// p: Pointer to metasprite data
|
||||
// x,y: Sprite coordinates
|
||||
// result: New position offset in OAM after the meta sprite has been added
|
||||
//
|
||||
// TODO: ubyte lnAddSpr(void* p, sword x, sword y);
|
||||
//
|
||||
// remarks:
|
||||
// - The format for the metasprite data is an array of unsigned bytes.
|
||||
// - Four bytes per sprite: x-offset, y-offset, tile, attributes
|
||||
// - The end of the list is marked by the value 128! (important!)
|
||||
// - It's the same format that's used in oam_meta_spr() from Shiru's neslib
|
||||
|
||||
|
||||
// Query joypad state
|
||||
// port: Joypad port (1 or 2)
|
||||
// result: Set of joypad flags (see below)
|
||||
//
|
||||
// TODO: ubyte lnGetPad(ubyte port);
|
||||
// TODO: enum { lfU=8, lfD=4, lfL=2, lfR=1, lfA=128, lfB=64, lfStart=16, lfSelect=32 };
|
||||
|
||||
|
||||
//
|
||||
// advanced usage
|
||||
//
|
||||
|
||||
__zp volatile ubyte
|
||||
lnSpr0Wait, // delay until scroll registers will be set after a SPR0HIT
|
||||
lnPPUCTRL, // current value of PPUCTRL register (will be written in NMI)
|
||||
lnPPUMASK; // current value of PPUMASK register (will be written in NMI)
|
||||
//
|
||||
// remark: The lazyNES NMI will write the PPUCTRL and PPUMASK registers,
|
||||
// so don't write PPUCTRL and PPUMASK directly - use these two
|
||||
// variables insead. Their values will be written in the next NMI.
|
||||
// Also, don't use these variables before the 1st call of lnSync()!
|
105
src/test/kc/complex/lazynes/lazynes.h
Normal file
105
src/test/kc/complex/lazynes/lazynes.h
Normal file
@ -0,0 +1,105 @@
|
||||
// lazyNES - As lazy as possible NES hardware support library for vbcc6502
|
||||
// (happily cooperates with Shiru's famitone2 replay code)
|
||||
// V1.0, 'Lazycow 2020
|
||||
|
||||
// Ported to KickC 2020 by Jesper Gravgaard
|
||||
// Original Source VBCC aplha 2 http://www.ibaug.de/vbcc/vbcc6502_2.zip
|
||||
|
||||
typedef signed char sbyte;
|
||||
typedef unsigned char ubyte;
|
||||
typedef signed short sword;
|
||||
typedef unsigned short uword;
|
||||
|
||||
// Wait for next vblank
|
||||
// flags: 0, lfBlank or lfSplit (see below)
|
||||
// result: Amount of frames since last sync [0..31], 128 is added on NTSC
|
||||
//
|
||||
ubyte lnSync(ubyte flags);
|
||||
enum {
|
||||
lfBlank = 1, // activates blank mode, blanks screen and allows lnPush() calls
|
||||
lfSplit = 2 // activates split mode, NMI waits for SPR0HIT and sets registers
|
||||
};
|
||||
|
||||
|
||||
// Write data into nametables, palettes, CHRRAM, etc.
|
||||
// (System must be in blank mode!)
|
||||
// o: Destination address offset in vram
|
||||
// a: Amount of bytes that should be written
|
||||
// p: Pointer to data
|
||||
//
|
||||
void lnPush(uword o, ubyte a, void* s);
|
||||
|
||||
|
||||
// Write data into nametables, palettes, CHRRAM, etc.
|
||||
// (Screen has to be visible, doesn't work in blank mode!)
|
||||
// updateList: Pointer to update list
|
||||
//
|
||||
// TODO: void lnList(void* updateList);
|
||||
// TODO: enum { lfHor=64, lfVer=128, lfEnd=255 };
|
||||
//
|
||||
// remarks:
|
||||
// - The format of the update list is an array of unsigned bytes.
|
||||
// - There can be 3 different commands in the update list:
|
||||
// a) addressHi, addressLo, value
|
||||
// b) addressHi|lfHor, addressLo, amountOfBytes, byte1, byte2, byte3, ...
|
||||
// c) addressHi|lfVer, addressLo, amountOfBytes, byte1, byte2, byte3, ...
|
||||
// - Multiple commands can be queued in one list,
|
||||
// but there can only be one activated updatelist at a time.
|
||||
// - The end of the list is marked by lfEnd! (important!)
|
||||
// - It's the same format that's used in set_vram_update() of Shiru's neslib
|
||||
|
||||
|
||||
// Common offsets for lnPush() and lnList()
|
||||
enum {
|
||||
lnNameTab0=0x2000, lnNameTab1=0x2400, lnNameTab2=0x2800, lnNameTab3=0x2C00,
|
||||
lnAttrTab0=0x23C0, lnAttrTab1=0x27C0, lnAttrTab2=0x2BC0, lnAttrTab3=0x2FC0,
|
||||
lnBackCol=0x3F00,
|
||||
lnChrPal0=0x3F01, lnChrPal1=0x3F05, lnChrPal2=0x3F09, lnChrPal3=0x3F0D,
|
||||
lnSprPal0=0x3F11, lnSprPal1=0x3F15, lnSprPal2=0x3F19, lnSprPal3=0x3F1D
|
||||
};
|
||||
|
||||
// Scroll background
|
||||
// x: New horizotnal scrolling offset in pixels, allowed range: [0..511]
|
||||
// y: New vertical scrolling offset in pixels, allowed range: [0..479]
|
||||
//
|
||||
// TODO: void lnScroll(uword x, uword y);
|
||||
//
|
||||
// remarks:
|
||||
// - If a SPR0HIT based splitscreen is used, the 1st call of lnScroll() sets
|
||||
// the scrolling offsets of the area above the split and the 2nd call of
|
||||
// lnScroll() sets the scrolling offsets of the area below the split.
|
||||
|
||||
// Add meta-sprite to display list
|
||||
// p: Pointer to metasprite data
|
||||
// x,y: Sprite coordinates
|
||||
// result: New position offset in OAM after the meta sprite has been added
|
||||
//
|
||||
// TODO: ubyte lnAddSpr(void* p, sword x, sword y);
|
||||
//
|
||||
// remarks:
|
||||
// - The format for the metasprite data is an array of unsigned bytes.
|
||||
// - Four bytes per sprite: x-offset, y-offset, tile, attributes
|
||||
// - The end of the list is marked by the value 128! (important!)
|
||||
// - It's the same format that's used in oam_meta_spr() from Shiru's neslib
|
||||
|
||||
|
||||
// Query joypad state
|
||||
// port: Joypad port (1 or 2)
|
||||
// result: Set of joypad flags (see below)
|
||||
//
|
||||
// TODO: ubyte lnGetPad(ubyte port);
|
||||
// TODO: enum { lfU=8, lfD=4, lfL=2, lfR=1, lfA=128, lfB=64, lfStart=16, lfSelect=32 };
|
||||
|
||||
//
|
||||
// advanced usage
|
||||
//
|
||||
|
||||
extern __zp volatile ubyte
|
||||
lnSpr0Wait, // delay until scroll registers will be set after a SPR0HIT
|
||||
lnPPUCTRL, // current value of PPUCTRL register (will be written in NMI)
|
||||
lnPPUMASK; // current value of PPUMASK register (will be written in NMI)
|
||||
//
|
||||
// remark: The lazyNES NMI will write the PPUCTRL and PPUMASK registers,
|
||||
// so don't write PPUCTRL and PPUMASK directly - use these two
|
||||
// variables insead. Their values will be written in the next NMI.
|
||||
// Also, don't use these variables before the 1st call of lnSync()!
|
BIN
src/test/kc/complex/lazynes/sprites.chr
Normal file
BIN
src/test/kc/complex/lazynes/sprites.chr
Normal file
Binary file not shown.
@ -247,7 +247,7 @@ void eorfill(char* line_buffer, char* canvas) {
|
||||
}
|
||||
}
|
||||
|
||||
// Get the absolute value of a u-bit unsigned number treated as a signed number.
|
||||
// Get the absolute value of a 8-bit unsigned number treated as a signed number.
|
||||
unsigned char abs_u8(unsigned char u) {
|
||||
if(u & 0x80) {
|
||||
return -u;
|
||||
|
65
src/test/kc/complex/xy-scroller/xy-scroller.c
Normal file
65
src/test/kc/complex/xy-scroller/xy-scroller.c
Normal file
@ -0,0 +1,65 @@
|
||||
// A full-screen x/y-scroller
|
||||
// The main screen is double-buffered with a shared charset. Most of the screen is expected to be empty (filled with char 0)
|
||||
// While scrolling through the pixels on one screen the second screen is prepared. When needed a swap is made.
|
||||
// At any time an object is being scrolled onto the screen. The object is fetched from an 8x8 char buffer.
|
||||
//
|
||||
// The scroll director orchestrates the movement of the main screen, rendering on the second screen from the buffer and the rendering of graphics in the buffer.
|
||||
// In practice the scroll director calculates a number of frames ahead to identify the operations needed on each frame. It calculates ahead until it encounters the next "new buffer needed".
|
||||
|
||||
#include <c64.h>
|
||||
#include <string.h>
|
||||
#include <conio.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Dispaly screen #1 (double buffered)
|
||||
char * const MAIN_SCREEN1 = 0x0400;
|
||||
// Display screen #2 (double buffered)
|
||||
char * const MAIN_SCREEN2 = 0x3800;
|
||||
// Display charset
|
||||
char * const MAIN_CHARSET = 0x1000;
|
||||
|
||||
// The render 8x8 buffer containing chars to be rendered onto the screen
|
||||
char RENDER_BUFFER[8*8] =
|
||||
" ** "
|
||||
" ****** "
|
||||
" ****** "
|
||||
"********"
|
||||
"********"
|
||||
" ****** "
|
||||
" ****** "
|
||||
" ** "z
|
||||
;
|
||||
|
||||
void main() {
|
||||
VICII->MEMORY = toD018(MAIN_SCREEN1, MAIN_CHARSET);
|
||||
memset(MAIN_SCREEN1, ' ', 1000);
|
||||
|
||||
// positions to render to
|
||||
char xpos=0, ypos=0;
|
||||
while(xpos < 40 && ypos < 25) {
|
||||
char *sc = MAIN_SCREEN1 + (unsigned int)ypos*40 + xpos;
|
||||
char i=0;
|
||||
for(char y=0; y<8; y++) {
|
||||
for(char x=0; x<8; x++) {
|
||||
char c=RENDER_BUFFER[i++];
|
||||
if(c!=' ' && xpos+x<40 && ypos+y<25)
|
||||
sc[x] = c;
|
||||
}
|
||||
sc+=40;
|
||||
}
|
||||
xpos+=5;
|
||||
ypos+=3;
|
||||
}
|
||||
|
||||
// count the number of chars
|
||||
unsigned int count = 0;
|
||||
for(char *sc = MAIN_SCREEN1;sc<MAIN_SCREEN1+1000;sc++)
|
||||
if(*sc!=' ') count++;
|
||||
gotoxy(0,0);
|
||||
printf("%u",count);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user