mirror of
https://gitlab.com/camelot/kickc.git
synced 2025-02-05 21:30:52 +00:00
Removed some tests.
This commit is contained in:
parent
f37128fa2e
commit
9bf44bcb2a
@ -1,236 +0,0 @@
|
||||
// Put a 2x2 font into sprites and show it on screen
|
||||
#include <c64.h>
|
||||
#include <multiplexer.h>
|
||||
|
||||
char * const CHARSET_DEFAULT = 0x1000;
|
||||
char * const SPRITES = 0x3000;
|
||||
char * const SCREEN = 0x0400;
|
||||
|
||||
// Address of sprite pointers on screen
|
||||
char * const SCREEN_SPRITES = SCREEN + OFFSET_SPRITE_PTRS;
|
||||
|
||||
// Sprite pointer for sprite 0
|
||||
char SPRITE_0 = toSpritePtr(SPRITES);
|
||||
|
||||
//char FONT[0x0800] = kickasm(resource "elefont.bin") {{
|
||||
// .import binary "elefont.bin"
|
||||
//}};
|
||||
|
||||
char FONT[0x0800];
|
||||
|
||||
char __align(0x100) YSIN[0x100] = kickasm {{
|
||||
.fill $100, round(142+89.5*sin(toRadians(360*i/256)))
|
||||
}};
|
||||
|
||||
char __align(0x100) XMOVEMENT[0x200] = kickasm {{
|
||||
//.lohifill $100, round(344-i*344/$100-86*sin(toRadians(360*i/$100)))
|
||||
//.lohifill $100, round(344-i*344/$100-129*sin(toRadians(360*i/$100)))
|
||||
.lohifill $100, round(344-i*344/$100 -86*sin(toRadians(360*i/$100)) -43*sin(toRadians(360*i/$80)))
|
||||
//.lohifill $100, round(344-i*344/$100-86*sin(toRadians(360*i/$80)))
|
||||
}};
|
||||
|
||||
// The high-value table
|
||||
char * const XMOVEMENT_HI = XMOVEMENT+0x100;
|
||||
|
||||
void main() {
|
||||
|
||||
// Create 2x2 font from CHARGEN
|
||||
asm { sei }
|
||||
*PROCPORT = PROCPORT_RAM_CHARROM;
|
||||
font_2x2(CHARGEN, FONT);
|
||||
*PROCPORT = PROCPORT_BASIC_KERNEL_IO;
|
||||
asm { cli }
|
||||
|
||||
// Convert font to sprites
|
||||
font_2x2_to_sprites(FONT, SPRITES, 64);
|
||||
|
||||
// Initialize the multiplexer
|
||||
plexInit(SCREEN);
|
||||
|
||||
// Show screen
|
||||
*D018 = toD018(SCREEN, CHARSET_DEFAULT);
|
||||
|
||||
// Set the x-positions & pointers
|
||||
for(char s=0, x=0;s<PLEX_COUNT;s++,x+=8) {
|
||||
PLEX_PTR[s] = SPRITE_0+' ';
|
||||
PLEX_XPOS[s] = { XMOVEMENT_HI[x], XMOVEMENT[x] };
|
||||
}
|
||||
// Enable & initialize sprites
|
||||
*SPRITES_ENABLE = 0xff;
|
||||
for(char s: 0..7) {
|
||||
SPRITES_COLOR[s] = WHITE;
|
||||
}
|
||||
|
||||
// Move the sprites
|
||||
plex_move();
|
||||
// Sort the sprites by y-position
|
||||
plexSort();
|
||||
|
||||
// Enable the plex IRQ
|
||||
asm { sei }
|
||||
// Disable CIA 1 Timer IRQ
|
||||
CIA1->INTERRUPT = CIA_INTERRUPT_CLEAR;
|
||||
// Set raster line to 0x00
|
||||
*VICII_CONTROL &=0x7f;
|
||||
*RASTER = 0x28;
|
||||
// Enable Raster Interrupt
|
||||
*IRQ_ENABLE = IRQ_RASTER;
|
||||
// Acknowledge any IRQ
|
||||
*IRQ_STATUS = IRQ_RASTER;
|
||||
// Set the IRQ routine
|
||||
*KERNEL_IRQ = &plex_irq;
|
||||
asm { cli }
|
||||
|
||||
// Move & Sort - when needed!
|
||||
while(true) {
|
||||
while(!frame_done) {
|
||||
}
|
||||
frame_done = false;
|
||||
//*BORDER_COLOR = RED;
|
||||
// Move the sprites
|
||||
plex_move();
|
||||
// Sort the sprites by y-position
|
||||
plexSort();
|
||||
//*BORDER_COLOR = BLACK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The scroll text
|
||||
char SCROLL_TEXT[] = "camelot presents a spanking new contribution to the always hungry c64 scene. "
|
||||
"in this time of the corona virus we have chosen to direct our efforts towards the safe haven of coding, pixeling and composing for our beloved old breadbin. "
|
||||
" ";
|
||||
|
||||
// The next char to use from the scroll text
|
||||
char* scroll_text_next = SCROLL_TEXT;
|
||||
|
||||
// Y-sine index
|
||||
char y_sin_idx = 0;
|
||||
// X-movement index
|
||||
char x_movement_idx = 0;
|
||||
|
||||
// Move the plex sprites in an Y-sine and scroll them to the left.
|
||||
void plex_move() {
|
||||
char y_idx = y_sin_idx;
|
||||
char x_idx = x_movement_idx;
|
||||
for(char s: 0..PLEX_COUNT-1) {
|
||||
// Assign sine value
|
||||
PLEX_YPOS[s] = YSIN[y_idx];
|
||||
y_idx += 8;
|
||||
PLEX_XPOS[s] = { XMOVEMENT_HI[x_idx], XMOVEMENT[x_idx] };
|
||||
if(x_idx==0) {
|
||||
// Page boundary crossed - new scroll char! Detection is a bit flaky!
|
||||
// Restart scroll text of needed
|
||||
if(*scroll_text_next==0)
|
||||
scroll_text_next = SCROLL_TEXT;
|
||||
// Read next char from the scroll text
|
||||
PLEX_PTR[s] = SPRITE_0+*scroll_text_next++;
|
||||
}
|
||||
x_idx +=8;
|
||||
}
|
||||
y_sin_idx += 3;
|
||||
x_movement_idx++;
|
||||
}
|
||||
|
||||
// Signal used between IRQ and main loop. Set to true when the IRQ is done showing the sprites.
|
||||
volatile bool frame_done = false;
|
||||
|
||||
// Show sprites from the multiplexer, rescheduling the IRQ as many times as needed
|
||||
__interrupt void plex_irq() {
|
||||
asm { sei }
|
||||
//*BORDER_COLOR = WHITE;
|
||||
// Show sprites until finding one that should not be shown until a few raster lines later
|
||||
char rasterY;
|
||||
do {
|
||||
plexShowSprite();
|
||||
rasterY = plexFreeNextYpos();
|
||||
} while (plex_show_idx < PLEX_COUNT && rasterY < *RASTER+3);
|
||||
|
||||
if (plex_show_idx<PLEX_COUNT) {
|
||||
// Set raster IRQ line to the next sprite Y-position
|
||||
*RASTER = rasterY;
|
||||
} else {
|
||||
// Reset the raster IRQ to the top of the screen
|
||||
*RASTER = 0x28;
|
||||
// Signal that the IRQ is done showing sprites
|
||||
frame_done = true;
|
||||
}
|
||||
// Acknowledge the IRQ
|
||||
*IRQ_STATUS = IRQ_RASTER;
|
||||
//*BORDER_COLOR = 0;
|
||||
asm { cli }
|
||||
}
|
||||
|
||||
// Convert a 2x2-font to sprites
|
||||
// - font_2x2 The source 2x2-font
|
||||
// - sprites The destination sprites
|
||||
// - num_chars The number of chars to convert
|
||||
void font_2x2_to_sprites(char* font_2x2, char* sprites, char num_chars) {
|
||||
char * char_current = font_2x2;
|
||||
char * sprite = sprites;
|
||||
for(char c=0;c<num_chars;c++) {
|
||||
// Upper char
|
||||
char * char_left = char_current;
|
||||
char * char_right = char_current + 0x40*8;
|
||||
char sprite_idx = 0;
|
||||
for(char i: 0..20) {
|
||||
sprite[sprite_idx++] = char_left[i&7];
|
||||
sprite[sprite_idx++] = char_right[i&7];
|
||||
sprite[sprite_idx++] = 0x00;
|
||||
if(i==7) {
|
||||
// Lower char
|
||||
char_left = char_current + 0x80*8;
|
||||
char_right = char_current + 0xc0*8;
|
||||
} else if(i==15) {
|
||||
// Empty char
|
||||
char_left = font_2x2+' '*8;
|
||||
char_right = font_2x2+' '*8;
|
||||
}
|
||||
}
|
||||
char_current += 8;
|
||||
sprite += 0x40;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a 2x2-font by doubling all pixels of the 64 first chars
|
||||
// The font layout is:
|
||||
// - 0x00 - 0x3f Upper left glyphs
|
||||
// - 0x40 - 0x7f Upper right glyphs
|
||||
// - 0x80 - 0xbf Lower left glyphs
|
||||
// - 0xc0 - 0xff Lower right glyphs
|
||||
void font_2x2(char* font_original, char* font_2x2) {
|
||||
char* next_original = font_original;
|
||||
char* next_2x2 = font_2x2;
|
||||
for(char c: 0..0x3f) {
|
||||
char* next_2x2_left = next_2x2;
|
||||
char* next_2x2_right = next_2x2 + 0x40*8;
|
||||
char l2 = 0;
|
||||
for(char l: 0..7) {
|
||||
char glyph_bits = next_original[l];
|
||||
unsigned int glyph_bits_2x2 = 0;
|
||||
for(char b: 0..7) {
|
||||
// Find the bit
|
||||
char glyph_bit = (glyph_bits&0x80)?1uc:0uc;
|
||||
// Roll the bit into the current char twice
|
||||
glyph_bits_2x2 = glyph_bits_2x2<<1|glyph_bit;
|
||||
glyph_bits_2x2 = glyph_bits_2x2<<1|glyph_bit;
|
||||
// Move to next bit
|
||||
glyph_bits <<= 1;
|
||||
}
|
||||
// Put the generated 2x2-line into the 2x2-font twice
|
||||
next_2x2_left[l2] = >glyph_bits_2x2;
|
||||
next_2x2_left[l2+1] = >glyph_bits_2x2;
|
||||
next_2x2_right[l2] = <glyph_bits_2x2;
|
||||
next_2x2_right[l2+1] = <glyph_bits_2x2;
|
||||
l2 += 2;
|
||||
if(l2==8) {
|
||||
// Move to bottom chars
|
||||
next_2x2_left = next_2x2 + 0x80*8;
|
||||
next_2x2_right = next_2x2 + 0xc0*8;
|
||||
l2 = 0;
|
||||
}
|
||||
}
|
||||
next_2x2 += 8;
|
||||
next_original += 8;
|
||||
}
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
xХOЛNУ@мHQ*+J<>ё
&$рТ<D180>РGр$g)т<>-лYљЖЋЖВ\ІB<D086><42>2ЂB.ЌГg$Јi2ђ=fwfі<$Ђ!ж+кIЎДZф<>.ЃеУrЧ*Ыщъ<D189>Cск+ЅuО<75>в<EFBFBD>$:ЩђЭ<D192>ЂЭРуd[Ќ7yЊЃ=иc<D0B8>Ко97рЩ<D180>Ъb<D0AA><;kiUЪнВО<D092>4Є, ѕћ<02>ѕ<EFBFBD><02> <EFBFBD>Џ2(цамКDpЅЖn<D096>Jw<4A>с<1E>эыG<D18B>З<>e4ГeІ<65>Y>
|
||||
ccLR[3sн<73>5І3ЦЪйЂC<15><>Й"v6Ів<17>tщuI№|ТKЦС<12>ьQtќёeп<1A>_PЩ<ю\<5C>аБ№І}mЈЌM5BпК\<5C>јG<D198>эT9<>і<EFBFBD><А
|
||||
g]зђВодПЧМр<0E>u@г`
|
||||
<1C>?ђќwKoJюEЈkыyд<79>n$џгIОeOђ<><D192>]<5D>в?7џР_џ7ц"в
|
Binary file not shown.
@ -1 +0,0 @@
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 4.3 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 2.7 KiB |
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 748 B |
@ -1,51 +0,0 @@
|
||||
#include "tetris-sprites.c"
|
||||
|
||||
char SIN[256] = kickasm {{
|
||||
.var AMPL = 200-21
|
||||
.for(var i=0; i<256; i++) {
|
||||
.byte 51+AMPL/2+sin(toRadians([i*360]/256))*AMPL/2
|
||||
}
|
||||
}};
|
||||
|
||||
__address(0x3800) char SIN_SPRITE[0x40] = kickasm {{
|
||||
.fill $40, $ff
|
||||
}};
|
||||
|
||||
char sin_idx = 0;
|
||||
|
||||
void main() {
|
||||
vicSelectGfxBank(PLAYFIELD_SCREEN_1);
|
||||
*D018 = toD018(PLAYFIELD_SCREEN_1, PLAYFIELD_CHARSET);
|
||||
sprites_init();
|
||||
|
||||
*SPRITES_ENABLE = 0xff;
|
||||
|
||||
char xpos = 24;
|
||||
char ypos = 50;
|
||||
for(char s:4..7) {
|
||||
char s2 = s*2;
|
||||
SPRITES_XPOS[s2] = xpos;
|
||||
SPRITES_YPOS[s2] = ypos;
|
||||
SPRITES_COLOR[s] = s-3;
|
||||
PLAYFIELD_SPRITE_PTRS_1[s] = toSpritePtr(SIN_SPRITE);
|
||||
xpos += 24;
|
||||
ypos += 24;
|
||||
}
|
||||
|
||||
sprites_irq_init();
|
||||
loop();
|
||||
}
|
||||
|
||||
|
||||
void loop() {
|
||||
while(true) {
|
||||
do {} while (*RASTER!=0xff);
|
||||
char idx = sin_idx;
|
||||
for(char s:4..7) {
|
||||
SPRITES_YPOS[s*2] = SIN[idx];
|
||||
idx += 10;
|
||||
}
|
||||
sin_idx++;
|
||||
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
// Tetris Game for the Commodore 64
|
||||
// Memory Layout and Shared Data
|
||||
|
||||
// Address of the first screen
|
||||
char* const PLAYFIELD_SCREEN_1 = 0x0400;
|
||||
// Address of the second screen
|
||||
char* const PLAYFIELD_SCREEN_2 = 0x2c00;
|
||||
// Screen Sprite pointers on screen 1
|
||||
char* const PLAYFIELD_SPRITE_PTRS_1 = (PLAYFIELD_SCREEN_1+OFFSET_SPRITE_PTRS);
|
||||
// Screen Sprite pointers on screen 2
|
||||
char* const PLAYFIELD_SPRITE_PTRS_2 = (PLAYFIELD_SCREEN_2+OFFSET_SPRITE_PTRS);
|
||||
|
||||
// Sprites covering the playfield
|
||||
__address(0x3000) char PLAYFIELD_SPRITES[30*64] = kickasm(resource "playfield-sprites.png") {{
|
||||
.var sprites = LoadPicture("playfield-sprites.png", List().add($010101, $000000))
|
||||
// Put the sprites into memory
|
||||
.for(var sy=0;sy<10;sy++) {
|
||||
.var sprite_gfx_y = sy*20
|
||||
.for(var sx=0;sx<3;sx++) {
|
||||
.for (var y=0;y<21; y++) {
|
||||
.var gfx_y = sprite_gfx_y + mod(2100+y-sprite_gfx_y,21)
|
||||
.for (var c=0; c<3; c++) {
|
||||
.byte sprites.getSinglecolorByte(sx*3+c,gfx_y)
|
||||
}
|
||||
}
|
||||
.byte 0
|
||||
}
|
||||
}
|
||||
}};
|
||||
|
||||
// Address of the charset
|
||||
__address(0x2800) char PLAYFIELD_CHARSET[] = kickasm(resource "playfield-screen.imap") {{
|
||||
.fill 8,$00 // Place a filled char at the start of the charset
|
||||
.import binary "playfield-screen.imap"
|
||||
}};
|
||||
|
||||
// The size of the playfield
|
||||
const char PLAYFIELD_LINES = 22;
|
||||
const char PLAYFIELD_COLS = 10;
|
||||
|
||||
// The playfield. 0 is empty non-zero is color.
|
||||
// The playfield is layed out line by line, meaning the first 10 bytes are line 1, the next 10 line 2 and so forth,
|
||||
char playfield[PLAYFIELD_LINES*PLAYFIELD_COLS];
|
||||
|
||||
// Pointer to the current piece in the current orientation. Updated each time current_orientation is updated.
|
||||
char* current_piece_gfx;
|
||||
|
||||
// The char of the current piece
|
||||
char current_piece_char;
|
||||
|
||||
// Position of top left corner of current moving piece on the playfield
|
||||
char current_xpos;
|
||||
char current_ypos;
|
||||
|
||||
// The screen currently being rendered to. 0x00 for screen 1 / 0x20 for screen 2.
|
||||
char render_screen_render = 0x20;
|
||||
// The screen currently to show next to the user. 0x00 for screen 1 / 0x20 for screen 2.
|
||||
char render_screen_show = 0;
|
||||
// The screen currently being showed to the user. 0x00 for screen 1 / 0x20 for screen 2.
|
||||
volatile char render_screen_showing = 0;
|
||||
|
||||
// Current score in BCD-format
|
||||
unsigned long score_bcd = 0;
|
||||
// Current number of cleared lines in BCD-format
|
||||
unsigned int lines_bcd = 0;
|
||||
// Current level BCD-format
|
||||
char level_bcd = 0;
|
||||
// Current level in normal (non-BCD) format
|
||||
char level = 0;
|
||||
// Is the game over?
|
||||
char game_over = 0;
|
||||
|
@ -1,182 +0,0 @@
|
||||
// Tetris Game for the Commodore 64
|
||||
// The tetris pieces
|
||||
|
||||
// The T-piece
|
||||
__align(0x40) char PIECE_T[64] = {
|
||||
0, 0, 0, 0,
|
||||
1, 1, 1, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
0, 1, 0, 0,
|
||||
1, 1, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
0, 1, 0, 0,
|
||||
1, 1, 1, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
0, 1, 0, 0,
|
||||
0, 1, 1, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 0, 0
|
||||
};
|
||||
|
||||
// The S-piece
|
||||
__align(0x40) char PIECE_S[64] = {
|
||||
0, 0, 0, 0,
|
||||
0, 1, 1, 0,
|
||||
1, 1, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
0, 1, 0, 0,
|
||||
0, 1, 1, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
0, 0, 0, 0,
|
||||
0, 1, 1, 0,
|
||||
1, 1, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
0, 1, 0, 0,
|
||||
0, 1, 1, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 0
|
||||
|
||||
};
|
||||
|
||||
// The Z-piece
|
||||
__align(0x40) char PIECE_Z[64] = {
|
||||
0, 0, 0, 0,
|
||||
1, 1, 0, 0,
|
||||
0, 1, 1, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
0, 0, 1, 0,
|
||||
0, 1, 1, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
0, 0, 0, 0,
|
||||
1, 1, 0, 0,
|
||||
0, 1, 1, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
0, 0, 1, 0,
|
||||
0, 1, 1, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 0, 0
|
||||
|
||||
};
|
||||
|
||||
// The L-piece
|
||||
__align(0x40) char PIECE_L[64] = {
|
||||
0, 0, 0, 0,
|
||||
1, 1, 1, 0,
|
||||
1, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
1, 1, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
0, 0, 1, 0,
|
||||
1, 1, 1, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
0, 1, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 1, 1, 0,
|
||||
0, 0, 0, 0
|
||||
|
||||
};
|
||||
|
||||
// The J-piece
|
||||
__align(0x40) char PIECE_J[64] = {
|
||||
0, 0, 0, 0,
|
||||
1, 1, 1, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
0, 1, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
1, 1, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
1, 0, 0, 0,
|
||||
1, 1, 1, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
0, 1, 1, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 0, 0
|
||||
|
||||
};
|
||||
|
||||
// The O-piece
|
||||
__align(0x40) char PIECE_O[64] = {
|
||||
0, 0, 0, 0,
|
||||
0, 1, 1, 0,
|
||||
0, 1, 1, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
0, 0, 0, 0,
|
||||
0, 1, 1, 0,
|
||||
0, 1, 1, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
0, 0, 0, 0,
|
||||
0, 1, 1, 0,
|
||||
0, 1, 1, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
0, 0, 0, 0,
|
||||
0, 1, 1, 0,
|
||||
0, 1, 1, 0,
|
||||
0, 0, 0, 0
|
||||
|
||||
};
|
||||
|
||||
// The I-piece
|
||||
__align(0x40) char PIECE_I[64] = {
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
1, 1, 1, 1,
|
||||
0, 0, 0, 0,
|
||||
|
||||
0, 1, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
1, 1, 1, 1,
|
||||
0, 0, 0, 0,
|
||||
|
||||
0, 1, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 1, 0, 0
|
||||
|
||||
};
|
||||
|
||||
// The different pieces
|
||||
unsigned int PIECES[] = { (unsigned int)PIECE_T, (unsigned int)PIECE_S, (unsigned int)PIECE_Z, (unsigned int)PIECE_J, (unsigned int)PIECE_O, (unsigned int)PIECE_I, (unsigned int)PIECE_L };
|
||||
|
||||
// The chars to use for the different pieces - when inside the playing area
|
||||
char PIECES_CHARS[] = { 0x65, 0x66, 0xa6, 0x66, 0x65, 0x65, 0xa6 };
|
||||
|
||||
// The chars to use for the different pieces - when outside the playing area (eg. the next area).
|
||||
char PIECES_NEXT_CHARS[] = { 0x63, 0x64, 0xa4, 0x64, 0x63, 0x63, 0xa4 };
|
||||
|
||||
// The initial X/Y for each piece
|
||||
char PIECES_START_X[] = { 4, 4, 4, 4, 4, 4, 4 };
|
||||
char PIECES_START_Y[] = { 1, 1, 1, 1, 1, 0, 1 };
|
@ -1,325 +0,0 @@
|
||||
// Tetris Game for the Commodore 64
|
||||
// Implementation of the tetris game play logic. Most of the logic is modelled after NES tetris
|
||||
// Source: https://meatfighter.com/nintendotetrisai/
|
||||
|
||||
#include "tetris-data.c"
|
||||
#include "tetris-pieces.c"
|
||||
|
||||
// Pointers to the playfield address for each playfield line
|
||||
char* playfield_lines[PLAYFIELD_LINES];
|
||||
|
||||
// Indixes into the playfield for each playfield line
|
||||
char playfield_lines_idx[PLAYFIELD_LINES+1];
|
||||
|
||||
// The index of the next moving piece. (0-6)
|
||||
char next_piece_idx = 0;
|
||||
|
||||
// The current moving piece. Points to the start of the piece definition.
|
||||
char* current_piece = 0;
|
||||
|
||||
// The curent piece orientation - each piece have 4 orientations (00/0x10/0x20/0x30).
|
||||
// The orientation chooses one of the 4 sub-graphics of the piece.
|
||||
char current_orientation = 0;
|
||||
|
||||
// The speed of moving down the piece when soft-drop is not activated
|
||||
// This array holds the number of frames per move by level (0-29). For all levels 29+ the value is 1.
|
||||
const char MOVEDOWN_SLOW_SPEEDS[] = { 48, 43, 38, 33, 28, 23, 18, 13, 8, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1 };
|
||||
|
||||
// The rate of moving down the current piece (number of frames between moves if movedown is not forced)
|
||||
char current_movedown_slow = 48;
|
||||
|
||||
// The rate of moving down the current piece fast (number of frames between moves if movedown is not forced)
|
||||
const char current_movedown_fast = 10;
|
||||
|
||||
// Counts up to the next movedown of current piece
|
||||
char current_movedown_counter = 0;
|
||||
|
||||
// Base Score values for removing 0-4 lines (in BCD)
|
||||
// These values are added to score_add_bcd for each level gained.
|
||||
const unsigned long SCORE_BASE_BCD[] = { 0x0000, 0x0040, 0x0100, 0x0300, 0x1200 };
|
||||
|
||||
// Score values for removing 0-4 lines (in BCD)
|
||||
// These values are updated based on the players level and the base values from SCORE_BASE_BCD
|
||||
unsigned long score_add_bcd[5];
|
||||
|
||||
// Initialize play data tables
|
||||
void play_init() {
|
||||
// Initialize the playfield line pointers;
|
||||
char idx = 0;
|
||||
char* pli = playfield;
|
||||
for(char j:0..PLAYFIELD_LINES-1) {
|
||||
playfield_lines[j] = pli;
|
||||
playfield_lines_idx[j] = idx;
|
||||
pli += PLAYFIELD_COLS;
|
||||
idx += PLAYFIELD_COLS;
|
||||
}
|
||||
playfield_lines_idx[PLAYFIELD_LINES] = PLAYFIELD_COLS*PLAYFIELD_LINES;
|
||||
|
||||
// Set initial speed of moving down a tetromino
|
||||
current_movedown_slow = MOVEDOWN_SLOW_SPEEDS[level];
|
||||
// Set the initial score add values
|
||||
for(char b: 0..4) {
|
||||
score_add_bcd[b] = SCORE_BASE_BCD[b];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Perform any movement of the current piece
|
||||
// key_event is the next keyboard_event() og 0xff if no keyboard event is pending
|
||||
// Returns a byte signaling whether rendering is needed. (0 no render, >0 render needed)
|
||||
char play_movement(char key_event) {
|
||||
char render = 0;
|
||||
render += play_move_down(key_event);
|
||||
if(game_over!=0) {
|
||||
return render;
|
||||
}
|
||||
render += play_move_leftright(key_event);
|
||||
render += play_move_rotate(key_event);
|
||||
return render;
|
||||
}
|
||||
|
||||
// Move down the current piece
|
||||
// Return non-zero if a render is needed
|
||||
char play_move_down(byte key_event) {
|
||||
// Handle moving down current piece
|
||||
++current_movedown_counter;
|
||||
char movedown = 0;
|
||||
// As soon as space is pressed move down once
|
||||
if(key_event==KEY_SPACE) {
|
||||
movedown++;
|
||||
}
|
||||
// While space is held down move down faster
|
||||
if(keyboard_event_pressed(KEY_SPACE)!=0) {
|
||||
if(current_movedown_counter>=current_movedown_fast) {
|
||||
movedown++;
|
||||
}
|
||||
}
|
||||
// Move down slowly otherwise
|
||||
if(current_movedown_counter>=current_movedown_slow) {
|
||||
movedown++;
|
||||
}
|
||||
// Attempt movedown
|
||||
if(movedown!=0) {
|
||||
if(play_collision(current_xpos,current_ypos+1,current_orientation)==COLLISION_NONE) {
|
||||
// Move current piece down
|
||||
current_ypos++;
|
||||
} else {
|
||||
// Lock current piece
|
||||
play_lock_current();
|
||||
// Check for any lines and remove them
|
||||
char removed = play_remove_lines();
|
||||
// Tally up the score
|
||||
play_update_score(removed);
|
||||
// Spawn a new piece
|
||||
play_spawn_current();
|
||||
}
|
||||
current_movedown_counter = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Move left/right or rotate the current piece
|
||||
// Return non-zero if a render is needed
|
||||
char play_move_leftright(char key_event) {
|
||||
// Handle keyboard events
|
||||
if(key_event==KEY_COMMA) {
|
||||
if(play_collision(current_xpos-1,current_ypos,current_orientation)==COLLISION_NONE) {
|
||||
current_xpos--;
|
||||
return 1;
|
||||
}
|
||||
} else if(key_event==KEY_DOT) {
|
||||
if(play_collision(current_xpos+1,current_ypos,current_orientation)==COLLISION_NONE) {
|
||||
current_xpos++;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Rotate the current piece based on key-presses
|
||||
// Return non-zero if a render is needed
|
||||
char play_move_rotate(char key_event) {
|
||||
// Handle keyboard events
|
||||
char orientation = 0x80;
|
||||
if(key_event==KEY_Z) {
|
||||
orientation = (current_orientation-0x10)&0x3f;
|
||||
} else if(key_event==KEY_X) {
|
||||
orientation = (current_orientation+0x10)&0x3f;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
if(play_collision(current_xpos, current_ypos, orientation) == COLLISION_NONE) {
|
||||
current_orientation = orientation;
|
||||
current_piece_gfx = current_piece + current_orientation;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// No collision
|
||||
const char COLLISION_NONE = 0;
|
||||
// Playfield piece collision (cell on top of other cell on the playfield)
|
||||
const char COLLISION_PLAYFIELD = 1;
|
||||
// Bottom collision (cell below bottom of the playfield)
|
||||
const char COLLISION_BOTTOM = 2;
|
||||
// Left side collision (cell beyond the left side of the playfield)
|
||||
const char COLLISION_LEFT = 4;
|
||||
// Right side collision (cell beyond the right side of the playfield)
|
||||
const char COLLISION_RIGHT = 8;
|
||||
|
||||
// Test if there is a collision between the current piece moved to (x, y) and anything on the playfield or the playfield boundaries
|
||||
// Returns information about the type of the collision detected
|
||||
char play_collision(char xpos, char ypos, char orientation) {
|
||||
char* piece_gfx = current_piece + orientation;
|
||||
char i = 0;
|
||||
char yp = ypos;
|
||||
for(char l:0..3) {
|
||||
char* playfield_line = playfield_lines[yp];
|
||||
char xp = xpos;
|
||||
for(char c:0..3) {
|
||||
if(piece_gfx[i++]!=0) {
|
||||
if(yp>=PLAYFIELD_LINES) {
|
||||
// Below the playfield bottom
|
||||
return COLLISION_BOTTOM;
|
||||
}
|
||||
if((xp&0x80)!=0) {
|
||||
// Beyond left side of the playfield
|
||||
return COLLISION_LEFT;
|
||||
}
|
||||
if(xp>=PLAYFIELD_COLS) {
|
||||
// Beyond left side of the playfield
|
||||
return COLLISION_RIGHT;
|
||||
}
|
||||
if(playfield_line[xp]!=0) {
|
||||
// Collision with a playfield cell
|
||||
return COLLISION_PLAYFIELD;
|
||||
}
|
||||
}
|
||||
xp++;
|
||||
}
|
||||
yp++;
|
||||
}
|
||||
return COLLISION_NONE;
|
||||
}
|
||||
|
||||
// Lock the current piece onto the playfield
|
||||
void play_lock_current() {
|
||||
char i = 0;
|
||||
char yp = current_ypos;
|
||||
for(char l:0..3) {
|
||||
char* playfield_line = playfield_lines[yp];
|
||||
char xp = current_xpos;
|
||||
for(char c:0..3) {
|
||||
if(current_piece_gfx[i++]!=0) {
|
||||
playfield_line[xp] = current_piece_char;
|
||||
}
|
||||
xp++;
|
||||
}
|
||||
yp++;
|
||||
}
|
||||
}
|
||||
|
||||
// Spawn a new piece
|
||||
// Moves the next piece into the current and spawns a new next piece
|
||||
void play_spawn_current() {
|
||||
// Move next piece into current
|
||||
char current_piece_idx = next_piece_idx;
|
||||
current_piece = PIECES[current_piece_idx];
|
||||
current_piece_char = PIECES_CHARS[current_piece_idx];
|
||||
current_orientation = 0;
|
||||
current_piece_gfx = current_piece + current_orientation;
|
||||
current_xpos = PIECES_START_X[current_piece_idx];
|
||||
current_ypos = PIECES_START_Y[current_piece_idx];
|
||||
if(play_collision(current_xpos,current_ypos,current_orientation)==COLLISION_PLAYFIELD) {
|
||||
game_over = 1;
|
||||
}
|
||||
|
||||
// Spawn a new next piece
|
||||
// Pick a random piece (0-6)
|
||||
char piece_idx = 7;
|
||||
while(piece_idx==7) {
|
||||
piece_idx = sid_rnd()&7;
|
||||
}
|
||||
next_piece_idx = piece_idx;
|
||||
|
||||
}
|
||||
|
||||
// Look through the playfield for lines - and remove any lines found
|
||||
// Utilizes two cursors on the playfield - one reading cells and one writing cells
|
||||
// Whenever a full line is detected the writing cursor is instructed to write to the same line once more.
|
||||
// Returns the number of lines removed
|
||||
char play_remove_lines() {
|
||||
// Start both cursors at the end of the playfield
|
||||
char r = PLAYFIELD_LINES*PLAYFIELD_COLS-1;
|
||||
char w = PLAYFIELD_LINES*PLAYFIELD_COLS-1;
|
||||
|
||||
char removed = 0;
|
||||
// Read all lines and rewrite them
|
||||
for(char y:0..PLAYFIELD_LINES-1) {
|
||||
char full = 1;
|
||||
for(char x:0..PLAYFIELD_COLS-1) {
|
||||
char c = playfield[r--];
|
||||
if(c==0) {
|
||||
full = 0;
|
||||
}
|
||||
playfield[w--] = c;
|
||||
}
|
||||
// If a line is full then re-write it.
|
||||
if(full==1) {
|
||||
w = w + PLAYFIELD_COLS;
|
||||
removed++;
|
||||
}
|
||||
}
|
||||
|
||||
// Write zeros in the rest of the lines
|
||||
while(w!=0xff) {
|
||||
playfield[w--] = 0;
|
||||
}
|
||||
// Return the number of removed lines
|
||||
return removed;
|
||||
}
|
||||
|
||||
// Update the score based on the number of lines removed
|
||||
void play_update_score(char removed) {
|
||||
if(removed!=0){
|
||||
char lines_before = <lines_bcd&0xf0;
|
||||
unsigned long add_bcd = score_add_bcd[removed];
|
||||
|
||||
asm { sed }
|
||||
lines_bcd += removed;
|
||||
score_bcd += add_bcd;
|
||||
asm { cld }
|
||||
|
||||
// If line 10-part updated increase the level
|
||||
char lines_after = <lines_bcd&0xf0;
|
||||
if(lines_before!=lines_after) {
|
||||
play_increase_level();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Increase the level
|
||||
void play_increase_level() {
|
||||
// Increase level
|
||||
level++;
|
||||
// Update speed of moving tetrominos down
|
||||
if(level>29) {
|
||||
current_movedown_slow = 1;
|
||||
} else {
|
||||
current_movedown_slow = MOVEDOWN_SLOW_SPEEDS[level];
|
||||
}
|
||||
// Increase BCD-format level
|
||||
level_bcd++;
|
||||
if((level_bcd&0xf)==0xa) {
|
||||
// If level low nybble hits 0xa change to 0x10
|
||||
level_bcd += 6;
|
||||
}
|
||||
// Increase the score values gained
|
||||
asm { sed }
|
||||
for(char b: 0..4) {
|
||||
score_add_bcd[b] += SCORE_BASE_BCD[b];
|
||||
}
|
||||
asm { cld }
|
||||
}
|
||||
|
@ -1,218 +0,0 @@
|
||||
// Tetris Game for the Commodore 64
|
||||
// All rendering logic for showing the playfield, the pieces and the scores
|
||||
// Also handles double buffering
|
||||
|
||||
#include "tetris-data.c"
|
||||
#include "tetris-pieces.c"
|
||||
|
||||
// Address of the original playscreen chars
|
||||
const char PLAYFIELD_SCREEN_ORIGINAL_WIDTH=32;
|
||||
const char PLAYFIELD_SCREEN_ORIGINAL[] = kickasm(resource "playfield-screen.iscr", resource "playfield-extended.col" ) {{
|
||||
// Load chars for the screen
|
||||
.var screen = LoadBinary("playfield-screen.iscr")
|
||||
// Load extended colors for the screen
|
||||
.var extended = LoadBinary("playfield-extended.col")
|
||||
// screen.get(i)+1 because the charset is loaded into PLAYFIELD_CHARSET+8
|
||||
// extended.get(i)-1 because the extended colors are 1-based (1/2/3/4)
|
||||
// <<6 to move extended colors to the upper 2 bits
|
||||
.fill screen.getSize(), ( (screen.get(i)+1) | (extended.get(i)-1)<<6 )
|
||||
}};
|
||||
|
||||
// Original Color Data
|
||||
const char PLAYFIELD_COLORS_ORIGINAL[] = kickasm(resource "playfield-screen.col") {{
|
||||
.import binary "playfield-screen.col"
|
||||
}};
|
||||
|
||||
// The color #1 to use for the pieces for each level
|
||||
char PIECES_COLORS_1[] = {
|
||||
BLUE, GREEN, PURPLE, BLUE, RED, LIGHT_GREEN, RED, BLUE, LIGHT_BLUE, RED,
|
||||
BLUE, GREEN, PURPLE, BLUE, RED, LIGHT_GREEN, RED, BLUE, LIGHT_BLUE, RED,
|
||||
BLUE, GREEN, PURPLE, BLUE, RED, LIGHT_GREEN, RED, BLUE, LIGHT_BLUE, RED
|
||||
};
|
||||
// The color #2 to use for the pieces for each level
|
||||
char PIECES_COLORS_2[] = {
|
||||
CYAN, LIGHT_GREEN, PINK, LIGHT_GREEN, LIGHT_GREEN, LIGHT_BLUE, DARK_GREY, PURPLE, RED, ORANGE,
|
||||
CYAN, LIGHT_GREEN, PINK, LIGHT_GREEN, LIGHT_GREEN, LIGHT_BLUE, DARK_GREY, PURPLE, RED, ORANGE,
|
||||
CYAN, LIGHT_GREEN, PINK, LIGHT_GREEN, LIGHT_GREEN, LIGHT_BLUE, DARK_GREY, PURPLE, RED, ORANGE
|
||||
};
|
||||
|
||||
// Pointers to the screen address for rendering each playfield line
|
||||
// The lines for screen 1 is aligned with 0x80 and screen 2 with 0x40 - so XOR'ing with 0x40 gives screen 2 lines.
|
||||
__align(0x80) char* screen_lines_1[PLAYFIELD_LINES];
|
||||
__align(0x40) char* screen_lines_2[PLAYFIELD_LINES];
|
||||
|
||||
// Initialize rendering
|
||||
void render_init() {
|
||||
vicSelectGfxBank(PLAYFIELD_CHARSET);
|
||||
// Enable Extended Background Color Mode
|
||||
*D011 = VICII_ECM | VICII_DEN | VICII_RSEL | 3;
|
||||
*BORDER_COLOR = BLACK;
|
||||
*BG_COLOR = BLACK;
|
||||
*BG_COLOR1 = PIECES_COLORS_1[0];
|
||||
*BG_COLOR2 = PIECES_COLORS_2[0];
|
||||
*BG_COLOR3 = GREY;
|
||||
|
||||
// Setup chars on the screens
|
||||
render_screen_original(PLAYFIELD_SCREEN_1);
|
||||
render_screen_original(PLAYFIELD_SCREEN_2);
|
||||
|
||||
// Initialize the screen line pointers;
|
||||
char* li_1 = PLAYFIELD_SCREEN_1 + 2*40 + 16;
|
||||
char* li_2 = PLAYFIELD_SCREEN_2 + 2*40 + 16;
|
||||
for(char i:0..PLAYFIELD_LINES-1) {
|
||||
screen_lines_1[i] = li_1;
|
||||
screen_lines_2[i] = li_2;
|
||||
li_1 += 40;
|
||||
li_2 += 40;
|
||||
}
|
||||
|
||||
// Show showing screen 1 and rendering to screen 2
|
||||
render_screen_show = 0;
|
||||
render_screen_render = 0x20;
|
||||
}
|
||||
|
||||
// Update 0xD018 to show the current screen (used for double buffering)
|
||||
void render_show() {
|
||||
char d018val = 0;
|
||||
if(render_screen_show==0) {
|
||||
d018val = toD018(PLAYFIELD_SCREEN_1, PLAYFIELD_CHARSET);
|
||||
} else {
|
||||
d018val = toD018(PLAYFIELD_SCREEN_2, PLAYFIELD_CHARSET);
|
||||
}
|
||||
*D018 = d018val;
|
||||
*BG_COLOR1 = PIECES_COLORS_1[level];
|
||||
*BG_COLOR2 = PIECES_COLORS_2[level];
|
||||
render_screen_showing = render_screen_show;
|
||||
}
|
||||
|
||||
// Swap rendering to the other screen (used for double buffering)
|
||||
void render_screen_swap() {
|
||||
render_screen_render ^= 0x20;
|
||||
render_screen_show ^= 0x20;
|
||||
}
|
||||
|
||||
// Show the current score
|
||||
void render_score() {
|
||||
char* screen;
|
||||
if(render_screen_render==0) {
|
||||
screen = PLAYFIELD_SCREEN_1;
|
||||
} else {
|
||||
screen = PLAYFIELD_SCREEN_2;
|
||||
}
|
||||
|
||||
char* score_bytes = (byte*)(&score_bcd);
|
||||
unsigned int score_offset = 40*0x05 + 0x1c;
|
||||
render_bcd( screen, score_offset, score_bytes[2], 0);
|
||||
render_bcd( screen, score_offset+2, score_bytes[1], 0);
|
||||
render_bcd( screen, score_offset+4, score_bytes[0], 0);
|
||||
|
||||
unsigned int lines_offset = 40*0x01 + 0x16;
|
||||
render_bcd( screen, lines_offset, >lines_bcd, 1);
|
||||
render_bcd( screen, lines_offset+1, <lines_bcd, 0);
|
||||
|
||||
unsigned int level_offset = 40*19 + 0x1f;
|
||||
render_bcd( screen, level_offset, level_bcd, 0);
|
||||
|
||||
}
|
||||
|
||||
// Render BCD digits on a screen.
|
||||
// - screen: pointer to the screen to render on
|
||||
// - offset: offset on the screen
|
||||
// - bcd: The BCD-value to render
|
||||
// - only_low: if non-zero only renders the low digit
|
||||
void render_bcd(char* screen, unsigned int offset, char bcd, char only_low) {
|
||||
const char ZERO_CHAR = 53;
|
||||
char* screen_pos = screen+offset;
|
||||
if(only_low==0) {
|
||||
*screen_pos++ = ZERO_CHAR + (bcd >> 4);
|
||||
}
|
||||
*screen_pos++ = ZERO_CHAR + (bcd & 0x0f);
|
||||
}
|
||||
|
||||
// Copy the original screen data to the passed screen
|
||||
// Also copies colors to 0xd800
|
||||
void render_screen_original(char* screen) {
|
||||
char SPACE = 0;
|
||||
char* oscr = PLAYFIELD_SCREEN_ORIGINAL+32*2;
|
||||
char* ocols = PLAYFIELD_COLORS_ORIGINAL+32*2;
|
||||
char* cols = COLS;
|
||||
for(char y:0..24) {
|
||||
char x=0;
|
||||
do {
|
||||
*screen++ = SPACE;
|
||||
*cols++ = BLACK;
|
||||
} while(++x!=4);
|
||||
do {
|
||||
*screen++ = *oscr++;
|
||||
*cols++ = *ocols++;
|
||||
} while(++x!=36);
|
||||
do {
|
||||
*screen++ = SPACE;
|
||||
*cols++ = BLACK;
|
||||
} while(++x!=40);
|
||||
}
|
||||
}
|
||||
|
||||
// Render the static playfield on the screen (all pieces already locked into place)
|
||||
void render_playfield() {
|
||||
// Do not render the top 2 lines.
|
||||
char i = PLAYFIELD_COLS*2;
|
||||
for(char l:2..PLAYFIELD_LINES-1) {
|
||||
char* screen_line = screen_lines_1[render_screen_render+l];
|
||||
for(char c:0..PLAYFIELD_COLS-1) {
|
||||
*(screen_line++) = playfield[i++];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Render the current moving piece at position (current_xpos, current_ypos)
|
||||
// Ignores cases where parts of the tetromino is outside the playfield (sides/bottom) since the movement collision routine prevents this.
|
||||
void render_moving() {
|
||||
char i = 0;
|
||||
char ypos = current_ypos;
|
||||
for(char l:0..3) {
|
||||
if(ypos>1) {
|
||||
char* screen_line = screen_lines_1[render_screen_render+ypos];
|
||||
char xpos = current_xpos;
|
||||
for(char c:0..3) {
|
||||
char current_cell = current_piece_gfx[i++];
|
||||
if(current_cell!=0) {
|
||||
screen_line[xpos] = current_piece_char;
|
||||
}
|
||||
xpos++;
|
||||
}
|
||||
} else {
|
||||
i += 4;
|
||||
}
|
||||
ypos++;
|
||||
}
|
||||
}
|
||||
|
||||
// Render the next tetromino in the "next" area
|
||||
void render_next() {
|
||||
// Find the screen area
|
||||
unsigned int next_area_offset = 40*12 + 24 + 4;
|
||||
char* screen_next_area;
|
||||
if(render_screen_render==0) {
|
||||
screen_next_area = PLAYFIELD_SCREEN_1+next_area_offset;
|
||||
} else {
|
||||
screen_next_area = PLAYFIELD_SCREEN_2+next_area_offset;
|
||||
}
|
||||
|
||||
// Render the next piece
|
||||
char* next_piece_gfx = PIECES[next_piece_idx];
|
||||
char next_piece_char = PIECES_NEXT_CHARS[next_piece_idx];
|
||||
for(char l:0..3) {
|
||||
for(char c:0..3) {
|
||||
char cell = *next_piece_gfx++;
|
||||
if(cell!=0) {
|
||||
*screen_next_area = next_piece_char;
|
||||
} else {
|
||||
*screen_next_area = 0;
|
||||
}
|
||||
screen_next_area++;
|
||||
}
|
||||
screen_next_area += 36;
|
||||
}
|
||||
}
|
||||
|
@ -1,112 +0,0 @@
|
||||
// Tetris Game for the Commodore 64
|
||||
// A sprite multiplexer covering the playfield with a black layer to allow for 3 single-pixel colors
|
||||
#include <c64.h>
|
||||
#include "tetris-data.c"
|
||||
|
||||
// Setup the sprites
|
||||
void sprites_init() {
|
||||
*SPRITES_ENABLE = %00001111;
|
||||
*SPRITES_EXPAND_X = *SPRITES_EXPAND_Y = *SPRITES_MC = 0;
|
||||
char xpos = 24+15*8;
|
||||
for(char s:0..3) {
|
||||
char s2 = s*2;
|
||||
SPRITES_XPOS[s2] = xpos;
|
||||
SPRITES_COLOR[s] = BLACK;
|
||||
xpos = xpos+24;
|
||||
}
|
||||
}
|
||||
|
||||
// The Y-position of the first sprite row
|
||||
const char SPRITES_FIRST_YPOS = 49;
|
||||
// The line of the first IRQ
|
||||
const char IRQ_RASTER_FIRST = SPRITES_FIRST_YPOS + 19;
|
||||
// The raster line of the next IRQ
|
||||
volatile char irq_raster_next = IRQ_RASTER_FIRST;
|
||||
// Y-pos of the sprites on the next IRQ
|
||||
volatile char irq_sprite_ypos = SPRITES_FIRST_YPOS + 21;
|
||||
// Index of the sprites to show on the next IRQ
|
||||
volatile char irq_sprite_ptr = toSpritePtr(PLAYFIELD_SPRITES) + 3;
|
||||
// Counting the 10 IRQs
|
||||
volatile char irq_cnt = 0;
|
||||
|
||||
|
||||
// Setup the IRQ
|
||||
void sprites_irq_init() {
|
||||
asm { sei }
|
||||
// Acknowledge any IRQ and setup the next one
|
||||
*IRQ_STATUS = IRQ_RASTER;
|
||||
asm { lda CIA1_INTERRUPT }
|
||||
|
||||
// Disable kernal & basic
|
||||
*PROCPORT_DDR = PROCPORT_DDR_MEMORY_MASK;
|
||||
*PROCPORT = PROCPORT_RAM_IO;
|
||||
// Disable CIA 1 Timer IRQ
|
||||
CIA1->INTERRUPT = CIA_INTERRUPT_CLEAR;
|
||||
// Set raster line
|
||||
*VICII_CONTROL &=0x7f;
|
||||
*RASTER = IRQ_RASTER_FIRST;
|
||||
// Enable Raster Interrupt
|
||||
*IRQ_ENABLE = IRQ_RASTER;
|
||||
// Set the IRQ routine
|
||||
*HARDWARE_IRQ = &sprites_irq;
|
||||
asm { cli }
|
||||
}
|
||||
|
||||
|
||||
// Raster Interrupt Routine - sets up the sprites covering the playfield
|
||||
// Repeats 10 timers every 2 lines from line IRQ_RASTER_FIRST
|
||||
// Utilizes duplicated gfx in the sprites to allow for some leeway in updating the sprite pointers
|
||||
__interrupt(hardware_clobber) void sprites_irq() {
|
||||
//(*BG_COLOR)++;
|
||||
// Clear decimal flag (because it is used by the score algorithm)
|
||||
asm { cld }
|
||||
// Place the sprites
|
||||
char ypos = irq_sprite_ypos;
|
||||
SPRITES_YPOS[0] = ypos;
|
||||
SPRITES_YPOS[2] = ypos;
|
||||
SPRITES_YPOS[4] = ypos;
|
||||
SPRITES_YPOS[6] = ypos;
|
||||
|
||||
// Wait for the y-position before changing sprite pointers
|
||||
volatile char raster_sprite_gfx_modify = irq_raster_next+1;
|
||||
do {
|
||||
} while(*RASTER<raster_sprite_gfx_modify);
|
||||
|
||||
char ptr = irq_sprite_ptr;
|
||||
if(render_screen_showing==0) {
|
||||
PLAYFIELD_SPRITE_PTRS_1[0] = ptr++;
|
||||
PLAYFIELD_SPRITE_PTRS_1[1] = ptr;
|
||||
PLAYFIELD_SPRITE_PTRS_1[2] = ptr++;
|
||||
PLAYFIELD_SPRITE_PTRS_1[3] = ptr;
|
||||
} else {
|
||||
PLAYFIELD_SPRITE_PTRS_2[0] = ptr++;
|
||||
PLAYFIELD_SPRITE_PTRS_2[1] = ptr;
|
||||
PLAYFIELD_SPRITE_PTRS_2[2] = ptr++;
|
||||
PLAYFIELD_SPRITE_PTRS_2[3] = ptr;
|
||||
}
|
||||
|
||||
// Find next raster line / sprite positions
|
||||
++irq_cnt;
|
||||
if(irq_cnt==9) {
|
||||
irq_raster_next += 21;
|
||||
irq_sprite_ypos = SPRITES_FIRST_YPOS;
|
||||
irq_sprite_ptr = toSpritePtr(PLAYFIELD_SPRITES);
|
||||
} else if(irq_cnt==10) {
|
||||
irq_cnt = 0;
|
||||
irq_raster_next = IRQ_RASTER_FIRST;
|
||||
irq_sprite_ypos += 21;
|
||||
irq_sprite_ptr += 3;
|
||||
} else {
|
||||
irq_raster_next += 20;
|
||||
irq_sprite_ypos += 21;
|
||||
irq_sprite_ptr += 3;
|
||||
}
|
||||
|
||||
// Setup next interrupt
|
||||
*RASTER = irq_raster_next;
|
||||
|
||||
// Acknowledge the IRQ and setup the next one
|
||||
*IRQ_STATUS = IRQ_RASTER;
|
||||
|
||||
//(*BG_COLOR)--;
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
// Tetris Game for the Commodore 64
|
||||
// The tetris game tries to match NES tetris gameplay pretty closely
|
||||
// Source: https://meatfighter.com/nintendotetrisai/
|
||||
|
||||
#include <c64.h>
|
||||
#include <keyboard.h>
|
||||
#include "tetris-data.c"
|
||||
#include "tetris-render.c"
|
||||
#include "tetris-sprites.c"
|
||||
#include "tetris-play.c"
|
||||
|
||||
void main() {
|
||||
sid_rnd_init();
|
||||
asm { sei }
|
||||
render_init();
|
||||
sprites_init();
|
||||
sprites_irq_init();
|
||||
play_init();
|
||||
// Spawn twice to spawn both current & next
|
||||
play_spawn_current();
|
||||
play_spawn_current();
|
||||
render_playfield();
|
||||
render_moving();
|
||||
render_next();
|
||||
while(true) {
|
||||
// Wait for a frame to pass
|
||||
while(*RASTER!=0xff) {}
|
||||
//*BORDER_COLOR = render_screen_show/0x10;
|
||||
// Update D018 to show the selected screen
|
||||
render_show();
|
||||
// Scan keyboard events
|
||||
keyboard_event_scan();
|
||||
char key_event = keyboard_event_get();
|
||||
char render = 0;
|
||||
if(game_over==0) {
|
||||
render = play_movement(key_event);
|
||||
} else {
|
||||
while(true) {
|
||||
(*BORDER_COLOR)++;
|
||||
}
|
||||
}
|
||||
if(render!=0) {
|
||||
render_playfield();
|
||||
render_moving();
|
||||
render_next();
|
||||
render_score();
|
||||
render_screen_swap();
|
||||
}
|
||||
//*BORDER_COLOR = 0;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user