The booting code and data permits to create MultiBoot compliant executables and to interface the application with a MultiBoot compliant boot loader.
The CPU handling code and data permits to identify the CPU type and to manage the CPU tables (GDT and IDT), while the hardware managing code permits to access some specific PC hardware (PIT and PIC).
First of all, some basic data types are defined (in ll/i386/hw-data.h):
Based on the basic types, some important structures are defined:
These are the functions provided by the hardware library to manage the CPU:
void int X86_get_CPU(struct ll_cpuInfo *p)
This function identifies the CPU present in the system and reports its characteristics in the ll_cpuInfo structure whose pointer is passed as an input. The ll_cpuInfo is described in Figure 2.2.
Basically, X86_get_CPU() checks if the CPU is a 386, a 486, or
better; in the last case, it uses the cpuid instruction to obtain
more information, otherwise it will use some custom code to determine the
manufacturer. In particular, it is based on the following functions:
void int X86_is386(void)
void int X86_isCyrix(void)
void int X86_hasCPUID(void)
It is recommended to invoke them through X86_get_CPU(), but sometime it can be useful to call one of the previous functions directly.
Similar code exists for detecting and initializing the FPU, but it still need some work.
Some other functions are provided to manage the most important CPU tables:
void GDT_place(WORD sel, DWORD base, DWORD lim, BYTE acc, BYTE gran)
This function permits to insert a new entry in the GDT; sel is the selector (equal to the entry number multiplied by sizeof(struct gdt_entry)), base and lim are the base and the limit of the segment described by the entry (and identified by sel), while acc and gran are the access and granularity bytes. They can be set using the constants defined in ll/i386/hw-data.h.
DWORD GDT_read(WORD sel, DWORD *lim, BYTE *acc, BYTE *gran)
This function permits to read an entry from the GDT, returning the base of the segment identified by the descriptor sel. Moreover, if lim, acc, and gran are not null, the limit, the access byte and the granularity of the segment are stored in *lim, *acc, and *gran.
LIN_ADDR addr2linear(WORD selector, DWORD offset)
This function can be used to translate an <selector>:<offset> address in a 32bit linear address. It uses GDT_read for obtaining the base of the segment identified by selector.
void IDT_place(BYTE num,void (*handler)(void))
This function permits to insert an entry in the IDT; num is the number of the interrupt, whereas handler is a pointer to the code that has to be specified as an handler for that interrupt.
After these very low level functionalities, the hardware support library provides some other facilities:
This function initializes a minimal programming environment, defining a standard handler for all the CPU exceptions and hardware interrupts (through IDT_place()), identifying the CPU type, initializing the FPU, setting up a TSS for executing the code, and initializing the PIC.
Moreover, it returns a pointer to the MultiBoot Information structure (see Figure 2.2).
x_exc_bind(int i, void (*f)(int n)) x_irq_bind(int i, void (*f)(int n))
These two functions permit to associate a handler to a CPU exception or to a hardware interrupt. Note that the handler is a C function, using the C conventions for passing the parameters. Since these two functions are based on the programming environment initialized by x_init(), they can be used only after that x_init() has been called.
void x_end(void) Restores the hardware settings to the DOS standards: it must be called on shutdown if the execution is expected to return to a 16bit OS.
Some other functions can be used for accessing the PIT:
int pit_init(BYTE c, BYTE m, WORD tconst)
This function initializes the PIT channel c (with c = 0, 1, or 2) in mode m, with the initial value of the counter equal to tconst. Returns a value < 0 on error (if the requested channel does not exist).
int pit_setconstant(BYTE c, WORD val)
This function sets the PIT channel c's counter to val. Returns a value < 0 on error (if the requested channel does not exist).
WORD pit_read(BYTE channel)
This function reads the value of the counter of channel.
Other functions permit to call real mode interrupts, either returning to real mode or using the VM86 mode. Some of these functions are used to access a FAT file system using DOS (they works only if the application is invoked through the eXtender). See ll/i386/x-*.h for more details.
Some other functions directly remap the corresponding ASM instructions (these functions are implemented by the xlib):
Moreover, the library provides some inline functions for reading and writing some CPU registers' values: