diff --git a/src/main/kc/lib/nes.c b/src/main/kc/lib/nes.c index 4c80cba3d..27e572be0 100644 --- a/src/main/kc/lib/nes.c +++ b/src/main/kc/lib/nes.c @@ -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; diff --git a/src/test/kc/complex/lazynes/example.chr b/src/test/kc/complex/lazynes/example.chr new file mode 100644 index 000000000..06c1e76c1 Binary files /dev/null and b/src/test/kc/complex/lazynes/example.chr differ diff --git a/src/test/kc/complex/lazynes/lazyhello.c b/src/test/kc/complex/lazynes/lazyhello.c new file mode 100644 index 000000000..eaf412d6d --- /dev/null +++ b/src/test/kc/complex/lazynes/lazyhello.c @@ -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; +} diff --git a/src/test/kc/complex/lazynes/lazymain.c b/src/test/kc/complex/lazynes/lazymain.c new file mode 100644 index 000000000..b5b563f15 --- /dev/null +++ b/src/test/kc/complex/lazynes/lazymain.c @@ -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 +#include +#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 +}; diff --git a/src/test/kc/complex/lazynes/lazynes.c b/src/test/kc/complex/lazynes/lazynes.c new file mode 100644 index 000000000..0569e9d6b --- /dev/null +++ b/src/test/kc/complex/lazynes/lazynes.c @@ -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 + + // 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()! diff --git a/src/test/kc/complex/lazynes/lazynes.h b/src/test/kc/complex/lazynes/lazynes.h new file mode 100644 index 000000000..76ac9d504 --- /dev/null +++ b/src/test/kc/complex/lazynes/lazynes.h @@ -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()! diff --git a/src/test/kc/complex/lazynes/sprites.chr b/src/test/kc/complex/lazynes/sprites.chr new file mode 100644 index 000000000..1a97ed881 Binary files /dev/null and b/src/test/kc/complex/lazynes/sprites.chr differ diff --git a/src/test/kc/complex/polygon/polygon.c b/src/test/kc/complex/polygon/polygon.c index e622f2109..bb09b081c 100644 --- a/src/test/kc/complex/polygon/polygon.c +++ b/src/test/kc/complex/polygon/polygon.c @@ -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; diff --git a/src/test/kc/complex/xy-scroller/xy-scroller.c b/src/test/kc/complex/xy-scroller/xy-scroller.c new file mode 100644 index 000000000..ba0649bc8 --- /dev/null +++ b/src/test/kc/complex/xy-scroller/xy-scroller.c @@ -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 +#include +#include +#include + +// 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