Files
MonOS/tp1.md
2025-09-17 19:34:16 +02:00

6.9 KiB

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

  1. 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 :

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 :

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.

  1. 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 :

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.

  1. 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

#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

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

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));
        }
    }
}
  1. 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

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.

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