Literally everything

post-sound, pre-music

Everything
This commit is contained in:
jdah
2021-03-15 14:19:55 +01:00
commit 862e24969c
51 changed files with 3196 additions and 0 deletions

59
.gitignore vendored Normal file
View File

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

52
Makefile Normal file
View File

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

BIN
bin/bootsect.bin Executable file

Binary file not shown.

BIN
bin/kernel.bin Executable file

Binary file not shown.

BIN
boot.iso Normal file

Binary file not shown.

158
src/font.c Normal file
View File

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

21
src/font.h Normal file
View File

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

BIN
src/font.o Normal file

Binary file not shown.

15
src/fpu.c Normal file
View File

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

8
src/fpu.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef FPU_H
#define FPU_H
#include "util.h"
void fpu_init();
#endif

BIN
src/fpu.o Normal file

Binary file not shown.

39
src/idt.c Normal file
View File

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

10
src/idt.h Normal file
View File

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

BIN
src/idt.o Normal file

Binary file not shown.

82
src/irq.c Normal file
View File

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

10
src/irq.h Normal file
View File

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

BIN
src/irq.o Normal file

Binary file not shown.

172
src/isr.c Normal file
View File

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

16
src/isr.h Normal file
View File

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

BIN
src/isr.o Normal file

Binary file not shown.

66
src/keyboard.c Normal file
View File

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

87
src/keyboard.h Normal file
View File

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

BIN
src/keyboard.o Normal file

Binary file not shown.

29
src/link.ld Normal file
View File

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

743
src/main.c Normal file
View File

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

BIN
src/main.o Normal file

Binary file not shown.

46
src/math.c Normal file
View File

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

15
src/math.h Normal file
View File

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

BIN
src/math.o Normal file

Binary file not shown.

359
src/music.c Normal file
View File

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

9
src/music.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef MUSIC_H
#define MUSIC_H
#include "util.h"
void music_tick();
void music_init();
#endif

41
src/screen.c Normal file
View File

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

53
src/screen.h Normal file
View File

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

BIN
src/screen.o Normal file

Binary file not shown.

355
src/sound.c Normal file
View File

@ -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(&notes, NOTE_NONE, sizeof(notes));
memset(&waves, WAVE_SIN, sizeof(waves));
}

50
src/sound.h Normal file
View File

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

BIN
src/sound.o Normal file

Binary file not shown.

44
src/speaker.c Normal file
View File

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

10
src/speaker.h Normal file
View File

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

BIN
src/speaker.o Normal file

Binary file not shown.

203
src/stage0.S Normal file
View File

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

BIN
src/stage0.o Normal file

Binary file not shown.

126
src/start.S Normal file
View File

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

BIN
src/start.o Normal file

Binary file not shown.

35
src/system.c Normal file
View File

@ -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 (;;) {}
}

21
src/system.h Normal file
View File

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

BIN
src/system.o Normal file

Binary file not shown.

48
src/timer.c Normal file
View File

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

12
src/timer.h Normal file
View File

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

BIN
src/timer.o Normal file

Binary file not shown.

202
src/util.h Normal file
View File

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