Hi there!
I rewrote my makefile to handle most of the work automatically (before this I needed to add each file by hand).
Any way, I noticed that the linking order affects the behavior of the console "driver".
Here are the relevant code:
kmain.h
Code: Select all
#ifndef _KERNEL32_H_
#define _KERNEL32_H_
#include <boot/multiboot.h>
/*
* Kernel entry point.
*/
extern int kmain(multiboot_info_t *mbi);
#endif
Code: Select all
#include <kmain.h>
#include <io/console.h>
#include <arch/i386/cpu.h>
#include <arch/i386/gdt.h>
#include <arch/i386/idt.h>
#include <arch/i386/pic.h>
#include <arch/i386/pit.h>
#include <arch/i386/intr.h>
#include <arch/i386/paging.h>
#include <mm/pmm.h>
/* Initialize Kernel. */
static void kinitialize(multiboot_info_t *mbi);
/* Parse multiboot info */
static void kparse_multiboot_info(multiboot_info_t *mbi);
/* PIT handler function */
static void kpit_handler(intr_registers_t regs);
/* Kernel entry point */
extern int kmain(multiboot_info_t *mbi){
//Init console
console_init();
//Initialize
kinitialize(mbi);
//Enable interrupts
intr_enable();
for(;;){}
return 0;
}
/* Kernel initialize */
static void kinitialize(multiboot_info_t *mbi){
//100Hz frequency, every 10ms IRQ0 fired
const uint32_t pit_frequency = 100;
console_set_color(MAKE_COLOR(0, CL_BLACK, 1, CL_WHITE));
kprintf("Booting kernel....\n");
kprintf("Setting up GDT... ");
gdt_init();
kprintf("Done!\n");
kprintf("Setting up IDT and interrupts... ");
idt_init();
kprintf("Done!\n");
kprintf("Remapping PIC... ");
pic_init();
kprintf("Done!\n\tMaster PIC vector offset 0x%.2X\n\tSlave PIC vector offest 0x%.2X\n",
PIC_MASTER_ISR, PIC_SLAVE_ISR);
kprintf("Setting PIT channel0 to %dHz...", pit_frequency);
pit_init(pit_frequency, kpit_handler);
kprintf("Done!\n");
kprintf("Setting up Physical Memory Manager... ");
pmm_init((mbi->mem_lower + mbi->mem_upper) * 1024);
kprintf("Done!\n");
kparse_multiboot_info(mbi);
kprintf("Initializing paging... ");
pg_init();
kprintf("Done!\n");
{
uint32_t total_mem, used_mem, reserved_mem, free_mem;
pmm_get_report(&total_mem, &used_mem, &reserved_mem, &free_mem);
kprintf("\n::pmm report::\n");
kprintf("\tTotal Memory: %dKiB\n\tFree memory: %dKiB\n\tUsed Memeory: %dKiB\n\tReserved Memory: %dKiB\n",
total_mem / 1024, free_mem / 1024, used_mem / 1024,
reserved_mem / 1024);
}
}
/* Multiboot parse */
static void kparse_multiboot_info(multiboot_info_t *mbi){
//Parses the mutiboot and prints memory map
}
/* PIT handler function */
static void kpit_handler(intr_registers_t __attribute__((unused))regs){
}
Code: Select all
#include <io/console.h>
#include <arch/i386/io.h>
static cursor_loc_t _cursor_x, _cursor_y; //Cursor position
static uint16_t *const _video_memory = (uint16_t *)VIDEO_MEMORY_START;
static color_t _attributes; //Current attributes
/* Check if given char is escape char (aka \n, \t...) */
static uint8_t is_escape_char(char ch);
/* Parse escape character. */
static void parse_escape_char(char ch);
/* Helper *printf function. */
static int doprintf(const char *fmt, va_list args);
/* Update cursor position. */
static void update_cursor(void);
/* Make sure cursor is not out of bounds */
static void assert_cursor_bounds(void);
/* Scroll screen */
static void scroll_screen(void);
/* Put character to screen. */
static void put_char(char ch);
/* Get number of digits in given number. */
static uint32_t get_number_of_digits(unsigned int num, uint8_t base,
int *multiplier);
/* Put number. */
static void put_num(int num, int width, int precision, uint8_t base,
bool_t hex_in_upper);
/* Init console. */
void console_init(void){
//Set initial cursor position, and attributes
_cursor_x = _cursor_y = 0;
_attributes = MAKE_COLOR(0, CL_BLACK, 0, CL_WHITE);
//Clear the screen
console_clear();
}
/* Clear screen */
void console_clear(void){
color_t color = MAKE_COLOR(0, CL_BLACK, 0, CL_WHITE);
uint16_t i;
uint16_t blank = (uint16_t)((color << 8) | 0x20); //Space character
//Fill screen with blank characters
for(i = 0; i < CONSOLE_SCREEN_H * CONSOLE_SCREEN_W; ++i){
_video_memory[i] = blank;
}
//Update cursor position.
_cursor_y = _cursor_x = 0;
//Update cursor hardware.
update_cursor();
}
/* Set color */
void console_set_color(color_t cl){
_attributes = cl;
}
/* Set cursor location */
void console_gotoxy(cursor_loc_t x, cursor_loc_t y){
if(x < CONSOLE_SCREEN_W) _cursor_x = x;
if(y < CONSOLE_SCREEN_H) _cursor_y = y;
}
/* Kernel printf. */
int kprintf(const char *str, ...){
int ret;
va_list args;
//Prepare the arguments and call helping function
va_start(args, str);
ret = doprintf(str, args);
va_end(args);
//Update cursor
update_cursor();
return ret;
}
/* is escape char. */
static bool_t is_escape_char(char ch){
if((ch == '\n') || (ch == '\t'))
return TRUE;
return FALSE;
}
/* parse escape char */
static void parse_escape_char(char ch){
if(ch == '\n'){ //New line
_cursor_x = 0;
_cursor_y ++;
}else if(ch == '\t'){ //Tab char
_cursor_x = (uint8_t)(_cursor_x + CONSOLE_TAB_SIZE);
}
//Make sure cursor is not out of bounds.
assert_cursor_bounds();
}
/* do printf */
static int doprintf(const char *fmt, va_list args){
int count = 0; //How much we've printed?
bool_t is_specifier = FALSE; //Are we in special situation?
bool_t is_precision = FALSE; //Are we setting the precision?
bool_t some_printed = FALSE; //Have we just printed to screen?
int width, precision; //Width and precision.
width = precision = 0;
//Keep iteration on the string while we can
while(*fmt){
if(is_specifier){ //If we encountered a % in prev character
if(*fmt == 'c'){ //Handle single char print
char ch = (char)va_arg(args, int);
put_char(ch);
count ++;
some_printed = TRUE;
}else if(*fmt == 's'){ //Handle string print
char *ch = (char *)va_arg(args, int);
count += kprintf(ch);
some_printed = TRUE;
}else if((*fmt == 'd') || (*fmt == 'i')){ //decimal number
int num = va_arg(args, int);
put_num(num, width, precision, 10, FALSE);
count ++;
some_printed = TRUE;
}else if(*fmt == 'x'){ //Hexadecimal number
int num = va_arg(args, int);
put_num(num, width, precision, 16, FALSE);
count ++;
some_printed = TRUE;
}else if(*fmt == 'X'){ //Uppercase hexadecimal
int num = va_arg(args, int);
put_num(num, width, precision, 16, TRUE);
count ++;
some_printed = TRUE;
}else if((*fmt >= '0') && (*fmt <= '9')){ //Width
//Calculate with or precision
if(is_precision) precision = *fmt - '0';
else width = *fmt - '0';
is_specifier = FALSE; //Small hack to stay in specifier
//mode :)
is_precision = FALSE;
}else if(*fmt == '.'){ //Precision mode
is_precision = TRUE; //Set percision and not width
is_specifier = FALSE; //Small hack to stay in specifier
//mode :)
}else if(*fmt == '%'){ //Double % char prints one % char
put_char('%');
count ++;
}
//If we printed number, erease
if(some_printed){
some_printed = FALSE;
width = precision = 0;
}
//Inverse specifier
is_specifier = ! is_specifier;
}else{
if(is_escape_char(*fmt)){ //Check for escape character
parse_escape_char(*fmt);
}else if(*fmt == '%'){ //Check for specifier mode
is_specifier = 1; //Specifier mode
}else{ //Any other case we just print the charater it self
put_char(*fmt);
count ++;
}
}
//Move to next char
fmt ++;
}
return count;
}
/* Update cursor */
static void update_cursor(void){
//Calculate cursor location
uint16_t cur_loc = (uint16_t)(_cursor_y * CONSOLE_SCREEN_W + _cursor_x);
//Say VGA controller that we send the high cursor location
outb(CRT_DR_PORT, CMD_CUR_HI_LOC);
//And send hight cursor location
outb(CRT_IR_PORT, (uint8_t)((cur_loc >> 8) & 0xFF));
//Say VGA controller that we send the low cursor location
outb(CRT_DR_PORT, CMD_CUR_LO_LOC);
//And send low cursor location
outb(CRT_IR_PORT, (uint8_t)(cur_loc & 0xFF));
}
/* Assert cursor bounds. */
static void assert_cursor_bounds(void){
//Check X bound
if(_cursor_x >= CONSOLE_SCREEN_W){
_cursor_x = 0;
_cursor_y ++;
}
//Check Y bound
if(_cursor_y >= CONSOLE_SCREEN_H){
scroll_screen();
}
}
/* Scroll screen. */
static void scroll_screen(void){
uint16_t i;
color_t attr = MAKE_COLOR(0, CL_BLACK, 0, CL_WHITE);
uint16_t blank = (uint16_t)((attr << 8) | 0x20); //Space char
//Each line goes up
for(i = 0; i < (CONSOLE_SCREEN_H - 1) * CONSOLE_SCREEN_W; ++i){
_video_memory[i] = _video_memory[i + CONSOLE_SCREEN_W];
}
//Last line clear
for(i = (CONSOLE_SCREEN_H - 1) * CONSOLE_SCREEN_W;
i < CONSOLE_SCREEN_H * CONSOLE_SCREEN_W; ++i){
_video_memory[i] = blank;
}
//Update cursor position
_cursor_y = CONSOLE_SCREEN_H - 1;
_cursor_x = 0;
}
/* put char */
static void put_char(char ch){
_video_memory[_cursor_y * CONSOLE_SCREEN_W + _cursor_x] =
(uint16_t)((_attributes << 8) | ch);
_cursor_x ++;
assert_cursor_bounds();
}
/* Get number of digits. */
static uint32_t get_number_of_digits(unsigned int num, uint8_t base,
int *multiplier){
int mult = 1; //Multiplier
uint32_t digit_count = 1; //Digit count
//Start dividing the number by base untill we reach 0
while((num / base) != 0){
num = num / base;
mult *= base;
digit_count ++;
}
//If user requested the mutiplier, provide it to him
if(multiplier != NULL) *multiplier = mult;
//Return digit count.
return digit_count;
}
/* Print number. */
static void put_num(int num, int width, int precision, uint8_t base,
bool_t hex_in_upper){
//Array of possible digits
static const char digits[] = { '0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'a', 'b', 'c', 'd',
'e', 'f' };
int multiplier; //Multiplier
uint32_t digit_count = 0; //Number of digits in number
bool_t is_negative = FALSE; //Is the number negative?
unsigned int num_to_print;
//No support for higher base than hexadecimal yet
//@TODO add support!
if(base > 16) return;
//If number negative mark it as negative and decrease width
if(num < 0){
is_negative = TRUE;
num_to_print = (unsigned int)(-num); //Inverse the number
//For 10 base we have to decrease the width and precision
//because of minus sign before the number
if(base == 10){
width --;
precision --;
}else{ //For any other base we have to use ones complement
num_to_print = ~ num_to_print; //swap bits
num_to_print += 1; //add one
}
}else{
num_to_print = num; //In any other case its just a number
}
//Calculate number of digits
digit_count = get_number_of_digits(num_to_print, base, &multiplier);
//Left padding characters are X - digit_count
width -= digit_count;
precision -= digit_count;
//Pad the left space with blank character
while(width > 0){
put_char(' ');
width--;
}
//Only for 10 base we put a minus sign before!
if(is_negative && (base == 10)){
put_char('-');
}
//Fill left space with leading zeros
while(precision > 0){
put_char('0');
precision --;
}
//Print the number
while(multiplier != 0){
//Get most left digit
unsigned int digit = num_to_print / multiplier;
if((digit >= 10) && (hex_in_upper)){
put_char(digits[digit] - 32);
//32 = Difference between A and a in ascii table
}else{
//Print the digit
put_char(digits[digit]);
}
//Get rid of the most left digit
num_to_print = num_to_print % multiplier;
//Decrease multiplier
multiplier /= base;
}
}
I highlighted the relevant area, notice that IO_OBJECTS are placed directly after the kernel.
#------------------------------------------
# Directories
#------------------------------------------
OBJDIR = build/
KERNELSRC =
KERNELINC = include/
#------------------------------------------
# Tools
#------------------------------------------
CC = @gcc
AS = @nasm
LD = @ld
AR = @ar
PRINT = @printf
#------------------------------------------
# Flags
#------------------------------------------
CCFLAGS = -c -std=gnu99 -Wall -Wextra -Winline -Werror -nostdlib -nostdinc \
-fno-builtin -fno-stack-protector -nodefaultlibs \
-pedantic -Wshadow -Wcast-align -Wmissing-declarations \
-Wredundant-decls -Wnested-externs \
-Wstrict-prototypes -Wmissing-prototypes -Wwrite-strings \
-I$(KERNELINC)
ASFLAGS = -f elf
LDFLAGS = -Tlinker.ld
ARFLAGS = -rcs
#------------------------------------------
# Files
#------------------------------------------
ENTRY_FILES := $(wildcard $(KERNELSRC)*.c $(KERNELSRC)*.asm)
ENTRY_OBJECTS := $(ENTRY_FILES)
ENTRY_OBJECTS := $(ENTRY_OBJECTS:.asm=.o)
ENTRY_OBJECTS := $(ENTRY_OBJECTS:.c=.o)
ADT_FILES := $(wildcard $(KERNELSRC)adt/*.c)
ADT_OBJECTS := $(ADT_FILES:.c=.o)
I386_FILES := $(wildcard $(KERNELSRC)arch/i386/*.c \
$(KERNELSRC)arch/i386/*.asm)
I386_OBJECTS := $(I386_FILES)
I386_OBJECTS := $(I386_OBJECTS:.c=.o)
I386_OBJECTS := $(I386_OBJECTS:.asm=.o)
IO_FILES := $(wildcard $(KERNELSRC)io/*.c)
IO_OBJECTS := $(IO_FILES:.c=.o)
LIB_FILES := $(wildcard $(KERNELSRC)lib/*.c)
LIB_OBJECTS := $(LIB_FILES:.c=.o)
MM_FILES := $(wildcard $(KERNELSRC)mm/*.c)
MM_OBJECTS := $(MM_FILES:.c=.o)
OBJECTS = \
$(patsubst %, $(OBJDIR)%, $(ENTRY_OBJECTS)) \
$(patsubst %, $(OBJDIR)%, $(IO_OBJECTS)) \
$(patsubst %, $(OBJDIR)%, $(I386_OBJECTS)) \
$(patsubst %, $(OBJDIR)%, $(MM_OBJECTS)) \
$(patsubst %, $(OBJDIR)%, $(LIB_OBJECTS)) \
$(patsubst %, $(OBJDIR)%, $(ADT_OBJECTS))
BINARY_NAME = $(OBJDIR)kernel32.bin
#------------------------------------------
# Rules
#------------------------------------------
kernel: $(OBJECTS) kernel_link
kernel_link:
$(PRINT) "\033[0;34m[LD ]\033[0m $(BINARY_NAME) \n"
$(LD) $(LDFLAGS) $(OBJECTS) -o $(BINARY_NAME)
$(OBJDIR)%.o: %.c
@mkdir -p $(OBJDIR)$(dir $@)
$(PRINT) "\033[0;34m[CC ]\033[0m $< \n"
$(CC) $(CCFLAGS) $< -o $@
$(OBJDIR)%.o: %.asm
@mkdir -p $(OBJDIR)$(dir $@)
$(PRINT) "\033[0;34m[AS ]\033[0m $< \n"
$(AS) $(ASFLAGS) $< -o $@
image: $(BINARY_NAME)
$(PRINT) "\033[0;34mCreating Image.\033[0m"
@(cp $(BINARY_NAME) ../../obj/)
$(PRINT) "\033[0;34m.\033[0m"
@(cd ../../; sh ./install.sh)
$(PRINT) "\033[0;34m.Done!\033[0m\n"
clean:
rm -r $(OBJDIR)
This is the normal behavior: (sorry for poor quality)
Now if I change the position of IO_OBJECTS inside OBJECTS lets say like this:
Code: Select all
OBJECTS = \
$(patsubst %, $(OBJDIR)%, $(ENTRY_OBJECTS)) \
$(patsubst %, $(OBJDIR)%, $(I386_OBJECTS)) \
$(patsubst %, $(OBJDIR)%, $(MM_OBJECTS)) \
$(patsubst %, $(OBJDIR)%, $(LIB_OBJECTS)) \
$(patsubst %, $(OBJDIR)%, $(IO_OBJECTS)) \
$(patsubst %, $(OBJDIR)%, $(ADT_OBJECTS))
You see that most of the test was gone.
I hope you could help me with this!
Thanks a lot!