Ordinateur 8-bit: ADKom A80

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Salut !

Je m’appelle Adrien, Ardakaniz sur internet, j’ai 15 ans (à l’heure où j’écris ces lignes), je suis donc lycéen, en seconde première S SI.

Vers mes 10/11 ans j’ai commencé la programmation en C puis rapidement en C++ qui est maintenant mon langage de prédilection. J’ai eu plusieurs projets de jeux vidéos mais je n’ai jamais eu la motivation d’en finir un seul, et ça ne m’intéresse plus comme avant.
Vers mes 12 ans j’ai eu une carte Arduino avec laquelle je n’ai jamais fait vraiment de projet et qui maintenant ne fonctionne plus >_<
L’année dernière j’ai eu un RaspberryPi et je compte donc mettre l’assembleur dessus et l’utiliser pour programmer et upload les programmes de mon PC vers mon… PC ordinateur :D

Genèse et idée

ADKom signifie ADK pour Ardakaniz et Kom pour Com(puter), donc la série d’ordinateurs construient par moi, et A80, A pour l’architecture basée sur un accumulateur, et 80 pour la première version (0) 8-bits

Il y a quelques mois, j’ai découvert un YouTuber qui m’a fait découvrir l’électronique numérique: Ben Eater. Ce dernier a construit un ordinateur 8-bit et après de nombreuses demandes de personnes pour qu’il explique comment il a fait, il a décidé de le reconstruire entièrement en expliquant chaque parties distinctes.

J’ai regardé toutes ses vidéos, puis je me suis mis en tête que moi aussi je pourrais construire le mien, surtout étant donné qu’il m’a donné gout à la microélectronique et que je compte poursuivre mes études dans ce domaine.

A la base je voulais faire un ordinateur qui est programmable en Brainf*ck :D . Puis des complications sur l’architecture sont arrivés, et puis je me suis dit qu’un "vrai" ordinateur serait plus intéressant (surtout sur le fait que programmer en BF c’est un peu limité et moins attrayant que de l’ASM, personnellement).

J’ai donc commencé à architecturer comment l’ordinateur sera, car je ne compte pas recopier module par module celui de Ben Eater, ce serait un peu trop simple. Je voulais donc l’améliorer (sans pour autant rajouter trop de complexité pour que ça soit faisable) en rajoutant principalement des instructions (jgt, jlt, etc…) et en améliorant l’ALU (celui de Ben Eater gère seulement l’addition/soustraction, je compte rajouter des comparaisons (>, <, ==) et des portes logiques (AND, OR) ).

Objectifs

Apprendre. Et m’amuser et être fier de moi. Je suis conscient que une fois fini (et prions pour que je le finisse) cela ne servira pas à grand chose, mais au moins j’aurais appris énormément de choses et je pourrais me dire "J’ai construit un ordinateur à 15 ans \o/".

Avancement

Actuellement, il n’y a absolument rien de fait physiquement parlant :D .
L’architecture est enfin fixé après de nombreux jours de réflexion et de re-réflexion, mais cela dit, il est tout à fait possible que je tombe face à un problème et que je change certains choses.
Je ferais des récapitulatifs des différents modules dans différents post que je linkerais ici (ou je mettrais tout directement ici, à voir).

Alors, à quelle allure je vais avancer ? Il n’y aura pas de rythme, le principal problème étant d’acheter les composants (que je n’ai pas)… Ben a indiqué qu’au final, son ordinateur lui aura couté entre \$150 et \$200, sachant que je veux lui apporter des améliorations, cela reviendra au final plus cher.

L’architecture

C’est un ordinateur à base d’accumulateur, c’est-à-dire que chaque opération utilise l’accumulateur pour le calcul (sauf dans certains cas) et remet le résultat dedans (en perdant l’ancienne valeur).

L’ALU est capable de faire des opérations bitwise not, or, and et nand, ainsi que des négations (x -> -x), et du shifting (seulement sur l’accumulateur, que de 1 et que vers la gauche, donc c’est très limité mais ça va permettre entre autre d’utiliser des I2C)

Il y a trois IO: un write-only (pour un afficheur), un read-only (pour une entrée) et un qui peut faire les deux qui sont mappés à la fin de la RAM, leur adresse sont respectivement $FFD, $FFE et $FFF

architecture

Le langage Assembleur

Voilà à quoi il ressemble:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
lda #0 ; "load A" : met 0 dans l'accumulateur
  sta $FFD ; "store A": met la valeur de l'acc à l'adresse $FFD (qui correspond au premier IO, et on va supposer que c'est un afficheur ici)
  sta $0 ; On enregistre la valeur à l'adresse 0
  pause ; pause le programme pour avoir le temps de voir la valeur

@loop
  inc $0 ; prend la valeur à l'adresse 0, l'incrémente et met le résultat dans A (sans overwrite la valeur de `$0`)
  sta $FFD
  sta $0

  pause

  sub #$FF, jeq @loop ; "soustrait 255 de A puis jump" : Soustrait 255 de A puis saute à l'adresse du label `@loop`
; l'instruction binaire est codée de telle sorte qu'on peut y intégrer une opération et un saut, ce qui économise de la place

  jmp @ ; saute à l'instruction courante -> boucle infinie

Chaque instruction prend au minimum 2 octets: 1 où est codée la source (l’argument dans la plupart des cas), la destination, et un saut (d’où le fait qu’on puisse faire une opération normale et un saut dans la même instruction) et 1 autre où est codé l’opération arithmétique (destiné à l’ALU donc) plus au maximum 2 autres pour les arguments (1 pour l’opération, 1 pour l’adresse de jump) :
s1 s2 s3 d1 d2 j1 j2 j3
p o1 o2 o3 o4 o5 o6 o7

Avec s = source, d = dest, j = jump, o = operation et p = pause.
Ce bit de pause, comme son nom l’indique met en pause l’exécution du programme (L’assembler transforme l’instruction pause en 1000 0000)

Ces deux octets sont tous les deux stockés ensemble car la ROM du programme renvoie 16 bits. Cependant, les arguments sont dans deux cases mémoires différentes même si ils ne font que 8 bits, cela signifie donc que les 8 bits de poids fort seront inutilisés.

Plus d’infos sur comment son codés les instructions ici (Obselète, à corriger)


Il y a plusieurs moyens d’adressage, reconnus par l’assembleur par 2 symboles:
- A renvoie la valeur de A, donc aucune valeur directe n’est mise
- # sert à faire des appels immédiats
Et bien sûr ces symboles sont cumulables, par exemple lda #5 fait un adressage immédiat alors que lda A fait un adressage indirect.


Les labels qui se déclarent de la sorte @nomDuLabel (chiffres autorisés à la suite d’une erreur de programmation puis je me suis dit pourquoi ne pas les laisser étant donné qu’il n’y a aucune ambiguïté avec le @ devant :D ), ils peuvent être mis juste avant l’instruction: @label lda 5 ou être mis une ligne avant:

1
2
3
@loop
  lda *A
  inca, jmp @loop

Tout ce qui n’est pas au début d’une ligne est interprété comme étant une référence à ce label (jmp @ld5). Si un label est référencé, sans nom (@), c’est un label sur l’instruction courrante (jmp @ = boucle infinie)


Il y a aussi du préprocesseur qui va être mis en place (du type define, etc…).

L’Assembleur

Trois logiciels sont compris dans une seule suite: L’assembleur, la VM et un compilateur d’un mini langage.

L’utilisation de l’assembleur est particulièrement simple: on lui donne un fichier asm d’entrée, et optionnellement un fichier de sortie (si aucun indiqué, out.bin par défaut) et l’argument -a pour lui indiquer qu’on veut assembler.
Lorsqu’il rencontre des erreurs, il nous donne le nom du fichier qu’on lui a donné, la ligne qui pose problème, et le problème en question.

TODO:

  • Structure de base (✓)
  • Sauts (✓)
  • Opérations de base (✓)
  • Labels (✓)
  • Ajouter le reste des opérations (❌)
  • Préprocesseur (define, reserve, etc…) (❌)
  • Sûrement d’autres d’autres qui ne me viennent pas à l’esprit (❌)

La VM

Etant donné que pendant ces 3 mois (et je suis désolé de rien n’avoir pu vous montrer :/ ) je n’ai rien pu tester, j’ai commencé la programmation d’une VM pour voir mes programmes fonctionner :D
Il est codé dans la même application que l’assembleur, il suffit de lui rajouter l’option -vm et il interprètera le binaire.

On peut choisir de pouvoir stopper l’exécution dès le début(pour pouvoir debug), pour cela, il faut utiliser l’option -s ou --stop-at-begin, sinon, l’émulation se stoppera seulement lors d’une instruction pause.

Passons à ces fameuses commandes, elles permettent de lire et d’écrire dans toutes les mémoires (RAM, ROM, registres, IO) et voici leur syntaxe:

1
2
read mem([offset])
write mem[offset] value

mem peut donc soit être RAM, ROM, IO, A ou O, offset, qui permet d’accéder à une case mémoire, est obligatoire pour l’écriture: il n’y a pas la possibilité d’écrire toute une mémoire d’un coup, ne l’est pas pour la lecture: il affichera l’entièreté de la mémoire, et finalement value qui est simplement la nouvelle valeur de la case.
Les valeurs (offset et value) peuvent être écrit directement en décimal, mais aussi en hexa en ajoutant 0x ou 0X devant le nombre, en octal en ajoutant un 0 (08(8) = 10(10)) ou en binaire en ajoutant 0b.
Il est interdit/impossible d’indiquer un offset pour les registres. (ils n’ont qu’une seule case mémoire…).
Avec read, la valeur de l’instruction qui va s’exécuter (dans le cas de l’option --stop-each-iter qui stoppe avant de n’avoir interpréter quoi que ce soit) ou finir de s’exécuter (dans l’autre cas) est coloré en jaune/orange contrairement aux autres qui sont bleues

Il y a aussi les commandes continue ou c pour continuer l’exécution jusqu’à la prochaine instruction pause, stop ou s pour s’arrêter après l’émulation de la prochain instruction, et exit pour tout simplement arrêter l’exécution.

Il y a une erreur si jamais on essaye de lire dans un IO de sortie (adresse $FFD) et si on essaye d’écrure dans un IO d’entrée (adresse $FFE).
Quand la valeur de celui de sortie est changé, c’est affiché par la VM. (c.f screen)

TODO:

  • Interprétation des opérations (✓)
  • Interprétation des sauts (✓)
  • Pouvoir voir le contenu des mémoires (✓)
  • Ajouter les registres d’IO (✓)
  • Saut dans la ROM (❌)

Affichage de l’output

Conclusion

Merci d’être venu à bout de cette présentation ! J’espère pouvoir avancer assez vite et finir ce projet pour ne pas laisser ce topic vide pendant 3 mois Trop tard…
A la base, je voulais attendre d’avoir les composants pour présenter mon projet, mais j’ai vu la FrenchiNES et ça m’a donné envie de vous en faire part dès maintenant, donc désolé de l’attente :)

Modules

  • TODO

Links

  • YT: Où je ferai des vidéos à propos des modules et expliquerai les différences avec ceux de Ben Eater.
  • GitHub: Où je stocke tous les fichiers correspondant. Actuellement, il y a les schémas fait avec KiCAD (Registres, ProgramCounter, ALU), une description du langage assembleur ainsi que l’assembleur (qui inclu la VM).

Vidéos

Programmation d’une suite de Fibonacci puis émulation avec ma VM

Édité par Ardakaniz

C’est un projet très ambitieux, et très intéressant ! Je me suis moi aussi intéressé à la construction d’ordinateurs, mais à l’aide de logiciels de simulation.

Je suis ton projet avec grand intérêt !

Quand un éléphant prend la défense d’un autre, est-ce de l’altruisme ou du vol ?

+1 -0

C’est bien mais je voudrais revenir sur quelque points :

1)C’est assez étrange d’utiliser une instruction out pour afficher un truc a l’écran , il est plus judicieux d’utiliser des registres I/O

2) un ordinateur 8bit n’est pas limité a 256 octets de RAM , sur la NES on pouvait écrire sur 2ko par exemple ;) un processeur 8bits est considérer comme tel quand les registres sont en 8 bits , mais rien n’empêche de lire /écrire au delà (comme sur le 65816 processeur 8/16 bits qui peut accéder a des adresses 24bits).

3) l’assembleur utilisé est vraiment étrange il mélange plusieurs syntaxe assembleur et c’est assez déroutant pour ma part.

Par exemple lda [0xff], les crochets sont utiliser pour le x86 pour les adresse certes, mais le lda est un mnémonique du 6502/6800 est s’écrit de cette façon lda $FF (si on veut mettre une valeur immédiate : lda #$FF).

Donc pour les adresses plus grosse (comme les I/O) sur NES il fallait écrire de cette façon : lda $2000 ou sta $2000.

4)si tu veux faire un assembleur 16 bits mon conseil est de s’inspirer du m68000 , ou de l’ARM , c’est probablement les processeurs les plus agréables a programmer en asm ;)

Édité par Kannagi

+2 -0

1)C’est assez étrange d’utiliser une instruction out pour afficher un truc a l’écran , il est plus judicieux d’utiliser des registres I/O

Je confirme, dédier une instruction pour ça c’est bizarre.

2) un ordinateur 8bit n’est pas limité a 256 octets de RAM , sur la NES on pouvait écrire sur 2ko par exemple ;) un processeur 8bits est considéreré comme tel quand les registres sont en 8 bits , mais rien n’empêche de lire /écrire au delà (comme sur le 65816 processeur 8/16 bits qui peut accéder a des adresses 24bits).

Pour citer un autre exemple, le 6801 est un microcontrôleur 8 bits avec deux registres de travail 8 bits mais un PC de 16 bits et un sélecteur d’adresses de 16 bits également, ce qui permet d’accéder à plus de cases.

De manière générale quand on parle de processeur N bits, c’est surtout pour désigner la capacité de l’ALU qui peut faire par exemple une addition sur N bits en une seule fois. De très nombreux processeurs 8 bits on un adressage 16 bits (en x86 c’est une autre paire de manches…).

3) l’assembleur utilisé est vraiment étrange il mélange plusieurs syntaxe assembleur et c’est assez déroutant pour ma part.

Par exemple lda [0xff], les crochets sont utiliser pour le x86 pour les adresse certes, mais le lda est un mnémonique du 6502/6800 est s’écrit de cette façon lda $FF (si on veut mettre une valeur immédiate : lda #$FF).

Donc pour les adresses plus grosse (comme les I/O) sur NES il fallait écrire de cette façon : lda $2000 ou sta $2000.

4)si tu veux faire un assembleur 16 bits mon conseil est de s’inspirer du m68000 , ou de l’ARM , c’est probablement les processeurs les plus agréables a programmer en asm ;)

Kannagi

Je confirme que la syntaxe du 68000 est très agréable. Je conseillerai également la syntaxe 6800/PIC qui m’avais bien plus également. Par contre la syntaxe x86 quelle horreur !

De manière générale c’est un très beau projet dans lequel tu te lances, je n’aurai pas ce courage. Je pense plutôt partir sur un ordi z80 ou 68000 fait maison plutôt que de devoir refaire une ALU avec des portes. Ça compte aussi comme « J’ai fait mon propre ordinateur ».

Un dernier point, je te conseille très vivement KiCAD plutôt que Fritzing pour faire des « vrais » schémas électroniques. Et dommage pour l’ordinateur Brainfuck, ça aurait été très original.

Il faut coder pour pouvoir spécifier… ou l’inverse ! Blog perso

+2 -0
Auteur du sujet

1)C’est assez étrange d’utiliser une instruction out pour afficher un truc a l’écran , il est plus judicieux d’utiliser des registres I/O

3) l’assembleur utilisé est vraiment étrange il mélange plusieurs syntaxe assembleur et c’est assez déroutant pour ma part.

Alors, pour la syntaxe, ce n’est pas tellement moi qui l’ai mise en place mais j’ai plutôt repris celle de Ben, qui je pense s’est inspiré de ça : http://www.yorku.ca/sychen/research/LMC/LMCInstructions.html.

En gros l’instruction out déplace la valeur du registre A dans le un registre pour l’affichage, alors que in met juste la valeur des DIP Switches sur le bus, pas besoin de registre là étant donné que les switches gardent leur valeur.

Au final, pour la syntaxe d’adressage je peux changer, ce n’est qu’une représentation graphique :)

2) un ordinateur 8bit n’est pas limité a 256 octets de RAM , sur la NES on pouvait écrire sur 2ko par exemple ;)

Oui, en effet, ça m’a rappelé que d’ailleurs l’ordinateur de Ben Eater n’a à la base qu’une mémoire de 16 octets car sur son registre 8-bits, il en utilise 4 pour l’instruction et 4 pour un paramètre. J’ai donc prévu de mettre les instructions sur une seule case mémoire et pour celles ayant besoin d’un paramètre, je le met dans la case mémoire après, mais c’est possible de dédier 2 cases pour avoir un paramètre de 16-bit et donc pouvoir addresser jusqu’à 65 536 octets.

4) si tu veux faire un assembleur 16 bits mon conseil est de s’inspirer du m68000 , ou de l’ARM , c’est probablement les processeurs les plus agréables a programmer en asm ;)

La mémoire réglé, le fait de l’améliorer en 16-bit est donc très secondaire maitenant, mais pourquoi pas, je garde ça de coté ;)

EDIT:

Un dernier point, je te conseille très vivement KiCAD plutôt que Fritzing pour faire des « vrais » schémas électroniques.

C’est téléchargé, je l’utiliserais pour les autres schémas, celui de la clock étant déjà fait. (D’ailleurs je compte le publier ce matin, ou en tout cas dans la journée au plus tard)

Édité par Ardakaniz

Auteur du sujet

Hello !

Je déterre mon topic pour vous annoncer qu’en attendant d’acheter les composants (ce qui va se passer vers la fin de l’année maintenant je pense :/), j’ai commencé à coder l’assembleur et une VM qui va avec pour pouvoir tester le tout !

J’ai refait entièrement le post de présentation, j’ai trouvé un nom à l’ordinateur (qui peut être temporaire si quelqu’un trouve quelque chose de mieux), et je pense pouvoir maintenant donner des nouvelles justement sur la partie software.

En espérant que l’attente ne vous gêne pas trop ^^

Auteur du sujet

More incoming…

VM: lecture des mémoires + indication instruction courrante

Le programme interprété ici est celui montré plus haut, la valeur en jaune est l’instruction courante, en train d’être interprété par la VM (donc au début, le lda puis le pause)

EDIT: Pensez à souvent retourner voir le premier post après des publications de ce style, j’explique tout plus en détail (ici, des explications sur la VM) ;)

Édité par Ardakaniz

Auteur du sujet

Salut ! :)

Quelques petites nouvelles:

  • Support des labels par l’Assembler
  • Support de la première instruction de préprocesseur par l’assembler (!sv: pour mettre une valeur custom à l’adresse courante)
  • Refacto…
  • Fixes…

Et une première vidéo où je code Fibonacci à partir d’un code C++ et je l’émule avec la VM ici !

Et encore une fois j’ai update le premier post avec plus de précision sur le langage assembleur, l’assembleur et la VM (+ 1 ligne et demi de plus sur l’archi :D )

Édité par Ardakaniz

Bon courage pour la suite!

Quel est l’intérêt d’ un ordinateur 8-bit (à part pour l’apprentissage de l’électronique et de la programmation)?

Édité par ToxicScorpius

“La musique est une mathématique sonore, la mathématique une musique silencieuse.” E.Herriot “Les mathématiques ne sont pas une moindre immensité que la mer.” V.Hugo

+0 -0

Moi je soutient totalement ce genre de projet , il n’existe malheureusement plus beaucoup de façon de comprendre/connaître le bas niveau et l’assembleur.

A une époque on pouvait connaître le bas niveau et l’assembleur justement grâce a des ordinateur 8 bits très simple en architecture. De nos jours on a des ordinateur très complexe qui demande a posteriori de très bien connaître le bas niveau et l’assembleur pour codé dessus.

+1 -0
Auteur du sujet

Modification de l’architecture pour pouvoir adresser jusqu’à 4Ko de RAM et avoir 4K x 16 d’espace pour le programme ! (16 car je stocke maintenant les deux octets de l’instruction en un, par contre les arguments prendront que 8 des 16 bits, même si il y en a deux)

Et en accompagnement, un schéma l’expliquant plus ou moins en détail:

Édité par Ardakaniz

Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte