1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2025-04-06 15:41:05 +00:00

Removed some tests.

This commit is contained in:
jespergravgaard 2021-01-04 00:49:50 +01:00
parent 9bf44bcb2a
commit 3cfc517fe2
27 changed files with 0 additions and 2101 deletions

View File

@ -1,172 +0,0 @@
// bubbles64 - Q&D C64 port of the bubbles demo from vbcc6502's NES exmaples
// Coded by Lazycow
// Source https://www.lemon64.com/forum/viewtopic.php?t=75283&start=15
#include "lazyply.h"
#define scrPtr (( ubyte*)0xE000)
#define colPtr (( ubyte*)0xD800)
#define vicPtr (( ubyte*)0xD000)
#define chrPtr (( ubyte*)0xF800)
#define onePtr (( ubyte*)0x0001)
#define scr 0xE000
enum {
maxSprites=32,
maxSprImages=8,
maxC64Images=256,
sprOff=64
};
ubyte
lcSprMapTab[maxSprImages],
lcSprColTab[maxC64Images];
enum {
maxDrawObjects=24,
F=3
};
ubyte cmIT[maxSprites+1],
cmSI[maxSprites], cmRX[maxSprites], cmRY[maxSprites], cmRF[maxSprites];
typedef struct {
ubyte
s; // sprite image offset into bubbles[] table
sword
x, y; // coordinates (shifted)
sbyte
vx, vy, // velocity
ax, ay; // accelleration
} DrawObject;
DrawObject dTab[maxDrawObjects];
void Print00(char* p, ubyte v0) {
char c; ubyte v=v0;
c='0'; while (v>=10) { ++c; v-=10; }
*p++=c;
c='0'; while (v>=1) { ++c; v-=1; }
*p++=c;
}
// sprite support functions
//
ubyte GetSprite() { // get unused or dropped sprite
ubyte t0; for (t0=0;t0<maxSprites;++t0) if (cmRF[t0]>=rfDrop)
{ cmRF[t0]=0; return t0; }
return 255;
}
void AddSprite(ubyte sn) { // activate prepared sprite into the (i)ndex (t)able
ubyte t0; if (rfDrop==cmRF[sn]) cmRF[sn]=0; else for
(t0=0;t0<maxSprites;t0+=1) if (cmIT[t0]>=128)
{ cmRF[sn]=0; cmIT[t0]=sn; return; }
}
void DelSprite(ubyte sn) { // drop sprite, will be removed from IT in IRQ
cmRY[sn]=255; cmRF[sn]=rfDrop;
}
//
//
int main() {
register DrawObject* d;
ubyte
i,
type=0,
stopIt=0,
objects=0,
preset=0; // 1 == preallocate 20 sprites (only for better benchmarking)
// 0 == allocate sprites on the fly (prefered)
uword
oCount=0,
c=0;
// setup sprite images
lcSprMapTab[0]=sprOff+0; lcSprMapTab[1]=sprOff+1;
lcSprMapTab[2]=sprOff+2; lcSprMapTab[3]=sprOff+3;
lcSprMapTab[4]=sprOff+4; lcSprMapTab[5]=sprOff+5;
// setup sprite colors + flags
lcSprColTab[sprOff+0]=lcSprColTab[sprOff+1]=10|lfMC;
lcSprColTab[sprOff+2]=lcSprColTab[sprOff+3]=5|lfMC;
lcSprColTab[sprOff+4]=lcSprColTab[sprOff+5]=14|lfMC;
// setup C64 / lazyply
lcVIC17=vicPtr[17]; lcVIC22=vicPtr[22]; lcVIC24=vicPtr[24];
lcVIC33=6; lcVIC37=1; lcVIC38=11;
for (i=0;i<maxSprites+1;i+=1) cmIT[i]=255; // clear (i)ndex (t)able
for (i=0;i<maxSprites ;i+=1) cmRF[i]=255;
for (c=0;c<1000;c+=1) { scrPtr[c]=32; colPtr[c]=14; }
i=lcSync(0); // install IRQ
*onePtr=0x33; // now it's save to change "01"
for (c=0;c<2040;c+=1) chrPtr[c]=vicPtr[c];
*onePtr=0x35;
vicPtr[0xd02]|=3; vicPtr[0xd00]&=~3ub; lcVIC24=128+14;
// PAL or NTSC?
if (i&128) {
scrPtr[996]='N'-'A'+1; scrPtr[997]='T'-'A'+1; scrPtr[998]='S'-'A'+1;
scrPtr[999]='C'-'A'+1;
} else {
scrPtr[997]='P'-'A'+1; scrPtr[998]='A'-'A'+1; scrPtr[999]='L'-'A'+1;
}
scrPtr[975+0]='B'-'A'+1;
scrPtr[975+1]='U'-'A'+1;
scrPtr[975+2]='B'-'A'+1;
scrPtr[975+3]='B'-'A'+1;
scrPtr[975+4]='L'-'A'+1;
scrPtr[975+5]='E'-'A'+1;
scrPtr[975+6]='S'-'A'+1;
scrPtr[975+7]=':';
// setup bubbles
type=0;
for (i=0;i<maxDrawObjects;i+=1) {
DrawObject* d=&dTab[i];
d->s=type;
type+=1; if (type>=6) type=0;
d->x=112<<F; d->vx=(sbyte)i; d->ax=1;
d->y=24<<F; d->vy=1<<F; d->ay=1;
if (preset && i<20) {
if ((c=GetSprite())<128) {
AddSprite((ubyte)c);
cmSI[i]=cmRX[i]=cmRF[i]=0;
cmRY[i]=i<<4;
}
}
}
// main loop
c=0; for (;;) {
// move objects
for (i=0,d=dTab; i<objects ;i+=1,++d) {
d->vx+=d->ax;
if (d->vx<-32) d->ax=1; else if (d->vx>32) d->ax=-1;
if (d->y>248<<F) d->vy=-1<<F; else if (d->y<24<<F) d->vy=1<<F;
d->x+=d->vx; d->y+=d->vy;
if (cmRF[i]<128) { cmSI[i]=d->s; cmRX[i]=(ubyte)(d->x>>F); cmRY[i]=(ubyte)(d->y>>F); }
}
// activate new bubble?
c+=1; if (c>=14) {
c=0;
if (0==stopIt && objects<maxDrawObjects) {
objects+=1;
if (0==preset && (i=GetSprite())<128) {
AddSprite(i);
cmSI[i]=cmRX[i]=cmRF[i]=cmRY[i]=0;
}
}
}
i=lcSync(0)&31;
if (i>1) oCount+=2; else if (oCount>0) oCount-=1;
if (oCount>2 && objects>0) // stop adding objects?
{ objects-=1; DelSprite(objects); stopIt=1; oCount=0; }
Print00(scr+(char*)983,objects);
}
return 0;
}

View File

@ -1,11 +0,0 @@
#!/bin/sh
rm out.raw
rm out.prg
#cl65 -Oris -Cl -C c64o.cfg -t c64 bubbles64.c lazyply-ca65.s c64irq-ca65.s c64sprcore-ca65.s -o out.raw || exit
vc +c64 -+ -Dmain=__main -O3 bubbles64.c -llazyply -o out.raw || exit
exomizer sfx sys out.raw sprites.spr@0xD000 -q -p 1 -m 1 -o out.prg || exit
#x64 -pal out.prg

View File

@ -1,78 +0,0 @@
// lazyply - experimental C64 sprite multiplying code, 'Lazycow 2015 (prealpha)
typedef signed char sbyte;
typedef unsigned char ubyte;
typedef signed short sword;
typedef unsigned short uword;
//
// sprites
//
enum {
lfMC=16, // sprite is multicolor
lfOL=32 // sprite is build out of 2 overlayed sprites (hires over multicolor)
};
extern ubyte lcSprMapTab[];
extern ubyte lcSprColTab[];
enum { rfXMSB=16, rfDrop=254, rfNull=255 };
//enum {
//scNull=255, // unallocated (63)
//scDrop=254, // will be removed (62)
//scHide=253, // invisible (61)
//scVoid=128, // no collision detection
//scHold=64, // sprite holds on sprite cmSC[]&scMask, offset cmSU[]/cmSV[]
//scMask=63
//};
extern ubyte
cmIT[], // sprite index table (dim must be maxSprites+1!)
cmSI[], // sprite image
//cmSC[], // sprite control
cmRX[], // raw sprite x coordinates (bits 0..7)
cmRY[], // raw sprite y coordinates (bits 0..7)
cmRF[]; // raw sprite flags: 1 == bit 8 of raw x coordinate
//
// raster interrupt
//
ubyte lcVIC17, lcVIC22, lcVIC24, lcVIC33, lcVIC37, lcVIC38;
#if defined(__CC65__)
ubyte fastcall lcSync(ubyte flags);
ubyte fastcall lcGetPad(ubyte port);
enum { lfU=1, lfD=2, lfL=4, lfR=8, lfA=16, lfB=32 };
#elif defined(__VBCC__)
// flags: 0
// result: amount of frames since the last call [0..31], +128 on NTSC
//
ubyte lcSync(__reg("a") ubyte flags);
// port: 0==query both ports (other values not supported, yet)
// result: set of joypad flags
//
ubyte lcGetPad(__reg("a") ubyte port);
enum { lfU=1, lfD=2, lfL=4, lfR=8, lfA=16, lfB=32 };
#elif defined(__KICKC__)
// flags: 0
// result: amount of frames since the last call [0..31], +128 on NTSC
//
ubyte lcSync(ubyte flags) { return 0; }
// port: 0==query both ports (other values not supported, yet)
// result: set of joypad flags
//
ubyte lcGetPad(ubyte port) { return 0; }
enum { lfU=1, lfD=2, lfL=4, lfR=8, lfA=16, lfB=32 };
#endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 430 B

View File

@ -1,47 +0,0 @@
// lazyNES balloon demo
// 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 alpha 2 http://www.ibaug.de/vbcc/vbcc6502_2.zip
#pragma target(nes)
#pragma emulator("java -jar c:/c64/Nintaco/Nintaco.jar")
#include "lazynes.h"
int lnMain() {
static const ubyte bgColors[]={2,33};
static const ubyte sprPal0[]={33};
static const ubyte balloonData[]={
0,0,0,0, // balloon is build out of 6 sprites, 4 bytes define a sprite
8,0,1,0, // x-offset, y-offset, tile, palette + flags
0,8,2,0,
8,8,3,0,
0,16,4,0,
8,16,5,0,
128 // end of list marker (important!)
};
sword x=0, y=0;
// To avoid glitches, always write color palettes immediately after lnSync()!
lnSync(lfBlank); // blank screen to enable lnPush() usage
lnSync(lfBlank); // blank screen to enable lnPush() usage
lnPush(lnBackCol,2,bgColors); // set colors, always directly after lnSync()!
lnPush(lnSprPal0,1,sprPal0); // set sprite colors
while(1) {
ubyte j=lnGetPad(1); // query 1st joypad
if (0==j) { // automatic movement?
x+=1; if (x>=240) x=0;
y+=1; if (y>=240) y=0;
}
if (j&lfL) x-=1; else if (j&lfR) x+=1; // move left/right?
if (j&lfU) y-=1; else if (j&lfD) y+=1; // move up/down?
lnAddSpr(balloonData,x,y); // add meta sprite to display list
lnSync(0); // sync with vblank
}
return 0;
}

View File

@ -1,145 +0,0 @@
// lazyNES bubbles demo
// 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 alpha 2 http://www.ibaug.de/vbcc/vbcc6502_2.zip
#pragma target(nes)
#include "lazynes.h"
enum {
F=3, // pseudo floating point shift value
maxDrawObjects=40,
dfAlive=1
};
typedef struct {
ubyte
f, // flags
s; // sprite image offset into bubbles[] table
sword
x, y; // coordinates (shifted)
sbyte
vx, vy, // velocity
ax, ay; // accelleration
} DrawObject;
DrawObject dTab[maxDrawObjects];
// print some text in the static area
//
void Print(uword offset, ubyte value) {
static ubyte b[]={0,0,10,'B','U','B','B','L','E','S',':',0,0,lfEnd};
b[0]=(offset>>8)|lfHor; b[1]=offset&255; b[11]=b[12]='0';
while (value>=10) { ++b[11]; value-=10; }
while (value>=1) { ++b[12]; value-=1; }
lnList(b);
}
//
//
int lnMain() {
static const ubyte bgColors[]={45,33,2};
static const ubyte bubblesPal[]={ 0, 37,22,5, 0, 42,26,10, 0, 33,18,3 };
static const ubyte bubbles[]={ // x-offset, y-offset, tile, palette+flags
0,0,7,0, 8,0,9,0, 128, // 16x16, red, offset 0
0,0,7,1, 8,0,9,1, 128, // 16x16, green, offset 9
0,0,7,2, 8,0,9,2, 128, // 16x16, blue, offest 18
0,4,11,0,128, // 8x8, red, offset 27
0,4,11,1,128, // 8x8, green, offset 32
0,4,11,2,128, // 8x8, blue, offset 37
},
bubbleTab[]={0,27,9,32,18,37}; // start offsets in bubbles[]
ubyte
i,
type=0,
stopIt=0,
objects=0;
uword
hScroll=0,
tics=0;
sbyte
hVel=0, hDir=1;
lnSync(lfBlank); // blank screen to enable lnPush() usage
lnPush(lnBackCol,3,bgColors); // set background colors
lnPush(lnSprPal0,3,&bubblesPal[1]); // set sprite colors
lnPush(lnSprPal1,3,&bubblesPal[5]);
lnPush(lnSprPal2,3,&bubblesPal[9]);
lnPPUCTRL|=32; // Select 8x16 sprites; has to be set after calling lnSync()!
// draw some backgrounds
{ //
static const ubyte g1[]={1}, g2[]={2,3,4,5}, g3[]={6,7,8,9};
ubyte x;
// draw a line: Sprite #0 will be placed ontop later to trigger the split
i=2; for (x=0;x<32;x+=1) lnPush(lnNameTab0+(i<<5)+x,1,g1);
// draw some diamond shapes
for (i=8;i<=24;i+=4) for (x=0;x<4;x+=1) {
lnPush(lnNameTab0+(i<<5)+12+x*4,4,g2);
lnPush(lnNameTab0+32+(i<<5)+12+x*4,4,g3);
}
}
// setup bubbles
type=0;
for (i=0;i<maxDrawObjects;i+=1) {
DrawObject* d=&dTab[i];
d->f=dfAlive;
d->s=bubbleTab[type];
type+=1; if (type>=6) type=0;
d->x=80<<F; d->vx=i; d->ax=1;
d->y=24<<F; d->vy=1<<F; d->ay=1;
}
while(1) {
DrawObject* d;
lnScroll(0,0); // reset scrolling offsets of area above split
lnScroll(hScroll>>6,32); // set scrolling offsets of area below split
hVel+=hDir; if (hVel>64 || hVel<-64) hDir=-hDir;
hScroll+=hVel;
// add 1st sprite (SPR0) at a fixed position on top of background blocks
// mandatory for the SPR0HIT check later with lnSync(lfSplit)
lnAddSpr(&bubbles[bubbleTab[5]],16,16);
lnSpr0Wait=55; // delay the set of scrolling registers a bit
// move bubbles
for (i=0,d=dTab; i<objects ;i+=1,++d) if (d->f&dfAlive) {
d->vx+=d->ax;
if (d->vx<-32) d->ax=1; else if (d->vx>32) d->ax=-1;
if (d->y>216<<F) d->vy=-1<<F; else if (d->y<24<<F) d->vy=1<<F;
d->x+=d->vx;
d->y+=d->vy;
lnAddSpr(&bubbles[d->s],d->x>>F,d->y>>F);
}
// activate new bubble?
tics+=1; if (tics>8) {
tics=0;
if (0==stopIt && objects<maxDrawObjects) objects+=1;
}
// display the amount of moving bubbles + spr0-bubble
Print(lnNameTab0+32,objects+1);
i=lnSync(lfSplit)&31; // sync with vblank, activate SPR0HIT splitscreen
// lnSync() returns the number of vblank NMI's that have been triggered
// since the last call of lnSync(). If the program code is too slow to
// move all objects in one frame (i>1), then we reduce the amount of
// objects (objects-=1) and everything moves with 60 fps again.
if (i>1 && objects>0) { objects-=1; stopIt=1; } // stop adding objects?
}
return 0;
}

View File

@ -1,23 +0,0 @@
// lazyNES lazyhello demo
// 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 alpha 2 http://www.ibaug.de/vbcc/vbcc6502_2.zip
#pragma target(nes)
#pragma emulator("java -jar c:/c64/Nintaco/Nintaco.jar")
#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
while(1)
lnSync(0); // sync with vblank, unblank screen
return 0;
}

View File

@ -1,286 +0,0 @@
// 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>
// Tile Set (in CHR ROM)
#pragma data_seg(Tiles)
export char TILES[] = kickasm(resource "example.chr", resource "sprites.chr") {{
.import binary "example.chr"
.import binary "sprites.chr"
}};
// 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
};
// 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);
// move all sprites off the screen
for(char i=0;i!=0x40;i++)
SPRITE_BUFFER[i].y = 0xff;
// Enable screen rendering and vblank
// Set sprite tileset to upper - enable vblank NMI
PPU->PPUCTRL = 0b10001000;
// Enable sprite and tile rendering
PPU->PPUMASK = 0b00011110;
// Reset scroll
scroll_x = scroll_y = 0;
// Execute lazynes main code
lnMain();
// Infinite loop
while(1) ;
}
// 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];
// Data (in PRG ROM)
#pragma data_seg(Data)
// Index of the next SpriteData in SPRITE_BUFFER to write to in lnAddSpr()
volatile char add_sprite_idx;
// Counts the vblanks between syncs
volatile char vblank_count;
// The current mode set by lnSync.
// lfBlank = 1, activates blank mode, blanks screen and allows lnPush() calls
// lfSplit = 2 activates split mode, NMI waits for SPR0HIT and sets registers
volatile char sync_mode;
// Signal when vblank occurs. The NMI sets this to zero. To wait for a vblank set this no non-zero and wait for it to become zero.
volatile char vblank_signal;
// Scroll x-position
volatile char scroll_x;
// Scroll y-position
volatile char scroll_y;
// Update list with data to be moved to VRAM during blank
// The data is moved by when lnSync() is called
// - 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
// See https://nesdoug.com/2017/04/13/my-neslib-notes/
char * volatile vram_update_list;
// NMI Called when the PPU refreshes the screen (also known as the V-Blank period)
__interrupt(hardware_clobber) void vblank() {
// Transfer any queued data to the PPU
lnListTransfer();
// DMA transfer the entire sprite buffer to the PPU
ppuSpriteBufferDmaTransfer(SPRITE_BUFFER);
// Set scroll
PPU->PPUSCROLL = scroll_x;
PPU->PPUSCROLL = scroll_y;
// count vblanks
vblank_count++;
// send vblank signal
vblank_signal = 0;
}
// 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
// lfBlank = 0, disables blank mode (if set), wait for vblank
// lfBlank = 1, activates blank mode, blanks screen and allows lnPush() calls
// lfSplit = 2 activates split mode, NMI waits for SPR0HIT and sets registers
ubyte lnSync(ubyte flags) {
// Is video output enabled (lfBlank==0)
if(!(flags&lfBlank)) {
// Move remaining sprites off the screen
for(char i=add_sprite_idx;i!=0x40;i++)
SPRITE_BUFFER[i].y = 0xff;
// Reset add sprite index
add_sprite_idx = 0;
// Enable video output (if it was disabled)
if(sync_mode&lfBlank) {
// Set sprite tileset to upper - enable vblank NMI
PPU->PPUCTRL = 0b10001000;
// Enable sprite and tile rendering
PPU->PPUMASK = 0b00011110;
}
}
// Wait for V-Blank signal from the NMI (if vblank is currently enabled)
if(!(sync_mode&lfBlank)) {
vblank_signal = 0xff;
while(vblank_signal) ;
}
// Disable video output if lfBlank set
if(flags&lfBlank) {
// lfBlank = 1, activates blank mode, blanks screen and allows lnPush() calls
// Disable vertical blank interrupt
PPU->PPUCTRL = 0;
// Disable sprite rendering
PPU->PPUMASK = 0;
}
// Update the mode
sync_mode = flags;
// TODO: Handle lfSplit = 2 : activates split mode, NMI waits for SPR0HIT and sets registers
// Return number of vblank since last sync
char res = vblank_count;
vblank_count = 0;
return res;
}
// 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);
}
// Plan a write of data into nametables, palettes, CHRRAM, etc.
// The data will be written next time lnSync() is called.
// (Screen has to be visible, doesn't work in blank mode!)
// updateList: Pointer to update list
//
// 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
// See https://nesdoug.com/2017/04/13/my-neslib-notes/
void lnList(void* update_list) {
vram_update_list = update_list;
}
// Execute any planned transfer of data into nametables, palettes, CHRRAM, etc. by lnList()
void lnListTransfer() {
if(vram_update_list) {
// Index into the update list (assumes no more than 256 bytes)
char idx = 0;
for(;;) {
char addrHi = vram_update_list[idx++];
// Have we reached the end of the lsit
if(addrHi==0xff) break;
if(addrHi&lfHor) {
// The write is horizontal
char addrLo = vram_update_list[idx++];
char* ppuAddr = (char*)(uword){ addrHi&0x3f, addrLo };
char size = vram_update_list[idx++];
ppuDataTransfer(ppuAddr, vram_update_list+idx, size);
idx += size;
} else if(addrHi&lfVer) {
// The write is vertical
char addrLo = vram_update_list[idx++];
char* ppuAddr = (char*)(uword){ addrHi&0x3f, addrLo };
char size = vram_update_list[idx++];
// Set vertical mode bit in PPUCTRL
PPU->PPUCTRL = 0b10001100;
ppuDataTransfer(ppuAddr, vram_update_list+idx, size);
// restore PPUCTRL
PPU->PPUCTRL = 0b10001000;
idx += size;
} else {
// The write is single-byte
char addrLo = vram_update_list[idx++];
char* ppuAddr = (char*)(uword){ addrHi, addrLo };
char value = vram_update_list[idx++];
ppuDataSet(ppuAddr, value);
}
}
// Set update list to zero
vram_update_list = 0;
}
}
// Scroll background
// x: New horizotnal scrolling offset in pixels, allowed range: [0..511]
// y: New vertical scrolling offset in pixels, allowed range: [0..479]
//
// 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.
void lnScroll(uword x, uword y) {
scroll_x = <x;
scroll_y = <y;
}
// 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
// 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
ubyte lnAddSpr(void* p, sword x, sword y) {
char* ptr = p;
while(*ptr!=128) {
SPRITE_BUFFER[add_sprite_idx].x = (char) (x+ptr[0]);
SPRITE_BUFFER[add_sprite_idx].y = (char) (y+ptr[1]);
SPRITE_BUFFER[add_sprite_idx].tile = ptr[2];
SPRITE_BUFFER[add_sprite_idx].attributes = ptr[3];
// Temporary debug code to detect bug with vblanking in balloon.c
if(add_sprite_idx>=6) SPRITE_BUFFER[add_sprite_idx].tile = 6;
ptr+=4;
add_sprite_idx++;
}
return add_sprite_idx*4;
}
// Query joypad state
// port: Joypad port (1 or 2)
// result: Set of joypad flags (see below)
//
ubyte lnGetPad(ubyte port) {
return readJoy1();
}
//
// advanced usage
//
// TODO: __zp volatile ubyte
// TODO: lnSpr0Wait, // delay until scroll registers will be set after a SPR0HIT
// TODO: lnPPUCTRL, // current value of PPUCTRL register (will be written in NMI)
// TODO: 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

@ -1,107 +0,0 @@
// 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;
// RESET Called when the NES is reset, including when it is turned on.
void main();
// NMI Called when the PPU refreshes the screen (also known as the V-Blank period)
__interrupt(hardware_clobber) void vblank();
// 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
//
// 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
// See https://nesdoug.com/2017/04/13/my-neslib-notes/
void lnList(void* update_list);
// Constants used to control VRAM updates in the list passed to lnList()
enum { lfHor=64, lfVer=128, lfEnd=255 };
// Common offsets for lnPush() and lnList()
const uword 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]
// 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.
void lnScroll(uword x, uword y);
// 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
//
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)
//
ubyte lnGetPad(ubyte port);
enum { lfU=8, lfD=4, lfL=2, lfR=1, lfA=128, lfB=64, lfStart=16, lfSelect=32 };
//
// advanced usage
//
// TODO: extern __zp volatile ubyte
// TODO: lnSpr0Wait, // delay until scroll registers will be set after a SPR0HIT
// TODO: lnPPUCTRL, // current value of PPUCTRL register (will be written in NMI)
// TODO: 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

@ -1,54 +0,0 @@
// lazyNES lnlist demo
// lazyNES - As lazy as possible NES hardware support library for vbcc6502
// V1.0, 'Lazycow 2020
// Ported to KickC 2020 by Jesper Gravgaard
// Original Source VBCC alpha 2 http://www.ibaug.de/vbcc/vbcc6502_2.zip
#pragma target(nes)
#pragma emulator("java -jar c:/c64/Nintaco/Nintaco.jar")
#include "lazynes.h"
#pragma data_seg(GameRam)
ubyte list[64];
#pragma data_seg(Data)
void lnMain() {
// Set-up an update list in the lnList() format
uword offset1 = lnNameTab0+96+4;
uword offset2 = lnNameTab0+96+2;
uword offset3 = lnNameTab0+160+2;
list[0]=(ubyte)(offset1>>8)|lfHor; // PPU address hi - horizontal
list[1]=(ubyte)offset1; // PPU address lo
list[2]=3; // Size
list[3] = 'R'; // Data
list[4] = 'X'; // Data
list[5] = '0'; // Data
list[6]=(ubyte)(offset2>>8); // PPU address hi - single
list[7]=(ubyte)offset2; // PPU address lo
list[8]='X'; // Size
list[9]=(ubyte)(offset3>>8)|lfVer; // PPU address hi - vertical
list[10]=(ubyte)offset3; // PPU address lo
list[11]=3; // Size
list[12] = 'R'; // Data
list[13] = 'X'; // Data
list[14] = '0'; // Data
list[15] = lfEnd; // End transfer
static const ubyte bgColors[]={45,33,2};
static const char text[]="HELLO LAZYNES!";
lnSync(lfBlank); // blank screen to enable lnPush() usage
lnPush(lnBackCol,3,bgColors); // set background colors
lnPush(lnNameTab0+32,14,text); // draw text in 2nd line
for (;;) {
// Update the list with dynamic values
list[5] = ((list[5]+1)&7)+'0';
list[8] = ((list[8]+1)&7)+'0';
list[14] = ((list[14]+1)&7)+'0';
lnList(list); // Send the list
lnSync(0); // sync with vblank
}
}

View File

@ -1,55 +0,0 @@
// lazyNES print demo (using lnlist)
// 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 alpha 2 http://www.ibaug.de/vbcc/vbcc6502_2.zip
#pragma target(nes)
#pragma emulator("java -jar c:/c64/Nintaco/Nintaco.jar")
#include "lazynes.h"
// A string in RAM
#pragma data_seg(GameRam)
ubyte b[14];
// A string in ROM
#pragma data_seg(Data)
ubyte b_init[]={0,0,10,'B','U','B','B','L','E','S',':',0,0,lfEnd};
// print some text in the static area using lnList
void Print(uword offset, ubyte value) {
b[0]=(ubyte)(offset>>8)|lfHor; b[1]=(ubyte)offset; b[11]=b[12]='0';
while (value>=10) { ++b[11]; value-=10; }
while (value>=1) { ++b[12]; value-=1; }
lnList(b);
}
int lnMain() {
static const ubyte bgColors[]={45,33,2};
ubyte objects=17;
uword tics=0;
lnSync(lfBlank); // blank screen to enable lnPush() usage
lnPush(lnBackCol,3,bgColors); // set background colors
// Copy string from ROM to RAM
for(char i=0;i<sizeof(b_init);i++)
b[i] = b_init[i];
static const char text[]="HELLO LAZYNES!";
lnPush(lnNameTab0+32, sizeof(text)-1, text); // draw text in 2nd line
for(;;) {
// update the number to display
tics+=1;
if (tics>8) {
tics=0;
objects = (objects+1)&0x3f;
}
// display a number
Print(lnNameTab0+64,objects+1);
lnSync(0); // sync with vblank
}
return 0;
}

View File

@ -1,39 +0,0 @@
// lazyNES scroll demo
// 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 alpha 2 http://www.ibaug.de/vbcc/vbcc6502_2.zip
#pragma target(nes)
#pragma emulator("java -jar c:/c64/Nintaco/Nintaco.jar")
#include "lazynes.h"
int lnMain() {
static const ubyte bgColors[]={45,33,2};
ubyte i;
uword hScroll=0;
sbyte hVel=0, hDir=1;
lnSync(lfBlank); // blank screen to enable lnPush() usage
lnPush(lnBackCol,3,bgColors); // set background colors
// draw some backgrounds
{ //
static const ubyte g1[]={1}, g2[]={2,3,4,5}, g3[]={6,7,8,9};
ubyte x;
// draw a line
i=2; for (x=0;x<32;x+=1) lnPush(lnNameTab0+((uword)i<<5)+x,1,g1);
// draw some diamond shapes
for (i=8;i<=24;i+=4) for (x=0;x<4;x+=1) {
lnPush(lnNameTab0+((uword)i<<5)+12+x*4,4,g2);
lnPush(lnNameTab0+32+((uword)i<<5)+12+x*4,4,g3);
}
}
for(;;) {
lnScroll(hScroll>>6,0); // set scrolling offsets
hVel+=hDir; if (hVel>64 || hVel<-64) hDir=-hDir;
hScroll+=hVel;
lnSync(0);
}
return 0;
}

View File

@ -1,68 +0,0 @@
// lazyNES spite 0 hit demo. Demonstrates sprite 0 raster NMI with different scroll values
// 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 alpha 2 http://www.ibaug.de/vbcc/vbcc6502_2.zip
#pragma target(nes)
#pragma emulator("java -jar c:/c64/Nintaco/Nintaco.jar")
#include "lazynes.h"
int lnMain() {
static const ubyte bgColors[]={45,33,2};
static const ubyte bubblesPal[]={ 0, 37,22,5, 0, 42,26,10, 0, 33,18,3 };
static const ubyte bubbles[]={ // x-offset, y-offset, tile, palette+flags
0,0,7,0, 8,0,9,0, 128, // 16x16, red, offset 0
0,0,7,1, 8,0,9,1, 128, // 16x16, green, offset 9
0,0,7,2, 8,0,9,2, 128, // 16x16, blue, offest 18
0,4,11,0,128, // 8x8, red, offset 27
0,4,11,1,128, // 8x8, green, offset 32
0,4,10,2,128, // 8x8, blue, offset 37
},
bubbleTab[]={0,27,9,32,18,37}; // start offsets in bubbles[]
static const ubyte bubble0[] = { 0,4,10,2,128 }; // x-offset, y-offset, tile, palette+flags
ubyte i;
uword hScroll=0;
sbyte hVel=0, hDir=1;
lnSync(lfBlank); // blank screen to enable lnPush() usage
lnPush(lnBackCol,3,bgColors); // set background colors
lnPush(lnSprPal0,3,&bubblesPal[1]); // set sprite colors
lnPush(lnSprPal1,3,&bubblesPal[5]);
lnPush(lnSprPal2,3,&bubblesPal[9]);
//TODO: lnPPUCTRL|=32; // Select 8x16 sprites; has to be set after calling lnSync()!
{
static const ubyte g1[]={1}, g2[]={2,3,4,5}, g3[]={6,7,8,9};
ubyte x;
// draw a line
i=2; for (x=0;x<32;x+=1) lnPush(lnNameTab0+((uword)i<<5)+x,1,g1);
// draw some diamond shapes
for (i=8;i<=24;i+=4) for (x=0;x<4;x+=1) {
lnPush(lnNameTab0+((uword)i<<5)+12+x*4,4,g2);
lnPush(lnNameTab0+32+((uword)i<<5)+12+x*4,4,g3);
}
}
while(1) {
lnScroll(0,0); // reset scrolling offsets of area above split
//lnScroll(hScroll>>6,32); // set scrolling offsets of area below split
hVel+=hDir; if (hVel>64 || hVel<-64) hDir=-hDir;
hScroll+=hVel;
// add 1st sprite (SPR0) at a fixed position on top of background blocks
// mandatory for the SPR0HIT check later with lnSync(lfSplit)
lnAddSpr(&bubbles[bubbleTab[5]],16,16);
//TODO: lnSpr0Wait=55; // delay the set of scrolling registers a bit
lnSync(lfSplit); // sync with vblank, activate SPR0HIT splitscreen
}
return 0;
}

View File

@ -1,27 +0,0 @@
// Display MEDUSA PETSCII by Buzz_clik
// https://csdb.dk/release/?id=178673
#include <c64.h>
#include <string.h>
char MEDUSA_SCREEN[1000] = kickasm(resource "medusas.prg" ) {{
.var fileScreen = LoadBinary("medusas.prg", BF_C64FILE)
.fill fileScreen.getSize(), fileScreen.get(i)
}};
char MEDUSA_COLORS[] = kickasm(resource "medusac.prg" ) {{
.var fileCols = LoadBinary("medusac.prg", BF_C64FILE)
.fill fileCols.getSize(), fileCols.get(i)
}};
byte* SCREEN = 0x0400;
void main() {
*BG_COLOR = BLACK;
memcpy(SCREEN, MEDUSA_SCREEN, 1000);
memcpy(COLS, MEDUSA_COLORS, 1000);
while(true) {
(*(SCREEN+999)) ^= 0x0e;
}
}

View File

@ -1,161 +0,0 @@
#pragma target(nes)
//#pragma emulator("java -jar /Applications/Nintaco_bin_2020-05-01/Nintaco.jar")
#include <nes.h>
#include <stdio.h>
#define MAX_BALLS 32
#define WEIGHT 0x0010
#define RELEASE_TIMER 0x09
#define poke(addr) (*(unsigned byte *)(addr))
typedef struct
{
unsigned short x_position;
unsigned short y_position;
unsigned short x_velocity;
unsigned short y_velocity;
} ball;
#pragma data_seg(GameRam)
// Moving balls (in GameRAM)
ball balls[64];
#pragma data_seg(Data)
static const unsigned char sine_table[256] = {
0x40,0x42,0x43,0x45,0x46,0x48,0x49,0x4b,0x4c,0x4e,0x50,0x51,0x53,0x54,0x56,0x57,
0x58,0x5a,0x5b,0x5d,0x5e,0x60,0x61,0x62,0x64,0x65,0x66,0x67,0x69,0x6a,0x6b,0x6c,
0x6d,0x6e,0x6f,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x78,0x79,0x7a,0x7b,
0x7b,0x7c,0x7c,0x7d,0x7d,0x7e,0x7e,0x7e,0x7f,0x7f,0x7f,0x80,0x80,0x80,0x80,0x80,
0x80,0x80,0x80,0x80,0x80,0x80,0x7f,0x7f,0x7f,0x7e,0x7e,0x7e,0x7d,0x7d,0x7c,0x7c,
0x7b,0x7b,0x7a,0x79,0x78,0x78,0x77,0x76,0x75,0x74,0x73,0x72,0x71,0x70,0x6f,0x6e,
0x6d,0x6c,0x6b,0x6a,0x69,0x67,0x66,0x65,0x64,0x62,0x61,0x60,0x5e,0x5d,0x5b,0x5a,
0x58,0x57,0x56,0x54,0x53,0x51,0x50,0x4e,0x4c,0x4b,0x49,0x48,0x46,0x45,0x43,0x42,
0x40,0x3e,0x3d,0x3b,0x3a,0x38,0x37,0x35,0x34,0x32,0x30,0x2f,0x2d,0x2c,0x2a,0x29,
0x28,0x26,0x25,0x23,0x22,0x20,0x1f,0x1e,0x1c,0x1b,0x1a,0x19,0x17,0x16,0x15,0x14,
0x13,0x12,0x11,0x10,0xf,0xe,0xd,0xc,0xb,0xa,0x9,0x8,0x8,0x7,0x6,0x5,
0x5,0x4,0x4,0x3,0x3,0x2,0x2,0x2,0x1,0x1,0x1,0x0,0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x2,0x2,0x2,0x3,0x3,0x4,0x4,
0x5,0x5,0x6,0x7,0x8,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,0x10,0x11,0x12,
0x13,0x14,0x15,0x16,0x17,0x19,0x1a,0x1b,0x1c,0x1e,0x1f,0x20,0x22,0x23,0x25,0x26,
0x28,0x29,0x2a,0x2c,0x2d,0x2f,0x30,0x32,0x34,0x35,0x37,0x38,0x3a,0x3b,0x3d,0x3e
};
static const unsigned char object[5] = { 0, 0, 10, 3, 128 };
const unsigned char palette[] = { 0x34,0x24,0x14,0x04, 0x34,0x24,0x14,0x04, 0x34,0x24,0x14,0x04, 0x34,0x24,0x14,0x04, 0x34,0x24,0x14,0x04, 0x34,0x24,0x14,0x04, 0x34,0x24,0x14,0x04, 0x34,0x24,0x14,0x04 };
const unsigned char h_bar_tilemap[] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 };
volatile unsigned char scroll_y = 0;
volatile unsigned char vblank_hit = 0;
int main(void)
{
// Initialize NES after RESET
initNES();
// Transfer the palette
ppuDataTransfer(PPU_PALETTE, palette, sizeof(palette));
// Fill the PPU attribute table
ppuDataFill(PPU_NAME_TABLE_0, 0, 32*30);
ppuDataFill(PPU_ATTRIBUTE_TABLE_0, 0, 0x40);
ppuDataTransfer(0x2040, h_bar_tilemap, sizeof(h_bar_tilemap));
// Enable screen rendering and vblank
enableVideoOutput();
// Enable vertical blank interrupt, select sprite pattern table 1
PPU->PPUCTRL = 0b10001000;
unsigned char i;
unsigned char active_balls = 0;
unsigned char timer = 0;
unsigned char timer_2 = 0;
unsigned char h_bar = 0x80;
unsigned char sprite_idx = 0;
for (i = 0; i < MAX_BALLS; i++)
{
balls[i].x_velocity = rand() & 0x3FF;
balls[i].y_velocity = rand() & 0x0FF;
}
while (1)
{
timer_2++;
h_bar = sine_table[timer_2] + 0x60;
scroll_y = h_bar ^ 0xFF;
if (active_balls < MAX_BALLS)
{
if (timer++ == RELEASE_TIMER)
{
timer = 0;
active_balls++;
balls[active_balls].x_position = 0;
balls[active_balls].y_position = 0;
}
}
sprite_idx = 0;
for (i = 0; i < active_balls; i++)
{
balls[i].x_position += balls[i].x_velocity;
balls[i].y_position += (balls[i].y_velocity += WEIGHT);
if ((balls[i].x_position >> 8) < 8)
{
balls[i].x_velocity ^= 0xFFFF;
}
if (((balls[i].y_position >> 8) >= h_bar) && (balls[i].y_position >> 8) < h_bar + 8)
{
balls[i].y_velocity ^= 0xFFFF;
balls[i].y_position = ((unsigned short)(h_bar - 2) << 8);
}
SPRITE_BUFFER[sprite_idx].y = (unsigned char) (balls[i].y_position >> 8);
SPRITE_BUFFER[sprite_idx].tile = 0x0a;
SPRITE_BUFFER[sprite_idx].attributes = 3;
SPRITE_BUFFER[sprite_idx].x = (unsigned char) (balls[i].x_position >> 8);
sprite_idx++;
}
poke(0x2001) = 0x98;
while (!vblank_hit); // wait for vblank
vblank_hit = 0;
poke(0x2001) = 0x18;
}
return 0;
}
// NMI Called when the PPU refreshes the screen (also known as the V-Blank period)
__interrupt(hardware_clobber) void vblank() {
// Set scroll
PPU->PPUSCROLL = 0;
PPU->PPUSCROLL = scroll_y;
// DMA transfer the entire sprite buffer to the PPU
ppuSpriteBufferDmaTransfer(SPRITE_BUFFER);
vblank_hit++;
}
// Tile Set (in CHR ROM)
#pragma data_seg(Tiles)
export char TILES[] = kickasm(resource "lazydata.chr") {{
.import binary "lazydata.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[0x100];
// 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

@ -1,161 +0,0 @@
#pragma target(nes)
#pragma emulator("java -jar /Applications/Nintaco_bin_2020-05-01/Nintaco.jar")
#include <nes.h>
#include <stdio.h>
#define MAX_BALLS 50
#define WEIGHT 0x0010
#define RELEASE_TIMER 0x09
#define poke(addr) (*(unsigned byte *)(addr))
typedef struct
{
unsigned short x_position;
unsigned short y_position;
unsigned short x_velocity;
unsigned short y_velocity;
} ball;
#pragma data_seg(GameRam)
// Moving balls (in GameRAM)
ball balls[64];
#pragma data_seg(Data)
static const unsigned char sine_table[256] = {
0x40,0x42,0x43,0x45,0x46,0x48,0x49,0x4b,0x4c,0x4e,0x50,0x51,0x53,0x54,0x56,0x57,
0x58,0x5a,0x5b,0x5d,0x5e,0x60,0x61,0x62,0x64,0x65,0x66,0x67,0x69,0x6a,0x6b,0x6c,
0x6d,0x6e,0x6f,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x78,0x79,0x7a,0x7b,
0x7b,0x7c,0x7c,0x7d,0x7d,0x7e,0x7e,0x7e,0x7f,0x7f,0x7f,0x80,0x80,0x80,0x80,0x80,
0x80,0x80,0x80,0x80,0x80,0x80,0x7f,0x7f,0x7f,0x7e,0x7e,0x7e,0x7d,0x7d,0x7c,0x7c,
0x7b,0x7b,0x7a,0x79,0x78,0x78,0x77,0x76,0x75,0x74,0x73,0x72,0x71,0x70,0x6f,0x6e,
0x6d,0x6c,0x6b,0x6a,0x69,0x67,0x66,0x65,0x64,0x62,0x61,0x60,0x5e,0x5d,0x5b,0x5a,
0x58,0x57,0x56,0x54,0x53,0x51,0x50,0x4e,0x4c,0x4b,0x49,0x48,0x46,0x45,0x43,0x42,
0x40,0x3e,0x3d,0x3b,0x3a,0x38,0x37,0x35,0x34,0x32,0x30,0x2f,0x2d,0x2c,0x2a,0x29,
0x28,0x26,0x25,0x23,0x22,0x20,0x1f,0x1e,0x1c,0x1b,0x1a,0x19,0x17,0x16,0x15,0x14,
0x13,0x12,0x11,0x10,0xf,0xe,0xd,0xc,0xb,0xa,0x9,0x8,0x8,0x7,0x6,0x5,
0x5,0x4,0x4,0x3,0x3,0x2,0x2,0x2,0x1,0x1,0x1,0x0,0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x2,0x2,0x2,0x3,0x3,0x4,0x4,
0x5,0x5,0x6,0x7,0x8,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,0x10,0x11,0x12,
0x13,0x14,0x15,0x16,0x17,0x19,0x1a,0x1b,0x1c,0x1e,0x1f,0x20,0x22,0x23,0x25,0x26,
0x28,0x29,0x2a,0x2c,0x2d,0x2f,0x30,0x32,0x34,0x35,0x37,0x38,0x3a,0x3b,0x3d,0x3e
};
static const unsigned char object[5] = { 0, 0, 10, 3, 128 };
const unsigned char palette[] = { 0x34,0x24,0x14,0x04, 0x34,0x24,0x14,0x04, 0x34,0x24,0x14,0x04, 0x34,0x24,0x14,0x04, 0x34,0x24,0x14,0x04, 0x34,0x24,0x14,0x04, 0x34,0x24,0x14,0x04, 0x34,0x24,0x14,0x04 };
const unsigned char h_bar_tilemap[] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 };
volatile unsigned char scroll_y = 0;
volatile unsigned char vblank_hit = 0;
int main(void)
{
// Initialize NES after RESET
initNES();
// Transfer the palette
ppuDataTransfer(PPU_PALETTE, palette, sizeof(palette));
// Fill the PPU attribute table
ppuDataFill(PPU_NAME_TABLE_0, 0, 32*30);
ppuDataFill(PPU_ATTRIBUTE_TABLE_0, 0, 0x40);
ppuDataTransfer(0x2040, h_bar_tilemap, sizeof(h_bar_tilemap));
// Enable screen rendering and vblank
enableVideoOutput();
// Enable vertical blank interrupt, select sprite pattern table 1
PPU->PPUCTRL = 0b10001000;
unsigned short i;
unsigned short active_balls = 0;
unsigned char timer = 0;
unsigned char timer_2 = 0;
unsigned char h_bar = 0x80;
unsigned char sprite_idx = 0;
for (i = 0; i < MAX_BALLS; i++)
{
balls[i].x_velocity = rand() & 0x3FF;
balls[i].y_velocity = rand() & 0x0FF;
}
while (1)
{
timer_2++;
h_bar = sine_table[timer_2] + 0x60;
scroll_y = h_bar ^ 0xFF;
if (active_balls < MAX_BALLS)
{
if (timer++ == RELEASE_TIMER)
{
timer = 0;
active_balls++;
balls[active_balls].x_position = 0;
balls[active_balls].y_position = 0;
}
}
sprite_idx = 0;
for (i = 0; i < active_balls; i++)
{
balls[i].x_position += balls[i].x_velocity;
balls[i].y_position += (balls[i].y_velocity += WEIGHT);
if ((balls[i].x_position >> 8) < 8)
{
balls[i].x_velocity ^= 0xFFFF;
}
if (((balls[i].y_position >> 8) >= h_bar) && (balls[i].y_position >> 8) < h_bar + 8)
{
balls[i].y_velocity ^= 0xFFFF;
balls[i].y_position = ((unsigned short)(h_bar - 2) << 8);
}
SPRITE_BUFFER[sprite_idx].y = (unsigned char) (balls[i].y_position >> 8);
SPRITE_BUFFER[sprite_idx].tile = 0x0a;
SPRITE_BUFFER[sprite_idx].attributes = 3;
SPRITE_BUFFER[sprite_idx].x = (unsigned char) (balls[i].x_position >> 8);
sprite_idx++;
}
poke(0x2001) = 0x98;
while (!vblank_hit); // wait for vblank
vblank_hit = 0;
poke(0x2001) = 0x18;
}
return 0;
}
// NMI Called when the PPU refreshes the screen (also known as the V-Blank period)
__interrupt(hardware_clobber) void vblank() {
// Set scroll
PPU->PPUSCROLL = 0;
PPU->PPUSCROLL = scroll_y;
// DMA transfer the entire sprite buffer to the PPU
ppuSpriteBufferDmaTransfer(SPRITE_BUFFER);
vblank_hit++;
}
// Tile Set (in CHR ROM)
#pragma data_seg(Tiles)
export char TILES[] = kickasm(resource "lazydata.chr") {{
.import binary "lazydata.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[0x100];
// 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

@ -1,278 +0,0 @@
// Pre-calculated bobs inside a charset (pre-moved to all x/y-combinations)
#include <c64.h>
#include <string.h>
#include <keyboard.h>
#include <time.h>
#include <print.h>
#include <fastmultiply.h>
// The prototype BOB (a 3x3 char image with a bob image in the upper 2x2 chars)
// The chars are layout as follows with data in chars 0, 1, 3, 4 initially
// 0 3 6
// 1 4 7
// 2 5 8
const char PROTO_BOB[3*3*8] = kickasm(resource "smiley.png") {{
.var pic = LoadPicture("smiley.png", List().add($000000, $ffffff))
.for (var x=0;x<3; x++)
.for (var y=0; y<24; y++)
.byte pic.getSinglecolorByte(x,y)
}};
// Sine and Cosine tables
// Angles: $00=0, $80=PI,$100=2*PI
// Sine/Cosine: signed fixed [-$7f,$7f]
signed char __align(0x40) SIN[0x140] = kickasm {{
.for(var i=0;i<$140;i++)
.byte >round($7fff*sin(i*2*PI/256))
}};
signed char* COS = SIN+$40; // sin(x) = cos(x+PI/2)
// The BASIC screen
char* const BASIC_SCREEN = 0x0400;
// The BASIC charset
char* const BASIC_CHARSET = 0x1000;
// The BOB screen
char* const BOB_SCREEN = 0x2800;
// The BOB charset
char* const BOB_CHARSET = 0x2000;
// Tables containing the char to use for a specific cell of a shifted BOB.
// char_id = BOB_TABLES[cell*BOB_SUBTABLE_SIZE + shift_y*BOB_SHIFTS_X + shift_x];
char BOB_TABLES[9*8*4];
// The number of different X-shifts
const char BOB_SHIFTS_X = 4;
// The number of different Y-shifts
const char BOB_SHIFTS_Y = 8;
// The size of a sub-table of BOB_TABLES
const char BOB_SUBTABLE_SIZE = BOB_SHIFTS_X*BOB_SHIFTS_Y;
// The number of BOBs to render
const char NUM_BOBS = 25;
void main() {
mulf_init();
prepareBobs();
renderBobInit();
vicSelectGfxBank(BOB_SCREEN);
*D018 = toD018(BOB_SCREEN, BOB_CHARSET);
// Clear screen
memset(BOB_SCREEN, 0x00, 1000);
// Origin point
int origX = 0x0a00;
int origY = 0x0a00;
// Row and column offset vectors
int rowOffsetX = 0x0c00;
int rowOffsetY = 0x0100;
int colOffsetX = 0x0100;
int colOffsetY = 0x1800;
// Render Grid of BOBs
while(true) {
do { } while (*RASTER<$f8);
*BORDER_COLOR = 0xf;
renderBobCleanup();
int rowX = origX;
int rowY = origY;
for(char col: 0..4) {
int x = rowX;
int y = rowY;
for(char row: 0..4) {
//kickasm {{ .break }}
*BORDER_COLOR = 1;
renderBob(>x, >y);
x += rowOffsetX;
y += rowOffsetY;
*BORDER_COLOR = 2;
}
rowX += colOffsetX;
rowY += colOffsetY;
}
origX += 0x0100;
rowOffsetY += 0x0080;
*BORDER_COLOR = 0;
if(keyboard_key_pressed(KEY_SPACE)) {
break;
}
}
// Wait for space release
while(keyboard_key_pressed(KEY_SPACE)) {}
// Return to BASIC
vicSelectGfxBank(BASIC_SCREEN);
*D018 = toD018(BASIC_SCREEN, BASIC_CHARSET);
}
// Table used for deleting rendered BOB's. Contains pointers to first char of each BOB.
char* RENDERBOB_CLEANUP[NUM_BOBS];
// Pointer to the next clean-up to add
char** renderBobCleanupNext;
// *40 Table unsigned int MUL40[0x20] = { ((unsigned int)i)*40 };
unsigned int MUL40[0x20];
// Initialize the tables used by renderBob()
void renderBobInit() {
for(char y: 0..0x1f)
MUL40[y] = ((unsigned int)y)*40;
for(char i: 0..NUM_BOBS-1)
RENDERBOB_CLEANUP[i] = BOB_SCREEN;
}
// Render a single BOB at a given x/y-position
// X-position is 0-151. Each x-position is 2 pixels wide.
// Y-position is 0-183. Each y-position is 1 pixel high.
void renderBob(char xpos, char ypos) {
char x_char_offset = xpos/BOB_SHIFTS_X;
char y_char_offset = ypos/BOB_SHIFTS_Y;
unsigned int y_offset = MUL40[y_char_offset];
char* screen = BOB_SCREEN+y_offset+x_char_offset;
char bob_table_idx = (ypos&7)*BOB_SHIFTS_X+(xpos&3);
*renderBobCleanupNext++ = screen;
screen[0] = (BOB_TABLES+0*BOB_SUBTABLE_SIZE)[bob_table_idx];
screen[40] = (BOB_TABLES+1*BOB_SUBTABLE_SIZE)[bob_table_idx];
screen[80] = (BOB_TABLES+2*BOB_SUBTABLE_SIZE)[bob_table_idx];
screen[1] = (BOB_TABLES+3*BOB_SUBTABLE_SIZE)[bob_table_idx];
screen[41] = (BOB_TABLES+4*BOB_SUBTABLE_SIZE)[bob_table_idx];
screen[81] = (BOB_TABLES+5*BOB_SUBTABLE_SIZE)[bob_table_idx];
screen[2] = (BOB_TABLES+6*BOB_SUBTABLE_SIZE)[bob_table_idx];
screen[42] = (BOB_TABLES+7*BOB_SUBTABLE_SIZE)[bob_table_idx];
screen[82] = (BOB_TABLES+8*BOB_SUBTABLE_SIZE)[bob_table_idx];
}
// Clean Up the rendered BOB's
void renderBobCleanup() {
for(char i: 0..NUM_BOBS-1) {
char* screen = RENDERBOB_CLEANUP[i];
screen[0] = 0;
screen[40] = 0;
screen[80] = 0;
screen[1] = 0;
screen[41] = 0;
screen[81] = 0;
screen[2] = 0;
screen[42] = 0;
screen[82] = 0;
}
// Prepare for next clean-up
renderBobCleanupNext = RENDERBOB_CLEANUP;
}
// Creates the pre-shifted bobs into BOB_CHARSET and populates the BOB_TABLES
// Modifies PROTO_BOB by shifting it around
void prepareBobs() {
progress_init(BASIC_SCREEN);
bob_charset_next_id = 0;
// Ensure that glyph #0 is empty
charsetFindOrAddGlyph(PROTO_BOB+48, BOB_CHARSET);
char bob_table_idx = 0;
for(char shift_y=0;shift_y<BOB_SHIFTS_Y;shift_y++) {
for(char shift_x=0;shift_x<BOB_SHIFTS_X;shift_x++) {
// Populate charset and tables
char* bob_glyph = PROTO_BOB;
char* bob_table = BOB_TABLES + bob_table_idx;
for(char cell = 0; cell<9; cell++) {
// Look for an existing char in BOB_CHARSET
*bob_table = charsetFindOrAddGlyph(bob_glyph, BOB_CHARSET);
// Move to the next glyph
bob_glyph+=8;
// Move to the next sub-table
bob_table += BOB_SHIFTS_X*BOB_SHIFTS_Y;
progress_inc();
}
// Move to the next bob table idx
bob_table_idx++;
// Shift PROTO_BOB right twice
protoBobShiftRight();
protoBobShiftRight();
}
// Shift PROTO_BOB down and 8px left
protoBobShiftDown();
}
}
// Shift PROTO_BOB right one X pixel
void protoBobShiftRight() {
char carry = 0;
char j = 0;
for(char i=0;i<3*3*8;i++) {
// Get the new carry (0x80 / 0x00)
char new_carry = (PROTO_BOB[j]&1)?0x80ub:0ub;
// Shift value and add old carry
PROTO_BOB[j] = carry | PROTO_BOB[j]>>1;
// Update carry
carry = new_carry;
// Increment j to iterate over the PROTO_BOB left-to-right, top-to-bottom (0, 24, 48, 1, 25, 49, ...)
if(j>=48) {
j-=47;
} else {
j+=24;
}
}
}
// Shift PROTO_BOB down one Y pixel
// At the same time restore PROTO_BOB X by shifting 8 pixels left
void protoBobShiftDown() {
for(char i=23;i>0;i--) {
PROTO_BOB[i] = (PROTO_BOB+23)[i];
(PROTO_BOB+24)[i] = (PROTO_BOB+47)[i];
(PROTO_BOB+48)[i] = 0x00;
}
PROTO_BOB[0] = 0;
PROTO_BOB[24] = 0;
PROTO_BOB[48] = 0;
}
// BOB charset ID of the next glyph to be added
char bob_charset_next_id;
// Looks through a charset to find a glyph if present. If not present it is added.
// Returns the glyph ID
char charsetFindOrAddGlyph(char* glyph, char* charset) {
char* glyph_cursor = charset;
char glyph_id = 0;
while(glyph_id!=bob_charset_next_id) {
char found = 1;
for(char i=0;i<8;i++) {
if(glyph_cursor[i]!=glyph[i]) {
found = 0;
break;
}
}
if(found) return glyph_id;
glyph_id++;
glyph_cursor +=8;
}
// Not found - add it
for(char i=0;i<8;i++)
glyph_cursor[i]=glyph[i];
bob_charset_next_id++;
return glyph_id;
}
// Current position of the progress cursor
char* progress_cursor;
// Current index within the progress cursor (0-7)
char progress_idx;
// Initialize the PETSCII progress bar
void progress_init(char* line) {
progress_cursor = line;
progress_idx = 0;
}
// Increase PETSCII progress one bit
// Done by increasing the character until the idx is 8 and then moving to the next char
void progress_inc() {
// Progress characters
const char progress_chars[] = { 0x20, 0x65, 0x74, 0x75, 0x61, 0xf6, 0xe7, 0xea, 0xe0 };
if(++progress_idx==8) {
*progress_cursor = progress_chars[8];
progress_cursor++;
progress_idx = 0;
}
*progress_cursor = progress_chars[progress_idx];
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 B

View File

@ -1,284 +0,0 @@
// Pre-calculated bobs inside a charset (pre-moved to all x/y-combinations)
#include <c64.h>
#include <string.h>
#include <keyboard.h>
#include <time.h>
#include <print.h>
#include <fastmultiply.h>
// The prototype BOB (a 3x3 char image with a bob image in the upper 2x2 chars)
// The chars are layout as follows with data in chars 0, 1, 3, 4 initially
// 0 3 6
// 1 4 7
// 2 5 8
const char PROTO_BOB[3*3*8] = kickasm(resource "smiley.png") {{
.var pic = LoadPicture("smiley.png", List().add($000000, $ffffff))
.for (var x=0;x<3; x++)
.for (var y=0; y<24; y++)
.byte pic.getSinglecolorByte(x,y)
}};
// Sine and Cosine tables
// Angles: $00=0, $80=PI,$100=2*PI
// Sine/Cosine: signed fixed [-$7f,$7f]
signed char __align(0x40) SIN[0x140] = kickasm {{
.for(var i=0;i<$140;i++)
.byte >round($7fff*sin(i*2*PI/256))
}};
signed char* COS = SIN+$40; // sin(x) = cos(x+PI/2)
// Vogel Sunflower polar coordinates
__align(0x100) const char VOGEL_THETA[] = kickasm {{
.const PHI = (1+sqrt(5))/2
.fill 100, round(mod(256*i/(PHI*PHI),256))
}};
__align(0x100) const char VOGEL_R[] = kickasm {{ .fill 100, round(sqrt(i)*15) }};
// The BASIC screen
char* const SCREEN_BASIC = 0x0400;
// The BASIC charset
char* const CHARSET_BASIC = 0x1000;
// The BOB screen
char* const BOB_SCREEN = 0x2800;
// The BOB charset
char* const BOB_CHARSET = 0x2000;
// Tables containing the char to use for a specific cell of a shifted BOB.
// char_id = BOB_TABLES[cell*BOB_SUBTABLE_SIZE + shift_y*BOB_SHIFTS_X + shift_x];
char BOB_TABLES[9*8*4];
// The number of different X-shifts
const char BOB_SHIFTS_X = 4;
// The number of different Y-shifts
const char BOB_SHIFTS_Y = 8;
// The size of a sub-table of BOB_TABLES
const char BOB_SUBTABLE_SIZE = BOB_SHIFTS_X*BOB_SHIFTS_Y;
// The number of BOBs to render
const char NUM_BOBS = 20;
void main() {
mulf_init();
prepareBobs();
renderBobInit();
vicSelectGfxBank(BOB_SCREEN);
*D018 = toD018(BOB_SCREEN, BOB_CHARSET);
/*
// Clear screen
memset(BOB_SCREEN, 0x00, 1000);
// Display a BOB grid
for(char x: 0..7)
for(char y: 0..3)
renderBob(x*12+y, y*24+x);
// Wait for space
while(!keyboard_key_pressed(KEY_SPACE)) {}
while(keyboard_key_pressed(KEY_SPACE)) {}
*/
// Clear screen
memset(BOB_SCREEN, 0x00, 1000);
// Render Rotated BOBs
char angle = 0;
while(true) {
do { } while (*RASTER<$f8);
*BORDER_COLOR = 0xf;
renderBobCleanup();
signed char r = 30;
char a = angle;
for(char i: 0..NUM_BOBS-1) {
//kickasm {{ .break }}
*BORDER_COLOR = 1;
int x = mulf8s(r, COS[a]) + 75*0x100;
int y = mulf8s(r, SIN[a])*2 + 90*0x100;
*BORDER_COLOR = 2;
a += 98;
r += 3;
renderBob(>x, >y);
}
angle += 3;
*BORDER_COLOR = 0;
if(keyboard_key_pressed(KEY_SPACE)) {
break;
}
}
// Wait for space release
while(keyboard_key_pressed(KEY_SPACE)) {}
// Return to BASIC
vicSelectGfxBank(SCREEN_BASIC);
*D018 = toD018(SCREEN_BASIC, CHARSET_BASIC);
}
// Table used for deleting rendered BOB's. Contains pointers to first char of each BOB.
char* RENDERBOB_CLEANUP[NUM_BOBS];
// Pointer to the next clean-up to add
char** renderBobCleanupNext;
// *40 Table unsigned int[0x20] MUL40 = { ((unsigned int)i)*40 };
unsigned int MUL40[0x20];
// Initialize the tables used by renderBob()
void renderBobInit() {
for(char y: 0..0x1f)
MUL40[y] = ((unsigned int)y)*40;
for(char i: 0..NUM_BOBS-1)
RENDERBOB_CLEANUP[i] = BOB_SCREEN;
}
// Render a single BOB at a given x/y-position
// X-position is 0-151. Each x-position is 2 pixels wide.
// Y-position is 0-183. Each y-position is 1 pixel high.
void renderBob(char xpos, char ypos) {
char x_char_offset = xpos/BOB_SHIFTS_X;
char y_char_offset = ypos/BOB_SHIFTS_Y;
unsigned int y_offset = MUL40[y_char_offset];
char* screen = BOB_SCREEN+y_offset+x_char_offset;
char bob_table_idx = (ypos&7)*BOB_SHIFTS_X+(xpos&3);
*renderBobCleanupNext++ = screen;
screen[0] = (BOB_TABLES+0*BOB_SUBTABLE_SIZE)[bob_table_idx];
screen[40] = (BOB_TABLES+1*BOB_SUBTABLE_SIZE)[bob_table_idx];
screen[80] = (BOB_TABLES+2*BOB_SUBTABLE_SIZE)[bob_table_idx];
screen[1] = (BOB_TABLES+3*BOB_SUBTABLE_SIZE)[bob_table_idx];
screen[41] = (BOB_TABLES+4*BOB_SUBTABLE_SIZE)[bob_table_idx];
screen[81] = (BOB_TABLES+5*BOB_SUBTABLE_SIZE)[bob_table_idx];
screen[2] = (BOB_TABLES+6*BOB_SUBTABLE_SIZE)[bob_table_idx];
screen[42] = (BOB_TABLES+7*BOB_SUBTABLE_SIZE)[bob_table_idx];
screen[82] = (BOB_TABLES+8*BOB_SUBTABLE_SIZE)[bob_table_idx];
}
// Clean Up the rendered BOB's
void renderBobCleanup() {
for(char i: 0..NUM_BOBS-1) {
char* screen = RENDERBOB_CLEANUP[i];
screen[0] = 0;
screen[40] = 0;
screen[80] = 0;
screen[1] = 0;
screen[41] = 0;
screen[81] = 0;
screen[2] = 0;
screen[42] = 0;
screen[82] = 0;
}
// Prepare for next clean-up
renderBobCleanupNext = RENDERBOB_CLEANUP;
}
// Creates the pre-shifted bobs into BOB_CHARSET and populates the BOB_TABLES
// Modifies PROTO_BOB by shifting it around
void prepareBobs() {
progress_init(SCREEN_BASIC);
bob_charset_next_id = 0;
// Ensure that glyph #0 is empty
bobCharsetFindOrAddGlyph(PROTO_BOB+48);
char bob_table_idx = 0;
for(char shift_y=0;shift_y<BOB_SHIFTS_Y;shift_y++) {
for(char shift_x=0;shift_x<BOB_SHIFTS_X;shift_x++) {
// Populate charset and tables
char* bob_glyph = PROTO_BOB;
char* bob_table = BOB_TABLES + bob_table_idx;
for(char cell = 0; cell<9; cell++) {
// Look for an existing char in BOB_CHARSET
*bob_table = bobCharsetFindOrAddGlyph(bob_glyph);
// Move to the next glyph
bob_glyph+=8;
// Move to the next sub-table
bob_table += BOB_SHIFTS_X*BOB_SHIFTS_Y;
progress_inc();
}
// Move to the next bob table idx
bob_table_idx++;
// Shift PROTO_BOB right twice
shiftProtoBobRight();
shiftProtoBobRight();
}
// Shift PROTO_BOB down and 8px left
shiftProtoBobDown();
}
}
// Shift PROTO_BOB right one X pixel
void shiftProtoBobRight() {
char carry = 0;
char j = 0;
for(char i=0;i<3*3*8;i++) {
// Get the new carry (0x80 / 0x00)
char new_carry = (PROTO_BOB[j]&1)?0x80ub:0ub;
// Shift value and add old carry
PROTO_BOB[j] = carry | PROTO_BOB[j]>>1;
// Update carry
carry = new_carry;
// Increment j to iterate over the PROTO_BOB left-to-right, top-to-bottom (0, 24, 48, 1, 25, 49, ...)
if(j>=48) {
j-=47;
} else {
j+=24;
}
}
}
// Shift PROTO_BOB down one Y pixel
// At the same time restore PROTO_BOB X by shifting 8 pixels left
void shiftProtoBobDown() {
for(char i=23;i>0;i--) {
PROTO_BOB[i] = (PROTO_BOB+23)[i];
(PROTO_BOB+24)[i] = (PROTO_BOB+47)[i];
(PROTO_BOB+48)[i] = 0x00;
}
PROTO_BOB[0] = 0;
PROTO_BOB[24] = 0;
PROTO_BOB[48] = 0;
}
// BOB charset ID of the next glyph to be added
char bob_charset_next_id;
// Looks through BOB_CHARSET to find the passed bob glyph if present.
// If not present it is added
// Returns the glyph ID
char bobCharsetFindOrAddGlyph(char* bob_glyph) {
char* glyph_cursor = BOB_CHARSET;
char glyph_id = 0;
while(glyph_id!=bob_charset_next_id) {
char found = 1;
for(char i=0;i<8;i++) {
if(glyph_cursor[i]!=bob_glyph[i]) {
found = 0;
break;
}
}
if(found) return glyph_id;
glyph_id++;
glyph_cursor +=8;
}
// Not found - add it
for(char i=0;i<8;i++)
glyph_cursor[i]=bob_glyph[i];
bob_charset_next_id++;
return glyph_id;
}
// Current position of the progress cursor
char* progress_cursor;
// Current index within the progress cursor (0-7)
char progress_idx;
// Initialize the PETSCII progress bar
void progress_init(char* line) {
progress_cursor = line;
progress_idx = 0;
}
// Increase PETSCII progress one bit
// Done by increasing the character until the idx is 8 and then moving to the next char
void progress_inc() {
// Progress characters
const char progress_chars[] = { 0x20, 0x65, 0x74, 0x75, 0x61, 0xf6, 0xe7, 0xea, 0xe0 };
if(++progress_idx==8) {
*progress_cursor = progress_chars[8];
progress_cursor++;
progress_idx = 0;
}
*progress_cursor = progress_chars[progress_idx];
}

View File

@ -1,105 +0,0 @@
// Same animation using a multiplexer
#include <c64.h>
#include <multiplexer.h>
#include <fastmultiply.h>
#include <string.h>
#include <keyboard.h>
// The BOB sprite
__align(0x1000) char SPRITE[] = kickasm(resource "smiley.png") {{
.var pic = LoadPicture("smiley.png", List().add($000000, $ffffff))
.for (var y=0; y<21; y++)
.for (var x=0;x<3; x++)
.byte pic.getSinglecolorByte(x,y)
}};
// Sine and Cosine tables
// Angles: $00=0, $80=PI,$100=2*PI
// Sine/Cosine: signed fixed [-$7f,$7f]
__align(0x40) signed char SIN[0x140] = kickasm {{
.for(var i=0;i<$140;i++)
.byte >round($7fff*sin(i*2*PI/256))
}};
signed char* COS = SIN+0x40; // sin(x) = cos(x+PI/2)
// The BASIC screen
char* const SCREEN = 0x0400;
// The number of BOBs to render
const char NUM_BOBS = 16;
void main() {
asm { sei }
init();
loop();
exit();
asm { cli }
}
// Initialize the program
void init() {
*D011 = VICII_DEN | VICII_RSEL | 3;
// Initialize the multiplexer
plexInit(SCREEN);
// Set the sprite pointers & initial positions
for(char i: 0..PLEX_COUNT-1) {
PLEX_PTR[i] = (char)(SPRITE/0x40);
PLEX_XPOS[i] = 24+i*5;
PLEX_YPOS[i] = 50+i*8;
}
// Enable & initialize sprites
*SPRITES_ENABLE = 0xff;
for(char i: 0..7) {
SPRITES_COLOR[i] = GREEN;
}
mulf_init();
// Clear screen
memset(SCREEN, ' ', 1000);
}
// Exit the program
void exit() {
// Wait for space release
while(keyboard_key_pressed(KEY_SPACE)) {}
}
// The main loop
void loop() {
// Render Rotated BOBs
char angle = 0;
while(true) {
do { } while (*RASTER<0xd8);
*BORDER_COLOR = 0xf;
signed char r = 30;
char a = angle;
for(char i: 0..NUM_BOBS-1) {
//kickasm {{ .break }}
*BORDER_COLOR = 6;
int x = mulf8s(r, COS[a])*2 + 125*0x100;
PLEX_XPOS[i] = >x;
int y = mulf8s(r, SIN[a])*2 + 125*0x100;
PLEX_YPOS[i] = >y;
a += 98;
r += 3;
}
*BORDER_COLOR = 3;
plexSort();
angle += 3;
*BORDER_COLOR = BLACK;
// Sort the sprites by y-position
while((*D011&VICII_RST8)!=0) {}
// Show the sprites
for( char i: 0..PLEX_COUNT-1) {
*BORDER_COLOR = BLACK;
char rasterY = plexFreeNextYpos();
while(*RASTER<rasterY) {}
(*BORDER_COLOR)++;
plexShowSprite();
}
*BORDER_COLOR = BLACK;
if(keyboard_key_pressed(KEY_SPACE)) {
break;
}
}
}