From 9ceb0d99cbd6221716e82a0c7ca6938567e45fba Mon Sep 17 00:00:00 2001 From: mike Date: Mon, 30 Sep 2024 15:59:54 -0700 Subject: [PATCH 01/10] setup repo for pull --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8524b54..205a7c7 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,5 @@ ![FogOS](docs/fogos.gif) +. + From b3cd6499235f79f370983b04f4df3aff42381d0c Mon Sep 17 00:00:00 2001 From: mike Date: Mon, 30 Sep 2024 16:27:08 -0700 Subject: [PATCH 02/10] pong template coded added to docs for reference purposes --- docs/template.c | 260 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 docs/template.c diff --git a/docs/template.c b/docs/template.c new file mode 100644 index 0000000..cc99683 --- /dev/null +++ b/docs/template.c @@ -0,0 +1,260 @@ +/* This is the template .c file that I tired to emulate with my FogOS version of pong + Note: the library imports being used here are the basis for my sys calls that inter- + face with the VGA/UART console -- causing my user input / screen refresh dilemmas. +*/ + +#include +#include +#include // For usleep() +#include // For terminal control +#include // For non-blocking input + +#define V 15 // Adjusted height of the game board +#define H 40 // Adjusted width of the game board + +// Function declarations +void playGame(); +void gameRules(); +void gotoxy(int x, int y); +void clearScreen(); +void draw(char map[V][H], int score); +void show(char map[V][H], int score); +void update(char map[V][H], int paddleStart, int paddleEnd, int py, int px); +void input(char map[V][H], int *paddleStart, int *paddleEnd, int *px, int *py, int *modx, int *mody, int *score, int *game_over); +void edge(char map[V][H]); +void player(char map[V][H], int paddleStart, int paddleEnd); +void ball(char map[V][H], int py, int px); +void enableRawMode(); +void disableRawMode(); +int kbhit(); +void handleArrowKeys(int *paddleStart, int *paddleEnd); + +struct termios orig_termios; // Store original terminal attributes + +int playerwin = 0; + +int main() { + int choice; + while (1) { + clearScreen(); // Clear screen on each menu display + printf("\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n"); + printf("| Ping Pong Game |\n"); + printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n"); + printf("\n1. Game Rules"); + printf("\n2. Play Game"); + printf("\n3. Quit\n"); + scanf("%d", &choice); + + switch (choice) { + case 1: + gameRules(); + break; + case 2: + playGame(); + break; + case 3: + disableRawMode(); // Ensure terminal is restored before quitting + exit(0); + default: + printf("Invalid choice"); + } + } + return 0; +} + +void gameRules() { + clearScreen(); + printf("\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n"); + printf("| Game Rules |\n"); + printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n"); + printf("\n- Use the up and down arrow keys to move your paddle up and down."); + printf("\n- The goal is to prevent the ball from passing your paddle on the left."); + printf("\n- The ball will bounce off the right wall."); + printf("\n- Every time you hit the ball, you earn 1 point."); + printf("\n\nPress any key to return to the main menu...\n"); + getchar(); + getchar(); // Wait for user input to go back +} + +void playGame() { + char map[V][H]; + int px = H / 2, py = V / 2; // Ball position + int modx = -1, mody = -1; // Ball direction + int paddleStart = V / 2 - 2, paddleEnd = V / 2 + 2; // Player paddle + int score = 0; // Player's score + int game_over = 0; + + enableRawMode(); // Enable raw mode for real-time input + + while (1) { + game_over = 0; + draw(map, score); // Clear the map and display the score + input(map, &paddleStart, &paddleEnd, &px, &py, &modx, &mody, &score, &game_over); + update(map, paddleStart, paddleEnd, py, px); // Update game state + show(map, score); // Show the updated map and score + usleep(100000); // Sleep for 100ms to slow down the game loop + + if (game_over == 1) { + break; + } + } + + disableRawMode(); // Restore terminal to original state +} + +void draw(char map[V][H], int score) { + int i, j; + clearScreen(); + for (i = 0; i < V; i++) { + for (j = 0; j < H; j++) { + map[i][j] = ' '; + } + } +} + +void show(char map[V][H], int score) { + int i, j; + clearScreen(); + printf(" --------------------------------------\n"); // Row of dashes above the score + printf(" Score: %d\n", score); // Display the score + for (i = 0; i < V; i++) { + for (j = 0; j < H; j++) { + printf("%c", map[i][j]); + } + printf("\n"); + } +} + +void update(char map[V][H], int paddleStart, int paddleEnd, int py, int px) { + edge(map); + player(map, paddleStart, paddleEnd); + ball(map, px, py); +} + +void input(char map[V][H], int *paddleStart, int *paddleEnd, int *px, int *py, int *modx, int *mody, int *score, int *game_over) { + // Check if the ball hits top or bottom edges + if (*py == 1 || *py == V - 2) { + *mody *= -1; + } + + // Ball reaches the left wall (player loses) + if (*px == 1) { + *game_over = 1; + playerwin = 0; // Player loses + } + + // Ball hits the right wall (ball bounces back) + if (*px == H - 2) { + *modx *= -1; // Ball bounces off the right wall + } + + // Ball collides with player paddle + if (*px == 4) { + if (*py >= *paddleStart && *py <= *paddleEnd) { + *modx *= -1; // Ball bounces back from player paddle + (*score)++; // Increment player's score when they hit the ball + } + } + + // Ball movement + if (*game_over == 0) { + *px += (*modx); + *py += (*mody); + } + + // Handle paddle movement using the arrow keys + handleArrowKeys(paddleStart, paddleEnd); +} + +void edge(char map[V][H]) { + for (int i = 0; i < H; i++) { + map[0][i] = '-'; // Top border remains a hyphen + map[V - 1][i] = '_'; // Bottom border is now an underscore + } + for (int i = 0; i < V; i++) { + map[i][0] = '|'; + map[i][H - 1] = '|'; + } +} + +void player(char map[V][H], int paddleStart, int paddleEnd) { + for (int i = paddleStart; i <= paddleEnd; i++) { + map[i][3] = '#'; // Player paddle + } +} + +void ball(char map[V][H], int px, int py) { + map[py][px] = 'O'; // Ball +} + +void gotoxy(int x, int y) { + printf("\033[%d;%dH", y + 1, x + 1); // Move the cursor to (x, y) +} + +void clearScreen() { + printf("\033[2J"); // ANSI escape code to clear the screen + gotoxy(0, 0); // Move cursor to the top +} + +// Enable raw mode for real-time input +void enableRawMode() { + tcgetattr(STDIN_FILENO, &orig_termios); // Get the terminal attributes + struct termios raw = orig_termios; + raw.c_lflag &= ~(ECHO | ICANON); // Disable echo and canonical mode + tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw); // Set the terminal attributes +} + +// Disable raw mode and restore the terminal +void disableRawMode() { + tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios); // Restore original terminal attributes +} + +// Check if a key is pressed (non-blocking input) +int kbhit() { + struct termios oldt, newt; + int ch; + int oldf; + + tcgetattr(STDIN_FILENO, &oldt); // Get terminal attributes + newt = oldt; + newt.c_lflag &= ~(ICANON | ECHO); // Disable canonical mode and echo + tcsetattr(STDIN_FILENO, TCSANOW, &newt); // Set new terminal attributes + oldf = fcntl(STDIN_FILENO, F_GETFL, 0); + fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); + + ch = getchar(); // Check if a key is pressed + + tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // Restore old terminal attributes + fcntl(STDIN_FILENO, F_SETFL, oldf); + + if (ch != EOF) { + ungetc(ch, stdin); // If a key is pressed, put it back + return 1; + } + + return 0; +} + +// Handle the up and down arrow keys for paddle movement +void handleArrowKeys(int *paddleStart, int *paddleEnd) { + if (kbhit()) { + char key = getchar(); + if (key == '\033') { // First part of the escape sequence + getchar(); // Skip the '[' part of the sequence + switch(getchar()) { // Check the final part of the sequence + case 'A': // Up arrow + if (*paddleStart > 1) { + *paddleStart -= 1; + *paddleEnd -= 1; + } + break; + case 'B': // Down arrow + if (*paddleEnd < V - 2) { + *paddleStart += 1; + *paddleEnd += 1; + } + break; + } + } + } +} From 891f76e8deafa577152c531af394ec4c45899265 Mon Sep 17 00:00:00 2001 From: mike Date: Mon, 30 Sep 2024 16:51:56 -0700 Subject: [PATCH 03/10] pong syscalls prototyping added to kernel space --- kernel/defs.h | 5 +++++ kernel/syscall.c | 23 +++++++++++++++++++++++ kernel/syscall.h | 12 ++++++++++++ 3 files changed, 40 insertions(+) diff --git a/kernel/defs.h b/kernel/defs.h index a3c962b..b3490e7 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -141,6 +141,11 @@ int fetchstr(uint64, char*, int); int fetchaddr(uint64, uint64*); void syscall(); +// rawmode.c +extern int raw_mode_enabled; +void enable_raw_mode(void); +void disable_raw_mode(void); + // trap.c extern uint ticks; void trapinit(void); diff --git a/kernel/syscall.c b/kernel/syscall.c index ed65409..184a26b 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -102,6 +102,19 @@ extern uint64 sys_link(void); extern uint64 sys_mkdir(void); extern uint64 sys_close(void); +// Pong syscall prototypes +extern uint64 sys_nonblock_read(void); +extern uint64 sys_clear_screen(void); +extern uint64 sys_gotoxy(void); +extern uint64 sys_delay(void); +extern uint64 sys_vga_draw(void); +extern uint64 sys_cursor_move(void); + +// Raw Mode +extern uint64 sys_enable_raw_mode(void); +extern uint64 sys_disable_raw_mode(void); + + // An array mapping syscall numbers from syscall.h // to the function that handles the system call. static uint64 (*syscalls[])(void) = { @@ -126,6 +139,16 @@ static uint64 (*syscalls[])(void) = { [SYS_link] sys_link, [SYS_mkdir] sys_mkdir, [SYS_close] sys_close, + +// Pong syscalls +[SYS_nonblock_read] sys_nonblock_read, +[SYS_clear_screen] sys_clear_screen, +[SYS_gotoxy] sys_gotoxy, +[SYS_delay] sys_delay, +[SYS_vga_draw] sys_vga_draw, +[SYS_cursor_move] sys_cursor_move, +[SYS_enable_raw_mode] sys_enable_raw_mode, +[SYS_disable_raw_mode] sys_disable_raw_mode, }; void diff --git a/kernel/syscall.h b/kernel/syscall.h index bc5f356..ddd72d4 100644 --- a/kernel/syscall.h +++ b/kernel/syscall.h @@ -20,3 +20,15 @@ #define SYS_link 19 #define SYS_mkdir 20 #define SYS_close 21 + +// Pong system calls +#define SYS_nonblock_read 22 // For non-blocking input reading +#define SYS_clear_screen 23 // For clearing the terminal screen +#define SYS_gotoxy 24 // For moving the cursor +#define SYS_delay 25 // For delaying (replacing usleep) +#define SYS_vga_draw 26 // For drawing on the VGA terminal +#define SYS_cursor_move 27 // For moving the cursor (VGA) + +// Raw mode +#define SYS_enable_raw_mode 28 +#define SYS_disable_raw_mode 29 From e2861d83394fa63e437e41df0fa7aad0a6bcbdf4 Mon Sep 17 00:00:00 2001 From: mike Date: Mon, 30 Sep 2024 17:19:31 -0700 Subject: [PATCH 04/10] function implementations added to sysproc --- kernel/sysproc.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/kernel/sysproc.c b/kernel/sysproc.c index 1de184e..71e5df4 100644 --- a/kernel/sysproc.c +++ b/kernel/sysproc.c @@ -5,6 +5,7 @@ #include "memlayout.h" #include "spinlock.h" #include "proc.h" +#include "vga.h" uint64 sys_exit(void) @@ -89,3 +90,180 @@ sys_uptime(void) release(&tickslock); return xticks; } + +// Pong Add-ons // + +/** + * Reads a character from UART in non-blocking mode. + * + * This function reads from the UART input without blocking. If an escape sequence + * is detected, it processes it as part of an arrow key input. + * + * @return The character read, or 0 if no input is available. + */ +uint64 +sys_nonblock_read(void) +{ + static int escape_seq = 0; // Track escape sequence state + int ch = uartgetc(); // Non-blocking read from UART + + if (ch == -1) { + return 0; // No input available + } + + if (ch == '\033') { // Escape character + escape_seq = 1; // Start escape sequence + return 0; // Continue reading + } + + if (escape_seq == 1 && ch == '[') { + escape_seq = 2; // Continue escape sequence + return 0; + } + + if (escape_seq == 2) { + escape_seq = 0; // End escape sequence + return ch; // Return arrow key code ('A' for up, 'B' for down) + } + + return ch; // Return character if not part of an escape sequence +} + +/** + * Clears the screen using an ANSI escape sequence. + * + * Sends the necessary escape sequence to clear the terminal screen. + * + * @return Always returns 0. + */ +uint64 +sys_clear_screen(void) +{ + uartputc_sync('\033'); // ESC + uartputc_sync('['); + uartputc_sync('2'); + uartputc_sync('J'); // ANSI escape sequence to clear the screen + return 0; +} + +/** + * Moves the cursor to a specific (x, y) position on the screen. + * + * @param x The x-coordinate (column) to move the cursor to. + * @param y The y-coordinate (row) to move the cursor to. + * + * @return Always returns 0. + */ +uint64 +sys_gotoxy(void) +{ + int x, y; + + argint(0, &x); // Get x-coordinate from user space + argint(1, &y); // Get y-coordinate from user space + + uartputc_sync('\033'); // ESC + uartputc_sync('['); + uartputc_sync('0' + (y + 1)); // Convert y to ASCII (1-based coord. index) + uartputc_sync(';'); + uartputc_sync('0' + (x + 1)); // Convert x to ASCII (1-based coord. index) + uartputc_sync('H'); // ANSI escape sequence to move cursor + + return 0; +} + +/** + * Delays the execution for a specified number of milliseconds. + * + * @param milliseconds The number of milliseconds to delay. + * + * @return Always returns 0. + */ +uint64 +sys_delay(void) +{ + int milliseconds; + + argint(0, &milliseconds); // Get milliseconds from user space + int ticks_to_sleep = milliseconds / 10; // Convert milliseconds to ticks + + for (int i = 0; i < ticks_to_sleep; i++) { + sleep(0, &tickslock); // Sleep one tick at a time + } + + return 0; +} + +/** + * Draws a pixel at the specified (x, y) position on the VGA display. + * + * This function draws on the VGA screen at the provided coordinates. + * + * @param x The x-coordinate (column) to draw the pixel. + * @param y The y-coordinate (row) to draw the pixel. + * + * @return Always returns 0. + */ +uint64 +sys_vga_draw(void) +{ + int x, y; + + argint(0, &x); // Get screen x-coordinate from user space + argint(1, &y); // Get screen y-coordinate from user space + + vga_draw(x, y); + + return 0; +} + +/** + * Moves the VGA cursor to a specific (x, y) position. + * + * @param x The x-coordinate (column) to move the cursor to. + * @param y The y-coordinate (row) to move the cursor to. + * + * @return Always returns 0. + */ +uint64 +sys_cursor_move(void) +{ + int x, y; + + argint(0, &x); // Get x-coordinate from user space + argint(1, &y); // Get y-coordinate from user space + + vga_cursor_move(x, y); + + return 0; +} + +/** + * Enables raw input mode for the terminal. + * + * Configures the terminal to operate in raw mode, reading input character-by-character + * without input echoing. + * + * @return Always returns 0. + */ +uint64 +sys_enable_raw_mode(void) +{ + enable_raw_mode(); // enable raw mode + return 0; +} + +/** + * Disables raw input mode and restores the terminal to normal mode. + * + * Restores the terminal's normal input behavior, including canonical + * input processing and input echoing. + * + * @return Always returns 0. + */ +uint64 +sys_disable_raw_mode(void) +{ + disable_raw_mode(); // disable raw mode + return 0; +} From 2777003ddb2a22f7601d37bfcfa5bda0ef8ca390 Mon Sep 17 00:00:00 2001 From: mike Date: Mon, 30 Sep 2024 17:31:59 -0700 Subject: [PATCH 05/10] console interrupt handler updated to support raw mode --- kernel/console.c | 68 ++++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/kernel/console.c b/kernel/console.c index 05dc526..dcbeb27 100644 --- a/kernel/console.c +++ b/kernel/console.c @@ -137,44 +137,50 @@ consoleintr(int c) { acquire(&cons.lock); - switch(c){ - case C('P'): // Print process list. - procdump(); - break; - case C('U'): // Kill line. - while(cons.e != cons.w && - cons.buf[(cons.e-1) % INPUT_BUF_SIZE] != '\n'){ - cons.e--; - consputc(BACKSPACE); - } - break; - case C('H'): // Backspace - case '\x7f': // Delete key - if(cons.e != cons.w){ - cons.e--; - consputc(BACKSPACE); + if(raw_mode_enabled){ // In raw mode, handle input immediately w/o line buffering + if(c != 0 && cons.e - cons.r < INPUT_BUF_SIZE){ + cons.buf[cons.e++ % INPUT_BUF_SIZE] = c; + cons.w = cons.e; + wakeup(&cons.r); // Wake up read immediately } - break; - default: - if(c != 0 && cons.e-cons.r < INPUT_BUF_SIZE){ - c = (c == '\r') ? '\n' : c; + } else { // Default line-buffered behavior + switch(c){ + case C('P'): // Print process list. + procdump(); + break; + case C('U'): // Kill line. + while(cons.e != cons.w && + cons.buf[(cons.e-1) % INPUT_BUF_SIZE] != '\n'){ + cons.e--; + consputc(BACKSPACE); + } + break; + case C('H'): // Backspace + case '\x7f': // Delete key + if(cons.e != cons.w){ + cons.e--; + consputc(BACKSPACE); + } + break; + default: + if(c != 0 && cons.e-cons.r < INPUT_BUF_SIZE){ + c = (c == '\r') ? '\n' : c; - // echo back to the user. - consputc(c); + // Echo the character to the user + consputc(c); - // store for consumption by consoleread(). - cons.buf[cons.e++ % INPUT_BUF_SIZE] = c; + // Store for consumption by consoleread() + cons.buf[cons.e++ % INPUT_BUF_SIZE] = c; - if(c == '\n' || c == C('D') || cons.e-cons.r == INPUT_BUF_SIZE){ - // wake up consoleread() if a whole line (or end-of-file) - // has arrived. - cons.w = cons.e; - wakeup(&cons.r); + if(c == '\n' || c == C('D') || cons.e-cons.r == INPUT_BUF_SIZE){ + cons.w = cons.e; + wakeup(&cons.r); // Wake up read if a full line has arrived + } } + break; } - break; } - + release(&cons.lock); } From 9186104e3ad068b8c071422622e44f98622b28c3 Mon Sep 17 00:00:00 2001 From: mike Date: Mon, 30 Sep 2024 17:55:33 -0700 Subject: [PATCH 06/10] extended console functionality added to kernel space --- kernel/rawmode.c | 14 ++++++++++++++ kernel/sysproc.c | 8 ++++---- kernel/vga.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ kernel/vga.h | 8 ++++++++ 4 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 kernel/rawmode.c create mode 100644 kernel/vga.c create mode 100644 kernel/vga.h diff --git a/kernel/rawmode.c b/kernel/rawmode.c new file mode 100644 index 0000000..3519de7 --- /dev/null +++ b/kernel/rawmode.c @@ -0,0 +1,14 @@ +#include "types.h" + +// Global variable used to track raw enable/disable status +int raw_mode_enabled = 0; + +void enable_raw_mode() { + raw_mode_enabled = 1; + // TODO: may need to put additional non-canonical / raw logic +} + +void disable_raw_mode() { + raw_mode_enabled = 0; + // TODO: may need to put additional logic here to return the terminal back to norm. +} diff --git a/kernel/sysproc.c b/kernel/sysproc.c index 71e5df4..6eec189 100644 --- a/kernel/sysproc.c +++ b/kernel/sysproc.c @@ -209,10 +209,10 @@ sys_vga_draw(void) { int x, y; - argint(0, &x); // Get screen x-coordinate from user space - argint(1, &y); // Get screen y-coordinate from user space - - vga_draw(x, y); + argint(0, &x); // Get screen x-coordinate from user space + argint(1, &y); // Get screen y-coordinate from user space + argint(2, &color); // Get color + vga_draw(x, y, color); return 0; } diff --git a/kernel/vga.c b/kernel/vga.c new file mode 100644 index 0000000..595467f --- /dev/null +++ b/kernel/vga.c @@ -0,0 +1,49 @@ +#include "types.h" +#include "riscv.h" +#include "defs.h" +#include "memlayout.h" +#include "stdint.h" + +// VGA screen dimensions +#define VGA_WIDTH 80 +#define VGA_HEIGHT 25 + +// VGA video memory address +#define VGA_MEMORY (0xB8000 + KERNBASE) // Offset for VGA memory + +// VGA color codes +#define VGA_COLOR_BLACK 0 +#define VGA_COLOR_WHITE 15 + +// VGA screen buffer is 80x25 characters, each with two bytes (char and attribute) +volatile uint16_t *vga_buffer = (uint16_t *) VGA_MEMORY; + +// Set a character with color at x, y position on the VGA screen +void vga_draw(int x, int y, int color) { + if (x < 0 || x >= VGA_WIDTH || y < 0 || y >= VGA_HEIGHT) { + // Out of bounds check + return; + } + + // Calculate the position in the VGA buffer + int offset = y * VGA_WIDTH + x; + + // Draw a blank space with the provided color + vga_buffer[offset] = (color << 8) | ' '; +} + +// Memory-mapped I/O for RISC-V +void vga_cursor_move(int x, int y) { + // TODO: figure out outb logic + // Apparently, RISC-V doesn't support outb for cursor movement. + return; +} + +// Clear the VGA screen w/ spaces and background color +void vga_clear_screen() { + for (int y = 0; y < VGA_HEIGHT; y++) { + for (int x = 0; x < VGA_WIDTH; x++) { + vga_draw(x, y, VGA_COLOR_BLACK); // Clear the screen w/ black background + } + } +} diff --git a/kernel/vga.h b/kernel/vga.h new file mode 100644 index 0000000..9e7bd7b --- /dev/null +++ b/kernel/vga.h @@ -0,0 +1,8 @@ +#ifndef VGA_H +#define VGA_H + +// Declaring the VGA console functions +void vga_draw(int x, int y, int color); +void vga_cursor_move(int x, int y); + +#endif From 884472f8e4a886ee25a0b41cf95df26cabce6a48 Mon Sep 17 00:00:00 2001 From: mike Date: Mon, 30 Sep 2024 18:19:28 -0700 Subject: [PATCH 07/10] syscalls linked in user space --- user/ulib.c | 36 ++++++++++++++++++++++++++++++++++++ user/user.h | 9 +++++++++ user/usys.pl | 2 ++ 3 files changed, 47 insertions(+) diff --git a/user/ulib.c b/user/ulib.c index f0d62c5..26e44ea 100644 --- a/user/ulib.c +++ b/user/ulib.c @@ -2,6 +2,7 @@ #include "kernel/stat.h" #include "kernel/fcntl.h" #include "user/user.h" +#include "../kernel/syscall.h" // // wrapper so that it's OK if main() does not call exit(). @@ -191,3 +192,38 @@ memcpy(void *dst, const void *src, uint n) { return memmove(dst, src, n); } + +// Pong Additions Below + +int +nonblock_read(void) +{ + return syscall(SYS_nonblock_read); +} + + +int +clear_screen(void) +{ + return syscall(SYS_clear_screen); +} + + +int +gotoxy(int x, int y) +{ + return syscall(SYS_gotoxy, x, y); +} + + +int +delay(int milliseconds) // (usleep-like functionality) +{ + return syscall(SYS_delay, milliseconds); +} + +int +cursor_move(int x, int y) +{ + return syscall(SYS_cursor_move, x, y); +} diff --git a/user/user.h b/user/user.h index 2e6fc55..53883a2 100644 --- a/user/user.h +++ b/user/user.h @@ -23,6 +23,15 @@ char* sbrk(int); int sleep(int); int uptime(void); +// pong.c +int nonblock_read(void); // For non-blocking input +int clear_screen(void); // Clears the terminal window +int gotoxy(int x, int y); // Moves cursor to (x, y) +int delay(int milliseconds); // Delays execution for specified milliseconds +int cursor_move(int x, int y); // Moves the cursor to (x, y) for VGA +int enable_raw_mode(void); // Enables raw input mode +int disable_raw_mode(void); // Disables raw input mode + // ulib.c int stat(const char*, struct stat*); char* strcpy(char*, const char*); diff --git a/user/usys.pl b/user/usys.pl index 01e426e..fd956b0 100755 --- a/user/usys.pl +++ b/user/usys.pl @@ -36,3 +36,5 @@ sub entry { entry("sbrk"); entry("sleep"); entry("uptime"); +entry("enable_raw_mode"); +entry("disable_raw_mode"); From 2b69bb7a52a34b0daa8c490556d5e1197dda7cbb Mon Sep 17 00:00:00 2001 From: mike Date: Mon, 30 Sep 2024 19:22:06 -0700 Subject: [PATCH 08/10] game is starting to rebuild, but user input is not being tracked --- Makefile | 36 ++++--- kernel/sysproc.c | 2 +- user/pong.c | 249 +++++++++++++++++++++++++++++++++++++++++++++++ user/ulib.c | 17 ++++ user/user.h | 30 +++--- 5 files changed, 300 insertions(+), 34 deletions(-) create mode 100644 user/pong.c diff --git a/Makefile b/Makefile index 2584e4a..c52149c 100644 --- a/Makefile +++ b/Makefile @@ -28,24 +28,26 @@ OBJS = \ $K/sysfile.o \ $K/kernelvec.o \ $K/plic.o \ - $K/virtio_disk.o + $K/virtio_disk.o \ + $K/vga.o \ + $K/rawmode.o # riscv64-unknown-elf- or riscv64-linux-gnu- # perhaps in /opt/riscv/bin -#TOOLPREFIX = +# TOOLPREFIX = # Try to infer the correct TOOLPREFIX if not set ifndef TOOLPREFIX TOOLPREFIX := $(shell if riscv64-unknown-elf-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \ - then echo 'riscv64-unknown-elf-'; \ - elif riscv64-linux-gnu-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \ - then echo 'riscv64-linux-gnu-'; \ - elif riscv64-unknown-linux-gnu-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \ - then echo 'riscv64-unknown-linux-gnu-'; \ - else echo "***" 1>&2; \ - echo "*** Error: Couldn't find a riscv64 version of GCC/binutils." 1>&2; \ - echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \ - echo "***" 1>&2; exit 1; fi) + then echo 'riscv64-unknown-elf-'; \ + elif riscv64-linux-gnu-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \ + then echo 'riscv64-linux-gnu-'; \ + elif riscv64-unknown-linux-gnu-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \ + then echo 'riscv64-unknown-linux-gnu-'; \ + else echo "***" 1>&2; \ + echo "*** Error: Couldn't find a riscv64 version of GCC/binutils." 1>&2; \ + echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \ + echo "***" 1>&2; exit 1; fi) endif QEMU = qemu-system-riscv64 @@ -74,7 +76,7 @@ endif LDFLAGS = -z max-page-size=4096 $K/kernel: $(OBJS) $K/kernel.ld $U/initcode - $(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS) + $(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS) $(OBJDUMP) -S $K/kernel > $K/kernel.asm $(OBJDUMP) -t $K/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $K/kernel.sym @@ -109,10 +111,6 @@ $U/_forktest: $U/forktest.o $(ULIB) mkfs/mkfs: mkfs/mkfs.c $K/fs.h $K/param.h gcc -Werror -Wall -I. -o mkfs/mkfs mkfs/mkfs.c -# Prevent deletion of intermediate files, e.g. cat.o, after first build, so -# that disk image changes after first build are persistent until clean. More -# details: -# http://www.gnu.org/software/make/manual/html_node/Chained-Rules.html .PRECIOUS: %.o UPROGS=\ @@ -132,18 +130,19 @@ UPROGS=\ $U/_grind\ $U/_wc\ $U/_zombie\ + $U/_pong fs.img: mkfs/mkfs README.md $(UPROGS) mkfs/mkfs fs.img README.md $(UPROGS) -include kernel/*.d user/*.d -clean: +clean: rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \ */*.o */*.d */*.asm */*.sym \ $U/initcode $U/initcode.out $K/kernel fs.img \ mkfs/mkfs .gdbinit \ - $U/usys.S \ + $U/usys.S \ $(UPROGS) # try to generate a unique GDB port @@ -170,4 +169,3 @@ qemu: $K/kernel fs.img qemu-gdb: $K/kernel .gdbinit fs.img @echo "*** Now run 'gdb' in another window." 1>&2 $(QEMU) $(QEMUOPTS) -S $(QEMUGDB) - diff --git a/kernel/sysproc.c b/kernel/sysproc.c index 6eec189..c5231f8 100644 --- a/kernel/sysproc.c +++ b/kernel/sysproc.c @@ -207,7 +207,7 @@ sys_delay(void) uint64 sys_vga_draw(void) { - int x, y; + int x, y, color; argint(0, &x); // Get screen x-coordinate from user space argint(1, &y); // Get screen y-coordinate from user space diff --git a/user/pong.c b/user/pong.c new file mode 100644 index 0000000..1a21e48 --- /dev/null +++ b/user/pong.c @@ -0,0 +1,249 @@ +#include "user.h" +#include "kernel/syscall.h" + +// Declare the sys call wrappers +extern int enable_raw_mode(); +extern int disable_raw_mode(); +extern int gotoxy(int x, int y); + +#define V 15 // Adjusted height of the game board +#define H 40 // Adjusted width of the game board + +// Function declarations +void playGame(); +void gameRules(); +int gotoxy(int x, int y); // Must return int, as per user.h +void clearScreen(); +void draw(char map[V][H], int score); +void show(char map[V][H], int score); +void update(char map[V][H], int paddleStart, int paddleEnd, int py, int px); +void input(char map[V][H], int *paddleStart, int *paddleEnd, int *px, int *py, int *modx, int *mody, int *score, int *gol); +void edge(char map[V][H]); +void player(char map[V][H], int paddleStart, int paddleEnd); +void ball(char map[V][H], int py, int px); +void handleArrowKeys(int *paddleStart, int *paddleEnd); +void busy_wait(int milliseconds); +int getchar_direct(); +int kbhit(); + +// Game variables +int playerwin = 0; + +int main() { + int choice; + + while (1) { + printf("\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n"); + printf("| Ping Pong Game |"); + printf("\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n"); + printf("\n1. Game Rules"); + printf("\n2. Play Game"); + printf("\n3. Quit\n"); + + choice = getchar_direct() - '0'; // Convert char to int + + switch (choice) { + case 1: + gameRules(); + break; + case 2: + playGame(); + break; + case 3: + return 0; + default: + printf("Invalid choice\n"); + } + } + return 0; +} + +// Display game rules +void gameRules() { + clearScreen(); + gotoxy(0, 0); + printf("\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n"); + printf("| Game Rules |\n"); + printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n"); + printf("- Use the up and down arrow keys to move your paddle up and down.\n"); + printf("- The goal is to prevent the ball from passing your paddle on the left.\n"); + printf("- The ball will bounce off the right wall.\n"); + printf("- Every time you hit the ball, you earn 1 point.\n"); + printf("\nPress any key to return to the main menu...\n"); + + getchar_direct(); // Wait for user input +} + +void playGame() { + + enable_raw_mode(); // Enable raw mode before game start + + char map[V][H]; + int px = H / 2, py = V / 2; // Ball position + int modx = -1, mody = -1; // Ball direction + int paddleStart = V / 2 - 2, paddleEnd = V / 2 + 2; // Player paddle + int score = 0; // Player score + int gol = 0; + + while (1) { + gol = 0; + draw(map, score); // Clear the map and display the score + input(map, &paddleStart, &paddleEnd, &px, &py, &modx, &mody, &score, &gol); + update(map, paddleStart, paddleEnd, py, px); // Update game state + show(map, score); // Show the updated map and score + + busy_wait(500); // Slow down or speed up the game loop (measured in ms between updates) + + if (gol == 1) { + break; + } + } + + // Disable raw mode after game ends + disable_raw_mode(); +} + +// Function to draw the game board +void draw(char map[V][H], int score) { + int i, j; + clearScreen(); + for (i = 0; i < V; i++) { + for (j = 0; j < H; j++) { + map[i][j] = ' '; + } + } +} + +// Function to display the game state +void show(char map[V][H], int score) { + int i, j; + clearScreen(); + gotoxy(0, 0); // Reset cursor position + printf(" --------------------------------------\n"); // Dispalys row of dashes above the score + printf(" Score: %d\n", score); + for (i = 0; i < V; i++) { + for (j = 0; j < H; j++) { + printf("%c", map[i][j]); + } + printf("\n"); + } +} + +// Function to pdate the game state (ball and paddle) +void update(char map[V][H], int paddleStart, int paddleEnd, int py, int px) { + edge(map); + player(map, paddleStart, paddleEnd); + ball(map, px, py); +} + +// Function to handle user input and ball movement +void input(char map[V][H], int *paddleStart, int *paddleEnd, int *px, int *py, int *modx, int *mody, int *score, int *gol) { + // Check if the ball hits top or bottom edges + if (*py == 1 || *py == V - 2) { + *mody *= -1; + } + + // Ball reaches the left wall (player loses) + if (*px == 1) { + *gol = 1; + playerwin = 0; // Player loses + } + + // Ball hits the right wall (ball bounces back) + if (*px == H - 2) { + *modx *= -1; // Ball bounces off the right wall + } + + // Ball collides with player paddle + if (*px == 4) { + if (*py >= *paddleStart && *py <= *paddleEnd) { + *modx *= -1; // Ball bounces back from player paddle + (*score)++; // Increment player's score when they hit the ball + } + } + + // Ball movement + if (*gol == 0) { + *px += (*modx); + *py += (*mody); + } + + // Handle paddle movement using the arrow keys + handleArrowKeys(paddleStart, paddleEnd); +} + +// Function to draw the edges of the game board +void edge(char map[V][H]) { + for (int i = 0; i < H; i++) { + map[0][i] = '-'; // Top border remains a hyphen + map[V - 1][i] = '_'; // Bottom border is now an underscore + } + for (int i = 0; i < V; i++) { + map[i][0] = '|'; + map[i][H - 1] = '|'; + } +} + +// Function to draw the player paddle +void player(char map[V][H], int paddleStart, int paddleEnd) { + for (int i = paddleStart; i <= paddleEnd; i++) { + map[i][3] = '#'; // Player paddle + } +} + +// Function to draw the ball +void ball(char map[V][H], int px, int py) { + map[py][px] = 'O'; // Ball +} + +// Function to clear the screen +void clearScreen() { + clear_screen(); // Call without sys_ prefix +} + +// Simple busy-wait delay function +void busy_wait(int milliseconds) { + volatile int i, j; + for (i = 0; i < milliseconds * 1000; i++) { + for (j = 0; j < 100; j++) { + // Busy-wait to simulate delay + } + } +} + +// Direct implementation of getchar using read +int getchar_direct() { + char c; + read(0, &c, 1); // Read one character from stdin (fd 0) + return c; +} + +// Check if a key is pressed (non-blocking input) +int kbhit() { + int result = nonblock_read(); // Call without sys_ prefix + return result != 0; // Return true if there's input +} + +// Handle the up and down arrow keys for paddle movement +void handleArrowKeys(int *paddleStart, int *paddleEnd) { + if (kbhit()) { + char key = getchar_direct(); + if (key == '\033') { // First part of the escape sequence + getchar_direct(); // Skip the '[' part of the sequence + switch(getchar_direct()) { // Check the final part of the sequence + case 'A': // Up arrow key + if (*paddleStart > 1) { + (*paddleStart)--; + (*paddleEnd)--; + } + break; + case 'B': // Down arrow key + if (*paddleEnd < V - 2) { + (*paddleStart)++; + (*paddleEnd)++; + } + break; + } + } + } +} diff --git a/user/ulib.c b/user/ulib.c index 26e44ea..fc2515c 100644 --- a/user/ulib.c +++ b/user/ulib.c @@ -194,6 +194,23 @@ memcpy(void *dst, const void *src, uint n) } // Pong Additions Below +// Call syscalls from user space + +// Generic system call function +uint64 +syscall(int num, ...) +{ + // Variadic argument handling for system call arguments + uint64 a0 = 0, a1 = 0, a2 = 0, a3 = 0, a4 = 0, a5 = 0; + register uint64 syscall_num asm("a7") = num; + + asm volatile("ecall" + : "=r"(a0) + : "r"(syscall_num), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5) + : "memory"); + + return a0; +} int nonblock_read(void) diff --git a/user/user.h b/user/user.h index 53883a2..e032347 100644 --- a/user/user.h +++ b/user/user.h @@ -1,3 +1,5 @@ +#include "../kernel/syscall.h" + struct stat; // system calls @@ -24,29 +26,29 @@ int sleep(int); int uptime(void); // pong.c -int nonblock_read(void); // For non-blocking input -int clear_screen(void); // Clears the terminal window -int gotoxy(int x, int y); // Moves cursor to (x, y) -int delay(int milliseconds); // Delays execution for specified milliseconds -int cursor_move(int x, int y); // Moves the cursor to (x, y) for VGA -int enable_raw_mode(void); // Enables raw input mode -int disable_raw_mode(void); // Disables raw input mode +int nonblock_read(void); +int clear_screen(void); +int gotoxy(int x, int y); +int delay(int milliseconds); +int cursor_move(int x, int y); +int enable_raw_mode(void); +int disable_raw_mode(void); // ulib.c int stat(const char*, struct stat*); char* strcpy(char*, const char*); -void *memmove(void*, const void*, int); +void* memmove(void*, const void*, int); char* strchr(const char*, char c); int strcmp(const char*, const char*); void fprintf(int, const char*, ...); void printf(const char*, ...); char* gets(char*, int max); int fgets(int fd, char*, int max); -int getline(char **lineptr, uint *n, int fd); -uint strlen(const char*); -void* memset(void*, int, uint); -void* malloc(uint); +int getline(char **lineptr, unsigned int *n, int fd); +unsigned int strlen(const char*); +void* memset(void*, int, unsigned int); +void* malloc(unsigned int); void free(void*); int atoi(const char*); -int memcmp(const void *, const void *, uint); -void *memcpy(void *, const void *, uint); +int memcmp(const void *, const void *, unsigned int); +void* memcpy(void *, const void *, unsigned int); From 19e3df6ecd0f21114bebf8fb6e0af58bf44c26c1 Mon Sep 17 00:00:00 2001 From: mike Date: Mon, 30 Sep 2024 19:58:33 -0700 Subject: [PATCH 09/10] main pong.c commented and cleaned up --- user/pong.c | 183 ++++++++++++++++++++++++++-------------------------- 1 file changed, 90 insertions(+), 93 deletions(-) diff --git a/user/pong.c b/user/pong.c index 1a21e48..49299d8 100644 --- a/user/pong.c +++ b/user/pong.c @@ -1,23 +1,23 @@ #include "user.h" #include "kernel/syscall.h" -// Declare the sys call wrappers +// Declare system call wrappers extern int enable_raw_mode(); extern int disable_raw_mode(); extern int gotoxy(int x, int y); -#define V 15 // Adjusted height of the game board -#define H 40 // Adjusted width of the game board +#define V 15 // Height of the game board +#define H 40 // Width of the game board // Function declarations void playGame(); void gameRules(); -int gotoxy(int x, int y); // Must return int, as per user.h -void clearScreen(); +void displayMenu(); +int gotoxy(int x, int y); void draw(char map[V][H], int score); void show(char map[V][H], int score); void update(char map[V][H], int paddleStart, int paddleEnd, int py, int px); -void input(char map[V][H], int *paddleStart, int *paddleEnd, int *px, int *py, int *modx, int *mody, int *score, int *gol); +void input(char map[V][H], int *paddleStart, int *paddleEnd, int *px, int *py, int *modx, int *mody, int *score, int *gameOver); void edge(char map[V][H]); void player(char map[V][H], int paddleStart, int paddleEnd); void ball(char map[V][H], int py, int px); @@ -26,19 +26,14 @@ void busy_wait(int milliseconds); int getchar_direct(); int kbhit(); -// Game variables +// Game state variables int playerwin = 0; int main() { int choice; while (1) { - printf("\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n"); - printf("| Ping Pong Game |"); - printf("\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n"); - printf("\n1. Game Rules"); - printf("\n2. Play Game"); - printf("\n3. Quit\n"); + displayMenu(); choice = getchar_direct() - '0'; // Convert char to int @@ -58,125 +53,134 @@ int main() { return 0; } +// Function to display the main menu +void displayMenu() { + printf("\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n"); + printf("\n\n"); + printf(" _______ _______ ______ _______ \n"); + printf(" | _ | _ | _ \\| _ |\n"); + printf(" |. 1 |. | |. | |. |___|\n"); + printf(" |. ____|. | |. | |. | |\n"); + printf(" |: | |: 1 |: | |: 1 |\n"); + printf(" |::.| |::.. . |::.| |::.. . |\n"); + printf(" `---' `-------`--- ---`-------'\n"); + printf("\n\n"); + printf("\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n"); + printf("1. Game Rules\n"); + printf("2. Play Game\n"); + printf("3. Quit\n"); +} + // Display game rules void gameRules() { - clearScreen(); + clear_screen(); gotoxy(0, 0); printf("\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n"); printf("| Game Rules |\n"); printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n"); - printf("- Use the up and down arrow keys to move your paddle up and down.\n"); - printf("- The goal is to prevent the ball from passing your paddle on the left.\n"); + printf("- Use the up and down arrow keys to move your paddle.\n"); + printf("- Prevent the ball from passing your paddle.\n"); printf("- The ball will bounce off the right wall.\n"); - printf("- Every time you hit the ball, you earn 1 point.\n"); + printf("- Each hit scores 1 point.\n"); printf("\nPress any key to return to the main menu...\n"); - - getchar_direct(); // Wait for user input + getchar_direct(); } +// Main game loop void playGame() { - - enable_raw_mode(); // Enable raw mode before game start + enable_raw_mode(); char map[V][H]; - int px = H / 2, py = V / 2; // Ball position - int modx = -1, mody = -1; // Ball direction - int paddleStart = V / 2 - 2, paddleEnd = V / 2 + 2; // Player paddle - int score = 0; // Player score - int gol = 0; + int px = H / 2, py = V / 2; + int modx = -1, mody = -1; + int paddleStart = V / 2 - 2, paddleEnd = V / 2 + 2; + int score = 0; + int gameOver = 0; while (1) { - gol = 0; - draw(map, score); // Clear the map and display the score - input(map, &paddleStart, &paddleEnd, &px, &py, &modx, &mody, &score, &gol); - update(map, paddleStart, paddleEnd, py, px); // Update game state - show(map, score); // Show the updated map and score + gameOver = 0; + draw(map, score); + input(map, &paddleStart, &paddleEnd, &px, &py, &modx, &mody, &score, &gameOver); + update(map, paddleStart, paddleEnd, py, px); + show(map, score); - busy_wait(500); // Slow down or speed up the game loop (measured in ms between updates) + busy_wait(500); - if (gol == 1) { + if (gameOver == 1) { break; } } - // Disable raw mode after game ends disable_raw_mode(); } -// Function to draw the game board +// Clear and initialize game board void draw(char map[V][H], int score) { - int i, j; - clearScreen(); - for (i = 0; i < V; i++) { - for (j = 0; j < H; j++) { + clear_screen(); + for (int i = 0; i < V; i++) { + for (int j = 0; j < H; j++) { map[i][j] = ' '; } } } -// Function to display the game state +// Display game state (paddle, ball, and score) void show(char map[V][H], int score) { - int i, j; - clearScreen(); - gotoxy(0, 0); // Reset cursor position - printf(" --------------------------------------\n"); // Dispalys row of dashes above the score + clear_screen(); + gotoxy(0, 0); + printf(" --------------------------------------\n"); printf(" Score: %d\n", score); - for (i = 0; i < V; i++) { - for (j = 0; j < H; j++) { + for (int i = 0; i < V; i++) { + for (int j = 0; j < H; j++) { printf("%c", map[i][j]); } - printf("\n"); + printf("\n"); } } -// Function to pdate the game state (ball and paddle) +// Update the game state (ball movement, paddle position) void update(char map[V][H], int paddleStart, int paddleEnd, int py, int px) { edge(map); player(map, paddleStart, paddleEnd); ball(map, px, py); } -// Function to handle user input and ball movement -void input(char map[V][H], int *paddleStart, int *paddleEnd, int *px, int *py, int *modx, int *mody, int *score, int *gol) { - // Check if the ball hits top or bottom edges +// Handle ball movement, paddle interaction, and scoring +void input(char map[V][H], int *paddleStart, int *paddleEnd, int *px, int *py, int *modx, int *mody, int *score, int *gameOver) { if (*py == 1 || *py == V - 2) { *mody *= -1; } - // Ball reaches the left wall (player loses) + // Player loses when the ball reaches the left wall if (*px == 1) { - *gol = 1; - playerwin = 0; // Player loses + *gameOver = 1; + playerwin = 0; } - // Ball hits the right wall (ball bounces back) + // Ball bounces off the right wall if (*px == H - 2) { - *modx *= -1; // Ball bounces off the right wall + *modx *= -1; } - // Ball collides with player paddle - if (*px == 4) { - if (*py >= *paddleStart && *py <= *paddleEnd) { - *modx *= -1; // Ball bounces back from player paddle - (*score)++; // Increment player's score when they hit the ball - } + // Ball hits the paddle + if (*px == 4 && *py >= *paddleStart && *py <= *paddleEnd) { + *modx *= -1; + (*score)++; } - // Ball movement - if (*gol == 0) { + if (*gameOver == 0) { *px += (*modx); *py += (*mody); } - // Handle paddle movement using the arrow keys handleArrowKeys(paddleStart, paddleEnd); } -// Function to draw the edges of the game board +// Draw the edges of the game board void edge(char map[V][H]) { for (int i = 0; i < H; i++) { - map[0][i] = '-'; // Top border remains a hyphen - map[V - 1][i] = '_'; // Bottom border is now an underscore + map[0][i] = '-'; + map[V - 1][i] = '_'; } for (int i = 0; i < V; i++) { map[i][0] = '|'; @@ -184,66 +188,59 @@ void edge(char map[V][H]) { } } -// Function to draw the player paddle +// Draw the player paddle void player(char map[V][H], int paddleStart, int paddleEnd) { for (int i = paddleStart; i <= paddleEnd; i++) { - map[i][3] = '#'; // Player paddle + map[i][3] = '#'; } } -// Function to draw the ball +// Draw the ball void ball(char map[V][H], int px, int py) { - map[py][px] = 'O'; // Ball + map[py][px] = 'O'; } -// Function to clear the screen -void clearScreen() { - clear_screen(); // Call without sys_ prefix -} - -// Simple busy-wait delay function +// Busy-wait to delay game loop void busy_wait(int milliseconds) { volatile int i, j; for (i = 0; i < milliseconds * 1000; i++) { - for (j = 0; j < 100; j++) { - // Busy-wait to simulate delay - } + for (j = 0; j < 100; j++) {} } } -// Direct implementation of getchar using read +// Directly get a character from user input int getchar_direct() { char c; - read(0, &c, 1); // Read one character from stdin (fd 0) + read(0, &c, 1); return c; } -// Check if a key is pressed (non-blocking input) +// Check if a key is pressed int kbhit() { - int result = nonblock_read(); // Call without sys_ prefix - return result != 0; // Return true if there's input + int result = nonblock_read(); + return result != 0; } -// Handle the up and down arrow keys for paddle movement +// Handle up and down arrow keys to move paddle void handleArrowKeys(int *paddleStart, int *paddleEnd) { if (kbhit()) { char key = getchar_direct(); - if (key == '\033') { // First part of the escape sequence - getchar_direct(); // Skip the '[' part of the sequence - switch(getchar_direct()) { // Check the final part of the sequence - case 'A': // Up arrow key + if (key == '\033') { + getchar_direct(); + switch(getchar_direct()) { + case 'A': if (*paddleStart > 1) { (*paddleStart)--; (*paddleEnd)--; } break; - case 'B': // Down arrow key + case 'B': if (*paddleEnd < V - 2) { (*paddleStart)++; (*paddleEnd)++; } break; } - } + } } } From 00deeac9a57b8721cb8d767f49fc94a7c94b3bbe Mon Sep 17 00:00:00 2001 From: mike Date: Mon, 30 Sep 2024 20:29:27 -0700 Subject: [PATCH 10/10] README.md file updated with project progress and future aspirations --- README.md | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 205a7c7..1524524 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,111 @@ # FogOS -![FogOS](docs/fogos.gif) +Project 1: Pong Command Integration for FogOS -. +Name: Michael H. Smith +Project: Terminal-Based Pong Game + +Description: + +This project implements a text-based version of the classic Atari game, Pong. +A single-player version of the game, the user controls a paddle and attempts +to prevent the ball from passing their paddle while the ball bounces back +from the opposite wall. + +The Pong command is designed to integrate with the existing OS and utilizes +several system calls for input handling and screen manipulation. The +implementation faced challenges with real-time user input, and while it is +functional, some areas—such as non-blocking input—still require further +refinement. + +Features: + +* Real-Time Input: The game relies on custom system calls to handle user input + in real time, although there are still challenges in making this fully + non-blocking. + +* Basic Game Mechanics: The player controls a paddle using the arrow keys, and + the ball bounces back and forth in the terminal window. + +* Game Logic: The game ends when the player fails to prevent the ball from + passing the paddle, and the score is displayed in the terminal. + +Files Modified/Created: + +Modified: + +* Makefile: Updated to compile pong.c along with the rest of the user programs + and link the necessary system calls. + +* kernel/console.c: Adjustments made for raw mode and real-time input handling + in conjunction with the new system calls. + +* kernel/defs.h: Updated with new function prototypes related to input handling + for the Pong game. + +* kernel/syscall.c: Integrated new system calls for cursor movement, clearing + the screen, and handling non-blocking input. + +* kernel/syscall.h: Added definitions for new system calls required for the + Pong command. + +* kernel/sysproc.c: Additional system calls implemented to handle user input + and terminal manipulations. + +* user/ulib.c: System calls integrated here to allow the Pong game to interact + with the terminal (e.g., moving the cursor and clearing the screen). + +* user/user.h: Function prototypes updated to include new system calls and + utilities needed for the Pong game. + +* user/usys.pl: System call numbers updated to accommodate new functions. + +Created: + +* kernel/rawmode.c: Created to handle raw input mode for the terminal, enabling + real-time input during the Pong game. + +* kernel/vga.c & kernel/vga.h: Implemented to support VGA-like cursor movement + and screen clearing features in the terminal. + +* user/pong.c: Main game logic and implementation of the Pong game, handling + game mechanics, input, and rendering. + +Challenges: + +The main challenge encountered in this project was implementing non-blocking +input. Due to the complexities involved in handling user input within a +terminal environment, input is still partially blocking. Furthermore, due to +the issues with input handling, the initially planned difficulty modes (easy, +medium, hard) were not implemented, as the underlying game mechanics were not +yet fully stable. + +Future Enhancements: + +* Non-Blocking Input: Further work is needed to ensure that the game can + process input without stalling, allowing for smoother and more responsive + gameplay. + +* Difficulty Modes: Once the input handling is refined, flags can be added to + adjust the difficulty by altering the ball's speed and the game's refresh + rate. + +* Multiplayer Mode: A future enhancement could include adding a multiplayer + mode, allowing two players to control separate paddles. + +* Game Logic Improvements: The ball's trajectory and speed could be adjusted + to make the gameplay more dynamic and challenging. + +Lessons Learned: + +Through the process of developing the Pong command, I gained a deeper +understanding of how to implement system calls in the xv6 OS. Specifically, I +learned how to manipulate the terminal by moving the cursor and clearing the +screen via system calls, as well as enabling and disabling raw input mode for +real-time user interaction. + +Additional Notes: + +The repository includes a fully-functional C template of the Pong game logic +in the docs folder, which was used as a reference during this development.