Page 1 sur 1

[TUTO]- L'Emulation en PureBasic : Premiers Pas.

Publié : mar. 06/déc./2011 14:01
par Mesa
Pour faire suite à flaith, plutôt qu'un tuto voici un "squelette" de travail pour celui qui voudrait se lancer dans l'émulation, en partant de zéro.

À la fin de cet exposé, vous trouverez une traduction en Purebasic que j'ai fait moi-même d'un émulateur qui n'est pas de moi.
Cet émulateur fonctionne.


Travail préparatoire.
Si vous commencez de zéro alors vous devez vous familiariser avec les notions suivantes : Je vous fais confiance pour y remédier, WikiPedia est là pour ça. (A lire : http://www.jbaumann.info/pdf/microprocesseurs.pdf)
Architecture RISC et CISC, bit, byte ou octet, bus du processeur, bus de donnée, bus de données, bus de commande, port I/O, interruption, microprocesseur, microcontrôleur, co-processeur arithmétique.

Vous devrez approfondir les notions suivantes (non exhaustif) : ALU (unité arithmétique et logique), Registre, opcode (jeu d'instruction), adressage (direct, indirect, indexé), décalage de bit, logique et ou xor non, calcul binaire, calcul hexadécimal, calcul bcd, assembleur. Pointeur de programme PC, pointeur de pile SP, registre indicateur d'état. Stockage MSB, LSB.

Pas besoin d'avoir un niveau ingénieur mais simplement savoir ce que cela signifie. Il est avantageux de savoir calculer en binaire et en hexadécimal.


Le choix du processeur à émuler.
La puce : Chip8

Je vous le dis tout de suite, si j'ai choisi Chip8, c'est parce que j'ai étudié un tuto à cette adresse http://www.siteduzero.com/tutoriel-3-55 ... art_555023
Un pdf se trouve icihttp://www.siteduzero.com/tuto_pdf.php?vrsid=555021

Évidemment, vous devrez l'étudier aussi.

Avant de vous jeter dessus, sachez que le tuto d'origine est codé en C avec la librairie SDL ... J'ai traduit le code en PureBasic sans SDL, voir en fin de post. Dans ma traduction, si l'émulation fonctionne, il y a un bug dans l'affichage et le clavier ne fonctionne pas. En guise d'exercice, essayez de faire fonctionner ce code !

Par contre l'auteur propose en téléchargement un deuxième code source de son émulateur en C++/SDL avec toute la panoplie du C++. J'ai aussi traduit ce code en PureBasic et j'en ai profité pour éliminer quelques boggues. De plus, pour le simplifier, exit la programmation orienté objet, dehors la SDL, oust la gestion des Thread, pas une ligne d'assembleur même pas un pointeur que du pure bonheur de PureBasic !

L'auteur (BestCoder) a choisi cette puce parce que son fonctionnement est le plus simple possible. En effet, entrer dans le monde de l'émulation peut être très vite décourageant car il faut gérer de front plusieurs problèmes et tous en même temps : le processeur, la mémoire, l'affichage, le clavier sans parler du son et du joystick quand il existe. Le déboggage devient vite un enfer. Nous commencerons petit et c'est tant mieux.


Faisons connaissance avec Chip8.
Chip8 apparaît dans deux consoles de jeu en 1977... Oui, oui en 1977, le COSMAC VIP et le TELMAC 1800 et deux ordinateurs en 1978 le dream6800 et le ETI660. Jetez donc un coup d'œil au site Internet dédié à cette puce: http://www.chip8.com

Chip8 ressemble plus à un microcontrôleur qu'à un microprocesseur, c'est même un interpréteur, peu importe... Cela veut dire que c'est un cas un peu particulier mais tout ce que vous apprendrez avec elle, ne sera pas perdu, bien au contraire, au cas où vous poursuivriez vers l'émulation d'une puce plus récente.

Elle a eu des descendants sous le nom de SuperChip8, MeagaChip8 (http://www.revival-studios.com/?page=21) et une version 16 bits Chip16 (http://www.siteduzero.com/forum-83-6105 ... hip16.html).
Pour info, Chip8 s'est réincarné dans les années 90 sous le nom HP48 que l'on trouve dans les calculatrices graphiques du même nom, avec quelques opcodes de plus.

Les acharnés de l'émulation se formeront en étudiant cet émulateur par eux-mêmes puis le feront évoluer vers l'émulation de SuperChip8, MeagaChip8, Chip16 puis la Nintendo NESS, la gameboy, etc...
La difficulté allant crescendo.

Intéressons-nous à Chip8. Pour cela, il nous faut un maximum d'informations sur cette puce. http://www.Chip8.com et http://devernay.free.fr/hacks/chip8/C8TECH10.HTM nous en donnent déjà beaucoup.

Chip8 : Puce 8 bits
Mémoire adressable : 4 Ko (oui, oui que 4 Ko) de RAM
Registres 8 bits : 16
Registres 16 bits : 1
2 timers compte à rebours de 8bits à 60 Hz pour le système et le son
1 PC (pointeur de programme) 16 bits
1 SP (pointeur de pile) 8bits
1 pile de 16 niveaux de 16 bits
Opcodes : 35 constitués de 2 octets en MSB (ce qui est très peu, comparez avec les opcode du pentium : http://ref.x86asm.net/coder32-abc.html) (http://en.wikipedia.org/wiki/X86_instruction_listings)
Mode d'adressage (direct) unique inclus dans l'opcode
Horloge : vitesse inconnue, probablement 1,7 MHz

Entrée : Clavier de 16 touches

Sortie : Écran monochrome noir et blanc de 64x32 pixels, comment faire plus simple !


L'Emulation
Maintenant, vous pouvez suivre le tuto de BestCoder sur http://www.siteduzero.com/tutoriel-3-55 ... nsole.html

La traduction du C en PB est beaucoup plus simple que je ne le pensais. Je déteste ce fichu langage C et mes connaissances dans ce langage sont minimales. Alors si j'ai pu y arriver, vous aussi. C'est un excellent exercice d'apprentissage de PB. (PB= PureBasic)

Pour la traduction du C en PB, traduire :
#define en MACRO/EndMacro
#include par rien du tout ou par xincludefile si vous utilisez des .pbi (inutile ici)
Uint8 en .a (octet non signé)
Uint16 en .u (mot non signé)
Typedef par rien du tout
Void par rien du tout (ne pas oublier procedure()/endprocedure)
[] en () dans les tableaux
. en \ dans les structures
for(i=0;i<16;i++) en for i=0 to 15... next i
break; par rien du tout
: par rien du tout
Certains ; en :
D'autres ; les laisser ou les supprimer (ça sera plus clair in situ)
&& en &
|| en |
Accent circonflexe en !

Ne traduisez pas la SDL, utilisez les fonctions PB : InitSprite(), InitKeyboard(), InitMouse(), InitSound(), OpenWindow(), OpenWindowedScreen(), StartDrawing(ScreenOutput()), StopDrawing(), FlipBuffers(),ExamineKeyboard(); KeyboardPushed(#PB_Key_xxxx) et Box().

Pour la gestion des fichiers : ReadFile(), Eof() , ReadByte(), CloseFile()

Timers : ElapsedMilliseconds()


Principes généraux de l'émulation :
En plus de l'émulateur nous avons besoin d'une ROM
Une ROM est un fichier contenant un jeu par exemple. C'est une suite d'octets représentant des opcodes. A l'origine, la ROM était gravée dans une puce et quelqu'un a dumpé (transferé) le contenu de cette puce dans un fichier.


Comment fonctionne un émulateur ?
Dans la fenêtre de l'émulateur :
Ouvrir et mettre en mémoire une ROM
Boucle:
Lire un octet ou un mot et pour chaque octet lu
Traduire l'octet ou le mot en opcode et l'opcode en instruction purebasic
(la compilation traduira les instructions PB en opcode compatible avec le microprocesseur de votre ordinateur)
Exécuter l'opcode
Délai d'attente sinon le jeu sera injouable tellement il sera rapide
(de plus certaine puce on besoin de synchroniser des actions pas chip8)
Gérer le son
Mise à jour de l'écran
Examiner le clavier et agir en conséquence (mettre en pause, etc)
Fin de la boucle quand on appuie sur Echap (par exemple)


Dans ce "squelette", 2 points sont délicats. La traduction des opcodes en PB n'est pas forcément difficile mais trèèès lonnnnnguuuuue à faire. Elle réclame aussi une bonne compréhension du fonctionnement de la puce.

Le deuxième point est la gestion du temps. Ici on a de la chance, c'est l'électronique de Chip8 qui s'occupe de ça. Nous ne nous occuperons que de ralentir l'émulateur. Mais quand il s'agit d'émuler la Wii par exemple, les programmeurs sont obligés d'utiliser la compilation dynamique, c'est-à-dire que l'émulateur lui-même peut recompiler une partie de son propre code en temps réel tout en émulant, afin de choisir la meilleure stratégie de gain de temps.

L'auteur du tuto a fourni une ROM qu'il a créée pour savoir si tous les opcodes de votre émulateur fonctionnent, elle s'appelle "BC_test.ch8".
Si tout va bien, vous devez obtenir ceci :
Image
By mesarc at 2011-12-06

Ensuite, vous pourrez jouer à breakout et à une cinquantaine de jeux dont PONG et INVADERS.
Image
By mesarc at 2011-12-06

Vous trouverez tout dans les rar à ces adresses :
http://www.mediafire.com/?p8hqakcvsn1y8oi
http://www.mediafire.com/?ajbdsdfo0mrs9yr


Pour finir, certaines ROM ne fonctionnent pas bien, cela est dû aux informations incomplètes de Chip8 utilisées ici. Il faudrait plus de renseignement sur cette puce.

Et enfin, j'ai eu la flemme de faire fonctionner le son mais ça reste abordable.

Bon courage.

Re: [TUTO]-Base d'un trés simple Emulateur

Publié : mar. 06/déc./2011 14:02
par Mesa
Code PB provenant du tuto :
Emulation ok
Video : bug
Clavier : bug
Son : non

Code : Tout sélectionner

; Traduction du code source
; PB 4.51  4.60
; Décembre 2011
; Traducteur Mesa
; Auteur original BestCoder
;
; BUG : Affichage, claver, son
;
;========================================


Macro NOIR : 0 :EndMacro
Macro BLANC :1 :EndMacro
Macro lw :64:EndMacro
Macro LH :32:EndMacro
Macro DIMPIXEL :8:EndMacro
Macro WIDTH  : lw*DIMPIXEL:EndMacro
Macro HEIGHT : LH*DIMPIXEL:EndMacro


  Structure SDL_Rect
   x.l  
   y.l  
   w.l  
   h.l  
 EndStructure

Structure PIXELS

    position.SDL_Rect; //Regroupe l’abscisse et l'ordonnée
    couleur.l;   //comme son nom l'indique, c'est la couleur
EndStructure


Global Dim pixel.PIXELS(lw,LH)


Enumeration
  #Imagedacceuil
  
  EndEnumeration

Procedure initialiserPixel() 
    ;0 pour le noir ou éteint ;
    ;1 pour le blanc ou allumé.

    x=0
    y=0;
     For x=0 To lw-1
        For y=0 To LH-1
            pixel(x,y)\position\x=x*DIMPIXEL;
            pixel(x,y)\position\y=y*DIMPIXEL;
            pixel(x,y)\couleur=NOIR; //on met par défaut les pixels en noir
        Next y
    Next x
  EndProcedure
Procedure initialiserEcran()
   If InitSprite()=0 Or InitKeyboard()=0 Or InitMouse()=0 Or InitSound()=0
     MessageRequester("Erreur", "Erreur DirectX", #PB_MessageRequester_Ok)
     End
   EndIf
   chemin.s=GetCurrentDirectory()
   
   OpenWindow(0, 100, 0, WIDTH+60, HEIGHT+60, "PureBasic - Emulateur Chip8  Appuyer sur ECHAP pour sortir", #PB_Window_SystemMenu|#PB_Window_ScreenCentered);#PB_Window_SizeGadget)
      OpenWindowedScreen(WindowID(0),0,0,WIDTH,HEIGHT,0,0,0, #PB_Screen_SmartSynchronization);OpenWindowedScreen(FenetreID, x, y, Largeur, Hauteur, RedimensionnementAuto, OffsetDroit, OffsetBas [, FlipMode])
      StartDrawing(ScreenOutput())
        DrawingMode(#PB_2DDrawing_Default )
                
         LoadImage(#Imagedacceuil, chemin+"images\home.bmp") 
        DrawImage(ImageID(#Imagedacceuil), 0, 0 ) 
      StopDrawing()  
    ;FlipBuffers()
EndProcedure
Procedure dessinerPixel(pixel,xx,yy);Array pix.PIXEL(2))
 StartDrawing(ScreenOutput())
 ;/*pixel.couleur prend deux valeur 0 ou 1, si c'est 0, on dessine le pixel noir sinon c'est 1 alors on dessine le pixel blanc*/
 If pixel(xx,yy)\couleur=0
   Box(pixel(xx,yy)\position\x,pixel(xx,yy)\position\y,DIMPIXEL,DIMPIXEL,RGB(0,0,0))
 Else
   Box(pixel(xx,yy)\position\x,pixel(xx,yy)\position\y,DIMPIXEL,DIMPIXEL,RGB(255,255,255))
 EndIf
 StopDrawing()
EndProcedure
Procedure effacerEcran()
    ;//Pour effacer l'ecran, on remet tous les pixels en noir
    ;Box(0, 0, WIDTH, HEIGHT, RGB(0,0,0))
    ClearScreen(RGB(0, 0, 0)) ; ne jamais utiliser dans un bloc startdrawing
    x=0
    y=0;
    For x=0 To lw-1
      For y=0 To LH-1
          pixel(x,y)\couleur=NOIR;
        Next y
    Next x
EndProcedure
Procedure updateEcran()
    ;//On dessine tous les pixels à l'écran.
  
 For x=0 To lw-1   
        For y=0 To LH-1       
             dessinerPixel(pixel(x,y),x,y);
         Next y
  Next x
  FlipBuffers()
        
EndProcedure
Procedure essaiinitpixel()
 ;StartDrawing(ImageOutput(#ecran))
  

    For x=0 To lw-1   
        For y=0 To LH-1 
            pixel(x,y)\position\x=x*DIMPIXEL;
            pixel(x,y)\position\y=y*DIMPIXEL;

            If x%(y+1)=0
              pixel(x,y)\couleur=NOIR;
            Else
              pixel(x,y)\couleur=BLANC;
            EndIf
            dessinerPixel(pixel,x,y)
    Next y
  Next x
  FlipBuffers()
  ;StopDrawing()
EndProcedure

Macro TAILLEMEMOIRE :4096:EndMacro
Macro ADRESSEDEBUT :512:EndMacro
Macro NBROPCODE :35:EndMacro 


    Structure CPUS
      Array memoire.a(TAILLEMEMOIRE) ;declaration d'un tableau pour la mémoire de 4Ko
      ;Registres
      Array V.a(16) ; 8bits
      I.u ; 16 bits stocke une adresse memoire ou dessinateur
      Array saut.u(16) ;//Pour gerer les sauts dans "memoire" 16 aux maximums
      nbrsaut.a ;//stocke le nombre de sauts effectués pour ne pas dépasser 16
      compteurJeu.a ;// Compteur pour la synchronisation 
      compteurSon.a ;// Compteur pour le son
      pc.u ;//pour parcourir le tableau "memoire" "program counter" 16bits
      Array touche.a(16)
    EndStructure

Global cpu.CPUS ;  //déclaration de notre CPU

   Structure JUMP
    Array masque.u(NBROPCODE) ;   // La Chip8 à 35 opérations, chaque opération a son masque
    Array id.u(NBROPCODE)  ;   //  idem , chaque opération a son identifiant
  EndStructure

 Global jp.JUMP;

Procedure initialiserJump ()

  jp\masque(0)= $0000: jp\id(0)=$0FFF;          /* 0NNN */
  jp\masque(1)= $FFFF: jp\id(1)=$00E0;          /* 00E0 */
  jp\masque(2)= $FFFF: jp\id(2)=$00EE;          /* 00EE */
  jp\masque(3)= $F000: jp\id(3)=$1000;          /* 1NNN */
  jp\masque(4)= $F000: jp\id(4)=$2000;          /* 2NNN */
  jp\masque(5)= $F000: jp\id(5)=$3000;          /* 3XNN */
  jp\masque(6)= $F000: jp\id(6)=$4000;          /* 4XNN */
  jp\masque(7)= $F00F: jp\id(7)=$5000;          /* 5XY0 */
  jp\masque(8)= $F000: jp\id(8)=$6000;          /* 6XNN */
  jp\masque(9)= $F000: jp\id(9)=$7000;          /* 7XNN */
  
  jp\masque(10)= $F00F: jp\id(10)=$8000;          /* 8XY0 */
  jp\masque(11)= $F00F: jp\id(11)=$8001;          /* 8XY1 */
  jp\masque(12)= $F00F: jp\id(12)=$8002;          /* 8XY2 */
  jp\masque(13)= $F00F: jp\id(13)=$8003;          /* BXY3 */
  jp\masque(14)= $F00F: jp\id(14)=$8004;          /* 8XY4 */
  jp\masque(15)= $F00F: jp\id(15)=$8005;          /* 8XY5 */
  jp\masque(16)= $F00F: jp\id(16)=$8006;          /* 8XY6 */
  jp\masque(17)= $F00F: jp\id(17)=$8007;          /* 8XY7 */
  jp\masque(18)= $F00F: jp\id(18)=$800E;          /* 8XYE */
  jp\masque(19)= $F00F: jp\id(19)=$9000;          /* 9XY0 */
  jp\masque(20)= $F000: jp\id(20)=$A000;          /* ANNN */
  
  jp\masque(21)= $F000: jp\id(21)=$B000;          /* BNNN */
  jp\masque(22)= $F000: jp\id(22)=$C000;          /* CXNN */
  jp\masque(23)= $F000: jp\id(23)=$D000;          /* DXYN */
  jp\masque(24)= $F0FF: jp\id(24)=$E09E;          /* EX9E */
  jp\masque(25)= $F0FF: jp\id(25)=$E0A1;          /* EXA1 */
  jp\masque(26)= $F0FF: jp\id(26)=$F007;          /* FX07 */
  jp\masque(27)= $F0FF: jp\id(27)=$F00A;          /* FX0A */
  jp\masque(28)= $F0FF: jp\id(28)=$F015;          /* FX15 */
  jp\masque(29)= $F0FF: jp\id(29)=$F018;          /* FX18 */
  jp\masque(30)= $F0FF: jp\id(30)=$F01E;          /* FX1E */
 
  jp\masque(31)= $F0FF: jp\id(31)=$F029;          /* FX29 */
  jp\masque(32)= $F0FF: jp\id(32)=$F033;          /* FX33 */
  jp\masque(33)= $F0FF: jp\id(33)=$F055;          /* FX55 */
  jp\masque(34)= $F0FF: jp\id(34)=$F065;          /* FX65 */
  
EndProcedure 
Procedure initialiserCpu()
  
  ;//On initialise le tout

    
    For i=0 To TAILLEMEMOIRE-1; 
         cpu\memoire(i)=0;
    Next i

     For i=0 To 15
        cpu\V(i)=0;
        cpu\saut(i)=0;
        cpu\touche(i)=0;
     Next i

    cpu\pc=ADRESSEDEBUT;
    cpu\nbrsaut=0;
    cpu\compteurJeu=0;
    cpu\compteurSon=0;
    cpu\I=0;
    
    initialiserJump()
  EndProcedure 
Procedure reset()
    
    For i=0 To 15
    
        cpu\V(i)=0;
        cpu\saut(i)=0;
        cpu\touche(i)=0;
    Next i

    cpu\pc=ADRESSEDEBUT;
    cpu\nbrsaut=0;
    cpu\compteurJeu=0;
    cpu\compteurSon=0;
    cpu\I=0;
    initialiserPixel();
    updateEcran();
  EndProcedure
Procedure.a recupererAction(opcode.u)

    action.a;
    resultat.u;

    For action=0 To NBROPCODE-1
        resultat= (jp\masque(action)&opcode);  /*On récupère les bits concernés par le test, l'identifiant de l'opcode */

        If resultat = jp\id(action) ;/*On a trouvé l'action a effectuée*/
           Break; /*Plus la peine de continuer la boucle car la condition n'est vraie qu'une seule fois*/
    EndIf
  Next action
  ProcedureReturn action;  //on renvoie l'indice de l'action à effectuer
EndProcedure 
Procedure decompter()

    If cpu\compteurJeu>0
      cpu\compteurJeu=cpu\compteurJeu-1;
    EndIf
  
    If cpu\compteurSon>0
      cpu\compteurSon=cpu\compteurSon-1;
    EndIf
    

  EndProcedure
Procedure.u recupererOpcode()
     
  ProcedureReturn (cpu\memoire(cpu\pc)<<8)+cpu\memoire(cpu\pc+1);
  
EndProcedure
Procedure dessinerEcran(b1.a, b2.a, b3.a)
  x.a=0
  y.a=0

  codage.a=0
  decalage.a=0;
  
    cpu\V($F)=0;

     For k=0 To b1-1
          codage=cpu\memoire(cpu\I+k);//on récupère le codage de la ligne à dessiner
          
          y=(cpu\V(b2)+k)%LH;//on calcule l'ordonnée de la ligne à dessiner, on ne doit pas dépasser L

           decalage=7
           For j=0 To 7
             decalage=decalage-1
                x=(cpu\V(b3)+j)%lw; //on calcule l’abscisse, on ne doit pas dépasser l

                        If ((codage)&($1<<decalage))<>0 ;// on récupère le bit correspondant //si c'est blanc
                        
                            If ( pixel(x,y)\couleur=BLANC)  ;// le pixel était blanc
                            
                                pixel(x,y)\couleur=NOIR; //on l’éteint
                                cpu\V($F)=1;//  il y a donc collusion

                            
                            Else ;//sinon
                            
                                 pixel(x,y)\couleur=BLANC;//On l'allume
                            EndIf


                        EndIf

            Next j
        Next k
      EndProcedure 
Declare.a attendAppui(b3.a)       
Procedure.a interpreterOpcode(opcode.u) ;interpreterOpcode
  
  continuer.a=1
  b4.a
  b3.a
  b2.a
  b1.a;

    b3=(opcode&($0F00))>>8;  // On prend les 4 bits ,  b3 représente X
    b2=(opcode&($00F0))>>4;  // Idem                 , b2 représente Y
    b1=(opcode&($000F));     // On prend les 4 bits de poids faible

      ;Pour obtenir NNN par exemple, If faut faire   (b3<<8) + (b2<<4) + (b1)

    
    b4= recupererAction(opcode);   //Permet de connaitre l'action à effectuer
 
    Select b4
    
     Case 0 ; Cet opcode n'est pas implémenté
                ;Break;
              
     Case 1 ;00E0 efface l'écran
                ;break;
               effacerEcran()
 
     Case 2 ;00EE: reviens du saut
              If cpu\nbrsaut>0
                   cpu\nbrsaut=cpu\nbrsaut-1;
                   cpu\pc=cpu\saut(cpu\nbrsaut);
              EndIf
             
              
            ;// sinon, on a effectué plus de retours que de sauts
               
                ;break;
            
    Case 3 ;1NNN : effectue un saut à l'adresse 1NNN
            cpu\pc=(b3<<8)+(b2<<4)+b1; //on prend le nombre NNN (pour jumper )
            cpu\pc=cpu\pc-2; //N'oublions pas le pc+=2 à la fin du bloc switch
               
                ;break;
           
    Case 4 ;2NNN :appelle le sous-programme en NNN mais on revient aprés
            cpu\saut(cpu\nbrsaut)=cpu\pc;//On garde là où on était 
            If cpu\nbrsaut<15
              cpu\nbrsaut=cpu\nbrsaut+1
            ;// sinon, on a effectué trop  de sauts
           EndIf
              cpu\pc=(b3<<8)+(b2<<4)+b1;//on prend le nombre NNN (pour jumper )
              cpu\pc=cpu\pc-2; //N'oublions pas le pc+=2 à la fin du block switch
             
           
                ;break;
            
    Case 5  ;3XNN  Saute l'instruction suivante si VX est égale à NN
            If cpu\V(b3)=((b2<<4)+b1)
                 cpu\pc=cpu\pc+2;
             EndIf
                
 
                ;break;
            
    Case 6  ;4XNN Saute l'instruction suivante si VX et NN ne sont pas égaux\
              If cpu\V(b3)<>((b2<<4)+b1)
                      cpu\pc=cpu\pc+2;
               EndIf  

                ;break;
            
    Case 7  ;5XY0 Saute l'instruction suivante si VX et VY sont égaux
               If cpu\V(b3)=cpu\V(b2)
                     cpu\pc=cpu\pc+2;
               EndIf 
                ;break;
           

    Case 8  ;6XNN 	Définit VX à NN
               cpu\V(b3)=(b2<<4)+b1; 
                ;break;
            
    Case 9   ;7XNN 	Ajoute NN à VX
               cpu\V(b3)=cpu\V(b3)+(b2<<4)+b1; 

                ;break;
           
    Case 10 ;8XY0 Définit VX à la valeur de VY
                cpu\V(b3)=cpu\V(b2);

                ;break;
            
    Case 11  ;8XY1	Définit VX à VX Or VY\
              cpu\V(b3)=cpu\V(b3)|cpu\V(b2);

                ;break;
             
    Case 12  ; 8XY2 Définit VX à VX And VY\
                cpu\V(b3)=cpu\V(b3)&cpu\V(b2);
                ;break;
            
    Case 13  ; 8XY3 Définit VX à VX XOr VY
               cpu\V(b3)=cpu\V(b3)!cpu\V(b2);
                ;break;
            
    Case 14 ; 8XY4 Ajoute VY à VX \VF est mis à 1 quand il y a un dépassement de mémoire (carry), et à 0 quand il n'y en pas\
               If((cpu\V(b3)+cpu\V(b2))>255)
                    cpu\V($F)=1; //V(15)
              Else
                     cpu\V(F)=0; //V(15)
              EndIf
                  cpu\V(b3)=cpu\V(b3)+cpu\V(b2);


                ;break;
            
    Case 15 ;8XY5 VY est soustraite de VX\ VF est mis à 0 quand il y a un emprunt, et à 1 quand il n'y a en pas\
               If (cpu\V(b3)<cpu\V(b2))
                      cpu\V($F)=0; //cpu\V(15)
                Else
                       cpu\V($F)=1; //cpu\V(15)
                EndIf
                cpu\V(b3)=-cpu\V(b2); 

                ;break;
            
    Case 16 ;  8XY6 Décale (shift) VX à droite de 1 bit\VF est fixé à la valeur du bit de poids faible de VX avant le décalage
               cpu\V($F)=(cpu\V(b3)&($01));
                cpu\V(b3)=(cpu\V(b3)>>1);

                ;break;
            
    Case 17  ; 8XY7 VX = VY- VX,VF est mis à 0 quand il y a un emprunt, et à 1 quand il n'y en a pas\
                If (cpu\V(b2)<cpu\V(b3))     ;// /!\ VF est mis à 0 quand il y a emprunt!!!!!!!!
                      cpu\V($F)=0; //cpu\V(15)
                Else
                      cpu\V($F)=1; //cpu\V(15)
                EndIf

                cpu\V(b3)=cpu\V(b2)-cpu\V(b3);;break;
            
    Case 18 ; 8XYE Décale (shift) VX à gauche de 1 bit\ VF est fixé à la valeur du bit de poids fort de VX avant le décalage
               cpu\V($F)=(cpu\V(b3)>>7);
                cpu\V(b3)=(cpu\V(b3)<<1); 

                ;break;
             

    Case 19 ;9XY0 Saute l'instruction suivante si VX et VY ne sont pas égaux\
                If(cpu\V(b3)<>cpu\V(b2))
                     cpu\pc=cpu\pc+2;
                    EndIf
                ;break;
            
    Case 20 ;ANNN 	Affecte NNN à I
              cpu\I=(b3<<8)+(b2<<4)+b1;

                

                ;break;
            
    Case 21 ; BNNN Passe à l'adresse NNN + V0\
            cpu\pc=(b3<<8)+(b2<<4)+b1+cpu\V(0);
            cpu\pc=cpu\pc-2;
            

            ;break;

            
    Case 22 ;CXNN Définit VX à un nombre aléatoire inférieur à NN
              ;cpu\V(b3)=(rand())%((b2<<4)+b1+1)
              cpu\V(b3)=Random((b2*16)+b1);Random((b2<<4)+b1-1)
               
            ;break;
          

    Case 23 ;DXYN Dessine un spirite aux coordonnées (VX, VY)\

            dessinerEcran(b1,b2,b3);

            ;break;

            
    Case 24  ; EX9E Saute l'instruction suivante si la clé stockée dans VX est pressé
              If cpu\touche(cpu\V(b3))=1 ;//1 pressé ; 0 relâché
                  cpu\pc=cpu\pc+2;
              EndIf 

                ;break;
           
    Case 25 ;EXA1 Saute l'instruction suivante si la clé stockée dans VX n'est pas pressé
                
                If cpu\touche(cpu\V(b3))=0;//1 pressé 0 ;relâché
                    cpu\pc=cpu\pc+2;
                EndIf 
                ;break;
            

    Case 26 ;FX07 Définit VX à la valeur de la temporisation
              cpu\V(b3)=cpu\compteurJeu; 

                ;break;
            
    Case 27 ;FX0A L'appuie sur une touche est attendue, et ensuite stockée dans VX
               continuer=attendAppui(b3);

                ;break;
     
 
    Case 28 ; FX15 Définit la temporisation à VX
                cpu\compteurJeu=cpu\V(b3);

                ;break;
            
    Case 29 ; FX18 Définit la minuterie sonore à VX
               cpu\compteurSon=cpu\V(b3); 

                ;break;
            
    Case 30 ; FX1E Ajoute à VX I\ VF est mis à 1 quand il y a overflow (I+VX>$FFF), et à 0 si tel n'est pas le cas
                If (cpu\I+cpu\V(b3))>$FFF
                        cpu\V($F)=1;
                Else
                       cpu\V($F)=0;
                EndIf
                cpu\I=cpu\I+cpu\V(b3)


    Case 31 ; FX29 Définit I à l'emplacement du caractère stocké dans VX\ Les caractères 0-F (en hexadécimal) sont représentés par une police 4x5
               cpu\I=5*cpu\V(X) 

                ;break;
            

    Case 32  ;FX33 Stocke dans la mémoire le code décimal représentant VX (dans I, I+1, I+2)
            cpu\memoire(cpu\I)=(cpu\V(b3)-cpu\V(b3)%100)/100; //stocke les centaines
            cpu\memoire(cpu\I+1)=(((cpu\V(b3)-cpu\V(b3)%10)/10)%10);//les dizaines
            cpu\memoire(cpu\I+2)=cpu\V(b3)-cpu\memoire(cpu\I)*100-10*cpu\memoire(cpu\I+1);//les unités
               

                ;break;
            
    Case 33  ;FX55 Stocke V0 à VX en mémoire à partir de l'adresse I\
               
                For j=0 To b3
                    cpu\memoire(cpu\I+j)=cpu\V(j);
                Next j

                ;break;
            
    Case 34  ; FX65 Remplit V0 à VX avec les valeurs de la mémoire à partir de l'adresse I
               
                For j=0 To b3;
                   cpu\V(j)=cpu\memoire(cpu\I+j);
                Next j

                ;break;
            

    ;Default; si ça arrive ,il y un truc qui cloche

                    ;break;
             

EndSelect
    cpu\pc=cpu\pc+2; // on passe au prochain opcode
    ProcedureReturn continuer    
 EndProcedure  
Procedure chargerFont()

    cpu\memoire(0)=$F0:cpu\memoire(1)=$90:cpu\memoire(2)=$90:cpu\memoire(3)=$90: cpu\memoire(4)=$F0:; O
    
    cpu\memoire(5)=$20:cpu\memoire(6)=$60:cpu\memoire(7)=$20:cpu\memoire(8)=$20:cpu\memoire(9)=$70:; 1

    cpu\memoire(10)=$F0:cpu\memoire(11)=$10:cpu\memoire(12)=$F0:cpu\memoire(13)=$80: cpu\memoire(14)=$F0:; 2

    cpu\memoire(15)=$F0:cpu\memoire(16)=$10:cpu\memoire(17)=$F0:cpu\memoire(18)=$10:cpu\memoire(19)=$F0:; 3

    cpu\memoire(20)=$90:cpu\memoire(21)=$90:cpu\memoire(22)=$F0:cpu\memoire(23)=$10:cpu\memoire(24)=$10:; 4

    cpu\memoire(25)=$F0:cpu\memoire(26)=$80:cpu\memoire(27)=$F0:cpu\memoire(28)=$10:cpu\memoire(29)=$F0:; 5

    cpu\memoire(30)=$F0:cpu\memoire(31)=$80:cpu\memoire(32)=$F0:cpu\memoire(33)=$90:cpu\memoire(34)=$F0:; 6

    cpu\memoire(35)=$F0:cpu\memoire(36)=$10:cpu\memoire(37)=$20:cpu\memoire(38)=$40:cpu\memoire(39)=$40:; 7

    cpu\memoire(40)=$F0:cpu\memoire(41)=$90:cpu\memoire(42)=$F0:cpu\memoire(43)=$90:cpu\memoire(44)=$F0:; 8

    cpu\memoire(45)=$F0:cpu\memoire(46)=$90:cpu\memoire(47)=$F0:cpu\memoire(48)=$10:cpu\memoire(49)=$F0:; 9

    cpu\memoire(50)=$F0:cpu\memoire(51)=$90:cpu\memoire(52)=$F0:cpu\memoire(53)=$90:cpu\memoire(54)=$90:; A

    cpu\memoire(55)=$E0:cpu\memoire(56)=$90:cpu\memoire(57)=$E0:cpu\memoire(58)=$90:cpu\memoire(59)=$E0:; B

    cpu\memoire(60)=$F0:cpu\memoire(61)=$80:cpu\memoire(62)=$80:cpu\memoire(63)=$80:cpu\memoire(64)=$F0:; C

    cpu\memoire(65)=$E0:cpu\memoire(66)=$90:cpu\memoire(67)=$90:cpu\memoire(68)=$90:cpu\memoire(69)=$E0:; D

    cpu\memoire(70)=$F0:cpu\memoire(71)=$80:cpu\memoire(72)=$F0:cpu\memoire(73)=$80:cpu\memoire(74)=$F0:; E

    cpu\memoire(75)=$F0:cpu\memoire(76)=$80:cpu\memoire(77)=$F0:cpu\memoire(78)=$80:cpu\memoire(79)=$80:; F

    ;OUF!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
EndProcedure
Procedure.a attendAppui(b3.a)

attend=1
continuer=1;

    While(attend)
    ExamineKeyboard()
      
      If KeyboardPushed(#PB_Key_Escape)  
        continuer=0
        attend=0
       ; End 
       
       
       
        ElseIf KeyboardPushed(#PB_Key_Pad5): cpu\V(b3)=$0: cpu\touche($0)=1: attend=0;:Break:
        ElseIf KeyboardPushed(#PB_Key_Return): cpu\V(b3)=$1: cpu\touche($1)=1: attend=0;:Break:
        ElseIf KeyboardPushed(#PB_Key_Down): cpu\V(b3)=$2: cpu\touche($2)=1: attend=0;:;Break:
        ElseIf KeyboardPushed(#PB_Key_Minus): cpu\V(b3)=$3: cpu\touche($3)=1: attend=0;:Break:
        ElseIf KeyboardPushed(#PB_Key_Left): cpu\V(b3)=$4: cpu\touche($4)=1: attend=0;:Break:
        ElseIf KeyboardPushed(#PB_Key_Pad4): cpu\V(b3)=$5: cpu\touche($5)=1: attend=0;:Break:
        ElseIf KeyboardPushed(#PB_Key_Right): cpu\V(b3)=$6: cpu\touche($6)=1: attend=0;:Break:
        ElseIf KeyboardPushed(#PB_Key_Pad1): cpu\V(b3)=$7: cpu\touche($7)=1: attend=0;:Break:
        ElseIf KeyboardPushed(#PB_Key_Up): cpu\V(b3)=$8: cpu\touche($8)=1: attend=0;:Break:
        ElseIf KeyboardPushed(#PB_Key_Pad2): cpu\V(b3)=$9: cpu\touche($9)=1: attend=0;:Break:
;         ElseIf KeyboardPushed(#PB_Key_Right):     cpu\V(b3)=$A:     cpu\touche($A)=1:  attend=0:Break:
;         ElseIf KeyboardPushed(#PB_Key_Period):    cpu\V(b3)=$B:     cpu\touche($B)=1:  attend=0:Break:
;         ElseIf KeyboardPushed(#PB_Key_Multiply):  cpu\V(b3)=$C:     cpu\touche($C)=1:  attend=0:Break:
;         ElseIf KeyboardPushed(#PB_Key_Minus):     cpu\V(b3)=$D:     cpu\touche($D)=1:  attend=0:Break:
;         ElseIf KeyboardPushed(#PB_Key_Add):       cpu\V(b3)=$E:     cpu\touche($E)=1:  attend=0:Break:
;         ElseIf KeyboardPushed(#PB_Key_Return):    cpu\V(b3)=$F:     cpu\touche($F)=1:  attend=0:Break:
               
      EndIf
      
    Wend
    ProcedureReturn continuer;
EndProcedure     


Macro VITESSECPU :4 :EndMacro;//nombre d'opérations par tour
Macro FPS :16     :EndMacro ;//Pour le rafraichissement

Procedure pause()

     continuer=1;
	  ExamineKeyboard()
    While(continuer=1)
    If KeyboardPushed(#PB_Key_Escape)  
        continuer=0
     ElseIf KeyboardPushed(#PB_Key_P) 
        continuer=0;
     EndIf  
   Wend
	
    ;Delay(200); //On fait une petite pause pour ne pas prendre le joueur à dépourvu
EndProcedure
Procedure.a chargerJeu(nomJeu.s);(char *nomJeu) ; charger à l'adresse $200=512
jeu$ = OpenFileRequester("Ouvrir un fichier chip8",nomJeu,"*.*",0)

 If ReadFile(0, jeu$)
  ; Si le fichier peut être lu , on continue...
  i=0
  ;zz.a=0
  While Eof(0) = 0  ;Boucle tant que la fin du fichier n'est pas atteinte. (Eof = 'End Of File') 
    
    cpu\memoire(ADRESSEDEBUT+i)=ReadByte(0);ADRESSEDEBUT
    i=i+1; Affiche ligne par ligne le contenu du fichier
   Wend
    CloseFile(0)               ; Ferme le fichier précédemment ouvert
    ProcedureReturn 1
  Else
    MessageRequester("Information","Impossible d'ouvrir le fichier!")
    ProcedureReturn 0
  EndIf 
EndProcedure
Procedure.a listen()

    continuer=1;
	
   ; While(continuer=1)
      ExamineKeyboard()
      
       If KeyboardPushed(#PB_Key_Escape)  
        continuer=0
        attend=0
       ; End 
       
        ElseIf KeyboardPushed(#PB_Key_Pad5)   : cpu\touche($0)=1
        ElseIf KeyboardPushed(#PB_Key_Return) : cpu\touche($1)=1
        ElseIf KeyboardPushed(#PB_Key_Down)   : cpu\touche($2)=1
        ElseIf KeyboardPushed(#PB_Key_Minus)  : cpu\touche($3)=1
        ElseIf KeyboardPushed(#PB_Key_Left)   : cpu\touche($4)=1
        ElseIf KeyboardPushed(#PB_Key_Pad4)   : cpu\touche($5)=1
        ElseIf KeyboardPushed(#PB_Key_Right)  : cpu\touche($6)=1
        ElseIf KeyboardPushed(#PB_Key_Pad1)   : cpu\touche($7)=1
        ElseIf KeyboardPushed(#PB_Key_Up)     : cpu\touche($8)=1
        ElseIf KeyboardPushed(#PB_Key_Pad2)   : cpu\touche($9)=1
;         ElseIf KeyboardPushed(#PB_Key_Right):         cpu\touche($A)= 1 
;         ElseIf KeyboardPushed(#PB_Key_Period):         cpu\touche($B)= 1 
;         ElseIf KeyboardPushed(#PB_Key_Multiply):       cpu\touche($C)= 1 
;         ElseIf KeyboardPushed(#PB_Key_Minus):          cpu\touche($D)= 1 
;         ElseIf KeyboardPushed(#PB_Key_Add):            cpu\touche($E)= 1 
;         ElseIf KeyboardPushed(#PB_Key_Return):        cpu\touche($F)=1  
        ElseIf KeyboardPushed(#PB_Key_P):pause();break
        ElseIf KeyboardPushed(#PB_Key_R):reset();break        
      EndIf
      
                                        
                                     									
; 			        
;        If KeyboardPushed(#PB_Key_Pad5)   : cpu\touche($0)=0
;         ElseIf KeyboardPushed(#PB_Key_Return) : cpu\touche($1)=0
;         ElseIf KeyboardPushed(#PB_Key_Down)   : cpu\touche($2)=0
;         ElseIf KeyboardPushed(#PB_Key_Minus)  : cpu\touche($3)=0
;         ElseIf KeyboardPushed(#PB_Key_Left)   : cpu\touche($4)=0
;         ElseIf KeyboardPushed(#PB_Key_Pad4)   : cpu\touche($5)=0
;         ElseIf KeyboardPushed(#PB_Key_Right)  : cpu\touche($6)=0
;         ElseIf KeyboardPushed(#PB_Key_Pad1)   : cpu\touche($7)=0
;         ElseIf KeyboardPushed(#PB_Key_Up)     : cpu\touche($8)=0
;         ElseIf KeyboardPushed(#PB_Key_Pad2)   : cpu\touche($9)=0
; ;         ElseIf KeyboardPushed(#PB_Key_Right):         cpu\touche($A)=0 
; ;         ElseIf KeyboardPushed(#PB_Key_Period):         cpu\touche($B)=0 
; ;         ElseIf KeyboardPushed(#PB_Key_Multiply):       cpu\touche($C)=0 
; ;         ElseIf KeyboardPushed(#PB_Key_Minus):          cpu\touche($D)=0 
; ;         ElseIf KeyboardPushed(#PB_Key_Add):            cpu\touche($E)=0 
; ;         ElseIf KeyboardPushed(#PB_Key_Return):        cpu\touche($F)=0  
;                 
;       EndIf
     ;Wend
 ProcedureReturn continuer;
EndProcedure

   
 ;#Beep=0
 
    initialiserEcran();
    initialiserPixel();
    effacerEcran()
    ;dessinerPixel()
    initialiserCpu();
    chargerFont();
    ;essaiinitpixel()
        
    continuer=1
    demarrer=0
    compteur=0;

     ;son=LoadSound(#Beep, "beep.wav")
     ;Resultat = PlaySound(#Son [, #PB_Sound_MultiChannel [, Volume 0..100]])
	
;         If son=0
;         MessageRequester("Erreur","Problème avec le son");
;         EndIf

     demarrer=chargerJeu("BREAKOUT.ch8");BC_test.ch8
      If demarrer=1
    
	Repeat
	
	   ;continuer=listen(); // pour les entrées utilisateurs

	   For compteur=0 To VITESSECPU-1
	     If continuer=1;compteur++)  //Si continuer=0, on quitte l'émulateur
	       continuer=interpreterOpcode(recupererOpcode());
	     EndIf
	     
	   Next compteur

; 	   If cpu.compteurSon<>0
; 	   		    ;Mix_PlayChannel(0, son, 0);
; 		    cpu\compteurSon=0;
; 	   EndIf

			updateEcran();
			decompter();
			continuer=listen()
       ;Delay(FPS); //Une pause de 16 ms
		   
	Until continuer<>1;

    EndIf  
End

Re: [TUTO]-Base d'un trés simple Emulateur

Publié : mar. 06/déc./2011 14:03
par Mesa
Emulateur final fonctionnel

Code : Tout sélectionner

; Traduction du code source
; PB 4.51  4.60
; Décembre 2011
; Traducteur Mesa
; Auteur original BestCoder
;
; BUG : 
;
;========================================


Enumeration
  #Imagedacceuil
  #ImageClavier
  
  #ImageGadgetClavier
  
  #Bouton_ROM
   
EndEnumeration

Macro read_mem(adr)
  (Chip8_MEM((adr)&4095))
EndMacro

Macro  write_mem(adr,val)
  Chip8_MEM((adr)&4095)=(val)
EndMacro


Structure Chip8Context 
	Array  reg_V.a(16);    // Les 16 registres; Essayer avec .c
    reg_PC.u;       // le registre PC
    reg_SP.u;       // le registre SP
    reg_I.u;        // le registre I
    delay.a
    sound.a;  // pour la gestion des timers delais et son
  EndStructure

  Global Chip8CPU.Chip8Context;
  Global Chip8_Exec.l;  // Pour gérer si le cpu fonctionne ou pas
  Global Chip8_Iperiod.a;        // Pour la gestion des cycles d'horloge
  Global Dim Chip8_Tches.a(16);      // Les 16 touches disponiblesdu Chip 8
  Global Dim Chip8_Display.a(64,32); // Les pixels de l'ecran
  Global Dim  Chip8_MEM.a(4096);      // Les 4Ko disponibles et les macros d'acces à la mémoire
  Global Dim Chip8_PILE.u(16);      // Les 16 emplacements de la pile
  
  Global OpCode_1.a
  Global OpCode_2.a
  Global OpCode_3.a
  Global OpCode_4.a;// Pour gérer la lecture des opcodes


  Global false=0
  Global true=1
  Global tpsParCycle
  Global largeur,hauteur
     
  
    Chip8_Exec=false;
    Chip8_Iperiod = 15; // Vitesse du jeu : nbre de cycles exec par écran
    
    largeur=640 ;ecran
    hauteur=480
    
   UsePNGImageDecoder()
 

;//*****************************************************************************
Declare Chip8InitFont()
Procedure Reset()

  ;// Init des registres, de la mémoire graphique et des touches
  For i=0 To 15
    Chip8CPU\reg_V(i)=0; 
    Chip8_Tches(i)=0
  Next i
    Chip8CPU\reg_SP = $F; // Init du PC et du SP
    Chip8CPU\reg_PC = $200;
    Chip8CPU\reg_I=0        
    Chip8CPU\delay=0
    Chip8CPU\sound=0 
    For j=0 To 31
      For i=0 To 63
        Chip8_Display.a(i,j)=0
      Next i
    Next j
   
  Chip8InitFont();

  Chip8_Exec=true; // démarrage du CPU
EndProcedure
Declare majEcran();(pChip8_Display)
Declare CheckClavier();(pChip8_Tches)
Declare Chip8GetOpcode()
Declare ExecuteOpcode()
Procedure Chip8Execute()
    For i = 0 To Chip8_Iperiod-1
        Chip8GetOpcode();
        ExecuteOpcode();
   Next i

    If (Chip8CPU\delay<>0): Chip8CPU\delay=Chip8CPU\delay-1:EndIf;
    If (Chip8CPU\sound<>0): Chip8CPU\sound=Chip8CPU\sound-1:EndIf;

    majEcran();(Chip8_Display); //met à jour l'écran avec SDL
    CheckClavier();(Chip8_Tches); //vérifie quelles touches sont appuyées
EndProcedure
Procedure Chip8GetOpcode() 
   OpCode_1 =  (read_mem(Chip8CPU\reg_PC    )  & $F0) >> 4;
   OpCode_2 =  read_mem(Chip8CPU\reg_PC    )  & $0F;
   OpCode_3 =  (read_mem(Chip8CPU\reg_PC + 1)  & $F0) >> 4;
   OpCode_4 =  read_mem(Chip8CPU\reg_PC + 1)  & $0F;

   Chip8CPU\reg_PC= Chip8CPU\reg_PC+2;
EndProcedure
Procedure ExecuteOpcode()
   ;// Opcode // Instruction assembleur // Description

;      i; //pour les boucles
;     uNbre.a; //variable tmp
;     Lig, Col;
;     gfxVal.a
;     xVal.a
;     yVal.a;

    Select OpCode_1 
      Case $0;
        Select OpCode_3
          Case $E;
            Select OpCode_4
              Case $0; // 00E0 // Cls //Efface l’écran

                For Lig = 0 To 31            ;// Pour toutes les lignes
                  For Col = 0 To 63          ;// Pour toutes les colonnes
                     Chip8_Display(Col,Lig) = 0;Chip8_Display(Lig , Col) = 0;       // Enlève les pixels
                  Next Col
                Next Lig
               

              Case $E; // 00EE // Rts //Fin de sous programme (donc retour au programme appelant)
                Chip8CPU\reg_SP=Chip8CPU\reg_SP+1;                             // Prend la précédente adresse de pile
                Chip8CPU\reg_PC = Chip8_PILE(Chip8CPU\reg_SP); // Recupere l'adresse du programme appelant
             EndSelect
         EndSelect
         
      Case $1; // 1xxx //Jump xxx // Saute à l’adresse xxx
        Chip8CPU\reg_PC = (OpCode_2 * 256) + (OpCode_3 *16) + OpCode_4; // PC = contenu des 3 opcodes (les xxx)
      
      Case $2; // 2xxx //Jsr xxx // Appel le sous-programme à l’adresse xxx // 16 appels maximum (cf la pile plus haut)
        Chip8_PILE(Chip8CPU\reg_SP) = Chip8CPU\reg_PC; // Sauvegarde l'adresse actuelle sur la pile
        Chip8CPU\reg_SP=Chip8CPU\reg_SP-1;                             // Une adresse de moins de disponible
        Chip8CPU\reg_PC = (OpCode_2 * 256) + (OpCode_3 *16) + OpCode_4; // PC = contenu des 3 opcodes (les xxx)
      
      Case $3; // 3rxx //Skeq vr,xx // Ne fait pas l’instruction suivante si registre vr = xx
        If Chip8CPU\reg_V(OpCode_2) = (OpCode_3 *16) + OpCode_4;    ;// Si condition remplie VX = xx
          Chip8CPU\reg_PC =Chip8CPU\reg_PC+ 2;                                         // On saute l'instruction suivante
        EndIf
      Case $4; // 4rxx //Skne vr,xx // Ne fait pas l’instruction suivante si registre vr <>xx
        If (Chip8CPU\reg_V(OpCode_2) <> ((OpCode_3 *16) + OpCode_4))    ;// Si condition remplie VX <> xx
          Chip8CPU\reg_PC =Chip8CPU\reg_PC + 2;                                         // On saute l'instruction suivante
        EndIf
      Case $5; // 5ry0 //Skeq vr,vy // Ne fait pas l’instruction suivante si registre vr = vy
        If (Chip8CPU\reg_V(OpCode_2) = Chip8CPU\reg_V(OpCode_3))       ;// Si condition remplie VX = VY
          Chip8CPU\reg_PC = Chip8CPU\reg_PC+2;                                         // On saute l'instruction suivante
        EndIf
      Case $6; // 6rxx // Mov vr,xx // Met xx dans vr
        Chip8CPU\reg_V(OpCode_2) = (OpCode_3 * 16) + OpCode_4;
      
      Case $7; // 7rxx // Add vr,xx // Ajoute xx à vr
        Chip8CPU\reg_V(OpCode_2) = Chip8CPU\reg_V(OpCode_2)+(OpCode_3 * 16) + OpCode_4;
      
      Case $8;
        Select OpCode_4
          Case $0; // 8ry0 // Mov vr,vy // Met le registre vy dans vr
            Chip8CPU\reg_V(OpCode_2) = Chip8CPU\reg_V(OpCode_3);
          
          Case $1; //8ry1  	Or vr,vy  	Fait l’opération OU avec vr et vy dans vr
            Chip8CPU\reg_V(OpCode_2) = Chip8CPU\reg_V(OpCode_2)|Chip8CPU\reg_V(OpCode_3);
          
          Case $2; // 8ry2  	And vr,vy  	Fait l’opération ET avec vr et vy dans vr
            Chip8CPU\reg_V(OpCode_2) =Chip8CPU\reg_V(OpCode_2)& Chip8CPU\reg_V(OpCode_3);
          
          Case $3; //8ry3  	XOr vr,vy  	Fait l’opération OU EXCLUSIF avec vr et vy dans vr
            Chip8CPU\reg_V(OpCode_2) =Chip8CPU\reg_V(OpCode_2)! Chip8CPU\reg_V(OpCode_3);
          
          Case $4; //8ry4    Add vr,vy  	Ajoute vy dans vr   Carry dans vf
            If(Chip8CPU\reg_V(OpCode_2) And Chip8CPU\reg_V(OpCode_3))   ;// Si condition (VX et VY <>0)
                Chip8CPU\reg_V($F) = 1;                                // Stocke la carry
            Else
              Chip8CPU\reg_V($F) = 0;
            EndIf
              Chip8CPU\reg_V(OpCode_2) =Chip8CPU\reg_V(OpCode_2)+ Chip8CPU\reg_V(OpCode_3);        // VX += VY
          
          Case $5; //8ry5  	Sub vr,vy  	Fait l’opération vr = vr –vy  	Carry dans vf si résultat <0
            If(Chip8CPU\reg_V(OpCode_2) > Chip8CPU\reg_V(OpCode_3))         ;// Si condition (VX > VY)
              Chip8CPU\reg_V($F) = 1;                                      // Stocke la carry
           Else
              Chip8CPU\reg_V($F) = 0;
            EndIf
           Chip8CPU\reg_V(OpCode_2) =Chip8CPU\reg_V(OpCode_2)- Chip8CPU\reg_V(OpCode_3);           // VX -= VY
          
          Case $6; //8r06  	Shr vr  	Décalage arithmétique à droite de vr
                                          ;//Equivaut donc à vr = vr /2\ Le bit 0 va dans vf
            If (Chip8CPU\reg_V(OpCode_2) & 1)                               ;// Si condition (VX & 1)
              Chip8CPU\reg_V($F) = 1;                                      // Stocke la carry
           Else
              Chip8CPU\reg_V($F) = 0;
          EndIf
           Chip8CPU\reg_V(OpCode_2) =  Chip8CPU\reg_V(OpCode_2)>>1;
          
          Case $7; //8ry7  	Rsb vr,vy  	Fait l’opération vr = vy – vr  	Carry dans vf si résultat < 0
            If(Chip8CPU\reg_V(OpCode_3) > Chip8CPU\reg_V(OpCode_2))        ; // Si condition (VY > VX
              Chip8CPU\reg_V($F) = 1;                                      // Stocke la carry
           Else
              Chip8CPU\reg_V($F) = 0;
            EndIf
           Chip8CPU\reg_V(OpCode_2) = Chip8CPU\reg_V(OpCode_3) - Chip8CPU\reg_V(OpCode_2);           // VX = VY - VX
          
          Case $E; //8r0E  	Shl vr  	Décalage arithmétique à gauche de vr
                                          ;//Equivaut donc à vr = vr * 2\ Le bit 7 va dans vf
           If (Chip8CPU\reg_V(OpCode_2) & 128)  ;// Si condition (VX & 128)
              Chip8CPU\reg_V($F) = 1;          // Stocke la carry
           Else
              Chip8CPU\reg_V($F) = 0;
           EndIf
           Chip8CPU\reg_V(OpCode_2) =Chip8CPU\reg_V(OpCode_2)<< 1;
          
        EndSelect
      Case $9; // 9ry0  	Skne vr,vy  	Ne fait pas l’instruction suivante si registre vr <>vy
        If (Chip8CPU\reg_V(OpCode_2) <> Chip8CPU\reg_V(OpCode_3))  ;// Si condition (VX <> VY)
            Chip8CPU\reg_PC = Chip8CPU\reg_PC+2;                                  // On saute l'instruction suivante
        EndIf
      Case $A; //Axxx  	Mvi xxx  	Met xxx dans le registre I
        Chip8CPU\reg_I = (OpCode_2 * 256) + (OpCode_3 *16) + OpCode_4; // I = xxx
      
      Case $B; //Bxxx  	Jmi xxx  	Va à l’adresse v0 + xxx
        Chip8CPU\reg_PC = Chip8CPU\reg_V(0) + (OpCode_2 * 256) + (OpCode_3 *16) + OpCode_4; // PC = V0 + xxx
      
      Case $C; //Crxx  	Rand vr,xx  	Met un nombre aléatoire entre 0 et xx dans vr
        Chip8CPU\reg_V(OpCode_2) = (Random((OpCode_3 *16) + OpCode_4));(Random(32767)%256) & ((OpCode_3 *16) + OpCode_4); // VX = random (0\\xx)
       
      Case $D; //Drys  	Sprite vr,vy,s  	Affiche le sprite en position vr,vy , de hauteur s

       ;// Pas de collision pour l'instant
       Chip8CPU\reg_V($F) = 0;

       For Lig = 0 To OpCode_4-1          ;// Pour toutes les lignes possibles (1\\15)
        gfxVal = read_mem(Chip8CPU\reg_I + Lig);     // Recup des 8 pixels de la ligne
        yVal = (Chip8CPU\reg_V(OpCode_3) + Lig);     // Recup la coordonnee Y d'affichage (opcode3 + 1\\15

          For Col = 0 To 7             ;// Pour les 8 pixels de la ligne
             xVal = (Chip8CPU\reg_V(OpCode_2) + Col); // Recup la coordonnee X d'affichage (opcode2 + 1\\8)
             
             If ((gfxVal & ($80 >> Col)) <> 0)      ;// Si le pixel est de couleur (donc <>0)
               
               If (Chip8_Display(((xVal % 64) ) , (yVal % 32)) = 1);(Chip8_Display(((yVal % 32) ) , (xVal % 64)) = 1)   ;//  Si il y a dejà un pixel a cet endroit
                   Chip8CPU\reg_V($F) = 1;           //   Stocke la collision de pixel
                EndIf
                Chip8_Display(((xVal % 64) ) , (yVal % 32)) = Chip8_Display(((xVal % 64) ) , (yVal % 32))! 1; ;Chip8_Display(((yVal % 32) ) , (xVal % 64)) = Chip8_Display(((yVal % 32) ) , (xVal % 64))! 1;    //  Allume ou eteind le pixel en conséquence
             EndIf
          Next Col
       Next Lig

      
      Case $E;
        Select OpCode_3
          Case $9; //Ek9E    Skpr k  	Ne fait pas l’instruction suivant sur la touche k (registre rk) est enfoncée K est un nombre
            If (Chip8_Tches(Chip8CPU\reg_V(OpCode_2)) = 1)  ;// Si touche enfoncee
              Chip8CPU\reg_PC =Chip8CPU\reg_PC+ 2;                          // On saute l'instruction suivante
            EndIf
          Case $A; //EkA1    Skup k  	Ne fait pas l’instruction suivant sur la touche k (registre rk) n’est pas enfoncée
            If (Chip8_Tches(Chip8CPU\reg_V(OpCode_2)) = 0)  ;// Si touche relachee
              Chip8CPU\reg_PC =Chip8CPU\reg_PC+ 2;                          // On saute l'instruction suivante
            EndIf
        EndSelect
        
      Case $F;
        Select OpCode_3
          Case $0;
            Select OpCode_4
              Case $7; //Fr07  	Gdelay vr  	Met le contenu du timer de délais dans vr
                Chip8CPU\reg_V(OpCode_2) = Chip8CPU\delay; // VX = timers delais
              
              Case $A; //Fr0A  	Key vr  	Attend qu’une touche soit appuyée et met cette touche dans vr
                  KeyPressed = false;
                  For i = 0 To 15;   // Regarde si une des 16 touches est pressée
                    If (Chip8_Tches(i) = 1) 
                      Chip8CPU\reg_V(OpCode_2) = i;     // Si Oui, Vx = Touche
                      KeyPressed = true;
                    EndIf
                  Next i

                  If  Not KeyPressed  
                       Chip8CPU\reg_PC =Chip8CPU\reg_PC- 2;  // Si Non, on reste sur la même instruction
                  EndIf
              EndSelect
            Case $1;
            Select OpCode_4
              Case $5; //Fr15  	Sdelay vr  	Met vr dans le timer de delais
                Chip8CPU\delay = Chip8CPU\reg_V(OpCode_2); // timers delais = VX
              
              Case $8; //Fr18  	Ssound vr  	Met le contenu du timer de son dans vr
                Chip8CPU\sound = Chip8CPU\reg_V(OpCode_2); // timers sound = VX
              
              Case $E; //Fr1E  	Adi vr  	Ajoute le registre vr au registre I
                Chip8CPU\reg_I =Chip8CPU\reg_I+ Chip8CPU\reg_V(OpCode_2); // I += VX
              
            EndSelect
          Case $2; //Fr29  	Font vr  	Pointe I vers le sprite de caractère hexadécimal contenu dans vr
                                            ;//Les sprites sont de 5 pixels de haut
            Chip8CPU\reg_I = $0000+(Chip8CPU\reg_V(OpCode_2) * $5);  // I = Carac num VX
          
          Case $3; //Fr33  	Bcd vr  	Stock la représentation BCD (Binaire codé décimal) du registre vr à
                                            ;//l’adresse  I, I+1 et I+2  	Ne change pas I
              uNbre = Chip8CPU\reg_V(OpCode_2);
           For i = 3 To 1 Step -1;   { //décimal->I+2, puis dizaine->I+1, puis centaine->I+0
              write_mem(Chip8CPU\reg_I + (i - 1),uNbre % 10);        // (I\\I+2) = BCD(VX)
              ;Chip8_MEM((Chip8CPU\reg_I + (i - 1))&4095)=(uNbre % 10)
              uNbre =uNbre/ 10;
           Next i
          
          Case $5; //Fr55  Str v0-vr  	Stock les registres v0-vr à l’adresse pointée par I
                                        ;//I est incrémenté pour arriver à l’adresse suivante, donc à r+1
           For i = 0 To OpCode_2
              write_mem(Chip8CPU\reg_I + i, Chip8CPU\reg_V(i));
           Next i
          
          Case $6; //Fr65  	Ldr v0-vr  	Stock dans les registres v0-vr le contenu pointé par I
                                            ;//I est incrémenté pour arriver à l’adresse suivante, donc à r+1
           For i = 0 To OpCode_2; 
              Chip8CPU\reg_V(i) = read_mem(Chip8CPU\reg_I + i);
           Next i
          
        EndSelect
        
      Default;  // Code illegal
        MessageRequester("ERREUR","ERREUR Opcode non reconnu !",#PB_MessageRequester_Ok );
        
    EndSelect ;// switch (OpCode_1)


  EndProcedure
Procedure.l getChip8_Exec() ;const{
    ProcedureReturn Chip8_Exec;
EndProcedure
Procedure setChip8_Exec(Val.l);(bool val){
    Chip8_Exec=val;
EndProcedure
Procedure.l LoadRom(fileName.s)
  ;jeu$ = OpenFileRequester("Ouvrir un fichier chip8",nomJeu,"*.*",0)

 If ReadFile(0, fileName)
  ; Si le fichier peut être lu , on continue...
  i=0
  
  While Eof(0) = 0  ;Boucle tant que la fin du fichier n'est pas atteinte. (Eof = 'End Of File') 
    Chip8_MEM($200+i)=ReadByte(0);ADRESSEDEBUT
    
     i=i+1; Affiche ligne par ligne le contenu du fichier
     
    Wend
    CloseFile(0)               ; Ferme le fichier précédemment ouvert
    ProcedureReturn true
  Else
    MessageRequester("Information","Impossible d'ouvrir le fichier!")
    ProcedureReturn false
  EndIf
  
  EndProcedure
Procedure Chip8InitFont() 

   Chip8_MEM($0000) = 96;     //.11.....
   Chip8_MEM($0001) = 144;    //1..1....
   Chip8_MEM($0002) = 144;    //1..1....
   Chip8_MEM($0003) = 144;    //1..1....
   Chip8_MEM($0004) = 96;     //.11.....

   Chip8_MEM($0005) = 96;     //.11.....
   Chip8_MEM($0006) = 224;    //111.....
   Chip8_MEM($0007) = 96;     //.11.....
   Chip8_MEM($0008) = 96;     //.11.....
   Chip8_MEM($0009) = 240;    //1111....

   Chip8_MEM($000A) = 96;     //.11.....
   Chip8_MEM($000B) = 144;    //1..1....
   Chip8_MEM($000C) = 32;     //..1.....
   Chip8_MEM($000D) = 64;     //.1......
   Chip8_MEM($000E) = 240;    //1111....

   Chip8_MEM($000F) = 224;    //111.....
   Chip8_MEM($0010) = 16;     //...1....
   Chip8_MEM($0011) = 96;     //.11.....
   Chip8_MEM($0012) = 16;     //...1....
   Chip8_MEM($0013) = 224;    //111.....

   Chip8_MEM($0014) = 160;    //1.1.....
   Chip8_MEM($0015) = 160;    //1.1.....
   Chip8_MEM($0016) = 240;    //1111....
   Chip8_MEM($0017) = 32;     //..1.....
   Chip8_MEM($0018) = 32;     //..1.....

   Chip8_MEM($0019) = 240;    //1111....
   Chip8_MEM($001A) = 128;    //1.......
   Chip8_MEM($001B) = 240;    //1111....
   Chip8_MEM($001C) = 16;     //...1....
   Chip8_MEM($001D) = 240;    //1111....

   Chip8_MEM($001E) = 240;    //1111....
   Chip8_MEM($001F) = 128;    //1.......
   Chip8_MEM($0020) = 240;    //1111....
   Chip8_MEM($0021) = 144;    //1..1....
   Chip8_MEM($0022) = 240;    //1111....

   Chip8_MEM($0023) = 240;    //1111....
   Chip8_MEM($0024) = 16;     //...1....
   Chip8_MEM($0025) = 32;     //..1.....
   Chip8_MEM($0026) = 64;     //.1......
   Chip8_MEM($0027) = 128;    //1.......

   Chip8_MEM($0028) = 96;     //.11.....
   Chip8_MEM($0029) = 144;    //1..1....
   Chip8_MEM($002A) = 96;     //.11.....
   Chip8_MEM($002B) = 144;    //1..1....
   Chip8_MEM($002C) = 96;     //.11.....

   Chip8_MEM($002D) = 240;    //1111....
   Chip8_MEM($002E) = 144;    //1..1....
   Chip8_MEM($002F) = 240;    //1111....
   Chip8_MEM($0030) = 16;     //...1....
   Chip8_MEM($0031) = 240;    //1111....

   Chip8_MEM($0032) = 96;     //.11.....
   Chip8_MEM($0033) = 144;    //1..1....
   Chip8_MEM($0034) = 240;    //1111....
   Chip8_MEM($0035) = 144;    //1..1....
   Chip8_MEM($0036) = 144;    //1..1....

   Chip8_MEM($0037) = 224;    //111.....
   Chip8_MEM($0038) = 144;    //1..1....
   Chip8_MEM($0039) = 224;    //111.....
   Chip8_MEM($003A) = 144;    //1..1....
   Chip8_MEM($003B) = 224;    //111.....

   Chip8_MEM($003C) = 112;    //.111....
   Chip8_MEM($003D) = 128;    //1.......
   Chip8_MEM($003E) = 128;    //1.......
   Chip8_MEM($003F) = 128;    //1.......
   Chip8_MEM($0040) = 112;    //.111....

   Chip8_MEM($0041) = 224;    //111.....
   Chip8_MEM($0042) = 144;    //1..1....
   Chip8_MEM($0043) = 144;    //1..1....
   Chip8_MEM($0044) = 144;    //1..1....
   Chip8_MEM($0045) = 224;    //111.....

   Chip8_MEM($0046) = 240;    //1111....
   Chip8_MEM($0047) = 128;    //1.......
   Chip8_MEM($0048) = 224;    //111.....
   Chip8_MEM($0049) = 128;    //1.......
   Chip8_MEM($004A) = 240;    //1111....

   Chip8_MEM($004B) = 240;    //1111....
   Chip8_MEM($004C) = 128;    //1.......
   Chip8_MEM($004D) = 224;    //111.....
   Chip8_MEM($004E) = 128;    //1.......
   Chip8_MEM($004F) = 128;    //1.......
 EndProcedure
Procedure refresh()
    StartDrawing(ScreenOutput())
    Box(0, 0, largeur,hauteur, RGB(0,0,0))
    StopDrawing()
    FlipBuffers()
    
    
EndProcedure
Procedure majEcran()
StartDrawing(ScreenOutput())
     Box(0, 0, largeur,hauteur, RGB(0,0,0)); // Effacement de l'écran

    For i=0 To 31 ;i++){
        For j=0 To 63 ;j++){63 ??????????????
            If Chip8_Display(j , i)=1 ;){ //si c'est un pixel blanc
                position_x = j*(largeur/64); //on détermine les coodonnées de la surface à poser
                position_y = i*(hauteur/32);
                
                Box(position_x,position_y ,largeur/64,hauteur/32,RGB(255,255,255))
            EndIf
        Next j
      Next i
      StopDrawing()
    FlipBuffers(); // Mise à jour de l'écran
EndProcedure
Procedure CheckClavier();unsigned char *Chip8_Tches
   
    For i=0 To 15 ;
        Chip8_Tches(i)=0;
    Next i

    tempsPrecedent =  ElapsedMilliseconds()
    tempsActuel = ElapsedMilliseconds()

    While (tempsActuel - tempsPrecedent <= tpsParCycle) ;//Si 40 ms se sont écoulées depuis le dernier tour de boucle
    
        tempsActuel = ElapsedMilliseconds()
        ExamineKeyboard(); 
        
        If KeyboardPushed(#PB_Key_Up):          Chip8_Tches(8)=1; break;
        ElseIf KeyboardPushed(#PB_Key_Down):   Chip8_Tches(2)=1; break;
        ElseIf KeyboardPushed(#PB_Key_Right):  Chip8_Tches(6)=1; break;
        ElseIf KeyboardPushed(#PB_Key_Left):   Chip8_Tches(4)=1; break;
        ElseIf KeyboardPushed(#PB_Key_Pad4):      Chip8_Tches(5)=1; break; //A;KP1
        ElseIf KeyboardPushed(#PB_Key_Pad5):      Chip8_Tches(0)=1; break; //B;KP2
        ElseIf KeyboardPushed(#PB_Key_Pad1):      Chip8_Tches(7)=1; break; //X;KP4
        ElseIf KeyboardPushed(#PB_Key_Pad2):      Chip8_Tches(9)=1; break; //Y;KP5
        ElseIf KeyboardPushed(#PB_Key_Return): Chip8_Tches(1)=1; break; //start
        ElseIf KeyboardPushed(#PB_Key_Minus):  Chip8_Tches(3)=1; break; //select
          
          ElseIf KeyboardPushed(#PB_Key_Escape):  End; break; //Echap = FIN
                
        EndIf
    Wend
EndProcedure
Procedure.l getTpsParCycle() 
    ProcedureReturn tpsParCycle;
EndProcedure
Procedure setTpsParCycle(tps.l)
    tpsParCycle=tps;
EndProcedure
Procedure ouvrir_rom()


fileName.s=OpenFileRequester("Ouvrir une ROM", "BREAKOUT.ch8", "*.ch8", 0)


    If LoadRom(fileName) = false
        MessageRequester( "Fichier", "Le fichier que vous avez sélectionné n'a pas pu être chargé",#PB_MessageRequester_Ok );
        romOuverte = false;
    Else
        ;// Permet de mettre le nom du fichier ouvert dans le titre
         nom_jeu.s = GetFilePart(fileName)
         SetWindowTitle(0,"PureBasic Emulateur Chip8 - " + nom_jeu);
         romOuverte = true;
    EndIf
    ;refreshT->start(); le thread est demarrer
EndProcedure

;{ ToDo
; Procedure.l getIperiod()
;     tmp = Chip8_Iperiod;
;     ProcedureReturn tmp;
; EndProcedure
; Procedure setIperiod(period)
;     tmp.a = period;           Transtypage ?
;     Chip8_Iperiod = tmp;
; EndProcedure
; Procedure redemarrer()
; 
;     If getChip8_Exec();){ //si le cpu est en train de tourner
;       setChip8_Exec(false);
;       EndIf
; ;         While(!chip8t->isFinished()){ //on attend que le thread se termine
; ;         }
; ;         chip8t->start(QThread::HighestPriority); //on relance le thread chip8
; ;     }
; EndProcedure
; Procedure arreter()
; 
;     setChip8_Exec(false); //on arrête le thread chip8
;     refresh(); //pour effacer l'écran et remettre l'image d'accueil
; EndProcedure
; Procedure resolutionx1()
; 
; ;     If(actionResolutionx1->isChecked()){ //décoché vers coché
; ;         bool estcequeleCPUtournait = false;
; ;         If(chip8->getChip8_Exec()){ //si le cpu est en train de tourner
; ;             chip8->setChip8_Exec(false);
; ;             While(!chip8t->isFinished()){ //on attend que le thread se termine
; ;             }
; ;             estcequeleCPUtournait = true;
; ;         }
; ;         delete sdlw;
; ;         sdlw = new QSDLScreenWidget(320,224,this);
; ;         setCentralWidget(sdlw); //on place le widget SDL dans la fenêtre Qt
; ;         setFixedSize(320,224+TAILLE_MENUBAR);
; ;         If(estcequeleCPUtournait){
; ;             chip8t->start(QThread::HighestPriority); //on relance le thread chip8
; ;         }Else{
; ;             sdlw->refresh();
; ;         }
; ;         actionResolutionx2->setChecked(false);
; ;         actionResolutionx3->setChecked(false);
; ;     }Else{ //coché vers décoché
; ;         actionResolutionx1->setChecked(true);
; ;     }
; EndProcedure
; Procedure resolutionx2()
; ; 
; ;     If(actionResolutionx2->isChecked()){ //décoché vers coché
; ;         bool estcequeleCPUtournait = false;
; ;         If(chip8->getChip8_Exec()){ //si le cpu est en train de tourner
; ;             chip8->setChip8_Exec(false);
; ;             While(!chip8t->isFinished()){ //on attend que le thread se termine
; ;             }
; ;             estcequeleCPUtournait = true;
; ;         }
; ;         delete sdlw;
; ;         sdlw = new QSDLScreenWidget(640,480,this);
; ;         setCentralWidget(sdlw); //on place le widget SDL dans la fenêtre Qt
; ;         setFixedSize(640,480+TAILLE_MENUBAR);
; ; 
; ;         If(estcequeleCPUtournait){
; ;             chip8t->start(QThread::HighestPriority); //on relance le thread chip8
; ;         }Else{
; ;             sdlw->refresh();
; ;         }
; ;         actionResolutionx1->setChecked(false);
; ;         actionResolutionx3->setChecked(false);
; ;     }Else{ //coché vers décoché
; ;         actionResolutionx2->setChecked(true);
; ;     }
; EndProcedure
; Procedure resolutionx3()

;     If(actionResolutionx3->isChecked()){ //décoché vers coché
;         bool estcequeleCPUtournait = false;
;         If(chip8->getChip8_Exec()){ //si le cpu est en train de tourner
;             chip8->setChip8_Exec(false);
;             While(!chip8t->isFinished()){ //on attend que le thread se termine
;             }
;             estcequeleCPUtournait = true;
;         }
;         delete sdlw;
;         sdlw = new QSDLScreenWidget(768,576,this);
;         setCentralWidget(sdlw); //on place le widget SDL dans la fenêtre Qt
;         setFixedSize(768,576+TAILLE_MENUBAR);
; 
;         If(estcequeleCPUtournait){
;             chip8t->start(QThread::HighestPriority); //on relance le thread chip8
;         }Else{
;             sdlw->refresh();
;         }
;         actionResolutionx1->setChecked(false);
;         actionResolutionx2->setChecked(false);
;     }Else{ //coché vers décoché
;         actionResolutionx3->setChecked(true);
;     }
;EndProcedure
;}


  If InitSprite()=0 Or InitKeyboard()=0 Or InitMouse()=0 Or InitSound()=0
     MessageRequester("Erreur", "Erreur DirectX", #PB_MessageRequester_Ok)
     End
   EndIf
   
  
   chemin.s=GetCurrentDirectory()
   
   OpenWindow(0, 100, 0, 750, 800, "PureBasic - Emulateur Chip8", #PB_Window_SystemMenu|#PB_Window_ScreenCentered);#PB_Window_SizeGadget)
      OpenWindowedScreen(WindowID(0),0,0,largeur,hauteur,0,0,0, #PB_Screen_SmartSynchronization);OpenWindowedScreen(FenetreID, x, y, Largeur, Hauteur, RedimensionnementAuto, OffsetDroit, OffsetBas [, FlipMode])
      StartDrawing(ScreenOutput())
        DrawingMode(#PB_2DDrawing_Default )
                
         LoadImage(#Imagedacceuil, chemin+"images\home.bmp") 
        DrawImage(ImageID(#Imagedacceuil), 0, 0 ) 
      StopDrawing()  
    ;FlipBuffers()
    tpsParCycle=40;

;//************************************ TODO *****************************************
;
;Menu
;Fichier/actionOuvrir_rom,
;Demarrer/actionExecuter, actionRedemarrer, actionArreter (F1,F2,F3)
;Configuer/Resolution/actionResolutionx1, actionResolutionx2,actionResolutionx3,
;         /actionCpu
;?/actionA_propos, actionclavier
;//**********************************************************************************

;//************************************ GO ! ****************************************
;largeur=640
;hauteur=480
     romOuverte = false;
   
     ButtonGadget(#Bouton_ROM , largeur+30, 10, 50, 50, "ROM")
     LoadImage(#ImageClavier, chemin+"images\clavier.png")
     ImageGadget(#ImageGadgetClavier, 0, hauteur+20, 650, 250, ImageID(#ImageClavier)) 
     
    ;refresh();CLS
    ;Reset();Init et chip8_exec=true
    ;ouvrir_rom(); rom_ouverte=true
    
    chip8_exec=true

   While(getChip8_Exec())
     Repeat 
       Chip8Execute();execution de l'emulation
       
      EventID = WindowEvent()
      If EventID = #PB_Event_CloseWindow:Chip8_Exec=false:EndIf ; fermer la fenetre
      If EventID = #PB_Event_Gadget ; Gestion du bouton "ROM",ouvre la rom et lance l'emulation
        Select EventGadget()
          Case #Bouton_ROM 
            romOuverte = false
            Chip8_Exec=false
            refresh();CLS
            Reset();Init et chip8_exec=true
            ouvrir_rom(); rom_ouverte=true
           ;Case  : Debug ""
         EndSelect

      EndIf 
     Until Chip8_Exec=false
   Wend
   
  End   
    
    
;ok

Re: [TUTO]-Base d'un trés simple Emulateur

Publié : mar. 06/déc./2011 15:47
par Mesa
Un Tuto de flaith sur l'émulation se trouve ici
http://www.purebasic.fr/french/viewtopi ... 21&t=11330

Mesa.

Re: [TUTO]-Base d'un trés simple Emulateur

Publié : mar. 06/déc./2011 16:12
par Ar-S
Pas le temps mais bravo pour le boulot et le tuto. Je ne manquerai pas de suivre ce topic. :D