Rétro Coding sur DOSBox
Ce que je trouve personnellement très excitant avec le rétro coding, c’est le fait que ça te force à penser autrement si tu n’as jamais eu la chance d’utiliser ces technologies avant. Et si tu les a déjà utilisées professionnellement, voir simplement par curiosité étant très jeune comme moi, ça nous fait vivre un beau moment de nostalgie à la fin duquel on se dit : Ouin… on est bien en 2021! Bref… aujourd’hui, je reprends la nouvelle série sur le rétro coding avec un deuxième article qui reste dans les mêmes saveurs que le premier. Aujourd’hui, on fait du QBasic sur DOS avec DOSBox!
Note : Pour cet article, comme pour le précédent, j’utiliserai une distribution Linux (Ubuntu). Sachez que tous ces outils sont disponibles sous Windows aussi!
Le code source final est disponible ici.
Préparation de l’environnement
DOSBox
Comme expliqué en introduction, nous allons avoir besoin de DOSBox. Cet outil est un émulateur DOS disponible sur plusieurs plateformes différentes, dont Ubuntu et autres distros basées sur Debian. Donc pour l’installer, exécutez simplement :
sudo apt install dosbox
C’est tout! Si vous avez une erreur à l’installation en lien avec SDL, exécutez :
1 |
sudo apt install libsdl1.2-dev |
QB45
Ensuite, on a besoin du fameux compilateur Microsoft QBasic pour DOS. Vous allez voir, le site qui héberge le compilateur est probablement aussi rétro que le compilateur lui-même. Donc, téléchargez ce compilateur, puis dézipez dans un dossier, comme par exemple /home/sylvain/QB45.
Ensuite, on va simplement utiliser la fonction MOUNT de DOSBox pour attacher notre répertoire local dans l’émulateur comme un disque réseau. Lancez d’abord DOSBox :
1 |
dosbox |
Puis, attachez le répertoire :
1 |
mount C /home/sylvain/QB45 |
Si vous êtes sur Windows, vous pouvez le faire comme ceci :
1 |
mount C C:\QB45 |
Ensuite vous naviguez dans le dossier attaché et exécutez QBasic :
1 2 |
C: qb |
Résultat :
🤯 Ce tendre écran bleu qui a bercé mon adolescence! On teste un hello world pour s’assurer que tout fonctionne :
1 |
PRINT "Hello World!" |
Faites shift + F5 pour lancer l’application et vous devriez voir « Hello World! » dans la console de DOSBox.
L’évolution du langage BASIC
Le BASIC du précédent article de la série était celui du Commodore 64, soit une version de BASIC assez basique 😅 Ici, on a accès aux mots clés THEN / ELSE / END IF, FOR / NEXT, DO / WHILE / UNTIL / LOOP, la possiblité de créer des fonctions et procédures, ainsi qu’un typage de variables. La Cadillac comparé au BASIC de Commodore 64!
Je vous laisse la documentation de QBasic 4.5 ici. Pour les curieux!
ToDo App
Parce que refaire toujours le même exercice permet d’expérimenter avec une technologie dans un même cadre pour pouvoir les comparer, je vais reprendre le même exemple que pour le premier article de la série.
La ToDo App permet de gérer une liste de choses à faire. On doit pouvoir les lister, en ajouter ou supprimer, puis les marquer comme complétées. Donc qu’est-ce qu’on a besoin?
- Une structure de données représentant un ToDo
- Une collection pour stocker les ToDos
- Un menu simple
La structure de données, c’est l’ancètre des objets. Aucun concept comme l’héritage ou le polymorphisme, mais on peut quand même garder une structure de données dans une collection, contrairement au Commodore 64. Pour ce faire, créez un type :
1 2 3 4 5 |
TYPE todotype desc AS STRING * 50 done AS INTEGER deleted AS INTEGER END TYPE |
Eh non, toujours pas de booléen… Créons donc des constantes :
1 |
CONST YES% = 1, NO% = 2 |
Astuce : Le suffixe % permet de créer un entier 🙂
On garde dans notre structure de donnée une propriété deleted parce qu’à l’initialisation de la collection, tous les items sont initialisés. Pour faire simple, je vais simplement limiter le nombre de ToDos à 5, puisque les collections en BASIC n’alloue pas automatiquement la mémoire. Il faut faire un REDIM et la collection est réinitialisée. Gardons-nous simplement des variables pour limiter le nombre de ToDos :
1 2 3 |
CONST MAXTODOS% = 5 DIM SHARED todocount AS INTEGER todocount = 0 |
On déclare la variable todocount comme SHARED afin qu’elle soit disponible dans les sous-routines. Un peu comme une variable globale. Les constantes, quant à elles, sont toujours implicitement SHARED.
Puis on crée la collection et initialise les ToDos comme supprimés :
1 2 3 4 |
DIM SHARED todos(MAXTODOS%) AS todotype FOR i% = 1 TO MAXTODOS% todos(i%).deleted = YES% NEXT i% |
Par soucis de propreté, créons aussi un point d’entrée :
1 2 3 |
SUB Main END SUB |
En ayant tapé la première ligne, l’éditeur devrait vous avoir ouvert un nouveau fichier. Pour naviguer dans les différentes sous-routines, faites F2 pour ouvrir le menu :
Revenez au module principal (ici Untitled) et appelez la sous-routine Main.
1 |
Main |
Dans cette sous-routine, nous allons afficher le menu à l’utilisateur, prendre son entrée, puis exécuter une sous-routine pour les opérations CRUD. Créons immédiatement ces sous-routines :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
SUB MainMenu END SUB SUB CreateTodo END SUB SUB DeleteTodo END SUB SUB MarkTodoAsDone END SUB SUB DisplayTodos END SUB |
Main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
SUB Main DIM userinput AS STRING DO MainMenu INPUT "Votre choix"; userinput IF userinput = "4" THEN EXIT DO IF userinput = "1" THEN CreateTodo IF userinput = "2" THEN MarkTodoAsDone IF userinput = "3" THEN DeleteTodo LOOP END SUB |
Rien de trop compliqué ici, on affiche le menu, on prend l’entrée de l’utilisateur et on boucle jusqu’à ce qu’il veuille quitter.
MainMenu
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
SUB MainMenu CLS PRINT "ToDo App - QBasic 4.5" PRINT IF todocount > 0 THEN DisplayTodos ELSE PRINT "Aucun todo" END IF PRINT PRINT "1. Creer un todo" PRINT "2. Marquer un todo comme fait" PRINT "3. Supprimer un todo" PRINT "4. Quitter" PRINT END SUB |
CLS ici permet d’éffacer l’écran. Puis, dépendant s’il y a des ToDos ou non, on les affiche. On termine par afficher un menu avec les options possible pour l’utilisateur.
DisplayTodos
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
SUB DisplayTodos FOR i = 1 TO MAXTODOS% DIM currenttodo AS todotype DIM donestring AS STRING currenttodo = todos(i) IF currenttodo.deleted = NO% THEN IF currenttodo.done = YES% THEN donestring = "Fait" ELSE donestring = "Pas fait" END IF PRINT currenttodo.desc + " - " + donestring END IF NEXT i END SUB |
Ici, on boucle sur toutes les entrées de la collection et on ne traite que celles qui ont le flag deleted à NO%. Ensuite on concatène la description du ToDo avec un indicateur à savoir si la tâche est faite ou non.
À noter, en BASIC, les collections commencent à l’indice 1 et non 0 😛
CreateTodo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
SUB CreateTodo DIM newtodo AS todotype newtodo.done = NO% newtodo.deleted = NO% IF todocount = MAXTODOS% THEN PRINT "Impossible d'ajouter plus de " + STR$(MAXTODOS%) + " todos" PRINT "Appuyez sur une touche pour continuer" DO LOOP UNTIL INKEY$ <> "" EXIT SUB END IF PRINT "Creation d'un nouveau todo." INPUT "Saisir la description : "; newtodo.desc todocount = todocount + 1 todos(todocount) = newtodo END SUB |
Cette sous-routine permet de valider si on a déjà atteint la limite de ToDos en mémoire. Si tel est le cas, on retourne au menu avec EXIT SUB. Sinon, on demande à l’utilisteur de fournir la description du ToDo, puis on l’ajoute à la collection.
DeleteTodo
1 2 3 4 5 6 7 8 9 10 11 12 13 |
SUB DeleteTodo DIM todoindex AS INTEGER IF ValidateAtLeastOneTodo = NO% THEN EXIT SUB DO PRINT "Entre 1 et" + STR$(todocount) INPUT "Lequel"; todoindex IF todoindex >= 1 AND todoindex <= todocount THEN EXIT DO LOOP todos(todoindex).deleted = YES% todocount = todocount - 1 END SUB |
Avant de permettre à l’utilisateur de choisir le ToDo à supprimer, on valide qu’il y a au moins un ToDo dans la collection. Cette fonction sera couverte plus bas. Si tout est OK, on demande l’indice du ToDo à supprimer et on marque le ToDo comme deleted.
MarkTodoAsDone
1 2 3 4 5 6 7 8 9 10 11 12 |
SUB MarkTodoAsDone DIM todoindex AS INTEGER IF ValidateAtLeastOneTodo = NO% THEN EXIT SUB DO PRINT "Entre 1 et" + STR$(todocount) INPUT "Lequel"; todoindex IF todoindex >= 1 AND todoindex <= todocount THEN EXIT DO LOOP todos(todoindex).done = YES% END SUB |
Comme pour la suppression, on valide qu’il existe d’abord un ToDo en mémoire. Si oui, on demande l’indice, puis on le marque comme done.
ValidateAtLeastOneTodo
1 2 3 4 5 6 7 8 9 10 11 12 13 |
FUNCTION ValidateAtLeastOneTodo DIM valid AS INTEGER valid = YES% IF todocount = 0 THEN valid = NO% PRINT "Aucun todo en memoire..." PRINT "Appuyez sur une touche pour continuer" DO LOOP UNTIL INKEY$ <> "" END IF ValidateAtLeastOneTodo = valid END FUNCTION |
J’attire votre attention sur le fait que ceci est un fonction, et non une sous-routine. En BASIC, le mot return n’existe pas. Il faut simplement affecter une valeur à la fonction comme le montre la dernière ligne.
On retourne YES% s’il y a au moins un ToDo, sinon NO%.
Résultat :
Le code source final est disponible ici.
Conclusion
Aujourd’hui, on a avancé dans l’histoire de BASIC. On peut bien voir l’évolution du langage à travers les deux articles. Personnellement, BASIC a bercé mon adolescence et j’y trouve un malin plaisir à le revisiter, ne serait-ce que par pur nostalgie.
Si vous avez apprécier ce retour dans le passé, partagez-le dans vos réseaux sociaux 🙂
CHEERS!
Commentaires
Laisser un commentaire