1
0
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:
jespergravgaard 2020-07-05 18:27:42 +02:00
parent 6b2d9d6522
commit a74d02203e
9 changed files with 354 additions and 2 deletions

View File

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

Binary file not shown.

View 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;
}

View 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
};

View 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()!

View 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()!

Binary file not shown.

View File

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

View 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);
}