successivo
precedente
inizio
fine
indice generale
aiuto
indice analitico
volume
parte
TXT
HTML
PDF
pdf
gdoc
P4
Librerie specifiche generali
build.h
u0.1
inb.s
u0.2
io.h
u0.2
mboot_info.c
u0.3
mboot_show.c
u0.3
multiboot.h
u0.3
os.h
u0.4
outb.s
u0.2
vga.h
u0.5
vga_clear.c
u0.5
vga_new_line.c
u0.5
vga_printf.c
u0.5
vga_putc.c
u0.5
vga_puts.c
u0.5
vga_set.c
u0.5
vga_vprintf.c
u0.5
Dopo le librerie standard vanno predisposte anche altre librerie specifiche per il proprio sistema. Quelle descritte nelle sezioni successive sono quelle di uso generale.
File «build.h»
Il file 05/include/kernel/build.h
viene prodotto dallo script 05/makeit
, allo scopo di generare la macro-variabile BUILD_DATE contenente il momento esatto della compilazione. Durante gli esperimenti per la realizzazione del sistema è importante rendersi conto se ciò che si sta osservando corrisponde effettivamente al risultato dell'ultima compilazione oppure no. Il contenuto del file ha un aspetto simile a quello seguente:
#define BUILD_DATE "20070817191030"
|
|
Libreria «io.h»
La libreria rappresentata dal file di intestazione io.h
contiene la dichiarazione di funzioni necessarie alla comunicazione con le componenti hardware. In questo caso si utilizzano solo funzioni per riprodurre le istruzioni INB e OUTB del linguaggio assemblatore, ma potrebbe essere estesa anche con altre funzioni per istruzioni analoghe, per la comunicazione con dati di dimensione maggiore del byte.
Listato u195.2. ./05/include/kernel/io.h
#ifndef _IO_H
#define _IO_H 1
void outb (unsigned int port, unsigned int data);
unsigned int inb (unsigned int port);
#endif
|
|
Naturalmente è necessario realizzare entrambe le funzioni. È il caso di ricordare che il valore restituito dalle funzioni scritte in linguaggio assemblatore è quello contenuto nel registro EAX.
Listato u195.3. ./05/lib/io/inb.s
.globl inb
#
inb:
enter $4, $0
pusha
.equ inb_port, 8 # Primo parametro.
.equ inb_data, -4 # Variabile locale.
mov inb_port(%ebp), %edx # Successivamente si usa
# solo DX.
inb %dx, %al
mov %eax, inb_data(%ebp) # Salva EAX nella variabile
# locale.
popa
mov inb_data(%ebp), %eax # Recupera EAX e termina.
leave
ret
|
|
Listato u195.4. ./05/lib/io/outb.s
.globl outb
#
outb:
enter $0, $0
pusha
.equ outb_port, 8 # Primo parametro.
.equ outb_data, 12 # Secondo parametro.
mov outb_port(%ebp), %edx # Successivamente si usa
# solo DX.
mov outb_data(%ebp), %eax # Successivamente si usa
# solo AL.
outb %al, %dx
popa
leave
ret
|
|
Libreria «multiboot.h»
La libreria rappresentata dal file di intestazione multiboot.h
contiene semplicemente una struttura per facilitare la lettura delle informazioni più importanti che offre un sistema di avvio aderente alle specifiche multiboot; inoltre dichiara due funzioni: una per la raccolta delle informazioni e l'altra per la loro visualizzazione.
Listato u195.5. ./05/include/kernel/multiboot.h
#ifndef _MULTIBOOT_H
#define _MULTIBOOT_H 1
#include <inttypes.h>
typedef struct {
uint32_t flags;
uint32_t mem_lower;
uint32_t mem_upper;
uint32_t boot_device;
char *cmdline;
} multiboot_t;
void mboot_info (multiboot_t *info);
void mboot_show (void);
#endif
|
|
La funzione mboot_info() deve raccogliere e salvare le informazioni multiboot, all'interno della variabile strutturata os.multiboot (la variabile os complessiva è descritta nel file os.h
).
Listato u195.6. ./05/lib/multiboot/mboot_info.c
#include <kernel/multiboot.h>
#include <string.h>
#include <stdio.h>
void
mboot_info (multiboot_t *info)
{
os.multiboot.flags = info->flags;
//
if ((info->flags & 1) > 0)
{
os.multiboot.mem_lower = info->mem_lower;
os.multiboot.mem_upper = info->mem_upper;
}
if ((info->flags & 2) > 0)
{
os.multiboot.boot_device = info->boot_device;
}
if ((info->flags & 4) > 0)
{
strncpy (os.multiboot.cmdline, info->cmdline, 1024);
}
}
|
|
La funzione mboot_show() deve visualizzare direttamente le informazioni multiboot, salvate in precedenza, pertanto si avvale della funzione printf() che deve essere ancora descritta.
Listato u195.7. ./05/lib/multiboot/mboot_show.c
#include <kernel/multiboot.h>
#include <stdio.h>
void
mboot_show (void)
{
printf ("[%s] flags: %032b ", __func__,
os.multiboot.flags);
//
if ((os.multiboot.flags & 1) > 0)
{
printf ("mlow: %04X mhigh: %08X",
os.multiboot.mem_lower,
os.multiboot.mem_upper);
}
printf ("\n");
printf ("[%s] ", __func__);
if ((os.multiboot.flags & 2) > 0)
{
printf ("bootdev: %08X ", os.multiboot.boot_device);
}
if ((os.multiboot.flags & 4) > 0)
{
printf ("cmdline: \"%s\"", os.multiboot.cmdline);
}
printf ("\n");
}
|
|
File «os.h»
Il file di intestazione os.h
serve esclusivamente per definire una struttura, con la quale si crea la variabile strutturata os, accessibile a ogni parte del sistema. In questa superstruttura vengono annotate tutte le informazioni che devono essere condivise. Il senso delle varie componenti della variabile os si chiarisce successivamente; a ogni modo è importante osservare che nel sistema non vengono usate altre variabili pubbliche.
Listato u195.8. ./05/include/kernel/os.h
#ifndef _OS_H
#define _OS_H 1
#include <stdint.h>
#include <kernel/multiboot.h>
#include <stdbool.h>
#include <time.h>
typedef struct {
//
// Multiboot.
//
struct {
uint32_t flags;
uint32_t mem_lower;
uint32_t mem_upper;
uint32_t boot_device;
char cmdline[1024];
} multiboot;
//
// Stato dello schermo VGA.
//
struct {
unsigned short *video;
unsigned short columns;
unsigned short rows;
unsigned int position;
unsigned char attribute;
} vga;
//
// «os.mem_ph» Mappa della memoria fisica.
//
struct {
uintptr_t total_s; // «..._s» = start
uintptr_t total_e; // «..._e» = end.
size_t total_l; // «..._l» = limit.
uintptr_t k_text_s; // «k_...» = kernel.
uintptr_t k_text_e; //
uintptr_t k_rodata_s; //
uintptr_t k_rodata_e; //
uintptr_t k_data_s; //
uintptr_t k_data_e; //
uintptr_t k_bss_s; //
uintptr_t k_bss_e; //
uintptr_t available_s; //
uintptr_t available_e; //
} mem_ph;
//
// «os.gtd» Tabella GTD.
//
union {
struct {
uint32_t limit_a : 16,
base_a : 16;
uint32_t base_b : 8,
accessed : 1,
write_execute : 1,
expansion_conforming : 1,
code_or_data : 1,
code_data_or_system : 1,
dpl : 2,
present : 1,
limit_b : 4,
available : 1,
reserved : 1,
big : 1,
granularity : 1,
base_c : 8;
} cd;
struct {
uint32_t limit_a : 16,
base_a : 16;
uint32_t base_b : 8,
type : 4,
code_data_or_system : 1,
dpl : 2,
present : 1,
limit_b : 4,
reserved : 3,
granularity : 1,
base_c : 8;
} system;
} gdt[3];
//
// «os.gtdr» Registro GTDR.
//
// È necessario che la struttura sia compattata, in modo
// da usare complessivamente 48 bit; pertanto si usa
// l'attributo «packed» del compilatore GNU C.
//
struct {
uint16_t limit;
uint32_t base;
} __attribute__ ((packed)) gdtr;
//
// «os.idt» Tabella IDT.
//
struct {
uint32_t offset_a : 16,
selector : 16;
uint32_t filler : 8,
type : 4,
system : 1,
dpl : 2,
present : 1,
offset_b : 16;
} idt[129];
//
// «os.idtr» Registro IDTR.
//
// È necessario che la struttura sia compattata, in modo
// da usare complessivamente 48 bit; pertanto si usa
// l'attributo «packed» del compilatore GNU C.
//
struct {
uint16_t limit;
uint32_t base;
} __attribute__ ((packed)) idtr;
//
// PIT: programmable interval timer.
//
struct {
clock_t freq;
clock_t clocks;
} timer;
//
// Stato della tastiera.
//
struct {
bool shift;
bool shift_lock;
bool ctrl;
bool alt;
bool echo;
char key;
char map1[128];
char map2[128];
} kbd;
//
} os_t;
//
// Struttura pubblica con tutte le informazioni sul sistema.
//
os_t os;
#endif
|
|
Libreria «vga.h»
La libreria che fa capo al file di intestazione vga.h
è responsabile della visualizzazione del testo attraverso lo schermo.
Listato u195.9. ./05/include/kernel/vga.h
#ifndef _VGA_H
#define _VGA_H 1
#include <restrict.h>
#include <kernel/io.h>
#include <kernel/os.h>
#include <stddef.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#define vga_char(c, attrib) \
((int16_t) c | ((((int16_t) attrib) << 8) & 0xFF00))
void vga_init (void);
void vga_set (unsigned short *video, int columns,
int rows, int x, int y, int position,
int attribute);
int vga_clear (void);
void vga_new_line (void);
void vga_putc (int c);
void vga_puts (char *string, size_t n);
int vga_vprintf (const char *restrict format,
va_list arg);
int vga_printf (const char *restrict format, ...);
#define clear() (vga_clear ())
#define echo() (os.kbd.echo = 1)
#define noecho() (os.kbd.echo = 0)
#endif
|
|
Alcune macroistruzioni definite nel file vga.h
si limitano a scrivere un valore all'interno di os.kbd.echo, la quale, se attiva, rappresenta la richiesta di visualizzare sullo schermo il testo che viene digitato. La macroistruzione vga_char() assembla due valori in modo da ottenere un valore a 16 bit adatto alla visualizzazione sullo schermo di un carattere (l'unione dell'attributo di visualizzazione e del carattere stesso).
La funzione vga_init()
va usata prima di fare qualunque cosa con lo schermo VGA, per attribuire dei valori iniziali corretti alla struttura os.vga, la quale serve a tenere memoria della posizione corrente del cursore di scrittura e dell'attributo corrente da usare per i colori dei caratteri da scrivere.
Listato u195.10. ./05/lib/vga/vga_init.c
#include <kernel/vga.h>
void
vga_init (void)
{
os.vga.video = (unsigned short *) 0xB8000;
os.vga.columns = 80;
os.vga.rows = 25;
os.vga.position = 0;
os.vga.attribute = 0x07;
}
|
|
La funzione vga_set()
che appare nel listato successivo ha lo scopo di spostare e di tenere traccia della posizione corrente del cursore di scrittura, in base alle informazioni che gli vengono fornite, determinando il resto in modo predefinito. Va osservato che, quando si tratta di valori interi, per dire alla funzione vga_set() di utilizzare i dati predefiniti si trasmette un valore negativo.
Listato u195.11. ./05/lib/vga/vga_set.c
#include <kernel/vga.h>
void
vga_set (unsigned short *video,
int columns, int rows,
int x, int y,
int position,
int attribute)
{
unsigned short int current_y = os.vga.position / os.vga.columns;
unsigned short int current_x = os.vga.position - current_y * os.vga.columns;
unsigned int screen_size = os.vga.columns * os.vga.rows;
char position_high;
char position_low;
//
if (video != NULL) os.vga.video = video;
if (columns >= 0) os.vga.columns = columns;
if (rows >= 0) os.vga.rows = rows;
if (columns >= 0 || rows >= 0)
screen_size = os.vga.columns * os.vga.rows;
if (x >= 0) current_x = x;
if (y >= 0) current_y = y;
if (x >= 0 || y >= 0)
{
os.vga.position = current_y * os.vga.columns + current_x;
os.vga.position = os.vga.position % screen_size;
}
if (position >= 0)
{
//
// Ricalcola la posizione anche se è già stata determinata
// con i parametri "x" and "y".
//
os.vga.position = position % screen_size;
}
if (x >= 0 || y >= 0 || position >= 0)
{
//
// Deve riposizionare il cursore.
//
position_high = (unsigned char) (os.vga.position >> 8);
position_low = (unsigned char) os.vga.position;
//
outb (0x3D4, 0x0E);
outb (0x3D5, position_high);
outb (0x3D4, 0x0F);
outb (0x3D5, position_low);
}
if (attribute >= 0) os.vga.attribute = attribute;
}
|
|
Listato u195.12. ./05/lib/vga/vga_clear.c
#include <kernel/vga.h>
int
vga_clear (void)
{
unsigned short blank = vga_char (' ', os.vga.attribute);
unsigned int i;
unsigned int screen_size = os.vga.columns * os.vga.rows;
for (i = 0; i < screen_size ; i++)
{
*(os.vga.video + i) = blank;
}
return 0; // Per essere compatibile, in qualche modo, con «clear()».
}
|
|
Listato u195.13. ./05/lib/vga/vga_new_line.c
#include <kernel/vga.h>
void
vga_new_line (void)
{
unsigned short int current_y = os.vga.position / os.vga.columns;
unsigned short int current_x = os.vga.position - current_y * os.vga.columns;
unsigned short blank = vga_char (' ', os.vga.attribute);
unsigned int screen_size = os.vga.columns * os.vga.rows;
int i;
int j;
current_x = 0;
current_y++;
if (current_y >= os.vga.rows)
{
//
// Copia il testo in su di una riga.
//
for (i = 0, j = os.vga.columns; j < screen_size; i++, j++)
{
*(os.vga.video + i) = *(os.vga.video + j);
}
//
// Ripulisce l'ultima riga di testo.
//
for (i = screen_size - os.vga.columns; i < screen_size; i++)
{
*(os.vga.video + i) = blank;
}
current_y--;
}
vga_set (NULL, -1, -1, current_x, current_y, -1, -1);
}
|
|
Listato u195.14. ./05/lib/vga/vga_putc.c
#include <kernel/vga.h>
void
vga_putc (int c)
{
unsigned short int current_y = os.vga.position / os.vga.columns;
unsigned short int current_x = os.vga.position - current_y * os.vga.columns;
unsigned short int cell;
if (c == '\n' || c == '\r')
{
vga_new_line ();
}
else
{
cell = vga_char (c, os.vga.attribute);
*(os.vga.video + os.vga.position) = cell;
if (current_x == os.vga.columns)
{
vga_new_line ();
}
else
{
vga_set (NULL, -1, -1, -1, -1, os.vga.position + 1, -1);
}
}
}
|
|
Listato u195.15. ./05/lib/vga/vga_puts.c
#include <kernel/vga.h>
void
vga_puts (char *string, size_t n)
{
size_t i;
for (i = 0; i < n ; i++)
{
if (string[i] == 0) break;
if (string[i] != 0) vga_putc (string[i]);
}
// Non aggiunge "\n"!
}
|
|
Listato u195.16. ./05/lib/vga/vga_vprintf.c
#include <kernel/vga.h>
int
vga_vprintf (const char *restrict format, va_list arg)
{
const size_t dim = 2000; // Dimensione massima dello schermo: 25x80.
char string[dim];
int ret;
string[0] = 0;
ret = vsprintf(string, format, arg);
vga_puts (string, dim);
return ret;
}
|
|
Listato u195.17. ./05/lib/vga/vga_printf.c
#include <kernel/vga.h>
int
vga_printf (const char *restrict format, ...)
{
va_list ap;
va_start (ap, format);
return vga_vprintf (format, ap);
}
|
|
«a2» 2013.11.11 --- Copyright © Daniele Giacomini -- appunti2@gmail.com http://informaticalibera.net