# El compilador
CC = gcc
# Los flags del compilador
CFLAGS = -std=c11 -m64 -Wall -Wsign-compare -Werror $(OPTFLAGS) -I$(INCLUDE_DIR)
# Sin esto no podemos hacer call trace 
CFLAGS += -fno-omit-frame-pointer
# Omite la sección .eh_frames del ABI de x86_64 para hacer stack unwinding que no usamos por ahora.
CFLAGS += -fno-asynchronous-unwind-tables
# No tenemos protección, el stack es super volatil
CFLAGS += -fno-stack-protector
# No compilamos contra la libc ni nada, somos un kernel
CFLAGS += -ffreestanding
# No asumir que la red zone es safe por las interrupciones
CFLAGS += -mno-red-zone

# El ensamblador
NASM = nasm
# Los flags del ensamblador
NASMFLAGS = -felf64 -I$(SRC_DIR)macros/ -I$(BUILD_DIR) -w+all

# El linker
LD = ld
# Los flags del linker
LDFLAGS = -static --oformat binary -T $(LINKSCRIPT) -Map=$(KERNEL_MAP) --cref $(OPTFLAGS)
# El script del linker
LINKSCRIPT = $(BUILD_DIR)linker_script.ld

OPTFLAGS = -O2

# Directorios
OBJ_DIR = obj/
SRC_DIR = src/
INCLUDE_DIR = $(SRC_DIR)include/
BUILD_DIR = build/
IMAGE_DIR = $(BUILD_DIR)image/

# Regla para compilar archivos C
$(OBJ_DIR)%.o: $(SRC_DIR)%.c
	$(CC) $(CFLAGS) -c -o $@ $^

# Regla para compilar archivos ASM
$(OBJ_DIR)%.o: $(SRC_DIR)%.asm
	$(NASM) $(NASMFLAGS) -o $@ $^

# Los directorios de SRC_DIR con archivos .c
# Si se agrega otro, agregarla en la lista DIRS
# Y luego sus objetos en una variable con su nombre
DIRS = kernel utils multicore interrupt memory io fs syscall shell
# Los equivalentes directorios completos en OBJ_DIR 
FULL_DIRS = $(addprefix $(OBJ_DIR), $(DIRS))

# Los objetos de delirios categorizados en directorios
kernel = protected_mode.o gdt.o long_mode.o kernel.o \
		 ap_real_mode.o ap_long_mode.o
multicore = multicore_boot.o lapic.o
interrupt = cmos.o ioapic.o idt.o isr.o
memory = kmalloc.o loader.o execute.o
io = hdd.o keyboard.o screen.o pci.o
fs = block_cache.o ext2.o ext2_write.o
syscall = syscall.o core_id.o io.o timer.o
utils = utils.o print.o exception.o bitmap.o
shell = shell.o command.o

# Los objetos de delirios a compilar con su path entero
OBJS = $(foreach d, $(DIRS), $(addprefix $(OBJ_DIR)$(d)/, $($(d))))

# Agregar los objetos que no queremos tracear en el calltrace aca
NO_TRACE = 
TRACED_OBJS = $(filter-out $(addprefix $(OBJ_DIR), $(NO_TRACE)), $(OBJS)) 

# Defines
C_DEFINES = $(INCLUDE_DIR)defines.h
NASM_DEFINES = $(BUILD_DIR)defines.mac

# Tabla de símbolos
SYMBOL_SCRIPT = $(BUILD_DIR)create_symbol_table.sh
SYMBOL_TABLE = $(BUILD_DIR)symbol_table.c
SYMBOL_OBJECT = $(SYMBOL_TABLE:.c=.o)

# Kernel
KERNEL_BIN = $(HDD_ROOT)boot/delirios.bin
KERNEL_MAP = $(BUILD_DIR)delirios.map
KERNEL_IMAGE = $(BUILD_DIR)delirios.img

# Imagen de disco
HDD_IMAGE = hdd/hdd.img
HDD_ROOT = hdd/root/
HDD_POPULATE = hdd/populatefs
HDD_UUID = hdd/uuid

# Qemu
QEMU = qemu-system-x86_64

# Grub
GRUB_IMAGE = hdd/grub2.img

# Sin sufijos default de make
.SUFFIXES:

# Estos nombres no son archivos
.PHONY: all grub hdd binaries bochs qemu qemu-usb help \
		clean clean-delirios clean-grub clean-hdd

# Target para crear todo lo necesario para correr delirios
all: $(KERNEL_IMAGE) $(HDD_IMAGE)

# Target para compilar los objetos con símbolos de debugging
debug: CFLAGS += -g -DDEBUG
debug: delirios

# Target phony para delirios
delirios: clean-delirios $(KERNEL_BIN)

# Target para compilar delirios
$(KERNEL_BIN): $(FULL_DIRS) $(NASM_DEFINES) $(OBJS)
	@echo 'Creando tabla de simbolos'
	@echo ''
	$(SYMBOL_SCRIPT) $(SYMBOL_TABLE) $(INCLUDE_DIR) $(TRACED_OBJS)
	@echo ''
	$(CC) $(CFLAGS) -c -o $(SYMBOL_OBJECT) $(SYMBOL_TABLE)
	@echo 'Linkeando kernel...'
	@echo ''
	$(LD) $(LDFLAGS) -o $@ $(OBJS) $(SYMBOL_OBJECT)

# Target phony para la imagen de delirios con GRUB 
grub: clean-grub $(KERNEL_IMAGE)

# Target para crear la imagen de delirios con GRUB
$(KERNEL_IMAGE): $(GRUB_IMAGE) $(HDD_IMAGE)
	cp $(GRUB_IMAGE) $(KERNEL_IMAGE)
	# La imagen de grub esta truncada llenamos con ceros hasta 1M
	dd if=/dev/zero	of=$(KERNEL_IMAGE) bs=256k oflag=append conv=notrunc count=3
	dd if=$(HDD_IMAGE) of=$(KERNEL_IMAGE) bs=256k oflag=append conv=notrunc

# Target para convertir los defines de C a NASM 
$(NASM_DEFINES): $(C_DEFINES)
	awk '{if ($$0 ~ /^#/) print "%"substr($$0,2);}' $< > $@

# Target phony para la imagen de disco
hdd: clean-hdd binaries $(HDD_IMAGE)

# Target para crear la imagen de disco
$(HDD_IMAGE): $(KERNEL_BIN) 
	@echo 'Creando imagen de disco con formato EXT2'
	@echo ''
	# Crear el .img
	dd if=/dev/zero bs=1M count=15 of=$(HDD_IMAGE)
	# Formatearlo con EXT2
	mkfs -t ext2 -V $(HDD_IMAGE)
	# Copiar los datos de la imagen
	$(HDD_POPULATE) -q -d $(HDD_ROOT) $(HDD_IMAGE)
	# Cambiar al UUID de la imagen de grub2
	dd conv=notrunc if=$(HDD_UUID) of=$(HDD_IMAGE) bs=1 seek=1128


# Target para crear los binarios de /bin/
binaries:
	@echo 'Creando los binarios de /bin/ ...'
	cd ./test/application/; make

# Inicializar delirios en Qemu
qemu: all
	@echo 'Inicializando qemu'
	@echo ''
	$(QEMU) -m 256 -enable-kvm -smp 2 -cpu host \
			-drive if=ide,bus=0,index=0,file=$(KERNEL_IMAGE),format=raw

qemu-usb: all
	@echo 'Inicializando qemu en USB'
	@echo ''
	$(QEMU) -m 256 -enable-kvm -smp 2 -cpu host \
			-usbdevice disk:$(KERNEL_IMAGE) \
			-drive if=ide,bus=0,index=0,file=$(HDD_IMAGE),format=raw \
			-boot menu=on

# Los directorios no estan creados en el repositorio 
$(FULL_DIRS):
	mkdir -p $@

# Target para borrar lo creado en el target delirios
clean-delirios:
	@echo 'Limpiando delirios'
	@echo ''
	rm -f $(OBJS)
	rm -f $(NASM_DEFINES)
	rm -f $(SYMBOL_OBJECT)
	rm -f $(SYMBOL_TABLE)
	rm -f $(KERNEL_MAP)
	rm -f $(KERNEL_BIN)

# Target para borrar lo creado en el target grub
clean-grub: clean-delirios
	@echo 'Limpiando la imagen de grub con delirios'
	@echo ''
	rm -f $(KERNEL_IMAGE)

# Target para borrar lo creado en el target hdd
clean-hdd: clean-binaries
	@echo 'Borrando imagen de disco con formato EXT2'
	@echo ''
	rm -f $(HDD_IMAGE)

# Target para borrar lo creado en el target binaries
clean-binaries:
	@echo 'Borrando binarios de /bin/'
	@echo ''
	cd ./test/application/; make clean

# Target para borrar todo
clean: clean-grub clean-hdd

help:
	@echo 'make delirios: Compila solo el binario del kernel'
	@echo 'make grub: Compila el binario del kernel y crea la imagen con grub'
	@echo 'make binaries: Compila los binarios de /bin/'
	@echo 'make hdd: Compila los binarios de /bin/ y crea la imagen de disco'
	@echo 'make all: Todo lo anterior menos binaries'
	@echo ''
	@echo 'make clean-delirios: Borra lo creado con make delirios'
	@echo 'make clean-grub: Borra lo creado con make grub'
	@echo 'make clean-binaries: Borra lo creado con make binaries'
	@echo 'make clean-hdd: Borra lo creado con make hdd'
	@echo 'make clean: Combina los anteriores clean'
	@echo ''
	@echo 'make qemu: make all y corre qemu'
	@echo 'make qemu-usb: make all y corre qemu emulando usb'
