forked from lucianoforks/falling-block-puzzle-game-os
Literally everything
post-sound, pre-music Everything
This commit is contained in:
59
.gitignore
vendored
Normal file
59
.gitignore
vendored
Normal 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
52
Makefile
Normal 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
BIN
bin/bootsect.bin
Executable file
Binary file not shown.
BIN
bin/kernel.bin
Executable file
BIN
bin/kernel.bin
Executable file
Binary file not shown.
158
src/font.c
Normal file
158
src/font.c
Normal 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
21
src/font.h
Normal 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
BIN
src/font.o
Normal file
Binary file not shown.
15
src/fpu.c
Normal file
15
src/fpu.c
Normal 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
8
src/fpu.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#ifndef FPU_H
|
||||||
|
#define FPU_H
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
void fpu_init();
|
||||||
|
|
||||||
|
#endif
|
39
src/idt.c
Normal file
39
src/idt.c
Normal 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
10
src/idt.h
Normal 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
|
82
src/irq.c
Normal file
82
src/irq.c
Normal 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
10
src/irq.h
Normal 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
|
172
src/isr.c
Normal file
172
src/isr.c
Normal 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
16
src/isr.h
Normal 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
|
66
src/keyboard.c
Normal file
66
src/keyboard.c
Normal 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
87
src/keyboard.h
Normal 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
BIN
src/keyboard.o
Normal file
Binary file not shown.
29
src/link.ld
Normal file
29
src/link.ld
Normal 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
743
src/main.c
Normal 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
BIN
src/main.o
Normal file
Binary file not shown.
46
src/math.c
Normal file
46
src/math.c
Normal 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
15
src/math.h
Normal 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
BIN
src/math.o
Normal file
Binary file not shown.
359
src/music.c
Normal file
359
src/music.c
Normal 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
9
src/music.h
Normal 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
41
src/screen.c
Normal 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
53
src/screen.h
Normal 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
BIN
src/screen.o
Normal file
Binary file not shown.
355
src/sound.c
Normal file
355
src/sound.c
Normal 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(¬es, NOTE_NONE, sizeof(notes));
|
||||||
|
memset(&waves, WAVE_SIN, sizeof(waves));
|
||||||
|
}
|
50
src/sound.h
Normal file
50
src/sound.h
Normal 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
BIN
src/sound.o
Normal file
Binary file not shown.
44
src/speaker.c
Normal file
44
src/speaker.c
Normal 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
10
src/speaker.h
Normal 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
BIN
src/speaker.o
Normal file
Binary file not shown.
203
src/stage0.S
Normal file
203
src/stage0.S
Normal 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
BIN
src/stage0.o
Normal file
Binary file not shown.
126
src/start.S
Normal file
126
src/start.S
Normal 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
BIN
src/start.o
Normal file
Binary file not shown.
35
src/system.c
Normal file
35
src/system.c
Normal 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
21
src/system.h
Normal 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
BIN
src/system.o
Normal file
Binary file not shown.
48
src/timer.c
Normal file
48
src/timer.c
Normal 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
12
src/timer.h
Normal 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
BIN
src/timer.o
Normal file
Binary file not shown.
202
src/util.h
Normal file
202
src/util.h
Normal 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
|
Reference in New Issue
Block a user