From 862e24969c946dba32ef348db68095ace7c204a5 Mon Sep 17 00:00:00 2001 From: jdah <20308439+jdah@users.noreply.github.com> Date: Mon, 15 Mar 2021 14:19:55 +0100 Subject: [PATCH] Literally everything post-sound, pre-music Everything --- .gitignore | 59 ++++ Makefile | 52 ++++ bin/bootsect.bin | Bin 0 -> 512 bytes bin/kernel.bin | Bin 0 -> 37152 bytes boot.iso | Bin 0 -> 1474560 bytes src/font.c | 158 ++++++++++ src/font.h | 21 ++ src/font.o | Bin 0 -> 5472 bytes src/fpu.c | 15 + src/fpu.h | 8 + src/fpu.o | Bin 0 -> 2288 bytes src/idt.c | 39 +++ src/idt.h | 10 + src/idt.o | Bin 0 -> 4492 bytes src/irq.c | 82 ++++++ src/irq.h | 10 + src/irq.o | Bin 0 -> 7628 bytes src/isr.c | 172 +++++++++++ src/isr.h | 16 + src/isr.o | Bin 0 -> 7396 bytes src/keyboard.c | 66 +++++ src/keyboard.h | 87 ++++++ src/keyboard.o | Bin 0 -> 5876 bytes src/link.ld | 29 ++ src/main.c | 743 +++++++++++++++++++++++++++++++++++++++++++++++ src/main.o | Bin 0 -> 57268 bytes src/math.c | 46 +++ src/math.h | 15 + src/math.o | Bin 0 -> 3604 bytes src/music.c | 359 +++++++++++++++++++++++ src/music.h | 9 + src/screen.c | 41 +++ src/screen.h | 53 ++++ src/screen.o | Bin 0 -> 6384 bytes src/sound.c | 355 ++++++++++++++++++++++ src/sound.h | 50 ++++ src/sound.o | Bin 0 -> 24328 bytes src/speaker.c | 44 +++ src/speaker.h | 10 + src/speaker.o | Bin 0 -> 5244 bytes src/stage0.S | 203 +++++++++++++ src/stage0.o | Bin 0 -> 1772 bytes src/start.S | 126 ++++++++ src/start.o | Bin 0 -> 18764 bytes src/system.c | 35 +++ src/system.h | 21 ++ src/system.o | Bin 0 -> 4156 bytes src/timer.c | 48 +++ src/timer.h | 12 + src/timer.o | Bin 0 -> 4452 bytes src/util.h | 202 +++++++++++++ 51 files changed, 3196 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100755 bin/bootsect.bin create mode 100755 bin/kernel.bin create mode 100644 boot.iso create mode 100644 src/font.c create mode 100644 src/font.h create mode 100644 src/font.o create mode 100644 src/fpu.c create mode 100644 src/fpu.h create mode 100644 src/fpu.o create mode 100644 src/idt.c create mode 100644 src/idt.h create mode 100644 src/idt.o create mode 100644 src/irq.c create mode 100644 src/irq.h create mode 100644 src/irq.o create mode 100644 src/isr.c create mode 100644 src/isr.h create mode 100644 src/isr.o create mode 100644 src/keyboard.c create mode 100644 src/keyboard.h create mode 100644 src/keyboard.o create mode 100644 src/link.ld create mode 100644 src/main.c create mode 100644 src/main.o create mode 100644 src/math.c create mode 100644 src/math.h create mode 100644 src/math.o create mode 100644 src/music.c create mode 100644 src/music.h create mode 100644 src/screen.c create mode 100644 src/screen.h create mode 100644 src/screen.o create mode 100644 src/sound.c create mode 100644 src/sound.h create mode 100644 src/sound.o create mode 100644 src/speaker.c create mode 100644 src/speaker.h create mode 100644 src/speaker.o create mode 100644 src/stage0.S create mode 100644 src/stage0.o create mode 100644 src/start.S create mode 100644 src/start.o create mode 100644 src/system.c create mode 100644 src/system.h create mode 100644 src/system.o create mode 100644 src/timer.c create mode 100644 src/timer.h create mode 100644 src/timer.o create mode 100644 src/util.h 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 0000000000000000000000000000000000000000..afe78312c02034fd85b374cf184f4dafc4ba6f51 GIT binary patch literal 512 zcmey>bE5A?-+{gdeJ}bh>|rqI5c^j1d*9uf7h4#1iZFDEeXH5`qvklhJoSH-7O3ogwG26dJ)R7;li_&7m*Bx z2c9GZyl`XKaPe8n3wwr$<`YcM68IGkq&1&lb;&kXL0P1La?*IS* literal 0 HcmV?d00001 diff --git a/bin/kernel.bin b/bin/kernel.bin new file mode 100755 index 0000000000000000000000000000000000000000..8a974262810d054588dfb458d96fe7391e3a531b GIT binary patch literal 37152 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 literal 0 HcmV?d00001 diff --git a/boot.iso b/boot.iso new file mode 100644 index 0000000000000000000000000000000000000000..0b92f22605fba4ad237d4a2806501180e1a9d289 GIT binary patch literal 1474560 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 + { 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 0000000000000000000000000000000000000000..4832b541d61dc53a3646f67af7a2e4a00eef323d GIT binary patch literal 5472 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..409d0d2ac7a481cfc12a906637ad308e166f1b43 GIT binary patch literal 2288 zcmb<-^>JflWMqH=Mh0dE1doB?2P;Iz1Wai#2r~#V@U!tN9B6*_I{-?0g`KgMBTCVr07wqL^5@7#SE?SXeXjN*I_pzz$-S2FdX98iObn78XVZ z1||-c2Mi1hY)2Rv7#Nw^K$d}QWSqlZ!obMFlgYrqzyT5jV@5$nRX$;EWz{S9(I3HvH2+M$c3}Qe78pH(o6GVe>8YoN|v_PgX{^sLlV1&djBLf3t zaZxe@Ls~(pUNR_(OG`3y^fEv!23A1^Rwj^U5mr`V5q(=$b~Z*rsd`97G$PE7{w)t$=SLEMfoME$tC$k5W%F<%$$eV6>?(%jU%l4AXm)RLmiV%_{=1_pO$XDbB_cfU|gNT4u);usY2 zj8#Dlj5QpL(mX6I9gGYN0t^fcpr{7PAxAHW55v%W1?I9aeEbh)Knc`b%)r3R0FGd& zEQrqxXM-5bNXZGp2j?IV6O5S|*ugZ6U`EPJATeeJE)WIB%nUqmHi*H@0Ls}Q76dah z@WM?5`A-Ox5Fx<~j}wp!7}*%WCNXj`Fo5y|h!zDUZY~A}Nf6D%z@P}CIT;uAexDR0h9+B*%=rXgZS(W3}E-MLgI>% ziGjfqB+kUZpa9CEpy1XkuFNe-Ok&V0E-8Z088B8!YDEcyUP@v~B7V(LD@-g(%u7#2(VLT*mkLu|TvEiKmz1k^K3wVyKw$~e0L376E1_%<1v5{Ffq{XOfq}sRB*?(PFo%VKVF3dqPC#x0VNfY+ zgJ#}$1_p)_s2&gnGtUc6T{a^FgBwTz6vNa-qN&@+2#I&7GEiy;GegnbX9GcCNFB)E zATwcRf%L6mW?%qS-=I9s3vES!`~-?ySXu$8>tTV!Er<=mAag)8NFTDg7*+;`Ss)20 HhN%Mpcrp8< literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..09316536415a25fe7401b68b16cc639a25734e61 GIT binary patch literal 4492 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..da1420c536c5fec29e9ae5e3f67d500a3205fe06 GIT binary patch literal 7628 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..5ac82485155562e4018f00446a6f3569b0bc7b55 GIT binary patch literal 7396 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', '?', 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 0000000000000000000000000000000000000000..3824b8a3c497eaf98d5579b142fea898d2a3e393 GIT binary patch literal 5876 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 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 0000000000000000000000000000000000000000..a3c8dc379a4983c4eb1d0a18505386394160389d GIT binary patch literal 57268 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 T`%yqFD89|Wa2m=4Q7|n4yRtp= literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e0ce7bd5f6eef3a9c7473abb2195ddbc3a634d04 GIT binary patch literal 3604 zcmb<-^>JflWMqH=Mh0dE1doAX2^U1h1Wai#h%tyV+;vf5x%uwK&HE3&9dRvXy7}O6 z+Jw63tNMBOCPa+o=D))en&0r;)&=Rmw=jJ9zWt%@;TS-o1J6_4 z3=Aw_CJUn~0|NukZw3YiC8!Sy!4eD%Ogt=Xj0_A+94rqR7#NrsIgW!UCiWK~iiMpI zL~*ch08xx=S3wjLs{|th12YS2W?l&c69+5EASM{C9BLd-Yu;-VSFfht;g1I1bL6|i; zznFnh9BhyQ0|SF30|Nu&97RS329QDGFoQTiBE}#&kS0b69!?`hh+Ffi~GXXY_53W7~$^kigU;H_X_6a*W{cMD{a5ZEOQ{0blrJ6N3v0|SE) zh&yKz)CKH(VCPO`V9RA-WEX{SI0V23aDWZSU|?WK16j#04bsUBHVDLr7_{Rfq{(` zNimZI0|UDWvViz@CvgS_4p~M+;ULBxPTZm55!{j7;oJ-ioU9Cn!b}i(E^fw9aRvr% z6NC^Cx~&S_AnSOMtz%%|GhtxnLpYTmtO+WB;Zy-+#Za3C^%xBi9%5h+0=r5A9AE_u z3=E)j&&UuB=7R#4fq@Z}XdDyBN+%u=xSi2;kGa^=rJ%b=%WhCfQVW@8zTb~3*#DA5XGX;D#2#Q$|@|(AlsO;G@G(h`%3 z8Pam|QyAR+LKU114HYc)jPwi?baRc36m)Y-O7l`}GmS0G6m)YFi;^>->~sZPes)+AtI7KV@i z!3-#YT2q1&EHhLFLNP;YCI}BC53WaGTn1(aHaHu^U}oR|QE<%6zzJuA7|f6o9>fG= zW(FQG4I`KtcwtO1&CI|LreOp#gCL9vrkNQ8;5LH7Pl$mLYyv2}V0;h_3rmo^7*aTb z_)p&eU@!$`X;ucX&l%Ynz&>E)VF0@fM1$%v5DkhaMkWRZ zMUZ-KNPL3C!Q~4N1A`ezoQHwI7D|KS93<}zwJVAuzuIT#oY zLFvmNnu&qoGnD=TqPZ9tSU^P&2Ll5Kl;#D|Tnr3iAew`LK?+LSf@mHF1}`Wb3Zj`9 z80tVY4+BFdh~{8m=!MdgK{O8o!#ohp#K5p0N?!xf91IM%pfo6@FtS1N0VrIoK<;8< zU;x$HOiT<6p!OMv_5c;QObiS$P#RQ{gTxv1iYs$V5|bG8ic5+hbOwx7l$xVgl3G#1 zpqG+ZlE|Q!R9wuUSCkLp=p`4I7@9HYfs~}ACY7egXXd5lGw4CsiAhOCsbw%;PJS}7 z!o;G)y!6y!m@JHkFexW9FBK+RTvEiKmzSDSgghur7$9j2 zqyrQt$dwJO3Ia7yKsgIk>44M~fZ~;bfk7CmAEXYH6Tz)Ha76@C#lWx>LcvK`*#N4h zKwBYU}s=(U}ON- uk)VFm(X`-l};3 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c0582b0fc7623449f19f49c0a611740f9f65e939 GIT binary patch literal 6384 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 LSOG{5gkkCcR=?PV literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..264cef588f24907e34e2c06de569b69a15ce0ec5 GIT binary patch literal 24328 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..fe608db340b0fc6026e403d238e8e8e3f90554fc GIT binary patch literal 5244 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..2d3b984fb34dbba343120d652916e81437df3be0 GIT binary patch literal 1772 zcmb<-^>JflWMqH=Mh0dE1doAX4J$;(1Wai#a4@igSuo;P&xyVpeFyp;^u6f2u!q5* zL+l&F?|pX}UTk65DZnqjqRKTh6~S9UPLk&9(a-v@WPE@!^LMQFYFm2nolr2 zOW;>Hkk)*HkzeV+YX*KsISWR9Mlk`#9Rdts_ds2gwuixhf#KCE1`dWD3=E76{~x^m zXL#UA3jeMLj78rLLzxW4-wyA4#qffG!QlTEzBM)sB`jwJUVlCu;u;d<8LSZE>FdhH z;NltVt>79IK1tnSlkI*1o|-85y`47#Qv%@x>V!7!aYw$RNwW!0-b} zehvcz!#^axCWsABz>J{SXD|ebfiWY44FdziNiZ8kFfzC^Fff2p9f$`?Abt!C3_%bM z1Bjo*z`(E>iC@gXz_1UA-@?Ga(1yhCVqjpfK(fCNlva@VlNlHo43PLs7#P4Q24Vyw z!#V~A21KATGMF(iFl>ZKg59%?fq~%_68|Uz1H&&Q{zWwVZ=#w1h=GAY1Ihm7aJ`^3 z&B(xzQj}Si8lP91%TS)0lboNM8ed#e#86O_nODM)l3AP`Uyzubom#?BoSIycUj(J% zbMo^GAkwKtMfpVx`Dtmza7FROi6CQA({oevN*Ge}5|eUL;}eYx;>!~=OAOEj3>ngL zN{cfXGE+(z(o;$bKty~>Vo73rG1P?gl#=-3lEfmAStUi4#zsgsgI$|fni~&sUolvB zYF-MM&MVEy0W*^GQ&Qo^F$gg*Fo-ZRFo;2E1t_fyrPZOdE|j*0(x4Imq|XD&2c;bl zzZ1#_#XX3>49Y(RrG*$7KqVmqgI;lEZb@PigI;k-5rocwv5Hc2^h#1IN*MG&UT4rt zDh8!Iq{zaNc2JAoB%S i7#Kc-7*Gr{2gDR)0F^Bu76jL@GJx|vga^vS5EcNZR0NLz literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..2fbf094b4e52e42acbd0eeddd70f564585e24216 GIT binary patch literal 18764 zcmb<-^>JflWMqH=Mh0dE1doAXk2^%h1Wai#a4~Q)bi1gq@H0vr{*}d$#qe?>BLf47 z#rU!v%3^w13uQ6CEQYdJUS>jBtS{rCEVh@SP!{`3Z!nAFr8Agj$l`oy31)G2)-SQhpr*qEue{f+*G3ZXinSwLOSZe{BJxG+rBmD9zVeAWG}C zGKkWCEd!!-UW^@Iop24)6k z6dlYAENFaI$e9ujFkxl}b_NE9#qfhB7#W0MQec{yK@Lnq2muCG22}_XObReCFld8W z5JHH7kpXl*1qX!7Ai%)LV8y@yHxtAMSuKMk@5aEupoYZvVPIe|K;j26Fo4Yf34!!S zFfcH*M&f%iGB6Y&@dFqc z7%GwY;fxFn4M_YrMg|7Z`6-}a0)x|7oR^AA z5|gtTGK-7ilk;6v*9@x>*HMI{XJxrv#144Ek<@j3a4DGc$M#YF~S z(hy77CA*L8ZOfiO-VgfP61Y(K_#1s>VDJBq8OdzJ1Kuj?)2M2jZVqQv4Dky*NWMp7q zvSt96e~e5F41x>{3{0F13=De3mANH}Nep_$B}EWA1I8*!&Cx4Kttes8OGzwAWY9}0 zE@se!3F;LT<>%z5m!^VZnm8=a0I{D9svJ~So<>S9u+mzZfq?~#xH>1O1e5}q0b+vO17gG6$Kb@k zAPSOzVvrn&2`ZnF-Iw7GDvKGwbqOm21Gv@!*#}Y&auP@n2unCIFdSfp_yr^n@&~96 o0htAN8v_Hw4Oa$+HjrkJCTN&}n4maAR#)N9z~Bs$fMS>y07> 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 0000000000000000000000000000000000000000..38592ba56deaf61eb5a9636d7a1dd989a5ec7736 GIT binary patch literal 4156 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..28586203b501e36bf9a0b0751a7b1dd196cbd840 GIT binary patch literal 4452 zcmb<-^>JflWMqH=Mh0dE1doBig%2WQ0;V(=L>Pn_npp)H7#J9vMMXe@hbJrqal661 z!_99*jKFbd`1Qa zc1@5xJEs@OSsZD_49x5zAT|p}Du^usVsmh$fY>r1HV;Q?N+ts{2UvpuM`|&M!v~TP z;Ydv?U|{AD25}@fQj0+xuznei)T9aqW)4}9i~>h$3W%cw;;3+>CWAQYAdUt{Y9ffE z1LEkg*MJp5|y3iU>Fsbl@g!w_|sXJB9uJq8k0Ucdo*I5PGn5*M||(!5mLOk)c(1>M}lqT~!HJ6%E7-$+5XxFp3k z+0alyH!ZCwHMO`TF)t-EFI_=5EiYfUATt%hC@x7%&ekm`$}dSxF3B%~2qu+g=9FaS z>86$DC6{F8=b?x)lw>An7c-<4r52W^<|S7$WEK_1XXX``BzAaK6lE6c<`+Z4K0ZCQ1gxE*C^fy9p|~WmB$c7GIJF2=W2NQfCzddz zB$gyX6=&vUmN1m&6=$aBrKTt(XCxLuC5lr^7>YCUi%JyWvY;9_K0Y%&FTW@?g`v18 zSsxbJDfy*IIjIamsp*-;C8=|G~E3{H6hW+0Bg)J zRs}II)^IRN^RTdVFfuTJ%4$$D2PJ1v84sdCVxYnfW(IKU4#s6*W?+Z2K@4UFP(u&If?#I2=`cPwgaIX)8NgK@ zlm~Jj$Q5ud12Y33gDe9i>_Fio#K6d)jubW^KBxkR=>UZzBqix3Luh!6K~f|n7C~th z!U88m?z4EP(RYLTONY6(k-EDnxl07}}upN+`VzN*{*O7oqe$5Y58C z@EAn%FfhCV(M${sKS4AP0|Pe`0|Usv0#I57L~}AQXh3O0C~XR*?LagW1A{Y^4hPYk z3=GjwIt5B+Kih zD4heP!ST-yNzVFI81%q}t6oxZF@qjRFeNpqG(A2uFD;)z55i7NN-9b%gYk0mlaUoB7A5AT zr=sZ1$;?ZIsV*)lV$e&@&jpqI5N)X$@o7bgxv8*>M+7Yo@+Si%{ezT){0VA1gV-Pp zE83(PAbHOLB*?(PAi~YS0BUf9^9i_R!vHR2K&@3!*#|4pRzuapD3Bb84a%pWQVFJR z4X9NNl7M2Ey&(6%^nj#5>Pldmpl%0=gUV}AnF(`W38+Y8g5+aR=?F3pWFJT#**pan z28Int0x+f@lGz~hFR(H&>;y5O7^V)?-iNV4Qk`s&Gy`ITFiZ|qKA^ad7n0UM;Rh0j zxvv1t-j!?&4A(#gK{3p{9JKgo;e~`h$UKnOVdm99%>#uG$cTD&1_n^s0b+wN$UG2k zM^op-$-p24QUJvuH6RQwzd(5aDjvcKNe56ayk?q!=Dr?YNVgB _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