RSS

La programmation des jeux dans les années 80

03 Sep

Au tout début de l’informatique grand public, les machines étaient si peu puissantes que la façon même de les programmer était différente d’aujourd’hui.
Il fallait se donner du mal pour n’obtenir qu’un résultat juste correct, l’environnement de programmation était assez rudimentaire.

Toutefois, ces difficultés allaient de pair avec l’enthousiasme d’apprendre un domaine tout nouveau et largement mystérieux pour le grand public.
Une des conséquences d’avoir appris à programmer à cette époque, outre l’entraînement à la persévérance😉 , c’est d’avoir finalement touché à toute la chaîne de l’informatique. Car pour obtenir un logiciel qui fonctionne normalement sur ces machines, il fallait tirer le maximum de leurs rares Mhz (oui, mega hertz, pas giga hertz !) et de leur mémoire d’oiseau amnésique. Il fallait donc les connaître dans leurs moindres détails.

Plutôt que de parler en général, je vais vous montrer en pratique à quoi cela ressemblait, en partie au travers de mon expérience personnelle et de quelques humbles œuvrettes de jeunesse.

Le ZX

Pour ma part, tout a démarré avec un précurseur de l’informatique accessible à tous, j’ai nommé le Sainclair ZX-81:

En résumé:
  • Pas cher (1000 FF à sa sortie, je crois, donc très accessible).
  • Enregistre sur des cassettes audio (il faut y brancher un magnétophone).
  • Mémoire ultra-faible même pour l’époque: 1 Ko !
  • Clavier de type industriel: une membrane avec des contacts dessous, comme certaines machines en plein-air.
  • Processeur 8 bits à 3,25 Mhz, que j’évaluerais à environ 0,15 Mips, le Z80.
  • Pas de mode graphique, tout est en texte.
  • Langage Basic intégré, assez simpliste.
  • Pas de son.

Détails sur Wikipedia.

Cette machine était construite pour que tout le monde puisse découvrir ce qu’est un ordinateur sans se ruiner.
Malheureusement, sa conception très bas-de-gamme le rendait presque inutilisable, sa faible puissance permettait à peine aux logiciels de tenir dans la mémoire et le manque de fiabilité de ses sauvegardes sur cassette audio nous faisait souvent perdre les programmes péniblement entrés à la main.

Il a toutefois servi à beaucoup de gens à se mettre à la programmation, car c’est bien ce qui était le plus passionnant à cette époque: créer quelque chose à partir de ces machines.

Comme toutes les machines 8 bits, il disposait d’un interpréteur Basic en ROM. D’ailleurs le système d’exploitation entier était en ROM, dans 8 petits Ko.

C’est une première chose étonnante vu d’aujourd’hui: comment un interpréteur Basic, en plus d’une gestion de fichiers sur cassette et de quelques autres ressources, pouvait tenir dans 8 minuscules Ko ?

La réponse est claire pour cette époque: tout le code de la ROM était écrit en Assembleur.
Pour les jeunes programmeurs, il n’est pas évident de savoir à quoi ressemble un code assembleur, puisqu’ aujourd’hui seuls quelques domaines particuliers nécessitent ce type de programmation.

Mais n’oublions pas que, par-dessus tout, la nécessité rend ingénieux. La capacité était faible, les gens ont trouvé des solutions pour en tirer partie malgré la difficulté.

Le code assembleur, dit code machine, en fait le jeu d’instructions du microprocesseur

Voici un extrait d’un code source, une partie de la routine de test de la RAM:

;; RAM-CHECK
L03CB: LD H,B ;
LD L,C ;
LD A,$3F ;

;; RAM-FILL
L03CF: LD (HL),$02 ;
DEC HL ;
CP H ;
JR NZ,L03CF ; to RAM-FILL

Vous trouverez le désassemblage complet de la ROM ici.
Malheureusement, ce genre de code est difficile à lire, c’est à dire à comprendre pour un humain.
Et encore, il faut imaginer un listing de 10000 lignes ainsi, on a vite fait de s’y perdre. Tout dépend alors de la qualité de la documentation.
Ce dernier code est à peu près équivalent à ce code C#:

void EffaceÉcran(byte* PFinÉcran)
{
// Remplissage:
byte* pécran = PFinÉcran;
while((pécran & 0xFF00) != 0)
*pécran-- = 2;
}

Vous pouvez voir que c’est déjà nettement plus lisible, surtout si vous savez programmer dans un langage proche syntaxiquement du C++. Et encore, on n’écrirait pas les choses ainsi en C#, on utiliserait un foreach.

Les pointeurs

À remarquer que mon transcodage en C# utilise un pointeur, déconseillé dans les langages modernes à cause des risques de dépassement. Ce pointeur reflète directement le registre HL du Z80 dans le code en assembleur.

On voit ici la raison d’être des pointeurs dans les langages comme le C++ : se calquer sur les instructions en code microprocesseur (ce qu’on appelle l’Assembleur, par un abus de langage, ou encore le code machine). On comprend bien, là, que le langage C est entièrement conçu pour coller au plus près des possibilités des microprocesseurs, ce qui le rend tout aussi rapide à l’exécution que dangereux.

L’inefficacité de la compilation C

Si je compile mon code C# (en fait une version en C K&R), j’obtiens ce long code machine (pour microprocesseur Z80):

 org &4000
nolist

jp main

main:
push bc
L1:
pop bc
ret
EffaceEcran:
push bc
ld hl,65536
add hl,sp
push hl
ld hl,6
add hl,sp
call Lgint
pop de
call Lpint
L3:
ld hl,65536
add hl,sp
call Lgint
push hl
ld hl,65280
pop de
call Land
push hl
ld hl,0
pop de
call Lne
ld a,h
or l
jp z,L4
ld hl,65536
add hl,sp
push hl
call Lgint
dec hl
pop de
call Lpint
inc hl
push hl
ld hl,2
pop de
call Lpchar
jp L3
L4:
L2:
pop bc
ret

; DATA

; CRT
Lpint:
ld a,l
ld (de),a
inc de
ld a,h
ld (de),a
ret

Lgint:
ld a,(hl)
inc hl
ld h,(hl)
ld l,a
ret

Lne:
R_crt1:
call Lcmp
ret nz
dec hl
ret

Lpchar:
ld a,l
ld (de),a
ret

Land:
ld a,l
and e
ld l,a
ld a,h
and d
ld h,a
ret

Lcmp:
ld a,e
sub l
ld e,a
ld a,d
sbc a,h
ld hl,1
jp m,Lcmp1
or e
ret
Lcmp1:
or e
scf
ret

Pourtant, si le compilateur C était efficace, je devrais obtenir le même code assembleur en 7 lignes que celui de la ROM du ZX-81.

L’exemple est un peu extrême, car ce compilateur (PhrozenC) est visiblement très inefficace. Ceci dit, même un bon compilateur (de cette époque ou de maintenant) produit un code 2 à 3 fois plus gros et plus lent que l’équivalent écrit à la main en code machine.
D’où la conclusion de tout programmeur qui veut écrire un logiciel raisonnablement rapide pour ce type d’ordinateur: il faut écrire entièrement en code machine.

L’incompatibilité des machines

Mais ici se pose un nouveau problème: un code machine dépend, par définition, d’un type de microprocesseur, et donc est incompatible avec d’autres microprocesseurs.
D’où le dilemme: soit on écrit en code machine et on obtient un logiciel petit et rapide, mais qui ne fonctionnera que sur des ordinateurs disposant du même microprocesseur, soit on écrit dans un langage plus évolué, comme le C, et on obtient un logiciel gros et lent mais plus portable.

Le système aussi est incompatible

À tous ces problèmes vient s’ajouter celui de l’incompatibilité des systèmes d’exploitation, la vraie plaie des années 80.
Même si deux machines ont le même microprocesseur, chacune a son propre système, en général écrit par le fabriquant de la machine.

Au début des années 80, lorsqu’on écrivait un logiciel destiné à plusieurs types de machines, il fallait écrire son architecture en conséquence. Le travail d’adaptation était tel qu’on appelait ça  » réécrire le logiciel pour telle machine« . Autant dire qu’écrire un logiciel pour 3 machines différentes demandait sans doute 4 ou 5 fois plus de travail que pour une seule. Certains logiciels tournaient sur une ou deux dizaines de plateformes, on imagine le travail nécessaire, et la maintenance cauchemardesque.

Il existait un semi-standard à peine émergeant: CP/M, mais il était sans doute trop « lourd » pour être largement installé sur les machines, et plutôt cher il me semble. Je ne l’ai personnellement jamais utilisé.

L’ensemble de ces difficultés explique pourquoi toute l’industrie de l’informatique s’est jeté sur les IBM PC: elle avait besoin d’un standard, pour n’écrire les logiciels et ne créer du matériel que pour une seule plateforme.

L’Amstrad CPC

Durant la première moitié des années 80, on voit une explosion des fabriquant d’ordinateurs, notamment entre 1982 et 1986.
À la fin de l’année 1984, un petit fabriquant d’électronique anglais, Amstrad, se lance sur le marché avec une machine simple et assez bien conçue. Son but n’est pas d’être révolutionnaire, mais d’être accessible aux familles, autant par son bas prix que par sa facilité d’installation et d’utilisation.

À cette époque, on avait le choix entre des machines hors de prix comme les Apple 2, et des ordinateurs plus familiaux comme les Commodore 64. Problème de ces derniers, et de leurs concurrents directs: la machine est vendue seule, il faut lui ajouter un magnétophone pour les sauvegardes et l’accès aux logiciels, un écran (en général une télévision), sans parler d’une éventuelle imprimante, le lecteur de disquette étant vraiment cher. Additionner tout cela pouvait monter facilement à environ 10000 FF, soit disons le tiers du prix d’une voiture.

D’où le choc à Noël 1984 lorsqu’ Amstrad annonce un ordinateur incluant le magnétophone et surtout l’écran, pour 4500 FF (en couleurs, sinon 3000 FF en N&B, il me semble). C’est la moitié du prix du matériel équivalent chez la concurrence.
De plus, bien que classique dans sa conception électronique, ses capacités sont bonnes, souvent meilleures que ses concurrents.

  • Processeur: classique 8 bits Z80 à 4 Mhz.
  • Mémoire 64 Ko. C’est vraiment beaucoup à cette époque, 2 à 4 fois plus que la concurrence.
  • Un vrai clavier, avec un pavé numérique. Le seul à son époque parmi les ordinateurs familiaux.
  • L’écran est de la qualité d’une télévision, mais sans tuner.
  • Le magnétophone est fiable (rare à l’époque), et il est même commandé par l’ordinateur (recherche automatique de fichiers, par exemple).
  • Un Basic intégré étendu très complet et rapide.

À l’époque, je pense que cette machine était une des plus astucieusement conçues, à mon sens globalement bien plus satisfaisante que la concurrence, et en plus bien moins chère.

Le choix classique du Z80 permettait à ceux qui avaient appris sa programmation en langage machine de pouvoir démarrer rapidement leurs essais sur cette toute nouvelle machine.

Au contraire du ZX-81, on peut dire qu’on avait enfin une machine qui permettait de vraiment programmer. Maintenant on pouvait enfin caser en mémoire à la fois un outil de programmation complet et le logiciel qu’on développait. Sur les machines moins puissantes, il fallait morceler les logiciels, ce qui est était lent et pénible à l’emploi.

J’aimerais vous montrer mes recherches de l’époque sur cette machine, mais malheureusement elles n’ont apparemment pas survécu au passage des cassettes aux disquettes.

La Bible du programmeur de l’Amstrad CPC : un sympathique pavé

En ce qui me concerne, c’est avec cette machine que j’ai vraiment commencé à explorer la programmation dans son ensemble. J’avais acheté un gros pavé, un livre appelé « La bible de l’Amstrad CPC » (maintenant téléchargeable en PDF), un classique pour comprendre et maîtriser la machine.

Ce type d’ouvrage est divisé en trois parties:

    * L’électronique de la machine.
    * Le système d’exploitation.
    * Le langage Basic.

Le système y était entièrement décompilé, c’est à dire que dans un premier temps un logiciel (un décompilateur) transforme le contenu de la ROM du système en un code source représentant les instructions machine du Z80 et les données, et dans un deuxième temps plusieurs personnes se sont donné le mal de comprendre et de commenter les 32 ko de la ROM. J’imagine combien de semaines il leur a fallu pour décrypter tout ce code, en expérimentant, en testant les routines système, etc.

Le plus passionnant, c’était de comprendre comment cette machine fonctionnait, d’en disséquer les composants tout autant que la programmation.
Et puis faire le lien entre les routines du système et l’explication sur le fonctionnement des circuits électroniques de la machine. Le but étant de pouvoir écrire un logiciel en langage machine qui puisse programmer directement les circuits, pour avoir un contrôle complet et une vitesse d’exécution maximale.

Autant dire que de telles notions apportent une excellente compréhension des concepts fondateurs des ordinateurs actuels:

  • programmation des circuits (parfait pour écrire des pilotes de périphérique aujourd’hui),
  • optimisation en langage machine (parfait pour programmer les micro-contrôleurs et les systèmes embarqués aujourd’hui, tels que les robots ou les ordinateurs d’avion ou de drone),
  • gestion des ressources, du partage du temps, des interruptions (parfait pour programmer des systèmes d’exploitation),
  • bibliothèque de routines de base (équivalent de la bibliothèque de base de C, ce qui fait qu’on comprend comment ce genre de chose s’écrit et fonctionne en pratique).

Durant mon travail actuel de programmeur, de temps en temps le souvenir d’une technique apprise sur cette ancestrale machine m’aide à résoudre un problème ou simplement à me faire une idée du fonctionnement d’un concept. J’ai par exemple appris dans ce livre comment générer des nombres pseudo-aléatoires, c’est une notion qui continue de m’être utile aujourd’hui. Pareil lorsque j’ai lu comment dessiner une ligne en mode graphique avec des méthodes optimisées, ce type d’optimisation m’a souvent servi dans des domaines pourtant très différents.

Voila une expérience que je suis content d’avoir vécu, car ce fut une époque de découverte, de défrichage, qui ne peut exister qu’une fois dans l’histoire d’une technique: à son départ.
Bien évidemment, on peut fixer le vrai départ de l’informatique dans les années 50, et surtout 60, mais il existe un vrai parallèle dans le monde de l’informatique « accessible », lorsqu’elle a vraiment atteint en masse l’industrie et les familles.

Ceci dit, la machine qui a été pour moi celle du commencement d’une programmation plus évoluée et riche, ça a été l’Atari ST.

L’Atari ST

Avec cette machine, une ère nouvelle s’ouvrait enfin au large public: l’interface graphique.

L’interface graphique est un concept essentiellement né dans les fameux laboratoires du Xerox PARC, durant les années 70.
Mais Xerox n’a pas vraiment produit de machine qui ait pu atteindre le public, que ce soit en entreprise ou en famille. Seuls quelques universitaires avaient accès aux prototypes de Xerox. D’où le mystère qui entourait ce nouveau concept d’interface utilisateur.

En 1985, il existait bien sûr une première matérialisation du concept grâce à Apple et son Lisa, suivi du Macintosh.
Malheureusement, le prix nettement prohibitif de ces machines les éloignait du grand public. Certes certaines entreprises les utilisaient, mais seules quelques personnes y avaient accès puisqu’il s’agit d’un ordinateur personnel.

J’étais jeune et j’ignorais presque tout de l’interface graphique, j’étais imprégné des interfaces textuelles classiques. J’avais certes lu des articles présentant le concept de l’interface graphique et aussi le Lisa, mais franchement il est nécessaire de manier une telle interface pour la comprendre, les mots et même les images sont insuffisants. Il faut la toucher, par la souris, la bouger, avec ses fenêtres, pour en ressentir le mouvement et la logique.

Ça a donc été un choc lorsque j’ai vu pour la 1ère fois un Atari ST, d’abord dans des articles puis dans des magasins.
Cette machine se présentait comme un concurrent du Macintosh, il avait clairement été conçu dans ce sens, mais il était à la fois plus puissant et beaucoup moins cher (à mon souvenir, environ 3 fois moins cher, c’est dire que les marges bénéficiaires d’Apple était déjà très confortables à l’époque).

Voyons à quoi cela ressemble:

  • Un microprocesseur 16/32 bits, le Motorola 68000, à 8 Mhz, excellent.
  • 512 Ko de RAM. Vraiment impressionnant, le Macintosh n’ayant que 128 Ko.
  • Une souris à deux boutons.
  • Un lecteur de disquettes 3,5 pouces, un futur standard (naissant à cette époque) de 720 ko.
  • Un clavier complet, avec des touches de fonction, un pavé numérique.

Bien qu’annoncé en 1985, L’Atari ST est arrivé en France véritablement en 1986, soit deux années après le Macintosh.

Autant l’Amstrad CPC était une machine bonne et bien conçue, mais très classique quant à son électronique, autant l’Atari ST nous faisait passer dans une nouvelle période. Tout était différent: un microprocesseur à code 32 bits, une RAM gigantesque permettant enfin de faire de grandes choses, l’interface graphique qui bousculait les habitudes, et à un moindre degré le lecteur de disquettes enfin devenu une norme (finies les cassettes audio lentes et moins que fiables), et puis aussi un clavier complet presque digne d’un ordinateur professionnel (sauf que le toucher était trop « rebondissant » pour moi).

Sa seule vraie concurrence était le Commodore Amiga, sorti lui aussi avec beaucoup de retard, mais bien plus cher que l’Atari et selon moi moins fourni en logiciels. J’en ai acquis un également, mais plus tard, à cause de son prix, ce qui fait que je ne l’ai jamais autant « pratiqué » que l’Atari; l’Amiga était une excellente machine, qui aurait mérité une meilleure finition de son système d’exploitation (mince, je vais relancer la gué-guerre ST contre Amiga).

Le Motorola 68000

Un programmeur plus jeune pourrait se demander en quoi un microprocesseur 68000 était vraiment différent d’un Z80. On pourrait en effet croire que la différence principale était la vitesse (environ 1 Mips contre 0,2) ou le passage des accès mémoire de 8 à 16 bits. Mais en fait il s’avère qu’une architecture différente peut représenter en soi une avancée et une ouverture vers d’autres possibilités.

Le Z80 est un bon processeur 8 bits, mais fondamentalement son jeu d’instruction est limité à 8 bits pour les données et 16 bits pour les adresses, il n’est pas évolutif. D’ailleurs dans la pratique ce processeur n’a pas eu de suite (commercialement viable en tout cas).
Au contraire, le jeu d’instructions du 68000 avait été minutieusement conçu pour l’avenir. Il était conçu dès le départ pour manipuler des adresses sur 32 bits (une taille restée valable jusqu’en 2011 environ), et des données également sur 32 bits pour l’essentiel. En d’autres termes, un logiciel conçu en 1979 pour un 68000 devait pouvoir tourner sur un descendant du 68000 en 2011, soit pérenne durant 33 ans. Pas mal, pour un milieu aussi mouvant que l’informatique.

À cette époque, le grand concurrent du 68000 était l’Intel 8086. Tout ceux qui ont programmé sur les deux savent à quel point utiliser le 8086 était une torture, en comparaison. Autant le code du 68000 était élégant, facile à lire et à écrire, autant celui du 8086 était tortueux, inutilement complexe et lent.

Pour ce qui est du reste de l’électronique du ST, elle était assez classique, sobre et bien conçue, mais pas révolutionnaire. C’était un ordinateur conçu pour le travail en entreprise, mais il n’a finalement pas rencontré ce marché. Toutefois il a trouvé son public dans les familles.
Son système d’exploitation marque le début d’une nouvelle époque: dorénavant cette partie de la machine demande beaucoup de travail à son fabriquant, sans doute trop pour les petits fabriquant qui avaient fleuri au début des années 80, et qui du coup ont tous brutalement disparu, voir Sainclair, Oric, Amstrad, Acorn, tous les MSX.

En 1986, le ST est donc une machine très intéressante, et ses capacités atteignent enfin un niveau qui permet de programmer à l’aise et de produire des logiciels puissants. Par contre, l’apprentissage de la programmation des interfaces graphiques représente un véritable cap à franchir pour tous les programmeurs qui ont passé beaucoup de temps sur des questions d’optimisation et d’utilisation directe des circuits.
Avec le ST, l’optimisation reste de mise car ces machines ne sont pas encore assez puissantes pour pouvoir se reposer sur un compilateur, mais d’un autre côté, la programmation d’un logiciel à interface graphique n’est pas du tout adaptée au langage machine et oblige donc dans ce cas à utiliser un compilateur (C ou Basic, principalement, sur cette machine).
On peut dire que le programmeur a donc le choix entre deux façons de travailler: l’une plus tournée vers l’efficacité et donc vers les jeux et le multimédia, et l’autre plus vers l’interface et donc vers les logiciels professionnels.

Le premier choix est le plus technique, celui qui réclame le plus de finesse, d’imagination, de maîtrise de la machine, il est aussi sans doute le plus attirant lorsqu’on est jeune, créatif, et qu’on se lance des défis.
Je n’y ai pas échappé, et j’ai donc expérimenté ce type de programmation. Voyons un peu comment ça se passe en pratique.

Claustrophobia

Une vidéo est disponible sur Dailymotion. Malheureusement, on dirait que le lecteur flash la limite à 25 fps au lieu de 50. Moi qui était si fier de mes 50 fps ! 😉







C’est un jeu en 2D dans lequel une tête se balade dans un château en volant, et doit récupérer ses membres perdus. On se demande comment cette tête peut se déplacer sans jambe et même sans corps, mais peu importe, elle vole !

D’un point de vue technique, déplacer cette tête a été un vrai casse-tête, justement.
Je ne voulais pas faire des « salles » séparées par des portes comme au temps des ordinateurs 8 bits, donc c’est le décor qui devait défiler.
Problème: l’électronique du ST ne permettait pas de faire défiler un écran, il fallait réécrire tout l’écran à chaque déplacement, du moins avec les premières versions du ST.
Comme le microprocesseur était un peu trop lent pour parvenir à redessiner l’écran 50 fois par seconde (oui, j’aime les défilements parfaits), j’ai dû à la fois réduire la taille de la zone de jeu (d’où les bordures vertes) et réduire le nombre de couleurs du décor. En fait le décor est réduit à quatre petites couleurs, alors que l’ordinateur pouvait en afficher 16. En nombre de bits, j’avais donc réduit la quantité de données de moitié par les couleurs, plus une petite réduction par les bordures, donc ouf!, l’animation était finalement parfaite.

Pour compenser le manque de couleurs du décor, j’ai intercepté les interruptions de « balayage horizontal » pour faire varier les couleurs verticalement.
Le balayage, c’est l’interruption qui se produit à chaque début de traçage d’une ligne horizontale de l’écran, soit environ 15000 fois par seconde. En termes de langages de programmation modernes, on appellerait peut-être ça un « évènement », mais ici il s’agit d’interruptions matérielles générées par le circuit graphique et directement reçues par le microprocesseur.

Étant donnée que le processeur ne fonctionne qu’à environ 1 Mips, 15000 interruptions à traiter chaque seconde consomment une bonne partie de son activité, d’où la nécessité de réutiliser ces interruptions pour générer en même temps les sons numériques.
Je rappelle qu’à cette époque, c’était le tout début des sons numériques sur ordinateurs. Auparavant, seuls des sons synthétiques étaient joués, d’où des bruitages simplistes mais mémorables dans les jeux d’arcade.

L’ennui avec le ST, c’est qu’il n’a pas été conçu comme une console de jeux (contrairement à son concurrent l’Amiga, oui j’attise la gué-guerre), et ne disposait pas d’un DMA pour gérer le son. Il devait ‘à la main’ modifier le son, quelques dizaines de milliers de fois par seconde.

Cet exemple de double utilisation d’une interruption, montre que l’optimisation d’un logiciel devait être globale, ici il a fallu programmer entièrement en langage machine, car presque chaque cycle d’instruction comptait.

Plus haut, une image montre l »écran d’accueil de la démo, qui présentait des couleurs.
Cet écran illustre bien la possibilité du changement de palettes. Chaque ligne horizontal affiche ici 24 couleurs différentes. Sachant que le ST était normalement limité à 16 couleurs pour la totalité de l’écran, on voit bien qu’il a fallu modifier cette palette à chaque ligne, et même plusieurs fois par ligne.

Voyons un peu à quoi ressemble le code machine 68000.

En résumé, le 68000 dispose de 8 registres de données en 32 bits et 8 registres d’adresse en 32 bits (servant de pointeur). Il n’existe pas de registre particulier (type Accumulateur en Z80 ou certains autres en 8086, que j’ai oublié – c’est dire si ce dernier processeur m’a peu passionné). Tous les registres de données sont généraux.

Par chance, il me reste une partie de mes codes sources de l’époque. J’ai quand même dû écrire un logiciel pour pouvoir relire les disquettes sous Windows, à cause de limitations de Windows NT.

e2i38:
move.l #e2coul1,$ffff825a.w
move.b #1,$fffffa21.w
move.l #e2i39,$120.w
bclr #0,$fffffa0f.w
rte

e2i39:; Avant-dernière inteeruption HBL.
move.l #e2coul1,$ffff825a.w
move.b #1,$fffffa21.w; laisser toujours 1.
move.l #e2i40,$120.w
bclr #0,$fffffa0f.w
rte

e2i40:; Dernière interruption HBL.
move.l #e2coul2,$ffff825a.w
;bset #3,$fffffa17.w
bclr #0,$fffffa0f.w
bclr #0,$fffffa13.w
rte

Cet exemple montre des routines qui traitent l’interruption des lignes d’écran (« HBL = horizontal blank line »).
On peut remarquer qu’elles se chaînent (par le move.l #e2i39,$120.w). En effet, au lieu de lire le contenu d’un tableau par indirection et d’incrémenter un index à chaque appel, j’ai préféré que chaque routine ne lise qu’une simple variable, puis elle modifie le vecteur (l’adresse de la routine) de l’interruption.
Ainsi, le code est plus rapide. Par contre il a nécessité l’écriture de 40 routines différentes. J’avais bien sûr utilisé un générateur de macros pour les créer.
Le gain peut paraître faible, mais il a lieu plus de 15000 fois par seconde. Sur ce type de machine, il faut savoir économiser le moindre cycle d’instruction.

À noter que des instructions d’indirection complexe sont apparus avec les versions suivantes du 68000. De plus le cache RAM change le calcul du temps d’exécution sur ces processeurs.
L’optimisation n’est pas toujours une activité pérenne, elle n’est efficace que temporairement, dans un contexte précis. Toutefois, ses principes restent valables et utiles.

La gué-guerre 68000 contre 8086

Cet exemple montre également que le 68000 gère effectivement les données et les adresses en 32 bits.
À titre de comparaison, son concurrent, le 8086, ne gère que des données en 16 bits et doit donc s’y prendre à plusieurs fois pour une seule donnée. De plus, sauf erreur, le 8086 ne peut pas écrire une valeur dans une variable (une case mémoire) en une seule instruction: il doit passer par l’intermédiaire d’un registre. D’où le véritable jonglage permanent de registres, d’autant que ses registres sont, en plus, spécialisés: on ne peut tout faire avec n’importe quel registre. Et hop, vive la jongle !
Et je ne parle pas de cette ignominie du 8086 que sont les Segments.
On voit ici par contre que le 68000 peut en une seule instruction écrire une valeur 32 bits dans une variable.

Si je voulais être mauvaise langue, je dirais qu’Intel et Microsoft ont ceci de commun qu’ils n’ont jamais eu besoin de sortir de bons produits, car il leur suffisait de représenter un standard.
Bon, c’est un peu faux car Intel a quand même eu un peu de concurrence avec AMD, durant quelques courtes années. 😉

Enfin bon, les compilateurs cachent toutes les plaies (c’est à dire le code machine).

Les outils improbables

Pour mon travail, j’avais aussi créé des tas d’outils en tous genres, qui peuvent paraître bizarroïdes maintenant, mais qui à l’époque me rendaient de bons services.

  • L’assembleur en Reset
    Mon outil préféré était un assembleur-désassembleur-débogueur-éditeur appelé K-Seka. Il était très pratique car il avait tout en un seul programme. Un peu un IDE rudimentaire, quoi.
    Mon problème était qu’à cette époque il n’existait pas de protection de mémoire dans le système d’exploitation, donc un logiciel pouvait joyeusement faire planter le système entier !
    Inutile de dire qu’écrire un jeu, lequel manipule les circuits intégrés directement, ça fait planter souvent.
    J’avais donc modifié cet assembleur pour intercepter la remise à zéro du ST, lorsqu’on presse la touche Reset à l’arrière de la machine, et remettre le système en état de fonctionner, notamment remettre tous les circuits dans un état valide, puis donner la main au débogueur de K-Seka.
    Imaginez: vous lancez votre jeu, il plante, vous pressez le bouton Reset, et hop!, vous vous retrouvez dans votre IDE. Magique !
    Quel gain temps, comparé avec devoir redémarrer la machine et recharger l’IDE.
  • Un filtreur graphique en simili-4096 couleurs
    Je m’en servais pour récupérer les images du numériseur, via un magnétoscope, et pour les traiter: réduction à 512 ou à 16 couleurs avec des contraintes de palette, détramage, débruitage, moyennage sur plusieurs images (vue la faible qualité des magnétoscopes), ajustage de la luminosité, séparation du fond, etc.
    J’avais besoin d’afficher les images dans un maximum de couleurs, or comme le ST était limité à 16 couleurs simultanées, j’utilisais à la fois la technique du changement de palette 3 fois par ligne, plus un clignotement (tramé pour ne pas trop me bousiller le cerveau), et enfin un tramage en affichage.
  • Des éditeurs de niveaux
    Forcément, ils sont nécessaires pour créer le décor des jeux, placer les obstacles ou les personnages, etc.
    Je pouvais lancer une version limitée du jeu à tout moment pour tester le niveau, puis revenir à l’éditeur.
    Ce type d’outil était programmé en Basic (avec GFA-Basic), car on programme beaucoup plus vite ainsi qu’en langage machine, avec moins d’erreurs, et que la vitesse d’exécution ne pose pas de problème.
    Le GFA-Basic permettait aussi d’écrire une interface graphique simple sans trop d’effort.
  • Un constructeur de macro-instructions
    Pour des besoins particuliers, j’avais besoin de créer des morceaux de code machine assez longs et répétitifs mais nécessaires pour ne pas perdre de précieux cycles d’exécution.
    Le 68000 disposait par exemple d’une instruction Movem qui pouvait à elle seule empiler plusieurs ou même tous les registres du processeur. Il était courant de la détourner de son but pour copier ou écrire des blocs complets de mémoire avec un minimum d’instructions.
    « movem.l d1-d7/a0-a5,-(a6) » peut ainsi écrire 52 octets en une seule instruction. Toutefois, cela occupe les registres. J’avais donc écrit différentes macros pour gérer les instructions et les registres en fonction de l’effet désiré, avec des paramètres.
    C’est typiquement le genre d’outil qui aujourd’hui ne s’utiliserait que dans un contexte très particulier, comme dans une routine essentielle dans un jeu, ou dans une analyse ou un tracé en boucle pour un robot par exemple.
  • Un formateur de disquettes avec sur-capacité et « protection »
    Une fois que j’ai su programmer le circuit contrôlant le lecteur de disquettes, j’ai commencé à jouer avec la capacité des disquettes. Sur le ST on pouvait ainsi facilement passer de 720 ko à 800 ko en toute sécurité, voire 880 ko avec des ralentissements et une fiabilité incertaine.
    On pouvait aussi formater d’une façon particulière pour éviter les copies pirates. À cette époque il y avait une vraie escalade entre les « déplombeurs » et les éditeurs de logiciels. Chaque nouveau type de protection entrainait une réplique sous forme d’un logiciel de copie.
    Cela rendait ce domaine assez stratégique et également intéressant d’un point de vue intellectuel.
    Ce genre de connaissance m’a permis d’écrire récemment un logiciel de récupération des vieilles disquettes ST, sous Windows. Il m’a fallu réviser le mode MFM , pour me rappeler des codes de correction de données et autres joyeusetés, ça m’a un peu rappelé le bon vieux temps.🙂
    À l’époque j’avais écrit un mode de protection de disquette dont je m’étais servi pour envoyer une démo de jeu à des éditeurs. Je me rappelle encore l’un d’eux se plaignant de n’être pas parvenu à copier la disquette pour ses collaborateurs. Ah ah ! Il avait essayé tous les logiciels de copie du marché, en vain, ça m’avait bien fait rire (ce qu’on peut être bête, à c’t’âge ! Je parle de moi..).
  • Un module lecteur de fichiers en multi-tâches
    Il permettait de charger des fichiers en RAM durant un jeu tout en continuant l’activité en cours. Par exemple, on pouvait jouer à un niveau pendant que le ST chargeait le prochain niveau.
    Ce genre de chose nécessitait une bonne connaissance du contrôleur de disquette, pour tout gérer par interruptions. En comparaison, le système du ST bloquait tout l’ordinateur lorsqu’il accédait à un fichier, et il ne savait pas accèder en parallèle à plusieurs fichiers.

Etc. J’ai presque tout oublié, tellement ces outils étaient nombreux et souvent particuliers à un besoin temporaire.
C’était vraiment l’époque de la débrouille et de la créativité. Il fallait à tout moment être assez inventif et ingénieux pour trouver une méthode qui permettrait d’obtenir un meilleur résultat, ou bien qui faciliterait ou raccourcirait le travail.
D’où ma façon personnelle de programmer: faire plus au départ pour faire beaucoup moins sur l’ensemble d’un projet. Ce que certains appellent perdre du temps pour mieux en gagner.

Car crash

Une vidéo est disponible sur Dailymotion. Malheureusement, on dirait que le lecteur flash la limite à 25 fps au lieu de 50.

C’est un jeu de voitures, comme on peut le voir.





Tramage et clignotement:
La première image introduit le jeu, le temps de le charger (en environ 40 secondes).
Pour améliorer l’image, j’ai utilisé un système de clignotement des pixels, histoire de simuler un dégradé de 16 niveaux d’orange au lieu de 8.
Mais là où j’ai passé du temps, c’est sur une chose qui ne se remarque pas: la structure du « tramage ». J’appelle tramage l’effet de points qui comble en quelque sorte le manque de niveaux dans le dégradé. Il existe des formules de tramage toutes faites, mais elles donnent un résultat soit trop grossier soit trop régulier. D’où du temps passé à chercher une meilleure méthode, adaptée à une basse résolution et de gros pixels. C’est par exemple un domaine de recherche pour les ingénieurs travaillant pour les fabriquant d’imprimantes.

Palette variable verticalement:
Sur les images du jeu proprement dit, on voit que pour améliorer les couleurs (problème récurrent sur ces machines, jusqu’en 1993 environ), j’ai utilisé tout autant le tramage (notamment dans le ciel) que le changement de palette vertical par interruption (comme décrit ci-dessus pour Claustrauphobia).

Calculs en 3D:
Contrairement à presque tous les jeux de voiture de l’époque, les calculs de la route étaient en vraies 3D. Il faut savoir que ce type de microprocesseur ne dispose pas d’unité de calculs en virgule flottante, et qu’une simple multiplication entière 16 bits * 32 bits prend environ 60 à 200 cycles d’instruction (à mon souvenir). Lorsqu’on doit effectuer beaucoup de calculs, et qu’on tente d’obtenir 50 images par seconde, ça devient vite un casse-tête qui nécessite des tas de caches de pré-calculs et d’optimisations diverses et variées.

Zoom pré-calculé et étirement vertical:
Les « sprites » (les images qui se déplacent sur l’écran) avaient, pour leur zoom, une taille variable pré-calculée (encore une petite optimisation), mais comme cela occupait finalement beaucoup de place en RAM (imaginez: toutes les tailles affichables d’un sprite !), j’avais fait une autre optimisation spéciale aux tailles les plus grandes: les lignes horizontales y étaient dupliquées lors du dessin.
Sur une image, on voit que le palmier le plus grand est en quelques sortes « étiré » en hauteur, ses pixels sont rectangulaires, plus hauts que larges. C’est donc essentiellement pour une économie de RAM.
Petite astuce, grosse économie de RAM.

Le jeu était sensé comporter un aspect aventures, j’avais donc présenté des images fixes de décors accompagnant l’histoire.

La difficulté était que le ST ne dispose que de 16 couleurs simultanées, choisies parmi 512. On est loin des 16 millions modernes.
Pour pallier ce manque de couleurs, j’ai utilisé une technique assez classique sur cet ordinateur: afficher 16 couleurs différentes sur chaque ligne horizontale, ce qui permet de démultiplier les possibilités.
En fait c’est même plus que ça car on change trois fois la palette de 16 couleurs sur chaque ligne horizontale.
Si on regarde attentivement cette image, on voit qu’il y a parfois des petits traits pleins horizontaux: c’est lorsque l’algorithme que j’avais créé pour la circonstance n’a pas pu trouver assez de couleurs à un endroit donné, malgré toutes les variations de palette.
C’est beaucoup de complications pour un peu plus de couleurs. Le principal problème, c’est que cela occupe complètement le microprocesseur, et ce durant environ 64 % du temps. À noter que l’Amiga n’a pas ce problème puisqu’il dispose d’un circuit programmable capable de gérer à lui seul ce type de changement de palette, sans compter son mode HAM.

Conclusion

Comme on peut le voir, ce type de programmation consiste à passer des jours à optimiser chaque petit détail à peine visible à l’écran, mais dont la somme fera au final la différence entre un logiciel rapide et un autre saccadé (et souvent moche en prime).

Voila pourquoi selon moi c’est un genre de programmation très pointu, très formateur car on y apprend toutes sortes de techniques. Il nous rend très minutieux à cause des nécessaires optimisations, et très prévoyant car une erreur de structure dans un programme en langage machine oblige souvent à le restructurer en grande partie.

J’ai aussi écrit ce petit article car bien souvent lorsqu’un programmeur dit qu’il écrit des jeux, les professionnels du secteur informatique l’imaginent en train de jouer devant sa console toute la journée, alors que c’est bien souvent l’inverse: ce type de travail demande plus de technicité, de sérieux et de compétence que bien d’autres travaux de programmation.
En ce qui me concerne, créer une application bureautique me demande moins d’efforts, ce sont presque des vacances, en comparaison, du boulot bien pépère ! 😉

De l’époque des premiers ordinateurs familiaux, j’ai gardé le goût du travail bien exécuté, bien pensé au départ, minutieux et pérenne, et aussi la passion d’une certaine forme de programmation, où la créativité a autant sa place que le sérieux, car les deux se complètent.

Espérons que dans le futur la programmation reste un plaisir pour tous les programmeurs ! 🙂

 
Poster un commentaire

Publié par le 2012-09-03 dans Programmation

 

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s