1
0
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:
jespergravgaard 2021-01-04 00:41:58 +01:00
parent f37128fa2e
commit 9bf44bcb2a
21 changed files with 0 additions and 1252 deletions

View File

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

View File

@ -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>З<>e 4ГeІ<65>Y>
ccLR[3sн<73>5І3ЦЪйЂC<15><>Й"v6Ів<17>uI№|ТС<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.

View File

@ -1 +0,0 @@


Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 748 B

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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