Lorsque j’ai commencé à programmer, l’informatique était différente de celle d’aujourd’hui. Le système d’exploitation était mono-tâche; seul un programme pouvait être exécuté à la fois. Il n’existait pas de ressources modernes, telles que la mémoire protégée et mappée. Même ainsi, il était possible de créer des comportements concurrents dans les programmes, mais il fallait faire attention pour éviter qu’une partie bloque le traitement. Par exemple, il ne pouvait pas y avoir de boucles attendant une entrée au clavier ou des processus trop longs. Créer un programme sans blocages était compliqué dans des langages comme Clipper.
L’Art des hooks dans les interruptions
Une autre option pour simuler le multitâche était de créer un petit programme qui effectuait un hook sur une interruption du système. Mais qu’est-ce qu’un hook? En gros, vous interceptez une interruption – un signal pour le processeur indiquant que quelque chose d’important est arrivé, comme un tick de l’horloge ou une touche enfoncée – et vous redirigez ce signal vers une fonction de votre programme. Pour cela, vous repérez l’adresse de la routine d’interruption dans la table du système et vous remplacez cette adresse par celle de votre fonction. À la fin de votre fonction, vous appelez l’interruption d’origine pour maintenir le fonctionnement du système.
Cela permettait d’utiliser l’interruption de l’horloge ou du clavier pour simuler le multitâche. Ainsi, votre programme continuait de tourner en arrière-plan sans interférer avec le fonctionnement principal du système. C’était limité mais fonctionnait.
Aujourd’hui, j’utilise encore hooks d’interruptions mais seulement sur des microcontrôleurs.
TSR : Terminate and Stay Resident
Pour que cette méthode fonctionne, le programme devait rester en mémoire après l’exécution initiale. Cela se faisait en utilisant l’interruption 27 du MS-DOS, qui permettait de créer un TSR (Terminate and Stay Resident) en spécifiant la taille du programme. Heureusement, l’en-tête de Turbo C, le dos.h, simplifiait cette partie pour nous.
Ce programme crée une horloge dans le coin supérieur droit de l’écran. C’est un TSR qui utilise l’interruption de l’horloge et accède directement à la mémoire vidéo pour afficher l’heure. Aujourd’hui, les systèmes d’exploitation sont plus sûrs, mais ce contrôle total sur la machine me manque, y compris la possibilité d’écrire directement dans la mémoire et sur les périphériques.
#include <dos.h>
#define COLOR 0x70 // (7 << 4) | 0 // Light Gray on Black
#define TSR_MEMORY_SIZE ((_SS + (_SP / 16)) - _psp)
#define OFFSET_INIT 140 // (0 * 80 + 70) * 2
struct dostime_t t;
char far *screen;
void interrupt (*prevtimer)();
void interrupt updatetimer() {
unsigned int offset;
static unsigned char last_second = 0xFF;
_dos_gettime(&t);
if (t.second != last_second) {
last_second = t.second;
offset = OFFSET_INIT;
screen[offset++] = t.hour / 10 + '0';
screen[offset++] = COLOR;
screen[offset++] = t.hour % 10 + '0';
screen[offset++] = COLOR;
screen[offset++] = ':';
screen[offset++] = COLOR;
screen[offset++] = t.minute / 10 + '0';
screen[offset++] = COLOR;
screen[offset++] = t.minute % 10 + '0';
screen[offset++] = COLOR;
screen[offset++] = ':';
screen[offset++] = COLOR;
screen[offset++] = t.second / 10 + '0';
screen[offset++] = COLOR;
screen[offset++] = t.second % 10 + '0';
screen[offset++] = COLOR;
}
// chama a interrupção de timer anterior
(*prevtimer)();
}
void main() {
screen = (char far *)MK_FP(0xB800, 0x0000);
disable(); // Desativa interrupções
prevtimer = getvect(8);
setvect(8, updatetimer);
enable(); // Habilita interrupções
keep(0, TSR_MEMORY_SIZE);
}
código fonte
L’Évolution des systèmes d’exploitation
Quand j’ai créé ce TSR, il n’existait pas UNIX pour faire tourner les PC comme nous les avons aujourd’hui avec Linux et BSD. La popularité des systèmes basés sur UNIX est venue plus tard, apportant des améliorations en multitâche, gestion de la mémoire et sécurité.
Avec l’arrivée de Windows et l’évolution vers des systèmes d’exploitation modernes, la nécessité des TSRs a diminué de manière significative. Aujourd’hui, le système d’exploitation gère les processus et les threads, ce qui offre davantage de sécurité, mais nous n’avons plus d’accès direct.
TSRs en Bash : Une approche moderne
Le script Bash qui suit est similaire au TSR précédent, mais adapté pour les systèmes de type UNIX et les émulateurs de terminaux modernes. Comme Bash ne peut pas accéder directement à la mémoire vidéo, il utilise des commandes ANSI pour contrôler le terminal. Cela inclut la sauvegarde de la position du curseur avant d’afficher l’horloge et sa restauration ensuite, garantissant que d’autres programmes qui redessinent l’écran n’interfèrent pas.

#!/bin/zsh
local last_second=0
display_time() {
seconds=$(date +"%S")
[ $seconds -eq $last_second ] && return
local current_time=$(date +"%H:%M:")
echo -ne "e7e[1;70He[30;47m$current_time$secondse[0me8"
}
while true; do
display_time
sleep 0.1
done
código fonte
Pour exécuter le script et obtenir le même effet que le TSR, il suffit de le lancer en arrière-plan :
./cloch.sh&
Réflexions finales
Aujourd’hui, nous disposons de plus de ressources et de systèmes plus sûrs et, d’une certaine manière, plus flexibles. Cependant, nous pouvons nous inspirer de techniques telles que les hooks d’interruptions pour créer des choses nouvelles et intéressantes. La nostalgie de programmer dans des environnements plus restreints nous rappelle la créativité et l’ingéniosité dont nous devions faire preuve à l’époque. Peut-être que certaines de ces techniques anciennes peuvent encore être adaptés ou inspirer des solutions innovantes aujourd’hui.
De plus, comprendre comment les systèmes fonctionnaient à des niveaux plus bas nous donne une plus grande appréciation de la sophistication des systèmes modernes. Alors, la prochaine fois que vous utiliserez une commande simple dans le terminal, rappelez-vous des couches d’abstraction entre votre clavier et le silicium.




