PLATFORM := $(shell uname)
DEBUG ?= false # lowers optimization levels and increases command verbosity

ifeq ($(PLATFORM), Darwin) 
CC := x86_64-elf-gcc
LD := x86_64-elf-ld
OBJCOPY := x86_64-elf-objcopy
GDB := x86_64-elf-gdb
else
CC := gcc
LD := ld
OBJCOPY := objcopy
GDB := gdb
endif

ifeq ($(DEBUG), true)
DEBUG_CFLAGS := -g3 -O0
DEBUG_NASM_FLAGS := -O0
DEBUG_QEMU_FLAGS := -monitor stdio
DEBUG_LFLAGS := -g
else
DEBUG_CFLAGS := -Os
DEBUG_NASM_FLAGS := -Ox
DEBUG_QEMU_FLAGS :=
DEBUG_LFLAGS :=
endif

QEMU_GDB_TIMEOUT ?= 10 # num. seconds to wait for qemu to start OS

export CC
export LD
export OBJCOPY
export DEBUG_CFLAGS
export DEBUG_NASM_FLAGS
export DEBUG_QEMU_FLAGS

export CFLAGS := -Wall -Werror $(DEBUG_CFLAGS) -Wl,--oformat=binary -no-pie -m32 -mno-mmx -mno-sse -mno-sse2 -mno-sse3 \
					-s -falign-functions=4 -ffreestanding -fno-asynchronous-unwind-tables

export LFLAGS := -melf_i386 --build-id=none $(DEBUG_LFLAGS)

ASM_BOOT_SECT_SOURCE := ./src/boot/boot_sect.asm
ASM_OS_ENTRY_SOURCE := ./src/boot/os_entry.asm

BOOT_OBJ := boot.o
OS_BIN := mOS.bin

C_FILES = $(shell find ./ -name '*.[ch]')

OBJ_NAMES := src/os/main.o src/os/test.o os_entry.o src/os/paging.o \
	src/os/hard/idt.o src/os/hard/except.o src/os/hard/pic.o \
	src/lib/device/serial.o src/lib/device/ps2.o src/lib/device/keyboard.o \
	src/lib/container/ring_buffer.o \
	src/lib/stdlib/stdio.o src/lib/stdlib/stdlib.o src/lib/stdlib/string.o \
	src/lib/pit/pit.o src/lib/device/key_handlers.o src/lib/video/VGA_text.o
.PHONY: clean qemu test

all: $(OS_BIN)
	echo DEBUG is set to $(DEBUG)

$(OS_BIN): $(OBJ_NAMES) $(BOOT_OBJ)
	$(LD) $(LFLAGS) -T link.ld $(OBJ_NAMES) -o mOS.elf
	$(OBJCOPY) -O binary mOS.elf intermediate.bin
	cat $(BOOT_OBJ) intermediate.bin > $(OS_BIN)

$(BOOT_OBJ): $(ASM_BOOT_SECT_SOURCE)
	nasm $^ -f bin -o $@ $(DEBUG_NASM_FLAGS)

os_entry.o: $(ASM_OS_ENTRY_SOURCE)
	nasm $^ -f elf32 -o $@ $(DEBUG_NASM_FLAGS)

%.o: %.c
	$(CC) -c $^ -o $@ $(CFLAGS) -I./src/lib/ -I./src/lib/stdlib/

%.o: %.asm
	nasm $^ -f elf32 -o $@ $(DEBUG_NASM_FLAGS)

qemu: $(OS_BIN)
	qemu-system-i386 $(DEBUG_QEMU_FLAGS) -boot c -drive format=raw,file=$^ -no-reboot -no-shutdown

qemu-gdb: $(OS_BIN)
	qemu-system-i386 $(DEBUG_QEMU_FLAGS) -s -S -boot c -drive format=raw,file=$^ \
		-no-reboot -no-shutdown &
	$(GDB) mOS.elf \
		-q \
		-ex 'set remotetimeout $(QEMU_GDB_TIMEOUT)' \
		-ex 'target remote localhost:1234' \
		-ex 'break os_main' \
		-ex 'continue'

qemu-gdb-boot: $(OS_BIN)
	qemu-system-i386 $(DEBUG_QEMU_FLAGS) -s -S -boot c -drive format=raw,file=$^ \
		-no-reboot -no-shutdown &
	$(GDB) mOS.elf \
		-q \
		-ix gdb_init_real_mode.txt \
		-ex 'set remotetimeout $(QEMU_GDB_TIMEOUT)' \
		-ex 'target remote localhost:1234' \
		-ex 'break *0x7c00' \
		-ex 'continue'

test: $(OS_BIN)
	cd tests && $(MAKE) clean
	cd tests && $(MAKE) test

format: $(C_FILES)
	for file in $?; do \
		clang-format -i $$file && echo "formatted $$file"; \
	done

clean:
	rm -f mOS
	rm -f *.o $(OBJ_NAMES) *.bin *.elf *.~ src/*~ src/boot/*~ docs/*~
	cd tests && $(MAKE) clean