208 lines
6.9 KiB
Markdown
208 lines
6.9 KiB
Markdown
# TP 1 Mini noyeau avec affichage graphique
|
|
|
|
## Installation
|
|
|
|
OK alors je suis sur macos donc c'est un peu sepcial. Ma vm de compilation etc... est pas une vm graphique car ca serait beaucoup trop lent. Donc on ne peut pas utiliser le makefile d'origine.
|
|
|
|
J'ai mis le makefile que j'ai du fabriquer moi même sur mesure pour faire fonctionner qemu en utilisant un vnc.
|
|
|
|
Desormais pour faire un test de l'iso depuis la vm ubuntu:
|
|
|
|
`make run_vnc`
|
|
|
|
l'invite de commande qemu va s'ouvrir
|
|
|
|
`change vnc password`
|
|
|
|
ensuite on entre un password et on peut revenir sur la machine macos
|
|
|
|
`open vnc://[ip_vm]:5901`
|
|
|
|
Et normalement après avoir entré le mot de passe on a droit à un magnifique ecran noir avec un curseur qui clignotte.
|
|
|
|
Yay.
|
|
|
|
## Objectif 1 affichage graphique VBE 640x480 en 16bpp
|
|
|
|
Pour se faire on a un certain nombre de choses à modifier dans le squelette original :
|
|
|
|
1. Changer le Bootstrap pour demander à GRUB d'init le mode grahique
|
|
|
|
Dans le bootstrap original il faut ajouter les lignes suivante :
|
|
|
|
`MULTIBOOT_VIDEO_MODE equ 4 ; ask grub to switch to VBE graphics modes` le flag qui dit a GRUB d'activer le mode graphique
|
|
|
|
Changer la ligne MULTIBOOT_FLAGS en `MULTIBOOT_FLAGS equ (MULTIBOOT_ALIGN|MULTIBOOT_MEMINFO|MULTIBOOT_VIDEO_MODE)`
|
|
|
|
```
|
|
; for MULTIBOOT_VIDEO_MODE
|
|
dd 0x00000000 ; mode_type
|
|
dd 640 ; width
|
|
dd 480 ; height
|
|
dd 16 ; depth
|
|
```
|
|
|
|
Après MULTIBOOT_MEMORY_INFOS
|
|
|
|
Normalement après si on relance un test avec `make run_vnc` on a un ecran noir sans le curseur qui clignotte
|
|
|
|
2. Faire une fonction d'init qui efface l'ecran et setup les structures internes.
|
|
|
|
Maintenant on peut s'attaquer au C directement dans `kernel.c`
|
|
|
|
Dans `kernel_main` on a en argument les informations que nous donne grub au démarrage. J'ai laissé le code du prof c'est à dire ces deux lignes :
|
|
|
|
```c
|
|
multiboot_set_info(mbi);
|
|
gdt_init();
|
|
```
|
|
|
|
Honnêtement gdt_init je ne sais pas exactement ce que ca fait à part mettre des registres à 0. J'imagine que c'est une fonction d'init classique qui doit se justifier en lisant un peu plus la doc.
|
|
|
|
Multiboot_set_info c'est simplement pour mettre mbi en variable globale mais j'ai pas encore essayé de l'utiliser comme tel.
|
|
|
|
On va essayer d'afficher un gradient de bleu sur tout l'ecran pour tester que tout fonctionne bien.
|
|
|
|
Pour ca dans un premier temps on récupère toutes les infos :
|
|
|
|
```c
|
|
multiboot_info_t* infos_grub = multiboot_get_info();
|
|
uint16_t* fb = (uint16_t*)infos_grub->framebuffer_addr;
|
|
uint32_t pitch = infos_grub->framebuffer_pitch;
|
|
uint32_t width = infos_grub->framebuffer_width;
|
|
uint32_t height = infos_grub->framebuffer_height;
|
|
uint32_t bpp = infos_grub->framebuffer_bpp;
|
|
```
|
|
|
|
Avec ces infos en principe on peut tout faire. Attention, ici on cast le tableau framebuffer en 16 bits car on sait deja que le but est d'utiliser 16 bits par couleur. Si on veut que le code puisse marcher de manière générique il faudrait garder un tableau de 32 bits et dans la boucle faire un déalage dans une même case de tableau.
|
|
|
|
3. Fonction affichant un pixel aux coordonées (x,y) avec la couleur demandée
|
|
|
|
Pour afficher un gradient de couleur sur tout l'ecran on peut utiliser ce petit bout de code :
|
|
|
|
```c
|
|
uint32_t true_pitch = pitch/(bpp/8);
|
|
uint32_t index = 0;
|
|
for(uint32_t y = 0; y < height; y++){
|
|
for(uint32_t x = 0; x < width; x++){
|
|
index = y * true_pitch + x; // this gives a position in 1D
|
|
fb[index] = (uint16_t)y;
|
|
}
|
|
}
|
|
```
|
|
|
|
Le "true pitch" convertis le pitch qui est la largeur réelle du framebuffer en bytes en pixels avec deux divisions.
|
|
|
|
Ensuite la seule "technicité" est de convertir la position 2D en position 1D pour le framebuffer et le calcul est plutôt simple.
|
|
|
|
Ensuite la valeur a chaque pixel est la valeur de y qui rolloff ce qui nous donne un gradient degeulasse mais qui fonctionne.
|
|
|
|
4. Fonction put_pixel(color) et clear_screen(color)
|
|
|
|
Dans un premier temps on doit faire une methode qui permet de convertir une couleur 32bit en 16bit 565
|
|
|
|
```c
|
|
#define RGB(r,g,b) ( ((uint16_t)(((r) >> 3) & 0x1F) << 11) | \
|
|
((uint16_t)(((g) >> 2) & 0x3F) << 5) | \
|
|
((uint16_t)(((b) >> 3) & 0x1F)) )
|
|
```
|
|
|
|
Avec ca on peut faire une nouvelle methode put pixel
|
|
|
|
```c
|
|
void put_pixel(uint32_t x,uint32_t y,uint16_t color){
|
|
uint32_t index = y * FRAMEBUFFER_TRUE_PITCH + x; // this gives a position in 1D
|
|
FRAMEBUFFER[index] = color;
|
|
}
|
|
```
|
|
|
|
Et ensuite on peut facilement faire des fonctions pour clear l'écran ou afficher un gradient de debug
|
|
|
|
```c
|
|
void clear_display(uint16_t color){
|
|
for(uint32_t y = 0; y < SCREEN_HEIGHT; y++){
|
|
for(uint32_t x = 0; x < SCREEN_WIDTH; x++){
|
|
put_pixel(x,y,color);
|
|
}
|
|
}
|
|
}
|
|
|
|
void display_debug_gradient(){
|
|
for(uint32_t y = 0; y < SCREEN_HEIGHT; y++){
|
|
for(uint32_t x = 0; x < SCREEN_WIDTH; x++){
|
|
put_pixel(x,y,RGB(y,x,100));
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
5. Fonction similaire à printf affichant un texte formaté à arguments variables à la position
|
|
2D courante du curseur ; les formats minimum à gérer sont : %c, %s, %d et %x ;
|
|
|
|
Deja on va commencer par faire une methode qui display les caractères.
|
|
|
|
Le fonctionnement est le suivant:
|
|
|
|
On a un tableau de bytes. La font fait 8 bits de large et 16 lignes. On a donc un tableau d'octets ou chaque octet est une ligne de font.
|
|
|
|
Chaque caractère est donc composé de 16 nombres 8 bits.
|
|
|
|
Voici la methode pour dessiner un caractère
|
|
|
|
```c
|
|
void display_char(char c,uint32_t x,uint32_t y, uint16_t color){
|
|
//#define FONT_HEIGHT 16
|
|
//#define FONT_WIDTH 8
|
|
uint32_t index = c * FONT_HEIGHT;
|
|
for(int i = 0; i < FONT_HEIGHT;i++){
|
|
uint8_t row = font_8x16[index + i];
|
|
for(int j = 0; j < 8;j ++){
|
|
if (((row << j) & 128))
|
|
put_pixel(x+j,y+i,color);
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Ensuite on a le moment un peu technique ou il a fallu reprendre la methode snprintf et l'adapter.
|
|
|
|
```c
|
|
int printf(char *fmt, ...) {
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
int count = vsnprintf(CHAR_BUFFER, CHAR_BUFFER_SIZE, fmt, args);
|
|
va_end(args);
|
|
char c;
|
|
for(int i =0; i < count;i++){
|
|
c = CHAR_BUFFER[i];
|
|
if(c == '\n'){
|
|
CURSOR_X = 0;
|
|
CURSOR_Y += FONT_HEIGHT + 2; //TODO HANDLE SCROLL
|
|
}else{
|
|
display_char(c,CURSOR_X,CURSOR_Y,FONT_COLOR);
|
|
CURSOR_X += FONT_WIDTH + 2;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
```
|
|
|
|
Dans cette fonction on utilise vsnprintf pour remplir un buffer puis on itere sur ce buffer pour acceder à chaque caractère.
|
|
|
|
La subtilité est que on doit utiliser vsnprintf car en c on ne peut pas forward des paramètres infinis.
|
|
|
|
J'ai aussi mis en variables globales le buffer de caractère le curseur la couleur de font et la couleur du background.
|
|
|
|
#### TODO
|
|
|
|
Maintenant il faudrait pouvoir scroller quand on arrive trop bas et detecter les inputs clavier pour faire des retours claviers
|
|
|
|
#### TODO
|
|
|
|
Afficher les infos que j'ai pu recuperer de grub ainsi que tous les caractères imprimables possibles
|
|
|
|
|
|
|
|
|
|
|