- SectorC, écrit en assembleur x86-16, est un compilateur C ultracompact qui tient dans le secteur d’amorçage (512 octets) d’une machine x86, et il prend en charge un sous-ensemble du langage C suffisant pour écrire de vrais programmes exécutables
- Il inclut notamment les variables globales, fonctions, instructions if/while, opérateurs, déréférencement de pointeurs, commentaires et assembleur inline, ce qui permet d’exécuter des programmes complets avec une structure minimale
- Pour simplifier le tokenizer, il introduit un langage Barely C fondé sur une tokenisation par espaces et un hachage via
atoi(), tout en conservant la syntaxe C existante pour obtenir une réduction de taille extrême
- Au cours de l’optimisation, diverses techniques de compression au niveau assembleur ont été appliquées, comme la suppression de sauts, la fusion d’appels, l’usage d’offsets sur 8 bits et de
stosw/lodsw, ce qui a permis de passer de 468 octets à 303 octets
- Au final, le projet parvient à implémenter dans 512 octets un compilateur C complet incluant tokenizer, parseur, générateur de code et runtime, illustrant un cas extrême de minimisation logicielle
Présentation de SectorC
- SectorC est un compilateur C écrit en assembleur x86-16 qui tient entièrement dans un secteur d’amorçage de 512 octets
- Le dépôt GitHub est xorvoid/sectorc
- Le langage pris en charge est un sous-ensemble de C suffisant pour écrire de vrais programmes
- Les fonctionnalités prises en charge comprennent les variables globales, fonctions, structures de contrôle (if/while), divers opérateurs, déréférencement de pointeurs, assembleur inline et commentaires
- Un programme d’exemple montre du code qui dessine une animation sinusoïdale en mode VGA
Contexte de conception et approche
- Un tokenizer C classique étant bien trop volumineux pour tenir dans 512 octets, il a fallu simplifier la structure même du langage
- Big Insight #1 : à la manière de Forth, le projet adopte une structure de tokens séparés par des espaces et conçoit une variante appelée « Barely C »
- Exemple :
int(main)(){while(!done){ est traité comme un unique « méga-token »
- Cela reste reconnu comme du code C valide par les compilateurs C existants
- Big Insight #2 : la fonction
atoi() est utilisée comme fonction de hachage pour convertir les tokens en nombres
- Les littéraux entiers, mots-clés et identifiants sont tous traités à partir du résultat de
atoi()
- Les identifiants sont accessibles comme index dans un tableau de 64K
Implémentation de Barely C
- La première implémentation faisait 468 octets et reposait sur un parseur récursif descendant utilisant des tokens basés sur
atoi
- Sans table des symboles, elle accédait directement au segment 64K via la valeur de hachage
- La génération de code suivait un style OTCC en utilisant le registre
ax comme emplacement de résultat
- Ensuite, une expérimentation avec du byte-threaded code a tenté une structure à la Forth, mais elle s’est révélée au contraire inefficace dans la limite des 512 octets et a donc été abandonnée
Techniques de minimisation du code
- Le projet est revenu à une structure linéaire et a réduit la taille de 468 octets à 303 octets
- Utilisation de techniques comme le fall-through (suppression de sauts), le tail-call, la fusion d’appels, l’usage de
stosw/lodsw et le maintien de déplacements de saut sur 8 bits
- Cela a libéré 207 octets d’espace pour ajouter des fonctionnalités
Extension vers des fonctionnalités C complètes
- Avec 200 octets supplémentaires, le projet a atteint une prise en charge complète de la syntaxe C visée
- Blocs
if/while imbriqués, divers opérateurs binaires (+, -, *, &, |, ^, <<, >>, ==, !=, <, >, <=, >=)
- Prise en charge des définitions de fonctions et appels récursifs, de l’assembleur inline (
asm), ainsi que des commentaires sur une ligne et multilignes
- Une table des opérateurs (
binary_oper_tbl) permet de définir chaque opérateur en 4 octets, soit 14 opérateurs pour 56 octets
Structure grammaticale
- La grammaire complète se compose notamment de
program, var_decl, func_decl, statement, expr, etc.
- Les commentaires
// et /* */ sont tous deux pris en charge
- Le texte décrivant la grammaire occupe à lui seul 704 octets, soit plus que l’implémentation elle-même
Assembleur inline et runtime
- L’instruction
asm permet d’insérer directement du code machine x86-16
- C’est une capacité indispensable pour les entrées/sorties
- Le runtime (
rt/) se compose de deux fichiers écrits en C
rt/lib.c : routines de bibliothèque fondées sur l’assembleur inline
rt/_start.c : point d’entrée du programme _start()
Programmes d’exemple
examples/hello.c : affiche du texte directement dans la mémoire 0xB8000
examples/sinwave.c : affiche une animation sinusoïdale en mode VGA 0x13
examples/twinkle.c : joue « Twinkle Twinkle Little Star » sur le haut-parleur PC (avec avertissement sonore)
Conclusion
- SectorC est un compilateur C minuscule qui concrétise un objectif apparemment impossible, et montre un cas extrême de minimisation logicielle et de conception créative de langage
- La fin de l’article se conclut sur des choix humoristiques autour de « ce que l’on a appris », soulignant avec ironie l’intérêt de s’attaquer à l’impossible et la valeur de la simplification logicielle
Aucun commentaire pour le moment.