diff --git a/Makefile b/Makefile index 2584e4a..c8c8dce 100644 --- a/Makefile +++ b/Makefile @@ -127,6 +127,7 @@ UPROGS=\ $U/_mkdir\ $U/_rm\ $U/_sh\ + $U/_snake\ $U/_stressfs\ $U/_usertests\ $U/_grind\ diff --git a/kernel/console.c b/kernel/console.c index 05dc526..108668c 100644 --- a/kernel/console.c +++ b/kernel/console.c @@ -126,6 +126,43 @@ consoleread(int user_dst, uint64 dst, int n) return target - n; } +//Get current keypress from console with no delay. +int +consolekeys(int user_dst, uint64 dst, int n) +{ + uint target; + int c; + char cbuf; + + target = n; + acquire(&cons.lock); + while(n > 0){ + + if(killed(myproc())){ + release(&cons.lock); + return -1; + } + + c = cons.buf[cons.r++ % INPUT_BUF_SIZE]; + // copy the input byte to the user-space buffer. + cons.w = cons.e; + cbuf = c; + if (c != '\033' && c!= '[' && c != 'A' && c != 'B' && c != 'C' && c != 'D'){ + cons.r--; + break; + } + + if(either_copyout(user_dst, dst, &cbuf, 1) == -1) + break; + + dst++; + --n; + } + release(&cons.lock); + + return target - n; +} + // // the console input interrupt handler. // uartintr() calls this for input character. @@ -188,5 +225,6 @@ consoleinit(void) // connect read and write system calls // to consoleread and consolewrite. devsw[CONSOLE].read = consoleread; + devsw[CONSOLE].keys = consolekeys; devsw[CONSOLE].write = consolewrite; } diff --git a/kernel/defs.h b/kernel/defs.h index a3c962b..e4f7d8c 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -30,6 +30,7 @@ struct file* filealloc(void); void fileclose(struct file*); struct file* filedup(struct file*); void fileinit(void); +int filekeys(struct file*, uint64, int n); int fileread(struct file*, uint64, int n); int filestat(struct file*, uint64 addr); int filewrite(struct file*, uint64, int n); diff --git a/kernel/file.c b/kernel/file.c index 25fa226..6bdf3c1 100644 --- a/kernel/file.c +++ b/kernel/file.c @@ -129,6 +129,34 @@ fileread(struct file *f, uint64 addr, int n) return r; } +// Read keys from file f. +// addr is a user virtual address. +int +filekeys(struct file *f, uint64 addr, int n) +{ + int r = 0; + + if(f->readable == 0) + return -1; + + if(f->type == FD_PIPE){ + r = piperead(f->pipe, addr, n); + } else if(f->type == FD_DEVICE){ + if(f->major < 0 || f->major >= NDEV || !devsw[f->major].keys) + return -1; + r = devsw[f->major].keys(1, addr, n); + } else if(f->type == FD_INODE){ + ilock(f->ip); + if((r = readi(f->ip, 1, addr, f->off, n)) > 0) + f->off += r; + iunlock(f->ip); + } else { + panic("fileread"); + } + + return r; +} + // Write to file f. // addr is a user virtual address. int diff --git a/kernel/file.h b/kernel/file.h index b076d1d..e3eaaf5 100644 --- a/kernel/file.h +++ b/kernel/file.h @@ -33,6 +33,7 @@ struct inode { struct devsw { int (*read)(int, uint64, int); int (*write)(int, uint64, int); + int (*keys)(int, uint64, int); }; extern struct devsw devsw[]; diff --git a/kernel/syscall.c b/kernel/syscall.c index ed65409..5d06f87 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -101,6 +101,7 @@ extern uint64 sys_unlink(void); extern uint64 sys_link(void); extern uint64 sys_mkdir(void); extern uint64 sys_close(void); +extern uint64 sys_keys(void); // An array mapping syscall numbers from syscall.h // to the function that handles the system call. @@ -126,6 +127,7 @@ static uint64 (*syscalls[])(void) = { [SYS_link] sys_link, [SYS_mkdir] sys_mkdir, [SYS_close] sys_close, +[SYS_keys] sys_keys, }; void diff --git a/kernel/syscall.h b/kernel/syscall.h index bc5f356..5872397 100644 --- a/kernel/syscall.h +++ b/kernel/syscall.h @@ -20,3 +20,4 @@ #define SYS_link 19 #define SYS_mkdir 20 #define SYS_close 21 +#define SYS_keys 22 diff --git a/kernel/sysfile.c b/kernel/sysfile.c index 16b668c..3e39708 100644 --- a/kernel/sysfile.c +++ b/kernel/sysfile.c @@ -79,6 +79,20 @@ sys_read(void) return fileread(f, p, n); } +uint64 +sys_keys(void) +{ + struct file *f; + int n; + uint64 p; + + argaddr(1, &p); + argint(2, &n); + if(argfd(0, 0, &f) < 0) + return -1; + return filekeys(f, p, n); +} + uint64 sys_write(void) { diff --git a/kernel/types.h b/kernel/types.h index ee73164..8f1bcf1 100644 --- a/kernel/types.h +++ b/kernel/types.h @@ -8,3 +8,13 @@ typedef unsigned int uint32; typedef unsigned long uint64; typedef uint64 pde_t; + +typedef struct { + int x, y; +} Point; + +typedef struct { + Point body[sizeof(Point)*20]; + int length; + Point direction; +} Snake; diff --git a/user/snake.c b/user/snake.c new file mode 100644 index 0000000..b7b81e0 --- /dev/null +++ b/user/snake.c @@ -0,0 +1,133 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +#define WIDTH 20 +#define HEIGHT 10 +#define SNAKE_MAX_LENGTH 20 + +uint32 rand() { + uint32 x = uptime(); + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + return x; +} + +void move_cursor() { + printf("\033[%d;1H", HEIGHT + 3); +} + +void clear_screen() { + printf("\033[H\033[J"); +} + +void draw_border() { + for (int i = 0; i < WIDTH + 2; i++) printf("#"); + printf("\n"); + for (int i = 0; i < HEIGHT; i++) { + printf("#"); + for (int j = 0; j < WIDTH; j++) printf(" "); + printf("#\n"); + } + for (int i = 0; i < WIDTH + 2; i++) printf("#"); + printf("\n"); +} + +void draw_snake(Snake *snake) { + for (int i = 0; i < snake->length; i++) { + printf("\033[%d;%dH@", snake->body[i].y + 2, snake->body[i].x + 2); + } +} + +void draw_food(Point *food) { + printf("\033[%d;%dH*", food->y + 2, food->x + 2); +} + +void update_snake(Snake *snake) { + for (int i = snake->length - 1; i > 0; i--) { + snake->body[i] = snake->body[i - 1]; + } + snake->body[0].x += snake->direction.x; + snake->body[0].y += snake->direction.y; +} + +int check_collision(Snake *snake) { + Point head = snake->body[0]; + if (head.x < 0 || head.x >= WIDTH || head.y < 0 || head.y >= HEIGHT) return 1; + for (int i = 1; i < snake->length; i++) { + if (head.x == snake->body[i].x && head.y == snake->body[i].y) return 1; + } + return 0; +} + +void place_food(Point *food, Snake *snake) { + int valid; + do { + valid = 1; + food->x = rand() % WIDTH; + food->y = rand() % HEIGHT; + for (int i = 0; i < snake->length; i++) { + if (food->x == snake->body[i].x && food->y == snake->body[i].y) { + valid = 0; + break; + } + } + } while (!valid); +} + +void game_over(Snake *snake) { + printf("\033[%d;%dHGame Over!\n", HEIGHT / 2 + 2, WIDTH / 2 - 4); + printf("\033[%d;%dHYou scored %d!\n", HEIGHT / 2 + 3, WIDTH / 2 - 4, snake->length-1); + move_cursor(); + return; +} + +int main() { + Snake snake; + Point food; + snake.length = 1; + snake.body[0].x = WIDTH / 2; + snake.body[0].y = HEIGHT / 2; + snake.direction.x = 1; + snake.direction.y = 0; + + place_food(&food, &snake); + + clear_screen(); + draw_border(); + draw_snake(&snake); + draw_food(&food); + + while (1) { + char input[3]; // Buffer to store escape sequences + int n = keys(0, input, 3); // Read up to 3 characters + if (n > 0) { + if (n == 3 && input[0] == '\033' && input[1] == '[') { + switch (input[2]) { + case 'A': if (snake.direction.y == 0) { snake.direction.x = 0; snake.direction.y = -1; } break; // Up + case 'B': if (snake.direction.y == 0) { snake.direction.x = 0; snake.direction.y = 1; } break; // Down + case 'C': if (snake.direction.x == 0) { snake.direction.x = 1; snake.direction.y = 0; } break; // Right + case 'D': if (snake.direction.x == 0) { snake.direction.x = -1; snake.direction.y = 0; } break; // Left + } + } + } + if (check_collision(&snake)){ + game_over(&snake); + return 0; + } + if (snake.body[0].x == food.x && snake.body[0].y == food.y) { + if (snake.length < SNAKE_MAX_LENGTH) snake.length++; + place_food(&food, &snake); + } + update_snake(&snake); + clear_screen(); + draw_border(); + draw_snake(&snake); + draw_food(&food); + move_cursor(); + sleep(3); + } + return 0; +} + diff --git a/user/user.h b/user/user.h index 2e6fc55..1ce2bcd 100644 --- a/user/user.h +++ b/user/user.h @@ -22,6 +22,7 @@ int getpid(void); char* sbrk(int); int sleep(int); int uptime(void); +int keys(int, void*, int); // ulib.c int stat(const char*, struct stat*); diff --git a/user/usys.pl b/user/usys.pl index 01e426e..520f65f 100755 --- a/user/usys.pl +++ b/user/usys.pl @@ -36,3 +36,4 @@ sub entry { entry("sbrk"); entry("sleep"); entry("uptime"); +entry("keys");