Ordinateur 8-bit: ADKom A80

a marqué ce sujet comme résolu.
Auteur du sujet

Salut !

Je m’appelle Adrien, Ardakaniz sur internet, j’ai 16 ans, je suis donc lycéen, en 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, pour apprendre.

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. Cependant, rien n’empêche d’en faire un interpréteur sur le résultat final).

J’ai donc commencé à l’architecture, 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 et en améliorant l’ALU, celui de Ben Eater gèrant seulement l’addition/soustraction, je compte rajouter des comparaisons (<, ==) et des portes logiques (AND, OR) ).

Avec des très longs mois à architecturer et rearchitecturer, refaire, reréflechir, j’ai en trouvé d’autres comme celui-ci: nibbler. Au final, mon archi sera un mélange de celui de Ben, du site BigMessO’Wires et du livre The Elements Of Computing System

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 à 16 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. C’est toujours ce que j’ai dis avant de trouver un truc trop limitant, ou mal pensé, et que je le change encore et encore.

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, 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
mov #$ff, D
mov #$0, A
mov D, (A)

loop
  inc.c (A), end
  mov #$fd, A
  mov D, ($f.A)
  mov #$0, A
  mov D, (A)

  pause

end
  nop.npz end ; infinite loop

Chaque instruction prend au minimum 2 octets: 1 où est codée la source, la destination, et un saut (On peut donc faire une opération normale et un saut dans la même instruction), un autre où est codé l’opération arithmétique (destiné à l’ALU donc) et un dernier falcultatif où sont stockés la valeur d’un adressage immédiat ou de l’offset
s1 s2 d1 d2 j1 j2 j3 j4
p o1 o2 o3 o4 o5 o6 o7

Avec s = source, d = destination, 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’assembleur 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.


Les labels se déclarent simplement en tapant leur nom nomDuLabel, et sont référencés tout aussi simplement

1
2
loop
  inc.np loop

Il y a quatre possibilités de jump:

  • Si l’opération a provoqué une retenu, un overflow : carry -> c
  • Si l’opération donne un résultat négatif : negative -> n
  • Si l’opération donne 0 : zero -> z
  • Et finalement, un peu à part, si l’opération donne un résultat positif qui vaut en fait (!n && !z): positive -> p

Il n’y a pas d’opération spécifique au saut, étant donné qu’il est codé avec les autres instructions. Si l’on ajoute à la fin d’une instruction normale (par exemple: sub.z #5, D, equal), cela permet d’économiser un peu d’espace en ROM (et un peu de temps d’éxécution, puisque cela prend seulement 3 cycles d’horloge au lieu de 2*2). Si on ne peut pas pour X raison, il y a l’instruction nop à la rescousse, qui ne modifie pas l’etat des registres mais qui permet de faire un saut quand même.

Le saut souhaité se spécifie à la suite de l’opération, après un point et l’adresse de saut est donné en tout dernier argument: op.jmp (arg1,) (arg2,) jumpArgjmp sont les lettres correspondantes au type de saut. Le saut final correspondra au ’ou’ de ceux-là.
Par exemple, un nombre soit positif (p), soit négatif (n), c’est un nombre différent de 0, ou encore, un nombre sera toujours soit positif, soit négatif, soit égal à 0 (z), ce qui correspond donc au saut qui permet de tout le temps jump:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
mov #$0, A ; On part de 0
mov D, (A)
loop
  inc (A) ; Et on incrémente
  mov D, (A)

  mov #42, D
  sub.np (A), D, loop ; Tant que c'est différent de 42

end
  nop.npz end ; infinite loop

`

L’argument du saut peut être un nombre brut, ou venir du registre d’adressage A.


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


Plus d’infos sur les instructions, j’ai fait un fichier de spécifactions ici

L’Assembleur

Deux logiciels sont compris dans une seule suite: L’assembleur et la VM

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). L’argument -a pour lui indiquer qu’on veut assembler et l’argument -e pour lui indiquer qu’on veut émuler. 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 (✓)
  • Opérations de base (✓)
  • Sauts (❌)
  • 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

J’ai commencé la programmation d’une VM pour pouvoir voir mes programmes fonctionner. Il est codé dans la même application que l’assembleur, il suffit de lui rajouter l’option -vm et il émulera le binaire.

Rien n’est fait pour l’instant, j’attends d’avoir fini de recoder l’assembleur en lui même.

TODO:

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

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 (obselète)

É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

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

Auteur du sujet

Salut tout le monde !

Non, le projet n’est pas abandonné, désolé du silence :( .

J’ai fini l’architecture et je compte ne plus la changer même si je trouve d’autre grosse limitations.
La seule que je vois pour l’instant est que malgré le fait que je peux indexer 4Kio de RAM, je peux y accéder que par tranche de 255 octets, en effet, le registre d’adresse (A) est le seul qui permet d’accéder à la RAM mais il ne fait que 8 bits. Pour accéder aux 12 bits possible, j’ai donc fait un système d’offset, cependant cet offset doit être hardcodé en assembleur, impossible de l’indiquer via un autre registre. La solution aurait été de mettre un nouveau registre, mais comme je l’ai dit, je ne veux plus changer l’architecture.
La dernière modification qui a été faite a permis d’avoir un système de fonction dans l’assembleur, en rajoutant justement un nouveau registre, qui est spécialisé dans l’adressage (qui s’appelle donc A, pour Addressing, alors que avant, quand il n’y avait qu’un seul registre, il s’appelait A aussi, pour Accumulator. Maintenant, il s’appelle D, pour Data).

J’ai recommencé l’assembleur et la VM pour avoir quelque chose de mieux codé et plus maintenable (mais aussi surtout parce que j’avais rien d’autre à faire en attendant l’argent tomber du ciel :-° )

Au niveau de la construction, je n’ai toujours rien >_< , mais quelqu’un m’a parlé de la possibilité d’avoir des échantillons gratuits de circuits intégrés auprès de Texas Instrument ou STMicroelectronics, il faut que je me renseigne plus, mais ça pourrait être pas mal.

Je up ce topic aussi pour réagir face à la faille trouvé dans les processeurs, Meltdown et Spectre, et je tiens à signaler que mon CPU n’est pas affecté, si vous êtes intéressé pour plus de sécurité, n’hésitez pas à me MP :D

EDIT: J’ai oublié de dire que j’ai update le premier post aussi

É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