#include #include #include #include "imagedata.h" #define LEFT_BIT 0x02 #define RIGHT_BIT 0x20 #define DOWN_BIT 0x01 #define ROTATE_BIT 0x10 #define INPUT_BITS (LEFT_BIT | RIGHT_BIT | DOWN_BIT | ROTATE_BIT) #define interrupt(x) void __attribute__((interrupt (x))) #define delay_us(n) __delay_cycles((n) * 16) #define LAST_LINE 255 int16_t y = LAST_LINE; uint8_t frame = 0; uint8_t random = 3; volatile bool vblank = false; uint16_t well_line = 0x3C3; uint16_t well_vert_line; uint16_t bmp_line[4] = { 0 }; #define WELL_HEIGHT 22 #define PIECE_BLOCKS 4 // +1 void delay_ms(uint16_t ms) { while (ms--) __delay_cycles(16000); } void mymemcpy(void *dest, void *src, uint16_t num) { char *dp = dest, *sp = src; while (num--) *dp++ = *sp++; } struct block { int8_t x, y, shim_dir; }; struct piece { int8_t x, y; bool symmetrical; struct block blocks[PIECE_BLOCKS]; }; const struct piece piece_data[] = { { .x = 11, .y = 4, .blocks = { {-1, 0, 1}, {0, 0, 1}, {1, 0, 3}, {2, 0, 3} } }, { .x = 11, .y = 4, .blocks = { {-1, -1, 2}, {-1, 0, 1}, {0, 0, 1}, {1, 0, 3} } }, { .x = 11, .y = 4, .blocks = { {1, -1, 2}, {-1, 0, 1}, {0, 0, 1}, {1, 0, 3} } }, { .symmetrical = true, .x = 11, .y = 4, .blocks = { {0, -1, 1}, {1, -1, 2}, {0, 0, 0}, {1, 0, 3} } }, { .x = 11, .y = 4, .blocks = { {0, -1, 2}, {1, -1, 3}, {-1, 0, 1}, {0, 0, 0} } }, { .x = 11, .y = 4, .blocks = { {0, -1, 2}, {-1, 0, 1}, {0, 0, 0}, {1, 0, 3} } }, { .x = 11, .y = 4, .blocks = { {-1, -1, 1}, {0, -1, 2}, {0, 0, 0}, {1, 0, 3} } }, }; struct piece *next_piece = (struct piece *)&piece_data[4]; struct piece cur_piece, temp_piece; volatile uint16_t debug[8]; volatile uint16_t well[WELL_HEIGHT] = { 0 }; volatile uint16_t well_horz[WELL_HEIGHT] = { 0 }; // -- volatile uint16_t well_vert[WELL_HEIGHT] = { 0 }; // | const uint16_t bits[] = { 1 << 0, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7, 1 << 8, 1 << 9, 1 << 10, 1 << 11, 1 << 12, 1 << 13, 1 << 14, 1 << 15, }; #define OP_SET 0 #define OP_CLEAR 1 #define OP_CHECK 2 bool bitmap_op(volatile uint16_t *bitmap, int8_t x, int8_t y, int op) { if (x < 0 || x >= 15 || y < 0 || y >= WELL_HEIGHT) return true; uint16_t mask = bits[x]; if (op == OP_CHECK) return x >= 10 || (bitmap[y] & mask) != 0; else if (op == OP_SET) bitmap[y] |= mask; else if (op == OP_CLEAR) bitmap[y] &= ~mask; return false; } void draw_block(struct block *b, int8_t ox, int8_t oy, int op) { int8_t x = ox + b->x, y = oy + b->y; bitmap_op(well, x, y, op); switch (b->shim_dir) { case 0: bitmap_op(well_horz, x, y, op); break; case 1: bitmap_op(well_vert, x, y, op); break; case 2: bitmap_op(well_horz, x, y + 1, op); break; case 3: bitmap_op(well_vert, x - 1, y, op); break; } } void draw_piece(struct piece *p, int op) { int i; for (i = 0; i < PIECE_BLOCKS; i++) draw_block(&p->blocks[i], p->x, p->y, op); //draw_block( //bitmap_op(well, p->x, p->y, op); //for (i = 0; i < PIECE_BLOCKS; i++) // bitmap_op(well, p->x + p->blocks[i].x, p->y + p->blocks[i].y, op); } void drop_next_piece() { mymemcpy(&cur_piece, next_piece, sizeof(struct piece)); cur_piece.x = 5; cur_piece.y = 2; next_piece = (struct piece *)&piece_data[random % 7]; } void rotate_piece() { int i; struct block *pts = temp_piece.blocks; for (i = 0; i < PIECE_BLOCKS; i++) { int8_t tmp = pts[i].y; pts[i].y = pts[i].x; pts[i].x = -tmp; pts[i].shim_dir = (pts[i].shim_dir + 1) & 3; } } bool collision() { if (bitmap_op(well, temp_piece.x, temp_piece.y, OP_CHECK)) return true; int i; for (i = 0; i < PIECE_BLOCKS; i++) { if (bitmap_op(well, temp_piece.x + temp_piece.blocks[i].x, temp_piece.y + temp_piece.blocks[i].y, OP_CHECK)) { return true; } } return false; } void reset_temp_piece() { mymemcpy(&temp_piece, &cur_piece, sizeof(struct piece)); } void apply_temp_piece() { mymemcpy(&cur_piece, &temp_piece, sizeof(struct piece)); } void clear_line(y) { int i; for (i = y; i >= 0; i--) { well[i] = well[i - 1]; well_horz[i] = well_horz[i - 1]; well_vert[i] = well_vert[i - 1]; } if (y < WELL_HEIGHT - 1) well_horz[y + 1] = 0; well[0] = 0; well_horz[0] = 0; well_vert[0] = 0; } uint8_t prev_input_down; uint8_t down_frame = 10; int main() { // Disable watchdog WDTCTL = WDTPW | WDTHOLD; // 16MHz clock BCSCTL1 = CALBC1_16MHZ; DCOCTL = CALDCO_16MHZ; // Small output on P1.2 (TA0.1), big output on P2.0 P1SEL = 0x04; // Timer output on P1.2 P1SEL2 = 0; P1OUT = 0; P1DIR = 0x04; P2SEL = 0; P2SEL2 = 0; P2OUT = 0; P2DIR = 0x01; // Pull-up resistors for controller P1OUT |= INPUT_BITS; P1REN |= INPUT_BITS; TACTL |= TACLR; // Clear timer TACCTL0 = CCIE; // Enable timer period interrupt TACCTL1 = OUTMOD_3; // Reset on TACCR0 - set on TACCR1 TACCR2 = 12 * 16; // Line generation interrupt @12 + alpha us TACCR0 = 64 * 16; // Force interrupt soon to setup __enable_interrupt(); TACTL |= MC_1 | TASSEL_2; // Enable timer, count up to TACCR0, SMCLK source drop_next_piece(); bool check_clear_line = false; while (true) { while (!vblank) __bis_status_register(LPM0_bits | GIE); vblank = false; uint8_t input_down = (~P1IN) & INPUT_BITS; uint8_t input_press = input_down & ~prev_input_down; if (input_down != prev_input_down) random ^= (random >> 4) ^ frame; random++; prev_input_down = input_down; draw_piece(&cur_piece, OP_CLEAR); draw_piece(next_piece, OP_CLEAR); reset_temp_piece(); if (input_press & LEFT_BIT) temp_piece.x--; else if (input_press & RIGHT_BIT) temp_piece.x++; if (collision()) reset_temp_piece(); else apply_temp_piece(); if (input_press & ROTATE_BIT && !cur_piece.symmetrical) rotate_piece(); if (collision()) reset_temp_piece(); else apply_temp_piece(); bool drop_next_now = false; if ((input_press & DOWN_BIT) || frame == down_frame) { if (input_down & DOWN_BIT) down_frame += 4; else down_frame += 20; temp_piece.y += 1; drop_next_now = collision(); if (drop_next_now) { check_clear_line = true; reset_temp_piece(); draw_piece(&cur_piece, OP_SET); } } apply_temp_piece(); if (check_clear_line) { check_clear_line = false; int ty; for (ty = WELL_HEIGHT - 1; ty > 1; ty--) { if (well[ty] == 0x3FF) { clear_line(ty); check_clear_line = true; break; } } } if (drop_next_now) drop_next_piece(); draw_piece(&cur_piece, OP_SET); draw_piece(next_piece, OP_SET); } return 0; } //#define FULL_SCAN 1017 #define FULL_SCAN (64 * 16) //#define SYNC_LEN 75 #define SYNC_LEN (3 * 16) int16_t well_y, well_cnt, bmp_offs, bmp_cnt; // Sync pulse interrupt interrupt (TIMER0_A0_VECTOR) timera0_isr() { switch (y) { case 10: TACCTL2 |= CCIE; // Enable line generation interrupt break; case 247: TACCTL2 &= ~CCIE; TACCR1 = (FULL_SCAN - SYNC_LEN); // Toggle SYNC_LEN before end break; case 248: TACCR1 = SYNC_LEN; // Toggle after SYNC_LEN frame++; vblank = true; well_line = 0; well_vert_line = 0; bmp_line[0] = bmp_line[1] = bmp_line[2] = bmp_line[3] = 0; __bic_status_register_on_exit(LPM0_bits); break; case 258: y = 0; well_y = 2; well_cnt = 0; bmp_offs = -bmp_playfield_wpl * 6; bmp_cnt = 0; break; } y++; if (y < 15 || y >= 248) return; // LINE UPDATE - must use at most 12*16 - x cycles (~167 cycles) if (well_y < 22) { if (well_cnt == 10) { well_cnt = 0; well_y++; well_line = well_horz[well_y]; well_vert_line = 0; } else if (well_cnt > 1) { well_line = well[well_y]; well_vert_line = well_vert[well_y]; } } else { if (well_cnt & 1) { well_line = 0x3FF; well_vert_line = 0x1FF; } else { well_line = 0; well_vert_line = 0; } } well_cnt++; if (bmp_cnt == 2) { bmp_offs += bmp_playfield_wpl; bmp_cnt = 0; } if (bmp_offs >= 0 && bmp_offs < sizeof(bmp_playfield) / sizeof(uint16_t)) { bmp_line[0] = bmp_playfield[bmp_offs]; bmp_line[1] = bmp_playfield[bmp_offs + 1]; bmp_line[2] = bmp_playfield[bmp_offs + 2]; bmp_line[3] = bmp_playfield[bmp_offs + 3]; } else { bmp_line[0] = bmp_line[1] = bmp_line[2] = bmp_line[3] = 0; } bmp_cnt++; }