Skyduino:~#
Articles
Autre, C/C++, Corrigé, programmation, projet

[AVR] Emulateur DCPU-16

Bonjour tout le monde !

Comme vous avez pu le remarquer cela fait une petite semaine environ que je n’ai rien publié sur le blog.
Mais j’ai une bonne excuse, c’était pour terminer le projet que je vais vous présenter aujourd’hui ;)

Le projet en question : un émulateur de DCPU-16 sur base d’AVR.
(On est très loin de mes premiers morceaux de code, tout juste bon à faire clignoter une led ;))

Introduction :

Avant de partir dans des trucs compliqués voici un petit résumé rapide de l’histoire.

Le DCPU-16 est un microprocesseur 16 bits crée par notch (le concepteur du jeu minecraft) qui s’est mis en tête de concevoir un jeu, avec comme but, d’apprendre le langage assembleur à des enfants (oui rien que ça …).

Histoire de faire passer le truc sans faire peur aux non programmeurs (l’assembleur c’est pas compliqué mais ça fait peur à beaucoup, allez savoir pourquoi …) il a englobé son « DCPU-16″ d’une histoire sur fond d’incident inter-galactique avec un vaisseaux spatial, un super ordinateur et divers objets, … bref un minecraft-like avec de la programmation en plus.

Pour plus d’info (Attention : le jeu n’est encore qu’au stade de développement) :
http://0x10c.com/

Bien qu’elles soient encore dans un stade de réflexion, modification, amélioration, les spécifications du DCPU-16 sont disponibles :
http://0x10c.com/doc/dcpu-16.txt

Remarque : ce lien pointe vers les spécifications v1.1, entre temps les choses ont bien évolué … pour arriver en v1.7 à l’heure actuelle.
Les spécifications à jour sont dispo sur le forum de 0x10c (voir en bas de page de mon premier lien).

J’ai cependant décidé de concevoir mon émulateur en me basant sur les « vieilles » spécifications 1.1 et non 1.7 car beaucoup de choses ont évolué entre temps, et demanderaient énormément de code pour implémenter seulement une ou deux des fonctionnalité des spécifications 1.7

Le DCPU-16

Avant de partir dans le montage / code de l’émulateur voici un résumé rapide du fonctionnement interne du DCPU-16.

Général :
– Bus de données et bus d’adresse commun sur 16 bits (= architecture de von Neumann)
– mémoire RAM de 65K mots (un mot = 2 octets, soit 128Ko) pour le programme et les données
– 8 registres de travail (A, B, C, X, Y, Z, I, J)
– 1 pointeur d’adresse PC (program counter) (stocke l’adresse à exécuter âpres celle en cours)
– 1 pointeur de pile (démarre à 0xFFFF (= fin de la ram) et va en diminuant) SP (stack pointer)
– un registre spécial O (overflow) pour les calculs

Les instructions binaire sont codé sur 1 à 3 mots, suivant ce schéma de base :
(MSB) BBBBBBAAAAAAOOOO (LSB) (16 bits)
O -> opcode (instructions cpu)
A -> argument 1 (TOUJOURS pris en charge par le cpu avant l’argument B)
B -> argument 2

Les 4 bits d’opcode peuvent prendre les valeurs suivante (spécifications 1.1) :
Basic opcodes: (4 bits)
0x0: non-basic instruction - see below
0x1: SET a, b - sets a to b
0x2: ADD a, b - sets a to a+b, sets O to 0x0001 if there's an overflow, 0x0 otherwise
0x3: SUB a, b - sets a to a-b, sets O to 0xffff if there's an underflow, 0x0 otherwise
0x4: MUL a, b - sets a to a*b, sets O to ((a*b)>>16)&0xffff
0x5: DIV a, b - sets a to a/b, sets O to ((a<<16)/b)&0xffff. if b==0, sets a and O to 0 instead.
0x6: MOD a, b - sets a to a%b. if b==0, sets a to 0 instead.
0x7: SHL a, b - sets a to a<<b, sets O to ((a<<b)>>16)&0xffff.
0x8: SHR a, b - sets a to a>b, sets O to ((a<<16)>>b)&0xffff.
0x9: AND a, b - sets a to a&b
0xa: BOR a, b - sets a to a|b
0xb: XOR a, b - sets a to a^b
0xc: IFE a, b - performs next instruction only if a==b
0xd: IFN a, b - performs next instruction only if a!=b
0xe: IFG a, b - performs next instruction only if a>b
0xf: IFB a, b - performs next instruction only if (a&b)!=0

Quand aux arguments A et B il peuvent prendre les valeurs suivantes :
Non-basic opcodes: (6 bits) // Seul A peut prendre ces valeurs
0x00: reserved for future expansion
0x01: JSR b - pushes the address of the next instruction to the stack, then sets PC to b
0x02-0x3f: reserved

Values: (6 bits)
0x00-0x07: register (A, B, C, X, Y, Z, I or J, in that order)
0x08-0x0f: [register]
0x10-0x17: [next word + register]
0x18: POP / [SP++]
0x19: PEEK / [SP]
0x1a: PUSH / [--SP]
0x1b: SP
0x1c: PC
0x1d: O
0x1e: [next word]
0x1f: next word (literal)
0x20-0x3f: literal value 0x00-0x1f (literal)

Note : tout ce qui est entre crochets signifie « la valeur pointé par », exemple [0x0000] signifie « la valeur en RAM à l’adresse 0x0000″.

En plus de ces divers spécifications il existe un clavier et un écran (en mode texte) non documentés (enfin dans les spécifications 1.1, dans les nouvelles spécifications 1.7 l’écran, le clavier, etc sont maintenant documentés mais horriblement compliqués à mettre en œuvre).

L’écran est un bête écran 32×16 en mode texte (comme avec les vieux programmes DOS, ou encore les bios) dont la mémoire commence à 0x8000 pour s’achever à 0x81FF (soit 512 mots).
Les mots sont gérés par l’écran de la manière suivante :
(MSB) FFFFBBBBLCCCCCC (LSB) (16 bits)
F -> couleur du texte (foreground color)
B -> couleur de fond (background color)
L -> mode clignotant (blink) « 1 » = activé, « 0 » = désactivé
C -> caractéres (code ASCII)

Le clavier se trouve quand à lui à l’adresse 0x9000, lors d’un appui sur une touche le code ASCII est stocké à cette adresse.
Si l’adresse en question contient déjà un caractère ([0x9000] != 0) un beep est émis et la nouvelle valeur est ignorée (l’ancienne valeur reste donc en place).

Le debuggeur pour PC :

Vous vous en doutez bien je ne me suis pas lancé dans un émulateur sans avoir un minimum d’informations et d’outils pour m’aider ;)

Pour pouvoir débugger mon DCPU-16 virtuel je me suis basé sur un émulateur pour PC du nom de DCPU-Studio :
http://badsector.github.com/dcpustud/

Cela m’as permis d’avoir l’état des différents registres à chaque instruction et ainsi de pouvoir comparer avec les résultats obtenus sur mon émulateur.

Et si j’ai choisi d’utiliser DCPU-studio qui est pourtant maintenant obsolète, c’est tout simplement parce qu’il s’agit du seul debuggeur pour DCPU-16 en version 1.1 …

Tout les autres sont conçu suivant les spécifications 1.7 (ce qui est logique du reste) mais la gestion de la l.7 (gestion d’un bus de périphériques avec énumération, interruptions, calcul en nombre signé & non signé, …) m’aurait pris beaucoup trop de temps et cela aurait enlevé une grande partie du plaisir de coder cet émulateur.

(Je le rappelle, je code cet émulateur pour le plaisir, je n’en ai strictement aucune utilité par la suite)

Le montage :

(On dirait presque un PC classique, la puissance et les écrans bleus en moins ;))

Le montage constituant mon émulateur est axé autour d’un ATmega328p (oui comme dans les cartes arduino … mais y a pas qu’arduino dans la vie ;)).

Avec en supplément :
– une carte MicroVGA pour l’affichage en mode texte
– une alimentation pour breadboard
– une banques de 4 RAM SPI (4x 32Ko = 128Ko = 64K mots)
– une mémoire flash SPI de 128Ko (= 64K mots)
– des leds pour afficher divers informations, un buzzer pour faire du bruit et un bouton pour lancer / stopper l’exécution du programme.
– un module FTDI basic pour programmer l’ATmega via le bootloader optiboot (oui comme dans une carte arduino …)
– un connecteur ICSP pour sauver les meubles si le bootloader plante

Détails de la partie central du montage (de gauche à droite) :
– Connecteur FTDI basic
– bouton run/pause
– buzzer
– oscillateur quartz + ATmega328p
– led d’activités : CPU, RAM, ROM
– connecteur ICSP

La partie RAM / ROM SPI n’a pas encore était fini (et le code qui lui est associé n’as donc pas encore pu être testé), je doit passer commande des différents composants sous peu ;)
Pour le moment le code DCPU est stocké en dure dans l’ATmega328, qui embarque avec lui une petite RAM de 478 octets et une pile de 32 octets.

Détail des différents CI (de haut en bas) :
– mémoire flash
– mémoire RAM (banque A 1/4)
– mémoire RAM (banque B 2/4)
– mémoire RAM (banque C 3/4)
– mémoire RAM (banque D 4/4)

Le montage ne s’alimentant pas de lui même (note pour plus tard : inventer un générateur d’énergie infini, ça pourrait être pratique. J’ai la tartine, le beurre, il me manque plus qu’un chat) j’utilise une alimentation pour breadboard (dont j’ai fait un article à son sujet il y a quelque temps).

Le montage est alimenté en 3v3, la carte MicroVGA elle est alimenté en 5v (elle possède sont propre régulateur 5v -> 3v3).

A noter : les petits interrupteurs dip-switch qui servent à couper la liaison série lors de la programmation de l’ATmega via le bootloader.

Le schéma :

Bon ok je vous montre plein de jolies photo mais vous devez vous dire « hé ! C’est bien beau mais il est où le schéma ! ».

Voici donc le schéma complet du montage :

Le code de l’émulateur :

Le code de l’émulateur a été réalisé en avr-c et devrait compiler sans problème avec la dernière version de avr-gcc, que se soit sous linux ou sous windows (avec winAVR).

Je fourni aussi (je suis vraiment trop généreux :)) un makefile complet permettant de compiler / programmer / bootloader votre montage.
Le makefile est configuré pour utiliser un programmateur usbtiny pour le bootloading et le bootloader optiboot (version arduino UNO) pour la programmation « normal ».

Remarque : c’est un makefile générique, en modifiant seulement deux lignes vous pouvez le réutiliser comme bon vous semble dans vos programmes ;)

Bon sans plus attendre voici le code source (licence GNU GPLv3) + .hex précompilé par mes soins (embarque le code .dcpu de « WriteAllChars ») :
https://github.com/skywodd/AVR_DCPU_16

Au passage pour ceux qui voudraient la doc des différents modules de mon programme (tous réutilisables à souhait ;)) j’ai documenté chaque fichier avec doxygen ;)

Démonstration :

Le montage en mode « stop » :

Puis en mode « run » :

L’écran de boot :

Le classique « Hello world » que tout programmeur se doit de faire :

L’exemple « writeallchars » de DCPU-Studio (la charmap de DCPU-studio et celle de MicroVGA sont légèrement différente d’où le décalage sur certaine lignes) :

Et pour finir en beauté une petite vidéo de démonstration :
(désolé pour la qualité, comme d’habitude ma caméra filme très mal dans l’obscurité et le son est pourri …)

Conclusion :

C’est un petit projet « pour le fun », je ne me suis pas amusé à respecter à la lettre les dernières spécifications, ni même le nombre de cycles d’horloges par instructions.
Mon émulateur tourne à environ 50 instructions par seconde, ce qui est très lent, vraiment très lent, on est loin des 100KHz que précise les spécifications …

Plein de chose pourrait être améliorées …
Je ne voulais pas passer 1 mois à coder, optimiser et tester un DCPU-16 respectant à 100% les spécifications officielles, je n’aurais rien a y gagner.

C’est vraiment sympa (et pas si compliqué en faite) de coder un émulateur.
Si j’ai le temps il faudrait un jour que je me lance dans un émulateur pour cpu 8051, ça pourrait être sympa.

About these ads

Discussion

10 réflexions sur “[AVR] Emulateur DCPU-16

  1. « Petit projet »… Et bah qu’est ce que c’est qu’un grand projet pour toi :).
    Très intéressant tout ça.
    (Quelques petites fautes dans l’article mais bon, personne n’est parfait)

    PS: Je viens de Hackaday : http://hackaday.com/2012/07/25/emulating-the-dcpu-on-an-avr/

    Publié par pyroesp | 26 juillet 2012, 3 h 41 min
    • >> “Petit projet”… Et bah qu’est ce que c’est qu’un grand projet pour toi :).

      Ça ne représente « que » 3h de codage … et 24h de debug en tout genre …
      J’ai des projets bien plus imposants en tête, mais pas le temps qui va pour les faire malheureusement.

      >> (Quelques petites fautes dans l’article mais bon, personne n’est parfait)

      Je relie deux / trois fois minimum, mais l’orthographe c’est vraiment pas mon fort …

      Publié par skywodd | 26 juillet 2012, 16 h 36 min
  2. Hello,

    any chance to get your post in English? After Hackaday a lot of peaple want to share your gratt project. ;-)

    Best wishes,

    Maurice

    Publié par Maurice | 26 juillet 2012, 10 h 45 min
    • >> any chance to get your post in English? After Hackaday a lot of peaple want to share your gratt project.

      My english is really poor, excepted for some technical terms …
      If I try to make a english version of my article it will be worst than a google translate version ;)

      The first part of my article is just a little summary of the dcpu specifications (already in english).
      For the rest is just some little descriptions of images.

      The source code is entirely written and commented in english (I thinks the comments will make smile (or cry) lot of english people … sorry ^^ » »).

      Publié par skywodd | 26 juillet 2012, 16 h 42 min
  3. Projet intéressant ! As-tu prévu un moyen de programmer la rom pour changer le programme ?
    J’ai regardé vite fait le code. Ton programme commence par un JSR non ? (0x7C10)
    Pourquoi ?

    Publié par KB | 15 août 2012, 0 h 06 min
    • >> Projet intéressant ! As-tu prévu un moyen de programmer la rom pour changer le programme ?

      Non pas pour le moment, je n’ai pas les composants sous la main donc je n’ai pas vraiment pu avancer sur cette partie.
      Je ferai surement un montage annexe pour programmer la ROM.

      >> J’ai regardé vite fait le code. Ton programme commence par un JSR non ? (0x7C10)

      Oui, j’ai oublié de publier le code asm maintenant que j’y pense, désolé.

      C’est un des exemples de base fourni avec dcpu-studio :

      JSR WriteAllChars
      
      :EndLoop
        ADD [0x8000], 1
        SET PC, EndLoop
      
      :WriteAllChars
        SET Y, 0
        SET Z, 0
        :WriteAllChars_YLoop
          SET X, 0
          SET B, Y
          :WriteAllChars_XLoop
            SET C, X
            JSR WriteCharAt
            ADD Z, 1
            AND Z, 0xFF
            ADD X, 1
            IFE X, 32
            SET PC, WriteAllChars_XLoop_End
            SET PC, WriteAllChars_XLoop
          :WriteAllChars_XLoop_End
          ADD Y, 1
          IFE Y, 16
          SET PC, WriteAllChars_YLoop_End
          SET PC, WriteAllChars_YLoop
        :WriteAllChars_YLoop_End
      
        SET PC, POP
      
      :WriteCharAt ; X, Y, Z, C, B
        SET PUSH, A
        SET PUSH, B
        SET PUSH, C
        SET A, Y
        SHL A, 5
        ADD A, X
        SHL C, 4
        BOR B, C
        SHL B, 8
        BOR B, Z
        SET [0x8000+A], B
        SET C, POP
        SET B, POP
        SET A, POP
        SET PC, POP
      

      >> Pourquoi ?

      J’ai pas pris la peine de le modifier.
      J’ai passé tellement de temps sur l’émulateur qu’au final j’en avais un peu marre ;)

      Publié par skywodd | 15 août 2012, 0 h 19 min
  4. Apparemment le projet de notch est abandonné, dommage :-(

    http://notch.tumblr.com/post/58707926941/so-thats-what-im-going-to-do

    La communauté va reprendre le projet mais ce sera pas pareil amha…

    Publié par KB | 19 octobre 2013, 20 h 52 min

Rétroliens/Pings

  1. Pingback: Emulating the DCPU on an AVR - Hack a Day - 25 juillet 2012

  2. Pingback: Emulating the DCPU on an AVR | Orange Claymore Red Slime - 25 juillet 2012

Laisser un commentaire

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

Logo WordPress.com

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

Image Twitter

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

Photo Facebook

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

Photo Google+

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

Connexion à %s

Archives

Wow. Dogecoin. Amaze.

Laissez un tip en Dogecoin

DMMNFk6WBVTpx2Wu1Z35GL61QXSd6r6WQx

Suivre

Recevez les nouvelles publications par mail.

Rejoignez 795 autres abonnés