Compare commits

...

10 Commits

54 changed files with 5210 additions and 976 deletions

6
.clang-format Normal file
View File

@ -0,0 +1,6 @@
BasedOnStyle: LLVM
IndentWidth: 4
ContinuationIndentWidth: 4
TabWidth: 4
UseTab: Never
BreakBeforeBraces: Attach

2
.gitignore vendored
View File

@ -9,9 +9,11 @@ tools/*
user/*
!user/*.*
user/*.o
snake/snake
.idea
cmake-build-debug
CMakeLists.txt
__pycache__
res.txt
vibing.txt

View File

@ -44,7 +44,9 @@ OBJECTS = ./kernel/kstart.o ./kernel.o ./console.o ./drivers/vga.o ./drivers/uar
./fs/fs.o ./drivers/ata.o ./lib/string.o ./proc.o ./drivers/pit.o ./kernel/vm.o
run: image.bin
qemu-system-i386 -drive format=raw,file=$< -serial mon:stdio -qmp unix:qemu-monitor-socket,server,nowait
qemu-system-i386 -drive format=raw,file=$< -serial mon:stdio -qmp unix:qemu-monitor-socket,server,nowait \
-audiodev pa,id=SAUND \
-machine pcspk-audiodev=SAUND
run-nox: image.bin
qemu-system-i386 -nographic -drive format=raw,file=$< -serial mon:stdio -qmp unix:qemu-monitor-socket,server,nowait
@ -106,7 +108,7 @@ debug-nox: image.bin
-ex "break _start" \
-ex "continue"
USERPROGS=./user/false ./user/greet ./user/div0 ./user/shout ./user/badputs ./user/bss
USERPROGS=./user/false ./user/greet ./user/div0 ./user/shout ./user/badputs ./user/bss ./user/player ./user/getc ./snake/snake
fs.img: ./kernel.bin ./tools/mkfs $(USERPROGS)
./tools/mkfs $@ $< $(USERPROGS)
@ -116,6 +118,9 @@ LDFLAGS=-m elf_i386
user/%: user/%.o user/crt.o
$(LD) $(LDFLAGS) -o $@ -Ttext 0x401000 $^
snake/snake: snake/snake.o user/crt.o
$(LD) $(LDFLAGS) -o $@ -Ttext 0x401000 $^
kernel.bin: $(OBJECTS)
$(LD) $(LDFLAGS) $(LDKERNELFLAGS) -o $@ -Ttext 0x80009000 $^

View File

@ -5,106 +5,96 @@
// bootmain() loads an ELF kernel image from the disk starting at
// sector 3 and then jumps to the kernel entry routine.
#include "elf.h"
#include "drivers/port.h"
#include "elf.h"
#define SECTSIZE 512
#define SECTSIZE 512
typedef unsigned char uchar;
typedef unsigned int uint;
void readseg(uchar*, uint, uint);
void readseg(uchar *, uint, uint);
inline void stosb(uchar* addr, uchar byte, uint count) {
inline void stosb(uchar *addr, uchar byte, uint count) {
asm volatile("cld; rep stosb" : "+D"(addr), "+a"(byte) : "c"(count) : "cc");
}
void
bootmain(void)
{
Elf32_Ehdr *elf;
Elf32_Phdr *ph, *eph;
void (*entry)(void);
uchar* pa;
void bootmain(void) {
Elf32_Ehdr *elf;
Elf32_Phdr *ph, *eph;
void (*entry)(void);
uchar *pa;
elf = (Elf32_Ehdr*)0x30000; // scratch space
elf = (Elf32_Ehdr *)0x30000; // scratch space
// Read 1st page off disk
readseg((uchar*)elf, 4096, 0);
// Read 1st page off disk
readseg((uchar *)elf, 4096, 0);
// Is this an ELF executable?
if(elf->magic != ELF_MAGIC)
return; // let bootasm.S handle error
// Is this an ELF executable?
if (elf->magic != ELF_MAGIC)
return; // let bootasm.S handle error
// Load each program segment (ignores ph flags).
ph = (Elf32_Phdr*)((uchar*)elf + elf->e_phoff);
eph = ph + elf->e_phnum;
for(; ph < eph; ph++) {
pa = (uchar*)(ph->p_paddr & 0x0fffffff);
readseg(pa, ph->p_filesz, ph->p_offset);
if(ph->p_memsz > ph->p_filesz)
stosb(pa + ph->p_filesz, 0, ph->p_memsz - ph->p_filesz);
}
// Load each program segment (ignores ph flags).
ph = (Elf32_Phdr *)((uchar *)elf + elf->e_phoff);
eph = ph + elf->e_phnum;
for (; ph < eph; ph++) {
pa = (uchar *)(ph->p_paddr & 0x0fffffff);
readseg(pa, ph->p_filesz, ph->p_offset);
if (ph->p_memsz > ph->p_filesz)
stosb(pa + ph->p_filesz, 0, ph->p_memsz - ph->p_filesz);
}
// Call the entry point from the ELF header.
// Does not return!
entry = (void(*)(void))(elf->e_entry & 0x0fffffff);
entry();
// Call the entry point from the ELF header.
// Does not return!
entry = (void (*)(void))(elf->e_entry & 0x0fffffff);
entry();
}
void
waitdisk(void)
{
// Wait for disk ready.
while((port_byte_in(0x1F7) & 0xC0) != 0x40)
;
void waitdisk(void) {
// Wait for disk ready.
while ((port_byte_in(0x1F7) & 0xC0) != 0x40)
;
}
static inline void
insl(int port, void *addr, int cnt)
{
asm volatile("cld; rep insl" :
"=D" (addr), "=c" (cnt) :
"d" (port), "0" (addr), "1" (cnt) :
"memory", "cc");
static inline void insl(int port, void *addr, int cnt) {
asm volatile("cld; rep insl"
: "=D"(addr), "=c"(cnt)
: "d"(port), "0"(addr), "1"(cnt)
: "memory", "cc");
}
// Read a single sector at offset into dst.
void
readsect(void *dst, uint offset)
{
// Issue command.
waitdisk();
port_byte_out(0x1F2, 1); // count = 1
port_byte_out(0x1F3, offset);
port_byte_out(0x1F4, offset >> 8);
port_byte_out(0x1F5, offset >> 16);
port_byte_out(0x1F6, (offset >> 24) | 0xE0);
port_byte_out(0x1F7, 0x20); // cmd 0x20 - read sectors
void readsect(void *dst, uint offset) {
// Issue command.
waitdisk();
port_byte_out(0x1F2, 1); // count = 1
port_byte_out(0x1F3, offset);
port_byte_out(0x1F4, offset >> 8);
port_byte_out(0x1F5, offset >> 16);
port_byte_out(0x1F6, (offset >> 24) | 0xE0);
port_byte_out(0x1F7, 0x20); // cmd 0x20 - read sectors
// Read data.
waitdisk();
insl(0x1F0, dst, SECTSIZE/4);
// Read data.
waitdisk();
insl(0x1F0, dst, SECTSIZE / 4);
}
// Read 'count' bytes at 'offset' from kernel into physical address 'pa'.
// Might copy more than asked.
void
readseg(uchar* pa, uint count, uint offset)
{
uchar* epa;
void readseg(uchar *pa, uint count, uint offset) {
uchar *epa;
epa = pa + count;
epa = pa + count;
// Round down to sector boundary.
pa -= offset % SECTSIZE;
// Round down to sector boundary.
pa -= offset % SECTSIZE;
// Translate from bytes to sectors; kernel starts at sector 3.
offset = (offset / SECTSIZE) + 2;
// Translate from bytes to sectors; kernel starts at sector 3.
offset = (offset / SECTSIZE) + 2;
// If this is too slow, we could read lots of sectors at a time.
// We'd write more to memory than asked, but it doesn't matter --
// we load in increasing order.
for(; pa < epa; pa += SECTSIZE, offset++)
readsect(pa, offset);
// If this is too slow, we could read lots of sectors at a time.
// We'd write more to memory than asked, but it doesn't matter --
// we load in increasing order.
for (; pa < epa; pa += SECTSIZE, offset++)
readsect(pa, offset);
}

View File

@ -1,15 +1,15 @@
#include "console.h"
#include "drivers/vga.h"
#include "drivers/uart.h"
#include "drivers/vga.h"
void printk(const char* msg) {
void printk(const char *msg) {
vga_print_string(msg);
for (; *msg; ++msg) {
uartputc(*msg);
}
}
void panic(const char* msg) {
void panic(const char *msg) {
printk("\nKernel panic: ");
printk(msg);
asm("cli");

View File

@ -1,6 +1,6 @@
#pragma once
void printk(const char* msg);
_Noreturn void panic(const char* msg);
void printk(const char *msg);
_Noreturn void panic(const char *msg);
#define check(expr) if (!(expr)) { panic("Assertion failed at " __FILE__ " : " " : " #expr "\n"); }
#define check(expr) if (!(expr)) {panic("Assertion failed at " __FILE__ " : " #expr "\n");}

View File

@ -1,48 +1,68 @@
#include "gdt.h"
#include "x86.h"
#include "memlayout.h"
#include "../lib/string.h"
#include "kernel/mem.h"
#include "memlayout.h"
#include "x86.h"
#include <stdint.h>
struct seg_desc_t {
uint16_t lim_15_0; // Low bits of segment limit
uint16_t base_15_0; // Low bits of segment base address
uint8_t base_23_16; // Middle bits of segment base address
uint8_t type : 4; // Segment type (see STA_ constants)
uint8_t s : 1; // 0 = system, 1 = application
uint8_t dpl : 2; // Descriptor Privilege Level
uint8_t p : 1; // Present
uint8_t lim_19_16 : 4; // High bits of segment limit
uint8_t avl : 1; // Unused (available for software use)
uint8_t rsv1 : 1; // Reserved
uint8_t db : 1; // 0 = 16-bit segment, 1 = 32-bit segment
uint8_t g : 1; // Granularity: limit scaled by 4K when set
uint8_t base_31_24; // High bits of segment base address
uint16_t lim_15_0; // Low bits of segment limit
uint16_t base_15_0; // Low bits of segment base address
uint8_t base_23_16; // Middle bits of segment base address
uint8_t type : 4; // Segment type (see STA_ constants)
uint8_t s : 1; // 0 = system, 1 = application
uint8_t dpl : 2; // Descriptor Privilege Level
uint8_t p : 1; // Present
uint8_t lim_19_16 : 4; // High bits of segment limit
uint8_t avl : 1; // Unused (available for software use)
uint8_t rsv1 : 1; // Reserved
uint8_t db : 1; // 0 = 16-bit segment, 1 = 32-bit segment
uint8_t g : 1; // Granularity: limit scaled by 4K when set
uint8_t base_31_24; // High bits of segment base address
} __attribute__((packed));
#define SEG(type, base, lim, dpl) (struct seg_desc_t) \
{ ((lim) >> 12) & 0xffff, (uint)(base) & 0xffff, \
((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \
(uint)(lim) >> 28, 0, 0, 1, 1, (uint)(base) >> 24 }
#define SEG16(type, base, lim, dpl) (struct seg_desc_t) \
{ (lim) & 0xffff, (uint)(base) & 0xffff, \
((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \
(uint)(lim) >> 16, 0, 0, 1, 0, (uint)(base) >> 24 }
#define SEG(type, base, lim, dpl) \
(struct seg_desc_t){((lim) >> 12) & 0xffff, \
(uint)(base) & 0xffff, \
((uint)(base) >> 16) & 0xff, \
type, \
1, \
dpl, \
1, \
(uint)(lim) >> 28, \
0, \
0, \
1, \
1, \
(uint)(base) >> 24}
#define SEG16(type, base, lim, dpl) \
(struct seg_desc_t){(lim) & 0xffff, \
(uint)(base) & 0xffff, \
((uint)(base) >> 16) & 0xff, \
type, \
1, \
dpl, \
1, \
(uint)(lim) >> 16, \
0, \
0, \
1, \
0, \
(uint)(base) >> 24}
struct seg_desc_t seg_desc[6];
void init_seg_desc() {
seg_desc[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, 0);
seg_desc[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0);
seg_desc[SEG_UCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, DPL_USER);
seg_desc[SEG_UDATA] = SEG(STA_W, 0, 0xffffffff, DPL_USER);
seg_desc[SEG_KCODE] = SEG(STA_X | STA_R, 0, 0xffffffff, 0);
seg_desc[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0);
seg_desc[SEG_UCODE] = SEG(STA_X | STA_R, 0, 0xffffffff, DPL_USER);
seg_desc[SEG_UDATA] = SEG(STA_W, 0, 0xffffffff, DPL_USER);
}
struct gdt_desc_t {
uint16_t size;
void* ptr;
void *ptr;
} __attribute__((packed));
void load_gdt() {
@ -51,20 +71,20 @@ void load_gdt() {
struct gdt_desc_t gdt_desc;
gdt_desc.size = sizeof(seg_desc) - 1;
gdt_desc.ptr = seg_desc;
asm("lgdt %0": : "m"(gdt_desc));
asm("lgdt %0" : : "m"(gdt_desc));
}
void switchuvm(struct taskstate *tss, void* esp, pde_t *pgdir) {
void switchuvm(struct taskstate *tss, void *esp, pde_t *pgdir) {
memset(tss, 0, sizeof(*tss));
seg_desc[SEG_TSS] = SEG16(STS_T32A, tss, sizeof(*tss)-1, 0);
seg_desc[SEG_TSS] = SEG16(STS_T32A, tss, sizeof(*tss) - 1, 0);
seg_desc[SEG_TSS].s = 0;
tss->ss0 = SEG_KDATA << 3;
tss->esp0 = (uint)esp;
// setting IOPL=0 in eflags *and* iomb beyond the tss segment limit
// forbids I/O instructions (e.g., inb and outb) from user space
tss->iomb = (ushort) 0xFFFF;
tss->iomb = (ushort)0xFFFF;
asm("ltr %0": : "r"((ushort)(SEG_TSS << 3)));
asm("ltr %0" : : "r"((ushort)(SEG_TSS << 3)));
lcr3(V2P(pgdir));
}

View File

@ -1,32 +1,32 @@
#pragma once
#define STA_X 0x8 // Executable segment
#define STA_W 0x2 // Writeable (non-executable segments)
#define STA_R 0x2 // Readable (executable segments)
#define STA_X 0x8 // Executable segment
#define STA_W 0x2 // Writeable (non-executable segments)
#define STA_R 0x2 // Readable (executable segments)
// System segment type bits
#define STS_T32A 0x9 // Available 32-bit TSS
#define STS_IG32 0xE // 32-bit Interrupt Gate
#define STS_TG32 0xF // 32-bit Trap Gate
#define STS_T32A 0x9 // Available 32-bit TSS
#define STS_IG32 0xE // 32-bit Interrupt Gate
#define STS_TG32 0xF // 32-bit Trap Gate
#define DPL_USER 3
#define FL_IF 0x00000200
#define FL_IF 0x00000200
#define SEG_KCODE 1
#define SEG_KDATA 2
#define SEG_UCODE 3
#define SEG_UDATA 4
#define SEG_TSS 5
#define SEG_TSS 5
#define SEG_ASM(type,base,lim) \
.word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \
.byte (((base) >> 16) & 0xff), (0x90 | (type)), \
(0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)
#define SEG_ASM(type, base, lim) \
.word(((lim) >> 12) & 0xffff), ((base) & 0xffff); \
.byte(((base) >> 16) & 0xff), (0x90 | (type)), \
(0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)
#define USER_BASE 0x400000 // 4 MB
#define USER_BASE 0x400000 // 4 MB
#define USER_STACK_BASE 0xf00000 // 15 MB
#define KERN_STACK_BASE 0x90000
#define KERN_STACK_BASE 0x90000
#ifndef __ASSEMBLER__
#include "kernel/mem.h"
@ -34,9 +34,9 @@ typedef unsigned uint;
typedef unsigned short ushort;
struct taskstate {
uint link; // Old ts selector
uint esp0; // Stack pointers and segment selectors
ushort ss0; // after an increase in privilege level
uint link; // Old ts selector
uint esp0; // Stack pointers and segment selectors
ushort ss0; // after an increase in privilege level
ushort padding1;
uint *esp1;
ushort ss1;
@ -44,10 +44,10 @@ struct taskstate {
uint *esp2;
ushort ss2;
ushort padding3;
void *cr3; // Page directory base
uint *eip; // Saved state from last task switch
void *cr3; // Page directory base
uint *eip; // Saved state from last task switch
uint eflags;
uint eax; // More saved state (registers)
uint eax; // More saved state (registers)
uint ecx;
uint edx;
uint ebx;
@ -55,7 +55,7 @@ struct taskstate {
uint *ebp;
uint esi;
uint edi;
ushort es; // Even more saved state (segment selectors)
ushort es; // Even more saved state (segment selectors)
ushort padding4;
ushort cs;
ushort padding5;
@ -69,10 +69,10 @@ struct taskstate {
ushort padding9;
ushort ldt;
ushort padding10;
ushort t; // Trap on task switch
ushort iomb; // I/O map base address
ushort t; // Trap on task switch
ushort iomb; // I/O map base address
};
void load_gdt();
void switchuvm(struct taskstate *tss, void* esp, pde_t *pgdir);
void switchuvm(struct taskstate *tss, void *esp, pde_t *pgdir);
#endif

252
cpu/idt.c
View File

@ -1,10 +1,13 @@
#include "isr.h"
#include "gdt.h"
#include "memlayout.h"
#include "../syscall.h"
#include "../proc.h"
#include "../drivers/port.h"
#include "../console.h"
#include "../drivers/keyboard.h"
#include "../drivers/pit.h"
#include "../drivers/port.h"
#include "../drivers/vga.h"
#include "../proc.h"
#include "../syscall.h"
#include "gdt.h"
#include "isr.h"
#include "memlayout.h"
enum {
IDT_HANDLERS = 256,
@ -14,10 +17,10 @@ typedef struct {
uint16_t low_offset;
uint16_t selector;
uint8_t always0;
uint8_t type: 4;
uint8_t s: 1;
uint8_t dpl: 2;
uint8_t p: 1;
uint8_t type : 4;
uint8_t s : 1;
uint8_t dpl : 2;
uint8_t p : 1;
uint16_t high_offset;
} __attribute__((packed)) idt_gate_t;
@ -26,8 +29,8 @@ idt_gate_t idt[IDT_HANDLERS];
#define low_16(address) (uint16_t)((address) & 0xFFFF)
#define high_16(address) (uint16_t)(((address) >> 16) & 0xFFFF)
#define STS_IG32 0xE // 32-bit Interrupt Gate
#define STS_TG32 0xF // 32-bit Trap Gate
#define STS_IG32 0xE // 32-bit Interrupt Gate
#define STS_TG32 0xF // 32-bit Trap Gate
void set_idt_gate(int n, _Bool istrap, uint32_t handler, uint8_t dpl) {
idt[n].low_offset = low_16(handler);
@ -53,7 +56,7 @@ void init_idt() {
set_idt_gate(T_SYSCALL, 1, default_handlers[T_SYSCALL], DPL_USER);
}
const char * const exception_messages[] = {
const char *const exception_messages[] = {
[0] = "Division By Zero",
[1] = "Debug",
[2] = "Non Maskable Interrupt",
@ -102,7 +105,7 @@ void trap(registers_t *r) {
}
if (r->int_no < 32) {
const char* msg = "Reserved";
const char *msg = "Reserved";
if (r->int_no < ARRLEN(exception_messages)) {
msg = exception_messages[r->int_no];
}
@ -118,50 +121,193 @@ void trap(registers_t *r) {
}
/* takes a ptr that is supposed to be from userspace. If it is not, we
* return 0, if it is a valid userspace pointer, we return the usable
* kernelspace pointer
* return 0, if it is a valid we return 1
*/
uint32_t is_userspace_ptr_mapped(uint32_t ptr) {
// todo: implement
return ptr;
bool is_userspace_ptr_mapped(uint32_t ptr) {
if (ptr >= KERNBASE) {
return 0;
}
pde_t *pgdir = get_user_proc_page_directory();
if (!pgdir) {
return 0;
}
pde_t pde = pgdir[PDX(ptr)];
if ((pde & (PTE_P | PTE_U)) != (PTE_P | PTE_U)) {
return 0;
}
if (pde & PDE_PS) {
return 0;
}
pte_t *table = (pte_t *)P2V(PTE_ADDR(pde));
pte_t pte = table[PTX(ptr)];
if ((pte & (PTE_P | PTE_U)) != (PTE_P | PTE_U)) {
return 0;
}
return 1;
}
static void* get_userspace_cstr(uint32_t ptr) {
// todo: implement
return (void*)(ptr);
static bool is_userspace_range_mapped(uint32_t ptr, uint32_t size) {
if (size == 0) {
return 1;
}
if (ptr >= KERNBASE) {
return 0;
}
uint32_t end = ptr + size - 1;
if (end < ptr || end >= KERNBASE) {
return 0;
}
uint32_t last_page = PGROUNDDOWN(end);
for (uint32_t addr = ptr;; addr = PGROUNDDOWN(addr) + PGSIZE) {
if (!is_userspace_ptr_mapped(addr)) {
return 0;
}
if (PGROUNDDOWN(addr) == last_page) {
return 1;
}
}
}
static int handle_puts(const char* s) {
if (!s) {
static bool is_userspace_cstr(uint32_t ptr) {
for (uint32_t addr = ptr;; addr++) {
if (addr == 0 || !is_userspace_ptr_mapped(addr)) {
return 0;
}
if (*(const char *)addr == '\0') {
return (void *)ptr;
}
}
}
static _Noreturn void userspace_panic(const char *msg) {
if (!vga_is_text_mode()) {
switch_to_text_mode();
vga_clear_screen();
}
printk(msg);
killproc();
}
static void handle_puts(uintptr_t s) {
if (!is_userspace_cstr(s)) {
userspace_panic("SYS_puts panic: page fault\n");
}
printk((const char *)s);
}
static void require_text_mode_for_userspace_text_syscall() {
if (!vga_is_text_mode()) {
userspace_panic("Userspace panic: text syscall in graphics mode\n");
}
}
static void handle_swap_frame(uintptr_t frame) {
enum {
VGA_GRAPHICS_FRAME_SIZE = VGA_GRAPHICS_WIDTH * VGA_GRAPHICS_HEIGHT,
};
if (vga_is_text_mode()) {
userspace_panic("Userspace panic: frame swap in text mode\n");
}
if (!is_userspace_range_mapped(frame, VGA_GRAPHICS_FRAME_SIZE)) {
userspace_panic("SYS_swap_frame panic: page fault\n");
}
uint8_t *video = (uint8_t *)(KERNBASE + 0xA0000);
uint8_t *user = (uint8_t *)frame;
for (uint32_t i = 0; i < VGA_GRAPHICS_FRAME_SIZE; i++) {
video[i] = user[i];
}
}
static void refill_keyboard_copy_buffer(void) {
size_t count = kbd_state_shrd.len;
if (count == 0) {
return;
}
cli();
size_t rem = KEYBOARD_INTERRUPT_BUF_CAP - kbd_state_shrd.copy_len;
size_t copying = rem < count ? rem : count;
memcpy(kbd_state_shrd.copy_buf, kbd_state_shrd.buf, copying);
kbd_state_shrd.len -= copying;
kbd_state_shrd.copy_len += copying;
sti();
}
static int handle_getc(void) {
if (kbd_can_take_from_copy_buffer()) {
return kbd_take_from_copy_buffer();
}
refill_keyboard_copy_buffer();
if (!kbd_can_take_from_copy_buffer()) {
return -1;
}
printk(s);
return 0;
return kbd_take_from_copy_buffer();
}
static void handle_syscall(registers_t* r) {
static void handle_syscall(registers_t *r) {
switch (r->eax) {
case SYS_exit:
if (r->ebx == 0) {
printk("* success\n");
} else {
printk("* failure\n");
}
killproc();
case SYS_greet:
printk("Hello world!\n");
r->eax = 0;
break;
case SYS_putc:
printk((const char[]){r->ebx, '\0'});
r->eax = 0;
break;
case SYS_puts:
r->eax = handle_puts(get_userspace_cstr(r->ebx));
break;
default:
printk("Unknown syscall\n");
r->eax = -1;
case SYS_exit:
if (r->ebx == 0) {
printk("* success\n");
} else {
printk("* failure\n");
}
killproc();
case SYS_greet:
require_text_mode_for_userspace_text_syscall();
printk("Hello world!\n");
r->eax = 0;
break;
case SYS_putc:
require_text_mode_for_userspace_text_syscall();
printk((const char[]){(char)r->ebx, '\0'});
r->eax = 0;
break;
case SYS_puts:
require_text_mode_for_userspace_text_syscall();
handle_puts(r->ebx);
r->eax = 0;
break;
case SYS_switch_to_text:
switch_to_text_mode();
r->eax = 0;
break;
case SYS_switch_to_graphics:
switch_to_graphics_mode();
r->eax = 0;
break;
case SYS_swap_frame:
handle_swap_frame(r->ebx);
r->eax = 0;
break;
case SYS_time_ms:
r->eax = get_uptime_ms();
break;
case SYS_halt:
asm volatile("hlt");
r->eax = 0;
break;
case SYS_getc:
r->eax = handle_getc();
break;
case SYS_set_beep:
if (r->ebx > MAX_BEEP_FREQUENCY_HZ) {
userspace_panic("Userspace panic: beep frequency out of range\n");
}
set_beep_frequency_hz(r->ebx);
r->eax = 0;
break;
default:
userspace_panic("Userspace panic: Unknown syscall\n");
}
}
@ -189,7 +335,7 @@ static void init_pic() {
typedef struct {
uint16_t limit;
void* base;
void *base;
} __attribute__((packed)) idt_register_t;
static idt_register_t idt_reg;
@ -206,10 +352,6 @@ void load_idt() {
register_interrupt_handler(T_SYSCALL, handle_syscall);
}
void cli() {
asm("cli");
}
void cli() { asm("cli"); }
void sti() {
asm("sti");
}
void sti() { asm("sti"); }

View File

@ -29,9 +29,11 @@ enum {
*/
typedef struct {
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; /* Pushed by pusha. */
uint32_t gs, fs, es, ds; /* Data segment selector */
uint32_t int_no, err_code; /* Interrupt number and error code (if applicable) */
uint32_t eip, cs, eflags, useresp, ss; /* Pushed by the processor automatically */
uint32_t gs, fs, es, ds; /* Data segment selector */
uint32_t int_no,
err_code; /* Interrupt number and error code (if applicable) */
uint32_t eip, cs, eflags, useresp,
ss; /* Pushed by the processor automatically */
} registers_t;
void isr_install();

View File

@ -1,42 +1,42 @@
#pragma once
#define KERNBASE 0x80000000
#define PGSIZE 0x1000
#define PHYSTOP 0x8000000 // 128 Mib
#define PGSIZE 0x1000
#define PHYSTOP 0x8000000 // 128 Mib
#define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1))
#define PGROUNDDOWN(a) (((a)) & ~((uintptr_t)(PGSIZE-1)))
#define PGROUNDUP(sz) (((sz) + PGSIZE - 1) & ~(PGSIZE - 1))
#define PGROUNDDOWN(a) (((a)) & ~((uintptr_t)(PGSIZE - 1)))
#define NPDENTRIES 1024 // # directory entries per page directory
#define NPTENTRIES 1024 // # PTEs per page table
#define NPDENTRIES 1024 // # directory entries per page directory
#define NPTENTRIES 1024 // # PTEs per page table
#define PTXSHIFT 12 // offset of PTX in a linear address
#define PDXSHIFT 22 // offset of PDX in a linear address
#define PTXSHIFT 12 // offset of PTX in a linear address
#define PDXSHIFT 22 // offset of PDX in a linear address
#define PXMASK 0x3FF
#define PXMASK 0x3FF
#ifndef __ASSEMBLER__
#include <stdint.h>
#define V2P(a) (((uintptr_t) (a)) - KERNBASE)
#define P2V(a) ((void *)(((uintptr_t) (a)) + KERNBASE))
#define V2P(a) (((uintptr_t)(a)) - KERNBASE)
#define P2V(a) ((void *)(((uintptr_t)(a)) + KERNBASE))
// page directory index
#define PDX(va) (((uintptr_t)(va) >> PDXSHIFT) & PXMASK)
#define PDX(va) (((uintptr_t)(va) >> PDXSHIFT) & PXMASK)
// page table index
#define PTX(va) (((uintptr_t)(va) >> PTXSHIFT) & PXMASK)
#define PTX(va) (((uintptr_t)(va) >> PTXSHIFT) & PXMASK)
// Address in page table or page directory entry
#define PTE_ADDR(pte) ((uintptr_t)(pte) & ~0xFFF)
#define PTE_ADDR(pte) ((uintptr_t)(pte) & ~0xFFF)
#endif
// Page table/directory entry flags.
#define PTE_P 0x001 // Present
#define PTE_W 0x002 // Writeable
#define PTE_U 0x004 // User
#define PTE_PWT 0x008 // Write-Through
#define PTE_PCD 0x010 // Cache-Disable
#define PTE_A 0x020 // Accessed
#define PTE_D 0x040 // Dirty
#define PTE_P 0x001 // Present
#define PTE_W 0x002 // Writeable
#define PTE_U 0x004 // User
#define PTE_PWT 0x008 // Write-Through
#define PTE_PCD 0x010 // Cache-Disable
#define PTE_A 0x020 // Accessed
#define PTE_D 0x040 // Dirty
// entries of page tables don't have PS
#define PDE_PS 0x080 // Page Size
#define PDE_PS 0x080 // Page Size

View File

@ -1,20 +1,20 @@
#pragma once
#include <stdint.h>
static inline void
stosl(void *addr, int data, int cnt)
{
asm volatile("cld; rep stosl" : : "D"(addr), "c"(cnt), "a"(data) : "memory");
static inline void stosl(void *addr, int data, int cnt) {
asm volatile("cld; rep stosl"
:
: "D"(addr), "c"(cnt), "a"(data)
: "memory");
}
static inline void
stosb(void *addr, unsigned char data, int cnt)
{
asm volatile("cld; rep stosb" : : "D"(addr), "c"(cnt), "a"(data) : "memory");
static inline void stosb(void *addr, unsigned char data, int cnt) {
asm volatile("cld; rep stosb"
:
: "D"(addr), "c"(cnt), "a"(data)
: "memory");
}
static inline void
lcr3(uint32_t val)
{
asm volatile("mov %0,%%cr3" : : "r" (val));
static inline void lcr3(uint32_t val) {
asm volatile("mov %0,%%cr3" : : "r"(val));
}

View File

@ -10,7 +10,7 @@
#define STATUS_BSY 0x80
#define STATUS_RDY 0x40
#define STATUS_DRQ 0x08
#define STATUS_DF 0x20
#define STATUS_DF 0x20
#define STATUS_ERR 0x01
// This is really specific to our OS now, assuming ATA bus 0 master
@ -19,8 +19,8 @@
static void ATA_wait_BSY();
static void ATA_wait_RDY();
void read_sectors_ATA_PIO(void* target_address, uint32_t LBA, uint8_t sector_count)
{
void read_sectors_ATA_PIO(void *target_address, uint32_t LBA,
uint8_t sector_count) {
ATA_wait_BSY();
port_byte_out(0x1F6, 0xE0 | ((LBA >> 24) & 0xF));
port_byte_out(0x1F2, sector_count);
@ -31,8 +31,7 @@ void read_sectors_ATA_PIO(void* target_address, uint32_t LBA, uint8_t sector_cou
uint16_t *target = (uint16_t *)target_address;
for (int j = 0; j < sector_count; j++)
{
for (int j = 0; j < sector_count; j++) {
ATA_wait_BSY();
ATA_wait_RDY();
for (int i = 0; i < 256; i++)
@ -41,8 +40,8 @@ void read_sectors_ATA_PIO(void* target_address, uint32_t LBA, uint8_t sector_cou
}
}
void write_sectors_ATA_PIO(uint32_t LBA, uint8_t sector_count, uint32_t *bytes)
{
void write_sectors_ATA_PIO(uint32_t LBA, uint8_t sector_count,
uint32_t *bytes) {
ATA_wait_BSY();
port_byte_out(0x1F6, 0xE0 | ((LBA >> 24) & 0xF));
port_byte_out(0x1F2, sector_count);
@ -51,12 +50,10 @@ void write_sectors_ATA_PIO(uint32_t LBA, uint8_t sector_count, uint32_t *bytes)
port_byte_out(0x1F5, (uint8_t)(LBA >> 16));
port_byte_out(0x1F7, 0x30); // Send the write command
for (int j = 0; j < sector_count; j++)
{
for (int j = 0; j < sector_count; j++) {
ATA_wait_BSY();
ATA_wait_RDY();
for (int i = 0; i < 256; i++)
{
for (int i = 0; i < 256; i++) {
port_long_out(0x1F0, bytes[i]);
}
}

View File

@ -1,4 +1,5 @@
#pragma once
#include <stdint.h>
void read_sectors_ATA_PIO(void* target_address, uint32_t LBA, uint8_t sector_count);
void read_sectors_ATA_PIO(void *target_address, uint32_t LBA,
uint8_t sector_count);

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,9 @@
#include "keyboard.h"
#include "console.h"
#include "cpu/isr.h"
#include "cpu/memlayout.h"
#include "console.h"
#include "port.h"
#include "kernel/mem.h"
#include "port.h"
keyboard_interrupt_shared_t kbd_state_shrd;
@ -14,12 +14,11 @@ static void kbd_interrupt_handler(registers_t *r) {
}
}
void init_keyboard() {
kbd_state_shrd.len = 0;
kbd_state_shrd.buf = (uint8_t*)kalloc();
kbd_state_shrd.buf = (uint8_t *)kalloc();
kbd_state_shrd.copy_len = 0;
kbd_state_shrd.copy_buf = (uint8_t*)kalloc();
kbd_state_shrd.copy_buf = (uint8_t *)kalloc();
/* 128/8 actually we need only 16 bytes for that */
memset(kbd_state_shrd.copy_pressed, 0, 16);
@ -32,16 +31,17 @@ uint8_t kbd_take_from_copy_buffer() {
uint8_t key_event_code = kbd_state_shrd.copy_buf[--kbd_state_shrd.copy_len];
uint8_t keycode = (key_event_code & 0x7f);
if (key_event_code & 0x80) {
kbd_state_shrd.copy_pressed[keycode >> 3] = kbd_state_shrd.copy_pressed[keycode >> 3] & (~(1u << (keycode & 0x7)));
kbd_state_shrd.copy_pressed[keycode >> 3] =
kbd_state_shrd.copy_pressed[keycode >> 3] &
(~(1u << (keycode & 0x7)));
} else {
kbd_state_shrd.copy_pressed[keycode >> 3] = kbd_state_shrd.copy_pressed[keycode >> 3] | (1u << (keycode & 0x7));
kbd_state_shrd.copy_pressed[keycode >> 3] =
kbd_state_shrd.copy_pressed[keycode >> 3] | (1u << (keycode & 0x7));
}
return key_event_code;
}
bool kbd_can_take_from_copy_buffer() {
return kbd_state_shrd.copy_len > 0;
}
bool kbd_can_take_from_copy_buffer() { return kbd_state_shrd.copy_len > 0; }
bool kbd_is_pressed(uint8_t code) {
check(code < 128);
return kbd_state_shrd.copy_pressed[code >> 3] & (1u << (code & 0x7));

View File

@ -6,12 +6,12 @@
#define KEYBOARD_INTERRUPT_BUF_CAP 100
typedef struct {
volatile uint32_t len;
uint8_t *buf;
volatile uint32_t len;
uint8_t *buf;
uint32_t copy_len;
uint8_t *copy_buf;
uint8_t copy_pressed[16];
uint32_t copy_len;
uint8_t *copy_buf;
uint8_t copy_pressed[16];
} keyboard_interrupt_shared_t;
#define KEYCODE_SHIFT 42
@ -20,22 +20,22 @@ typedef struct {
/* decoding */
static const char keysym_mapped_ascii_lower[] = {
0 , 0 , '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '-', '=', 0 , 0 , 'q', 'w', 'e', 'r', 't', 'y',
'u', 'i', 'o', 'p', '[', ']', '\n', 0 , 'a', 's', 'd', 'f', 'g',
'h', 'j', 'k', 'l', ';', '\'', '`', 0 , '\\', 'z', 'x', 'c', 'v',
'b', 'n', 'm', ',', '.', '/', 0 , 0 , 0 , ' ',
0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0,
0, 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0,
'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z',
'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, 0, 0, ' ',
};
static const char keysym_mapped_ascii_upper[] = {
0 , 0 , '!', '@', '#', '$', '%', '^',
'&', '*', '(', ')', '_', '+', 0 , 0 , 'Q', 'W', 'E', 'R', 'T', 'Y',
'U', 'I', 'O', 'P', '{', '}', '\n', 0 , 'A', 'S', 'D', 'F', 'G',
'H', 'J', 'K', 'L', ':', '"', '~', 0 , '\\', 'Z', 'X', 'C', 'V',
'B', 'N', 'M', '<', '>', '?' /* an actual question mark */, 0 , 0 , 0 , ' ',
0, 0, '!', '@', '#', '$', '%', '^', '&',
'*', '(', ')', '_', '+', 0, 0, 'Q', 'W',
'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{',
'}', '\n', 0, 'A', 'S', 'D', 'F', 'G', 'H',
'J', 'K', 'L', ':', '"', '~', 0, '\\', 'Z',
'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?' /* an actual question mark */,
0, 0, 0, ' ',
};
extern keyboard_interrupt_shared_t kbd_state_shrd;
void init_keyboard();

View File

@ -1,8 +1,7 @@
#pragma once
#include "port.h"
__attribute__((noreturn))
static inline void qemu_shutdown() {
port_word_out(0x604, 0x2000);
__builtin_unreachable();
__attribute__((noreturn)) static inline void qemu_shutdown() {
port_word_out(0x604, 0x2000);
__builtin_unreachable();
}

View File

@ -1,6 +1,6 @@
#include "pit.h"
#include "port.h"
#include "../cpu/isr.h"
#include "port.h"
enum {
PIT_CRYSTAL_HZ = 1193182,
@ -23,10 +23,18 @@ enum {
PIT_MODES_SQUARE_WAVE_GENERATOR = 0x3,
PIT_MODES_SW_TRIGGERRED_STROBE = 0x4,
PIT_MODES_HW_TRIGGERRED_STROBE = 0x5,
PIT_COMMAND_PORT = 0x43,
PIT_CHANNEL0_DATA_PORT = 0x40,
PIT_CHANNEL2_DATA_PORT = 0x42,
PC_SPEAKER_PORT = 0x61,
};
// Wtf is that O_o
static timer_callback callbacks[100];
static int registered_callbacks = 0;
static volatile uint32_t uptime_ms = 0;
static volatile uint32_t speaker_frequency_hz = 0;
void add_timer_callback(timer_callback tc) {
callbacks[registered_callbacks++] = tc;
@ -45,7 +53,7 @@ struct pit_command_t {
unsigned char select_channel : 2;
} __attribute__((packed));
static void dec_sleep_counter(void);
static void timer_int_callback(void);
void init_pit() {
struct pit_command_t cmd = {
@ -54,20 +62,60 @@ void init_pit() {
.operating_mode = PIT_MODES_SQUARE_WAVE_GENERATOR,
.bcd = 0,
};
port_byte_out(0x43, *(unsigned char *)(&cmd));
port_byte_out(0x40, PIT_PROGRAM_REG & 0xff);
port_byte_out(0x40, (PIT_PROGRAM_REG & 0xff00) >> 8);
port_byte_out(PIT_COMMAND_PORT, *(unsigned char *)(&cmd));
port_byte_out(PIT_CHANNEL0_DATA_PORT, PIT_PROGRAM_REG & 0xff);
port_byte_out(PIT_CHANNEL0_DATA_PORT, (PIT_PROGRAM_REG & 0xff00) >> 8);
register_interrupt_handler(IRQ0, timer_interrupt_handler);
add_timer_callback(dec_sleep_counter);
add_timer_callback(timer_int_callback);
}
static volatile int sleep_counter = 0;
static void dec_sleep_counter(void) {
static void timer_int_callback(void) {
uptime_ms += 1000 / CLOCK_PRECISION_HZ;
sleep_counter--;
}
uint32_t get_uptime_ms(void) { return uptime_ms; }
void set_beep_frequency_hz(uint32_t frequency_hz) {
if (frequency_hz == 0) {
disable_beep();
return;
}
uint32_t divisor = PIT_CRYSTAL_HZ / frequency_hz;
if (divisor == 0) {
divisor = 1;
}
if (divisor > 0xffff) {
divisor = 0xffff;
}
struct pit_command_t cmd = {
.select_channel = PIT_SELECT_CHANNEL2,
.access_mode = PIT_ACCESS_MODE_LOHIBYTE,
.operating_mode = PIT_MODES_SQUARE_WAVE_GENERATOR,
.bcd = 0,
};
port_byte_out(PIT_COMMAND_PORT, *(unsigned char *)(&cmd));
port_byte_out(PIT_CHANNEL2_DATA_PORT, divisor & 0xff);
port_byte_out(PIT_CHANNEL2_DATA_PORT, (divisor & 0xff00) >> 8);
uint8_t speaker = port_byte_in(PC_SPEAKER_PORT);
port_byte_out(PC_SPEAKER_PORT, speaker | 0x03);
speaker_frequency_hz = frequency_hz;
}
void disable_beep(void) {
uint8_t speaker = port_byte_in(PC_SPEAKER_PORT);
port_byte_out(PC_SPEAKER_PORT, speaker & 0xfc);
speaker_frequency_hz = 0;
}
uint32_t get_beep_frequency_hz(void) { return speaker_frequency_hz; }
void msleep(int ms) {
sleep_counter = ms / 10;
while (sleep_counter > 0) {

View File

@ -1,8 +1,16 @@
#pragma once
#include <stdint.h>
#define MAX_BEEP_FREQUENCY_HZ 44100u
typedef void (*timer_callback)(void);
void init_pit(void);
void add_timer_callback(timer_callback tc);
uint32_t get_uptime_ms(void);
void set_beep_frequency_hz(uint32_t frequency_hz);
void disable_beep(void);
uint32_t get_beep_frequency_hz(void);
void msleep(int ms);

View File

@ -2,25 +2,25 @@
static inline unsigned char port_byte_in(unsigned short port) {
unsigned char result;
asm volatile("in %%dx, %%al" : "=a" (result) : "d" (port));
asm volatile("in %%dx, %%al" : "=a"(result) : "d"(port));
return result;
}
static inline unsigned short port_word_in(unsigned short port) {
unsigned short result;
asm volatile("in %%dx, %%ax" : "=a" (result) : "d" (port));
asm volatile("in %%dx, %%ax" : "=a"(result) : "d"(port));
return result;
}
static inline void port_byte_out(unsigned short port, unsigned char data) {
asm volatile("outb %%al, %%dx" : : "a" (data), "d" (port));
asm volatile("outb %%al, %%dx" : : "a"(data), "d"(port));
}
static inline void port_word_out(unsigned short port, unsigned short data) {
asm volatile("outw %%ax, %%dx" : : "a" (data), "d" (port));
asm volatile("outw %%ax, %%dx" : : "a"(data), "d"(port));
}
/* assembler-long, not c-long */
static inline void port_long_out(unsigned short port, unsigned int data) {
asm volatile("outl %%eax, %%dx" : : "a" (data), "d" (port));
asm volatile("outl %%eax, %%dx" : : "a"(data), "d"(port));
}

View File

@ -9,37 +9,35 @@ enum {
void uartinit() {
// Turn off the FIFO
port_byte_out(COM1+2, 0);
port_byte_out(COM1 + 2, 0);
// 9600 baud, 8 data bits, 1 stop bit, parity off.
port_byte_out(COM1+3, 0x80); // Unlock divisor
port_byte_out(COM1+0, 115200/9600);
port_byte_out(COM1+1, 0);
port_byte_out(COM1+3, 0x03); // Lock divisor, 8 data bits.
port_byte_out(COM1+4, 0);
port_byte_out(COM1+1, 0x01); // Enable receive interrupts.
port_byte_out(COM1 + 3, 0x80); // Unlock divisor
port_byte_out(COM1 + 0, 115200 / 9600);
port_byte_out(COM1 + 1, 0);
port_byte_out(COM1 + 3, 0x03); // Lock divisor, 8 data bits.
port_byte_out(COM1 + 4, 0);
port_byte_out(COM1 + 1, 0x01); // Enable receive interrupts.
// If status is 0xFF, no serial port.
if(port_byte_in(COM1+5) == 0xFF)
if (port_byte_in(COM1 + 5) == 0xFF)
return;
uart = 1;
// Acknowledge pre-existing interrupt conditions;
// enable interrupts.
port_byte_in(COM1+2);
port_byte_in(COM1+0);
port_byte_in(COM1 + 2);
port_byte_in(COM1 + 0);
}
void
uartputc(char c)
{
void uartputc(char c) {
int i;
if (!uart)
return;
/* What is that *skeleton emoji*? */
for (i = 0; i < 128 && !(port_byte_in(COM1+5) & 0x20); i++) {
for (i = 0; i < 128 && !(port_byte_in(COM1 + 5) & 0x20); i++) {
asm("pause");
}
port_byte_out(COM1+0, c);
port_byte_out(COM1 + 0, c);
}

View File

@ -1,9 +1,9 @@
#include "vga.h"
#include "port.h"
#include "../lib/string.h"
#include "cpu/memlayout.h"
#include "port.h"
static char* const video_memory = (char*) (KERNBASE + 0xb8000);
static char *const video_memory = (char *)(KERNBASE + 0xb8000);
enum colors16 : unsigned char {
black = 0,
@ -32,15 +32,13 @@ unsigned vga_get_offset(unsigned col, unsigned row) {
return row * VGA_COLS + col;
}
unsigned vga_get_row_from_offset(unsigned offset) {
return offset / VGA_COLS;
}
unsigned vga_get_row_from_offset(unsigned offset) { return offset / VGA_COLS; }
void vga_set_cursor(unsigned offset) {
port_byte_out(VGA_CTRL_REGISTER, VGA_OFFSET_HIGH);
port_byte_out(VGA_DATA_REGISTER, (unsigned char) (offset >> 8));
port_byte_out(VGA_DATA_REGISTER, (unsigned char)(offset >> 8));
port_byte_out(VGA_CTRL_REGISTER, VGA_OFFSET_LOW);
port_byte_out(VGA_DATA_REGISTER, (unsigned char) (offset & 0xff));
port_byte_out(VGA_DATA_REGISTER, (unsigned char)(offset & 0xff));
}
unsigned vga_get_cursor() {
@ -64,14 +62,15 @@ void vga_clear_screen() {
}
static unsigned scroll() {
kmemmove(video_memory, video_memory + 2 * VGA_COLS, 2 * VGA_COLS * (VGA_ROWS-1));
kmemmove(video_memory, video_memory + 2 * VGA_COLS,
2 * VGA_COLS * (VGA_ROWS - 1));
for (int col = 0; col < VGA_COLS; col++) {
vga_set_char(vga_get_offset(col, VGA_ROWS - 1), ' ');
}
return vga_get_offset(0, VGA_ROWS - 1);
}
void vga_print_string(const char* s) {
void vga_print_string(const char *s) {
unsigned offset = vga_get_cursor();
while (*s != 0) {
if (*s == '\n') {

View File

@ -1,19 +1,18 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
enum {
VGA_ROWS = 25,
VGA_COLS = 80,
VGA_ROWS = 25,
VGA_COLS = 80,
VGA_CTRL_REGISTER = 0x3d4,
VGA_DATA_REGISTER = 0x3d5,
VGA_OFFSET_LOW = 0x0f,
VGA_OFFSET_HIGH = 0x0e,
VGA_CTRL_REGISTER = 0x3d4,
VGA_DATA_REGISTER = 0x3d5,
VGA_OFFSET_LOW = 0x0f,
VGA_OFFSET_HIGH = 0x0e,
};
unsigned vga_get_cursor();
unsigned vga_get_row_from_offset(unsigned offset);
unsigned vga_get_offset(unsigned col, unsigned row);
@ -21,7 +20,19 @@ void vga_set_cursor(unsigned offset);
void vga_clear_screen();
void vga_set_char(unsigned offset, char c);
void vga_print_string(const char* s);
void vga_print_string(const char *s);
#define VGA_GRAPHICS_WIDTH 320
#define VGA_GRAPHICS_HEIGHT 200
typedef enum {
VGA_DISPLAY_MODE_TEXT = 0,
VGA_DISPLAY_MODE_GRAPHICS = 1,
} vga_display_mode_t;
void vgaMode13();
void vgaMode3();
void switch_to_graphics_mode();
void switch_to_text_mode();
vga_display_mode_t vga_get_display_mode();
bool vga_is_text_mode();

50
elf.h
View File

@ -3,39 +3,39 @@
#include <stdint.h>
enum {
EI_NIDENT = 16,
EI_NIDENT = 16,
};
typedef uint32_t Elf32_Addr;
typedef uint32_t Elf32_Off;
#define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian
#define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian
typedef struct {
uint32_t magic;
unsigned char e_ident[EI_NIDENT - 4];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
Elf32_Addr e_entry;
Elf32_Off e_phoff;
Elf32_Off e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
uint32_t magic;
unsigned char e_ident[EI_NIDENT - 4];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
Elf32_Addr e_entry;
Elf32_Off e_phoff;
Elf32_Off e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
} Elf32_Ehdr;
typedef struct {
uint32_t p_type;
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
uint32_t p_filesz;
uint32_t p_memsz;
uint32_t p_flags;
uint32_t p_align;
uint32_t p_type;
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
uint32_t p_filesz;
uint32_t p_memsz;
uint32_t p_flags;
uint32_t p_align;
} Elf32_Phdr;

View File

@ -1,12 +1,12 @@
#include "fs.h"
#include "../lib/string.h"
#include "../drivers/ata.h"
#include "../lib/string.h"
enum {
fs_start = 1, // sector where the FS starts
};
int stat(const char* name, struct stat *buf) {
int stat(const char *name, struct stat *buf) {
struct dir dir;
read_sectors_ATA_PIO(&dir, fs_start, 1);
for (int i = 0; i < ents_in_dir; ++i) {
@ -23,7 +23,7 @@ int stat(const char* name, struct stat *buf) {
/* Find file by name and read it into buffer with size bufsize.
* At most (bufsize & -512) bytes will be read.
* Return number of bytes read or -1 on failure. */
int read_file(const struct stat *statbuf, void* buf, uint32_t bufsize) {
int read_file(const struct stat *statbuf, void *buf, uint32_t bufsize) {
uint32_t sector = fs_start + statbuf->start_sector;
uint32_t bytes_read = 0;
uint32_t file_sectors = (statbuf->size + sector_size - 1) / sector_size;

26
fs/fs.h
View File

@ -20,34 +20,34 @@ size is in bytes, name is 0-terminated.
*/
enum {
sector_size = 512,
ents_in_dir = 15,
sector_size = 512,
ents_in_dir = 15,
};
struct dirent {
uint32_t offset_sectors;
uint32_t size_bytes;
uint32_t reserved;
char name[20];
uint32_t offset_sectors;
uint32_t size_bytes;
uint32_t reserved;
char name[20];
};
/* This way we have 512 bytes (1 sector) per dir */
struct dir {
char reserved[32];
struct dirent entries[ents_in_dir];
char reserved[32];
struct dirent entries[ents_in_dir];
};
struct stat {
uint32_t size;
uint32_t start_sector;
uint32_t reserved1, reserved2;
uint32_t size;
uint32_t start_sector;
uint32_t reserved1, reserved2;
};
/* Find file by name and fill information in buf.
* Returns zero on success, nonzero on failure. */
int stat(const char* name, struct stat *buf);
int stat(const char *name, struct stat *buf);
/* Find file by name and read it into buffer with size bufsize.
* At most (bufsize & ~511) bytes will be read.
* Return number of bytes read or -1 on failure. */
int read_file(const struct stat *statbuf, void* buf, uint32_t bufsize);
int read_file(const struct stat *statbuf, void *buf, uint32_t bufsize);

View File

@ -1,36 +1,36 @@
#include "console.h"
#include "cpu/isr.h"
#include "cpu/gdt.h"
#include "cpu/isr.h"
#include "cpu/memlayout.h"
#include "drivers/keyboard.h"
#include "drivers/vga.h"
#include "drivers/ata.h"
#include "drivers/keyboard.h"
#include "drivers/misc.h"
#include "drivers/pit.h"
#include "drivers/uart.h"
#include "drivers/vga.h"
#include "fs/fs.h"
#include "kernel/mem.h"
#include "lib/string.h"
#include "proc.h"
#include "kernel/mem.h"
void vga_set_pixel(int x, int y, int color) {
unsigned char* pixel = (unsigned char*) (KERNBASE + 0xA0000 + 320 * y + x);
unsigned char *pixel = (unsigned char *)(KERNBASE + 0xA0000 + 320 * y + x);
*pixel = color;
}
void graphtest() {
vgaMode13();
switch_to_graphics_mode();
for (int i = 0; i < 320; ++i) {
for (int j = 0; j < 200; ++j) {
vga_set_pixel(i, j, (i+j)/2);
vga_set_pixel(i, j, (i + j) / 2);
}
}
msleep(5000);
vgaMode3();
switch_to_text_mode();
vga_clear_screen();
}
void read_line_input(char* buf, size_t* ret_len, size_t cap) {
void read_line_input(char *buf, size_t *ret_len, size_t cap) {
size_t len = 0;
while (1) {
while (kbd_can_take_from_copy_buffer()) {
@ -50,8 +50,8 @@ void read_line_input(char* buf, size_t* ret_len, size_t cap) {
len--;
}
} else if (keycode < keycodes) {
char ch = shift_pressed ? keysym_mapped_ascii_upper[keycode] :
keysym_mapped_ascii_lower[keycode];
char ch = shift_pressed ? keysym_mapped_ascii_upper[keycode]
: keysym_mapped_ascii_lower[keycode];
if (ch != 0) {
char haha[2] = {ch, 0};
printk(haha);
@ -76,16 +76,15 @@ void read_line_input(char* buf, size_t* ret_len, size_t cap) {
sti();
}
}
end:
end:
printk("\n");
*ret_len = len;
}
void kmain() {
freerange(P2V(3u<<20), P2V(4u<<20));
kvmalloc(); // map all of physical memory at KERNBASE
freerange(P2V(4u<<20), P2V(PHYSTOP));
freerange(P2V(3u << 20), P2V(4u << 20));
kvmalloc(); // map all of physical memory at KERNBASE
freerange(P2V(4u << 20), P2V(PHYSTOP));
load_gdt();
init_keyboard();
@ -106,7 +105,9 @@ void kmain() {
input[input_len] = 0;
printk(input);
printk("\n");
if (cstr_equal(input, "halt")) {
if (cstr_equal(input, "")) {
run_elf("snake");
} else if (cstr_equal(input, "halt")) {
printk("Bye\n");
qemu_shutdown();
} else if (cstr_equal(input, "work")) {
@ -117,7 +118,7 @@ void kmain() {
} else if (cstr_equal(input, "graphtest")) {
graphtest();
} else if (cstr_starts_with(input, "run ")) {
const char* cmd = input + 4;
const char *cmd = input + 4;
run_elf(cmd);
} else {
printk("unknown command, try: halt | run CMD\n");

View File

@ -2,69 +2,62 @@
// memory for user processes, kernel stacks, page table pages,
// and pipe buffers. Allocates 4096-byte pages.
#include <stdint.h>
#include "mem.h"
#include "console.h"
#include "cpu/memlayout.h"
#include "cpu/x86.h"
#include <stdint.h>
struct run {
struct run *next;
struct run *next;
};
struct {
struct run *freelist;
struct run *freelist;
} kmem;
void
freerange(void *vstart, void *vend)
{
char *p;
p = (char*)PGROUNDUP((uintptr_t)vstart);
for(; p + PGSIZE <= (char*)vend; p += PGSIZE)
kfree(p);
void freerange(void *vstart, void *vend) {
char *p;
p = (char *)PGROUNDUP((uintptr_t)vstart);
for (; p + PGSIZE <= (char *)vend; p += PGSIZE)
kfree(p);
}
void*
memset(void *dst, unsigned c, uint64_t n)
{
if ((uintptr_t)dst%4 == 0 && n%4 == 0){
c &= 0xFF;
stosl(dst, (c<<24)|(c<<16)|(c<<8)|c, n/4);
} else
stosb(dst, c, n);
return dst;
void *memset(void *dst, unsigned c, uint64_t n) {
if ((uintptr_t)dst % 4 == 0 && n % 4 == 0) {
c &= 0xFF;
stosl(dst, (c << 24) | (c << 16) | (c << 8) | c, n / 4);
} else
stosb(dst, c, n);
return dst;
}
// Free the page of physical memory pointed at by v,
// which normally should have been returned by a
// call to kalloc(). (The exception is when
// initializing the allocator; see kinit above.)
void
kfree(void *v)
{
struct run *r;
void kfree(void *v) {
struct run *r;
if((uintptr_t)v % PGSIZE || V2P(v) >= PHYSTOP)
panic("kfree");
if ((uintptr_t)v % PGSIZE || V2P(v) >= PHYSTOP)
panic("kfree");
// Fill with junk to catch dangling refs.
memset(v, 1, PGSIZE);
// Fill with junk to catch dangling refs.
memset(v, 1, PGSIZE);
r = v;
r->next = kmem.freelist;
kmem.freelist = r;
r = v;
r->next = kmem.freelist;
kmem.freelist = r;
}
// Allocate one 4096-byte page of physical memory.
// Returns a pointer that the kernel can use.
// Returns 0 if the memory cannot be allocated.
void* kalloc(void)
{
struct run *r;
void *kalloc(void) {
struct run *r;
r = kmem.freelist;
if(r)
kmem.freelist = r->next;
return (char*)r;
r = kmem.freelist;
if (r)
kmem.freelist = r->next;
return (char *)r;
}

View File

@ -5,11 +5,11 @@
typedef uintptr_t pde_t;
typedef uintptr_t pte_t;
void* memset(void *dst, unsigned c, uint64_t n);
void *memset(void *dst, unsigned c, uint64_t n);
void freerange(void *vstart, void *vend);
void* kalloc(void);
void kfree(void*);
void *kalloc(void);
void kfree(void *);
pde_t *setupkvm();
void kvmalloc();

View File

@ -1,7 +1,7 @@
#include "mem.h"
#include "console.h"
#include "cpu/memlayout.h"
#include "cpu/x86.h"
#include "console.h"
#include "mem.h"
pde_t *kvm;
@ -22,8 +22,7 @@ void kvmalloc() {
switchkvm();
}
void switchkvm(void)
{
void switchkvm(void) {
lcr3(V2P(kvm)); // switch to the kernel page table
}
@ -33,9 +32,7 @@ void switchkvm(void)
// Return the address of the PTE in page table pgdir
// that corresponds to virtual address va. If alloc!=0,
// create any required page table pages.
static pte_t *
walkpgdir(pde_t *pgdir, const void *va, int alloc)
{
static pte_t *walkpgdir(pde_t *pgdir, const void *va, int alloc) {
pde_t *pde;
pte_t *pgtab;
@ -59,19 +56,19 @@ walkpgdir(pde_t *pgdir, const void *va, int alloc)
// entries, if necessary.
*pde = V2P(pgtab) | PTE_P | PTE_W | PTE_U;
}
return &pgtab[PTX(va)];
return &pgtab[PTX(va)];
}
// Create PTEs for virtual addresses starting at va that refer to
// physical addresses starting at pa.
// size might not be page-aligned.
static int
mappages(pde_t *pgdir, void *va, uintptr_t size, uintptr_t pa, int perm){
static int mappages(pde_t *pgdir, void *va, uintptr_t size, uintptr_t pa,
int perm) {
if ((uintptr_t)va % PGSIZE != 0)
panic("Why??");
char* a = va;
char* last = (char *)PGROUNDDOWN(((uintptr_t)va) + size - 1);
for (;;){
char *a = va;
char *last = (char *)PGROUNDDOWN(((uintptr_t)va) + size - 1);
for (;;) {
pte_t *pte = walkpgdir(pgdir, a, 1);
if (pte == 0)
return -1;
@ -82,8 +79,8 @@ mappages(pde_t *pgdir, void *va, uintptr_t size, uintptr_t pa, int perm){
break;
a += PGSIZE;
pa += PGSIZE;
}
return 0;
}
return 0;
}
// top may be not page-aligned
@ -96,7 +93,7 @@ int allocuvm(pde_t *pgdir, uintptr_t base, uintptr_t top) {
return -1;
}
memset(pa, 0, PGSIZE);
if (mappages(pgdir, (void*)a, PGSIZE, V2P(pa), PTE_W | PTE_U) < 0) {
if (mappages(pgdir, (void *)a, PGSIZE, V2P(pa), PTE_W | PTE_U) < 0) {
kfree(pa);
return -1;
}
@ -107,16 +104,16 @@ int allocuvm(pde_t *pgdir, uintptr_t base, uintptr_t top) {
static void freept(pte_t *pt) {
for (int i = 0; i < NPTENTRIES; i++) {
if (pt[i] & PTE_P) {
kfree((char*)P2V(PTE_ADDR(pt[i])));
kfree((char *)P2V(PTE_ADDR(pt[i])));
}
}
kfree((char*)pt);
kfree((char *)pt);
}
void freevm(pde_t *pgdir) {
for (int i = 0; i < NPDENTRIES / 2; i++) {
if (pgdir[i] & PTE_P) {
freept((pte_t*)P2V(PTE_ADDR(pgdir[i])));
freept((pte_t *)P2V(PTE_ADDR(pgdir[i])));
}
}
kfree(pgdir);

View File

@ -5,17 +5,17 @@
/* Bribki are stored here */
typedef struct {
char chars[5];
char chars[5];
} DebugBribka;
DebugBribka byte_to_string_bribka(uint8_t byte) {
DebugBribka res = {.chars = {' ', ' ', ' ', ' ', 0}};
int p = 3;
while (byte > 0) {
uint8_t d = byte % 10;
byte /= 10;
res.chars[p] = (char)(d) + '0';
p--;
}
return res;
DebugBribka res = {.chars = {' ', ' ', ' ', ' ', 0}};
int p = 3;
while (byte > 0) {
uint8_t d = byte % 10;
byte /= 10;
res.chars[p] = (char)(d) + '0';
p--;
}
return res;
}

View File

@ -1,28 +1,28 @@
#include "string.h"
void memcpy(void*dst, const void *src, size_t size) {
void memcpy(void *dst, const void *src, size_t size) {
for (size_t i = 0; i < size; i++) {
((char*)dst)[i] = ((const char*)src)[i];
((char *)dst)[i] = ((const char *)src)[i];
}
}
void kmemmove(char* dst, char* src, size_t size) {
if (dst == src) return;
if (dst > src && dst < src + size) { // s d
// copy right-to-left
for (; size != 0; size--) {
dst[size - 1] = src[size - 1];
}
} else {
// copy left-to-right
for (size_t i = 0; i < size; ++i) {
dst[i] = src[i];
}
}
void kmemmove(char *dst, char *src, size_t size) {
if (dst == src)
return;
if (dst > src && dst < src + size) { // s d
// copy right-to-left
for (; size != 0; size--) {
dst[size - 1] = src[size - 1];
}
} else {
// copy left-to-right
for (size_t i = 0; i < size; ++i) {
dst[i] = src[i];
}
}
}
int strncmp(const char* s1, const char* s2, size_t size) {
int strncmp(const char *s1, const char *s2, size_t size) {
while (size && *s1 && *s2 && *s1 == *s2) {
size--;
s1++;
@ -34,7 +34,7 @@ int strncmp(const char* s1, const char* s2, size_t size) {
return (unsigned char)(*s1) - (unsigned char)(*s2);
}
bool cstr_equal(const char* s1, const char* s2) {
bool cstr_equal(const char *s1, const char *s2) {
while (*s1 && *s2 && *s1 == *s2) {
s1++;
s2++;
@ -42,7 +42,6 @@ bool cstr_equal(const char* s1, const char* s2) {
return *s1 == 0 && *s2 == 0;
}
bool cstr_starts_with(const char *A, const char *B) {
while (*A && *B) {
if (*A != *B)

View File

@ -4,8 +4,8 @@
typedef unsigned size_t;
void memcpy(void*dst, const void *src, size_t size);
void kmemmove(char* dst, char* src, size_t size);
int strncmp(const char* s1, const char* s2, size_t size);
bool cstr_equal(const char* s1, const char* s2);
void memcpy(void *dst, const void *src, size_t size);
void kmemmove(char *dst, char *src, size_t size);
int strncmp(const char *s1, const char *s2, size_t size);
bool cstr_equal(const char *s1, const char *s2);
bool cstr_starts_with(const char *A, const char *B);

29
proc.c
View File

@ -1,4 +1,6 @@
#include "proc.h"
#include "drivers/pit.h"
#include "drivers/vga.h"
struct context {
// matches the behavior of swtch()
@ -25,13 +27,16 @@ struct vm {
} vm;
void trapret();
void swtch(void** oldstack, void* newstack);
void swtch(void **oldstack, void *newstack);
pde_t *get_user_proc_page_directory() {
if (!vm.user_task) {
return 0;
}
return vm.user_task->pgdir;
}
void run_elf(const char* name) {
void run_elf(const char *name) {
struct stat statbuf;
if (stat(name, &statbuf) != 0) {
printk(name);
@ -42,24 +47,27 @@ void run_elf(const char* name) {
vm.user_task = kalloc();
}
// todo: this code contains 9999 memory leaks but I don't care
// todo: yabloko is fucking shit made my monkeys, I want to forget this fucking nightmare
// todo: yabloko is fucking shit made my monkeys, I want to forget this
// fucking nightmare
vm.user_task->pgdir = setupkvm();
if (allocuvm(vm.user_task->pgdir, USER_BASE, USER_BASE + statbuf.size)) {
printk("Fail: out of memory\n");
return;
}
if (allocuvm(vm.user_task->pgdir, USER_STACK_BASE - 2 * PGSIZE, USER_STACK_BASE)) {
if (allocuvm(vm.user_task->pgdir, USER_STACK_BASE - 2 * PGSIZE,
USER_STACK_BASE)) {
printk("Fail: out of memory\n");
return;
}
switchuvm(&vm.user_task->tss, vm.user_task->stack.bottom, vm.user_task->pgdir);
switchuvm(&vm.user_task->tss, vm.user_task->stack.bottom,
vm.user_task->pgdir);
if (read_file(&statbuf, (void*)USER_BASE, 100 << 20) <= 0) {
if (read_file(&statbuf, (void *)USER_BASE, 100 << 20) <= 0) {
printk(name);
printk(": file not found\n");
return;
}
Elf32_Ehdr *hdr = (void*)USER_BASE;
Elf32_Ehdr *hdr = (void *)USER_BASE;
struct kstack *u = &vm.user_task->stack;
memset(u, 0, sizeof(*u));
@ -83,7 +91,12 @@ void run_elf(const char* name) {
}
_Noreturn void killproc() {
void* task_stack;
void *task_stack;
disable_beep();
if (!vga_is_text_mode()) {
switch_to_text_mode();
vga_clear_screen();
}
switchkvm();
freevm(vm.user_task->pgdir);
sti();

11
proc.h
View File

@ -1,16 +1,15 @@
#pragma once
#include "elf.h"
#include "fs/fs.h"
#include "console.h"
#include "cpu/gdt.h"
#include "cpu/isr.h"
#include "cpu/memlayout.h"
#include "elf.h"
#include "fs/fs.h"
#include "kernel/mem.h"
#include "lib/string.h"
#include "console.h"
pde_t *get_user_proc_page_directory();
pde_t* get_user_proc_page_directory();
void run_elf(const char* name);
void run_elf(const char *name);
_Noreturn void killproc();

39
snake/keymapping.h Normal file
View File

@ -0,0 +1,39 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
/* We still have beef with keyboard, even in userspace */
#define KEYCODE_SHIFT 42
#define KEYCODE_ENTER 28
#define KEYCODE_BACKSPACE 14
#define KEYCODE_SPACE 57
#define KEYCODE_ESCAPE 1
#define KEYCODE_A 30
#define KEYCODE_S 31
#define KEYCODE_D 32
#define KEYCODE_W 17
#define KEYCODE_Q 16
#define KEYCODE_ENTER 28
#define KEYCODE_LEFT 75
#define KEYCODE_RIGHT 77
#define KEYCODE_UP 72
#define KEYCODE_DOWN 80
#define KEYCODE_1 2
#define KEYCODE_2 3
bool is_keycode_for_press_left(uint8_t keycode) {
return keycode == KEYCODE_LEFT || keycode == KEYCODE_A;
}
bool is_keycode_for_press_right(uint8_t keycode) {
return keycode == KEYCODE_RIGHT || keycode == KEYCODE_D;
}
bool is_keycode_for_press_up(uint8_t keycode) {
return keycode == KEYCODE_UP || keycode == KEYCODE_W;
}
bool is_keycode_for_press_down(uint8_t keycode) {
return keycode == KEYCODE_DOWN || keycode == KEYCODE_S;
}

714
snake/map_data.h Normal file
View File

@ -0,0 +1,714 @@
#pragma once
#include "maps.h"
static const MapList
map_list = {.maps =
{
/* CLUSTERTRACT.png */
[0] =
{
.name = "CLUSTERTRACT",
.map =
{
[0] = {tile_wall_t, tile_wall_lr,
tile_wall_lr, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_wall_lr,
tile_wall_lr, tile_wall_b},
[1] = {tile_wall_rb, tile_wall_lt,
tile_wall_lr, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_wall_lr,
tile_wall_lb, tile_wall_rt},
[2] =
{tile_wall_lr,
tile_wall_rb, tile_wall_lt, tile_empty, tile_empty, tile_empty, tile_empty, tile_wall_lb, tile_wall_rt, tile_wall_lr},
[3] =
{tile_wall_lr,
tile_wall_lr, tile_empty, tile_empty, tile_empty, tile_empty, tile_empty, tile_empty, tile_wall_lr, tile_wall_lr},
[4] =
{tile_wall_lr,
tile_wall_lr, tile_empty, tile_empty, tile_empty, tile_empty, tile_empty, tile_empty, tile_wall_lr, tile_wall_lr},
[5] =
{tile_wall_lr,
tile_wall_lr, tile_empty, tile_empty, tile_empty, tile_empty, tile_empty, tile_wall_b, tile_wall_lrt, tile_wall_lr},
[6] =
{tile_wall_lr,
tile_wall_lr, tile_empty, tile_empty, tile_empty, tile_empty, tile_empty, tile_empty, tile_wall_lr, tile_wall_lr},
[7] =
{tile_wall_lr,
tile_wall_lrb, tile_wall_rt, tile_empty, tile_empty, tile_empty, tile_empty, tile_empty, tile_wall_lr, tile_wall_lr},
[8] =
{tile_wall_lr,
tile_wall_lrb, tile_wall_lt, tile_empty, tile_empty, tile_empty, tile_empty, tile_wall_rb, tile_wall_lrt, tile_wall_lr},
[9] =
{tile_wall_lr,
tile_wall_lr, tile_empty, tile_empty, tile_empty, tile_empty, tile_empty, tile_wall_lb, tile_wall_lrt, tile_wall_lr},
[10] =
{tile_wall_lr,
tile_wall_lr, tile_empty, tile_empty, tile_empty, tile_empty, tile_empty, tile_empty, tile_wall_lr,
tile_wall_lr},
[11] =
{tile_wall_lr,
tile_wall_lrb, tile_wall_t, tile_empty, tile_empty, tile_empty, tile_empty, tile_empty, tile_wall_lr,
tile_wall_lr},
[12] =
{tile_wall_lr, tile_wall_lr,
tile_empty, tile_empty, tile_empty, tile_empty, tile_empty, tile_empty, tile_wall_lr,
tile_wall_lr},
[13] =
{tile_wall_lr, tile_wall_lr,
tile_empty, tile_empty, tile_empty, tile_empty, tile_empty, tile_empty, tile_wall_lr,
tile_wall_lr},
[14] =
{tile_wall_lr, tile_wall_lb,
tile_wall_rt,
tile_empty, tile_empty, tile_empty, tile_empty, tile_wall_rb,
tile_wall_lt, tile_wall_lr},
[15] =
{tile_wall_lb, tile_wall_rt,
tile_wall_lr,
tile_empty, tile_empty, tile_empty, tile_empty, tile_wall_lr,
tile_wall_rb, tile_wall_lt},
},
},
/* EMPTY.png */
[1] =
{
.name = "EMPTY",
.map =
{
[0] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[1] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[2] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[3] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[4] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[5] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[6] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[7] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[8] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[9] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[10] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty},
[11] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty},
[12] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty},
[13] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty},
[14] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty},
[15] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty},
},
},
/* GREEN DAYS.png */
[2] =
{
.name = "GREEN DAYS",
.map =
{
[0] = {tile_wall_rb, tile_wall_tb,
tile_wall_tb, tile_wall_t,
tile_empty, tile_empty,
tile_wall_b, tile_wall_tb,
tile_wall_tb, tile_wall_rt},
[1] = {tile_wall_lr, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_wall_lr},
[2] = {tile_wall_lr, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_wall_lr},
[3] = {tile_wall_l, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_wall_l},
[4] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[5] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[6] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[7] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[8] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[9] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[10] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty},
[11] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty},
[12] =
{tile_wall_r, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_wall_r},
[13] =
{tile_wall_lr, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_wall_lr},
[14] =
{tile_wall_lr, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_wall_lr},
[15] =
{tile_wall_lb, tile_wall_tb,
tile_wall_tb, tile_wall_t,
tile_empty, tile_empty,
tile_wall_b, tile_wall_tb,
tile_wall_tb, tile_wall_lt},
},
},
/* QUARTERHOUSE.png */
[3] =
{
.name = "QUARTERHOUSE",
.map =
{
[0] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[1] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[2] = {tile_empty, tile_empty,
tile_empty, tile_wall_b,
tile_wall_rtb, tile_wall_tb,
tile_wall_t, tile_empty,
tile_empty, tile_empty},
[3] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_wall_lr,
tile_wall, tile_empty,
tile_empty, tile_empty,
tile_empty},
[4] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_wall_lr,
tile_wall, tile_empty,
tile_empty, tile_empty,
tile_empty},
[5] = {tile_wall_rt, tile_empty,
tile_empty, tile_empty,
tile_wall_lb, tile_wall_rt,
tile_empty, tile_empty,
tile_empty, tile_wall_rb},
[6] = {tile_wall_lrt, tile_empty,
tile_empty, tile_empty,
tile_wall, tile_wall_lr,
tile_empty, tile_empty,
tile_empty, tile_wall_lrb},
[7] = {tile_wall_lrt,
tile_wall, tile_wall_rb,
tile_wall_tb, tile_wall_rtb,
tile_wall_lrtb, tile_wall_tb,
tile_wall_rt,
tile_wall, tile_wall_lrb},
[8] = {tile_wall_lrtb, tile_wall_tb,
tile_wall_lt,
tile_wall, tile_wall_lrb,
tile_wall_lt,
tile_wall, tile_wall_lb,
tile_wall_tb, tile_wall_lrtb},
[9] = {tile_wall_lrt, tile_empty,
tile_empty, tile_empty,
tile_wall_lr,
tile_wall, tile_empty,
tile_empty, tile_empty,
tile_wall_lrb},
[10] =
{tile_wall_lt, tile_empty,
tile_empty, tile_empty,
tile_wall_lb, tile_wall_rt,
tile_empty, tile_empty, tile_empty,
tile_wall_lb},
[11] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_wall,
tile_wall_lr, tile_empty,
tile_empty, tile_empty,
tile_empty},
[12] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_wall,
tile_wall_lr, tile_empty,
tile_empty, tile_empty,
tile_empty},
[13] =
{tile_empty, tile_empty, tile_empty,
tile_wall_b, tile_wall_tb,
tile_wall_ltb, tile_wall_t,
tile_empty, tile_empty,
tile_empty},
[14] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty},
[15] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty},
},
},
/* SERPENTINE STRUTS.png */
[4] =
{
.name = "SERPENTINE STRUTS",
.map =
{
[0] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[1] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_wall_r, tile_empty,
tile_empty, tile_wall_r,
tile_empty, tile_empty},
[2] = {tile_empty, tile_empty,
tile_empty, tile_wall_b,
tile_wall_lrtb, tile_wall_tb,
tile_wall_tb, tile_wall_lrtb,
tile_wall_t, tile_empty},
[3] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_wall_l, tile_empty,
tile_empty, tile_wall_l,
tile_empty, tile_empty},
[4] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[5] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[6] = {tile_empty, tile_empty,
tile_empty, tile_wall_r,
tile_empty, tile_empty,
tile_wall_r, tile_empty,
tile_empty, tile_empty},
[7] = {tile_empty, tile_empty,
tile_wall_b, tile_wall_lrtb,
tile_wall_tb, tile_wall_tb,
tile_wall_lrtb, tile_wall_t,
tile_empty, tile_empty},
[8] = {tile_empty, tile_empty,
tile_empty, tile_wall_l,
tile_empty, tile_empty,
tile_wall_l, tile_empty,
tile_empty, tile_empty},
[9] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[10] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty},
[11] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty},
[12] =
{tile_empty, tile_empty,
tile_wall_r, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_wall_r, tile_empty,
tile_empty},
[13] = {tile_empty, tile_wall_b,
tile_wall_lrtb, tile_wall_tb,
tile_wall_tb, tile_wall_tb,
tile_wall_tb, tile_wall_lrtb,
tile_wall_t, tile_empty},
[14] =
{tile_empty, tile_empty,
tile_wall_l, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_wall_l, tile_empty,
tile_empty},
[15] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty},
},
},
/* SHEER GIRTH.png */
[5] =
{
.name = "SHEER GIRTH",
.map =
{
[0] = {tile_empty, tile_wall_rb,
tile_wall_rtb, tile_wall_rtb,
tile_wall_rtb, tile_wall_rtb,
tile_wall_rtb, tile_wall_rtb,
tile_wall_rt, tile_empty},
[1] = {tile_empty, tile_wall_lrb,
tile_wall_lrtb, tile_wall_lrtb,
tile_wall_lrtb, tile_wall_lrtb,
tile_wall_lrtb, tile_wall_lrtb,
tile_wall_lrt, tile_empty},
[2] = {tile_empty, tile_wall_lb,
tile_wall_ltb, tile_wall_ltb,
tile_wall_ltb, tile_wall_ltb,
tile_wall_ltb, tile_wall_ltb,
tile_wall_lt, tile_empty},
[3] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[4] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[5] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[6] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[7] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[8] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[9] = {tile_empty, tile_wall_rb,
tile_wall_rtb, tile_wall_rt,
tile_empty, tile_empty,
tile_wall_rb, tile_wall_rtb,
tile_wall_rt, tile_empty},
[10] =
{tile_empty, tile_wall_lrb,
tile_wall_lrtb, tile_wall_lrt,
tile_empty, tile_empty,
tile_wall_lrb, tile_wall_lrtb,
tile_wall_lrt, tile_empty},
[11] =
{tile_empty, tile_wall_lrb,
tile_wall_lrtb, tile_wall_lrt,
tile_empty, tile_empty,
tile_wall_lrb, tile_wall_lrtb,
tile_wall_lrt, tile_empty},
[12] =
{tile_empty, tile_wall_lrb,
tile_wall_lrtb, tile_wall_lrt,
tile_empty, tile_empty,
tile_wall_lrb, tile_wall_lrtb,
tile_wall_lrt, tile_empty},
[13] =
{tile_empty, tile_wall_lb,
tile_wall_ltb, tile_wall_lt,
tile_empty, tile_empty,
tile_wall_lb, tile_wall_ltb,
tile_wall_lt, tile_empty},
[14] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty},
[15] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty},
},
},
/* VAULT.png */
[6] =
{
.name = "VAULT",
.map =
{
[0] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[1] = {tile_empty, tile_empty,
tile_empty, tile_wall_rb,
tile_wall_tb, tile_wall_tb,
tile_wall_tb, tile_wall_tb,
tile_wall_rt, tile_empty},
[2] = {tile_empty, tile_empty,
tile_empty, tile_wall_lr,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_wall_lr, tile_empty},
[3] = {tile_empty, tile_empty,
tile_empty, tile_wall_lr,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_wall_lr, tile_empty},
[4] = {tile_empty, tile_empty,
tile_empty, tile_wall_lr,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_wall_lr, tile_empty},
[5] = {tile_empty, tile_empty,
tile_empty, tile_wall_lr,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_wall_lr, tile_empty},
[6] = {tile_empty, tile_empty,
tile_empty, tile_wall_lb,
tile_wall_t, tile_empty,
tile_empty, tile_wall_b,
tile_wall_lrt, tile_empty},
[7] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_wall_lr, tile_empty},
[8] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_wall_lr, tile_empty},
[9] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_wall_lr, tile_empty},
[10] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty,
tile_wall_lr, tile_empty},
[11] =
{tile_empty, tile_empty,
tile_wall_rb, tile_wall_t,
tile_empty, tile_empty,
tile_wall_b, tile_wall_tb,
tile_wall_lrt, tile_empty},
[12] =
{tile_empty, tile_empty,
tile_wall_lr, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_wall_lr,
tile_empty},
[13] =
{tile_empty, tile_empty,
tile_wall_lr, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_wall_lr,
tile_empty},
[14] =
{tile_empty, tile_empty,
tile_wall_lb, tile_wall_tb,
tile_wall_tb, tile_wall_tb,
tile_wall_tb, tile_wall_tb,
tile_wall_lt, tile_empty},
[15] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty},
},
},
/* WALLS.png */
[7] =
{
.name = "WALLS",
.map =
{
[0] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[1] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[2] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[3] = {tile_wall_tb, tile_wall_t,
tile_empty, tile_empty,
tile_wall_b, tile_wall_tb,
tile_wall_tb, tile_wall_tb,
tile_wall_tb, tile_wall_tb},
[4] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[5] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[6] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[7] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[8] = {tile_wall_tb, tile_wall_tb,
tile_wall_tb, tile_wall_tb,
tile_wall_t, tile_empty,
tile_empty, tile_wall_b,
tile_wall_tb, tile_wall_tb},
[9] = {tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty,
tile_empty, tile_empty},
[10] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty},
[11] =
{tile_wall_t, tile_empty,
tile_empty, tile_wall_b,
tile_wall_tb, tile_wall_tb,
tile_wall_tb, tile_wall_tb,
tile_wall_tb, tile_wall_tb},
[12] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty},
[13] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty},
[14] =
{tile_wall_tb, tile_wall_tb,
tile_wall_tb, tile_wall_tb,
tile_wall_tb, tile_wall_tb,
tile_wall_t, tile_empty,
tile_empty, tile_wall_b},
[15] =
{tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty, tile_empty, tile_empty,
tile_empty},
},
},
}};

72
snake/maps.h Normal file
View File

@ -0,0 +1,72 @@
#pragma once
#include "misc_utils.h"
typedef enum {
tile_empty = 0,
tile_apple = 1,
tile_wall = 2,
tile_wall_lr = 3,
tile_wall_l = 4,
tile_wall_r = 5,
tile_wall_lrt = 6,
tile_wall_lt = 7,
tile_wall_rt = 8,
tile_wall_lrb = 9,
tile_wall_lb = 10,
tile_wall_rb = 11,
tile_wall_lrtb = 12,
tile_wall_ltb = 13,
tile_wall_rtb = 14,
tile_wall_t = 15,
tile_wall_b = 16,
tile_wall_tb = 17,
tile_snake_l = 18,
tile_snake_r = 19,
tile_snake_t = 20,
tile_snake_b = 21,
tile_snake_bl = 22,
tile_snake_br = 23,
tile_snake_lb = 24,
tile_snake_rb = 25,
tile_snake_tl = 26,
tile_snake_tr = 27,
tile_snake_lt = 28,
tile_snake_rt = 29,
tile_pupa_m2_l = 30,
tile_pupa_m2_r = 31,
tile_pupa_m1_l = 32,
tile_pupa_m1_r = 33,
tile_pupa_0_l = 34,
tile_pupa_0_r = 35,
tile_pupa_p1_l = 36,
tile_pupa_p1_r = 37,
tile_pupa_p2_l = 38,
tile_pupa_p2_r = 39,
tile_pupa_m2_t = 40,
tile_pupa_m1_t = 41,
tile_pupa_0_t = 42,
tile_pupa_p1_t = 43,
tile_pupa_p2_t = 44,
tile_pupa_m2_b = 45,
tile_pupa_m1_b = 46,
tile_pupa_0_b = 47,
tile_pupa_p1_b = 48,
tile_pupa_p2_b = 49,
} tile_t;
#define WORLD_WIDTH 16
#define WORLD_HEIGHT 10
typedef struct {
const char *name;
tile_t map[WORLD_WIDTH][WORLD_HEIGHT];
} MapConfig;
#define PLAYABLE_MAPS_COUNT 8
typedef struct {
MapConfig maps[PLAYABLE_MAPS_COUNT];
} MapList;

91
snake/misc_utils.h Normal file
View File

@ -0,0 +1,91 @@
#pragma once
#include "../syscall.h"
#include <stdbool.h>
#include <stdint.h>
void _Noreturn panic(const char *message) {
syscall(SYS_switch_to_text, 0); // ???
syscall(SYS_puts, (uintptr_t)message);
syscall(SYS_exit, 1);
__builtin_unreachable();
}
#define check(expr) \
if (!(expr)) { \
panic("Assertion failed at " __FILE__ ": " #expr "\n"); \
}
typedef struct {
int x, y;
} ivec2;
void *memcpy(void *dst, const void *src, uint32_t size) {
uint8_t *d = (uint8_t *)dst;
const uint8_t *s = (const uint8_t *)src;
for (uint32_t i = 0; i < size; i++) {
d[i] = s[i];
}
return dst;
}
void *memset(void *dst, int value, uint32_t size) {
uint8_t *d = (uint8_t *)dst;
uint8_t byte = (uint8_t)value;
for (uint32_t i = 0; i < size; i++) {
d[i] = byte;
}
return dst;
}
#define STRING_BUILDER_CAPACITY 150
typedef struct {
char buf[STRING_BUILDER_CAPACITY];
int len;
} StringBuilder;
void StringBuilder_putc(StringBuilder *self, char ch) {
if (self->len + 1 > STRING_BUILDER_CAPACITY) {
panic("string builder\n");
}
self->buf[self->len++] = ch;
}
void StringBuilder_append_cstr(StringBuilder *self, const char *str) {
for (const char *p = str; *p; p++) {
StringBuilder_putc(self, *p);
}
}
void StringBuilder_append_u32(StringBuilder *self, uint32_t x) {
char b[11];
int len = 0;
if (x == 0) {
b[len++] = '0';
} else {
while (x > 0) {
b[len++] = (char)('0' + (x % 10));
x /= 10;
}
int i = 0, j = len - 1;
while (i < j) {
char tmp = b[i];
b[i] = b[j];
b[j] = tmp;
i++;
j--;
}
}
b[len] = 0;
StringBuilder_append_cstr(self, b);
}
const char *StringBuilder_get_cstr(StringBuilder *self) {
StringBuilder_putc(self, 0);
return self->buf;
}
uint32_t time_ms(void) { return (uint32_t)syscall(SYS_time_ms, 0); }

22
snake/pause_effect.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include <stdint.h>
static const uint8_t VGA_GRAY_LUT[256] = {
16, 17, 23, 24, 20, 21, 23, 27, 22, 23, 28, 29, 25, 27, 30, 31, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 18, 19, 21, 22, 23, 23,
23, 22, 22, 24, 26, 28, 30, 29, 28, 27, 26, 27, 27, 27, 28, 26, 23, 21, 26,
26, 27, 27, 28, 28, 27, 27, 27, 28, 29, 30, 30, 30, 30, 29, 29, 29, 29, 29,
30, 29, 28, 27, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 30, 30, 31, 30, 30,
30, 30, 30, 30, 30, 30, 30, 29, 29, 17, 17, 18, 18, 19, 19, 19, 18, 18, 19,
21, 22, 23, 23, 22, 21, 21, 21, 21, 22, 22, 21, 19, 18, 20, 21, 21, 21, 22,
22, 22, 21, 21, 22, 23, 23, 24, 23, 23, 23, 22, 23, 23, 23, 23, 22, 22, 21,
22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 23, 23, 23, 23, 23,
23, 23, 23, 23, 22, 16, 17, 17, 17, 18, 17, 17, 17, 17, 18, 18, 19, 20, 20,
19, 19, 18, 19, 19, 19, 19, 18, 17, 17, 18, 18, 19, 19, 19, 19, 19, 19, 19,
19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 19, 19, 19, 19, 19, 19, 20,
20, 20, 20, 20, 19, 20, 20, 20, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
19, 16, 16, 16, 16, 16, 16, 16, 16,
};
static inline uint8_t vga_to_gray(uint8_t color) { return VGA_GRAY_LUT[color]; }

32
snake/random.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
#include "misc_utils.h"
#include <stdint.h>
typedef struct {
uint32_t state;
} RandomEngine;
static void RandomEngine_init(RandomEngine *self) { self->state = 0x6d2b79f5u; }
static uint32_t RandomEngine_rnd(RandomEngine *self) {
uint32_t x = self->state;
if (x == 0) {
x = 0x6d2b79f5u;
}
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
x = x * 1664525u + 1013904223u;
self->state = x;
return x;
}
static uint32_t RandomEngine_rnd_interval(RandomEngine *self, uint32_t from,
uint32_t until) {
check(from < until);
return from + RandomEngine_rnd(self) % (until - from);
}

807
snake/snake.c Normal file
View File

@ -0,0 +1,807 @@
#include "../drivers/vga.h"
#include "../syscall.h"
#include "keymapping.h"
#include "map_data.h"
#include "maps.h"
#include "misc_utils.h"
#include "pause_effect.h"
#include "random.h"
#include "sprite_data.h"
#include "sprites.h"
#include <stdbool.h>
#include <stdint.h>
#define FRAME_SIZE (VGA_GRAPHICS_WIDTH * VGA_GRAPHICS_HEIGHT)
#define HUD_OFFSET 3
#define GLYPH_SPACING 3
#define FONT_SPACE_INDEX 0
#define FONT_DIGIT_BASE 1
#define FONT_LETTER_BASE (FONT_DIGIT_BASE + 10)
uint8_t frame[FRAME_SIZE] = {1};
#define SNAKE_GAMEMODE_MAX_APPLES 10
#define SNAKE_GAMEMODE_DEFAULT_APPLES 5
typedef enum {
game_screen_gaming,
game_screen_pause,
game_screen_select_map,
game_screen_select_apple_count,
} game_screen_t;
#define SNAKE_START_Y 2
#define SNAKE_START_HEAD_X 4
#define SNAKE_START_PUPA_X 6
typedef enum {
snake_direction_left = 0,
snake_direction_right = 1,
snake_direction_top = 2,
snake_direction_bottom = 3,
} snake_direction_t;
#define WALKING_ANIM_TIME_VERY_SLOW 600
#define HATCHING_ANIM_TIME_VERY_SLOW 400
#define WALKING_ANIM_TIME_SLOW 450
#define HATCHING_ANIM_TIME_SLOW 150
#define WALKING_ANIM_TIME_FAST 200
#define HATCHING_ANIM_TIME_FAST 40
#define WEIRD_SOUND 400
typedef enum {
waiting_reason_step,
waiting_reason_p1,
waiting_reason_p2,
waiting_reason_cross,
/* we go from cross pupa state to m1 pupa state through 'step',
* not through a separate animation target */
waiting_reason_m1,
waiting_reason_zero_pupa,
waiting_reason_postmortum,
} waiting_reason_t;
struct Snake {
bool is_space_pressed;
bool is_shift_pressed;
game_screen_t game_screen;
bool have_game;
/* from 0 to PLAYABLE_MAPS_COUNT - 1 */
int selected_map_index;
/* from 1 to SNAKE_GAMEMODE_MAX_APPLES, default is
* SNAKE_GAMEMODE_DEFAULT_APPLES */
int gamemode_apples_count;
RandomEngine r_eng;
tile_t world[WORLD_WIDTH][WORLD_HEIGHT];
uint8_t ghost_apples[WORLD_WIDTH][WORLD_HEIGHT];
ivec2 snake_head;
ivec2 snake_tail;
snake_direction_t cur_snake_direction;
snake_direction_t new_snake_direction;
/* If we are about to make a step into a wall, we start dying,
* we enter into dying mode, where we keep making steps, but the head
* just disappears after walking, moving back.
* When the pupa disappears this way, we enter 'awaiting postmortum' state,
* where nothing moves. If we are about to hit ourselves, we don't even
* move, we immediately draw one small splash and enter 'awaiting
* postmortum' state. There is no winning state, you just raise your score
* until you are dead.
*/
bool is_dying;
int score;
/* from -1 to PUDDLE_SPRITES - 1 (where -1 means there is no puddle) */
int puddle_sprite;
ivec2 puddle_center;
/* This stuff regulates game flow and animation progress */
waiting_reason_t waiting_reason;
uint32_t waiting_time; // Measures how much time we already accumulated
int animation_speed_boost;
} snake = {1};
void init_snake() {
snake.is_space_pressed = false;
snake.is_shift_pressed = false;
snake.game_screen = game_screen_pause;
snake.have_game = false;
snake.selected_map_index = 6;
snake.gamemode_apples_count = SNAKE_GAMEMODE_DEFAULT_APPLES;
snake.r_eng = (RandomEngine){.state = time_ms()};
/* snake_head and snake_pupa and snake.world
* and other gameplay-related variables will be initialized during
* map loading */
}
bool place_random_apple() {
for (int t = 0; t < 50; t++) {
uint32_t i = RandomEngine_rnd_interval(&snake.r_eng, 0,
WORLD_WIDTH * WORLD_HEIGHT);
if ((&snake.world[0][0])[i] == tile_empty) {
(&snake.world[0][0])[i] = tile_apple;
return true;
}
}
/* I had enough */
for (uint32_t i = 0; i < WORLD_WIDTH * WORLD_HEIGHT; i++) {
if ((&snake.world[0][0])[i] == tile_empty) {
(&snake.world[0][0])[i] = tile_apple;
return true;
}
}
return false;
}
void start_snake_game() {
snake.have_game = true;
snake.game_screen = game_screen_gaming;
const MapConfig *map_config = &map_list.maps[snake.selected_map_index];
memcpy(&snake.world[0][0], &map_config->map[0][0],
sizeof(tile_t) * WORLD_WIDTH * WORLD_HEIGHT);
memset(&snake.ghost_apples, 0, sizeof(snake.ghost_apples));
for (int tx = SNAKE_START_HEAD_X; tx < SNAKE_START_PUPA_X; tx++) {
snake.world[tx][SNAKE_START_Y] = tile_snake_l;
}
snake.world[SNAKE_START_PUPA_X][SNAKE_START_Y] = tile_pupa_0_l;
snake.snake_head = (ivec2){SNAKE_START_HEAD_X, SNAKE_START_Y};
snake.snake_tail = (ivec2){SNAKE_START_PUPA_X, SNAKE_START_Y};
for (int app = 0; app < snake.gamemode_apples_count; app++) {
place_random_apple();
}
snake.new_snake_direction = snake.cur_snake_direction =
snake_direction_left;
snake.is_dying = false;
snake.score = 0;
snake.waiting_reason = waiting_reason_step;
snake.waiting_time = 0;
snake.animation_speed_boost = 0;
snake.puddle_sprite = -1;
}
void set_tile(ivec2 pos, tile_t val) {
check(0 <= pos.x && pos.x < WORLD_WIDTH && 0 <= pos.y &&
pos.y < WORLD_HEIGHT);
snake.world[pos.x][pos.y] = val;
}
tile_t get_tile(ivec2 pos) {
check(0 <= pos.x && pos.x < WORLD_WIDTH && 0 <= pos.y &&
pos.y < WORLD_HEIGHT);
return snake.world[pos.x][pos.y];
}
snake_direction_t get_opposite_direction(snake_direction_t x) {
if (x == snake_direction_left) {
return snake_direction_right;
} else if (x == snake_direction_right) {
return snake_direction_left;
} else if (x == snake_direction_top) {
return snake_direction_bottom;
} else {
return snake_direction_top;
}
}
ivec2 world_walk_direction(ivec2 v, snake_direction_t dir) {
if (dir == snake_direction_left) {
return (ivec2){v.x == 0 ? WORLD_WIDTH - 1 : v.x - 1, v.y};
} else if (dir == snake_direction_right) {
return (ivec2){v.x + 1 == WORLD_WIDTH ? 0 : v.x + 1, v.y};
} else if (dir == snake_direction_top) {
return (ivec2){v.x, v.y == 0 ? WORLD_HEIGHT - 1 : v.y - 1};
} else {
return (ivec2){v.x, v.y + 1 == WORLD_HEIGHT ? 0 : v.y + 1};
}
}
ivec2 get_puddle_center_for_contact(ivec2 head, snake_direction_t dir) {
ivec2 tile_pos = (ivec2){TILE_WIDTH * head.x, TILE_WIDTH * head.y};
if (dir == snake_direction_left || dir == snake_direction_right) {
tile_pos.y += TILE_WIDTH / 2;
} else if (dir == snake_direction_bottom) {
tile_pos.y += TILE_WIDTH;
}
if (dir == snake_direction_top || dir == snake_direction_bottom) {
tile_pos.x += TILE_WIDTH / 2;
} else if (dir == snake_direction_right) {
tile_pos.x += TILE_WIDTH;
}
return tile_pos;
}
/* takes snake tile, returns cardinal direction of snake origin */
snake_direction_t get_snake_origin_dir(tile_t tile) {
switch (tile) {
case tile_snake_l:
return snake_direction_right;
case tile_snake_r:
return snake_direction_left;
case tile_snake_t:
return snake_direction_bottom;
case tile_snake_b:
return snake_direction_top;
case tile_snake_bl:
return snake_direction_bottom;
case tile_snake_br:
return snake_direction_bottom;
case tile_snake_lb:
return snake_direction_left;
case tile_snake_rb:
return snake_direction_right;
case tile_snake_tl:
return snake_direction_top;
case tile_snake_tr:
return snake_direction_top;
case tile_snake_lt:
return snake_direction_left;
case tile_snake_rt:
return snake_direction_right;
default:
check(false);
}
}
tile_t construct_snake_tile(snake_direction_t from, snake_direction_t to) {
check(from != to);
if (from == snake_direction_left) {
if (to == snake_direction_right)
return tile_snake_r;
if (to == snake_direction_top)
return tile_snake_lt;
/* to bottom */
return tile_snake_lb;
}
if (from == snake_direction_right) {
if (to == snake_direction_left)
return tile_snake_l;
if (to == snake_direction_top)
return tile_snake_rt;
/* to bottom */
return tile_snake_rb;
}
if (from == snake_direction_top) {
if (to == snake_direction_left)
return tile_snake_tl;
if (to == snake_direction_right)
return tile_snake_tr;
/* to bottom */
return tile_snake_b;
}
if (from == snake_direction_bottom) {
if (to == snake_direction_left)
return tile_snake_bl;
if (to == snake_direction_right)
return tile_snake_br;
/* to top */
return tile_snake_t;
}
check(false);
}
tile_t construct_straight_snake_tile(snake_direction_t to) {
return construct_snake_tile(get_opposite_direction(to), to);
}
tile_t construct_zero_pupa(snake_direction_t to) {
if (to == snake_direction_left)
return tile_pupa_0_l;
if (to == snake_direction_right)
return tile_pupa_0_r;
if (to == snake_direction_top)
return tile_pupa_0_t;
if (to == snake_direction_bottom)
return tile_pupa_0_b;
check(false);
}
/* takes snake tile, returns it's target direction*/
snake_direction_t get_snake_destination(tile_t tile) {
switch (tile) {
case tile_snake_l:
return snake_direction_left;
case tile_snake_r:
return snake_direction_right;
case tile_snake_t:
return snake_direction_top;
case tile_snake_b:
return snake_direction_bottom;
case tile_snake_bl:
return snake_direction_left;
case tile_snake_br:
return snake_direction_right;
case tile_snake_lb:
return snake_direction_bottom;
case tile_snake_rb:
return snake_direction_bottom;
case tile_snake_tl:
return snake_direction_left;
case tile_snake_tr:
return snake_direction_right;
case tile_snake_lt:
return snake_direction_top;
case tile_snake_rt:
return snake_direction_top;
default:
check(false);
}
}
snake_direction_t get_zero_pupa_destination(tile_t tile) {
if (tile == tile_pupa_0_r)
return snake_direction_right;
if (tile == tile_pupa_0_l)
return snake_direction_left;
if (tile == tile_pupa_0_t)
return snake_direction_top;
if (tile == tile_pupa_0_b)
return snake_direction_bottom;
check(false);
}
bool is_tile_pupa(tile_t tile) {
if (tile == tile_pupa_0_r || tile == tile_pupa_0_l ||
tile == tile_pupa_0_b || tile == tile_pupa_0_t) {
return true;
}
return false;
}
void clear_frame(uint8_t color) {
for (uint32_t i = 0; i < FRAME_SIZE; i++) {
frame[i] = color;
}
}
void put_pixel(int x, int y, uint8_t color) {
if (x < 0 || x >= VGA_GRAPHICS_WIDTH || y < 0 || y >= VGA_GRAPHICS_HEIGHT) {
return;
}
frame[y * VGA_GRAPHICS_WIDTH + x] = color;
}
void draw_sprite(int dst_x, int dst_y, int sprite_width, int sprite_height,
const uint8_t *sprite) {
for (int x = 0; x < sprite_width; x++) {
for (int y = 0; y < sprite_height; y++) {
uint8_t color = sprite[x * sprite_height + y];
if (color != TRANSPARENCY_COLOR) {
put_pixel(dst_x + x, dst_y + y, color);
}
}
}
}
bool font_index_for_char(char ch, int *out_index) {
if (ch == ' ') {
*out_index = FONT_SPACE_INDEX;
return true;
}
if (ch >= '0' && ch <= '9') {
*out_index = FONT_DIGIT_BASE + (ch - '0');
return true;
}
if (ch >= 'A' && ch <= 'Z') {
*out_index = FONT_LETTER_BASE + (ch - 'A');
return true;
}
return false;
}
void draw_hud(const char *text) {
// int lines_count = 1;
int y0 = HUD_OFFSET;
for (const char *line_start = text; *line_start;) {
int glyph_count = 0;
const char *p = line_start;
for (; *p != '\n' && *p != 0; p++) {
glyph_count++;
}
if (glyph_count > 0) {
const int text_width =
glyph_count * FONT_WIDTH + (glyph_count - 1) * GLYPH_SPACING;
int x0 = VGA_GRAPHICS_WIDTH - HUD_OFFSET - text_width;
for (const char *q = line_start; q != p; q++) {
int glyph_index;
check(font_index_for_char(*q, &glyph_index));
draw_sprite(x0, y0, FONT_WIDTH, FONT_HEIGHT,
&sprite_data.font[glyph_index].tex[0][0]);
x0 += FONT_WIDTH + GLYPH_SPACING;
}
}
if (*p == 0) {
break;
}
line_start = p + 1;
y0 += FONT_HEIGHT + GLYPH_SPACING;
}
}
/* You see, we never store pupa hatching stage in world array.
* Instead, we store pupa 0 in world matrix and deduce the correct tile
* in draw_game_world using snake.waiting_reason
*/
tile_t correct_game_tile(tile_t x) {
/* waiting for p2 => we are in p1 */
if (snake.waiting_reason == waiting_reason_p2) {
if (x == tile_pupa_0_l)
return tile_pupa_p1_l;
if (x == tile_pupa_0_r)
return tile_pupa_p1_r;
if (x == tile_pupa_0_t)
return tile_pupa_p1_t;
if (x == tile_pupa_0_b)
return tile_pupa_p1_b;
}
/* waiting for cross => we are in p2 */
if (snake.waiting_reason == waiting_reason_cross) {
if (x == tile_pupa_0_l)
return tile_pupa_p2_l;
if (x == tile_pupa_0_r)
return tile_pupa_p2_r;
if (x == tile_pupa_0_t)
return tile_pupa_p2_t;
if (x == tile_pupa_0_b)
return tile_pupa_p2_b;
}
/* waiting for m1 => we are in m2 */
if (snake.waiting_reason == waiting_reason_m1) {
if (x == tile_pupa_0_l)
return tile_pupa_m2_l;
if (x == tile_pupa_0_r)
return tile_pupa_m2_r;
if (x == tile_pupa_0_t)
return tile_pupa_m2_t;
if (x == tile_pupa_0_b)
return tile_pupa_m2_b;
}
/* waiting for zero_pupa => we are in m1 */
if (snake.waiting_reason == waiting_reason_zero_pupa) {
if (x == tile_pupa_0_l)
return tile_pupa_m1_l;
if (x == tile_pupa_0_r)
return tile_pupa_m1_r;
if (x == tile_pupa_0_t)
return tile_pupa_m1_t;
if (x == tile_pupa_0_b)
return tile_pupa_m1_b;
}
/* When waiting postmortum, step or p1, pupa looks exactly the same
* (default is zero pupa) */
return x;
}
void draw_game_world() {
for (int tx = 0; tx < WORLD_WIDTH; tx++) {
for (int ty = 0; ty <= WORLD_HEIGHT; ty++) {
int iy = ty < WORLD_HEIGHT ? ty : 0;
tile_t tt = correct_game_tile(snake.world[tx][iy]);
if (tt == tile_empty)
continue;
check((uint8_t)tt < TILE_SPRITES);
draw_sprite(TILE_WIDTH * tx, TILE_WIDTH * ty - TILE_HEIGHT_OFFSET,
TILE_WIDTH, TILE_HEIGHT,
&sprite_data.tile[tt].tex[0][0]);
}
}
if (snake.game_screen != game_screen_gaming) {
for (uint32_t pix = 0; pix < FRAME_SIZE; pix++) {
frame[pix] = vga_to_gray(frame[pix]);
}
}
}
void draw_game_text() {
StringBuilder hud = {0};
if (snake.game_screen == game_screen_gaming) {
if (snake.waiting_reason == waiting_reason_postmortum) {
StringBuilder_append_cstr(&hud, "YOU DIED ");
} else if (snake.is_dying) {
StringBuilder_append_cstr(&hud, "LOL ");
}
// StringBuilder_append_u32(&hud, snake.animation_speed_boost);
// StringBuilder_append_cstr(&hud, " ");
StringBuilder_append_u32(&hud, snake.score);
} else if (snake.game_screen == game_screen_pause) {
if (snake.have_game) {
StringBuilder_append_cstr(&hud, "PAUSED\n");
}
StringBuilder_append_cstr(&hud, "PRESS 1 FOR NEW GAME\n");
if (snake.have_game) {
StringBuilder_append_cstr(&hud, "PRESS ESC TO CONTINUE\n");
}
StringBuilder_append_cstr(&hud, "PRESS Q TO QUIT\n");
} else if (snake.game_screen == game_screen_select_map) {
StringBuilder_append_cstr(&hud, "SELECT LEVEL\nCURRENTLY SELECTED\n");
check(snake.selected_map_index < PLAYABLE_MAPS_COUNT);
StringBuilder_append_cstr(&hud,
map_list.maps[snake.selected_map_index].name);
} else if (snake.game_screen == game_screen_select_apple_count) {
StringBuilder_append_cstr(&hud,
"SELECT DIFFICULTY\n"
"HOW MANY APPLES\nCURRENTLY SELECTED\n");
StringBuilder_append_u32(&hud, snake.gamemode_apples_count);
}
draw_hud(StringBuilder_get_cstr(&hud));
}
void draw_game_puddles() {
if (snake.puddle_sprite >= 0) {
const PuddleSprite *sprite = &sprite_data.puddle[snake.puddle_sprite];
draw_sprite(snake.puddle_center.x - PUDDLE_WIDTH / 2,
snake.puddle_center.y - PUDDLE_WIDTH / 2, PUDDLE_WIDTH,
PUDDLE_WIDTH, (&sprite->tex[0][0]));
}
}
void draw_frame() {
clear_frame(226);
if (snake.have_game) {
draw_game_puddles();
draw_game_world();
}
draw_game_text();
}
/* Called when game_screen is game_screen_gaming */
void handle_gaming_keycode(uint8_t keycode) {
if (keycode == KEYCODE_ESCAPE) {
snake.game_screen = game_screen_pause;
}
if (!snake.is_dying) {
if (is_keycode_for_press_left(keycode) &&
snake.cur_snake_direction != snake_direction_right) {
snake.new_snake_direction = snake_direction_left;
} else if (is_keycode_for_press_right(keycode) &&
snake.cur_snake_direction != snake_direction_left) {
snake.new_snake_direction = snake_direction_right;
} else if (is_keycode_for_press_up(keycode) &&
snake.cur_snake_direction != snake_direction_bottom) {
snake.new_snake_direction = snake_direction_top;
} else if (is_keycode_for_press_down(keycode) &&
snake.cur_snake_direction != snake_direction_top) {
snake.new_snake_direction = snake_direction_bottom;
}
}
}
/* return 1 if we are quitting */
int handle_incoming_keycode_after_halt(uint8_t keycode) {
if (keycode == KEYCODE_SPACE) {
snake.is_space_pressed = true;
} else if (keycode == (KEYCODE_SPACE | 0x80)) {
snake.is_space_pressed = false;
}
if (keycode == KEYCODE_SHIFT) {
snake.is_shift_pressed = true;
} else if (keycode == (KEYCODE_SHIFT | 0x80)) {
snake.is_shift_pressed = false;
}
if (keycode == KEYCODE_Q && snake.game_screen != game_screen_gaming) {
return 1;
}
if (snake.game_screen == game_screen_gaming) {
handle_gaming_keycode(keycode);
} else if (snake.game_screen == game_screen_pause) {
if (keycode == KEYCODE_1 ||
(keycode == KEYCODE_ENTER && !snake.have_game)) {
snake.game_screen = game_screen_select_map;
} else if (keycode == KEYCODE_ESCAPE && snake.have_game) {
snake.game_screen = game_screen_gaming;
}
} else if (snake.game_screen == game_screen_select_map) {
if (keycode == KEYCODE_ESCAPE) {
snake.game_screen = game_screen_pause;
} else if (is_keycode_for_press_left(keycode)) {
snake.selected_map_index--;
if (snake.selected_map_index < 0)
snake.selected_map_index = PLAYABLE_MAPS_COUNT - 1;
} else if (is_keycode_for_press_right(keycode)) {
snake.selected_map_index++;
if (snake.selected_map_index >= PLAYABLE_MAPS_COUNT)
snake.selected_map_index = 0;
} else if (keycode == KEYCODE_ENTER) {
snake.game_screen = game_screen_select_apple_count;
}
} else if (snake.game_screen == game_screen_select_apple_count) {
if (keycode == KEYCODE_ESCAPE) {
snake.game_screen = game_screen_select_map;
} else if (is_keycode_for_press_left(keycode)) {
if (snake.gamemode_apples_count > 1)
snake.gamemode_apples_count--;
} else if (is_keycode_for_press_right(keycode)) {
if (snake.gamemode_apples_count < SNAKE_GAMEMODE_MAX_APPLES)
snake.gamemode_apples_count++;
} else if (keycode == KEYCODE_ENTER) {
start_snake_game();
}
}
return 0;
}
/* Time flows only in gaming mode. This function can call frame drawing */
void handle_time_difference(uint32_t time_diff) {
if (snake.game_screen != game_screen_gaming)
return;
if (snake.waiting_reason == waiting_reason_postmortum)
return;
snake.waiting_time += time_diff;
uint32_t target_required_time;
if (snake.animation_speed_boost == 1) {
if (snake.waiting_reason == waiting_reason_step) {
target_required_time = WALKING_ANIM_TIME_FAST;
} else {
target_required_time = HATCHING_ANIM_TIME_FAST;
}
} else if (snake.animation_speed_boost == 0) {
if (snake.waiting_reason == waiting_reason_step) {
target_required_time = WALKING_ANIM_TIME_SLOW;
} else {
target_required_time = HATCHING_ANIM_TIME_SLOW;
}
} else {
if (snake.waiting_reason == waiting_reason_step) {
target_required_time = WALKING_ANIM_TIME_VERY_SLOW;
} else {
target_required_time = HATCHING_ANIM_TIME_VERY_SLOW;
}
}
if (snake.waiting_time > target_required_time) {
snake.waiting_time -= target_required_time;
if (snake.waiting_reason == waiting_reason_step) {
ivec2 tile_pos_ahead = world_walk_direction(
snake.snake_head, snake.new_snake_direction);
tile_t tile_ahead = get_tile(tile_pos_ahead);
/* Processing collisions */
if (tile_ahead == tile_apple) {
snake.ghost_apples[tile_pos_ahead.x][tile_pos_ahead.y] = 1;
snake.score += 5;
} else if (18 <= tile_ahead && tile_ahead <= 49) {
snake.waiting_reason = waiting_reason_postmortum;
snake.puddle_center = get_puddle_center_for_contact(
snake.snake_head, snake.new_snake_direction);
snake.puddle_sprite = 0;
return;
} else if (2 <= tile_ahead && tile_ahead <= 17) {
snake.is_dying = true;
snake.puddle_center = get_puddle_center_for_contact(
snake.snake_head, snake.new_snake_direction);
if (snake.puddle_sprite + 1 < PUDDLE_SPRITES) {
snake.puddle_sprite++;
}
}
/* normal stuff */
/* Step 1 */
ivec2 old_head_pos = snake.snake_head;
if (tile_ahead == tile_empty || tile_ahead == tile_apple) {
set_tile(tile_pos_ahead, construct_straight_snake_tile(
snake.new_snake_direction));
snake.snake_head = tile_pos_ahead;
}
/* Step 2 */
set_tile(old_head_pos,
construct_snake_tile(
get_opposite_direction(snake.cur_snake_direction),
snake.new_snake_direction));
snake.cur_snake_direction = snake.new_snake_direction;
/* Steps 3... */
if (is_tile_pupa(get_tile(snake.snake_tail))) {
/* Step 3a */
snake_direction_t pupa_dir =
get_zero_pupa_destination(get_tile(snake.snake_tail));
ivec2 penultimate_pos =
world_walk_direction(snake.snake_tail, pupa_dir);
set_tile(snake.snake_tail, tile_empty);
/* Step 4a */
snake_direction_t penultimate_part_dir =
get_snake_destination(get_tile(penultimate_pos));
set_tile(penultimate_pos,
construct_zero_pupa(penultimate_part_dir));
snake.snake_tail = penultimate_pos;
/* WARNING: we updated snake_tail by moving pupa forward, we
* WARNING: should check if we moved onto a ghost apple */
// check, this is important. This is the only place where we
// move pupa to a new pos we check for ghost apples. Clear ghost
// apples and enter p1-waiting state (instead of remaining in
// step awaiting state)
if (snake.snake_tail.x == snake.snake_head.x &&
snake.snake_tail.y == snake.snake_head.y) {
snake.waiting_reason = waiting_reason_postmortum;
return;
}
if (snake.ghost_apples[penultimate_pos.x][penultimate_pos.y]) {
snake.ghost_apples[penultimate_pos.x][penultimate_pos.y] =
0;
syscall(SYS_set_beep, WEIRD_SOUND);
snake.waiting_reason = waiting_reason_p1;
/* Nothing changed on screen because we need to wait even
* for the first stage of hatching.
* We could go to p1 immediately, though */
}
} else {
snake_direction_t tail_dir =
get_snake_destination(get_tile(snake.snake_tail));
set_tile(snake.snake_tail, construct_zero_pupa(tail_dir));
syscall(SYS_set_beep, WEIRD_SOUND);
snake.waiting_reason = waiting_reason_m1;
}
/* If we went with route 3a, pupa has moved and we definitely can
* place an apple. But if it had not and we went through route 3b,
* well, okay, we just won't have a free space for apple */
if (tile_ahead == tile_apple) {
place_random_apple();
}
} /* But there are more animation
targets that can finish, other than 'step' */
else if (snake.waiting_reason == waiting_reason_p1) {
snake.waiting_reason = waiting_reason_p2;
} else if (snake.waiting_reason == waiting_reason_p2) {
snake.waiting_reason = waiting_reason_cross;
} else if (snake.waiting_reason == waiting_reason_cross) {
/* Finished pupa hatching */
snake_direction_t tail_dir =
get_zero_pupa_destination(get_tile(snake.snake_tail));
set_tile(snake.snake_tail, construct_straight_snake_tile(tail_dir));
syscall(SYS_set_beep, 0);
snake.waiting_reason = waiting_reason_step;
} else if (snake.waiting_reason == waiting_reason_m1) {
snake.waiting_reason = waiting_reason_zero_pupa;
} else if (snake.waiting_reason == waiting_reason_zero_pupa) {
/* Finished pupa regrowing */
syscall(SYS_set_beep, 0);
snake.waiting_reason = waiting_reason_step;
}
}
}
void after_awake_action(uint32_t time_diff) {
handle_time_difference(time_diff);
snake.animation_speed_boost = 0;
draw_frame();
syscall(SYS_swap_frame, (uintptr_t)frame);
}
int main(void) {
init_snake();
syscall(SYS_switch_to_graphics, 0);
uint32_t prev_time = time_ms();
while (1) {
/* Returned from halt, we can enjoy keyboard input and animation
* progress*/
for (int ch; (ch = syscall(SYS_getc, 0)) >= 0;) {
int ret = handle_incoming_keycode_after_halt((uint8_t)ch);
if (ret == 1)
goto quit;
}
/* As an additional benefit, we can check if is_space_pressed here */
if (snake.is_space_pressed) {
snake.animation_speed_boost = 1;
} else if (snake.is_shift_pressed) {
snake.animation_speed_boost = -1;
}
/* snake.is_time_sped_up will be relevant when checking animation end */
uint32_t current_time = time_ms();
after_awake_action(current_time - prev_time);
prev_time = current_time;
syscall(SYS_halt, 0);
}
quit:
syscall(SYS_switch_to_text, 0);
syscall(SYS_puts, (uintptr_t)"Quit from game\n");
return 0;
}

2024
snake/sprite_data.h Normal file

File diff suppressed because it is too large Load Diff

37
snake/sprites.h Normal file
View File

@ -0,0 +1,37 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#define TRANSPARENCY_COLOR 47
#define TILE_WIDTH 20
#define TILE_HEIGHT 32
#define TILE_HEIGHT_OFFSET 12
typedef struct {
uint8_t tex[TILE_WIDTH][TILE_HEIGHT];
} TileSprite;
#define FONT_WIDTH 12
#define FONT_HEIGHT 21
typedef struct {
uint8_t tex[FONT_WIDTH][FONT_HEIGHT];
} FontSprite;
#define PUDDLE_WIDTH 40
typedef struct {
uint8_t tex[PUDDLE_WIDTH][PUDDLE_WIDTH];
} PuddleSprite;
#define TILE_SPRITES 50
#define FONT_SPRITES (1 + 10 + 26)
#define PUDDLE_SPRITES 5
typedef struct {
TileSprite tile[TILE_SPRITES];
FontSprite font[FONT_SPRITES];
PuddleSprite puddle[PUDDLE_SPRITES];
} SpriteData;

View File

@ -3,11 +3,18 @@
#include <stdint.h>
enum {
T_SYSCALL = 0x84,
SYS_exit = 0,
SYS_greet = 1,
SYS_putc = 2,
SYS_puts = 3,
T_SYSCALL = 0x84,
SYS_exit = 0,
SYS_greet = 1,
SYS_putc = 2,
SYS_puts = 3,
SYS_switch_to_text = 4,
SYS_switch_to_graphics = 5,
SYS_swap_frame = 6,
SYS_time_ms = 7,
SYS_halt = 8,
SYS_getc = 9,
SYS_set_beep = 10,
};
int syscall(int call, uintptr_t arg);

View File

@ -1,19 +1,19 @@
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
enum {
MBR_END = 510,
};
int main(int argc, char* argv[]) {
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "Usage: %s mbr.bin fs.img\n", argv[0]);
return 1;
}
const char* filename = argv[1];
const char* fsimg_path = argv[2];
const char *filename = argv[1];
const char *fsimg_path = argv[2];
int fd = open(filename, O_RDWR);
off_t length = lseek(fd, 0, SEEK_END);
if (length > MBR_END) {

View File

@ -3,15 +3,15 @@
#include <stdio.h>
#include <string.h>
char* basename(char* path) {
char* c = strrchr(path, '/');
char *basename(char *path) {
char *c = strrchr(path, '/');
if (c && *c) {
return c + 1;
}
return path;
}
int main(int argc, char* argv[]) {
int main(int argc, char *argv[]) {
char sector[sector_size];
struct dir dir = {{0}};
@ -20,7 +20,7 @@ int main(int argc, char* argv[]) {
return 1;
}
FILE* image = fopen(argv[1], "wb");
FILE *image = fopen(argv[1], "wb");
if (!image) {
perror(argv[1]);
return 1;
@ -33,12 +33,12 @@ int main(int argc, char* argv[]) {
uint32_t sector_offset = 1;
for (int i = 2; i < argc; ++i) {
char* name = argv[i];
struct dirent *dirent = &dir.entries[i-2];
char *name = argv[i];
struct dirent *dirent = &dir.entries[i - 2];
dirent->offset_sectors = sector_offset;
dirent->size_bytes = 0;
FILE* file = fopen(name, "rb");
FILE *file = fopen(name, "rb");
if (!file) {
perror(name);
return 1;

View File

@ -2,7 +2,7 @@
#include <stdint.h>
int main() {
const char* message = "I hope the kernel does not panic...\n";
const char *message = "I hope the kernel does not panic...\n";
syscall(SYS_puts, (uint32_t)message);
syscall(SYS_puts, 0x1bad1dea);
return 0;

View File

@ -3,16 +3,13 @@
int main();
int syscall(int call, uintptr_t arg) {
asm volatile("int $0x84": "+a"(call) : "b"(arg));
asm volatile("int $0x84" : "+a"(call) : "b"(arg));
return call;
}
_Noreturn
void _exit(int exit_status) {
_Noreturn void _exit(int exit_status) {
syscall(SYS_exit, exit_status);
__builtin_unreachable();
}
void _start() {
_exit(main());
}
void _start() { _exit(main()); }

View File

@ -1,3 +1 @@
int main() {
return 1;
}
int main() { return 1; }

18
user/getc.c Normal file
View File

@ -0,0 +1,18 @@
#include "../snake/misc_utils.h"
#include "../syscall.h"
#include <stdint.h>
int main(void) {
while (1) {
int keycode = syscall(SYS_getc, 0);
if (keycode == -1) {
syscall(SYS_halt, 0);
continue;
}
StringBuilder sb = {0};
StringBuilder_append_u32(&sb, (uint32_t)keycode);
StringBuilder_putc(&sb, '\n');
syscall(SYS_puts, (uintptr_t)StringBuilder_get_cstr(&sb));
}
}

22
user/player.c Normal file
View File

@ -0,0 +1,22 @@
#include "../syscall.h"
#include <stdint.h>
static uint32_t time_ms(void) { return (uint32_t)syscall(SYS_time_ms, 0); }
static void wait_ms(uint32_t duration_ms) {
uint32_t start = time_ms();
while ((uint32_t)(time_ms() - start) < duration_ms) {
syscall(SYS_halt, 0);
}
}
int main(void) {
syscall(SYS_set_beep, 10000);
wait_ms(1000);
syscall(SYS_set_beep, 15000);
wait_ms(1000);
// syscall(SYS_set_beep, 0);
return 0;
}

View File

@ -1,7 +1,7 @@
#include "../syscall.h"
#include <stdint.h>
void userspace_puts(const char* s) {
void userspace_puts(const char *s) {
char c;
while ((c = *s++)) {
syscall(SYS_putc, c);
@ -9,9 +9,9 @@ void userspace_puts(const char* s) {
}
int main() {
const char* spell = "cra cra trif traf not sgnieflet\n";
const char* spell2 = "Pam pam pam pam parapapapapam\n";
const char* spell3 = "Zhopu podotri\n";
const char *spell = "cra cra trif traf not sgnieflet\n";
const char *spell2 = "Pam pam pam pam parapapapapam\n";
const char *spell3 = "Zhopu podotri\n";
// userspace_puts(spell);
syscall(SYS_puts, (uintptr_t)spell);
syscall(SYS_puts, (uint32_t)spell2);