Je me le demande toujours, et j'ai peut-être besoin d'une bonne leçon d'histoire sur les langages de programmation. Mais comme la plupart des compilateurs sont de nos jours fabriqués en C, comment les tout premiers compilateurs ont-ils été créés (AKA avant C) ou tous les langages ont-ils simplement été interprétés?
Cela étant dit, je ne comprends toujours pas comment même la première langue d'assemblage a été faite, je comprends ce qu'est la langue d'assemblage mais je ne vois pas comment ils ont fait fonctionner la TRÈS première langue d'assemblage (comme, comment ont-ils fait la première commandes (comme mov R21
) ou w/e défini sur l'équivalent binaire?
Ha, je l'ai fait. De nombreux processeurs ont des instructions simples et de taille fixe qui ne font que quelques octets. Pour un simple processeur comme un Motorola 6800 par exemple, vous pouvez adapter toutes ses instructions sur une feuille de papier unique . Chaque instruction serait associée à un opcode de deux octets et à des arguments. Vous pouvez assembler à la main un programme en recherchant l'opcode de chaque instruction. Vous écririez alors votre programme sur papier , annotant chaque instruction avec son opcode correspondant. Une fois que vous avez écrit votre programme, vous pouvez graver chaque opcode en séquence dans un EPROM qui stockera ensuite votre programme. Câblez l'EPROM jusqu'au CPU avec juste les bonnes instructions aux bonnes adresses, et vous avez un programme de travail simple. Et pour répondre à votre prochaine question, oui. C'était douloureux (nous l'avons fait au lycée). Mais je dois dire que le câblage de chaque puce dans un ordinateur 8 bits et l'écriture manuelle d'un programme m'ont donné une compréhension approfondie de l'architecture informatique que je n'aurais probablement pas pu obtenir autrement.
Les puces plus avancées (comme x86) sont beaucoup plus difficiles à coder manuellement, car elles contiennent souvent des instructions de longueur variable. Les processeurs VLIW/EPIC comme le Itanium sont presque impossibles à coder à la main efficacement car ils traitent en paquets d'instructions qui sont optimisés et assemblés par des compilateurs avancés. Pour les nouvelles architectures, les programmes sont presque toujours écrits et assemblés d'abord sur un autre ordinateur, puis chargés dans la nouvelle architecture. En fait, pour des entreprises comme Intel qui construisent des processeurs, elles peuvent exécuter des programmes réels sur des architectures qui n'existent pas encore en les exécutant sur des simulateurs. Mais je m'égare ...
Quant aux compilateurs, dans leur plus simple expression, ils peuvent être un peu plus que des programmes "couper-coller". Vous pouvez écrire un "langage de haut niveau" très simple et non optimisant qui regroupe simplement les instructions simples du langage d'assemblage sans trop d'efforts.
Si vous voulez une histoire des compilateurs et des langages de programmation, je vous suggère de GOTO une histoire de FORTRAN .
C'est à cela que sert amorçage du compilateur (puisque personne n'a mentionné comment cela s'appelle =).
le processus d'écriture d'un compilateur (ou assembleur) dans le langage de programmation cible qu'il est destiné à compiler. L'application de cette technique conduit à un compilateur auto-hébergé.
De nombreux compilateurs pour de nombreux langages de programmation sont amorcés, notamment les compilateurs pour BASIC, ALGOL, C, Pascal, PL/I, Factor, Haskell, Modula-2, Oberon, OCaml, Common LISP, Scheme, Java, Python, Scala et plus ...
Le problème du poulet et des œufs
Si l'on a besoin d'un compilateur pour la langue X pour obtenir un compilateur pour la langue X (qui est écrit en langage X), comment le premier compilateur a-t-il été écrit? Les méthodes possibles pour résoudre ce poulet ou le problème des œufs comprennent:
- Implémenter un interpréteur ou un compilateur pour la langue X dans la langue Y. Niklaus Wirth a rapporté qu'il a écrit le premier compilateur Pascal dans Fortran.
- Un autre interpréteur ou compilateur pour X a déjà été écrit dans une autre langue Y; c'est ainsi que Scheme est souvent amorcé.
- Les versions antérieures du compilateur étaient écrites dans un sous-ensemble de X pour lequel il existait un autre compilateur; c'est ainsi que certains sur-ensembles de Java, Haskell et du compilateur Free Pascal initial sont amorcés.
- Le compilateur pour X est compilé de manière croisée à partir d'une autre architecture où il existe un compilateur pour X; c'est ainsi que les compilateurs pour C sont généralement portés sur d'autres plateformes. C'est également la méthode utilisée pour Free Pascal après le bootstrap initial.
- Écriture du compilateur en X; puis le compiler à la main à partir de la source (très probablement de manière non optimisée) et l'exécuter sur le code pour obtenir un compilateur optimisé. Donald Knuth l'a utilisé pour son système de programmation lettré WEB ...
En fin de compte, tous les ordinateurs fonctionnent sur des codes binaires, qui sont introduits dans le CPU. Ces codes binaires sont parfaitement naturels pour un CPU, mais aussi parfaitement inutiles pour les êtres humains. L'une des premières façons d'écrire un programme était de percer des trous dans les cartes. La position des trous représentait une position de bit particulière dans un mot, et la présence ou l'absence du trou était interprétée comme un zéro ou un. Ces cartes ont été placées dans le bon ordre dans une boîte, puis introduites dans un lecteur de carte, qui les a effectivement converties en code binaire pour le processeur (et votre vie a été effectivement perdue si vous avez laissé tomber la boîte).
De toute évidence, les tout premiers programmeurs ont élaboré les codes binaires un par un et disposaient d'une machine pour perforer les cartes. Il s'agit essentiellement d'une programmation en langage assembleur sur vos mains et vos genoux. Une fois que vous avez cela, vous pouvez en créer toutes les autres: un simple éditeur de texte, un compilateur de langage d'assemblage (pour convertir les instructions d'assemblage de texte en codes binaires), un éditeur de liens et un chargeur. Et le reste, comme on dit, c'est de l'histoire.
Une petite recherche sur Google apparaît Commandes initiales EDSAC à partir de la fin des années 40. Comme il s'agissait du premier assembleur, il était probablement codé en langage machine.
Plus tard sont venus des assembleurs pour d'autres machines, comme SOAP I et II pour IBM 650. SOAP J'ai aussi probablement été codé en langage machine, bien que je n'aie pas trouvé le déclaration définitive.
Un peu plus tard est venu Fortran (traducteur de formule), pour l'IBM 704. On suppose qu'il a été écrit en assembleur pour le 704. Un ancien assembleur pour le 701 est crédité à Nathan Rochester .
Si vous voulez avoir une idée de la façon de programmer un ordinateur en langage machine, consultez l'un de mes sites préférés, ordinateur relais de Harry Porter .
Il est possible (si fastidieux) d'écrire du code machine direct. Peut-être que vous écrivez le programme dans l'assembleur sur un morceau de papier, puis vous le traduisez à la main dans les instructions de code machine numérique que vous entrez dans la mémoire de la machine. Vous pouvez même ignorer l'étape d'assemblage sur papier si vous avez mémorisé les valeurs numériques de toutes les instructions de code machine - ce qui n'est pas rare à l'époque, croyez-le ou non!
Les tout premiers ordinateurs ont été directement programmés en binaire en basculant des commutateurs physiques. Ce fut une grande amélioration de la productivité lorsque le matériel a évolué pour permettre au programmeur (ou à l'assistant de saisie de données) d'entrer le code en chiffres hexadécimaux via un clavier!
Un assembleur logiciel n'est devenu pertinent que lorsque plus de mémoire est devenue disponible (puisque le code assembleur prend plus d'espace que le code machine brut) et que le matériel a évolué pour permettre la saisie alphanumérique. Les premiers assembleurs ont donc été écrits directement par des personnes maîtrisant le code machine.
Lorsque vous avez un assembleur, vous pouvez écrire un compilateur pour un langage de niveau supérieur dans l'assembleur.
L'histoire de C comporte plusieurs étapes. Le premier compilateur C a été écrit en B (un prédécesseur de C) qui à son tour a été écrit en BCPL. BCPL est un langage assez simple (par exemple, il n'a pas de types du tout), mais toujours un pas en avant de l'assembleur brut. Vous voyez donc comment des langages plus complexes sont progressivement construits dans des langages plus simples jusqu'à l'assembleur. Et lui-même C est un langage assez petit et simple selon les normes d'aujourd'hui.
Aujourd'hui, le premier compilateur d'un nouveau langage est souvent écrit en C, mais lorsque le langage atteint une certaine maturité il est souvent réécrit "en soi". Le premier compilateur Java a été écrit en C, mais réécrit plus tard en Java. Le premier compilateur C # a été écrit en C++, mais récemment il a été réécrit en C #. Le Python le compilateur/interprète est écrit en C, mais le projet PyPy est une tentative de le réécrire en Python.
Cependant, il n'est pas toujours possible d'écrire un compilateur/interprète pour une langue dans la langue elle-même. Il existe un interpréteur JavaScript écrit en JavaScript, mais les compilateurs/interprètes des navigateurs actuels sont toujours écrits en C ou C++ pour des raisons de performances. JavaScript écrit en JavaScript est tout simplement trop lent.
Mais vous n'avez pas besoin d'utiliser C comme "langage de démarrage" pour un compilateur. Le premier compilateur F # a été écrit en OCaml, qui est l'autre langage le plus étroitement lié à F #. Une fois le compilateur terminé, il a été réécrit en F #. Le premier compilateur pour Perl 6 a été écrit en Haskell (un langage fonctionnel pur très différent de Perl) mais a maintenant un compilateur écrit en C.
Un cas intéressant est Rust, où le premier compilateur a été écrit en OCaml (maintenant il est réécrit en Rust). Cela est remarquable car OCaml est généralement considéré comme un niveau supérieur à Rust, qui est un langage de systèmes plus proche du métal. Il ne s'agit donc pas toujours de langages de niveau supérieur implémentés dans des langages de niveau inférieur, mais cela pourrait également être l'inverse.
En supposant que vous commencez avec un jeu d'instructions nues et rien d'autre, vous commencez par créer un assembleur ou un compilateur minimal, à peine fonctionnel qui peut charger un fichier, analyser un sous-ensemble minimal du langue cible et générer un fichier exécutable en sortie, en écrivant le code machine brut à l'aide d'un éditeur hexadécimal ou similaire.
Vous utiliseriez ensuite ce compilateur ou assembleur à peine fonctionnel pour implémenter un compilateur ou un assembleur légèrement plus performant capable de reconnaître un sous-ensemble plus important du langage cible. Faire mousser, rincer, répéter jusqu'à l'obtention du produit final.
Ce n'est pas si difficile qu'il n'y paraît. Dans l'enfance;) J'ai fait un démontage x86 à l'esprit.
Vous n'avez même pas besoin de l'apprendre spécialement. Cela se produit simplement lorsque vous pouvez programmer dans ASM, puis essayer de réparer un binaire tiers à l'aide de désassembleurs interactifs. Ou lorsque vous écrivez votre propre protection avec le cryptage de code.
C'est à dire. parfois vous migrez même de la langue vers les codes sans étonnement.
Les premiers compilateurs ont été implémentés en utilisant le langage d'assemblage. Et les premiers assembleurs ont été implémentés en codant des programmes en binaire ...
Il n'y a pas si longtemps que la programmation en binaire était encore une compétence que les gens utilisaient.
Quand j'étais étudiant, je me souviens avoir fait un exercice de programmation qui impliquait d'écrire un petit programme en code machine PDP-8 (je pense), de l'entrer via les commutateurs du panneau avant et de l'exécuter. Quelques années plus tard, je me suis acheté un kit de développement de système 6502 qui avait un clavier hexadécimal pour entrer des programmes ... et 4k octets de RAM.