Retour au menu principal

APOLLO AGC






Je suis un ingénieur informatique professionel, et j'ai connu le microprocesseur depuis ses débuts.
J'ai eu la curiosité de jeter un regard sur l'ordinateur de guidage Apollo qui a été rendu public.
J'ai lu la documentation du manuel opérateur, et c'est vraiment la plus bizarre qu'il m'ait été donné de lire, si bizarre qu'elle m'a fait me dresser les cheveux sur la tête quand je l'ai lue (et j'ai lu de nombreuses documentations techniques).
Le programme du module de commande lunaire est aussi três bizarre; je doute fortement qu'il ait piloté quoique ce soit; il ne pourrait même être compilé, c'est à dire transformé en code machine pour être exécuté.

Si je résume quelques uns des principaux problèmes de l'ordinateur Apollo, avant de rentrer plus dans les détails, je peux citer les points suivants:

- L'ordinateur Apollo utilise une technique de commutation de mémoire qui est absurde car elle n'utilise pas la pleine capacité du système d'adressage, et mène à gaspiller du temps et de la mémoire qui sont très limités sur l'ordinateur Apollo; et commuter de la mémoire exécutable n'a pas de sens, car cela signifie que le code qui suit l'instruction de commutation ne sera jamais exécuté.

- L'ordinateur Apollo n'a pas le jeu minimal basique d'instructions que tout processeur a d'habitude, mais a à la place des instructions alambiquées qui sont très peu pratiques à utiliser.

- L'ordinateur Apollo fait des choses inutiles qui gaspillent du temps de calcul (comme sauver le contenu de l'instruction suivant l'appel à un sous-programme en plus de sauver son adresse qui est la seule chose qu'il devrait sauver).

- L'ordinateur Apollo fournit des instructions qui calculent quelque chose de si bizarre dans l'accumulateur (principal registre du processeur) que cela est équivalent à détruire son contenu, et ainsi rend ces instructions pratiquement inutilisables.

- L'ordinateur Apollo a des instructions qui ne requièrent pas un paramètre qui serait pourtant nécessaire pour leur fonctionnement, ou inversement requièrent un paramètre qui est inutile pour leur fonctionnement.

- L'ordinateur Apollo a des instructions qui ne sont pas claires; elles ne spécifient pas vraiment ce qu'elles font.

- L'ordinateur Apollo est dit capable de faire du temps réel (le temps réel permet à plusieurs tâches de s'exécuter en parallèle), et pourtant il n'a même pas l'environnement minimal qui serait nécessaire pour permettre au temps réel de fonctionner (pas de pile, et pas de fonction pour gérer le temps réel).

- L'ordinateur Apollo a des instructions qui gaspillent inutilement du temps processeur (comme les instructions "non programmées" qui comptent des impulsions matérielles; de telles instructions n'ont jamais existé dans aucun processeur pour la bonne raison qu'elles n'ont pas de raison d'y exister).

- Tout ce qui tourne sur un processeur vient d'instructions programmées; donc, le fait que quelque chose "volerait" du temps à l'ordinateur, comme ils disent, est hilarant...à moins qu'un ingénieur ait programmé quelque chose et ne l'ait pas dit aux autres, Hum!

- L'ordinateur Apollo utilise le système complément à 1 (qui fait une distinction entre +0 et -0 et est moins performant que le système complément à 2), quoique ce système de représentation était déjà caduque au temps d'Apollo.
Le chapitre III traite spécifiquement ce sujet.








I) Critique du manuel opérateur.


1) Même si dans ce qui suit vous avez quelque difficulté à suivre mes explications techniques, il y a quelque chose que tout le monde devrait pouvoir comprendre:
Dans la documentation il y a des lignes qui ont été barrées; Cela a t'il du sens? Sur une documentation tapée, vous ne barrez jamais du texte, vous l'enlevez simplement.

2) PAge 3: AGC memory "words" are 15 bits in size plus a parity bit.
But the internal registers are 16 bits long; their 16th bit was used as an "overflow".
The parity bit is electronically maintained and not visible to the user.
Why restrict the words to 15 bits instead of 16 bits which would have been more logical (especially since the internal registers are 16 bits).
The memory data words should have been 16 bits long, and the parity bit would have been a 17th bit invisible to the user.
And the "overflow" bit has nothing to do in the accumulator and other registers; it should have been a bit in a special register called "Status register" along with other status bits, like the carry bit which is missing.

2) Page 3: les mots mémoire de l'AGC sont de 15 bits plus un bit de parité.
Mais les registres internes sont de 16 bits; leur seizième bit était utilisé comme "overflow".
Le bit de parité est maintenu electroniquement et invisible à l'utilisateur.
Pourquoi restreindre les mots à 15 bits au lieu de 16 bits, ce qui aurait été plus logique (surtout si les registres internes sont de 16 bits).
Les mots mémoire auraient du être de 16 bits, et le bit de parité aurait été un 17ème bit invisible à l'utilisateur.
Et le bit d'overflow n'a rien à faire dans l'accumulateur et autres registres; il aurait du se trouver dans un registre spécial, appelé registre d'état, en même temps que d'autres bits d'état, comme le bit de carry qui est manquant.



3) Page 3: Le système de représentation complément à 1 était déjà caduque au temps d'Apollo (voir chapitre III).



4) Page 5: Ils disent les choses suivantes:
a) Les registres compteurs/timers sont incrémentés par des impulsions matérielles.
b) L'incrémentation d'un compteur/timer prend du temps CPU.
Ceci est contradictoire: Si l'incrémentation d'un compteur prend du temps CPU, celc signifie que l'incrémentation est logicielle, et elle n'est donc pas faite par impulsions matérielles.
Si elle faite par impulsions matérielles, l'incrémentation n'a pas de raison de prendre des cycles CPU.
Il n'y a rien de tel que des "séquences non programmées", même si ces séquences non programmées portent des noms bizarres tels que "PINC" ou "MINC".
L'idée qu'une impulsion matérielle répétitive puisse générer du temps CPU est hérétique.
Seul du code exécuté peut gégérer du temps CPU; un signal externe ne peut pas générer du temps CPU, à l'exception des interruptions qui provoquent l'exécution de séquences programmées.
Si un signal hardware générait du temps CPU pour incrémenter un compteur, cela serait parfaitement stupide (et un grand gaspillage de temps CPU) car un compteur électronique peut aussi bien le faire et la CPU a des choses plus intelligentes à faire que compter un signal matériel.
Lorsqu'un signal matériel répétiif doit être compté, un compteur électronique est utilisé; non seulement c'est couramment le cas, mais cela a toujours été le cas depuis le début (les compteurs électroniques sont des circuits basiques et ont existé avant même les processeurs).
Le processeur peut lire le compteur à travers un canal I/O; bien sûr, quand il le fait, cela prend des cycles CPU, mais il n'a pas à le faire à chaque impulsion matérielle, seulement quand il a besoin de l'information.
Un rebouclage du compteur peut aussi générer une interruption sur le processeur, permettant au processeur de faire un traitement quand un compte programmé d'impulsions a été atteint.


5) Page 6: Ils disent que le 16ème bit de l'accumulateur (registre spécial de la CPU) est utilisé en association avec le 15éme bit pour indiquer le dépassement (overflow).
Il ya d'autre bits d'état qui existent (le carry par exemple) et ces bits d'état sont placés dans un registre spécial appelé "status register".
L'accumulateur n'est jamais utilisé pour indiquer un dépassement; un dépassement de l'accumulateur positionne le bit de dépassement du registre d'état comme pour toute autre donnée mémoire.
Cela a toujours été le cas depuis les premiers processeurs.
Il n'y a pas de raison de gaspiller un bit de l'accumulateur pour y mettre l'indicateur de dépassement.


6) Page 8: LRUPT est un registre qui sert à mémoriser la valeur du registre L (compteur programme) lors d'une interruption.
Mais ce qui est comique est qu'il disent que le fait de se dérouter vers la routine d'interruption ne met pas à jour automatiquement le registre LRUPT avec le compteur programme (L); et la restauration de L depuis LRUPT n'est pas automatique non plus.
C'est tout à fait ridicule car L pourrait être plus commodément poussé sur une pile pour le sauver, et retiré de celle-ci au retour de l'interruption.
Si la sauvegarde et la restauration de LRUPT ne sont pas automatiques, alors ce registre n'est pas três utile.


7) Page 9: TIME1 est un compteur sur 14 bits (pourquoi pas sur 16 bits) qui reboucle toutes les 163,84 secondse; sur rebouclage de TIME1, le compteur de 14 bits TIME2 est automatiquement incrémenté.
Ceci est ridicule: Pouquoi est(ce que TIME1 et TIME2 ne sont pas sur 16 bits?
Ils pourraient compter une valeur 16 fois plus grande.
Avec une horloge de une milliseconde, ils pourraient compter plus que 31 jours, et avec une plus grande précision.

8) Page 9/10: TIME3 est un compteur incrémenté toutes les 10 millisecondes qui génére une interruption sur rebouclage.
TIME4 est également un compteur incrémenté toutes les 10 millisecondes qui génére une interruption sur rebouclage.
Ils disent que l'incrémentation de TIME3 est déphasée de 5 millisecondes relativement à celle de TIME4 de telle sorte que leurs interruptions ne puissent pas se produire simultanément.
Ensuite ils disent que TIME5 est aussi un compteur incrémenté toutes les 10 millisecondes qui génére une interruption sur rebouclage; mais rien n'est prévu pour synchroniser TIME5 ni avec TIME3, ni avec TIME4.
Cela signifie que la routine d'interruption associée à TIME5 peut interrompre celle associée à TIME3 ou celle associée à TIME4.

De plus les adresses des routines d'interruption associées à ces compteurs ne sont séparées que par 10 octal, et 10 octal cela lesse seulement 8 octets pour programmer la routine d'interruption; et cette routine d'interruption doit obligatoirement se terminer avec une instruction permettant le retour vers le programme interrompu (ce qui rend la routine d'nterruption encore plus courte).
Que voulez-vous programmer en moins de 8 octets (cela fait seulement 4 instructions, puisque chaque instruction occupe deux octets)?
C'est ridicule!
Bien sûr, il peut y avoir un saut vers une routine située ailleurs en mémoire, à un endroit où il y a plus d'espace pour programmer la routine; mais, pour programmer le saut, deux octets suffisent, et il y a alors 8 octets non utilisés; cela veut dire que l'intervalle entre les adresses des routines d'interruption aurait pu être réduit à deux octets.
Donc, pour résumer, l'intervalle entre les routines d'interruption est soit trop court, soit trop long.


9) Page 10: TIME6 esr un compteur incrémenté tous les 1/1600 de seconde par une séquence non programmée.
Cela ne veut absolument rien dire, une séquence est toujours programmée, sinon ce n'est pas une séquence.
Après avoir chargé TIME6 avec le compte correspondant au délai désiré, l'utilisateur autorise le compteur en positionnant un but dans un registre I/O.
Sur rebouclage de TIME6, une routine d'interruption est appelée, et ils disent que le bit d'autorisation de registre I/O est automatiquament remis à zéro, mais en fait il ne pourrait être automatiquement remis à zéro, ce serait à l'utilisateur de le faire.

10) Page 11: PIPAX est un registre qui veut dire "accélarateur pendulaire d'intégration d'impulsions".
C'est absolument ridicue: Les registres de la CPU ne portent pas de tels noms, ils portent des noms qui représente leur fonction qui est une fonction purement numérique.


11) Page 13: La manière dont la mémoire est mappée n'a pas de sens.
Les banques de mémoire peuvent être adressées sans avoir besoin de faire du mapping mémoire.
La commutation de mémoire consomme à la fois de la place et du temps de manière complétement inutile.
La mémoire RAM et ROM peut être n'importe où.
Les registres CPU qui permettent la commutation de mémoire sont une une pure hérésie; de tels registres n'ont jamais existé dans aucune CPU.

12) Page 15: Ils disent que le traitement d'une routine d'interruption peut être reporté si une interruption est déjà en cours de traitement et pas encore terminée (par une instruction RESUME).
Dans ce cas, pourquoi avoir déphasé l'incrémentation du compteur TIME3 relativement à celle du compteur TIME4 de manière à éviter la simultaneité de leurs interruptions, puisque celle qui arriverait en deuxième attendrait que la première soit terminée avant d'être traitée?

13) Page 15: Ils disent que la deuxième étape du traitement d'une routine d'interruption est de sauver l'instruction courante dans le registre BRUPT.
Cela n'a absolument pas de sens.
C'est l'adresse de l'instruction qui doit être sauvée, mais l'instruction elle-même ne serait jamais sauvée; aucune CPU ne l'a jamais fait.
Ils ne terminent pas la description du traitement de l'interruption, c'est à dire expliquer que l'instruction RESUME recharge le compteur de programme depuis le registre ZRUPT.

14) Page 16: Ils disent qu'une instruction est représentée de la manière suivante:
CCC AAA AAA AAA AAA
C'est à dire un code instruction sur 3 bits seulement, une adresse mémoire de 12 bits.
Normalement le code instruction ne se mélange pas avec l'adresse, mais en est séparé.
Le code instruction est typiquement spécifié sur un octet, ce qui autorise un jeu de jusqu'à 256 instructions.
L'adresse ne serait pas systématiquement spécifiée derrière le code de l'instruction, mais seulement si l'instruction requiert une adresse.
L'adresse serait fournie sur 16 bits dans le mot qui suit; elle n'aurait pas de raison d'être fournie sur 12 bits seulement; cela étendrait la capcité d'adressage par un facteur 16, et eviterait d'avoir à faire de la commutation d'adressage qui est extrêmement pénalisante, à la fois en place mémoire et en temps d'exécution.

Maintenant, dans l'AGC d'apollo, le code instruction est en fait mélangé avec l'adresse, parce que toutes les adresses ne sont pas permises pour l'adressage de mémoire.
Les adresses débutant à zéro ne sont pas utilisées (elles sont utilisées comme registres de la CPU) et sont utilisées comme complément du code instruction.
Par exemple, si l'instruction est "01000" octal, l'adresse est "1000" et est une adresse valide, dans ce cas, le code instruction "0" indique qu'il s'agit d'une instruction "TC" appelant le sous-programme à l'adresse 1000 octal.
Mais si l'instruction est "00001", le code instruction est aussi "0", mais l'adresse "0001" indique que l'instruction est en fait "XLQ" au lieu de "TC".
Cela veut dire que la connaissance du code de l'instruction n'est pas suffisante pour savoir quelle instruction exécuter, le processeur doit aussi analyser l'adresse avant de savoir quelle adresse exécuter; ceci est moins efficace que si le processeur pouvait directement savoir à partir du code instruction quelle instruction il doit exécuter.


15) Dans toutes les CPUs, y compris les três anciennes, il y avait un jeu d'instructions spéciallement dédié à faire des sauts conditionnels.
Ces instructions testent les bits de status positionnés par des opérations précédentes: cela peut être une addition, une soustraction, mais cela peut aussi être une simple comparaison.
Ces instructions incluent des sauts conditionnels tels que: Sauter is égal, sauter si plus grand sauter si plus grand ou égal, sauter si plus petit, sauter si plus petit ou égal, sauter si retenue, sauter si pas de retenue ..
Sur cette CPU, il y a seulement deux instructions conditionnelles: BZF et CCS.
BZF teste seulement si l'accumulateur est nul, et c'est totalement insuffisant, il devrait aussi y avoir une instruction testant le signe.
Il y a bien l'instruction CCS qui peut tester le signe d'une donnée mémoire.. le problème est que cette instruction détruit le contenu de l'accumulateur en calculant quelque chose dedans depuis la donnée mémoire de manière déterminée que l'utilisateur ne peut pas choisir; et elle réalise des sauts d'instruction suivant le résultat du test, ce qui veut dire que l'utilisateur doit rajouter des sauts derrière CCS pour exécuter la séquence désirée en fonction du résultat du test.
C'est fait pour être aussi peu pratique que possible, de manière totalement irrationnelle; aucun concepteur sérieux de CPU ne ferait des instructions aussi peu pratiques à utiliser.
Ce n'est pas que cette CPU fonctionne différemment des autres CPUs, c'est qu"elle fonctionne de manière irrationnelle.


16) Page 24: l'instruction "DTCB" (Double transfer control switching both banks) est dite réaliser un saut et commuter simultanément les mémoires fixe et écrasable.
Ceci est hilarant: cette instruction est si peu commode à utiliser qu'il est difficile d'imaginer dans quel contexte elle pourrait être utilisée.
Commuter juste une banque mémoire est déjà peu pratique à utiliser, mais commuter les deux banques en même temps a encore moins de sens!

17) L'instruction "DV" divise la pair de registres CPU A et L par une donnée mémoire dont l'adresse est donnée sur 12 bits.
Ils disent que cette instruction peut fonctionner suivant deux modes différents (diviser la paire A&L par une valeur simple précision ou par une valeur double longueur complémentée à 1 pointée par l'adresse).
Le problème est qu'il n'y a absolument rien qui dit à la CPU quel mode utiliser, puisqu'il y a juste l'instruction et l'adresse mémoire et pas d'information additionnelle.
la CPU doit être extralucide pour déterminer quel mode utiliser!
Ils donnent quelques exemples dont la division fonctionne dans une table.
Parmi ces exemples, ils disent que, lorsque +0,0 est dans la paire A,L, et +0,0 dans le registre K, alors l'instruction DV calcule un quotient de "0,999389648" dans le registre A.
Alors la division de 0 par 0 donne quelque chose différent de 0?
Comme c'est original!

18) Page 28: La manière dont l'instruction "INDEX" fonctionne est hilarante.
Elle est dite changer le comportement de l'instruction qui suit; ils donnent l'exemple qui suit:

INDEX A
TC JMPTAB
...
TCF LOC-2
TCF LOC-1
JMPTAB TCF LOC0
TCF LOC1
TCF LOC2
TCF LOC3

L'instruction TC appelle normalement un sous-programme, mais le fait qu'elle soit précédée par l'instruction INDEX fait qu'elle devient un saut conditionel suivant le contenu de l'accumulateur.
Ils disent que si l'accumulateur contient 0, elle saute à l'étiquette JMPTAB qui fait un saut à LOC0, si l'accumulateur contient 1, elle saute à l'instruction après JMPTAB qui saute à LOC1, si l'accumulateur contient 2, elle saute à la deuxième instruction après JUMPTAB sui saute à LOC2...
Mais là où cela devient hilrant, c'est que si l'accumulateur contient -1, elle saute à l'instruction avant JMPTAB, et si l'accululateur contient -2, elle saute à l'instruction encore avant.
L'instruction avant JMPTAB saute à l'instruction un octet avant l'étiquette LOC, et l'instruction avant cette dernière saute à l'instruction deux octets avant l'étiquette LOC.
Mais s'il y a un saut à l'instruction deux mots avant l'étiquette LOC, l'instruction un mot avant l'étiquette LOC sera aussi exécutée...à moins qu'il n'y ait un saut à une autre étiquette dans l'instruction deux mots avant l'étiquette LOC, mais dans ce cas pourquoi ne pas directement utiliser cette étiquette dans l'instruction "TCF LOC-2".


19) Page 32: L'instruction NOOP est également hilarante; non pacrce que cela n'a pas de sens d'avoir une instruction qui ne fait rien, car cette instruction existe effectivement dans des CPUs normales, et est utilisé pour fournir des délais courts.
Ce qui est hilarant est que cette instruction est dite prendre deux cycles si exécutée en mémoire écrasable et un cycle en mémoire fixe.
Dans des CPUs normale, cette instruction prend toujours un cycle, où qu'elle soit exécutée.

20) Page 33: L'instruction "RAND" est dite faire des ET des bits de l'accumulateur à partir d'un registre I/O.
Oh vraiment: Aucune CPU qui existe et a existé n'a jamais offert cette possibilité.
Il y a seulement une instruction pour lire une adresse I/O (quand elle peut être lue), et une pour l'écrire (quand elle peut être écrite).
Ceci est une instruction purement imaginaire.

21) Page 34: L'instruction "RESUME" permet de terminer une interruption et de retourner vers l'instruction qui était sur le point d'être traitée lorsque l'interruption s'est produite.
Ils disent que lorsque l'interruption se produit, l'instruction pointée par le compteur de programme est automatiquement sauvée dans le registre BRUPT de la CPU.
Au retour, l'instruction sauvée dans BRUPT est automatiquement exécutée; mais pourquoi la sauver dans BRUPT, puisqu'elle sera exécutée de toute manière au retour de l'interruption si le registre BRUPT n'est pas modifié.
Et si la routine d'interruption modifie le registre BRUPT pour qu'une autre instruction soit exécutée au retour, pourquoi ne pas directement l'exécuter, ce qui serait plus rapide, puisque, si la routine d'interruption copie l'instruction dans BRUPT, le temps de la copie s'ajoute à celui de l'exécution de l'instruction, alors qu'il ny a que le temps d'exécution si l'instruction est directement exécutée.
Ceci est totalement illogique et n'a absolument pas de sens!

22) Page 35: L'instruction "RETURN" permet de retourner d'un sous-proramme en chargeant le compteur de programme (le registre Z) depuis le registre Q qui contient normalement l'adresse de retour; l'instruction TC qui permet d'appeler un sous-programme sauve automatiquement l'adresse de retour dans le registre Q.
Puisqu'il n'y a qu'un registre unique pour sauver l'adresse de retour, un sous-programme ne peut pas en appeler un autre.
Dans une CPU normale, les adresses de retour sont sauvées sur une pile (une partie de mémoire qui est spécialement dédier à sauver/rappeler des données mémoire, des adresses de retour..) qui permet d'appeler un sous-programme depuis un autre sous-programme.
Dans cette CPU, l'appel d'un sous-programme (par l'instruction TC) depuis un autre sous-programme n'est pas possible, car l'adresse de retour est sauvée dans un registre unique; l'appel d'un sous-programme à l'intérieur d'un sous-programme résulterait en ce que l'adresse de retour du premier sous-programme soit écrasée par l'adresse de retour du deuxième sous-programme; ce qui rendrait le retour depuis le premier sous-programme impossible.

23) Page 39: L'instruction "TS" (transfer to storage) est hilarante.
Elle transfére d'abord le contenu de l'accumulateur vers une donnée mémoire dont l'adresse est indiquée en opérande.
Jusque là, rien d'anormal.
Mais ce qui est vraiment bizarre est ce qui est fait avec l'accumulateur.
Si l'accumulateur contient un dépassement, et seulement dans ce cas, ses bits sont mis à +1 oo -1 (qu'est ce que cela veut dire?), et la prochaine instruction est sautée.
Cette instruction est três peu pratique à utiliser.
Il devrait y avoir une instruction juste pour faire le transfert, et une autre pour réaliser cette fonction três spéciale sur l'accumulateur, mais avoir une instruction qui fait les deux en même temps n'a pas de sens, c'est pratiquement impossible à utiliser.

24) Page 43: Depuis la page 43, ils décrivent ce qu'ils appellent des "Pseudo-operations".
Si une instruction n'est pas une instruction existante dans le jeu d'instruction de la CPU, alors elle peut seulement être une "macro-instruction", c'est à dire un jeu programmé d'instructions qui est associé à cette macro-instruction.

Ils décrivent la pseudo-operation 1DNADR comme transmettant les deux mots pointés par l'adresse mémoire spécifiée...mais transmettant à quoi?
Il y a toujours une destination dans une transmission, dire que c'est juste transmis de signifie rien, si la destination de la transmission et la manière dont la transmission se fait ne sont pas spécifiés!

25) Page 45: La pseudo-opération "BANK" est dite repositionner l'adresse interne du yaYUL à la première adresse inutilisée la la banque de mémoire 5!
Mais que veut dire "adresse inutilisée"?
Comment la CPU peut elle savoir où se trouve cette "adresse inutilisée"?
Et s'il y a une partie de cette banque dans laquelle rien n'est programmé, quel est l'intérêt de positionner le compteur de programme sur une partie de la mémoire dans laquelle rien n'est programmé?
Cela n'a absolument pas de sens!

26) Ils décrivent la "pseudo-operation" STCALL de la manière suivante:
STCALL X
Y
et disent que X est dans une banque écrasable non commutable.
Mais ils ne décrivent pas ce que cette opération fait!

27) Page 48: Ils disent que la psudo opération "SETLOC" place la prochaine instruction ou pseudo opération à l'adresse spécifiée.
Mais qu'est ce que cela veut dire?
Si seulement une instruction est placée à cette adresse, exécuter depuis cette adresse exécutera seulement cette instruction.

28) Page 50.
L'instruction "STORE" mémorise probablement quelque chose à l'adresse spécifiée; Je dis "probablement" car ce qu'elle fait exactement n'est pas spécifié.
Ils disent que 'X' est soit dans la banque écrasable non commutable ou dans la banque écrasable courante.
S'il s'agit de la banque écrasable non commutable, l'adresse est fournie telle quelle dans l'instruction.
S'il s'agit de la banque écrasable courante, elle est calculée comme: 0400 * Numéro de banque écrasable + (X-1400)
C'est là où cela devient comique: Pourquoi fournir le numéro de la banque écrasable dans la formule, puisque cela peut seulement être celui de la banque écrasable courante et pas une autre?
Et que se passe t'il si dans la formule un autre numéro de banque écrasable est fourni à la place (ce qui est en principe interdit) ? Comment se comportera l'instruction?
Vous voyez la contradiction!


29) Pourquoi la commutationde banque de mémoire n'a pas de sens.

L'AGC a réduit l'adressage mémoire à 12 bits, alors que 16 bits devraient normalement être utilisés pour cet adressage mémoire, il n' a pas de raison d'être limité à 12 bits.
Cela permettrait d'adresser 16 fois plus de mémoire, et éviterait d'uiliser la commutation mémoire qui est três pénalisante.

La commutation de mémoire sur les données n'a déjà pas de sens.
Imaginez que vous ayez une donnée dans la banque 1, que vous vouliez l'ajouter à une donnée en banque 2, et mettre le résultat en banque 3.
Vous devez commuter sur la banque 1, prendre la première donnée, puis commuter en banque 2, ajouter la seconde donnée, et finalement commuter en banque 3, pour écrire le résultat.
Cela veut dire que vous devez programmer des instructions de commutation de banque, qu'elles prennent de la place en mémoire programme, ainsi que du temps d'éxécution
Ce gaspillage pourrait être évité s'il n'y avait pas de commutation mémoire.

La commutation de banque en mémoire programme a encore moins de sens.
Lorsque vous faites de la commutation en mémoire programme, aucune valeur n'est initialement prévue pour le compteur de programme.
Cela veut dire que l'exécution part normalement depuis le début de la nouvelle banque.
Bien sûr, il serait possible de placer une valeur dans une donnée que la nouvelle banque pourrait tester pour savoir où sauter, mais c'est plutôt compliqué à utiliser.
Lorque vous appelez un sous-programme ou vous sautez à une autre séquence, elle doit être dans la même banque de mémoire, elle ne peut pas être dans une autre banque; ce n'est pas pratique du tout; il n'y aurait pas de tel problème s'il n'y avait pas de commutation de banque; un programme unique pourrait être utilisé pour toute la mémoire.
Et si vous mettez une instruction de commutation de banque programme dans votre code, cela veut dire que le code qui suit cette instruction ne sera pas exécuté puisque le processeur aura commuté vers une autre banque de mémoire programme; si l'instruction qui suit n'a pas d'étiquette, elle ne sera pas atteignable.






Link to the CM program




II) Critique du programme de pilotage du module de commande lunaire.


Considérations generales sur le programme du module de commande.

a) Même si vous avez des difficultés à comprendre mes explications techniques, pensez vous vraiment que les commentaires(le texte après le caractère '#') va avec les instructions?

b) Les étiquettes sont les chaînes de caractères qui commencent sur le premier caractère d'une ligne; elles sont utilisées pour identifier une adresse mémoire dans le programme, et permettent de faire un branchement direct à cette adresse.
Les étiquettes doivent seulement contenir des lettres et des caractères numériques, ainsi que quelques autres caractères (comme underscore, par exemple).
Elles ne peuvent pas contenir des espaces, des caractères de ponctuation (.,;), des caractères numériques (+-*/).
Les étiquettes doivent également être uniques; il ne peut pas y avoir deux étiquettes de même nom dans le programme (autrement, lorsqu'un branchement est fait à cette étiquette, la CPU ne saurait pas à laquelle de ces étiquettes se brancher).
Un programme contenant une étiquette dupliquée ne peut pas être compilé, c'est à dire transformé en code machine, et donc ne peut pas être exécuté.

c) Dans les instructions requérant une adresse mémoire, cette adresse mémoire peut seulement être spécifiée comme un symbole (eventuellement avec une valeur ajoutée ou soustraite) ou éventuellement comme une adresse octale (généralement c'est une adresse c'est une adresse hexadécimale, mais dans cette CPU l'adressage octal semble être privilégié).
En aucun cas cette adresse mémoire ne peut contenir une multiplication ou une division (telle que "A/B") ou être une valeur numérique flottante (telle que "0.1234").

d) Plusieurs instructions ne sont pas référencées dans la manuel opérateur, ni comme instruction CPU, ni comme pseudo opération; et il est donc difficile de savoir ce qu'elles font!

e) Il y a plusieurs exemples d'instructions inutiles, comme sauver plusieurs fois de suite une même donnée qui n'est jamais modifiée (pas même initialisée) et jamais utilisée; ou une donnée mémoire qui est écrite avec une valeur, et réécrite avec une autre valeur sans que la valeur précédente ait été utilisée.

f) L'instruction TC permet d'appeler un sous-programme; dans la documentation il est dit qu'un sous-programme appelé par TC ne peut pas appeler un autre sous-programme à l'intérieur de son traitement pour la bonne raison que l'adresse de retour est sauvée dans un registre unique et non sur une pile.
.



Je donne ci-après quelque exemples d'incongruités dans le programme du module de commande.

1) L'instruction "BANK 35" commute sur la banque de mémoire 35; cette banque contient un autre programme, ce qui veut dire que les instructions qui suivent ne vont pas être exécutées puisqu'elles ne sont pas dans la même banque!
Et l'instruction qui suit l'instruction BANK n'a pas d'étiquette, ce qui signifie qu'il n'est pas possible de s'y brancher; elle n'a donc aucune chance d'être jamais exécutée!

2) L' instruction "SETLOC BODYATT" est utilisée pour placer la prochaine instruction à l'adresse "BODYATT".
Mais "BODYATT" n'est défini nulle part, seulement utilisé dans cette instruction.

3) L' instruction "BANK" permet de charger le compteur de programme avec la première adresse "inutilisée" de la banque fixe courante.
Cela veut dire que nous allons exécuter des instructions non programmées...três intéressant!

4) CM/POSE n'est pas une étiquette valide.
.

5) L'instruction "TC INTPRET" appelle un sous-programme INTPRET, mais ce sous-programme n'est défini nulle part, en tout cas dans le code qui est montré.

6) Dans le programme, il y a la séquence:
SETPD VLOAD
0
VN
L'instruction "SETPD" n'est pas définie dans le manuel opérateur.
Cela doit être une opération virtuelle!
De même pour "VXSC" et "VXV".

7) L'instruction "STORE -VREL" permet de mémoriser quelque chose à l'adresse mémoire spécifiée.
Cette pseudo-operation est documentée, mais la documentation ne dit pas ce qu'elle fait.
De plus l'adresse devrait être un symbole mémoire en ne pourrait en aucun cas être précédé du signe "-".

8) L'étiquette "REF COORDS" ne peut contenir un espace.

9) L'instruction ou pseudo-operation UNIT n'est pas documentée.
L'operand "LXA,1" est étrange.

10) L'étiquette "COORDS" apparaît deux fois; une étiquette peut seulement apparaître une seule fois; la duplication d'une étiquette génére toujours une erreur de compilation.
Si un branchement était fait à cette étiquette, le processeur ne saurait pas à laquelle de ces étiquettes se brancher.

11) L'instruction "STORE UXA/2" mémorise quelque chose (non spécifié) à l'adresse mémoire spécifiée.
Le second membre peut seulement être un nom symbolique (avec éventuellement une valeur ajoutée ou soustraite) ou une adresse octale, mais ne peut pas contenir une division.

12) L'instruction "DEC .019405" decremente la donnée mémoire dont l'adresse est donnée par le second membre de l'instruction.
Le second membre est une adresse mémoire et non une valeur numérique; ".019405" est une valeur numérique flottante et invalide en tant qu'adresse mémoire.

13) A different points du programme nous trouvons la séquence "PUSH CDULOGIC".
Apparemment elle pousse une variable CDULOGIC sur une pile; c'est bizarre pour les raisons suivantes:
- CDULOGIC n'est pas initialisé.
- CDULOGIC nest jamais modifié.
- CDULOGIC est répétitivement poussé sans avoir été modifié, et n'est jamais retiré de la pile, ce qui veut dire que son contenu n'est jamais utilisé.
- Et d'ailleurs, d'après la documentation, la CPU n'utilise pas de pile!

14) L'instruction "BZF DOGAMDOT" saute à l'étiquette DOGAMDOT" si l'accumulateur contient zéro; et sinon continue en séquence; l'instruction "TC NOGAMDOT" appelle le sous-programme "NOGAMDOT"; notez que ce sous-programme est imbriqué dans le programme principal; ce qui ne devrait pas être le cas, il devrait en être séparé.
Ce sous-programme NOGAMDOT appelle lui-même un sous-programme CORANGOV par l'instruction "TC CORANGOV"; normalement un sous-programme ne doit pas appeler un autre sous-programme (à moins de sauvegarder l'adresse de retour), mais il est vrai que NOGAMPDOT ne fait pas de retour; mais dans ce cas pourquoi ne pas l'appeler par "TCF" plutôt que par "TC"?
Le sous-programme CORANGOV est étrange:

CORANGOV TS L
TC Q
INDEX A
CA LIMITS
ADS L
TC Q

Il fait seulement quelque chose si l'accumulateur contient un overflow (sinon il revient avec la deuxième instruction "TC Q"); et au cas où l'accumulateur contient un overflow, il est mis à +1 ou -1 selon que l'overflow est positif ou négatif.
Si l'accumulateur contient un overflow, il est ajouté à une variable; cette variable serait "LIMITS".
Mais en fait il y a une instruction "INDEX A" qui modifie le sens de l'instruction "CA LIMITS"; dans la documentation ils donnent seulement un exemple de comment l'instruction "INDEX A" peut modifier le comportement de l'instruction "TC" (pour la transformer en un saut conditionnel).
Ici cela signifie que le contenu de l'accumulateur est ajouté à l'adresse de la variable "LIMITS" pour former l'adresse de la variable qui doit être ajoutée à l'accumulateur.
Mais l'accumulateur ne peut avoir qu'une seule valeur (suivant qu'il contenait un overflow positif ou négatif).
L'indexage est dont très limité.
Cela ne semble pas avoir beaucoup de sens.


15) Le sous-programme "NOGAMDOT" appelé par "TC NOGAMDOT" tranfére le contenu de l'accumulateur vers la donnée mémoire "GAMDOT" par l'instruction "TS GAMDOT".
Au retour de ce sous-programme (s'il y a retour), l'exécution continue en séquence et rencontre l'instruction "TS GAMDOT" à nouveau.
Entre ces deux instructions, GAMDOT n'a pas été utilisé (et n'est jamais utilisé); alors quel est l'intérêt de copier l'accumulateur dans GAMDOT, si c'est pour le copier à nouveau sans avoir utilisé la copie précédente?

16) Pour en terminer avec ce programme, la conclusion est que le programme contient 302 lignes generant 7027 octets.
Cela fait une moyenne de 23 octets par ligne.
Pour un programme assembleur, cela semble absolument délirant.












III) Les étranges équations de guidage





J'ai jeté un oeil sur les équations de guidage dans la documentation de la NASA.
Si vous les regardez de loin, abec un oeil non informé, elles peuvent sembler impressionantes et très sérieuses.
Mais, lorsque vous les regardez plus attentivement, vous commencez à voir plein d'incohérences et absurdités.
Je ne vais pas montrer toutes les absurdités que j'ai vues dans la documentation, je vais juste donner quelques exemples.









Cette fonction "maximum of", d'un extrait d'organigramme de la documentation de la NASA, est censée donner le maximum de deux valeurs; ces valeurs doivent bien sûr être uniques et précises; mais l'une de ces valeurs données en paramètre ne représente pas qu'une seule valeur, mais une variation entre deux valeurs, nous pouvons donc nous demander comment le maximum va être obtenu!
Cette formule est manifestement incohérente.









Dans cet extrait d'organigramme de la documentation de la NASA, un sous-programme pour transformer les coordonnées relatives du module lunaire en coordonnées relatives à la lune est appelée, mais elle est seulement appelée si le module lunaire est sur le site d'alunissage; ceci est complétement absurde, car le module lunaire a besoin des coordonnées relatives à la lune de manière permanente, pas seulement lorsqu'il est au dessus du site d'alunissage...autrement il aura quelque difficulté à alunir!









Dans cet extrait d'organigramme, un test est réalisé pour voir si une "solution Lambert" est couramment disponible; si non, un traitement est fait qui ne l'utilise pas; mais, si une solution existe, un test est fait pour voir si une variable "SF" est couramment égale à 1, auquel cas la solution Lambert n'est pas utilisée, alors pourquoi ne pas permettre de passer le test précédent si cette variable est égale à 1 puisque la solution Lambert n'est pas utilisée dans ce cas?









Il est évident que, si le guidage avait voulu être sérieux, chaque orgranigramme aurait été écrit de manière à faire sa tâche dans le temps le plus court possible.
Ceci est particulièrement important, car l'ordinateur n'est pas puissant, et avait de gros problèmes pour finir sa tâche à temps (au point de se bloquer parfois pour cette raison et de devoir redémarrer!).
Nous aurions donc pu nous attendre à ce que les organigrammes soient aussi optimisés que possible.









Dans cet exemple, un jeu de valeurs doit être calculé pour des valeurs de la variable P allant de 20 à 0; le calcul de ces valeurs utilise deux variables auxiliaires i et j associées avec la valeur P.
La variable j commence par la valeur 29 et décrémente depuis cette valeur, mais elle saute les valeurs 26 à 3,ce qui signifie qu'elle va directement de la valeur 27 à la valeur 2; la variable i part de la valeur courante de j, et saute également les valeurs 26 à 3.









Ceci est l'organigramme qu'ils donnent pour le calcul de la table de valeurs.
Est ce que je veux dire que cet organigramme ne fonctionne pas?
Oh non, il marche parfaitement.
Alors, où est le problème?
Le problème est comme suit: Lorsque la variable i a la valeur 27, les opérations suivantes sont réalisées dessus:
- La valeur 27 de i est comparée avec 0; est-elle nulle? Non, elle ne l'est pas, et elle va donc sur la branche "Non" du test.
- Puis la valeur 27 de i est décrémentée ce qui donne 26 comme résultat.
- La nouvelle valeur de i est alors comparée avec 26; est-elle égale à 26? Oui, elle est couramment égale à 26.
- Le test sort donc sur la branche "Oui", et la valeur 2 est mise dans la variable i.
Donc, lorsque la variable i sort du calcul avec la valeur 27, quatre opérations sont réalisées dessus.
Et, pour la variable j, c'est exactement la même chose: Quatre opérations sont réalisées sur la variable j lorsqu'elle a couramment la valeur 27.









Maintenant voyons cet organigramme que j'ai corrigé.
Il fonctionne également, et les variables i et j seront traitées exactement dans le même ordre que dans l'organigramme précédent.
Lorsque i et j sont différents de 27, le même nombre d'opérations sont réalisées sur ces variables.
C'est lorsque i ou j a la valeur 27 que la différence apparaît: Lorsque la variable i sort avec la valeur 27, la valeur 27 est directement comparée avec la valeur 27, et, si elle a cette valeur, la valeur 2 est directement mise dedans, le calcul est appelé à nouveau pour le jeu suivant de valeurs; cela fait donc seulement deux opérations pour la valeur 27 au lieu de quatre dans l'organigramme précédent, que ce soit pour la variable i ou j.
Ceci signifie que ce nouvel organigramme fonctionne plus efficacement, et prend moins d'opérations que l'organigramme précédent, et sera donc plus rapide que ce dernier.









Vous allez maintenant dire: Les deux solutions fonctionnent, et la différence de traitement n'est pas très importante.
Elle peut ne pas être très importante, mais le point est QU'ELLE EXISTE!
Si le guidage utilise de manière permanente des processus non optimisés, il est manifeste que la tâche de guidage va perdre du temps sur son traitement.









Si l'AGC avait été un ordinateur puissant, et que plein de temps soit resté après qu'il ait fini sa tâche, cela n'aurait pas été un problème...Mais nous savons que l'AGC avait de gros problèmes de performance, et la fameuse alarme 1202 réultait du fait qu'il n'arrivait pas à terminer la tâche de guidage à temps parfois.









Un programmeur compétent écrit toujours ses programmes de manière à ce qu'ils soient aussi performants et optimisés que possible, même s'ils tournent sur un ordinateur puissant qui n'a pas de problème de temps de traitement comme l'AGC.
Il n'y a donc que deux solutions possibles:
- Ou bien ils avaient donné la tâche d'écrire les programmes de guidage à des ingénieurs incompétents, ce qui serait surprenant pour un projet aussi gros que celui d'Apollo.
- Ou bien les ingénieurs étaient compétents, mais ils n'avaient pas l'intention d'écrire un programme sérieux dont ils savaient parfaitement qu'il ne ferait jamis alunir un vaisseau spatial.
pour moi, il ne fait pas de doute que c'est la seconde hypothèse qui est la bonne.












IV) Le choix absurde du système de représentation binaire







Ce chapitre traite du système de représentation binaire qui a été adopté pour l'ordinateur d'APollo (AGC).









Au début des années 60, les ordinateurs étaient encore des grosses machines pas encore arrivées à maturité; elles n'existaient que comme grosses machines coûteuses, et seules les grosses sociétés pouvaient se l'offrir.
Ils ne fonctionnaient pas encore de manière optimale, et c'était le temps où les ingénieurs cherchaient des solutions qui étaient encore loin d'être aussi performantes qu'aujourd'hui.









Les ordinateurs ont d'abord utilisé le système de représentation binaire appelé "complément à 1", car c'était celui qui semblait le plus simple, le plus évident.
Dans cette représentation, les nombre négatifs sont juste représentés en inversant les bits des nombres positifs (i.e. un bit à 1 devient uun 0, et vice versa).
Ce qui est bizarre dans cette représentation est que 0 a deux valeurs différentes, une appelée "+0" et codée avec tous les bits à zéro, et l'autre appelée "-0" et codée avec tous les bits à un; pourtant, on nous a appris dans nos cours de maths que +0 et -0 représentent la même valeur!
Le fait que 0 pouvait être représenté avec deux valeurs différentes dans ce système de représentation n'a pas semblé troubler ceux qui furent les premiers à l'utiliser.









L'IBM 7090, un odinateur du début des années 60 (1962) a utilisé ce système de représentation.









L'UNIVAC 1100 de la même période a également utilisé ce système de représentation.









Toutefois, dès 1963, les ordinateurs ont commencé à utiliser un système de représentation appelé "complément à deux".
Dans ce système de représentation, les nombre négatifs sont les nombre négatifs du système complément à 1 (avec tous les bits complémentés) avec ajout d'une unité.
Donc la représentation (sur un octet) de -1 n'est plus 11111110 mais 11111110+1=11111111, c'est à dire la représentation de -0 dans le système complément à 1 (-1 prend la place de -0 dans le système complément à 2).

Donc, dans ce système de représentation, 0 a une valeur unique, et non plus deux valeurs différentes comme dans le système complément à 1.
La conséquence de ceci est que les nombre négatifs peuvent encoder un nombre de plus que les nombres positifs.
Dans le système comlément à 1, un mot de 16 bits peut représenter des nombres de -32767 à +32767, alors que, dans le système complément à 2, le même mot de 16 bits peut représenter des nombre de -32768 à 32767.
Vous aller penser que, si c'est la seule différence, cela semble plutôt pauvre pour expliquer le succès du complément à deux, et l'abandon du système complément à un, mais en fait la différence est bien plus importante que cela, comme je vais vous l'expliquer.









Le premier ordinateur (où l'un des premiers) à utiliser le système commplément à deux est le PDP-5 de DEC.
Il a été développé en 1963, et offert au public en 1964.









DEC a fait un successeur au PDP-5, le PDP-8, utilisant également le système complément à deux, et qui a été développé en 1964, et offert au public en 1965.









En Avril 1964, IBM a annoncé l'IBM 360, son premier ordinateur à utiliser le système complément à deux.
Cet ordinateur a été utilisé par la NASA.









Et, en ce qui concerne les microprocesseurs, qui ont commencé à apparaître dans les années 70 (ici le premier d'entre eux, l'intel 8080), ils ont utilisé le système complément à deux depuis le début, et jamais le système complément à un.
Alors, pourquoi les constructeurs d'ordinateur ont-ils délaissé le système complément à un pour le système complément à deux?









Je vais d'abord avoir un mot à propos de l'addition de bits, les bits sont additionnés en prenant en compte une retenue (carry) qui est produite par l'addition précédente de bits, comme dans les additions décimales que vous avez l'habitude de faire.
La partie supérieure de la figure montre l'addition de bits lorsqu'il n'y a pas de retenue, et la partie inférieure l'addition de bits lorsqu'il y a une retenue.
J'indique en bas à droite de chaque addition la retenue qui est générée par l'addition de bits, et qui est prise en compte dans l'addition suivante de bits.









Cette animation montre le mécanisme de l'addition de -0 avec +1 dans le système complément à un.
Bizarrement, le résultat obtenu est ...+0!
Pourtant on nous appris dans nos cours de maths que 1+0=1 et non 0.
Toutefois, cela ne veut pas dire qu'un ordinateur utilisant le complément à un ne peut pas faire correctement des additions, car le dépassement est ensuite testé pour mettre à jour l'addition dans une seconde passe.









Cette seconde animation montre le mécanisme de l'addition de -1 avec -2 dans le système complément à un.
Bizarrement, le résultat obtenu est...-4!
Pourtant on nous a appris dans cours de maths que -1 ajouté à -2 fait -3 et non -4.
Une fois de plus le test de dépassement permet de corriger le résultat de l'addition et d'obtenir le résultat correct.
Mais cela montre que le résultat des additions n'est pas immédiat et requiert des tests complémentaires.









Maintenant voyons comment une addition de nombre négatifs marche dans le système complément à deux:
Si nous ajoutons -1 à -2, nous obtenons...-3, c'est à dire directement le bon résultat, pas besoin de le mettre à jour comme dans le système complément à un.
Ceci signifie que le système complément à deux donne des résultats plus directs que le système complément à un, et évite de faire des tests.









Comme il vaut mieux faire matériellement les tests qui doivent être faits par le système complément à un plutôt que de les faire logiciellement, ce qui diminuerait de manière importante les performances du système, cela signifie que le système matériel est plus compliqué pour implémenter un système complément à un que pour implémenter un système complément à deux.
Et, même avec le matériel rajouté, les système complément à un reste moins performant que le système complément à deux.
Vous commencez donc à comprendre pourquoi le système complément à deux est largement préférable au système complément à un.









En 1964, la NASA a organisé des réunions pour parler du matériel d'Apollo, et les choix qui devaient être faits, en partculier pour le matériel électronique de l'ordinateur.









En particulier, il ont discuté à propos de l'utilisation des circuits intégrés pour résuire le volume de l'ordinateur.
C'était le moment de faire des choix judicieux pour le matériel et obtenir la meilleure efficacité pour l'ordinateur (i.e. minimisation du matériel, et optimisation des performances).









A cette époque, les avantages du système complement à deux sur le système complément à un étaient connus, et les constructeurs d'ordinateur commençaient à utiliser le complément à deux au lieu du complément à un pour un meilleur rendement de leurs ordinateurs, et réduire les coûts.
Les ingénieurs du MIT ne pouvaient l'ignorer (l'ingénieur qui a developpé le PDP-5 était même diplômé du MIT).
!

Ils savaient qu'il existait deux systèmes de représentation, avec l'un d'eux plus performant que l'autre.









Ils avaient le choix entre le système complément à deux, requérant moins de matériel pour réaliser les mêmes fonctions, et plus performant...









...Et le système complément à un, moins aisé à implémenter matériellement, et moins performant.









Alors, qui a été couronné?
Le système le plus performant ou le moins performant?
Très étonnamment, c'est le second qui été choisi, contre toute logique!









Alors, pourquoi les ingénieurs du MIT, qui n'étaient certainement pas stupides, ont choisi ce qu'ils savaient être le système le moins performant, celui qui nécessitait le plus de circuiterie électronique pour obtenir un résultat moins performant que celui qui aurait été obtenu avec le système qu'ils ont rejeté?









Je doute que ce soit parce qu'ils avaient la "nostalgie" du système complément à un!









je pense plutôt que la raison était qu'ils n'avaient pas l'intention de faire un ordinateur sérieux qu'il savaient parfaitement qu'il ne ferait jamais alunir un module lunaire.












V) La gestion des tâches dans l'ordinateur d'Apollo







J'ai jeté un regard sur le livre de Frank O'Brien "Apollo Guidance Computer, Architecture and operation".
Frank O'Brien commence par décrire la philosophie des systèmes temp-réel avec des tâches tournant concuramment.
Ce qu'il dit dans sa préface tient debout.
Puis il commence à décrire la gestion des tâches dans l'ordinateur d'Apollo.
Mais O'Brien n'est pas un spécialiste des systèmes temps-réel; il est plus un écrivain qu'un ingénieur informaticien.
Il n'est donc pas conscient de certains aspects absurdes sur la manière dont l'ordinateur d'Apollo était géré.
De mon côté, je suis un ingénieur informaticien spécialisé dans les applications temps-réel, et j'ai travaillé sur des processus high-tech.
Alors, ce à quoi O'Brien passe à coté, je peux le voir, et je vais le décrire dans ce chapitre.









Les ordinateurs modernes ont une pile.
Un pile permet de mémoriser des variables temporaires locales, et également de faire des appels imbriqués.
Dans un système multi-tâches; la pile rend plus commode l'échange des tâches.
Chaque tâche a sa propre pile, et, lorsqu'une tâche doit tourner, le pointeur de pile du processeur a simplement à pointer vers la pile du processus courant.
Une pile fonctionne suivant le principe "Dernier rentré - premier sorti"; lorsqu'une donnée doit être sauvée dans la pile, elle est poussée sur le sommet de la pile, et, quand elle doit être restaurée, elle est extraite du sommet de la pile; si elle est la dernière à avoir été poussée, elle sera la première à être extraite.









Une pile permet de gérer inteligemment des données locales, et de faire des appels successifs à des sous-programmes: A chaque fois qu'un sous-programme est appelé, son adresse de retour et ses paramètres sont poussés sur la pile; ce sous-programme peut aussi avoir des données temporaires qu'il pousse aussi sur la pile: ce sous-programme peut à son tour appeler un autre sous-programme, et l'adresse de ce nouveau sous-programme et ses paramètres seront à leur tour poussés sur la pile; lorsque le sous-programme se termine, ses données locales et paramètres sont extraits de la pile, ainsi que son adresse de retour qui lui permet de savoir où il doit retourner, c'est à dire l'adresse qui suit immédiatement l'appel au sous-programme; la seule limitation d'une pile est sa taille.








Maintenant, l'ordinateur d'Apollo n'avait pas de pile.
Il avait un registre unique pour sauver l'adresse de retour, appelé Q.
Lorsque le processeur exécutait une instruction du programme principal appelant un sous-programme, avant d'aller au début du sous-programme, il sauvait l'adresse de l'instruction suivant l'appel dans le registre Q.
Puis il commençait à exécuter le sous-programme.









Quand la fin du sous-programme était atteinte, le processeur rechargeait le compteur de programme avec le contenu de Q, c'est à dire l'adresse de l'instruction suivant l'appel, et le processeur pouvait alors reprendre le traitement du programme principal depuis cette instruction.










LE registre Q fonctionne donc parfaitement pour appeler un sous-programme depuis le programme principal.
Une pile n'est pas nécessaire pour un simple appel de sous-programme.









Maintenant supposons que le sous-programme appelé par le programme principal veuille appeler un second sous-programme à son tour.
Il doit également sauver l'adresse de l'instruction suivant l'appel dans le registre Q, mais il y a un problème, car Q contient couramment l'adresse de l'instruction suivant l'appel dans le programme principal.
Alors, que va faire le sous-programme?









Supposons que le sous-programme passe outre et ré-écrive Q avec l'adresse de l'instruction suivant l'appel au deuxième sous-programme, de sorte que le deuxième sous-programme puis revenir au premier sous-programme après qu'il se termine.
Le deuxième sous-programme commence à s'exécuter.









Le second sous-programme se termine, et le compteur de programme est rechargé à partir du contenu de Q qui contient l'adresse de l'instruction suivant l'appel au deuxième sous-programme.
Le premier sous-programme peut continuer son traitement.
Alors j'ai été trop pessimiste, cela peut fonctionner?









Non en fait, parce que, lorsque le premier sous-programme se termine, le compteur de programme est rechargé avec le contenu de Q; mais Q contient couramment l'adresse de l'instruction suivant l'appel au second sous-programme dans le premier sous-programme, et non l'adresse de l'instruction suivant l'appel au premier sous-programme dans le programme principal.
Donc, les instructions entre l'appel au second sous-programme et la fin du premier sous-programme vont être exécutées à nouveau, et ce indéfiniment...









Vous voyez donc l'effet d'avoir un seul registre pour sauver l'adresse de retour: Cela interdit de faire des appels imbriqués multiples.
Le programme principal peut appeler un sous-programme, mais un sous-programme ne peut appeler lui-même un autre sous-programme, sinon cela crée une boucle sans fin fatale dont l'ordinateur ne peut sortir.
Maintenant vous allez dire qu'il n'est pas forcément nécessaire qu'un sous-programme appelle un autre sous-programme, que cela ne vaut pas la peine d'implémenter un gestion de pile dans le processeur.
Peut-être, mais le programmeur doit en être consient, et être sûr de ne jamais appeler un sous-programme depuis un autre sous-programme.









Certes, nous pouvons accepter la simplification d'utiliser un registre unique pour gérer l'appel aux sous-programmes, ce qui peut se justifier par le fait de ne pas avoir à gérer de pile.
Mais il y a cependant quelque chose qui est moins justifiable:
Lorsqu'une interruption se produit, non seulement l'adresse de l'instruction interrompue est sauvée dans le registre ZRUPT, ce qui est parfaitement normal, mais de plus le contenu de cette instruction est sauvée dans le registre BRUPT, et ceci est beaucoup moins normal.









En effet, lorsque l'intettuption se termine, le compteur de programme est rechargé à partir du registre ZRUPT pour continuer le traitement à partir de l'instruction interrompue, ce qui est un processus parfaitement normal, mais de plus l'instruction interrompue serait mise à jour avec le contenu du registre BRUPT dans laquelle elle aurait été sauvée avant le traitement de l'interruption?
Pour quoi faire???
ceci est totalement inutile, car l'instruction interrompue a gardé son contenu pendant l'exécution de l'interruption, et il n'y a donc pas lieu de la recharger à partir du registre BRUPT!
Le processeur réalise donc des opérations qui sont totalement inutiles; cela complique inutilement le traitement de l'appel, et de plus cela gaspille des temps de cycle, car le fait de sauver dans et restaurer depuis le registre BRUPT prend des temps de cycles au processeur.
La gestion du registre BRUPT n'est donc absolument pas justifiée par la simplification, bien au contraire, car elle crée une complication complètement inutile, et gaspille de la puissance de l'ordinateur (qui n'en a déjà pas beaucoup).









Nous allons maintenant voir comment la gestion des tâches était faite sur l'ordinateur d'Apollo.
Le principe était que chaque tâche allouait un jeu de ressources; ces ressources consistaient en un "core set" dans lequel la tâche mémorisait ses principaux attributs (son addresse courante, sa priorité...), et une zone plus importante appelée zone VAC, dans laquelle la tâche pouvait mémoriser les variables que lesquelles elle travaillait spécifiquement.
La tâche pouvait aussi utiliser des données globales qui pouvaient être partagées par d'autres tâches.
La première opération qu'une tâche lancée faisait était d'allouer ces ressources depuis des blocs de mémoires réservés pour ces ressources.
Cela signifie qu'il devait rester au moins un core set dans le bloc des core sets, et une zone VAC dans le bloc des zones VAC; lorsque la tâche avait trouvé un core set et une zone VAC, elle marquait le premier mot de ceux-ci comme étant reservés, de sorte que les autres tâches ne puissent les prendre.
Inversement, lorsqu'une tâche se terminait, elle libérait ses ressources en démarquant les premiers mots des ressources comme étant libres.
Mais ce n'était pas parce qu'une tâche avait alloué des ressources qu'elle pouvait immédiatement tourner.
Les tâches de priorité supérieure avait précédence sur les tâches de priorité inférieure.
Supposez quer deux tâches soient couramment définies; un tâche P3 de priorité 3, et une tâche P1 de priorité 1; comme P3 a une priorité supérieure à P1, il est couramment en train de tourner, et P1 attend que P3 se termine pour pouvoir tourner à son tour.
Un registre NEWJOB indique si la tâche couramment en train de tourner est la plus prioritaire ou non; si c'est le cas, NEWJOB contient 0, et sinon NEWJOB contient l'adresse du core set d'une tâche plus prioritaire en attente de pouvoir tourner.
Généralement NEWJOB contient 0.









A présent arrive une tâche P2 de priorité 2 intermédiaire entre les priorités de P1 et P3.
La première chose que P2 fait est d'allouer ses ressources, soit un core set and une zone VAC, et d'initialiser le core set (le marquant comme réservé, mettant dedans l'adresse de départ du programme, sa priorité...).









Mais P2 voit qu'il est moins prioritaire que la tâche courante P3, et il ne change donc pas le contenu de NEWJOB qui reste à 0.
P3 continue donc son traitement sans être dérangé par P2.









P3 atteint sa fin et libère les ressources qu'il avait réservées (le core set et la zone VAC).
Le processeur recherche à présent la tâche la plus prioritaire parmi les tâches en attente en examinant les priorités mémorisées dans les core sets, et trouve que c'est P2.









Le core set et la zone VAC de P2 sont copiés dans les premiers core set and zone VAC qui sont toujours ceux de la tâche en cours d'exécution.
Le compteur de programme est chargé avec l'adresse de la première instruction de P2 qui commence à se dérouler.









P2 se termine finalement, et libère ses ressources (le core set et la zone VAC).









A présent P1 est la seule tâche restante; et donc son core set et zone VAC sont copiés dans les premiers core set et zone VAC qui sont ceux de la tâche en cours d'exécution, et elle commence à se dérouler.









P1 se termine finalement, et libère ses ressources.
Il n'y a plus de tâche restante, et il disent que dans ce cas ils font tourner une pseudo tâche appelée "DUMMY JOB".
Mais ce cas a peu de chance de se produire, car ils disent que les tâches SERVICER s'accumulaient comme elles étaient retardées par les autres tâches, et il y avait donc toujours plusieurs tâches restantes (jusqu'au point d'épuiser les ressources!).









Supposons à présent que la tâche de plus haute priorité courante P2 ait une priorité 2.









Arrive à présent une tâche P3 de priorité 3 plus haute que les priorités des deux autres tâches.
La première chose que P3 fait est d'allouer ses ressources, soit un core set et une zone VAC, et d'initialiser le core set (le marquant comme réservé, mettant dedans l'adresse de départ du programme, sa priorité...).









Mais, contrairement à l'exemple précédent, P3 voit qu'il a une priorité plus haute que la tâche courante P2.
Il ne laisse donc par le registre NEWJOB inchangé, mais au contraire y écrit l'adresse de son core set, afin que la tâche P2 soit informée qu'une tâche plus prioritaire est en attente d'exécution.









P2 continue de s'exécuter jusqu'à ce qu'il rencontre un point de test du changement de tâche.
Un point de test est décrit comme étant un jeu des deux instructions suivantes:
CCS NEWJOB
TC CHANG1
Si NEWJOB contient couramment 0, la tâche courante est couramment la plus prioritaire, et le test de NEWJOB par CCS fait que le processeur saute l'instruction suivante; la tâche continue de d'exécuter; mais, si NEWJOB ne contient pas zéro, le test de NEWJOB par CCS fait que l'instruction suivante est exécutée, laquelle appelle un programme qui faisait l'échange des tâches pour remplacer la tâche courante avec la tâche plus prioritaire.
C'est ici le cas, NEWJOB est non nul, et contient l'adresse du core set de P3.









Conséquemment, l'échange des tâches doit être fait.
Le core set et la zone VAC de P2 sont d'abord mis à jour avec les premiers core set et zone VAC de la tâche en cours (précédemment P2), de sorte que P2 puisse recouvrer son contexte d'exécution lorsqu'il reprendra la main.









Puis le core set et la zone VAC de P3 sont copiés dans les premiers core set et zone VAC de la tâche en cours, et P3 commence de s'exécuter depuis son début.









P3 se termine finalement et libère ses ressources (core set et zone VAC).









Après la fin de P3, P2 est à présent la tâche la plus prioritaire.
les premiers core set et zone VAC de la tâche en cours sont mis à jour avec le core set et zone VAC de P2; comme le compteur de programme de P2 a été mémorisé dans son core set, P2 peut repartir depuis le point où il a été interrompu par P3, et non depuis son début.









P2 se termine et libère ses ressources (core set et zone VAC).









P1 est à présent la seule tâche restante, et son core set et VAC area sont donc copiés dans les premiers core set et VAC area de la tâche courante, et P1 commence à s'exécuter.









P1 se termine finalement, et libère ses ressources.









Ce processus est inutilement compliqué.
le regisre NEWJOB n'est même pas nécessaire.
Dès que P3 arrive, au lieu d'attendre que P2 atteigne un point de test, il serait plus pratique si le core set et la zone VAC de P2 etaient immédiatelent mis à jour avec les premiers core set et zone VAC de la tâche en cours (P2).








Et le core set et la zone VAC de P3 seraient copiés dans les premiers core set et zone VAC de la tâche en cours, de manière à ce que P3 puisse commencer à s'exécuter.









Avec cette gestion, P3 n'aurait pas à attendre que P2 s'aperçoive qu'il y a une tâche plus prioritaire en attente; il commencerait immédiatement à s'excuter dès que lancé, sans délai.









Dans la gestion qu'ils ont imaginée, comparée avec la gestion que j'ai décrite, il y a des opérations que la gestion que j'ai d'écrite n'a pas à faire, et qui surchargent le changement de tâche.
D'abord la tâche entrante doit mettre à jour le registre NEWJOB; mais ce n'est pas l'opération qui prend le plus de temps au processeur.
L'opération qui prend le plus de temps est que la tâche en cours doit régulièrement tester si une tâche plus prioritaire n'est pas arrivée, en insérant régulièrement ces deux instructions dans son programme:
CCS NEWJOB
TC CHANG1
Si elle ne fait pas ce test assez souvent, alors la nouvelle tâche entrante aura à attendre plus de temps avant d'être servie.
D'un autre côté, si la tâche courante fait ce test assez souvent, alors, peut-être que la nouvelle tâche entrante peut être servie assez rapidement après être arrivée, mais la tâche courante gâchera beaucoup de temps à faire ces tests.
Avec le processus que j'ai décrit, il y a pas de temps gaspillé à faire des tests, et la nouvelle tâche entrante est servie immédiatement.
Ils disent que le programmeur devrait s'assurer qu'il n'y ait pas plus de vingt millisecondes séparant deux tests successifs, mais comment voulez-vous que le programmeur estime ceci? C'est délirant!









Maintenant, vous pouvez dire: Dans le système que j'ai décrit, une tâche plus prioritaire peut interrompre la tâche courante à n'importe quel endroit qu'elle soit
La tâche courante ne peut pas choisir l'endroit où elle est interrompue.
La plupart du temps, cela n'a pas d'importance, car, lorsque la tâche courante travaille sur son propre VAC, comme elle est la seule qui y accède et le modifie, elle peut être interrompue à tout moment.
Mais, dans certains cas, la tâche courante peut désirer ne pas être interrompue durant une séquence limitée (lorsqu'elle lit ou modifie des variables globales aussi utilisées par d'autres tâches, par exemple).
Dans ce cas, il existe une solution possible.









Nous utilisons encore le registre NEWJOB dans cette solution, mais nous ajoutons un autre registre spécial que j'appellerai "PROTECT".
Généralement, les registres NEWJOB et PROTECT contiennent 0.









Lorsqu'une tâche plus prioritaire P3 est lancée, elle teste le registre PROTECT.









Si le registre PROTECT contient 0, cela signifie que la tâche courante n'est pas en train d'exécuter une zone protégée de code, et la nouvelle tâche P3, au lieu de mettre à jour P3, réalise l'échange de tâches, mettant à jour le core set et la zone VAC de P2 avec ceux de la tâche en cours, et copiant ses propres core set et zone VAC dans ceux de la tâche en cours.
Elle commence ensuite à s'éxécuter depuis son début.









Supposons à présent que la tâche courante veuille exécuter une séquence de code dans laquelle elle ne soit pas interrompue par une tâche plus prioritaire.
Elle met d'abord 1 dans le registre PROTECT, pour notifier le fait qu'elle exécute couramment une zone de code protégé.









Puis, alors que la tâche courante exécute son code protégé, une nouvelle tâche P3 plus prioritaire arrive.
P3 teste le contenu de PROTECT et voit qu'il est à 1.









P3 sait alors que la tâche courante exécute une zone de code protégé, et, au lieu de réaliser immédiatement l'échange des tâches, écrit simplement l'adresse de son core set dans le registre NEWJOB, de la même manière que dans la solution qui a été retenue dans Apollo.
Puis, elle ne s'exécute pas, mais attend que P2 la démarre.









P2 termine sa zone de code protégé, et écrit un 0 dans le registre PROTECT de manière à terminer la protection d'interruption par une autre tâche.









Immédiatement après avoir déverrouillé PROTECT, P2 doit tester NEWJOB pour voir si une tâche plus prioritaire a été lancée pendant l'exécution de la zone de code protégé.









Si c'est le cas (i.e. NEWJOB ne contient pas 0), P2 réalise lui-même l'échange des tâches et démarre P3, tandis qu'il se met en attente que P3 se termine avant de reprendre son propre traitement.









Je montre ici l'organigramme pour une nouvelle tâche entrante.









Et je montre ici l'organigramme pour la tâche courante lorsqu'elle veut exécuter une zone de code protégé dans laquelle elle ne veut pas être interrompue.









Dans cette solution, il y a encore des tests du registre NEWJOB, alors pourquoi ai-je protesté contre son emploi tel qu'ils l'ont prévu?
Pace que, dans cette dernière solution, l'usage de NEWJOB n'est qu'occasionel, exceptionel...









...alors que dans la solution Apollo, ce test est systématique et fait répétitivement à intervalles rapprochés.
Il va donc sucer une bonne part de la puissance de l'ordinateur.









Maintenant, quel était le principal problème de la gestion des tâches de l'ordinateur d'Apollo?
Le principal problème est qu'ils avaient donné la plus basse priorité à la tâche la plus importante, c'est à dire la tâche "SERVICER", qui était chargée du guidage (ce qui incluait l'acquisition des données, le calcul d'équations à partir de ces données et les données attendues qui résultaient en des corrections à appliquer au réacteur principal et les réacteur latéraux, et l'envoi des commandes correspondantes aux réacteurs).
Le résultat était que la tâche SERVICER était souvent interrompue par des tâches plus prioritaires qui retardaient son exécution.
La conséquence était que le temps d'exécution de SERVICER était souvent plus long que prévu, et dépassait sa période d'exécution (après chaque période une nouvelle tâche SERVICER était automatiquement lancée, car elle devait tourner périodiquement); cette période était de deux secondes, ce qui aurait normalement du suffire pour que la tâche SERVICER termine son traitement avant l'arrivée de la tâche SERVICER suivante: mais ce n'était souvent pas le cas, à cause de la perturbation créée par les autres tâches (y compris la tâche de gestion du clavier et de l'affichage).









Donc, à force d'être constamment retardée, et de ne pouvoir terminer son traitement à temps avant la tâche SERVICER suivante, les tâches SERVICER en attente s'accumulaient progressivement.
Et, à chaque fois qu'une nouvelle tâche SERVICER était lancée, elle réservait des resources des blocs de ressources disponibles (un core set and une zone VAC).
A un moment donné, il y avait tant de tâches SERVICER en attente, qu'elles avaient épuisé les ressources disponibles, et qu'il ne restait plus de core set ou zone VAC libre dans les blocs de ressources.









Si, à ce moment là, une nouvelle tâche plus prioritaire se présentait, elle essayait d'allouer ses propres ressources, mais elle n'y parvenait pas, car les ressources avaient toutes été prises par les tâches SERVICER en attente.
L'ordinateur était alors dans une situation qu'il ne pouvait gérer, car la nouvelle tâche entrante ne pouvait allouer les ressources dont elle avait absolument besoin pour tourner.









Dans cette situation, ils disent que la seule solution était un redémarrage de l'ordinateur.
Ils disent qu'ils avaient des solutions pratiques pour faire le redémarrage, pour minimiser le travail que l'ordinateur avait à faire pour revenir dans la situation dans laquelle il était avant le blocage, mais c'est du pipeau, un redémarrage n'est jamais anodin.
La réalité est que cette sutuation n'aurait jamais du se produire.
Et il existe des solutions pour ne pas la laisser arriver.









Une première solution serait que, même s'il y a plusieurs tâches SERVICER en attente, une seule devrait couramment réserver des ressources.
Supposez qu'une nouvelle tâche SERVICER soit lancée, et que la précedente soit encore en train de tourner (pour avoir été trop interrompue par des tâches plus prioritaires).









Puisqu'il y a déjà une tâche SERVICER en cours, cette nouvelle tâche ne réserverait pas de ressources (i.e. elle ne reserverait pas un core set et une zone VAC).









Au lieu de cela, elle incrémenterait simplement un compteur de tâche dans le core set de la tâche SERVICER courante, afin de l'informer qu'une nouvelle tâche SERVICER a été lancée pendant qu'elle s'exécutait.









Puis elle disparaîtrait simplement, et la tâche SERVICER courante continuerait à s'exécuter normalement (à moins d'être interrompue par une tâche plus prioritaire).









Lorsqu'elle se termine, la tâche SERVICER courante testerait son compteur de tâche dans son core set de manière à déterminer sa prochaine action.









Si le compteur de tâche est différent de 0, cela signifie qu'au moins une nouvelle tâche SERVICER a été lancée pendant qu'elle s'exécutait; dans ce cas, elle garderait ses ressources au lieu de les libérer, décrémenterait le compteur de tâche, et recommencerait depuis son début.









Et, si le compteur de tâche est nul, cela signifie qu'aucune tâche SERVICER est en attente, et la tâche SERVICER courante libérerait simplement ses ressources.
Avec ce processus, même s'il y a plusieurs tâches SERVICER en attente, et quelque soit ce nombre, seul un core set et une zone VAC seraient réservés pour toutes les tâches SERVICER en attente; il n'y a pas de danger que les tâches SERVICER épuisent les ressources disponibles, et l'ordinateur ne se bloquerait pas, et n'aurait jamais à redémarrer.









Même si cette solution n'est pas retenue, et qu'une nouvelle tâche SERVICER entrante réserve des ressources même si une autre tâcher SERVICER tourne déjà, il y a encore une autre solution pour empècher les tâches SERVICER d'épuiser les ressources disponibles.
Supposez qu'une tâche SERVICER soit couramment en train de tourner, et qu'une autre tâche SERVICER soit en attente.
Alors arrive une troisième tâche SERVICER.
Que devrait faire cette troisième tâcher SERVICER?
Allouer des ressources et attendre comme la seconde tâche SERVICER?









Pas du tout, il est tout à fait inutile que cette nouvelle tâche SERVICER attende...Parce que d'ici qu'elle puisse Il est donc préférable de juste la jeter; il n'est pas sain de laisser les tâches SERVICER s'accumuler, car il n'y a pas moyen que trop de tâches SERVICER accumulées puissent être servies à intervalles réguliers, et il est impportant qu'elles soient servies à un intervalle aussi régulier que possible, et pas un intervalle flottant.
Donc, le fait que les tâches SERVICER épuisent les ressources disponibles est loin d'être une fatalité et peut être aisément évité.









Tout d'abord, et rien que cela aurait pu suffire à ce que les tâches SERVICER ne s'accumulent pas et n'épuisent pas les ressources, la tâche SERVICER, qui est la tâche la plus importante, car elle assure le guidage du vaisseau spatial, aurait du se voir attribuer la plus haute priorité et non la plus basse.
Quelles sont les conséquences d'attribuer la plus basse priorité à cette tâche?









Dans son "Tales about the Apollo guidance computer", Don Eyles dit que le délai de réaction du réacteur était de 0,3 seconde, mais ils avaient initialement prévu qu'il serait de 0,2 seconde; le réacteur était donc en retard de 0,1 seconde sur la prévision; la conséquence, d'après Don Eyles, était qu'ils observaient de sérieuse perturbations dans le comportement du réacteur, qu'ils appelaient "throttle excursions" et dont je représente le graphe sur la figure, et ces perturbations pouvaient être potentiellement dangereuses, d'après Don Eyles, et mettre en danger le guidage du vaisseau spatial.









Alors ils prévoyaient de compenser le délai de réaction du réacteur en envoyant la commande 0,1 seconde plus tôt, de manière à faire comme si le délai de réaction du réacteur était seulement de 0,2 seconde.









Mais un ingénieur décida de na pas compenser, et il eut raison, car la NASA parvint à réduire le temps de réaction du réacteur de 0,3 seconde à 0,2 seconde, ce qui fit que la compensation n'était plus nécessaire, et ce qui fit disparaître les perturbations observées.









Maintenant imaginez, si la tâche SERVICER ne peut pas être servie régulièrement, et son exécution est constamment retardée de manière aléatoire, cela signifie que le moment auquel la commande est envoyée va varier de manière consistante, davantage que 0,1 seconde.
Donc, si le fait que la commande soit retardée de 0,1 seconde peut avoir de sérieuses conséquences, imaginez ce que cela peut donner si la commande est retardée davantage de manière aléatoire?
Ne serait-ce pas une bonne raison pour donner la priorité la plus haute à la tâche SERVICER, de manière à ce qu'elle puisse tourner régulièrement et envoyer les commandes à temps?









Et, si la tâche SERVICER n'avait pas à faire des test répétitifs pour voir si une tâche plus prioritaire n'est pas en attente, elle aurait économisé pas mal de temps pour son exécution, et elle aurait eu plus de chance de finir à temps!









Et aussi, s'il n'y avait pas eu des instructions de l'ordinateur (PINC and MINC) pour compter des impulsions matérielles (et peu importe que ces instructions soient appelées 'non programmées", car elles prennent quand même des temps de cycle à l'ordinateur)...









...Et que la tâche de compter les impulsions matérielles avait été dévolue à un simple compteur électronique que le processeur peut lire à tout moment avec un opération I/O (et l'opération I/O existe, elle est décrite dans la documentation!)...









...cela aurait permis de sauver une importante quantité de temps de cycle qui auraient été plus intelligemment utilisés pour le traitement de la tâche SERVICER qui aurait eu alors plus de chance de terminer à temps et d'être servie à intervalles réguliers au lieu d'intervalles irréguliers (jusqu'au point de provoquer l'épuisement des ressources à cause de la gestion absurde, et conséquemment le redémarrage de l'ordinateur!).









IL N'Y AURAIT JAMAIS DU AVOIR UN REDEMARRAGE DE L'ORDINATEUR, JAMAIS!
Et c'était parfaitement possible, même avec les ressources limitées de l'ordinateur, sans ajouter de complication au matériel (et même en le simplifiant).









Cela va même encore plus loin dans l'absurdité, car ils disent qu'un programme inconnu volait des temps de cycle à l'ordinateur, dégradant ses performances.
LOL, les pirates informatiques existaient même en ce temps!









Donc, définitivement, l'ordinateur d'Apollo n'était pas un ordinateur sérieux, mais un clown, juste bon pour le cirque!












VI) La mémoire de l'ordinateur Apollo






Ce chapitre traite de la mémoire à cordes de cores d'Apollo qui est supposée avoit contenu le programme d'Apollo.








De nos jours, la mémoire d'un ordinateur est contenue dans ces circuits intégrés.
Celui-ci est assez ancien, les circuits actuels sont plus petits et contiennent davantage de mémoire, mais relativement à la mémoire qui existait au temps d'Apollo, il est révolutionnaire et super concentré.
Ce type ce mémoire est appelé mémoire "RAM" (pour "Random access memory"), ce qui signifie wqu'il est posible à la fois d'ecrire dans et lire depuis ce circuit.
Mais ce type de mémoire perd son contenu lorsqu'il n'est plus alimenté.









Ce type ce mémoire est appelé "ROM" ("Read only memory") ou "PROM" (pour "Programmable read only memory"), et différemment du précédent, il ne peut être que lu et non écrit.
Mais il a l'avantage sur le précédent de ne pas perdre son contenu lorsqu'il n'est plus alimenté.
Ce type de mémoire est utilisé quand il doit contenir un programme résident qui doit toujours rester dedans, et permet à l'ordinateur de démarrer quand il est mis sous tension.
Il est aussi possible d'utiliser de tels circuits si l'ordinateur doit faire une tâche donnée lorsque mis sous tension.









Les PROM sont programmées à l'aide d'un dispositif spécial; un programme qui commande le programmeur de PROM est lancé, et l'utilisateur rentre son programme; le programme de commande envoie ensuite le programme de l'utilisateur au programmeur de PROM qui brûle les instructions dans la PROM, de sorte que celle-ci contienne définitivement ces instructions.
La PROM programmée contiendra toujours ces instructions, que l'ordinateur soit sous tension ou non (mais elle ne peut être dynamiquement écrite, différemment d'une RAM).









Il y a également des PROM spéciales appelées EPROM (pour PROM effaçables) qui peuvent être réécrites plusieurs fois, quoique non dynamiquement.
De manière à réécrire une EPROM, son contenu doit d'abord être effacé: pour ce faire, l'EPROM a une sorte de petite "fenêtre" qui peut être traversée par des rayons UV et qui permet aux rayons UV d'effacer le contenu de l'EPROM.









Lorsqu'une EPROM doit être reprogrammée, elle est mise dans un appareil spécial qui envoie des rayons UV à travers la fenêtre de l'EPROM; après quelque temps d'exposition, le contenu de l'EPROM est effacé, et elle peut être programmée à nouveau de la même manière qu'une PROM ordinaire avec le programmeur de PROM.
Bien sûr, ce n'est pas aussi rapide que l'écriture dans une RAM, qui est immédiate, mais cela permet de réutiliser plusieurs fois l'EPROM différemment d'une PROM ordinaire, tandis que l'EPROM peut conserver son programme lorsqu'elle n'est pas alimentée, ce qui n'est pas le cas d'une RAM.
Une EPROM est donc plus économique d'une PROM ordinaire lorsqu'elle doit être modifiée plusieurs fois.









Dans les années 50, 60, et 70 (au moins au début des années 70), les circuits intégrés n'existaient pas encore.
Les ordinateurs étaient de grosses machines qui n'étaient pas accessibles aux gens ordinaires, et que seulement les grosses industries pouvaient s'offrir.
La mémoire de ces ordinateurs consistaient en matrices de cores de ferrites; chaque core représentait un bit de mémoire; cela vous donne une idée du nombre de tels cores qu'il fallait pour avoir une quantité confortable de mémoire (quoique ces ordinateurs n'avaient pas autant de mémoire que les ordinateurs actuels).
En ce temps il n'y avait pas de mémoire morte qui gardait l'information lorsqu'elle n'était pas alimentée, car le besoin n'existait pas.









Lorsqu'un ordinateur était démarré, le système opératoire était d'abord lu depuis des bandes magnétiques, et mémorisé dans la mémoire à cores qui lui était réservée.









Puis les utilisateurs donnaient leurs programmes sur des cartes perforées (une instruction par carte, ce qui signifie que les programmes avaient autant de cartes que d'instructions); et l'opérateur introduisait les paquets de cartes perforées dans un lecteur spécial qui les décodait et les envoyait au processeur qui les enregistrait dans la mémoire à cores.
Ces programmes utilisaient des données qui étaient également mémorisées et modifiées dans la mémoire à cores.









Les avions auraient pu avoir besoin de mémoire morte pour l'ordinateur si les ordinateurs embarqués existaient en ce temps, mais les ordinateurs n'étaient pas encore assez compacts pour être embarqués à bord d'avions; alors les avions utilisaient des calculateurs analogiques qui étaient adéquats pour guider les avions de ce temps, même s'ils n'auraient pas pu être utilisés pour gérer votre budget ou jouer à des jeux vidéos.









Alors, comment est-ce que la mémoire à cores fonctionnait?
Les cores étaient placés à l'intersection de lignes et de colonnes d'une matrice de fils; il y avait deux fils perpendiculaires traversant chaque core.
Le champ magnétique du core pouvait être modifié si chacun des fils le traversant était parcouru par un demi-courant, car la somme des deux demi-courants faisait un courant complet qui permettait la modification du champ magnétique du core.
D'un autre côté, si seulement un fil est alimenté avec un demi-courant et pas l'autre, ce demi-courant tout seul n'est pas assez pour changer le champ magnétique du core.
Sur le schéma qui est montré, seul le core au centre est traversé par deux fils parcourus par un demi-courant, et c'est le seul qui aura son champ magnétique modifié.
Les autres cores n'ont qu'un seul fil parcouru par un demi-courant et l'autre fil n'est pas parcouru par un courant, de sorte que leur champ magnétique ne sera pas modifié.
Ce processus permet de cibler spécifiquement le core qui doit être modifié (ou lu).









Lorsque les deux fils se croisant sont parcourus par un courant négatif, le core de ferrite est polarisé négativement, et inversement, lorsque les deux fils se croisant sont parcourus par un courant positif, le core de ferrite est polarisé positivement.
Le fait que le core de ferrite soit polarisé négativement correspond à un bit mis à zéro, et le fait qu'il soit polarité positivement correspond à un bit mis à un.









Lorsque le core est déjà polarisé négativement, et des courants négatifs parcourent les fils qui se croisent, le champ magnétique du core demeure inchangé; un fil de détection traversant le core ne détecte alors rien; l'ordinateur sait alors qu'un zéro était mémorisé sur ce core.
D'un autre côté, lorsque le core est couramment polarisé positivement, et que des courants négatifs sont envoyés dans les fils qui se croisent, le champ magnétique du core change, et ce changement crée une courte impulsion dans le fil de détection; quoique cette impulsion soit courte, l'électronique est assez rapide pour la détecter; c'est la détection de cette impulsion qui dit que le core de ferrite était polarisé positivement avant que les courants ne soient envoyés dans les fils; l'ordinateur sait alors qu'un bit à un était mémorisé dans ce core.
Le problème de la lecture est qu'elle est toujours faite par envoi de courants négatifs dans les fils, et le core sera donc toujours polarisé négativement après la lecture, même s'il était polarisé positivement avant celle-ci; cela signifie que, si une impulsion a été détectée dans le fil de détection, des courants positifs doivent à nouveau être envoyés dans les fils pour faire retourner le core à son état précédent (sinon, la fois suivante, un 0 serait lu sur le core à la place d'un 1).
Cela peut sembler un peu compliqué expliqué de cette manière, mais en fait ce processus est très rapide, et permet de lire la mémoire à une vitesse assez rapide.









les cores sont donc insérés dans une matrice de fils, et ces fils sont commandés avec des circuits dont le schéma est représenté sur la droite; ce circuit peut envoyer soit un courant négatif (pour lire les cores) ou un courant positif (pour reprogrammer les 1 des cores qui ont été détectés avec un champ positif); le courant négatif ou positif sera seulement envoyé si un signal de validation l'autorise; ce signal de validation dépend de l'adresse mémoire courante qui doit être lue ou réécrite.
Vous voyez donc que le principe de la mémoire à cores repose sur un changement dynamique du champ magnétique.
Lorsque la mémoire à cores n'est pas alimentée, elle est toujours déprogrammée et ne peut pas retenir d'information; lorsqu'elle est alimentée, les cores doivent d'avord être initialement polarisés positivement ou négativement en envoyant soit un courant positif ou un courant négatif dans les fils qui se croisent, suivant que les cores doivent mémoriser un 1 ou un 0.
Et lorsqu'une donnée mémoire doit être modifiée, les fils traversant les cores correspondants aux 1 de cette donnée doivent être parcourus par un courant positif (à moins d'être déja polarisés positivement), tandis que les fils traversant les cores correspondants aux 0 de cette donnée doivent être parcourus par un courant négatif (à moins d'être déjà polarisés négativement).









Dans la mémoires à tores normale, vous voyez que les fils d'activations qui passent à traverts les tores ont un diamètre assez important relativement au tore.
Il doivent en effet pouvoir transporter un courant suffisant pour changer the champ magnétique du tore, et, s'ils étaient trop fins, il ne pourraient supporter ce courant.
Dans l'article de Wikipedia, ils disent que chaque demi-courant était compris entre 0,4 et 0,8 ampères (deux demi-courants sont nécessaires pour changer le champ magnétique du tore, un ne suffit pas); si un fil unique doit changer la champ magnétique du tore, ce courant doit être doublé; il serait alors compris entre 0,8 et 1,6 ampère.
En ce qui converne les fils de détection, ils peuvent être plus fins, car l'impulsion qu'ils reçoivent est relativement faible.









Alors, comment la mémoire à cordes de tores d'Apollo fonctionnait elle?
Il faisaient passer les fils de détection à travers les tores ou les faisait contourner les tores; quand un fil de détection passait à travers un tore, il était supposé représenter un 1, et quand il le contournait, il était sensé représenter un 0.
Jusqu'à 64 fils de détection pouvaient passer à travers un tore!
J'ai trouvé une documentation sur le site de la NASA expliquant comment la mémoire à cordes de tores (core rope memory) et la mémoire effaçable aussi fonctionnaient.









D'abord je dois donner quelque explications à propos de la diode et du transistor, car je me reférerai à ces explications dans ce qui suit.
Une diode est un composant electronique qui laisse passer le courant dans un sens et le bloque dans l'autre sens; et, dans le sens dans lequel elle laisse passer le courant, la diode se comporte comme une résistance pratiquement nulle.









Un transistor est un composant plus sophistiqué qui a trois électrodes appelées la base (l'électrode de gauche), l'émetteur (l'électrode avec une flèche), et le collecteur (l'électrode du haut).
La particularité du transistor est qu'une faible variation de courant entre la base et l'émetteur génére une plus grande variation de courant entre le collecteur et l'émetteur; cette particularité permet d'utiliser le transistor comme un amplificateur de signal.
Un transistor peut également être utilisé pour bloquer ou autoriser un courant entre le collecteur et l'émetteur à partir d'une commande rentrée sur la base du transistor.
Il y a deux types de transistors:
- Dans les transistors appelés "NPN", le courant va du collecteur vers l'émetteur; ces transistors sont représentés avec la flèche de l'émetteur orientée vers l'extérieur du transistor.
- Dans les transistors appelés "PNP", le courant va de l'émetteur vers le collecteur; ces transistors sont représentés avec la flèche de l'émetteur orientée vers l'intérieur du transistor.









Lorsque le transistor est connecté de manière à ce que le courant aille de la base vers l'émetteur, le courant va du collecteur vers l'émetteur (ou de l'émetteur vesr le collecteur dans le case d'un PNP): le transistor est alors non bloqué.









Mais, lorsque le transistor est connecté de manière à ce que le courant ne puisse aller de la base vesr l'émetteur, le courant ne peut aller du collecteur vers l'émetteur (ou vice versa dans le cas d'un PNP); le transistor est alors bloqué.









Ceci est le schéma simplifié qu'ils donnent pour expliquer comment la mémoire à cordes de tores d'Apollo fonctionne.
Pour tester à travers quels tores un fil de détection passe, et quels tores il contourne, il n'est pas possible d'activer plusieurs tores en même temps.










En effet, si deux tores qui sont traversés par un fil de détection sont activés simultanément, la ligne de détection recevra une impulsion, mais il ne sera pas possible de dire si la ligne de détection passe à travers le premier de ces deux tores, ou bien le deuxième, ou encore tous les deux, car, que le premier tore génére une impulsion, ou que ce soit le second, ou les deux, dans tous les cas le fil de détection recevra la même impulsion (représentant un 1).
D'un autre côté, si le fil de détection ne reçoit pas d'impulsion, là il peut être sûr qu'il ne passe à travers aucun de ces deux tores.
La seule solution pour tester les bits sur une ligne de détection est d'activer les tores un par un (mais, lorsqu'un tore est activé, il est possible de tester ttoutes les lignes de détections en même temps pour ce tore).









Si seul le second tore est activé, si une ligne de détection voit une impulsion, elle sait alors pour sûr qu'elle passe à travers ce tore; et si elle ne reçoit pas d'impulsion, elle sait aussi pour sûr qu'elle contourne ce tore.









De même, si seul le troisième tore est activé, su une ligne de détection voit une impulsion, elle sait alors pour sûr qu'elle passe à travers ce tore; et, si elle ne voit pas d'impulsion, elle sait pour sûr qu'elle contourne ce tore.
Quand les second et troisième tores sont successivement activés, la deuxièle reçoit une impulsion dans les deux cas, et elle sait donc qu'elle passe à travers ces deux tores, et donc qu'elle a un 1 programmé sur ces deux tores.









Alors, comment activer les tores indépendamment, de manière à ce qu'un seul soit activé à un moment donné?
Le moyen le plus logique est de faire passer chaque ligne d'activation à travers un seul tore, une par tore.
Il y a deux manières possibles:
1) La manière décrite sur la partie haute de la figure.
Un courant d'activation (à gauche sur la figure) est d'abord envoyé dans un sens dans la ligne d'activation, changeant le champ magnétique du tore.
Puis un courant de désactivation (à droite sur la figure) est envoyé en sens inverse pour remettre le champ magnétique du tore dans son état initial.
Ce changement double du champ magnétique du tore génére une impulsion dans une ligne de détection passant à travers le tore (et, si la ligne de détection contourne le tore, elle ne verra pas d'impulsion).
2) La manière décrite sur la partie basse de la figure.
Un courant d'activation (à gauche sur la figure) est envoyé dans un sens dans la ligne d'activation, changeant le champ magnétique du tore.
Puis un courant de désactivation (à droite sur la figure) est envoyé en sens inverse, non dans la ligne d'activation, mais dans une ligne commune de désactivation qui passe à travers tous les tores, lequel remet le tore dans son état initial.
Ce changement double du champ magnétique du tore génére également une impulsion dans une ligne de détection passant à travers le tore (et, si la ligne de détection contourne le tore, elle ne verra pas d'impulsion).
Dans ce mode, seuls des courants d'activation sont envoyés dans les lignes d'activation, et seule la ligne commune de désactivation replace les tores dans leur état initial.









Mais, curieusement, ils n'ont pas choisi ce mode naturel pour activer les tores.
Les lignes qui permettent de sélectionner quel tore doit être activé ne sont pas des "lignes d'activation", mais des "lignes d'inhibition", et elles fonctionnent en sens inverse; elles ne permettent pas d'activer un tore, mais bien au contraire d'empêcher qu'il ne soit activé.
Sur mes exemples, le fil du bas traversant un tore est une ligne d'inhibition, le fil central est la ligne commune d'activation/désactivation qui traverse tous les tores, and le fil du haut plus mince est un fil de détection.
1) Le processus d'activation d'un tore est décrit sur la partie supérieure de la figure.
Un courant d'activation (à gauche sur la figure) est d'abord envoyé dans un sens à travers la ligne commune d'activation/Désactivation, changeant le champ magnétique du tore.
Puis un courant de désactivation (à droite sur la figure) est envoyé en sens opposé à travers la même ligne commune d'activation/désactivation, remettant le tore dans son état initial.
Ce double changement du champ magnétique du tore génére une impulsion dans une ligne de détection qui traverse le tore (et si elle contourne le tore, elle ne voit pas d'impulsion).

2) Dans le cas où un tore ne doit pas être activé parce qu'il n'est le tore couramment testé, le processus d'inhibition de l'activation du tore est décrit sur la partie inférieure de la figure.
Le courant d'activation (à gauche sur la figure) est envoyé dans la ligne commune d'activation/désactivation, mais un courant de désactivation est envoyé en sens inverse dans la ligne d'inhibition simultanément de sorte que le courant d'activation de la ligne commune ne peut changer le champ magnétique du tore, et conséquemment il n'y a pas d'impulsion générée dans une fil de détection passant à travers ce tore (venant de ce tore).
Le courant de désac tivation (à droite sur la figure) est ensuite envoyé dans la ligne commune d'activation/désactivation, mais,; comme le champ magnétique du tore n'a pas été changé, ce courant de désactivation n'a pas d'effet sur le tore.

Donc, de manière à n'activer qu'un seul tore, aucun courant de doit être envoyé dans la ou les ligne(s) d'inhibition traversant ce tore, et, dans tous les autres tores, il doit y a voir (au moins) une ligne d'inhibition dans laquelle un courant de désactivation est envoyé en même temps que le courant d'activation dans la ligne commune.

(Remarquez aussi que les courants envoyés dans les fils ne doivent pas être comparés avec les courants envoyés dans deux fils dans la mémoire à tores normale; dans la mémoire à tores, ce sont deux demi-courants qui se complémentent, et ici ce sont deux courants pleins qui se neutralisent).

Donc, le concept de lignes d'inhibition semble moins évident que celui des lignes d'activation, mais il semble néanmoins fonctionner.
Pourtant ce dernier concept crée de gros problèmes que nous allons voir.









J'ai coloré avec différentes couleurs les quatre lignes d'inhibition qui permettent de sélectionner ou inhiber les tores.









Vous pouvez voir que chacune de ces lignes d'inhibition traverse deux tores.
La troisième ligne d'activation traverse les deuxième et quatrième tores.
Si un courant de désactivation est envoyé dans cette ligne seulement, le courant d'activation envoyé dans la ligne commune d'activation/désactivation activera les premier et troisième tores, car seuls les deuxième et quatrième tores sont empêchés de changer leur champ magnétique.









Si nous voulons que le premier tore soit le seul à être activé, alors des courants de désactivation doivent être envoyés dans les troisième et quatrième lignes d'inhibition.
Remarquez que ces lignes traversent toutes deux le quatrième tore; cela signifie que deux courants de désactivation au lieu d'un seul sont envoyés à travers ce tore.









Le processus de lecture des bits programmés sur les tores pour les 16 lignes de détection est décrit dans cette démonstration.
Des courants de désactivation sont d'abord envoyés dans les troisième et quatrième lignes d'inhibition lorsque le courant est envoyé dans la ligne commune d'activation/désactivation de manière à ce que le premier tore soit le seul à être activé.
Comme seule la première ligne de détection traverse le premier tore, mais ni la deuxième ligne de détection et ni la seizième (les autres lignes de détection ne sont pas spécifiées), seule la première ligne de détection a un 1 programmé sur ce tore alors que les autres lignes de détection ont un 0 programmé sur ce tore.
Donc, pour le premier tore, nous avons la combinaison "10...0" (en commençant par la première ligne de détection).









Puis un courant de désactivation est envoyé dans les seconde et quatrième lignes d'inhibition, causant l'inhibition de tous les tores sauf le deuxième, qui est le tore couramment testé.
Comme seule la deuxième ligne de détection traverse le deuxième tore, elle est la seule à recevoir une impulsion.
Donc, pour le deuxième tore, nous avons la combinaison "01...0".









Puis un courant de désactivation est envoyé à travers la première et troisième ligne d'inhibition, causant l'inhibition de tous les tores sauf le troisième, qui le tore couramment testé.
Comme la deuxième et seizième lignes de détection traversent le troisième tore, ce sont ces deux-là qui reçoivent une impulsion.
Donc, pour le troisième tore, nous avons la combinaison "01...1".









Puis un courant de désactivation est envoyé à travers les première et deuxième lignes d'activation, causant l'inhibition de tous les tores sauf le quatrième, qui est le tore couramment testé.
Comme seule la première ligne de détection traverse le quatrième tore, c'est la seule à recevoir une impulsion.
Donc, pour le quatrième tore, nous avons la combinaison "10...0".









Donc, en activant successivement un tore à la fois, il est possible de lire les bits qui ont été programmés sur les fils d'activation en les faisant soit traverser les tores (pour un 1) ou les contourner (pour un 0).
Avec ces 4 tores et 16 lignes d'activation, il est possible de mémoriser 4*16=64 bits.
Remarquez toutefois que nous devons envoyer deux courants de désactivation à chaque étape du procesus (et qu'il y a à chaque fois un tore qui reçoit deux courants de désactivation au lieu d'un seul).









Une autre solution serait de faire passer chaque ligne d'inhibition à travers tous les tores sauf un, donc à travers 3 tores sur cet exemple simplifié.
. La première ligne d'inhibition traverse les deuxième, troisième et quatrième tores, mais par le premier; donc, envoyant un courant de désactivation dans cette ligne, cela inhibe tous les tores sauf le premier.
. La deuxième ligne d'inhibition traverse les premier, troisième et quatrième tores, mais par le deuxième; donc, envoyant un courant de désactivation dans cette ligne, cela inhibe tous les tores sauf le deuxième.
. La troisième ligne d'inhibition traverse les premier, deuxième et quatrième tores, mais par le troisième; donc, envoyant un courant de désactivation dans cette ligne, cela inhibe tous les tores sauf le troisième.
. La quatrième ligne d'inhibition traverse les premier, deuxième, et troisième tores, mais par le quatrième; donc, envoyant un courant de désactivation dans cette ligne, cela inhibe tous les tores sauf le quatrième.









Donc nous avons maintenant à n'envoyer qu'un seul courant de désactivation pour tester chaque tore...mais nous avons à faire passer à travers chaque tore un nombre de fils d'inhibition égal au nombre de cores moins un (3 dans notre exemple de 4 tores).
En réalité, il y a 256 tores sur la carte mémoire, ce qui signifie que nous devrions faire passer 255 fils d'inhibition à travers chaque tore; ceci est strictement impossible, vu le diamètre minimal des fils d'inhibition.
Nous devons donc chercher une autre solution.









Une autre solution est de faire passer une seule ligne d'inhibition à travers chaque tore:
. la première ligne d'inhibition traverse seulement le premier tore.
. La deuxième ligne d'inhibition traverse seulement le deuxième tore.
...
Donc, dans cette solution, une seule ligne d'inhibition traverse un tore, et cela résoud le nombre de lignes d'inhibition traversant un tore.









Mais, lorsqu'un tore doit être activé, tous les autres tores doivent être inhibés, ce qui signifie que des courants de désactivation doivent être envoyés dans toutes les lignes d'inhibition sauf celle traversant le tore couramment testé.
Donc, à chaque fois qu'un tore est testé, il doit y avoir un nombre de courants de désactivation égal au nombre de tores moins un; et chacun de ces courants de désactivation est de l'ordre d'un ampère.
Avec une mémoire de 256 tores, à chaque fois qu'un tore est testé, 255 courants de l'ordre d'un ampère doivent être envoyés dans les lignes d'inhibition correspondant au tores autres que le tore couramment testé.
Nous avons donc maintenant un problème d'usage excessif d'énergie; il n'est pas acceptable que la carte mémoire utilise tant d'énergie.









Nous avons donc là un dilemne:
Ou bien nous avons trop de fils d'inhibition passant à travers un tore, ou trop d'énergie est gaspillée.
Et ceci seulement à cause du concept de "lignes d'inhibition" qui a été utilisé.









Alors, pourquoi ne pas utiliser le concept plus normal de "lignes d'activation" au lieu de "lignes d'inhibition"?
Dans le concept de "lignes d'activation", seul un courant d'activation est envoyé dans une ligne d'activation, et seulement une ligne d'activation passe à travers un tore.
Pas de problème de lignes multiples traversant un tore et pas de gaspillage d'énergie.
ceci montre que l'utilisation du concept de "lignes d'inhibition" pour alternativement activer les tores qui sont testés n'a pas de sens, et que le concept de "lignes d'activation" est le seul concept raisonable qui peut fonctionner.









Mais le fait qu'ils ont illogiquement utilisé des "lignes d'inhibition" au lieu de "lignes d'activation" n'est pas le seul problème de la carte ROM.
Ce schéma montre comment l'impulsion générée par les tores était lue.
Il y avait des commandes pour activer la lecture des impultions générées par les tores individuellement.
Mais la sélection des tores est (normalement) seulement faite pour l'activation du tore qui est testé, pas pour l'activation de la lecture de l'impulsion générée.
De plus, à chaque fois qu'il y avait une sélection du tore sur lequel l'impulsion était lue, cela générait automatiquement une impulsion, COMPLETEMENT INDEPENDAMMENT DES LIGNES DE DETECTION TRAVERSANT OU CONTOURNANT LES TORES!!!
En bref, l'impulsion lue ne venait pas des lignes de détection traversant ou contournant les tores qui étaient supposées mémoriser les bits à 1 ou 0 de la mémoire (1 quand traversant un tore, et 0 quand le contournant).









L'impulsion détectée dans la ligne de détection est amplifiée pour être utilisée par un circuit dont le schéma est montré ici.
Nous avons vu que cette impulsion ne venait pas vraiment des lignes de détection traversant ou contournant les tores.
Nous allons maintenant voir qu'elle était même incorrectement amplifiée.










D'abord, à l'entrée du circuit, il y a deux transistors (T1 et T2) qui sont montés en ce qui est communément appelé un "push-pull".
Lorsque le courant de l'impulsion monte, le transistor du haut (T1) a sa base polarisée, et le courant peut aller de son collecteur vers son émetteur.









Et, de même, lorsque le courant de l'impulsion descend, la transistor du bas (T2) a sa base polarisée, et le courant peut aller de son collecteur vers son émetteur.









Lorsqu'il n'y a aucune impulsion dans la bobine d'entrée, les deux transistors (T3 et T4) en haut du schèma sont normalement bloqués, forçant la tension du point que j'ai cerclé en bleu à 14 volts.
Lorsque le transistor suspérieur (T1) du push-pull est débloqué, il laisse le courant passer, et celui-ci passe à travers le chemin que j'ai coloré en rouge; logiquement cela devrait permettre de débloquer le transistor cerclé d'orange (T3) et conséquemment le courant peut aller de son collecteur vers son émetteur, forçant la tension du point cerclé de bleu à la tension de référence VY.
La manière dont le circuit fonctionne est donc très claire: Lorsque 'une impulsion est détectée, la tension du point cerclé de bleu va de 14V à VY.
Mais ceci vaut seulement lorsque le courant monte dans la bobine d'entrée qui reçoit l'impulsion du tore.
Normalement, cela devrait être la même chose lorsque cette impulsion redescend dans la bobine.









Lorsque le courant descend dans la bobine d'entrée, c'est le transistor inférieur (T2) du push-pull qui est débloqué à son tour. Ce transistor, différemment de l'autre, n'a pas d'action sur le transistor T3 cerclé d'orange.
Mais il n'a pas d'action non plus sur le transistor T4, car il n'est pas connecté à celui-ci.
Ce transistor n'aura donc pas d'action sur la tension du point cerclé de bleu (qui reste à 14 Volts si les deux transistors T3 et T4 sont bloqués).









Maintenant, si la base du transistor T4 (cerclé d'orange) avait été connectée au collecteur du transistor T2 (cerclé de vert), ce dernier aurait permis de débloquer le transistor T4, ce qui aurait permis de forcer la tension du point cerclé de bleu à VY également.
Cela signifie que le point cerclé de bleu aurait pu être forcé à la tension VY que le courant monte ou descende dans la bobine (à travers l'action du transistor supérieur T1 du push-pull lorsque le courant monte, ou l'action du transistor inférieur T2 lorsque le courant descend).
Cette connection a été délibérément omise pour créer une incohérence.









Maintenant, la sortie du circuit supposée être l'impulsion détectée amplifiée, utilisable dans la logique qui suit, est connectée au collecteur du transistor de sortie (T5) à travers une résistance.
Comme l'émetteur du transistor est soit à une tension de 14 volts lorsqu'il n'y a pas d'impulsion détectée dans la bobine d'entrée, et VY lorsqu'il y a une impulsion détectée, la sortie devrait changer en présence d'impulsions...
...devrait, mais ne peut en fait, car le collecteur du transistor T5 n'est pas connecté à une tension; alors peu importe que la tension de l'émetteur change, aucun courant ne peut aller du collecteur vers l'émetteur de T5.









Ceci est la manière dont la commection aurait du être faite: La sortie directement connectée au collecteur du transistor de sortie T5, et l'autre bout de la résistance connectée à 14 volts.
De cette manière, lorsque la tension du point cerclé de bleu serait à 14 volts, la sortie du circuit serait aussi à 14 Volts, et, lorsque la tension de ce point serait à Vy, la sortie de l'amplificateur serait à Vy également.
De plus, la sortie du circuit ne peut changer que lorsque la base du transistor de sortie T5 est validée par une impulsion; si la base du transistor T5 n'est pas validée, la sortie du circuit ne chnagera pas, même en cas de présence d'impulsion sur la bobine d'entrée.
Mais ceci est complètement stupide, car le but de ce circuit est détecter une impulsion, alors pourquoi la bloquer, puisque c'est la logique de détection qui active les tores et permet ainsi la génération de l'impulsion?
Le transistor de sortie T5 n'est donc même pas nécessaire, et la sortie du circuit aurait pu être directement connectée au point cerclé de bleu.









Donc, à la suite d'incohérences multiples, cette mémoire à cordes de tores ne peut pas fonctionner correctement.
Mais la question est: Si des lignes d'activation avaient été utilisées au lieu de lignes d'inhibition, et que l'amplificateur de détection d'impulsion avait été monté correctement, est_ce que cela aurait pu marcher, aurait-ce pu mémoriser 64*256=16384 bits (puisque'il y a 64 lignes de déteftion passant à travers ou contournant 256 tores)?









Non en fait, car il y a un autre problème physique.
L'ensemble des fils de détection passant à travers les tores (jusqu-à 64!) forment un groupe compact à l'intérieur duquel certains fils, qui cont complètement entourés par plusieurs épaisseurs d'autre fils, peuvent être isolés du champ magnétique du tore, et de là être incapable de voir le changement du champ magnétique du tore; il ne seront alors pas en mesure de générer une impulsion (pas d'impulsion, ou trop faible pour pouvoir être utilisée).
Des mémoires à tores cablées en dur ont été utilisées en dehors d'Apollo, mais ausune mémorisant tant de bits, et ceci pour une bonne raison: Pour la simple raison qu'il n'y a pas moyen que tous les fils du groupe passant à travers les tores puissent recevoir la même impulsion, et que certains seront masqués par les autres fils, et ne verront pas d'impulsion (ou trop faible).









Il y a également le problème du transport des fils.
Nous avons 256 lignes d'inhibition plus les 64 lignes de détection, ce qui fait un total de 320 fils pour controntrôler cette carte mémoire.
Les trois connecteurs que j'ai cerclés devraient pour tranporter 320 connections, ce qui fait 106.66 connections par connecteur; et, comme une connection ne peut être fractionelle, cela fait que chaque connecteur devrait avoir au poins 107 points.









Le capot des connecteurs permet de voir partiellement les broches du connecteur; nous en voyons trois pour chaque connecteur; nous pouvons mesurer l'intervalle qui les sépare, et nous pouvons en déduire le nombre de points de ces connecteurs.









J'ai reconstitué ici les broches d'un connecteur en dupliquant les trois broches visibles sur la longueur totale du connecteur; dans ma reconstitution, le connecteur a 19 broches; bien sûr il peut y avoir une marge d'erreur, mais nous somme très loin des 107 broches que le connecteur devrait avoir; et même s'il y a deux rangées de ces broches, voire trois, nous sommes encore loin du compte.









De plus, il est difficile de croire qu'il y a 107 fils dans le groupe de fils venant à chaque connecteur!









Et, même si la mémoire à cordes de tores fonctionnait réellement, avec les 64 lignes de détection traversant ou contournant les 256 tores de la carte mémoire, elle pourrait au maximum ùmémoriser 64*256=16384 bits de mémoire.
Dans l'article de Wikipedia, ils disent que l'AGC avait 36864 mots de 16 bits de mémoire ROM, ce qui fait 36864*16=589824 bits mémorisés.
Vu qu'une carte mémoire peut seulement mémoriser 16384 bits (ce qui n'est déjà pas si mal), 589824/16384=36 cartes de mémoire auraient été nécessaires pour fournir la mémoire annoncée.
36 cartes de mémoires placées dans un pied cubique seulement comme ils disent!
Je pense qu'il y a un sérieux problème ici!









Et la manière manuelle dont la mémoire était "tissée" n'a pas de sens non plus: Espérer que des travailleurs ne fassent pas d'erreur.
Les tisseuses devaient faire passer les fils à travers les tores ou les contourner 16384 fois (pour une carte mémoire). Cela fait 16384 occasions de faire une erreur!
Et s'ils avaient vraiment fait toutes les cartes mémoire pour mémoriser les 36384 mots annoncés de mémoire, cela aurait fait 589824 occasions de faire une erreur!
Bien sûr, après que les tisseuses aient enfilé les bons fils dans les tores, il y avait certainement une procédure pour vérifier qu'il n'y avait pas d'erreur, mais, s'il y en avait une ou plusieurs, la carte devait être retournée à l'usine pour être corrigée; et les corrections étaient très difficiles (un fil défectueux devait être complètement enlevé pour être "tissé" à nouveau - à condition qu'il localisent le fil défectueux correctement, et ne le confondent pas avec un fil correctement tissé!).
Faire des corrections dans la carte mémoire étaint donc un vrai cauchemar!









Il n'y a donc absolument aucun doute que la mémoire à cordes de tores d'Apollo était une plaisanterie et non destinée à fonctionner réellement.









Quand ils ont baptisé cette mémoire "LOL, cela ne voulait donc pas dire "Little Old Lady"...









..mais ce que cela veut généralement dire, soit "Laughing Out Loud".
Cela signifiait clairement que cette mémoire était un gag, et qu'elle ne pouvait pas marcher.









Maintenant certains affirment que l'expression "Laughing Out Lout", abrégée "LOL", a été spécialement créée pour Internet et qu'elle n'existait pas auparavant de sorte que les ingénieurs de la NASA n'auraient pu l'utiliser pour baptiser la mémoire à cordes de tores.









Rien n'est plus faux:
L'expression "Laugh Out Loud" est loin d'être récente.
Une vielle bande dessinée "Laugh Out Loud cats" peut en témoigner; ceette bande dessinée a été créée vers 1912, ce qui prouve que l'expression "Laugh Out Loud" est loin d'être récente.
Et il est très probable que cette expression est encore plus ancienne.
Les créateurs d'Internet n'ont pas créé l'expression "laughing out Loud", ils ont juste réactualisé une expression qui existait déjà.









Ceci est le schèma simplifié de la mémoire volatile (RAM).
Le pavé qui est à l'intérieur des boucles de courant représente le tableau de tores.
Si aucune mémoire à cordes de tores n'a été utilisée en dehors d'Apollo (au moins de la manière dont elle exisre pour Apollo), tel n'est pas le cas de la mémoire normale volatile à tores qui peut parfaitement fonctionner.
Donc, si la mémoire ROM ne pouvait fonctionner, nous aurions au moins pu nous attendre à ce que la mémoire RAM fonctionne?
Mais ils ont même réussi à ne pas la faire fonctionner!









J'ai représenrté en rouge le chemin du courant d'éxriture; le courant tourne dans le sens des aiguilles d'une montre; cela le fait monter dans le pavé du tableau de tores.










Et j'ai représenté en bleu le chemin du courant de lecture (la ligne de détection); le courant tourne également dans le sens des auguilles d'une montre, mais, comme la boucle de lecture est à gauche de la boucle d'écriture, cela le fait descendre dans le pavé du tableau de tores.









Maintenant, ce qui est anormal est que la boucle d'écriture et la boucle de lecture ont une partie commune; c'est le même fil qui permet à la fois de changer le champ magnétique d'un fil et de détecter l'impulsion qui est générée par le changement du champ magnétique du tore.









Ceci est absolument impossible; le fil qui permet de générer le changement du champ magnétique du tore, en envoyant un courant à travers celui-ci, ne peut être en même temps le fil qui détecte cette impulsion, TOTALEMENT IMPOSSIBLE!
Et, encore plus absurde, le courant qui circule dans la boucle d'écriture et celui circulant dans la boucle de lecture vont en sens opposé dans leur partie commune!









Vous auriez pu penser que cela était déjà assez incohérent pour satisfaire les saboteurs?
On non, ils ont rajouté un grain de sel!
Ceci est le schéma du circuit qui permet de lire d'impulsion de détection.









L'impulsion lue va vers la base du transistor d'entrée (cerclé de rouge); le collecteur du transistor est connecté à la base d'un transistor PNP (cerclé d'orange) à travers une résistance, mais, comme cette base est connectée au 14 volts à travers des diodes (cerclées d'orange), ce transistor va rester bloqué, que l'impulsion d'entrée soit présente ou non, de sorte qu'il ne suivra pas l'impulsion d'entrée.
Le collecteur du transistor (bloqué) cerclé d'orange est connecté sur la base du transistor de sortie (NPN) cerclé de bleu; mais ce transistor a sa base connectée à la masse à travers des diodes, et ceci le bloque; aucun courant ne peut donc aller de son collecteur à son émetteur (quelque soit l'état de l'impulsion d'entrée).
Donc, le fait qu'un courant régulé aille à travers ce transistor est une plaisanterie.









Donc, aussi incroyable que cela paraisse, ni la mémoire ROM ni la mémoire RAM d'Apollo ne pouvaient fonctionner.
Elles apparaissent toutes deux comme une plaisanterie!
Mais n'ayez pas d'inquiètude pour Apollo, Otto est alà pour contrôler la situation!









Et, entre l'homme et l'ordinateur, il y avait l'unité DSKY, c'est à dire un affichage couplé avec un clavier.
Vous auriez pu penser que cette unité fonctionnait correctement?
FAUX ESPOIR!
Ce schéma montre comment l'affichage était commandé.









Le circuit cerclé de vert montre l'interface de commande de la sélection de relais.
Les collecteurs des transistors PNP (cerclés de bleu) ne sont pas connectés à une référence; il sont directement connectés aux relais, mais deux diodes (cerclées de rouge) sont connectées sur le chemin des relais, et ces diodes sont montées en opposition; ces diodes bloquent complètement le courant qui pourrait passer à travers les relais, ce qui signifie qu'aucun courant ne va passer à travers les relais.
Un bloc de décodage matriciel commandé par l'ordinateur contrôle un transistor, mais, comme le collecteur du transistor de reçoit pas de courant (puisque les relais sont tous bloqués, comme je l'ai montré), la commande venant de l'ordinateur n'aura pas d'effet sur lui.
En résumé, si nous nous rapportons à ce schéma, l'affichage restera désespéremment mort!









Ce schéma montre comment l'unité DSKY était connectée.









Mais il y a plusieurs problèmes dans ce qui est montré.
D'abord le collecteur du transistor que j'ai cerclé de rouge n'est pas connecté à une référence positive.
Donc, la matrice de relais qui suit ne pourra fonctionner correctement.
Et, deuxièmement, sur l'entrée de la porte NOR des entrées de code clavier, il y a un condensateur "feedthrough" dont nous pouvons nous étonner ce qu'il fait ici.









J'ai ajouté une connection à une référence positive sur le transistor connecté à l'éclairage de l'affichage, laquelle est nécessaire pour que l'affichage fonctionne correctement.









Donc, avec une unité DSKY qui ne fonctionnait même pas correctement, l'AGC apparaît définitivement comme une plaisanterie.









Avec les équations de guidage que l'ai vues, tournant sur un ordinateur ayant un système opératoire étrange, une gestion des tâches absurde, et de plus de la mémoire inexistante, je n'ai pas de doute que le module lunaire aurait aluni...mais pas exactement de la manière espérée!