commit 862e24969c946dba32ef348db68095ace7c204a5 Author: jdah <20308439+jdah@users.noreply.github.com> Date: Mon Mar 15 14:19:55 2021 +0100 Literally everything post-sound, pre-music Everything diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3345747 --- /dev/null +++ b/.gitignore @@ -0,0 +1,59 @@ +bin/ +*.iso + +*.icloud +.DS_Store +.vscode + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..af179a4 --- /dev/null +++ b/Makefile @@ -0,0 +1,52 @@ +CC=i386-elf-gcc +ASM=i386-elf-as +LD=i386-elf-ld + +GFLAGS= +CCFLAGS=-m32 -std=c11 -O2 -g -Wall -Wextra -Wpedantic -Wstrict-aliasing +CCFLAGS+=-Wno-pointer-arith -Wno-unused-parameter +CCFLAGS+=-nostdlib -nostdinc -ffreestanding -fno-pie -fno-stack-protector +CCFLAGS+=-fno-builtin-function -fno-builtin +ASFLAGS= +LDFLAGS= + +BOOTSECT_SRCS=\ + src/stage0.S + +BOOTSECT_OBJS=$(BOOTSECT_SRCS:.S=.o) + +KERNEL_C_SRCS=$(wildcard src/*.c) +KERNEL_S_SRCS=$(filter-out $(BOOTSECT_SRCS), $(wildcard src/*.S)) +KERNEL_OBJS=$(KERNEL_C_SRCS:.c=.o) $(KERNEL_S_SRCS:.S=.o) + +BOOTSECT=bootsect.bin +KERNEL=kernel.bin +ISO=boot.iso + +all: dirs bootsect kernel + +dirs: + mkdir -p bin + +clean: + rm ./**/*.o + rm ./*.iso + rm ./**/*.elf + rm ./**/*.bin + +%.o: %.c + $(CC) -o $@ -c $< $(GFLAGS) $(CCFLAGS) + +%.o: %.S + $(ASM) -o $@ -c $< $(GFLAGS) $(ASFLAGS) + +bootsect: $(BOOTSECT_OBJS) + $(LD) -o ./bin/$(BOOTSECT) $^ -Ttext 0x7C00 --oformat=binary + +kernel: $(KERNEL_OBJS) + $(LD) -o ./bin/$(KERNEL) $^ $(LDFLAGS) -Tsrc/link.ld + +iso: bootsect kernel + dd if=/dev/zero of=boot.iso bs=512 count=2880 + dd if=./bin/$(BOOTSECT) of=boot.iso conv=notrunc bs=512 seek=0 count=1 + dd if=./bin/$(KERNEL) of=boot.iso conv=notrunc bs=512 seek=1 count=2048 diff --git a/bin/bootsect.bin b/bin/bootsect.bin new file mode 100755 index 0000000..afe7831 Binary files /dev/null and b/bin/bootsect.bin differ diff --git a/bin/kernel.bin b/bin/kernel.bin new file mode 100755 index 0000000..8a97426 Binary files /dev/null and b/bin/kernel.bin differ diff --git a/boot.iso b/boot.iso new file mode 100644 index 0000000..0b92f22 Binary files /dev/null and b/boot.iso differ diff --git a/src/font.c b/src/font.c new file mode 100644 index 0000000..f276499 --- /dev/null +++ b/src/font.c @@ -0,0 +1,158 @@ +#include "font.h" +#include "screen.h" +#include "system.h" + +// 8x8 font for ASCII 0..127 +static const u8 FONT[128][8] = { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0000 (nul) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0001 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0002 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0003 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0004 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0005 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0006 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0007 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0008 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0009 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000A + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000B + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000C + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000D + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000E + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000F + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0010 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0011 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0012 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0013 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0014 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0015 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0016 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0017 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0018 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0019 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001A + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001B + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001C + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001D + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001E + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001F + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0020 (space) + { 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00}, // U+0021 (!) + { 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0022 (") + { 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00}, // U+0023 (#) + { 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00}, // U+0024 ($) + { 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00}, // U+0025 (%) + { 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00}, // U+0026 (&) + { 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0027 (') + { 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00}, // U+0028 (() + { 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00}, // U+0029 ()) + { 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00}, // U+002A (*) + { 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00}, // U+002B (+) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+002C (,) + { 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, // U+002D (-) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+002E (.) + { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // U+002F (/) + { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // U+0030 (0) + { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // U+0031 (1) + { 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // U+0032 (2) + { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // U+0033 (3) + { 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // U+0034 (4) + { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // U+0035 (5) + { 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // U+0036 (6) + { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // U+0037 (7) + { 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+0038 (8) + { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}, // U+0039 (9) + { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+003A (:) + { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+003B (;) + { 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00}, // U+003C (<) + { 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00}, // U+003D (=) + { 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00}, // U+003E (>) + { 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00}, // U+003F (?) + { 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00}, // U+0040 (@) + { 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // U+0041 (A) + { 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // U+0042 (B) + { 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00}, // U+0043 (C) + { 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00}, // U+0044 (D) + { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // U+0045 (E) + { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00}, // U+0046 (F) + { 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00}, // U+0047 (G) + { 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // U+0048 (H) + { 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0049 (I) + { 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00}, // U+004A (J) + { 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // U+004B (K) + { 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00}, // U+004C (L) + { 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // U+004D (M) + { 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // U+004E (N) + { 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // U+004F (O) + { 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00}, // U+0050 (P) + { 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00}, // U+0051 (Q) + { 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00}, // U+0052 (R) + { 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00}, // U+0053 (S) + { 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0054 (T) + { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // U+0055 (U) + { 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0056 (V) + { 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00}, // U+0057 (W) + { 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // U+0058 (X) + { 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // U+0059 (Y) + { 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // U+005A (Z) + { 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00}, // U+005B ([) + { 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00}, // U+005C (\) + { 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00}, // U+005D (]) + { 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00}, // U+005E (^) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, // U+005F (_) + { 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0060 (`) + { 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00}, // U+0061 (a) + { 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00}, // U+0062 (b) + { 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00}, // U+0063 (c) + { 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00}, // U+0064 (d) + { 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00}, // U+0065 (e) + { 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00}, // U+0066 (f) + { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0067 (g) + { 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00}, // U+0068 (h) + { 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0069 (i) + { 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E}, // U+006A (j) + { 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00}, // U+006B (k) + { 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+006C (l) + { 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00}, // U+006D (m) + { 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // U+006E (n) + { 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+006F (o) + { 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // U+0070 (p) + { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78}, // U+0071 (q) + { 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00}, // U+0072 (r) + { 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00}, // U+0073 (s) + { 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+0074 (t) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00}, // U+0075 (u) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0076 (v) + { 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // U+0077 (w) + { 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00}, // U+0078 (x) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0079 (y) + { 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00}, // U+007A (z) + { 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00}, // U+007B ({) + { 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, // U+007C (|) + { 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00}, // U+007D (}) + { 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007E (~) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // U+007F +}; + +void font_char(char c, size_t x, size_t y, u8 color) { + assert(c >= 0, "INVALID CHARACTER"); + + const u8 *glyph = FONT[(size_t) c]; + + for (size_t yy = 0; yy < 8; yy++) { + for (size_t xx = 0; xx < 8; xx++) { + if (glyph[yy] & (1 << xx)) { + screen_set(color, x + xx, y + yy); + } + } + } +} + +void font_str(const char *s, size_t x, size_t y, u8 color) { + char c; + + while ((c = *s++) != 0) { + font_char(c, x, y, color); + x += 8; + } +} diff --git a/src/font.h b/src/font.h new file mode 100644 index 0000000..9e695e4 --- /dev/null +++ b/src/font.h @@ -0,0 +1,21 @@ +#ifndef FONT_H +#define FONT_H + +#include "util.h" +#include "screen.h" + +#define font_width(_s) (strlen((_s)) * 8) +#define font_height() (8) +#define font_str_doubled(_s, _x, _y, _c) do {\ + const char *__s = (_s);\ + __typeof__(_x) __x = (_x);\ + __typeof__(_y) __y = (_y);\ + __typeof__(_c) __c = (_c);\ + font_str(__s, __x + 1, __y + 1, COLOR_ADD(__c, -2));\ + font_str(__s, __x, __y, __c);\ + } while (0); + +void font_char(char c, size_t x, size_t y, u8 color); +void font_str(const char *s, size_t x, size_t y, u8 color); + +#endif diff --git a/src/font.o b/src/font.o new file mode 100644 index 0000000..4832b54 Binary files /dev/null and b/src/font.o differ diff --git a/src/fpu.c b/src/fpu.c new file mode 100644 index 0000000..b743580 --- /dev/null +++ b/src/fpu.c @@ -0,0 +1,15 @@ +#include "fpu.h" + +void fpu_init() { + size_t t; + + asm("clts"); + asm("mov %%cr0, %0" : "=r"(t)); + t &= ~(1 << 2); + t |= (1 << 1); + asm("mov %0, %%cr0" :: "r"(t)); + asm("mov %%cr4, %0" : "=r"(t)); + t |= 3 << 9; + asm("mov %0, %%cr4" :: "r"(t)); + asm("fninit"); +} diff --git a/src/fpu.h b/src/fpu.h new file mode 100644 index 0000000..5b1fa7b --- /dev/null +++ b/src/fpu.h @@ -0,0 +1,8 @@ +#ifndef FPU_H +#define FPU_H + +#include "util.h" + +void fpu_init(); + +#endif diff --git a/src/fpu.o b/src/fpu.o new file mode 100644 index 0000000..409d0d2 Binary files /dev/null and b/src/fpu.o differ diff --git a/src/idt.c b/src/idt.c new file mode 100644 index 0000000..f2197ec --- /dev/null +++ b/src/idt.c @@ -0,0 +1,39 @@ +#include "idt.h" + +struct IDTEntry { + u16 offset_low; + u16 selector; + u8 __ignored; + u8 type; + u16 offset_high; +} PACKED; + +struct IDTPointer { + u16 limit; + uintptr_t base; +} PACKED; + +static struct { + struct IDTEntry entries[256]; + struct IDTPointer pointer; +} idt; + +// in start.S +extern void idt_load(); + +void idt_set(u8 index, void (*base)(struct Registers*), u16 selector, u8 flags) { + idt.entries[index] = (struct IDTEntry) { + .offset_low = ((uintptr_t) base) & 0xFFFF, + .offset_high = (((uintptr_t) base) >> 16) & 0xFFFF, + .selector = selector, + .type = flags | 0x60, + .__ignored = 0 + }; +} + +void idt_init() { + idt.pointer.limit = sizeof(idt.entries) - 1; + idt.pointer.base = (uintptr_t) &idt.entries[0]; + memset(&idt.entries[0], 0, sizeof(idt.entries)); + idt_load((uintptr_t) &idt.pointer); +} diff --git a/src/idt.h b/src/idt.h new file mode 100644 index 0000000..9f4ca2e --- /dev/null +++ b/src/idt.h @@ -0,0 +1,10 @@ +#ifndef IDT_H +#define IDT_H + +#include "util.h" +#include "isr.h" + +void idt_set(u8 index, void (*base)(struct Registers*), u16 selector, u8 flags); +void idt_init(); + +#endif diff --git a/src/idt.o b/src/idt.o new file mode 100644 index 0000000..0931653 Binary files /dev/null and b/src/idt.o differ diff --git a/src/irq.c b/src/irq.c new file mode 100644 index 0000000..4a02786 --- /dev/null +++ b/src/irq.c @@ -0,0 +1,82 @@ +#include "irq.h" +#include "idt.h" +#include "isr.h" + +// PIC constants +#define PIC1 0x20 +#define PIC1_OFFSET 0x20 +#define PIC1_DATA (PIC1 + 1) + +#define PIC2 0xA0 +#define PIC2_OFFSET 0x28 +#define PIC2_DATA (PIC2 + 1) + +#define PIC_EOI 0x20 +#define PIC_MODE_8086 0x01 +#define ICW1_ICW4 0x01 +#define ICW1_INIT 0x10 + +#define PIC_WAIT() do { \ + asm ("jmp 1f\n\t" \ + "1:\n\t" \ + " jmp 2f\n\t"\ + "2:"); \ + } while (0) + +static void (*handlers[32])(struct Registers *regs) = { 0 }; + +static void stub(struct Registers *regs) { + if (regs->int_no <= 47 && regs->int_no >= 32) { + if (handlers[regs->int_no - 32]) { + handlers[regs->int_no - 32](regs); + } + } + + // send EOI + if (regs->int_no >= 0x40) { + outportb(PIC2, PIC_EOI); + } + + outportb(PIC1, PIC_EOI); +} + +static void irq_remap() { + u8 mask1 = inportb(PIC1_DATA), mask2 = inportb(PIC2_DATA); + outportb(PIC1, ICW1_INIT | ICW1_ICW4); + outportb(PIC2, ICW1_INIT | ICW1_ICW4); + outportb(PIC1_DATA, PIC1_OFFSET); + outportb(PIC2_DATA, PIC2_OFFSET); + outportb(PIC1_DATA, 0x04); // PIC2 at IRQ2 + outportb(PIC2_DATA, 0x02); // Cascade indentity + outportb(PIC1_DATA, PIC_MODE_8086); + outportb(PIC1_DATA, PIC_MODE_8086); + outportb(PIC1_DATA, mask1); + outportb(PIC2_DATA, mask2); +} + +static void irq_set_mask(size_t i) { + u16 port = i < 8 ? PIC1_DATA : PIC2_DATA; + u8 value = inportb(port) | (1 << i); + outportb(port, value); +} + +static void irq_clear_mask(size_t i) { + u16 port = i < 8 ? PIC1_DATA : PIC2_DATA; + u8 value = inportb(port) & ~(1 << i); + outportb(port, value); +} + +void irq_install(size_t i, void (*handler)(struct Registers *)) { + CLI(); + handlers[i] = handler; + irq_clear_mask(i); + STI(); +} + +void irq_init() { + irq_remap(); + + for (size_t i = 0; i < 16; i++) { + isr_install(32 + i, stub); + } +} diff --git a/src/irq.h b/src/irq.h new file mode 100644 index 0000000..27a23b6 --- /dev/null +++ b/src/irq.h @@ -0,0 +1,10 @@ +#ifndef IRQ_H +#define IRQ_H + +#include "util.h" +#include "isr.h" + +void irq_install(size_t i, void (*handler)(struct Registers*)); +void irq_init(); + +#endif diff --git a/src/irq.o b/src/irq.o new file mode 100644 index 0000000..da1420c Binary files /dev/null and b/src/irq.o differ diff --git a/src/isr.c b/src/isr.c new file mode 100644 index 0000000..a3884aa --- /dev/null +++ b/src/isr.c @@ -0,0 +1,172 @@ +#include "isr.h" +#include "idt.h" +#include "system.h" + +#define NUM_ISRS 48 + +extern void _isr0(struct Registers*); +extern void _isr1(struct Registers*); +extern void _isr2(struct Registers*); +extern void _isr3(struct Registers*); +extern void _isr4(struct Registers*); +extern void _isr5(struct Registers*); +extern void _isr6(struct Registers*); +extern void _isr7(struct Registers*); +extern void _isr8(struct Registers*); +extern void _isr9(struct Registers*); +extern void _isr10(struct Registers*); +extern void _isr11(struct Registers*); +extern void _isr12(struct Registers*); +extern void _isr13(struct Registers*); +extern void _isr14(struct Registers*); +extern void _isr15(struct Registers*); +extern void _isr16(struct Registers*); +extern void _isr17(struct Registers*); +extern void _isr18(struct Registers*); +extern void _isr19(struct Registers*); +extern void _isr20(struct Registers*); +extern void _isr21(struct Registers*); +extern void _isr22(struct Registers*); +extern void _isr23(struct Registers*); +extern void _isr24(struct Registers*); +extern void _isr25(struct Registers*); +extern void _isr26(struct Registers*); +extern void _isr27(struct Registers*); +extern void _isr28(struct Registers*); +extern void _isr29(struct Registers*); +extern void _isr30(struct Registers*); +extern void _isr31(struct Registers*); +extern void _isr32(struct Registers*); +extern void _isr33(struct Registers*); +extern void _isr34(struct Registers*); +extern void _isr35(struct Registers*); +extern void _isr36(struct Registers*); +extern void _isr37(struct Registers*); +extern void _isr38(struct Registers*); +extern void _isr39(struct Registers*); +extern void _isr40(struct Registers*); +extern void _isr41(struct Registers*); +extern void _isr42(struct Registers*); +extern void _isr43(struct Registers*); +extern void _isr44(struct Registers*); +extern void _isr45(struct Registers*); +extern void _isr46(struct Registers*); +extern void _isr47(struct Registers*); + +static void (*stubs[NUM_ISRS])(struct Registers*) = { + _isr0, + _isr1, + _isr2, + _isr3, + _isr4, + _isr5, + _isr6, + _isr7, + _isr8, + _isr9, + _isr10, + _isr11, + _isr12, + _isr13, + _isr14, + _isr15, + _isr16, + _isr17, + _isr18, + _isr19, + _isr20, + _isr21, + _isr22, + _isr23, + _isr24, + _isr25, + _isr26, + _isr27, + _isr28, + _isr29, + _isr30, + _isr31, + _isr32, + _isr33, + _isr34, + _isr35, + _isr36, + _isr37, + _isr38, + _isr39, + _isr40, + _isr41, + _isr42, + _isr43, + _isr44, + _isr45, + _isr46, + _isr47, +}; + +static const char *exceptions[32] = { + "Divide by zero", + "Debug", + "NMI", + "Breakpoint", + "Overflow", + "OOB", + "Invalid opcode", + "No coprocessor", + "Double fault", + "Coprocessor segment overrun", + "Bad TSS", + "Segment not present", + "Stack fault", + "General protection fault", + "Page fault", + "Unrecognized interrupt", + "Coprocessor fault", + "Alignment check", + "Machine check", + "RESERVED", + "RESERVED", + "RESERVED", + "RESERVED", + "RESERVED", + "RESERVED", + "RESERVED", + "RESERVED", + "RESERVED", + "RESERVED", + "RESERVED" +}; + +static struct { + size_t index; + void (*stub)(struct Registers*); +} isrs[NUM_ISRS]; + +static void (*handlers[NUM_ISRS])(struct Registers*) = { 0 }; + +void isr_install(size_t i, void (*handler)(struct Registers*)) { + handlers[i] = handler; +} + +// referenced from start.S +void isr_handler(struct Registers *regs) { + if (handlers[regs->int_no]) { + handlers[regs->int_no](regs); + } +} + +static void exception_handler(struct Registers *regs) { + panic(exceptions[regs->int_no]); +} + +void isr_init() { + for (size_t i = 0; i < NUM_ISRS; i++) { + isrs[i].index = i; + isrs[i].stub = stubs[i]; + idt_set(isrs[i].index, isrs[i].stub, 0x08, 0x8E); + } + + for (size_t i = 0; i < 32; i++) { + isr_install(i, exception_handler); + } +} diff --git a/src/isr.h b/src/isr.h new file mode 100644 index 0000000..0507ee8 --- /dev/null +++ b/src/isr.h @@ -0,0 +1,16 @@ +#ifndef ISR_H +#define ISR_H + +#include "util.h" + +struct Registers { + u32 __ignored, fs, es, ds; + u32 edi, esi, ebp, esp, ebx, edx, ecx, eax; + u32 int_no, err_no; + u32 eip, cs, efl, useresp, ss; +}; + +void isr_install(size_t i, void (*handler)(struct Registers*)); +void isr_init(); + +#endif diff --git a/src/isr.o b/src/isr.o new file mode 100644 index 0000000..5ac8248 Binary files /dev/null and b/src/isr.o differ diff --git a/src/keyboard.c b/src/keyboard.c new file mode 100644 index 0000000..6ec3cec --- /dev/null +++ b/src/keyboard.c @@ -0,0 +1,66 @@ +#include "keyboard.h" +#include "irq.h" +#include "system.h" +#include "timer.h" + +u8 keyboard_layout_us[2][128] = { + { + KEY_NULL, KEY_ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', + '-', '=', KEY_BACKSPACE, KEY_TAB, 'q', 'w', 'e', 'r', 't', 'y', 'u', + 'i', 'o', 'p', '[', ']', KEY_ENTER, 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', + 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', + ',', '.', '/', 0, 0, 0, ' ', 0, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, + KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, 0, 0, KEY_HOME, KEY_UP, + KEY_PAGE_UP, '-', KEY_LEFT, '5', KEY_RIGHT, '+', KEY_END, KEY_DOWN, + KEY_PAGE_DOWN, KEY_INSERT, KEY_DELETE, 0, 0, 0, KEY_F11, KEY_F12 + }, { + KEY_NULL, KEY_ESC, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', + '_', '+', KEY_BACKSPACE, KEY_TAB, 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', + 'I', 'O', 'P', '{', '}', KEY_ENTER, 0, 'A', 'S', 'D', 'F', 'G', 'H', + 'J', 'K', 'L', ':', '\"', '~', 0, '|', 'Z', 'X', 'C', 'V', 'B', 'N', + 'M', '<', '>', '?', 0, 0, 0, ' ', 0, KEY_F1, KEY_F2, KEY_F3, KEY_F4, + KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, 0, 0, KEY_HOME, KEY_UP, + KEY_PAGE_UP, '-', KEY_LEFT, '5', KEY_RIGHT, '+', KEY_END, KEY_DOWN, + KEY_PAGE_DOWN, KEY_INSERT, KEY_DELETE, 0, 0, 0, KEY_F11, KEY_F12 + } +}; + +struct Keyboard keyboard; + +// bad hack! for a better RNG +static bool seeded = false; + +static void keyboard_handler(struct Registers *regs) { + u16 scancode = (u16) inportb(0x60); + + if (!seeded) { + seed(((u32) scancode) * 17 + timer_get()); + seeded = true; + } + + if (KEY_SCANCODE(scancode) == KEY_LALT || + KEY_SCANCODE(scancode) == KEY_RALT) { + keyboard.mods = BIT_SET(keyboard.mods, HIBIT(KEY_MOD_ALT), KEY_IS_PRESS(scancode)); + } else if ( + KEY_SCANCODE(scancode) == KEY_LCTRL || + KEY_SCANCODE(scancode) == KEY_RCTRL) { + keyboard.mods = BIT_SET(keyboard.mods, HIBIT(KEY_MOD_CTRL), KEY_IS_PRESS(scancode)); + } else if ( + KEY_SCANCODE(scancode) == KEY_LSHIFT || + KEY_SCANCODE(scancode) == KEY_RSHIFT) { + keyboard.mods = BIT_SET(keyboard.mods, HIBIT(KEY_MOD_SHIFT), KEY_IS_PRESS(scancode)); + } else if (KEY_SCANCODE(scancode) == KEY_CAPS_LOCK) { + keyboard.mods = BIT_SET(keyboard.mods, HIBIT(KEY_MOD_CAPS_LOCK), KEY_IS_PRESS(scancode)); + } else if (KEY_SCANCODE(scancode) == KEY_NUM_LOCK) { + keyboard.mods = BIT_SET(keyboard.mods, HIBIT(KEY_MOD_NUM_LOCK), KEY_IS_PRESS(scancode)); + } else if (KEY_SCANCODE(scancode) == KEY_SCROLL_LOCK) { + keyboard.mods = BIT_SET(keyboard.mods, HIBIT(KEY_MOD_SCROLL_LOCK), KEY_IS_PRESS(scancode)); + } + + keyboard.keys[(u8) (scancode & 0x7F)] = KEY_IS_PRESS(scancode); + keyboard.chars[KEY_CHAR(scancode)] = KEY_IS_PRESS(scancode); +} + +void keyboard_init() { + irq_install(1, keyboard_handler); +} diff --git a/src/keyboard.h b/src/keyboard.h new file mode 100644 index 0000000..2beceb2 --- /dev/null +++ b/src/keyboard.h @@ -0,0 +1,87 @@ +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#include "util.h" + +// TODO: some of this it 100% wrong lmao +#define KEY_NULL 0 +#define KEY_ESC 27 +#define KEY_BACKSPACE '\b' +#define KEY_TAB '\t' +#define KEY_ENTER '\n' +#define KEY_RETURN '\r' + +#define KEY_INSERT 0x90 +#define KEY_DELETE 0x91 +#define KEY_HOME 0x92 +#define KEY_END 0x93 +#define KEY_PAGE_UP 0x94 +#define KEY_PAGE_DOWN 0x95 +#define KEY_LEFT 0x4B +#define KEY_UP 0x48 +#define KEY_RIGHT 0x4D +#define KEY_DOWN 0x50 + +#define KEY_F1 0x80 +#define KEY_F2 (KEY_F1 + 1) +#define KEY_F3 (KEY_F1 + 2) +#define KEY_F4 (KEY_F1 + 3) +#define KEY_F5 (KEY_F1 + 4) +#define KEY_F6 (KEY_F1 + 5) +#define KEY_F7 (KEY_F1 + 6) +#define KEY_F8 (KEY_F1 + 7) +#define KEY_F9 (KEY_F1 + 8) +#define KEY_F10 (KEY_F1 + 9) +#define KEY_F11 (KEY_F1 + 10) +#define KEY_F12 (KEY_F1 + 11) + +#define KEY_LCTRL 0x1D +#define KEY_RCTRL 0x1D + +#define KEY_LALT 0x38 +#define KEY_RALT 0x38 + +#define KEY_LSHIFT 0x2A +#define KEY_RSHIFT 0x36 + +#define KEY_CAPS_LOCK 0x3A +#define KEY_SCROLL_LOCK 0x46 +#define KEY_NUM_LOCK 0x45 + +#define KEY_MOD_ALT 0x0200 +#define KEY_MOD_CTRL 0x0400 +#define KEY_MOD_SHIFT 0x0800 +#define KEY_MOD_CAPS_LOCK 0x1000 +#define KEY_MOD_NUM_LOCK 0x2000 +#define KEY_MOD_SCROLL_LOCK 0x4000 + +#define KEYBOARD_RELEASE 0x80 + +#define KEYBOARD_BUFFER_SIZE 256 + +#define KEY_IS_PRESS(_s) (!((_s) & KEYBOARD_RELEASE)) +#define KEY_IS_RELEASE(_s) (!!((_s) & KEYBOARD_RELEASE)) +#define KEY_SCANCODE(_s) ((_s) & 0x7F) +#define KEY_MOD(_s, _m) (!!((_s) & (_m))) +#define KEY_CHAR(_s) __extension__({\ + __typeof__(_s) __s = (_s);\ + KEY_SCANCODE(__s) < 128 ?\ + keyboard_layout_us[KEY_MOD(__s, KEY_MOD_SHIFT) ? 1 : 0][KEY_SCANCODE(__s)] :\ + 0;\ + }) + +struct Keyboard { + u16 mods; + bool keys[128]; + bool chars[128]; +}; + +extern u8 keyboard_layout_us[2][128]; +extern struct Keyboard keyboard; + +#define keyboard_key(_s) (keyboard.keys[(_s)]) +#define keyboard_char(_c) (keyboard.chars[(u8) (_c)]) + +void keyboard_init(); + +#endif diff --git a/src/keyboard.o b/src/keyboard.o new file mode 100644 index 0000000..3824b8a Binary files /dev/null and b/src/keyboard.o differ diff --git a/src/link.ld b/src/link.ld new file mode 100644 index 0000000..a723260 --- /dev/null +++ b/src/link.ld @@ -0,0 +1,29 @@ +OUTPUT_FORMAT("binary") +ENTRY(_start) +SECTIONS +{ + . = 0x10000; + + .text BLOCK(4K) : ALIGN(4K) + { + *(.text.prologue) + *(.text) + } + + .rodata BLOCK(4K) : ALIGN(4K) + { + *(.rodata) + } + + .data BLOCK(4K) : ALIGN(4K) + { + *(.data) + } + + .bss BLOCK(4K) : ALIGN(4K) + { + *(.bss) + } + + end = .; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..7783c28 --- /dev/null +++ b/src/main.c @@ -0,0 +1,743 @@ +#include "util.h" +#include "screen.h" +#include "idt.h" +#include "isr.h" +#include "irq.h" +#include "timer.h" +#include "font.h" +#include "system.h" +#include "keyboard.h" +#include "speaker.h" +#include "fpu.h" +#include "sound.h" +#include "music.h" + +#define FPS 30 +#define LEVELS 30 + +#define TILE_SIZE 10 + +#define LOGO_HEIGHT 5 +static const char *LOGO[LOGO_HEIGHT] = { + "AAA BBB CCC DD EEE FFF", + " A B C D D E F ", + " A BBB C DD E FFF", + " A B C D D E F", + " A BBB C D D EEE FFF", +}; + +#define NUM_TILES (BORDER + 1) +enum Tile { + NONE = 0, + GREEN, + ORANGE, + YELLOW, + PURPLE, + PINK, + BLUE, + CYAN, + RED, + BORDER +}; + +#define TILE_MASK 0x0F +#define TILE_FLAG_FLASH 0x10 +#define TILE_FLAG_DESTROY 0x20 + +static const u8 TILE_COLORS[NUM_TILES] = { + [NONE] = COLOR(7, 0, 3), + [GREEN] = COLOR(0, 5, 0), + [ORANGE] = COLOR(5, 3, 0), + [YELLOW] = COLOR(5, 5, 0), + [PURPLE] = COLOR(3, 0, 3), + [PINK] = COLOR(5, 0, 5), + [BLUE] = COLOR(0, 0, 2), + [CYAN] = COLOR(0, 3, 3), + [RED] = COLOR(5, 0, 0), + [BORDER] = COLOR(2, 2, 1), +}; + +u8 TILE_SPRITES[NUM_TILES][TILE_SIZE * TILE_SIZE] = { 0 }; + +#define BOARD_WIDTH 10 +#define BOARD_HEIGHT 20 +#define BOARD_SIZE (BOARD_WIDTH * BOARD_HEIGHT) + +#define BOARD_WIDTH_PX (BOARD_WIDTH * TILE_SIZE) +#define BOARD_HEIGHT_PX (BOARD_HEIGHT * TILE_SIZE) +#define BOARD_X ((SCREEN_WIDTH - BOARD_WIDTH_PX) / 2) +#define BOARD_Y 0 + +#define IN_BOARD(_x, _y) ((_x) >= 0 && (_y) >= 0 && (_x) < BOARD_WIDTH && (_y) < BOARD_HEIGHT) + +// max size of 4x4 to account for all rotations +#define TTM_SIZE 4 + +#define TTM_BLOCK(_t, _i, _j) (((_t) & (1 << (((_j) * 4) + (_i)))) != 0) + +#define TTM_OFFSET_X(_t)\ + MIN(_t & 0x000F ? LOBIT((_t >> 0) & 0xF) : 3,\ + MIN(_t & 0x00F0 ? LOBIT((_t >> 4) & 0xF) : 3,\ + MIN(_t & 0x0F00 ? LOBIT((_t >> 8) & 0xF) : 3,\ + _t & 0xF000 ? LOBIT((_t >> 12) & 0xF) : 3))) + +#define TTM_WIDTH(_t)\ + 1 + MAX(HIBIT((_t >> 0) & 0xF),\ + MAX(HIBIT((_t >> 4) & 0xF),\ + MAX(HIBIT((_t >> 8) & 0xF), HIBIT((_t >> 12) & 0xF)))) -\ + TTM_OFFSET_X(_t) + +#define TTM_HEIGHT(_t) ((HIBIT(_t) / 4) - (LOBIT(_t) / 4) + 1) +#define TTM_OFFSET_Y(_t) (LOBIT(_t) / 4) + +#define TTM_FOREACH(_xname, _yname, _xxname, _yyname, _xbase, _ybase)\ + for (i32 _yname = 0, _yyname = (_ybase); _yname < TTM_SIZE; _yname++,_yyname++)\ + for (i32 _xname = 0, _xxname = (_xbase); _xname < TTM_SIZE; _xname++,_xxname++)\ + +struct Tetromino { + enum Tile color; + u16 rotations[4]; +}; + +#define NUM_TETROMINOS 7 +static const struct Tetromino TETROMINOS[NUM_TETROMINOS] = { + { + // line + .color = CYAN, + .rotations = { + 0x00F0, + 0x2222, + 0x0F00, + 0x4444 + } + }, { + // left L + .color = BLUE, + .rotations = { + 0x8E00, + 0x6440, + 0x0E20, + 0x44C0 + } + }, { + // right L + .color = ORANGE, + .rotations = { + 0x2E00, + 0x4460, + 0x0E80, + 0xC440 + } + }, { + // cube + .color = YELLOW, + .rotations = { + 0xCC00, + 0xCC00, + 0xCC00, + 0xCC00 + } + }, { + // right skew + .color = GREEN, + .rotations = { + 0x6C00, + 0x4620, + 0x06C0, + 0x8C40 + } + }, { + // left skew + .color = RED, + .rotations = { + 0xC600, + 0x2640, + 0x0C60, + 0x4C80 + } + }, { + // T + .color = PURPLE, + .rotations = { + 0x4E00, + 0x4640, + 0x0E40, + 0x4C40 + } + } +}; + +#define NUM_LEVELS 30 + +// from listfist.com/list-of-tetris-levels-by-speed-nes-ntsc-vs-pal +static u8 FRAMES_PER_STEP[NUM_LEVELS] = { + 48, 43, 38, 33, 28, 23, 18, 13, 8, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1 +}; + +static u32 LINE_MULTIPLIERS[4] = { + 40, 100, 300, 1200 +}; + +#define NUM_CONTROLS 7 +struct Control { + bool down; + bool last; + bool pressed; + u32 pressed_frames; +}; + +static struct { + u8 board[BOARD_HEIGHT][BOARD_WIDTH]; + + u32 frames, steps, frames_since_step; + u32 score, lines, level; + i32 lines_left; + bool menu, pause, stopped, destroy, game_over, music; + + const struct Tetromino *next; + + struct { + const struct Tetromino *ttm; + u8 r; + i32 x, y; + bool done; + } curr; + + union { + struct { + struct Control rotate_left; + struct Control rotate_right; + struct Control rotate; + struct Control left; + struct Control right; + struct Control down; + struct Control fast_down; + }; + struct Control raw[NUM_CONTROLS]; + } controls; +} state; + +static void done() { + // flash tetromino which was just placed + TTM_FOREACH(x, y, xx, yy, state.curr.x, state.curr.y) { + if (IN_BOARD(xx, yy) && + TTM_BLOCK(state.curr.ttm->rotations[state.curr.r], x, y)) { + state.board[yy][xx] |= TILE_FLAG_FLASH; + } + } + + // check for lines + u32 lines = 0; + + for (size_t y = 0; y < BOARD_HEIGHT; y++) { + bool line = true; + + for (size_t x = 0; x < BOARD_WIDTH; x++) { + if ((state.board[y][x] & TILE_MASK) == NONE) { + line = false; + break; + } + } + + if (line) { + lines++; + + for (size_t x = 0; x < BOARD_WIDTH; x++) { + state.board[y][x] |= TILE_FLAG_FLASH | TILE_FLAG_DESTROY; + } + + state.destroy = true; + } + } + + if (lines > 0) { + state.lines += lines; + state.score += LINE_MULTIPLIERS[lines - 1] * (state.level + 1); + + // check for leveling up + if (state.level != NUM_LEVELS - 1) { + state.lines_left -= lines; + if (state.lines_left <= 0) { + state.level++; + state.lines_left = 10; + } + } + } + + // new tetromino is spawned in update() after destroy + state.curr.done = true; +} + +static bool try_modify( + const struct Tetromino *ttm, u16 tc, i32 xc, i32 yc, u16 tn, i32 xn, i32 yn) { + u8 board[BOARD_HEIGHT][BOARD_WIDTH]; + memcpy(&board, &state.board, sizeof(board)); + + // clear current tiles + if (tc != 0) { + TTM_FOREACH(x, y, xx, yy, xc, yc) { + if (IN_BOARD(xx, yy) && TTM_BLOCK(tc, x, y)) { + state.board[yy][xx] = NONE; + } + } + } + + TTM_FOREACH(x, y, xx, yy, xn, yn) { + if (yy < 0) { + if (TTM_BLOCK(tn, x, y) && + (xx < 0 || xx >= BOARD_WIDTH)) { + goto fail; + } + + continue; + } else if (!TTM_BLOCK(tn, x, y)) { + continue; + } else if (!IN_BOARD(xx, yy) || state.board[yy][xx] != NONE || + xx < 0 || xx > (BOARD_WIDTH - 1)) { + goto fail; + } + + state.board[yy][xx] = ttm->color; + } + + return true; +fail: + memcpy(&state.board, &board, sizeof(board)); + return false; +} + +static bool spawn() { + if (state.next == NULL) { + state.next = &TETROMINOS[rand() % NUM_TETROMINOS]; + } + + state.curr.ttm = state.next; + state.curr.r = 0; + state.curr.x = (BOARD_WIDTH / 2) - 2; + state.curr.y = -TTM_OFFSET_Y(state.curr.ttm->rotations[state.curr.r]) - 1; + state.curr.done = false; + + if (!try_modify( + state.curr.ttm, + 0, 0, 0, + state.curr.ttm->rotations[state.curr.r], + state.curr.x, state.curr.y)) { + return false; + } + + state.next = &TETROMINOS[rand() % NUM_TETROMINOS]; + return true; +} + +static bool move(i32 dx, i32 dy) { + if (try_modify( + state.curr.ttm, + state.curr.ttm->rotations[state.curr.r], + state.curr.x, state.curr.y, + state.curr.ttm->rotations[state.curr.r], + state.curr.x + dx, state.curr.y + dy)) { + state.curr.x += dx; + state.curr.y += dy; + return true; + } + + return false; +} + +static bool rotate(bool right) { + u8 r = (state.curr.r + (right ? 1 : -1) + 4) % 4; + + if (try_modify( + state.curr.ttm, + state.curr.ttm->rotations[state.curr.r], + state.curr.x, state.curr.y, + state.curr.ttm->rotations[r], + state.curr.x, state.curr.y)) { + state.curr.r = r; + return true; + } + + return false; +} + +static void generate_sprites() { + for (enum Tile t = 0; t < NUM_TILES; t++) { + if (t == NONE) { + continue; + } + + u8 color = TILE_COLORS[t]; + u8 *pixels = TILE_SPRITES[t]; + + for (size_t y = 0; y < TILE_SIZE; y++) { + for (size_t x = 0; x < TILE_SIZE; x++) { + u8 c = color; + + if (y == 0 || x == 0) { + c = COLOR_ADD(color, 1); + } else if (y == TILE_SIZE - 1 || x == TILE_SIZE - 1) { + c = COLOR_ADD(color, -1); + } + + pixels[y * TILE_SIZE + x] = c; + } + } + } +} + +static void render_tile(enum Tile tile, size_t x, size_t y) { + u8 *pixels = TILE_SPRITES[tile]; + for (size_t j = 0; j < TILE_SIZE; j++) { + memcpy(&screen_offset(x, y + j), pixels + (j * TILE_SIZE), TILE_SIZE); + } +} + +static void render_border() { + for (size_t y = 0; y < (SCREEN_HEIGHT / TILE_SIZE); y++) { + size_t yy = BOARD_Y + (y * TILE_SIZE); + + render_tile( + BORDER, + BOARD_X - TILE_SIZE, + yy + ); + + render_tile( + BORDER, + BOARD_X + (BOARD_WIDTH * TILE_SIZE), + yy + ); + } +} + +static void render_board() { + for (size_t y = 0; y < BOARD_HEIGHT; y++) { + for (size_t x = 0; x < BOARD_WIDTH; x++) { + u8 data = state.board[y][x]; + enum Tile tile = data & TILE_MASK; + + size_t xs = BOARD_X + (x * TILE_SIZE), + ys = BOARD_Y + (y * TILE_SIZE); + + if (data & TILE_FLAG_FLASH) { + screen_fill(COLOR(4, 4, 1), xs, ys, TILE_SIZE, TILE_SIZE); + } else if (tile != NONE) { + render_tile(tile, xs, ys); + } + } + } +} + +static void render_ui() { +#define X_OFFSET_RIGHT (BOARD_X + BOARD_WIDTH_PX + (TILE_SIZE * 2)) + +#define RENDER_STAT(_title, _value, _color, _x, _y, _w) do {\ + char buf[32];\ + itoa((_value), buf, 32);\ + font_str_doubled((_title), (_x), (_y), COLOR(7, 7, 3));\ + font_str_doubled(buf, (_x) + (_w) - font_width(buf), (_y) + TILE_SIZE, (_color));\ + } while (0); + + size_t w = font_width("SCORE"); + RENDER_STAT("SCORE", state.score, COLOR(5, 5, 0), X_OFFSET_RIGHT, TILE_SIZE * 1, w); + RENDER_STAT("LINES", state.lines, COLOR(5, 3, 0), X_OFFSET_RIGHT, TILE_SIZE * 4, w); + RENDER_STAT("LEVEL", state.level, COLOR(5, 0, 0), X_OFFSET_RIGHT, TILE_SIZE * 7, w); + +#define X_OFFSET_LEFT (BOARD_X - (TILE_SIZE * 8)) +#define Y_OFFSET_LEFT TILE_SIZE + + font_str_doubled("NEXT", X_OFFSET_LEFT, TILE_SIZE, COLOR(7, 7, 3)); + + for (size_t j = 0; j < TTM_SIZE; j++) { + for (size_t i = 0; i < TTM_SIZE; i++) { + u16 tiles = state.next->rotations[0]; + + if (TTM_BLOCK(tiles, i, j)) { + render_tile( + state.next->color, + X_OFFSET_LEFT + ((i - TTM_OFFSET_X(tiles)) * TILE_SIZE), + Y_OFFSET_LEFT + (TILE_SIZE / 2) + ((j - TTM_OFFSET_Y(tiles) + 1) * TILE_SIZE) + ); + } + } + } +} + +static void render_game_over() { + const size_t w = SCREEN_WIDTH / 3, h = SCREEN_HEIGHT / 3; + screen_fill( + COLOR(4, 4, 2), + (SCREEN_WIDTH - w) / 2, + (SCREEN_HEIGHT - h) / 2, + w, + h + ); + + screen_fill( + COLOR(2, 2, 1), + (SCREEN_WIDTH - (w - 8)) / 2, + (SCREEN_HEIGHT - (h - 8)) / 2, + w - 8, + h - 8 + ); + + font_str_doubled( + "GAME OVER", + (SCREEN_WIDTH - font_width("GAME OVER")) / 2, + (SCREEN_HEIGHT / 2) - TILE_SIZE, + (state.frames / 5) % 2 == 0 ? + COLOR(6, 2, 1) : + COLOR(7, 4, 2) + ); + + char buf_score[64]; + itoa(state.score, buf_score, 64); + + font_str_doubled( + "SCORE:", + (SCREEN_WIDTH - font_width("SCORE:")) / 2, + (SCREEN_HEIGHT / 2), + COLOR(6, 6, 0) + ); + + font_str_doubled( + buf_score, + (SCREEN_WIDTH - font_width(buf_score)) / 2, + (SCREEN_HEIGHT / 2) + TILE_SIZE, + COLOR(7, 7, 3) + ); +} + +static void step() { + bool stopped = !move(0, 1); + + if (stopped && state.stopped) { + // twice stop = end for this tetromino + done(); + } + + state.stopped = stopped; +} + +void reset(u32 level) { + // initialize game state + memset(&state, 0, sizeof(state)); + state.frames_since_step = FRAMES_PER_STEP[0]; + state.level = 0; + state.lines_left = state.level * 10 + 10; + spawn(); +} + +static void update() { + if (state.game_over) { + if (keyboard_char('\n')) { + reset(0); + } + + return; + } + + // un-flash flashing tiles, remove destroy tiles + for (size_t y = 0; y < BOARD_HEIGHT; y++) { + bool destroy = false; + + for (size_t x = 0; x < BOARD_WIDTH; x++) { + u8 data = state.board[y][x]; + + if (data & TILE_FLAG_DESTROY) { + state.board[y][x] = NONE; + destroy = true; + } else { + state.board[y][x] &= ~TILE_FLAG_FLASH; + } + } + + if (destroy) { + if (y != 0) { + memmove( + &state.board[1], + &state.board[0], + sizeof(state.board[0]) * y + ); + } + + memset(&state.board[0], NONE, sizeof(state.board[0])); + } + } + + // spawn a new tetromino if the current one is done + if (state.curr.done && !spawn()) { + state.game_over = true; + return; + } + + if (state.destroy) { + state.destroy = false; + return; + } + + const bool control_states[NUM_CONTROLS] = { + keyboard_char('a'), + keyboard_char('d'), + keyboard_char('r'), + keyboard_key(KEY_LEFT), + keyboard_key(KEY_RIGHT), + keyboard_key(KEY_DOWN), + keyboard_char(' ') + }; + + for (size_t i = 0; i < NUM_CONTROLS; i++) { + struct Control *c = &state.controls.raw[i]; + c->last = c->down; + c->down = control_states[i]; + c->pressed = !c->last && c->down; + + if (c->pressed) { + c->pressed_frames = state.frames; + } + } + + if (state.controls.rotate_left.pressed) { + rotate(false); + } else if (state.controls.rotate_right.pressed || + state.controls.rotate.pressed) { + rotate(true); + } + + if (state.controls.left.down && + (state.frames - state.controls.left.pressed_frames) % 2 == 0) { + move(-1, 0); + } else if (state.controls.right.down && + (state.frames - state.controls.right.pressed_frames) % 2 == 0) { + move(1, 0); + } else if (state.controls.down.down && + (state.frames - state.controls.down.pressed_frames) % 2 == 0) { + if (!move(0, 1)) { + done(); + } + } else if (state.controls.fast_down.pressed) { + while (move(0, 1)); + done(); + } + + if (--state.frames_since_step == 0) { + step(); + state.steps++; + state.frames_since_step = FRAMES_PER_STEP[state.level]; + } +} + +static void render() { + screen_clear(COLOR(0, 0, 0)); + render_border(); + render_board(); + render_ui(); + + if (state.game_over) { + render_game_over(); + } +} + +void update_menu() { + if (keyboard_char('\n')) { + reset(0); + state.menu = false; + } +} + +void render_menu() { + screen_clear(COLOR(0, 0, 0)); + + // render logo + size_t logo_width = strlen(LOGO[0]), + logo_x = (SCREEN_WIDTH - (logo_width * TILE_SIZE)) / 2, + logo_y = TILE_SIZE * 3; + + for (i32 x = -1; x < (i32) logo_width + 1; x++) { + render_tile(BORDER, logo_x + (x * TILE_SIZE), logo_y - (TILE_SIZE * 2)); + render_tile(BORDER, logo_x + (x * TILE_SIZE), logo_y + (TILE_SIZE * (1 + LOGO_HEIGHT))); + } + + for (size_t y = 0; y < LOGO_HEIGHT; y++) { + for (size_t x = 0; x < logo_width; x++) { + char c = LOGO[y][x]; + + if (c == ' ' || c == '\t' || c == '\n') { + continue; + } + + render_tile( + GREEN + ((((state.frames / 10) + (6 - (c - 'A'))) / 6) % 8), + logo_x + (x * TILE_SIZE), + logo_y + (y * TILE_SIZE) + ); + } + } + + const char *play = "PRESS ENTER TO PLAY"; + font_str_doubled( + play, + (SCREEN_WIDTH - font_width(play)) / 2, + logo_y + ((LOGO_HEIGHT + 6) * TILE_SIZE), + (state.frames / 6) % 2 == 0 ? + COLOR(6, 6, 2) : + COLOR(7, 7, 3) + ); +} + +void _main(u32 magic) { + idt_init(); + isr_init(); + fpu_init(); + irq_init(); + screen_init(); + timer_init(); + keyboard_init(); + sound_init(); + generate_sprites(); + music_init(); + + state.menu = true; + + state.music = true; + sound_master(255); + + bool last_music_toggle = false; + u32 last_frame = 0, last = 0; + + while (true) { + const u32 now = (u32) timer_get(); + + if (now != last) { + music_tick(); + last = now; + } + + if ((now - last_frame) > (TIMER_TPS / FPS)) { + last_frame = now; + + if (state.menu) { + update_menu(); + render_menu(); + } else { + update(); + render(); + } + + if (keyboard_char('m')) { + if (!last_music_toggle) { + state.music = !state.music; + sound_master(state.music ? 255 : 0); + } + + last_music_toggle = true; + } else { + last_music_toggle = false; + } + + screen_swap(); + state.frames++; + } + } +} diff --git a/src/main.o b/src/main.o new file mode 100644 index 0000000..a3c8dc3 Binary files /dev/null and b/src/main.o differ diff --git a/src/math.c b/src/math.c new file mode 100644 index 0000000..8b4b7e3 --- /dev/null +++ b/src/math.c @@ -0,0 +1,46 @@ +#include "math.h" + +f64 fabs(f64 x) { + return x < 0.0 ? -x : x; +} + +f64 fmod(f64 x, f64 m) { + f64 result; + asm("1: fprem\n\t" + "fnstsw %%ax\n\t" + "sahf\n\t" + "jp 1b" + : "=t"(result) : "0"(x), "u"(m) : "ax", "cc"); + return result; +} + +f64 sin(f64 x) { + f64 result; + asm("fsin" : "=t"(result) : "0"(x)); + return result; +} + +f64 cos(f64 x) { + return sin(x + PI / 2.0); +} + +// black magic +f64 pow(f64 x, f64 y) { + f64 out; + asm( + "fyl2x;" + "fld %%st;" + "frndint;" + "fsub %%st,%%st(1);" + "fxch;" + "fchs;" + "f2xm1;" + "fld1;" + "faddp;" + "fxch;" + "fld1;" + "fscale;" + "fstp %%st(1);" + "fmulp;" : "=t"(out) : "0"(x),"u"(y) : "st(1)" ); + return out; +} diff --git a/src/math.h b/src/math.h new file mode 100644 index 0000000..dbf1a6f --- /dev/null +++ b/src/math.h @@ -0,0 +1,15 @@ +#ifndef MATH_H +#define MATH_H + +#include "util.h" + +#define E 2.71828 +#define PI 3.14159265358979323846264338327950 + +f64 fmod(f64 x, f64 m); +f64 fabs(f64 x); +f64 sin(f64 x); +f64 cos(f64 x); +f64 pow(f64 x, f64 y); + +#endif diff --git a/src/math.o b/src/math.o new file mode 100644 index 0000000..e0ce7bd Binary files /dev/null and b/src/math.o differ diff --git a/src/music.c b/src/music.c new file mode 100644 index 0000000..9865812 --- /dev/null +++ b/src/music.c @@ -0,0 +1,359 @@ +#include "music.h" +#include "timer.h" +#include "sound.h" +#include "math.h" + +struct Note { + u8 octave; + u8 note; + u16 duration; +}; + +struct NoteActive { + struct Note note; + double ticks; +}; + +#define TRACK_BPM 150 +#define TRACK_BPS (TRACK_BPM / 60.0) +#define TICKS_PER_BEAT (TIMER_TPS / TRACK_BPS) +#define TICKS_PER_SIXTEENTH (TICKS_PER_BEAT / 16.0) + +#define CHORUS_MELODY_LENGTH (sizeof(CHORUS_MELODY) / sizeof(CHORUS_MELODY[0])) +static const struct Note CHORUS_MELODY[] = { + // CHORUS MELODY + { OCTAVE_5, NOTE_E, 16 }, + { OCTAVE_4, NOTE_B, 8 }, + { OCTAVE_5, NOTE_C, 8 }, + { OCTAVE_5, NOTE_D, 16 }, + + { OCTAVE_5, NOTE_C, 8 }, + { OCTAVE_4, NOTE_B, 8 }, + { OCTAVE_4, NOTE_A, 16 }, + + { OCTAVE_4, NOTE_A, 8 }, + { OCTAVE_5, NOTE_C, 8 }, + { OCTAVE_5, NOTE_E, 16 }, + + { OCTAVE_5, NOTE_D, 8 }, + { OCTAVE_5, NOTE_C, 8 }, + { OCTAVE_4, NOTE_B, 16 }, + + { OCTAVE_4, NOTE_B, 8 }, + { OCTAVE_5, NOTE_C, 8 }, + { OCTAVE_5, NOTE_D, 16 }, + { OCTAVE_5, NOTE_E, 16 }, + { OCTAVE_5, NOTE_C, 16 }, + { OCTAVE_4, NOTE_A, 16 }, + { OCTAVE_4, NOTE_A, 16 }, + { OCTAVE_4, NOTE_NONE, 24 }, + + { OCTAVE_5, NOTE_D, 16 }, + { OCTAVE_5, NOTE_F, 8 }, + { OCTAVE_5, NOTE_A, 8 }, + { OCTAVE_5, NOTE_A, 4 }, + { OCTAVE_5, NOTE_A, 4 }, + { OCTAVE_5, NOTE_G, 8 }, + { OCTAVE_5, NOTE_F, 8 }, + { OCTAVE_5, NOTE_E, 16 }, + { OCTAVE_5, NOTE_NONE, 8 }, + + { OCTAVE_5, NOTE_C, 8 }, + { OCTAVE_5, NOTE_E, 8 }, + { OCTAVE_5, NOTE_E, 4 }, + { OCTAVE_5, NOTE_E, 4 }, + { OCTAVE_5, NOTE_D, 8 }, + { OCTAVE_5, NOTE_C, 8 }, + { OCTAVE_4, NOTE_B, 16 }, + + { OCTAVE_4, NOTE_B, 8 }, + { OCTAVE_5, NOTE_C, 8 }, + { OCTAVE_5, NOTE_D, 16 }, + { OCTAVE_5, NOTE_E, 16 }, + { OCTAVE_5, NOTE_C, 16 }, + { OCTAVE_4, NOTE_A, 16 }, + { OCTAVE_4, NOTE_A, 16 }, + { OCTAVE_4, NOTE_NONE, 16 }, +}; + +#define BRIDGE_MELODY_LENGTH (sizeof(BRIDGE_MELODY) / sizeof(BRIDGE_MELODY[0])) +static const struct Note BRIDGE_MELODY[] = { + // BRIDGE + { OCTAVE_4, NOTE_E, 32 }, + { OCTAVE_4, NOTE_C, 32 }, + { OCTAVE_4, NOTE_D, 32 }, + { OCTAVE_3, NOTE_B, 32 }, + { OCTAVE_4, NOTE_C, 32 }, + { OCTAVE_3, NOTE_A, 32 }, + { OCTAVE_3, NOTE_AF, 48 }, + { OCTAVE_3, NOTE_NONE, 16 }, + + { OCTAVE_4, NOTE_E, 32 }, + { OCTAVE_4, NOTE_C, 32 }, + { OCTAVE_4, NOTE_D, 32 }, + { OCTAVE_3, NOTE_B, 32 }, + { OCTAVE_3, NOTE_A, 16 }, + { OCTAVE_4, NOTE_E, 16 }, + { OCTAVE_4, NOTE_A, 32 }, + { OCTAVE_4, NOTE_AF, 48 }, + { OCTAVE_4, NOTE_NONE, 16 }, +}; + +#define BASS_NOTE(_octave, _note)\ + { _octave, _note, 8 },\ + { (_octave + 1), _note, 8 } + +#define BASS_HALF_MEASURE(_octave, _note)\ + BASS_NOTE(_octave, _note),\ + BASS_NOTE(_octave, _note) + +#define BASS_MEASURE(_octave, _note)\ + BASS_HALF_MEASURE(_octave, _note),\ + BASS_HALF_MEASURE(_octave, _note) + +#define CHORUS_BASS_LENGTH (sizeof(CHORUS_BASS) / sizeof(CHORUS_BASS[0])) +static const struct Note CHORUS_BASS[] = { + // CHORUS BASS + BASS_MEASURE(OCTAVE_2, NOTE_E), + BASS_MEASURE(OCTAVE_2, NOTE_A), + BASS_HALF_MEASURE(OCTAVE_2, NOTE_AF), + BASS_HALF_MEASURE(OCTAVE_2, NOTE_E), + BASS_HALF_MEASURE(OCTAVE_2, NOTE_A), + { OCTAVE_2, NOTE_A, 8 }, + { OCTAVE_2, NOTE_A, 8 }, + { OCTAVE_2, NOTE_B, 8 }, + { OCTAVE_3, NOTE_C, 8 }, + + BASS_MEASURE(OCTAVE_2, NOTE_D), + BASS_MEASURE(OCTAVE_2, NOTE_C), + BASS_HALF_MEASURE(OCTAVE_2, NOTE_AF), + BASS_HALF_MEASURE(OCTAVE_2, NOTE_E), + BASS_MEASURE(OCTAVE_2, NOTE_A), +}; + +#define BRIDGE_BASS_NOTE(_octave_0, _note_0, _octave_1, _note_1)\ + { _octave_0, _note_0, 8 },\ + { _octave_1, _note_1, 8 } + +#define BRIDGE_BASS_HALF_MEASURE(_octave_0, _note_0, _octave_1, _note_1)\ + BRIDGE_BASS_NOTE(_octave_0, _note_0, _octave_1, _note_1),\ + BRIDGE_BASS_NOTE(_octave_0, _note_0, _octave_1, _note_1) + +#define BRIDGE_BASS_MEASURE(_octave_0, _note_0, _octave_1, _note_1)\ + BRIDGE_BASS_HALF_MEASURE(_octave_0, _note_0, _octave_1, _note_1),\ + BRIDGE_BASS_HALF_MEASURE(_octave_0, _note_0, _octave_1, _note_1) + +#define BRIDGE_BASS_LENGTH (sizeof(BRIDGE_BASS) / sizeof(BRIDGE_BASS[0])) +static const struct Note BRIDGE_BASS[] = { + BRIDGE_BASS_MEASURE(OCTAVE_2, NOTE_A, OCTAVE_3, NOTE_E), + BRIDGE_BASS_MEASURE(OCTAVE_2, NOTE_AF, OCTAVE_3, NOTE_E), + + BRIDGE_BASS_HALF_MEASURE(OCTAVE_2, NOTE_A, OCTAVE_3, NOTE_E), + BRIDGE_BASS_HALF_MEASURE(OCTAVE_2, NOTE_E, OCTAVE_2, NOTE_A), + + BRIDGE_BASS_HALF_MEASURE(OCTAVE_2, NOTE_E, OCTAVE_2, NOTE_AF), + BRIDGE_BASS_NOTE(OCTAVE_2, NOTE_E, OCTAVE_2, NOTE_AF), + { OCTAVE_2, NOTE_E, 8 }, + { OCTAVE_2, NOTE_NONE, 8 }, + + BRIDGE_BASS_MEASURE(OCTAVE_2, NOTE_A, OCTAVE_3, NOTE_E), + BRIDGE_BASS_MEASURE(OCTAVE_2, NOTE_AF, OCTAVE_3, NOTE_E), + + BRIDGE_BASS_HALF_MEASURE(OCTAVE_2, NOTE_A, OCTAVE_3, NOTE_E), + BRIDGE_BASS_HALF_MEASURE(OCTAVE_2, NOTE_E, OCTAVE_2, NOTE_A), + + BRIDGE_BASS_HALF_MEASURE(OCTAVE_2, NOTE_E, OCTAVE_2, NOTE_AF), + BRIDGE_BASS_NOTE(OCTAVE_2, NOTE_E, OCTAVE_2, NOTE_AF), + { OCTAVE_2, NOTE_E, 8 }, + { OCTAVE_2, NOTE_NONE, 8 } +}; + +#define CHORUS_HARMONY_LENGTH (sizeof(CHORUS_HARMONY) / sizeof(CHORUS_HARMONY[0])) +static const struct Note CHORUS_HARMONY[] = { + // CHORUS HARMONY + { OCTAVE_5, NOTE_NONE, 16 }, + { OCTAVE_4, NOTE_AF, 8 }, + { OCTAVE_4, NOTE_A, 8 }, + { OCTAVE_4, NOTE_B, 16 }, + + { OCTAVE_4, NOTE_A, 8 }, + { OCTAVE_4, NOTE_AF, 8 }, + { OCTAVE_4, NOTE_E, 16 }, + + { OCTAVE_4, NOTE_E, 8 }, + { OCTAVE_4, NOTE_A, 8 }, + { OCTAVE_4, NOTE_A, 16 }, + + { OCTAVE_4, NOTE_B, 8 }, + { OCTAVE_4, NOTE_A, 8 }, + { OCTAVE_4, NOTE_AF, 16 }, + + { OCTAVE_4, NOTE_AF, 8 }, + { OCTAVE_4, NOTE_A, 8 }, + { OCTAVE_4, NOTE_B, 16 }, + { OCTAVE_5, NOTE_C, 16 }, + { OCTAVE_4, NOTE_A, 16 }, + { OCTAVE_4, NOTE_E, 16 }, + { OCTAVE_4, NOTE_E, 16 }, + { OCTAVE_4, NOTE_NONE, 24 }, + + { OCTAVE_4, NOTE_F, 16 }, + { OCTAVE_4, NOTE_A, 8 }, + { OCTAVE_5, NOTE_C, 16 }, + { OCTAVE_4, NOTE_B, 8 }, + { OCTAVE_4, NOTE_A, 8 }, + { OCTAVE_4, NOTE_G, 16 }, + { OCTAVE_4, NOTE_NONE, 8 }, + + { OCTAVE_4, NOTE_E, 8 }, + { OCTAVE_4, NOTE_G, 16 }, + { OCTAVE_4, NOTE_F, 8 }, + { OCTAVE_4, NOTE_E, 8 }, + { OCTAVE_4, NOTE_AF, 16 }, + + { OCTAVE_4, NOTE_AF, 8 }, + { OCTAVE_4, NOTE_A, 8 }, + { OCTAVE_4, NOTE_B, 16 }, + { OCTAVE_5, NOTE_C, 16 }, + { OCTAVE_4, NOTE_A, 16 }, + { OCTAVE_4, NOTE_E, 16 }, + { OCTAVE_4, NOTE_E, 16 }, + { OCTAVE_4, NOTE_NONE, 16 }, +}; + +#define BRIDGE_HARMONY_LENGTH (sizeof(BRIDGE_HARMONY) / sizeof(BRIDGE_HARMONY[0])) +static const struct Note BRIDGE_HARMONY[] = { + { OCTAVE_4, NOTE_C, 32 }, + { OCTAVE_3, NOTE_A, 32 }, + { OCTAVE_3, NOTE_B, 32 }, + { OCTAVE_3, NOTE_AF, 32 }, + { OCTAVE_3, NOTE_A, 32 }, + { OCTAVE_3, NOTE_E, 32 }, + { OCTAVE_3, NOTE_E, 48 }, + { OCTAVE_3, NOTE_NONE, 16 }, + + { OCTAVE_4, NOTE_C, 32 }, + { OCTAVE_3, NOTE_A, 32 }, + { OCTAVE_3, NOTE_B, 32 }, + { OCTAVE_3, NOTE_AF, 32 }, + { OCTAVE_3, NOTE_E, 16 }, + { OCTAVE_3, NOTE_A, 16 }, + { OCTAVE_4, NOTE_E, 32 }, + { OCTAVE_4, NOTE_E, 48 }, + { OCTAVE_4, NOTE_NONE, 16 }, +}; + +#define SNARE_OCTAVE OCTAVE_4 +#define SNARE_NOTE NOTE_E +#define SNARE_AND\ + { SNARE_OCTAVE, NOTE_NONE, 8},\ + { SNARE_OCTAVE, SNARE_NOTE, 2},\ + { SNARE_OCTAVE, NOTE_NONE, 6} +#define SNARE_EIGTH\ + { SNARE_OCTAVE, SNARE_NOTE, 2},\ + { SNARE_OCTAVE, NOTE_NONE, 6} + +#define SNARE_MEASURE\ + SNARE_AND,\ + SNARE_AND,\ + SNARE_AND,\ + SNARE_AND + +#define CHORUS_SNARE_LENGTH (sizeof(CHORUS_SNARE) / sizeof(CHORUS_SNARE[0])) +static const struct Note CHORUS_SNARE[] = { + SNARE_MEASURE, + SNARE_MEASURE, + SNARE_MEASURE, + SNARE_AND, + SNARE_AND, + SNARE_AND, + SNARE_EIGTH, + SNARE_EIGTH, + SNARE_MEASURE, + SNARE_MEASURE, + SNARE_MEASURE, + SNARE_AND, + SNARE_AND, + SNARE_AND, + SNARE_EIGTH, + SNARE_EIGTH, +}; + +#define BRIDGE_SNARE_LENGTH (sizeof(BRIDGE_SNARE) / sizeof(BRIDGE_SNARE[0])) +static const struct Note BRIDGE_SNARE[] = { + SNARE_MEASURE, + SNARE_MEASURE, + SNARE_MEASURE, + SNARE_MEASURE, + SNARE_MEASURE, + SNARE_MEASURE, + SNARE_MEASURE, + SNARE_AND, + SNARE_AND, + SNARE_AND, + SNARE_EIGTH, + SNARE_EIGTH, +}; + +#define TRACK_MAX_LENGTH (4 * (CHORUS_MELODY_LENGTH + BRIDGE_MELODY_LENGTH)) +#define TRACK_PARTS 4 +static struct Note TRACK[TRACK_PARTS][TRACK_MAX_LENGTH]; +static size_t PART_LENGTHS[TRACK_PARTS]; + +static i32 indices[TRACK_PARTS]; +static struct NoteActive current[NUM_NOTES]; + +void music_tick() { + for (size_t i = 0; i < TRACK_PARTS; i++) { + if (indices[i] == -1 || (current[i].ticks -= 1) <= 0) { + indices[i] = (indices[i] + 1) % PART_LENGTHS[i]; + + double remainder = fabs(current[i].ticks); + + struct Note note = TRACK[i][indices[i]]; + current[i].note = note; + current[i].ticks = TICKS_PER_SIXTEENTH * note.duration - remainder; + + sound_note(i, note.octave, note.note); + } + + // remove last tick to give each note an attack + if (current[i].ticks == 1) { + sound_note(i, OCTAVE_1, NOTE_NONE); + } + } +} + +void music_init() { + sound_wave(0, WAVE_TRIANGLE); + sound_volume(0, 255); + + sound_wave(1, WAVE_NOISE); + sound_volume(1, 0); + + sound_wave(2, WAVE_TRIANGLE); + sound_volume(2, 0); + + sound_wave(3, WAVE_TRIANGLE); + sound_volume(3, 0); + + memcpy(&TRACK[0][0], CHORUS_MELODY, sizeof(CHORUS_MELODY)); + memcpy(&TRACK[0][CHORUS_MELODY_LENGTH], BRIDGE_MELODY, sizeof(BRIDGE_MELODY)); + PART_LENGTHS[0] = CHORUS_MELODY_LENGTH + BRIDGE_MELODY_LENGTH; + + memcpy(&TRACK[1][0], CHORUS_SNARE, sizeof(CHORUS_SNARE)); + memcpy(&TRACK[1][CHORUS_SNARE_LENGTH], BRIDGE_SNARE, sizeof(BRIDGE_SNARE)); + PART_LENGTHS[1] = CHORUS_SNARE_LENGTH + BRIDGE_SNARE_LENGTH; + + memcpy(&TRACK[2][0], CHORUS_BASS, sizeof(CHORUS_BASS)); + memcpy(&TRACK[2][CHORUS_BASS_LENGTH], BRIDGE_BASS, sizeof(BRIDGE_BASS)); + PART_LENGTHS[2] = CHORUS_BASS_LENGTH + BRIDGE_BASS_LENGTH; + + memcpy(&TRACK[3][0], CHORUS_HARMONY, sizeof(CHORUS_HARMONY)); + memcpy(&TRACK[3][CHORUS_HARMONY_LENGTH], BRIDGE_HARMONY, sizeof(BRIDGE_HARMONY)); + PART_LENGTHS[3] = CHORUS_HARMONY_LENGTH + BRIDGE_HARMONY_LENGTH; + + for (size_t i = 0; i < TRACK_PARTS; i++) { + indices[i] = -1; + } +} diff --git a/src/music.h b/src/music.h new file mode 100644 index 0000000..fd28a90 --- /dev/null +++ b/src/music.h @@ -0,0 +1,9 @@ +#ifndef MUSIC_H +#define MUSIC_H + +#include "util.h" + +void music_tick(); +void music_init(); + +#endif diff --git a/src/screen.c b/src/screen.c new file mode 100644 index 0000000..1a3ec26 --- /dev/null +++ b/src/screen.c @@ -0,0 +1,41 @@ +#include "screen.h" + +static u8 *BUFFER = (u8 *) 0xA0000; + +// double buffers +u8 _sbuffers[2][SCREEN_SIZE]; +u8 _sback = 0; + +#define CURRENT (_sbuffers[_sback]) +#define SWAP() (_sback = 1 - _sback) + +// VGA control port addresses +#define PALETTE_MASK 0x3C6 +#define PALETTE_READ 0x3C7 +#define PALETTE_WRITE 0x3C8 +#define PALETTE_DATA 0x3C9 + +void screen_swap() { + memcpy(BUFFER, &CURRENT, SCREEN_SIZE); + SWAP(); +} + +void screen_clear(u8 color) { + memset(&CURRENT, color, SCREEN_SIZE); +} + +void screen_init() { + // configure palette with 8-bit RRRGGGBB color + outportb(PALETTE_MASK, 0xFF); + outportb(PALETTE_WRITE, 0); + for (u8 i = 0; i < 255; i++) { + outportb(PALETTE_DATA, (((i >> 5) & 0x7) * (256 / 8)) / 4); + outportb(PALETTE_DATA, (((i >> 2) & 0x7) * (256 / 8)) / 4); + outportb(PALETTE_DATA, (((i >> 0) & 0x3) * (256 / 4)) / 4); + } + + // set color 255 = white + outportb(PALETTE_DATA, 0x3F); + outportb(PALETTE_DATA, 0x3F); + outportb(PALETTE_DATA, 0x3F); +} diff --git a/src/screen.h b/src/screen.h new file mode 100644 index 0000000..ddee993 --- /dev/null +++ b/src/screen.h @@ -0,0 +1,53 @@ +#ifndef SCREEN_H +#define SCREEN_H + +#include "util.h" + +#define SCREEN_WIDTH 320 +#define SCREEN_HEIGHT 200 +#define SCREEN_SIZE (SCREEN_WIDTH * SCREEN_HEIGHT) + +#define COLOR(_r, _g, _b)((u8)( \ + (((_r) & 0x7) << 5) | \ + (((_g) & 0x7) << 2) | \ + (((_b) & 0x3) << 0))) + +#define COLOR_R(_index) (((_index) >> 5) & 0x7) +#define COLOR_G(_index) (((_index) >> 2) & 0x7) +#define COLOR_B(_index) (((_index) >> 0) & 0x3) + +#define COLOR_ADD(_index, _d) __extension__({ \ + __typeof__(_index) _c = (_index); \ + __typeof__(_d) __d = (_d); \ + COLOR( \ + CLAMP(COLOR_R(_c) + __d, 0, 7), \ + CLAMP(COLOR_G(_c) + __d, 0, 7), \ + CLAMP(COLOR_B(_c) + __d, 0, 3) \ + );}) + +extern u8 _sbuffers[2][SCREEN_SIZE]; +extern u8 _sback; + +#define screen_buffer() (_sbuffers[_sback]) + +#define screen_set(_p, _x, _y)\ + (_sbuffers[_sback][((_y) * SCREEN_WIDTH + (_x))]=(_p)) + +#define screen_offset(_x, _y) (screen_buffer()[(_y) * SCREEN_WIDTH + (_x)]) + +#define screen_fill(_c, _x, _y, _w, _h) do {\ + __typeof__(_x) __x = (_x);\ + __typeof__(_y) __y = (_y);\ + __typeof__(_w) __w = (_w);\ + __typeof__(_y) __ymax = __y + (_h);\ + __typeof__(_c) __c = (_c);\ + for (; __y < __ymax; __y++) {\ + memset(&screen_buffer()[__y * SCREEN_WIDTH + __x], __c, __w);\ + }\ + } while (0) + +void screen_swap(); +void screen_clear(u8 color); +void screen_init(); + +#endif diff --git a/src/screen.o b/src/screen.o new file mode 100644 index 0000000..c0582b0 Binary files /dev/null and b/src/screen.o differ diff --git a/src/sound.c b/src/sound.c new file mode 100644 index 0000000..cf567bf --- /dev/null +++ b/src/sound.c @@ -0,0 +1,355 @@ +#include "sound.h" +#include "system.h" +#include "irq.h" +#include "math.h" + +static const f64 NOTES[NUM_OCTAVES * OCTAVE_SIZE] = { + // O1 + 32.703195662574764, + 34.647828872108946, + 36.708095989675876, + 38.890872965260044, + 41.203444614108669, + 43.653528929125407, + 46.249302838954222, + 48.99942949771858, + 51.913087197493056, + 54.999999999999915, + 58.270470189761156, + 61.735412657015416, + + // O2 + 65.406391325149571, + 69.295657744217934, + 73.416191979351794, + 77.781745930520117, + 82.406889228217381, + 87.307057858250872, + 92.4986056779085, + 97.998858995437217, + 103.82617439498618, + 109.99999999999989, + 116.54094037952237, + 123.4708253140309, + + // O3 + 130.8127826502992, + 138.59131548843592, + 146.83238395870364, + 155.56349186104035, + 164.81377845643485, + 174.61411571650183, + 184.99721135581709, + 195.99771799087452, + 207.65234878997245, + 219.99999999999989, + 233.08188075904488, + 246.94165062806198, + + // O4 + 261.62556530059851, + 277.18263097687202, + 293.66476791740746, + 311.12698372208081, + 329.62755691286986, + 349.22823143300383, + 369.99442271163434, + 391.99543598174927, + 415.30469757994513, + 440, + 466.16376151808993, + 493.88330125612413, + + // O5 + 523.25113060119736, + 554.36526195374427, + 587.32953583481526, + 622.25396744416196, + 659.25511382574007, + 698.456462866008, + 739.98884542326903, + 783.99087196349899, + 830.60939515989071, + 880.00000000000034, + 932.32752303618031, + 987.76660251224882, + + // O6 + 1046.5022612023952, + 1108.7305239074892, + 1174.659071669631, + 1244.5079348883246, + 1318.5102276514808, + 1396.9129257320169, + 1479.977690846539, + 1567.9817439269987, + 1661.2187903197821, + 1760.000000000002, + 1864.6550460723618, + 1975.5332050244986, + + // O7 + 2093.0045224047913, + 2217.4610478149793, + 2349.3181433392633, + 2489.0158697766506, + 2637.020455302963, + 2793.8258514640347, + 2959.9553816930793, + 3135.9634878539991, + 3322.437580639566, + 3520.0000000000055, + 3729.3100921447249, + 3951.0664100489994, +}; + +#define MIXER_IRQ 0x5 +#define MIXER_IRQ_DATA 0x2 + +// SB16 ports +#define DSP_MIXER 0x224 +#define DSP_MIXER_DATA 0x225 +#define DSP_RESET 0x226 +#define DSP_READ 0x22A +#define DSP_WRITE 0x22C +#define DSP_READ_STATUS 0x22E +#define DSP_ACK_8 DSP_READ_STATUS +#define DSP_ACK_16 0x22F + +// TODO: ??? +#define DSP_PROG_16 0xB0 +#define DSP_PROG_8 0xC0 +#define DSP_AUTO_INIT 0x06 +#define DSP_PLAY 0x00 +#define DSP_RECORD 0x08 +#define DSP_MONO 0x00 +#define DSP_STEREO 0x20 +#define DSP_UNSIGNED 0x00 +#define DSP_SIGNED 0x10 + +#define DMA_CHANNEL_16 5 +#define DMA_FLIP_FLOP 0xD8 +#define DMA_BASE_ADDR 0xC4 +#define DMA_COUNT 0xC6 + +// commands for DSP_WRITE +#define DSP_SET_TIME 0x40 +#define DSP_SET_RATE 0x41 +#define DSP_ON 0xD1 +#define DSP_OFF 0xD3 +#define DSP_OFF_8 0xD0 +#define DSP_ON_8 0xD4 +#define DSP_OFF_16 0xD5 +#define DSP_ON_16 0xD6 +#define DSP_VERSION 0xE1 + +// commands for DSP_MIXER +#define DSP_VOLUME 0x22 +#define DSP_IRQ 0x80 + +#define SAMPLE_RATE 48000 +#define BUFFER_MS 30 + +#define BUFFER_SIZE ((size_t) (SAMPLE_RATE * (BUFFER_MS / 1000.0))) +static i16 buffer[BUFFER_SIZE]; +static bool buffer_flip = false; + +static u64 sample = 0; + +static u8 volume_master; +static u8 volumes[NUM_NOTES]; +static u8 notes[NUM_NOTES]; +static u8 waves[NUM_NOTES]; + +void sound_note(u8 index, u8 octave, u8 note) { + notes[index] = (octave << 4) | note; +} + +void sound_volume(u8 index, u8 v) { + volumes[index] = v; +} + +void sound_master(u8 v) { + volume_master = v; +} + +void sound_wave(u8 index, u8 wave) { + waves[index] = wave; +} + +static void fill(i16 *buf, size_t len) { + for (size_t i = 0; i < len; i++) { + double f = 0.0; + + for (size_t j = 0; j < NUM_NOTES; j++) { + u8 octave = (notes[j] >> 4) & 0xF, + note = notes[j] & 0xF; + + if (note == NOTE_NONE) { + continue; + } + + double note_freq = NOTES[octave * OCTAVE_SIZE + note], + freq = note_freq / (double) SAMPLE_RATE, + d = 0.0, + offset = 0.0; + + switch (waves[j]) { + case WAVE_SIN: + d = sin(2.0 * PI * sample * freq); + break; + case WAVE_SQUARE: + d = sin(2.0 * PI * sample * freq) >= 0.0 ? 1.0 : -1.0; + break; + case WAVE_TRIANGLE: + d = fabs(fmod(4 * (sample * freq) + 1.0, 4.0) - 2.0) - 1; + break; + case WAVE_NOISE: + offset = (freq * 128.0) * ((rand() / 4294967295.0) - 0.5); + d = fabs(fmod(4 * (sample * freq + offset) + 1.0, 4.0) - 2.0) - 1; + break; + } + + d *= (volumes[j] / 255.0); + f += d; + } + + buf[i] = (i16) (((volume_master / 255.0) * 4096.0) * f); + + sample++; + + // avoid double overflow errors, instead just mess up one note every + // few minutes + sample %= (1 << 24); + } +} + +static void dsp_write(u8 b) { + while (inportb(DSP_WRITE) & 0x80); + outportb(DSP_WRITE, b); +} + +static void dsp_read(u8 b) { + while (inportb(DSP_READ_STATUS) & 0x80); + outportb(DSP_READ, b); +} + +static void reset() { + char buf0[128], buf1[128]; + + outportb(DSP_RESET, 1); + + // TODO: maybe not necessary + // ~3 microseconds? + for (size_t i = 0; i < 1000000; i++); + + outportb(DSP_RESET, 0); + + u8 status = inportb(DSP_READ_STATUS); + if (~status & 128) { + goto fail; + } + + status = inportb(DSP_READ); + if (status != 0xAA) { + goto fail; + } + + outportb(DSP_WRITE, DSP_VERSION); + u8 major = inportb(DSP_READ), + minor = inportb(DSP_READ); + + if (major < 4) { + status = (major << 4) | minor; + goto fail; + } + + return; +fail: + strlcpy(buf0, "FAILED TO RESET SB16: ", 128); + itoa(status, buf1, 128); + strlcat(buf0, buf1, 128); + panic(buf0); +} + +static void set_sample_rate(u16 hz) { + dsp_write(DSP_SET_RATE); + dsp_write((u8) ((hz >> 8) & 0xFF)); + dsp_write((u8) (hz & 0xFF)); +} + +static void transfer(void *buf, u32 len) { + u8 mode = 0x48; + + // disable DMA channel + outportb(DSP_ON_8, 4 + (DMA_CHANNEL_16 % 4)); + + // clear byte-poiner flip-flop + outportb(DMA_FLIP_FLOP, 1); + + // write DMA mode for transfer + outportb(DSP_ON_16, (DMA_CHANNEL_16 % 4) | mode | (1 << 4)); + + // write buffer offset (div 2 for 16-bit) + u16 offset = (((uintptr_t) buf) / 2) % 65536; + outportb(DMA_BASE_ADDR, (u8) ((offset >> 0) & 0xFF)); + outportb(DMA_BASE_ADDR, (u8) ((offset >> 8) & 0xFF)); + + // write transfer length + outportb(DMA_COUNT, (u8) (((len - 1) >> 0) & 0xFF)); + outportb(DMA_COUNT, (u8) (((len - 1) >> 8) & 0xFF)); + + // write buffer + outportb(0x8B, ((uintptr_t) buf) >> 16); + + // enable DMA channel + outportb(0xD4, DMA_CHANNEL_16 % 4); +} + +static void sb16_irq_handler(struct Registers *regs) { + buffer_flip = !buffer_flip; + + fill( + &buffer[buffer_flip ? 0 : (BUFFER_SIZE / 2)], + (BUFFER_SIZE / 2) + ); + + inportb(DSP_READ_STATUS); + inportb(DSP_ACK_16); +} + +static void configure() { + irq_install(MIXER_IRQ, sb16_irq_handler); + outportb(DSP_MIXER, DSP_IRQ); + outportb(DSP_MIXER_DATA, MIXER_IRQ_DATA); + + u8 v = MIXER_IRQ; + if (v != MIXER_IRQ) { + char buf0[128], buf1[128]; + itoa(v, buf0, 128); + strlcpy(buf1, "SB16 HAS INCORRECT IRQ: ", 128); + strlcat(buf1, buf0, 128); + panic(buf1); + } +} + +void sound_init() { + irq_install(MIXER_IRQ, sb16_irq_handler); + reset(); + configure(); + + transfer(buffer, BUFFER_SIZE); + set_sample_rate(SAMPLE_RATE); + + u16 sample_count = (BUFFER_SIZE / 2) - 1; + dsp_write(DSP_PLAY | DSP_PROG_16 | DSP_AUTO_INIT); + dsp_write(DSP_SIGNED | DSP_MONO); + dsp_write((u8) ((sample_count >> 0) & 0xFF)); + dsp_write((u8) ((sample_count >> 8) & 0xFF)); + + dsp_write(DSP_ON); + dsp_write(DSP_ON_16); + + memset(¬es, NOTE_NONE, sizeof(notes)); + memset(&waves, WAVE_SIN, sizeof(waves)); +} diff --git a/src/sound.h b/src/sound.h new file mode 100644 index 0000000..d1f3e95 --- /dev/null +++ b/src/sound.h @@ -0,0 +1,50 @@ +#ifndef SOUND_H +#define SOUND_H + +#include "util.h" + +#define NUM_NOTES 8 + +#define NUM_OCTAVES 7 +#define OCTAVE_SIZE 12 + +#define OCTAVE_1 0 +#define OCTAVE_2 1 +#define OCTAVE_3 2 +#define OCTAVE_4 3 +#define OCTAVE_5 4 +#define OCTAVE_6 5 +#define OCTAVE_7 6 + +#define NOTE_C 0 +#define NOTE_CS 1 +#define NOTE_DF NOTE_CS +#define NOTE_D 2 +#define NOTE_DS 3 +#define NOTE_EF NOTE_DS +#define NOTE_E 4 +#define NOTE_F 5 +#define NOTE_FS 6 +#define NOTE_GF NOTE_FS +#define NOTE_G 7 +#define NOTE_GS 8 +#define NOTE_AF NOTE_GS +#define NOTE_A 9 +#define NOTE_AS 10 +#define NOTE_BF NOTE_AS +#define NOTE_B 11 + +#define NOTE_NONE 12 + +#define WAVE_SIN 0 +#define WAVE_SQUARE 1 +#define WAVE_NOISE 2 +#define WAVE_TRIANGLE 3 + +void sound_init(); +void sound_note(u8 index, u8 octave, u8 note); +void sound_master(u8 v); +void sound_volume(u8 index, u8 v); +void sound_wave(u8 index, u8 wave); + +#endif diff --git a/src/sound.o b/src/sound.o new file mode 100644 index 0000000..264cef5 Binary files /dev/null and b/src/sound.o differ diff --git a/src/speaker.c b/src/speaker.c new file mode 100644 index 0000000..ab5efc1 --- /dev/null +++ b/src/speaker.c @@ -0,0 +1,44 @@ +#include "speaker.h" +#include "fpu.h" + +// SEE: https://wiki.osdev.org/PC_Speaker +// SEE ALSO: https://web.archive.org/web/20171115162742/http://guideme.itgo.com/atozofc/ch23.pdf + +static float notes[7][12] = { + { 130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185.0, + 196.0, 207.65, 220.0, 227.31, 246.96 }, + { 261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.63, + 392.0, 415.3, 440.0, 454.62, 493.92 }, + { 523.25, 554.37, 587.33, 622.25, 659.26, 698.46, 739.99, + 783.99, 830.61, 880.0, 909.24, 987.84 }, + { 1046.5, 1108.73, 1174.66, 1244.51, 1328.51, 1396.91, 1479.98, + 1567.98, 1661.22, 1760.0, 1818.48, 1975.68 }, + { 2093.0, 2217.46, 2349.32, 2489.02, 2637.02, 2793.83, 2959.96, + 3135.96, 3322.44, 3520.0, 3636.96, 3951.36 }, + { 4186.0, 4434.92, 4698.64, 4978.04, 5274.04, 5587.86, 5919.92, + 6271.92, 6644.88, 7040.0, 7273.92, 7902.72 }, + { 8372.0, 8869.89, 9397.28,9956.08,10548.08,11175.32, 11839.84, + 12543.84, 13289.76, 14080.0, 14547.84, 15805.44 } +}; + +void speaker_note(u8 octave, u8 note) { + speaker_play((u32) notes[octave][note]); +} + +void speaker_play(u32 hz) { + u32 d = 1193180 / hz; + outportb(0x43, 0xB6); + //outportb(0x42, (u8) (d & 0xFF)); + //outportb(0x42, (u8) ((d >> 8) & 0xFF)); + outportb(0x42, 140); + outportb(0x42, 140); + + u8 t = inportb(0x61); + if (t != (t | 0x3)) { + outportb(0x61, t | 0x3); + } +} + +void speaker_pause() { + outportb(0x61, inportb(0x61) & 0xFC); +} diff --git a/src/speaker.h b/src/speaker.h new file mode 100644 index 0000000..464c370 --- /dev/null +++ b/src/speaker.h @@ -0,0 +1,10 @@ +#ifndef SPEAKER_H +#define SPEAKER_H + +#include "util.h" + +void speaker_note(u8 octave, u8 note); +void speaker_play(u32 hz); +void speaker_pause(); + +#endif diff --git a/src/speaker.o b/src/speaker.o new file mode 100644 index 0000000..fe608db Binary files /dev/null and b/src/speaker.o differ diff --git a/src/stage0.S b/src/stage0.S new file mode 100644 index 0000000..9cb289e --- /dev/null +++ b/src/stage0.S @@ -0,0 +1,203 @@ +.code16 +.org 0 + +.text + +.global _start +_start: + cli + + /* segment setup */ + mov %cs, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + + /* place stack pointer in middle of free memory area */ + movw $0x3000, %sp + + /* save drive number to read kernel later */ + mov %dl, drive_num + + sti + + /* should print TETRIS TIME */ + movw $welcome_str, %si + call print + + /* read kernel into memory at 0x10000 (segment 0x1000). + kernel binary has been placed on the disk directly after the first sector + reading $20 * num_sectors sectors after (value in %cx) + */ + movw $20, %cx + movb drive_num, %dl + movw $disk_packet, %si + movw $0x1000, segment + movw $1, sector +sector_loop: + movb $0x42, %ah + int $0x13 + jc disk_error + + addw $64, sector + addw $0x8000, offset + jnc sector_same_segment + + /* increment segment, reset offset if on different segment */ + addw $0x1000, segment + movw $0x0000, offset +sector_same_segment: + /* decrements %cx and loops if nonzero */ + loop sector_loop + + /* video mode: 320x200 @ 16 colors */ + movb $0x00, %ah + movb $0x13, %al + int $0x10 + + /* enable A20 line */ + cli + + /* read and save state */ + call enable_a20_wait0 + movb $0xD0, %al + outb $0x64 + call enable_a20_wait1 + xorw %ax, %ax + inb $0x60 + + /* write new state with A20 bit set (0x2) */ + pushw %ax + call enable_a20_wait0 + movb $0xD1, %al + outb $0x64 + call enable_a20_wait0 + popw %ax + orw $0x2, %ax + outb $0x60 + + /* enable PE flag */ + movl %cr0, %eax + orl $0x1, %eax + movl %eax, %cr0 + + /* jmp to flush prefetch queue */ + jmp flush +flush: + lidt idt + lgdt gdtp + + movw $(gdt_data_segment - gdt_start), %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + movl $0x3000, %esp + ljmp $0x8, $entry32 + +.code32 +entry32: + /* jump to kernel loaded at 0x10000 */ + movl $0x10000, %eax + jmpl *%eax + +_loop: + jmp _loop + +.code16 +enable_a20_wait0: + xorw %ax, %ax + inb $0x64 + btw $1, %ax + jc enable_a20_wait0 + ret + +enable_a20_wait1: + xorw %ax, %ax + inb $0x64 + btw $0, %ax + jnc enable_a20_wait1 + ret + +disk_error: + movw $disk_error_str, %si + call print + +/* prints string in %ds:si */ +print: + xorb %bh, %bh + movb $0x0E, %ah + + lodsb + + /* NULL check */ + cmpb $0, %al + je 1f + + /* print %al to screen */ + int $0x10 + jmp print + +1: ret + +welcome_str: + .asciz "TETRIS TIME\n" +disk_error_str: + .asciz "DISK ERROR\n" + +/* SAVED DRIVE NUMBER TO READ FROM */ +drive_num: + .word 0x0000 + +/* INT 13H PACKET */ +disk_packet: + .byte 0x10 + .byte 0x00 +num_sectors: + .word 0x0040 +offset: + .word 0x0000 +segment: + .word 0x0000 +sector: + .quad 0x00000000 + +/* GDT */ +.align 16 +gdtp: + .word gdt_end - gdt_start - 1 + /* .long (0x07C0 << 4) + gdt */ + .long gdt_start + +.align 16 +gdt_start: +gdt_null: + .quad 0 +gdt_code_segment: + .word 0xffff + .word 0x0000 + .byte 0x00 + .byte 0b10011010 + .byte 0b11001111 + .byte 0x00 +gdt_data_segment: + .word 0xffff + .word 0x0000 + .byte 0x00 + .byte 0b10010010 + .byte 0b11001111 + .byte 0x00 +gdt_end: + +/* IDT */ +idt: + .word 0 + .long 0 + +/* MBR BOOT SIGNATURE */ +.fill 510-(.-_start), 1, 0 +.word 0xAA55 diff --git a/src/stage0.o b/src/stage0.o new file mode 100644 index 0000000..2d3b984 Binary files /dev/null and b/src/stage0.o differ diff --git a/src/start.S b/src/start.S new file mode 100644 index 0000000..3c8aa4b --- /dev/null +++ b/src/start.S @@ -0,0 +1,126 @@ +.code32 +.section .text.prologue + +.global _start +_start: + movl $stack, %esp + andl $-16, %esp + movl $0xDEADBEEF, %eax + pushl %esp + pushl %eax + cli + call _main + +.section .text +.align 4 + +.global idt_load +.type idt_load, @function +idt_load: + mov 4(%esp), %eax + lidt (%eax) + ret + +.macro ISR_NO_ERR index + .global _isr\index + _isr\index: + cli + push $0 + push $\index + jmp isr_common +.endm + +.macro ISR_ERR index + .global _isr\index + _isr\index: + cli + push $\index + jmp isr_common +.endm + +ISR_NO_ERR 0 +ISR_NO_ERR 1 +ISR_NO_ERR 2 +ISR_NO_ERR 3 +ISR_NO_ERR 4 +ISR_NO_ERR 5 +ISR_NO_ERR 6 +ISR_NO_ERR 7 +ISR_ERR 8 +ISR_NO_ERR 9 +ISR_ERR 10 +ISR_ERR 11 +ISR_ERR 12 +ISR_ERR 13 +ISR_ERR 14 +ISR_NO_ERR 15 +ISR_NO_ERR 16 +ISR_NO_ERR 17 +ISR_NO_ERR 18 +ISR_NO_ERR 19 +ISR_NO_ERR 20 +ISR_NO_ERR 21 +ISR_NO_ERR 22 +ISR_NO_ERR 23 +ISR_NO_ERR 24 +ISR_NO_ERR 25 +ISR_NO_ERR 26 +ISR_NO_ERR 27 +ISR_NO_ERR 28 +ISR_NO_ERR 29 +ISR_NO_ERR 30 +ISR_NO_ERR 31 +ISR_NO_ERR 32 +ISR_NO_ERR 33 +ISR_NO_ERR 34 +ISR_NO_ERR 35 +ISR_NO_ERR 36 +ISR_NO_ERR 37 +ISR_NO_ERR 38 +ISR_NO_ERR 39 +ISR_NO_ERR 40 +ISR_NO_ERR 41 +ISR_NO_ERR 42 +ISR_NO_ERR 43 +ISR_NO_ERR 44 +ISR_NO_ERR 45 +ISR_NO_ERR 46 +ISR_NO_ERR 47 + +/* defined in isr.c */ +.extern isr_handler +.type isr_handler, @function + +isr_common: + pusha + push %ds + push %es + push %fs + push %gs + + mov $0x10, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + cld + + push %esp + call isr_handler + add $4, %esp + + pop %gs + pop %fs + pop %es + pop %ds + + popa + + add $8, %esp + iret + +.section .data +.align 32 +stack_begin: + .fill 0x4000 +stack: diff --git a/src/start.o b/src/start.o new file mode 100644 index 0000000..2fbf094 Binary files /dev/null and b/src/start.o differ diff --git a/src/system.c b/src/system.c new file mode 100644 index 0000000..f579e85 --- /dev/null +++ b/src/system.c @@ -0,0 +1,35 @@ +#include "system.h" +#include "screen.h" +#include "font.h" + +static u32 rseed = 1; + +void seed(u32 s) { + rseed = s; +} + +u32 rand() { + static u32 x = 123456789; + static u32 y = 362436069; + static u32 z = 521288629; + static u32 w = 88675123; + + x *= 23786259 - rseed; + + u32 t; + + t = x ^ (x << 11); + x = y; y = z; z = w; + return w = w ^ (w >> 19) ^ t ^ (t >> 8); +} + +void panic(const char *err) { + screen_clear(COLOR(7, 0, 0)); + + if (err != NULL) { + font_str(err, (SCREEN_WIDTH - font_width(err)) / 2, SCREEN_HEIGHT / 2 - 4, COLOR(7, 7, 3)); + } + + screen_swap(); + for (;;) {} +} diff --git a/src/system.h b/src/system.h new file mode 100644 index 0000000..d7eed35 --- /dev/null +++ b/src/system.h @@ -0,0 +1,21 @@ +#ifndef SYSTEM_H +#define SYSTEM_H + +#include "util.h" + +#define _assert_0() __error_illegal_macro__ +#define _assert_1(_e) do { if (!(_e)) panic(NULL); } while (0) +#define _assert_2(_e, _m) do { if (!(_e)) panic((_m)); } while (0) + +#define _assert(x, _e, _m, _f, ...) _f + +#define assert(...) _assert(,##__VA_ARGS__,\ + _assert_2(__VA_ARGS__),\ + _assert_1(__VA_ARGS__),\ + _assert_0(__VA_ARGS__)) + +void panic(const char *err); +u32 rand(); +void seed(u32 s); + +#endif diff --git a/src/system.o b/src/system.o new file mode 100644 index 0000000..38592ba Binary files /dev/null and b/src/system.o differ diff --git a/src/timer.c b/src/timer.c new file mode 100644 index 0000000..549fe83 --- /dev/null +++ b/src/timer.c @@ -0,0 +1,48 @@ +#include "timer.h" +#include "isr.h" +#include "irq.h" + +#define PIT_A 0x40 +#define PIT_B 0x41 +#define PIT_C 0x42 +#define PIT_CONTROL 0x43 + +#define PIT_MASK 0xFF +#define PIT_SET 0x36 + +#define PIT_HZ 1193181 +#define DIV_OF_FREQ(_f) (PIT_HZ / (_f)) +#define FREQ_OF_DIV(_d) (PIT_HZ / (_d)) +#define REAL_FREQ_OF_FREQ(_f) (FREQ_OF_DIV(DIV_OF_FREQ((_f)))) + +static struct { + u64 frequency; + u64 divisor; + u64 ticks; +} state; + +static void timer_set(int hz) { + outportb(PIT_CONTROL, PIT_SET); + + u16 d = (u16) (1193131.666 / hz); + outportb(PIT_A, d & PIT_MASK); + outportb(PIT_A, (d >> 8) & PIT_MASK); +} + +u64 timer_get() { + return state.ticks; +} + +static void timer_handler(struct Registers *regs) { + state.ticks++; +} + +void timer_init() { + const u64 freq = REAL_FREQ_OF_FREQ(TIMER_TPS); + state.frequency = freq; + state.divisor = DIV_OF_FREQ(freq); + state.ticks = 0; + //timer_set(state.divisor); + timer_set(TIMER_TPS); + irq_install(0, timer_handler); +} diff --git a/src/timer.h b/src/timer.h new file mode 100644 index 0000000..0980c0e --- /dev/null +++ b/src/timer.h @@ -0,0 +1,12 @@ +#ifndef TIMER_H +#define TIMER_H + +#include "util.h" + +// number chosen to be integer divisor of PIC frequency +#define TIMER_TPS 363 + +u64 timer_get(); +void timer_init(); + +#endif diff --git a/src/timer.o b/src/timer.o new file mode 100644 index 0000000..2858620 Binary files /dev/null and b/src/timer.o differ diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..87373c4 --- /dev/null +++ b/src/util.h @@ -0,0 +1,202 @@ +#ifndef UTIL_H +#define UTIL_H + +// fixed width integer types +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef char i8; +typedef short i16; +typedef int i32; +typedef long long i64; +typedef u32 size_t; +typedef u32 uintptr_t; +typedef float f32; +typedef double f64; + +typedef u8 bool; +#define true (1) +#define false (0) + +#define NULL (0) + +#define CONCAT_IMPL(x, y) x##y +#define CONCAT(x, y) CONCAT_IMPL(x, y) + +#define __MIN_IMPL(_x, _y, _xn, _yn) __extension__({\ + __typeof__(_x) _xn = (_x);\ + __typeof__(_y) _yn = (_y);\ + (_xn < _yn ? _xn : _yn);\ + }) +#define MIN(_x, _y) __MIN_IMPL(_x, _y, CONCAT(__x, __COUNTER__), CONCAT(__y, __COUNTER__)) + +#define __MAX_IMPL(_x, _y, _xn, _yn) __extension__({\ + __typeof__(_x) _xn = (_x);\ + __typeof__(_y) _yn = (_y);\ + (_xn > _yn ? _xn : _yn);\ + }) +#define MAX(_x, _y) __MAX_IMPL(_x, _y, CONCAT(__x, __COUNTER__), CONCAT(__y, __COUNTER__)) + +#define CLAMP(_x, _mi, _ma) (MAX(_mi, MIN(_x, _ma))) + +// returns the highest set bit of x +// i.e. if x == 0xF, HIBIT(x) == 3 (4th index) +// WARNING: currently only works for up to 32-bit types +#define HIBIT(_x) (31 - __builtin_clz((_x))) + +// returns the lowest set bit of x +#define LOBIT(_x)\ + __extension__({ __typeof__(_x) __x = (_x); HIBIT(__x & -__x); }) + +// returns _v with _n-th bit = _x +#define BIT_SET(_v, _n, _x) __extension__({\ + __typeof__(_v) __v = (_v);\ + (__v ^ ((-(_x) ^ __v) & (1 << (_n))));\ + }) + +#define PACKED __attribute__((packed)) + +#ifndef asm +#define asm __asm__ volatile +#endif + +#define CLI() asm ("cli") +#define STI() asm ("sti") + +static inline u16 inports(u16 port) { + u16 r; + asm("inw %1, %0" : "=a" (r) : "dN" (port)); + return r; +} + +static inline void outports(u16 port, u16 data) { + asm("outw %1, %0" : : "dN" (port), "a" (data)); +} + +static inline u8 inportb(u16 port) { + u8 r; + asm("inb %1, %0" : "=a" (r) : "dN" (port)); + return r; +} + +static inline void outportb(u16 port, u8 data) { + asm("outb %1, %0" : : "dN" (port), "a" (data)); +} + +static inline size_t strlen(const char *str) { + size_t l = 0; + while (*str++ != 0) { + l++; + } + return l; +} + +static inline char *itoa(i32 x, char *s, size_t sz) { + // TODO: holy god this is bad code we need some error handling here + if (sz < 20) { + extern void panic(const char *); + panic("ITOA BUFFER TOO SMALL"); + } + + u32 tmp; + i32 i, j; + + tmp = x; + i = 0; + + do { + tmp = x % 10; + s[i++] = (tmp < 10) ? (tmp + '0') : (tmp + 'a' - 10); + } while (x /= 10); + s[i--] = 0; + + for (j = 0; j < i; j++, i--) { + tmp = s[j]; + s[j] = s[i]; + s[i] = tmp; + } + + return s; +} + +static inline void memset(void *dst, u8 value, size_t n) { + u8 *d = dst; + + while (n-- > 0) { + *d++ = value; + } +} + +static inline void *memcpy(void *dst, const void *src, size_t n) { + u8 *d = dst; + const u8 *s = src; + + while (n-- > 0) { + *d++ = *s++; + } + + return d; +} + +static inline void *memmove(void *dst, const void *src, size_t n) { + // OK since we know that memcpy copies forwards + if (dst < src) { + return memcpy(dst, src, n); + } + + u8 *d = dst; + const u8 *s = src; + + for (size_t i = n; i > 0; i--) { + d[i - 1] = s[i - 1]; + } + + return dst; +} + +// SEE: https://opensource.apple.com/source/Libc/Libc-1158.30.7/string/strlcat.c.auto.html +static inline size_t strlcat(char *dst, const char *src, size_t size) { + const size_t sl = strlen(src), + dl = strlen(dst); + + if (dl == size) { + return size + sl; + } + + if (sl < (size - dl)) { + memcpy(dst + dl, src, sl + 1); + } else { + memcpy(dst + dl, src, size - dl - 1); + dst[size - 1] = '\0'; + } + + return sl + dl; +} + +static inline size_t strlcpy(char *dst, const char *src, size_t n) { + // copy as many bytes as can fit + char *d = dst; + const char *s = src; + size_t size = n; + + while (--n > 0) { + if ((*d++ = *s++) == 0) { + break; + } + } + + // if we ran out of space, null terminate + if (n == 0) { + if (size != 0) { + *d = 0; + } + + // traverse the rest of s + while (*s++); + } + + return s - src - 1; +} + +#endif