1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-11-22 16:33:48 +00:00

Added NES balls example.

This commit is contained in:
jespergravgaard 2020-09-28 08:56:30 +02:00
parent 81b638f060
commit 79484c4b37
2 changed files with 161 additions and 0 deletions

View File

@ -0,0 +1,161 @@
#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 int 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_stack) 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
};

Binary file not shown.