Looking at the header file
isrs.h in your Gitlab repo, I see that the functions in question are in the header itself; this means that they get redefined every time the header is used, which will cause this problem when the linker then has to find which function to attach to which function name. See the wiki page
Why function implementations shouldn't be put In header files for a detailed (if tongue in cheek) explanation of the topic, but the short solution is to never put the body of a non-inline function into the header itself.
In this case, since most of these functions are used in several places and are not the sort of tiny ones suited for inserting directly into code repeatedly (i.e., ones under four or five lines of code), simply declaring them as
inline probably isn't the best idea. This means you will want to extract them from the header and put them in a source file (e.g.,
isrs.c would make sense) and add them using the linker. If some of them
are used only once, the current C standard does have an
inline clause you can use to have the function calls replaced by the body of the function (I can see you've used that in your
kernel.h file), which would allow you to keep them in the header (though I would still recommend moving them, as inlining large functions can have odd side effects).
Oh, and for the exception handler, you might want to use inline assembly to insert a
hlt instruction in that loop; otherwise, the CPU will be spinning at full tilt, doing nothing but still burning energy and generating excessive heat. And you really, really don't want to declare that as an inline function, because its sole purpose is to be called implicitly by the interrupt handler in multiple places.
I can't say for certain why it isn't finding the defined macros; you do have the
terminal.h header included in both of those, but there is no such header file - you have a
terminal.c but no
terminal.h, which I would have expected to cause a 'missing file' error, too.
I suggest the following as the
terminal.h file:
Code:
#ifndef TERMINAL_H
#define TERMINAL_H
#define BLACK 0
#define BLUE 1
#define GREEN 2
#define CYAN 3
#define RED 4
#define MAGENTA 5
#define BROWN 6
#define LIGHTGRAY 7
#define DARKGRAY 8
#define LIGHTBLUE 9
#define LIGHTGREEN 10
#define LIGHTCYAN 11
#define LIGHTRED 12
#define LIGHTMAGENTA 13
#define YELLOW 14
#define WHITE 15
#define nl "\r\n"
/* you will want to remove these from 'kernel.h' anyway */
void scroll(void);
void move_csr(void);
void gotoxy(int x, int y);
void clear_screen(void);
void putch(unsigned char c);
void rawputch(unsigned char c);
void println(char *text);
void settextcolor(unsigned char forecolor, unsigned char backcolor);
void init_video(void);
unsigned char getch(void);
char *readln(char *str, int maxnumchars);
char *itoa(int val, int base);
#endif
Similarly for
timer.h:
Code:
#ifndef TIMER_H
#define TIMER_H
void timer_phase(int hz);
void timer_handler(struct regs *r);
void timer_install();
void timer_wait(int ticks);
void sleep(int seconds);
#endif
... and
serial.h:
Code:
#ifndef SERIAL_H
#define SERIAL_H
void init_serial(void);
int serial_received(void);
unsigned char read_serial(void);
int is_transmit_empty(void);
void write_serial(char a);
#endif
This makes the code more modular and fine-grained. It also means you don't need to include
kernel.h everywhere - while most of these files will still need it anyway, others you develop later might not need everything thrown into one big pile.
Finally, you can't use the normal
stddef.h and
stdint.h as defined by the compiler - those are versions designed for applications, not kernels.