Assignment 6 completed

This commit is contained in:
Teodor
2026-04-18 22:58:45 +00:00
parent 1840342765
commit d4d5665ddc
11 changed files with 318 additions and 112 deletions

View File

@@ -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.

View File

@@ -72,6 +72,7 @@ add_executable(uiaos-kernel
src/memory.c
src/pit.c
src/songPlayer.c
src/snake.c
)

View File

@@ -6,6 +6,8 @@
#define KEYBOARD_DATA_PORT 0x60
#define KEYBOARD_BUFFER_SIZE 256
char GetLastKeyPressed(void);
void KeyboardHandler(struct Registers* registers);
#endif

View File

@@ -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);

View File

@@ -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

View File

@@ -1,8 +1,11 @@
#ifndef SNAKEAPP_SNAKE_H
#define SNAKEAPP_SNAKE_H
#define BOARD_SIZE 25
#include <libc/stdint.h>
#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

View File

@@ -8,6 +8,7 @@
#include <kernel/pit.h>
#include <songApp/song.h>
#include <songApp/frequencies.h>
#include <snakeApp/snake.h>
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");

View File

@@ -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;
}
}
}

View File

@@ -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;
}

View File

@@ -1,16 +1,70 @@
#include <snakeApp/snake.h>
#include <kernel/memory.h>
#include <kernel/pit.h>
#include <kernel/keyboard.h>
#include <kernel/terminal.h>
#include <songApp/song.h>
#include <songApp/frequencies.h>
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);
}

View File

@@ -1,6 +1,8 @@
#include <kernel/gdt.h>
#include <kernel/terminal.h>
#include <kernel/io.h>
#include <kernel/keyboard.h>
#include <kernel/pit.h>
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);
}
}