diff --git a/notes/work-summary-2026-04-16-to-18.md b/notes/work-summary-2026-04-16-to-18.md new file mode 100644 index 0000000..07d96ff --- /dev/null +++ b/notes/work-summary-2026-04-16-to-18.md @@ -0,0 +1,7 @@ +I developed a simple application framework inside my OS by creating a menu system that lets the user switch between applications, currently music and Snake. I implemented a basic terminal input method using keyboard interrupts so the user can select an application from the menu and return back to it after use. This demonstrates integration between screen output, keyboard handling, and overall OS control flow. + +For the Snake application, I designed a full game loop with its own game state, including the snake, food, score, and board representation. I used dynamic memory allocation to create and destroy the game state, which shows practical use of memory management in my OS. The game uses PIT/timing to control movement speed and game pacing, while keyboard input is used in real time to change direction, restart, or quit. + +I also integrated sound through the PC speaker to give feedback for events such as eating food, dying, and winning. Overall, this task shows integration of multiple OS components, real-time system programming, practical application of operating system concepts, and creative problem-solving through building an interactive menu-driven game environment. + +Finally everything is printed to the screen using existing TerminalWrite...(); infrastructure. \ No newline at end of file diff --git a/src/OSDev_18/CMakeLists.txt b/src/OSDev_18/CMakeLists.txt index 4bedf51..a32e753 100644 --- a/src/OSDev_18/CMakeLists.txt +++ b/src/OSDev_18/CMakeLists.txt @@ -72,6 +72,7 @@ add_executable(uiaos-kernel src/memory.c src/pit.c src/songPlayer.c + src/snake.c ) diff --git a/src/OSDev_18/include/kernel/keyboard.h b/src/OSDev_18/include/kernel/keyboard.h index 876f15a..ac4448f 100644 --- a/src/OSDev_18/include/kernel/keyboard.h +++ b/src/OSDev_18/include/kernel/keyboard.h @@ -6,6 +6,8 @@ #define KEYBOARD_DATA_PORT 0x60 #define KEYBOARD_BUFFER_SIZE 256 +char GetLastKeyPressed(void); + void KeyboardHandler(struct Registers* registers); #endif \ No newline at end of file diff --git a/src/OSDev_18/include/kernel/pit.h b/src/OSDev_18/include/kernel/pit.h index c8e1ea1..8215241 100644 --- a/src/OSDev_18/include/kernel/pit.h +++ b/src/OSDev_18/include/kernel/pit.h @@ -17,6 +17,8 @@ #define DIVIDER (PIT_BASE_FREQ / TARGET_FREQ) #define TICKS_PER_MS (TARGET_FREQ / TARGET_FREQ) // = 1, needed for converting ms into ticks +uint32_t GetCurrentTick(void); + void PitInitialize(); void SleepInterrupt(uint32_t ticks); void SleepBusy(uint32_t milliseconds); diff --git a/src/OSDev_18/include/kernel/terminal.h b/src/OSDev_18/include/kernel/terminal.h index 11d4b97..0a31658 100644 --- a/src/OSDev_18/include/kernel/terminal.h +++ b/src/OSDev_18/include/kernel/terminal.h @@ -26,6 +26,7 @@ enum vga_colour{ uint8_t VgaEntryColour(enum vga_colour fg, enum vga_colour bg); uint16_t VgaEntry(unsigned char uc, uint8_t color); +void TerminalClear(void); void TerminalEntryAt(char c, uint8_t colour, size_t x, size_t y); void TerminalInitialize(void); void TerminalSetColour(uint8_t colour); @@ -35,5 +36,6 @@ void TerminalWrite(const char* data, size_t size); void TerminalWriteString(const char* data); void TerminalWriteUInt(uint32_t num); void TerminalWriteHex(uint32_t memory); +char TerminalGetChar(void); #endif \ No newline at end of file diff --git a/src/OSDev_18/include/snakeApp/snake.h b/src/OSDev_18/include/snakeApp/snake.h index 79618bb..c818557 100644 --- a/src/OSDev_18/include/snakeApp/snake.h +++ b/src/OSDev_18/include/snakeApp/snake.h @@ -1,8 +1,11 @@ #ifndef SNAKEAPP_SNAKE_H #define SNAKEAPP_SNAKE_H -#define BOARD_SIZE 25 +#include + +#define BOARD_SIZE 15 #define SNAKE_MAX_LENGTH (BOARD_SIZE * BOARD_SIZE) +#define GAME_SPEED_MS 500 enum Direction { UP, @@ -24,7 +27,7 @@ struct SnakeSegment { }; struct Snake { - int length; + uint32_t length; enum Direction direction; struct SnakeSegment body[SNAKE_MAX_LENGTH]; }; @@ -34,14 +37,36 @@ struct Food { int y; }; -void InitializeBoard(void); +struct GameState { + char board[BOARD_SIZE][BOARD_SIZE][3]; + struct Snake* snake; + struct Food* food; + uint32_t score; + uint32_t rngState; +}; + +struct GameState* CreateGame(void); +void DestroyGame(struct GameState* game); +void ResetGame(struct GameState* game); + +void InitializeBoard(struct GameState* game); void InitializeSnake(struct Snake* snake); void InitializeFood(struct Food* food); + +uint32_t Random(uint32_t* rngState); +void HandleInput(struct GameState* game, char input); + +void PlayFoodSound(void); +void PlayDeathSound(void); +void PlayWinSound(void); + struct SnakeSegment MoveSnake(struct Snake* snake); -void SpawnFood(struct Snake* snake, struct Food* food); +void SpawnFood(struct GameState* game); void AddSegment(struct Snake* snake, int x, int y); enum CollisionType CheckCollision(struct Snake* snake, struct Food* food); -void DrawBoard(struct Snake* snake, struct Food* food); + +void DrawBoard(struct GameState* game); + void PlayGame(void); #endif \ No newline at end of file diff --git a/src/OSDev_18/src/kernel.c b/src/OSDev_18/src/kernel.c index 0e2bf77..49dfa71 100644 --- a/src/OSDev_18/src/kernel.c +++ b/src/OSDev_18/src/kernel.c @@ -8,6 +8,7 @@ #include #include #include +#include extern uint32_t end; @@ -30,11 +31,9 @@ void PlayMusic(void) { return; } - while(1) { - for(uint32_t i = 0; i < songCount; i++) { - player->play_song(&songs[i]); - SleepInterrupt(1000); - } + for(uint32_t i = 0; i < songCount; i++) { + player->play_song(&songs[i]); + SleepInterrupt(1000); } } @@ -50,37 +49,23 @@ void main(void) { InitPaging(); PrintMemoryLayout(); - /* + while (1) { + TerminalClear(); + TerminalWriteString("Enter application number (0 for music, 1 for snake): "); + char input = TerminalGetChar(); - void* memory1 = malloc(48261); - void* memory2 = malloc(27261); - void* memory3 = malloc(12617); - - TerminalWriteString("memory1 = "); - TerminalWriteHex((uint32_t)memory1); - TerminalWriteString("\n"); - - TerminalWriteString("memory2 = "); - TerminalWriteHex((uint32_t)memory2); - TerminalWriteString("\n"); - - TerminalWriteString("memory3 = "); - TerminalWriteHex((uint32_t)memory3); - TerminalWriteString("\n"); - - free(memory2); - - void* memory4 = malloc(1000); - - TerminalWriteString("memory4 = "); - TerminalWriteHex((uint32_t)memory4); - TerminalWriteString("\n"); - - SleepTest(); - - */ - - PlayMusic(); + switch (input) { + case '0': + PlayMusic(); + break; + case '1': + PlayGame(); + break; + default: + TerminalWriteString("Invalid application number.\n"); + break; + } + } for (;;) { __asm__ volatile("hlt"); diff --git a/src/OSDev_18/src/keyboard.c b/src/OSDev_18/src/keyboard.c index a62f24f..7c87685 100644 --- a/src/OSDev_18/src/keyboard.c +++ b/src/OSDev_18/src/keyboard.c @@ -7,6 +7,8 @@ static uint32_t index = 0; static uint8_t keyboardBuffer[KEYBOARD_BUFFER_SIZE]; +static volatile char lastKeyPressed = 0; + static const char scancodeToAscii[128] = { 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', @@ -19,6 +21,12 @@ static const char scancodeToAscii[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +char GetLastKeyPressed(void) { + char key = lastKeyPressed; + lastKeyPressed = 0; + return key; +} + void KeyboardHandler(struct Registers* registers) { (void) registers; @@ -37,7 +45,7 @@ void KeyboardHandler(struct Registers* registers) { char ascii = scancodeToAscii[scancode]; if (ascii != 0) { - TerminalPutChar(ascii); + lastKeyPressed = ascii; } } } \ No newline at end of file diff --git a/src/OSDev_18/src/pit.c b/src/OSDev_18/src/pit.c index 6ffab74..29e499f 100644 --- a/src/OSDev_18/src/pit.c +++ b/src/OSDev_18/src/pit.c @@ -5,7 +5,7 @@ static volatile uint32_t pit_ticks = 0; // Encasulated to pit.c to avoid accidental overwrite -static uint32_t GetCurrentTick(void){ +uint32_t GetCurrentTick(void){ return pit_ticks; } diff --git a/src/OSDev_18/src/snake.c b/src/OSDev_18/src/snake.c index 6a27368..d336e03 100644 --- a/src/OSDev_18/src/snake.c +++ b/src/OSDev_18/src/snake.c @@ -1,16 +1,70 @@ #include +#include +#include +#include +#include +#include +#include -char board[BOARD_SIZE][BOARD_SIZE][3]; -struct Snake snake; -struct Food food; -enum CollisionType collisionType; +struct GameState* CreateGame(void) { + struct GameState* game = (struct GameState*)malloc(sizeof(struct GameState)); + if (!game) { + return 0; + } -void InitializeBoard(void) { - for (int i = 0; i < BOARD_SIZE; i++) { - for (int j = 0; j < BOARD_SIZE; j++) { - board[i][j][0] = ' '; - board[i][j][1] = ' '; - board[i][j][2] = '\0'; + game->snake = (struct Snake*)malloc(sizeof(struct Snake)); + if (!game->snake) { + free(game); + return 0; + } + + game->food = (struct Food*)malloc(sizeof(struct Food)); + if (!game->food) { + free(game->snake); + free(game); + return 0; + } + + game->score = 0; + game->rngState = GetCurrentTick(); + + InitializeBoard(game); + InitializeSnake(game->snake); + InitializeFood(game->food); + + return game; +} + +void DestroyGame(struct GameState* game) { + if (!game) return; + + if (game->snake) { + free(game->snake); + } + + if (game->food) { + free(game->food); + } + + free(game); +} + +void ResetGame(struct GameState* game) { + if (!game) return; + + InitializeBoard(game); + InitializeSnake(game->snake); + InitializeFood(game->food); + game->score = 0; + game->rngState = GetCurrentTick(); +} + +void InitializeBoard(struct GameState* game) { + for (uint32_t i = 0; i < BOARD_SIZE; i++) { + for (uint32_t j = 0; j < BOARD_SIZE; j++) { + game->board[i][j][0] = ' '; + game->board[i][j][1] = ' '; + game->board[i][j][2] = '\0'; } } } @@ -27,6 +81,79 @@ void InitializeFood(struct Food* food) { food->y = (BOARD_SIZE / 2) - 3; } +uint32_t Random(uint32_t* rngState) { + *rngState = (*rngState * 1103515245) + 12345; + return *rngState; +} + +void HandleInput(struct GameState* game, char input) { + switch (input) { + case 'w': { + if (game->snake->direction != DOWN) { + game->snake->direction = UP; + } + break; + } + case 's': { + if (game->snake->direction != UP) { + game->snake->direction = DOWN; + } + break; + } + case 'a': { + if (game->snake->direction != RIGHT) { + game->snake->direction = LEFT; + } + break; + } + case 'd': { + if (game->snake->direction != LEFT) { + game->snake->direction = RIGHT; + } + break; + } + case 'r': { + if (game) { + ResetGame(game); + } + break; + } + case 'q': { + TerminalWriteString("Quitting game...\n"); + DestroyGame(game); + } + default: { + break; + } + } +} + +void PlayFoodSound(void) { + PlaySound(B5); + SleepInterrupt(50); + StopSound(); +} + +void PlayDeathSound(void) { + PlaySound(G3); + SleepInterrupt(100); + StopSound(); +} + +void PlayWinSound(void) { + PlaySound(C5); + SleepInterrupt(100); + StopSound(); + + PlaySound(E5); + SleepInterrupt(100); + StopSound(); + + PlaySound(G5); + SleepInterrupt(200); + StopSound(); +} + struct SnakeSegment MoveSnake(struct Snake* snake) { int x = snake->body[0].x; int y = snake->body[0].y; @@ -34,7 +161,7 @@ struct SnakeSegment MoveSnake(struct Snake* snake) { switch (snake->direction) { case UP: { snake->body[0].y--; - for (int i = 1; i < snake->length; i++) { + for (uint32_t i = 1; i < snake->length; i++) { int tempX = snake->body[i].x; int tempY = snake->body[i].y; snake->body[i].x = x; @@ -46,7 +173,7 @@ struct SnakeSegment MoveSnake(struct Snake* snake) { } case DOWN: { snake->body[0].y++; - for (int i = 1; i < snake->length; i++) { + for (uint32_t i = 1; i < snake->length; i++) { int tempX = snake->body[i].x; int tempY = snake->body[i].y; snake->body[i].x = x; @@ -58,7 +185,7 @@ struct SnakeSegment MoveSnake(struct Snake* snake) { } case LEFT: { snake->body[0].x--; - for (int i = 1; i < snake->length; i++) { + for (uint32_t i = 1; i < snake->length; i++) { int tempX = snake->body[i].x; int tempY = snake->body[i].y; snake->body[i].x = x; @@ -70,7 +197,7 @@ struct SnakeSegment MoveSnake(struct Snake* snake) { } case RIGHT: { snake->body[0].x++; - for (int i = 1; i < snake->length; i++) { + for (uint32_t i = 1; i < snake->length; i++) { int tempX = snake->body[i].x; int tempY = snake->body[i].y; snake->body[i].x = x; @@ -80,34 +207,37 @@ struct SnakeSegment MoveSnake(struct Snake* snake) { } break; } + default: { + break; + } } return (struct SnakeSegment){ x, y }; } -void SpawnFood(struct Snake* snake, struct Food* food) { +void SpawnFood(struct GameState* game) { int x, y; - int occupied; + uint32_t occupied; - if (snake->length == SNAKE_MAX_LENGTH) { + if (game->snake->length == SNAKE_MAX_LENGTH) { return; } do { - x = rand() % BOARD_SIZE; - y = rand() % BOARD_SIZE; + x = Random(&game->rngState) % BOARD_SIZE; + y = Random(&game->rngState) % BOARD_SIZE; occupied = 0; - for (int i = 0; i < snake->length; i++) { - if (snake->body[i].x == x && snake->body[i].y == y) { + for (uint32_t i = 0; i < game->snake->length; i++) { + if (game->snake->body[i].x == x && game->snake->body[i].y == y) { occupied = 1; break; } } } while (occupied); - food->x = x; - food->y = y; + game->food->x = x; + game->food->y = y; } void AddSegment(struct Snake* snake, int x, int y) { @@ -124,7 +254,7 @@ enum CollisionType CheckCollision(struct Snake* snake, struct Food* food) { } else if (snake->body[0].x < 0 || snake->body[0].x >= BOARD_SIZE || snake->body[0].y < 0 || snake->body[0].y >= BOARD_SIZE) { return WALL; } else { - for (int i = 1; i < snake->length; i++) { + for (uint32_t i = 1; i < snake->length; i++) { if (snake->body[0].x == snake->body[i].x && snake->body[0].y == snake->body[i].y) { return SELF; } @@ -133,77 +263,95 @@ enum CollisionType CheckCollision(struct Snake* snake, struct Food* food) { } } -void DrawBoard(struct Snake* snake, struct Food* food) { - InitializeBoard(); +void DrawBoard(struct GameState* game) { + TerminalClear(); + InitializeBoard(game); - if (snake->direction == UP) { - board[snake->body[0].y][snake->body[0].x][0] = '^'; - board[snake->body[0].y][snake->body[0].x][1] = '^'; - } else if (snake->direction == DOWN) { - board[snake->body[0].y][snake->body[0].x][0] = 'v'; - board[snake->body[0].y][snake->body[0].x][1] = 'v'; - } else if (snake->direction == LEFT) { - board[snake->body[0].y][snake->body[0].x][0] = '<'; - board[snake->body[0].y][snake->body[0].x][1] = '<'; - } else if (snake->direction == RIGHT) { - board[snake->body[0].y][snake->body[0].x][0] = '>'; - board[snake->body[0].y][snake->body[0].x][1] = '>'; + if (game->snake->direction == UP) { + game->board[game->snake->body[0].y][game->snake->body[0].x][0] = '^'; + game->board[game->snake->body[0].y][game->snake->body[0].x][1] = '^'; + } else if (game->snake->direction == DOWN) { + game->board[game->snake->body[0].y][game->snake->body[0].x][0] = 'v'; + game->board[game->snake->body[0].y][game->snake->body[0].x][1] = 'v'; + } else if (game->snake->direction == LEFT) { + game->board[game->snake->body[0].y][game->snake->body[0].x][0] = '<'; + game->board[game->snake->body[0].y][game->snake->body[0].x][1] = '<'; + } else if (game->snake->direction == RIGHT) { + game->board[game->snake->body[0].y][game->snake->body[0].x][0] = '>'; + game->board[game->snake->body[0].y][game->snake->body[0].x][1] = '>'; } - for (int i = 1; i < snake->length; i++) { - board[snake->body[i].y][snake->body[i].x][0] = '['; - board[snake->body[i].y][snake->body[i].x][1] = ']'; + for (uint32_t i = 1; i < game->snake->length; i++) { + game->board[game->snake->body[i].y][game->snake->body[i].x][0] = '['; + game->board[game->snake->body[i].y][game->snake->body[i].x][1] = ']'; } - board[food->y][food->x][0] = '{'; - board[food->y][food->x][1] = '}'; + game->board[game->food->y][game->food->x][0] = '{'; + game->board[game->food->y][game->food->x][1] = '}'; - for (int i = 0; i < BOARD_SIZE + 2; i++) { - printf("##"); + for (uint32_t i = 0; i < BOARD_SIZE + 2; i++) { + TerminalWriteString("##"); } - printf("\n"); + TerminalPutChar('\n'); - for (int i = 0; i < BOARD_SIZE; i++) { - printf("##"); - for (int j = 0; j < BOARD_SIZE; j++) { - printf("%s", board[i][j]); + for (uint32_t i = 0; i < BOARD_SIZE; i++) { + TerminalWriteString("##"); + for (uint32_t j = 0; j < BOARD_SIZE; j++) { + TerminalWriteString(game->board[i][j]); } - printf("##\n"); + TerminalWriteString("##\n"); } - for (int i = 0; i < BOARD_SIZE + 2; i++) { - printf("##"); + for (uint32_t i = 0; i < BOARD_SIZE + 2; i++) { + TerminalWriteString("##"); } - printf("\n"); + TerminalPutChar('\n'); + + TerminalWriteString("Score: "); + TerminalWriteUInt(game->score); + TerminalPutChar('\n'); } void PlayGame(void) { - InitializeSnake(&snake); - InitializeFood(&food); + struct GameState* game = CreateGame(); + if (!game) { + TerminalWriteString("Failed to create game.\n"); + return; + } + + char input = 0; + struct SnakeSegment tail; + enum CollisionType collisionType = NONE; while(1) { - if (snake.length == SNAKE_MAX_LENGTH) { + if (game->snake->length == SNAKE_MAX_LENGTH) { break; } + + input = GetLastKeyPressed(); + HandleInput(game, input); + if (input == 'q') return; + tail = MoveSnake(game->snake); - struct SnakeSegment tail = MoveSnake(&snake); - - collisionType = CheckCollision(&snake, &food); + collisionType = CheckCollision(game->snake, game->food); if (collisionType == FOOD) { - AddSegment(&snake, tail.x, tail.y); - SpawnFood(&snake, &food); - } else if (collisionType == WALL) { - InitializeSnake(&snake); - InitializeFood(&food); - } else if (collisionType == SELF) { - InitializeSnake(&snake); - InitializeFood(&food); + game->score++; + PlayFoodSound(); + AddSegment(game->snake, tail.x, tail.y); + SpawnFood(game); + } else if (collisionType == WALL || collisionType == SELF) { + PlayDeathSound(); + ResetGame(game); } collisionType = NONE; - DrawBoard(&snake, &food); + DrawBoard(game); + + SleepInterrupt(GAME_SPEED_MS); } - printf("You win!\n"); + TerminalWriteString("You win!\n"); + PlayWinSound(); + DestroyGame(game); } \ No newline at end of file diff --git a/src/OSDev_18/src/terminal.c b/src/OSDev_18/src/terminal.c index 9c8fd38..a0ec6dc 100644 --- a/src/OSDev_18/src/terminal.c +++ b/src/OSDev_18/src/terminal.c @@ -1,6 +1,8 @@ #include #include #include +#include +#include const size_t VGA_HEIGHT = 25; const size_t VGA_WIDTH = 80; @@ -21,6 +23,18 @@ uint16_t VgaEntry(unsigned char uc, uint8_t color) { return (uint16_t) uc | (uint16_t) color << 8; } +void TerminalClear(void) { + for (size_t y = 0; y < VGA_HEIGHT; y++) { + for (size_t x = 0; x < VGA_WIDTH; x++) { + TerminalEntryAt(' ', terminal_colour, x, y); + } + } + + terminal_row = 0; + terminal_column = 0; + TerminalUpdateCursor(); +} + void TerminalEntryAt(char c, uint8_t colour, size_t x, size_t y){ size_t index = y * VGA_WIDTH + x; terminal_buffer[index] = VgaEntry(c, colour); @@ -125,4 +139,16 @@ void TerminalWriteHex(uint32_t num) { uint8_t digit = (num >> i) & 0xF; TerminalPutChar(hexChars[digit]); } +} + +char TerminalGetChar(void) { + while (1) { + char key = GetLastKeyPressed(); + + if (key != 0) { + return key; + } + + SleepInterrupt(100); + } } \ No newline at end of file