#include typedef unsigned char uint8_t; __sfr __at(0x70) UART_TX; __sfr __at(0x71) UART_RX; __sfr __at(0x72) UART_STATUS; #define GRID_W 20 #define GRID_H 10 #define MAX_LEN 10 #define OVF_PER_FRAME 10 #define DIR_UP 0 #define DIR_DOWN 1 #define DIR_LEFT 2 #define DIR_RIGHT 3 static __near uint8_t snake_x[MAX_LEN]; static __near uint8_t snake_y[MAX_LEN]; static __near uint8_t snake_len; static __near uint8_t direction; static __near uint8_t food_x; static __near uint8_t food_y; static __near uint8_t score; static volatile __near uint8_t ovf_count; static __near uint8_t input_state; /* 0=idle, 1=got ESC, 2=got ESC+[ */ void isr(void) __interrupt(0) { if (INTCON & 0x04) { ovf_count++; INTCON &= ~(uint8_t)0x04; } } static void uart_send(uint8_t b) { while (UART_STATUS & 0x01); UART_TX = b; } static void uart_puts(const char *s) { while (*s) uart_send((uint8_t)*s++); } static uint8_t uart_recv(void) { if (UART_STATUS & 0x02) // empty return 0; return UART_RX; } /* Non-blocking state machine — drains the whole FIFO each frame. * Handles both ESC [ x (CSI) and ESC O x (SS3) cursor key modes. * Never blocks, so a stray ESC can't freeze the game. */ static void handle_input(void) { uint8_t c; for (int i = 0; i < 5; i++) { c = uart_recv(); if (c == 0) return; switch (input_state) { case 0: input_state = (c == 0x1B) ? 1 : 0; break; case 1: input_state = (c == '[' || c == 'O') ? 2 : 0; break; case 2: switch (c) { case 'A': if (direction != DIR_DOWN) direction = DIR_UP; break; case 'B': if (direction != DIR_UP) direction = DIR_DOWN; break; case 'C': if (direction != DIR_LEFT) direction = DIR_RIGHT; break; case 'D': if (direction != DIR_RIGHT) direction = DIR_LEFT; break; } input_state = 0; break; } } } static void place_food(void) { uint8_t x = ovf_count; uint8_t y = ovf_count; x += x; x += x; x += x; x -= ovf_count; x += 3; /* x = ovf*7 + 3 */ y += y; y += y; y += ovf_count; y += 1; /* y = ovf*5 + 1 */ while (x >= GRID_W) x -= GRID_W; while (y >= GRID_H) y -= GRID_H; food_x = x; food_y = y; } static void print_uint8(uint8_t v) { uint8_t started = 0; if (v >= 200) { uart_send('2'); v -= 200; started = 1; } else if (v >= 100) { uart_send('1'); v -= 100; started = 1; } { uint8_t t = '0'; while (v >= 10) { v -= 10; t++; } if (started || t != '0') uart_send(t); } uart_send(v + '0'); } static void render(void) { uint8_t x, y, i; uart_puts("\x1B[H"); /* top border */ uart_send('+'); for (x = 0; x < GRID_W; x++) uart_send('-'); uart_puts("+\r\n"); for (y = 0; y < GRID_H; y++) { uart_send('|'); for (x = 0; x < GRID_W; x++) { uint8_t ch = ' '; if (x == food_x && y == food_y) { ch = '*'; } else { for (i = 0; i < snake_len; i++) { if (snake_x[i] == x && snake_y[i] == y) { ch = (i == 0) ? 'O' : 'o'; break; } } } uart_send(ch); } uart_puts("|\r\n"); } /* bottom border */ uart_send('+'); for (x = 0; x < GRID_W; x++) uart_send('-'); uart_puts("+\r\n"); uart_puts("Score: "); print_uint8(score); uart_puts(" \r\n"); } /* returns 1 if alive, 0 if dead */ static uint8_t update(void) { uint8_t nx, ny, i; nx = snake_x[0]; ny = snake_y[0]; switch (direction) { case DIR_UP: if (ny == 0) return 0; ny--; break; case DIR_DOWN: if (ny == GRID_H-1) return 0; ny++; break; case DIR_LEFT: if (nx == 0) return 0; nx--; break; case DIR_RIGHT: if (nx == GRID_W-1) return 0; nx++; break; } /* self-collision (exclude tail: it moves away this step) */ for (i = 0; i < snake_len - 1; i++) { if (snake_x[i] == nx && snake_y[i] == ny) return 0; } if (nx == food_x && ny == food_y) { if (snake_len < MAX_LEN) snake_len++; score++; place_food(); } /* shift body back, add new head */ for (i = snake_len - 1; i > 0; i--) { snake_x[i] = snake_x[i-1]; snake_y[i] = snake_y[i-1]; } snake_x[0] = nx; snake_y[0] = ny; return 1; } void main(void) { uint8_t alive; OPTION_REG = 0x07; TMR0 = 0; INTCON = 0xa0; while (1) { /* init */ direction = DIR_RIGHT; snake_len = 3; score = 0; snake_x[0] = 5; snake_y[0] = 5; snake_x[1] = 4; snake_y[1] = 5; snake_x[2] = 3; snake_y[2] = 5; place_food(); uart_puts("\x1B[2J\x1B[H"); alive = 1; while (alive) { ovf_count = 0; while (ovf_count < OVF_PER_FRAME); handle_input(); alive = update(); render(); PORTA ^= 0x01; } uart_puts("\x1B[2J\x1B[H"); uart_puts("GAME OVER score: "); print_uint8(score); uart_puts("\r\npress any key\r\n"); while (uart_recv() == 0); /* wait for any byte */ input_state = 0; } }