Browse Source

TI-NESulator SVN reimport

godzil 16 years ago
commit
3f4b4990ea
67 changed files with 19026 additions and 0 deletions
  1. 105 0
      doc/Changements.txt
  2. 127 0
      doc/Changes.txt
  3. 233 0
      doc/LisezMoi.txt
  4. 7 0
      doc/TODO.txt
  5. 112 0
      src/NESCarts.c
  6. 418 0
      src/apu/SndAlleg.c
  7. 554 0
      src/apu/SndUnixT.c
  8. 497 0
      src/apu/Sound.c
  9. 837 0
      src/corecpu/Codes.h
  10. 480 0
      src/corecpu/Debug.c
  11. 317 0
      src/corecpu/M6502.c
  12. 148 0
      src/corecpu/M6502.h
  13. 71 0
      src/corecpu/Tables.h
  14. 1038 0
      src/include/MIDIFreq.h
  15. 39 0
      src/include/NESCarts.h
  16. 232 0
      src/include/Sound.h
  17. 60 0
      src/include/mappers/manager.h
  18. 68 0
      src/include/memory/manager.h
  19. 36 0
      src/include/paddle.h
  20. 261 0
      src/include/palette.h
  21. 46 0
      src/include/plugins/manager.h
  22. 24 0
      src/include/ppu/ppu.debug.h
  23. 68 0
      src/include/ppu/ppu.h
  24. 32 0
      src/include/ppu/ppu.memory.h
  25. 28 0
      src/include/types.h
  26. 1232 0
      src/main.c
  27. 62 0
      src/mappersmanager/manager.c
  28. 361 0
      src/mappersmanager/mappers/mmc1.c
  29. 16 0
      src/mappersmanager/mappers/mmc1.h
  30. 48 0
      src/mappersmanager/mappers/norom.c
  31. 16 0
      src/mappersmanager/mappers/norom.h
  32. 63 0
      src/mappersmanager/mappers/temp/aorom.c
  33. 47 0
      src/mappersmanager/mappers/temp/cnrom.c
  34. 35 0
      src/mappersmanager/mappers/temp/genericmapper.c
  35. 125 0
      src/mappersmanager/mappers/temp/iremh3001.c
  36. 317 0
      src/mappersmanager/mappers/temp/mmc3.c
  37. 137 0
      src/mappersmanager/mappers/temp/mmc4.c
  38. 51 0
      src/mappersmanager/mappers/temp/unrom.c
  39. 20 0
      src/mappersmanager/mappers_list.h
  40. 63 0
      src/mappersmanager/unused/aorom.h
  41. 47 0
      src/mappersmanager/unused/cnrom.h
  42. 35 0
      src/mappersmanager/unused/genericmapper.h
  43. 125 0
      src/mappersmanager/unused/iremh3001.h
  44. 355 0
      src/mappersmanager/unused/mmc1.h
  45. 317 0
      src/mappersmanager/unused/mmc3.h
  46. 137 0
      src/mappersmanager/unused/mmc4.h
  47. 51 0
      src/mappersmanager/unused/unrom.h
  48. 118 0
      src/mappersmanager/utils.c
  49. 275 0
      src/memorymanager/memory.c
  50. 31 0
      src/os/unix/loadfile.c
  51. 121 0
      src/paddle.c
  52. 136 0
      src/pluginsmanager/manager.c
  53. 802 0
      src/pluginsmanager/plugins/gamegenie.c
  54. 2 0
      src/pluginsmanager/plugins/gamegenie.h
  55. 10 0
      src/pluginsmanager/plugins_list.h
  56. 348 0
      src/ppu/debug/ppu.debug.c
  57. 45 0
      src/ppu/debug/tmp.c.txt
  58. 1061 0
      src/ppu/oldppu.c
  59. 1311 0
      src/ppu/ppu.24.c
  60. 997 0
      src/ppu/ppu.c
  61. 181 0
      src/ppu/ppu.memory.c
  62. 1258 0
      src/ppu/ppu.new.c
  63. 1328 0
      src/ppu/ppu.new2.c
  64. 1279 0
      src/ppu/ppu.old.c
  65. 75 0
      src/utils/bin2h/bin2h.c
  66. 94 0
      src/utils/sendromdata.py
  67. 56 0
      unix/Makefile

+ 105 - 0
doc/Changements.txt

@@ -0,0 +1,105 @@
+05/10/2007  V0.30 part 2 ... In progress ...
+      - Start rewrite of mapper manager. The goal, like for plugin manager, is
+        to allow in the future to have plugins or mapper in external libraries.
+      - New sound layer using Allegro. Not really accurate, but more portable.
+        A lot of work has to be done on this (especialy because the actual sound
+        is really bad.)
+	  
+03/05/2007  V0.30 part 1
+      - Started the process of PPU total rewrite.
+      - Bg seem to draw fine, sprite is under the way
+      - Add new test rom
+      - Blargg sprite_ram test sucessfully ^^
+      - Correct a lot of bug in implementation of a lot of things.
+      - Try to make it accurate as much as possible...
+	  - All Blargg PPU test pass with success (in fact only timing is not good,
+	    but it's enought good to let Battletoad work correctly... Maybe it's 
+		sprite0 related)
+	  
+03/04/2007  V0.29
+      - Totaly new memory manager
+      - Simple sound support (only on UNIX, but maybe Windows & DOS can support 
+        it)
+      - New plugin manager (a gamegenie plugin like is under development)
+      - New mapper manager
+      - Large bug correction on mappers (MMC3 now behave correclty)
+      - Large bug correction on larges things (like Signal handler now exit 
+        correctly if a signal occure when in signal)
+      - Output now much simpler & readable
+      - New cart opening & internal strictures
+      - A very good speed boost with the new memory manager (it's incredible, 
+        without speed limitation I can now go near 300/350fps!)
+      - Large part of code reorganisation (needed for the new memory manager and
+        plugin manager)
+      - And an incredible number of modification/bug correction and more and 
+        more...
+      - Switch to SVN source control
+      - New command line parser (now it no more a stupid command line parser)
+      - Palette is no more externalized. (removing some path bad behaviour with
+        Mac/unix/Windows version) 
+      - Corrections on paddle emulation, now should work near perfect.
+
+21/02/2007 V0.28
+      - Internal support for mapper #65 (Irem h3001) but not actually active.
+      - Some new debug options
+
+19/02/2007 V0.27 Internal release
+      - Correction of a blocking bug in the memory module.
+      - The mapper MMC3 now work at nearly 99%. Only IRQ emulation is not as
+        good as I want..
+      - Bug correction on 8x16 sprite support
+      - Now the crashdump generate a file instead of displaying on screen.
+      - The crashdump is more complete
+      
+21/10/2004  V0.26
+	  - J'ai rien changé de plus mais now fullspeed !! (ou presque, c'est incompréhensible)
+	  - MMC3 en voie de finalisation (reste a débugguer)
+	  - Inteception des erreurs du systemes pour l'aide au crash report (cf LisezMoi.txt)
+	  - Changement dans la gestion de la mémoire (qq bugs on du donc se glisser :/ )
+
+19/10/2004  V0.25
+      - Le MMC1 est fonctionel a 99% (manque juste le support des cartouche de 1024Ko)
+      - Support des sprites 8x16 OK
+      - Support de la couleur et des Attribute Tables fonctionnel a 100%
+      - Changement dans l'affichage des sprites (1 passe au lieu de deux)
+      - Un peu plus lent :'(
+      - Version MacOS X
+      - Reorganisation du code sources (pour eviter le garbage lié au différentes versions)
+      - Derniere version avec le code actuelle du PPU (le recodage va commencer)
+      - Toujours l'erreur dans SMB1 :| (me demande si le fichier a pas été abimé) (non il est nickel)
+      - amélioration du support des miroirs
+      - Gestion (et sauvegarde automatique) des jeux a sauvegarde par batterie !! (zelda1, zelda2 fonctionnent !)
+      - Support du mapper AOROM (bonne partie des jeux RARE dont BattleToad)
+	  
+12/10/2004  V0.2 allegé(oui oui) nommé "Blocks.nes edition"
+      - Le support du PPU a été amélioré (Sprite0 detect "perfect" pour nestress et ajout du sprite limiter bit)
+      - Le nouveau PPU a été inséré dans le code 68k
+      - /slap nerick pour essayer de faire avancer la date de 2042 !!
+      - et d'autre bricoles que j'ai pu oublier
+
+13/07/2004  V0.2 (Et oui plus d'1 an apres !!)
+      - Le scrolling est near perfect :)
+      - Support de la couleur #love#
+      - MMC1 fonctionnel a 90% (manque juste la gestion de la VROM, mais bon MM2 l'utilise pas ^^)
+      - Quelque nouvelles fonctionnalitées, cf lisez moi (affichage des name tables, palette, déplacement de l'affichage du fps)
+      - Un (plus ?) nouveau bug (on ne peut plus jouer a smb1 :'( )
+
+23/05/2003  V0.1 Prealpha
+      - Nouveau mapper supporté ! et d'autres en préparations :)
+      - La version distribué est dorenavent une version Release plus en Debug
+
+22/05/2003  V0.1 Prealpha - Interne uniquement
+      - Nouvelle implémentation du PPU, devrait etre plus proche de l'original
+        et il est un peu plus rapide. (2 FPS de gagné sur mon P233 :) )
+      - Le scrolling marche :D (enfin presque pbm de timing ?)
+      - Le joystick est la :D on peu jouer maintenant :D
+
+21/05/2003  V0.0 prealpha release 2
+      - Maintenant le FPS et IPS sont affiché.
+
+19/05/2003  v0.0 prealpha
+	  - Premiere version public, ne sert que de démonstration.
+	  
+--------------------------------------------------------------------------------
+$Id: Changements.txt 29 2007-04-03 14:34:02Z mtrapier $
+--------------------------------------------------------------------------------

+ 127 - 0
doc/Changes.txt

@@ -0,0 +1,127 @@
+05/10/2007  V0.30 part 2 ... In progress ...
+      - Start rewrite of mapper manager. The goal, like for plugin manager, is
+        to allow in the future to have plugins or mapper in external libraries.
+      - New sound layer using Allegro. Not really accurate, but more portable.
+        A lot of work has to be done on this (especialy because the actual sound
+        is really bad.)
+      - Started a new source organisation. Should be the good one this time !
+25/10/2007:
+      - Savestate now work correctly (It was not loaded at the right time...)
+      - Mapper manager seems to work correctly ok
+      - [MACOSX] Buils options are better now
+       
+
+      - [TODO] Remerge old PPU debug utilities in real plugin form
+      - [TODO] Change the make util. Maybe cmake ?
+      - [TODO] Remerge all mappers with the new manager
+      - [TODO] Rewrite UNIX Makefile for support new src organization.
+	
+03/05/2007  V0.30 part 1
+      - Started the process of PPU total rewrite.
+      - Bg seem to draw fine, sprite is under the way
+      - Add new test rom
+      - Blargg sprite_ram test sucessfully ^^
+      - Correct a lot of bug in implementation of a lot of things.
+      - Try to make it accurate as much as possible...
+      - All Blargg PPU test pass with success (in fact only timing is not good,
+        but it's enought good to let Battletoad work correctly... Maybe it's 
+        sprite0 related)
+      
+03/04/2007  V0.29 
+      - Totaly new memory manager
+      - Simple sound support (only on UNIX, but maybe Windows & DOS can support 
+        it)
+      - New plugin manager (a gamegenie plugin like is under development)
+      - New mapper manager
+      - Large bug correction on mappers (MMC3 now behave correclty)
+      - Large bug correction on larges things (like Signal handler now exit 
+        correctly if a signal occure when in signal)
+      - Output now much simpler & readable
+      - New cart opening & internal strictures
+      - A very good speed boost with the new memory manager (it's incredible, 
+        without speed limitation I can now go near 300/350fps!)
+      - Large part of code reorganisation (needed for the new memory manager and
+        plugin manager)
+      - And an incredible number of modification/bug correction and more and 
+        more...
+      - Switch to SVN source control
+      - New command line parser (now it no more a stupid command line parser)
+      - Palette is no more externalized. (removing some path bad behaviour with
+        Mac/unix/Windows version)
+      - Corrections on paddle emulation, now should work near perfect.
+
+02/21/2007 V0.28
+      - Internal support for mapper #65 (Irem h3001) but not actually active.
+      - Some new debug options
+
+02/19/2007 V0.27 Internal release
+      - Correction of a blocking bug in the memory module.
+      - The mapper MMC3 now work at nearly 99%. Only IRQ emulation is not as
+        good as I want..
+      - Bug correction on 8x16 sprite support
+      - Now the crashdump generate a file instead of displaying on screen.
+      - The crashdump is more complete
+
+21/10/2004 V0.26
+      - I've changed nothing, but now it's really fullspeed ! I can't understand
+        why - MMC3 is nearly finished (need debugging)
+      - NEW - Signal interception for making bug reports. Don't hesitate to copy
+        paste the whole thing and mail it to me
+      - Change on memory management.
+
+19/10/2004 V0.25
+      - MMC1 is functional at 99% (no support for 1024Kb cart, but I can't find
+        anyone..)
+      - 8x16 sprites are now OK
+      - Attribute tables, and color is now fully functional
+      - No more two pass on sprite display (speed improvement)
+      - A bit more slow :(
+      - NEW - MacOS X version !
+      - Source code reorganisation
+      - May be the lastest version of the actual PPU source code before total
+        rewriting
+      - SMB1 support is always broken.. I can't understand why..
+      - Mirrors mode improved
+      - NEW - Save Ram support added !
+      - AOROM mappers added ! (nearly all RARE game) but timings are not good
+        so..
+
+12/10/2004  V0.2 allegé(oui oui) nommé "Blocks.nes edition"
+      - Le support du PPU a été amélioré (Sprite0 detect "perfect" pour nestress
+        et ajout du sprite limiter bit)
+      - Le nouveau PPU a été inséré dans le code 68k
+      - /slap nerick pour essayer de faire avancer la date de 2042 !!
+      - et d'autre bricoles que j'ai pu oublier
+
+13/07/2004  V0.2 (Et oui plus d'1 an apres !!)
+      - Le scrolling est near perfect :)
+      - Support de la couleur #love#
+      - MMC1 fonctionnel a 90% (manque juste la gestion de la VROM, mais bon MM2
+        l'utilise pas ^^)
+      - Quelque nouvelles fonctionnalitées, cf lisez moi (affichage des name    
+        tables, palette, déplacement de l'affichage du fps)
+      - Un (plus ?) nouveau bug (on ne peut plus jouer a smb1 :'( )
+
+23/05/2003  V0.1 Prealpha
+
+      - Nouveau mapper supporté ! et d'autres en préparations :)
+      - La version distribué est dorenavent une version Release plus en Debug
+
+22/05/2003  V0.1 Prealpha - Interne uniquement
+
+      - Nouvelle implémentation du PPU, devrait etre plus proche de l'original
+        et il est un peu plus rapide. (2 FPS de gagné sur mon P233 :) )
+      - Le scrolling marche :D (enfin presque pbm de timing ?)
+      - Le joystick est la :D on peu jouer maintenant :D
+
+21/05/2003  V0.0 prealpha release 2
+
+      - Maintenant le FPS et IPS sont affiché.
+
+19/05/2003  V0.0 prealpha
+
+      - Premiere version public, ne sert que de dŽmonstration.
+	
+--------------------------------------------------------------------------------
+$Id: Changes.txt 51 2007-05-22 16:33:04Z mtrapier $
+--------------------------------------------------------------------------------

+ 233 - 0
doc/LisezMoi.txt

@@ -0,0 +1,233 @@
+<---------------------------------------------------------------------------------->
+                                  TI-NESulator
+                               Version 0.26 beta
+
+                           Par Manoël TRAPIER aka Godzil
+                             godzil at godzil point net
+
+1 -> #include <disclamer.h>
+
+L'utilisation de se logiciel se fait a vos risque et périls. Il s'agit d'une pars
+d'une version non terminée, et en cours de dévellopement, et la diffusion n'a été
+faite que pour permettre le teste, et la démonstration de se que sera le logiciel
+final.
+
+Je ne peut donc être tenu pour responsable en cas de problème lié a l'utilisation 
+ou posession de se logiciel. 
+
+Vous êtes libre de l'utiliser à partir du moment au se logiciel n'a pas été modifié
+que sa soit de manière binaire, ou par désassemblage. Si vous trouver une version
+modifié ou fourni avec des fichiers illégaux, veuillez me le faire savoir. Vous
+trouverez comment me contacter dans la suite de se fichier.
+
+Vous êtes libre aussi de le distribuer tant que les fichiers contenus dans le
+paquetage original sont laissé intouché (les fichiers Changements.txt LisezMoi.txt
+et TINes.exe)
+
+Information légales :
+NES, Famicon, Nintendo Entertainment System, Nintendo sont des marques déposé de
+Nintendo France, Nintendo of america et Nintendo Company, Ltd.
+Tout les titres et marques apparaisant dans se fichier texte sont la propriété de
+leurs auteurs respectifs.
+
+N'oubliez pas aussi que la posession de ROM (Fichier binaire représentant le
+contenue d'une cartouche) sans en posseder l'originale (la cartouche) est absolument
+illégale, que vous la gardiez 24 heures ou seulement 30 secondes
+
+
+2 -> Qu'est-ce que TI-NESulator ?
+
+TI-NESulator est un émulateur de la console Nintendo Entertainment System
+(connunément appelé NES) fabriqué par Nintendo au milieu des année 80. L'originalité
+de cet emulateur est que sa plateforme de fonctionnement principal est les
+calculatrices TI-89 et TI-92+ de chez Texas Instrument. Ses calculatrices on la
+particularité de posseder un microprocesseur 68000 de chez motorola, qui est pour
+se genre de plateforme relativement puissant et programmer un emulateur, meme de
+NES, sur ses machine est un véritable défit.
+Tant au niveau matériel que mémoire, la NES et les TI-68k sont completements
+différents. 
+La NES utilise une version légérement personalisé du microprocesseur 6502 
+fonctionnant a environ 1.7Mhz.
+
+Vous avez actuellement une version spéciale (comprendre pas faite pour 
+calculatrices TI.) Cette version ne me sert qu'a mettre en oeuvre de nouvelles
+choses dans la version TI de l'émulateur (notemment a cause d'un gros manque 
+de debuggueur C dans le monde TI.)
+
+Cette version est et sera toujours plus avancé que la version TI dans le sens ou
+elle me sert a expérimenter les nouveaux hardware émulé et a finaliser le hardware
+deja emuler. Une fois fonctionnant d'un maniere convenable sur le portage Windows,
+les mises a jours sont faites dans la version TI. Mais la version Windows au final
+contiendra plus de fonctionnalité que la version TI (support des palletes, de la 
+couleur, voir meme le son etc...)
+
+
+3 -> Utilisation de TI-NESulator
+
+[A faire.]
+
+Version courte :
+
+C:\TINes\>TINES jeux.nes
+
+Utilisation du Joystick :
+
+Manette NES		Clavier
+
+HAUT			HAUT
+BAS			BAS
+GAUCHE			GAUCHE
+DROITE			DROITE
+A			W
+B			S
+START			ENTER
+SELECT			P
+
+--------------------------
+
+Autres touches :
+R			identique a l'apuis du bouton Reset de la console
+
+
+1-2			A un effet, mais vous risque de pas le voir ;)
+3			Affiche les Name Tables
+4			Affiche les Tables d'attributs (de couleur)
+5			Affiche les palettes
+6                       Affiche la table des sprites
+
+--------------------------
+
+Il faut noter aussi qu'afficher ces tables ralenti considérablement la vitesse de 
+l'émulateur
+
+4 -> Compatibilité
+	
+TI-NESulator version Win32 est normalement compatible avec tous les Windows (95, 98,
+Me, NT 4, 2000, XP)
+TI-NESulator version .X (MacOS X) est normalement compatible toutes version de OS X
+(.1.x .2.x .3.x)
+L'émulateur émule actuellement avec les mappers :
+
+	- 0 aucun mapper (Super Mario Bros 1, Donkey Kong, ...)
+
+	- 1 MMC1 (a 99%, Megaman 2, Zelda1 & 2, Final Fantasy 1, ...)
+
+	- 2 UNROM (MegaMan, Final fantasy 2, ...) 
+
+	- 3 CNROM
+
+	- 4 *NOUVEAU* MMC3 (Super mario Bross 2 & 3, MegaMan 3 à 6, ...)
+
+	- 7 *NOUVEAU* AOROM (Battletoad, ...)
+
+Les mappers marqué comme *NOUVEAU* sont ceux qui on été ajouté dans la derniere 
+version disponible. Merci de faire un rapport détaillé si vous rencontrer des 
+problèmes avec.
+
+
+5 -> A faire
+
+* Optimisation du coeur de l'émulation du CPU de la NES.
+
+* Son ?
+
+* Supprimer les printf et les remplacer par l'affichage dans une console (prochaine
+  maj) pour eviter les soucis sur
+
+  - PC: car la console n'est pas forcement ouverte tout le temps
+  - Unix: pour la meme raison
+  - MacOS: aussi (%))
+  - TI: pasqu'on peut pas avoir les deux en meme temps.
+
+* Recoder le ppu (gestion de l'affichage)
+
+6 -> Question Réponses
+
+Q: Pourquoi le jeu xxx ne fonctionne pas ?
+R: TI-NESulator est actuellement en cours de dévellopement. Beaucoup de jeux ne 
+sont pas supporté.
+
+Q: Vous dites que certain de jeux ne sont pas supporté, mais si j'essaye xxx, il 
+marche ! Pourquoi ?
+R: Ceci est du a une des particularité du Hardware de la NES/Famicon. Le hardware 
+de base de la console est assé limité, et la mémoire est aussi tres limité. pour 
+palier à ses limitations, certains jeux utilise ce qu'on appele des Mappers, qui
+permettent notemment d'avoir des jeux plus gros, et qui sont implémenté dans la
+cartouche du jeu. Malheureusement supporter tout ses mappers font grossir
+l'emulateur, et certain on un fonctionnement qui est malheureusement encore
+inconnu. N'oubliez pas non plus que TI-NESulator est encore en dévellopement.
+Certain mapper connu ne sont pas encore implémenté.
+
+Q: Oui puis-je trouver le jeux xxxx ?
+R: Désolé, je ne donne aucun lien menant, ni aucune ROM de jeux commerciaux.
+N'oubliez pas que les personnes vous disant que vous avez le droit de ne garder que
+24h une ROM, vous raconte absolument n'importe quoi. Il est absolument *ILLEGAL* de
+posseder la ROM d'un jeu que vous ne possedez pas officiellement.
+
+Q: XXX donne l'air de fonctionner, pourtant il ne reagis pas au touches
+R: Cet emulateur n'est pas parfait. Certain jeux/demo demandent un fonctionnement
+tres proche de celui de la console originale, ce que TI-NESulator est encore loin
+d'arriver a faire
+
+Q:J'ai des problèmes graphiques avec XXXX
+R:Cf réponse-ci dessus
+
+Q: C'est lent :(
+R: Désolé. Le code est toujours en cours de devellopement. Si vous trouvez trop lent,
+attendez la prochaine version une surprise vous y attendra peut-etre !
+
+7 -> En cas de problème
+
+a) Un jeu indiqué compatible ne fonctionne pas (ie "Mapper non supporté")
+
+Alors plusieurs possibilité, soit vous avez une autre version du jeu officielle 
+ou non, cela peut changer par exemple le mapper utilisé par le jeux, soit vous
+avez recuperer un "bad dump", c'est a dire plus simplement une rom foireuse.
+
+Dans ces deux cas essayez d'en recuperer une autre version.
+
+b) TI-NESulator à planté !!
+Si cela arrive regulierement pour le meme jeu et au meme endroit faites moi un
+crash report avec les information que TI-NESulator vous met dans la console
+(pour pouvoir la copier tranquillement il faut lancer a partir d'une console
+et pas faire glisser la rom sur l'executable)
+
+N'oubliez pas de préciser le nom complet du jeu, la taille du fichier et toutes
+les infos que vous trouverez pertinante (manipulation a faire etc...)
+
+
+8 -> Remerciement
+
+PpHd			pour PreOS, et SMA :)
+TiMad			Membre de la XTeam sans lequel XLib existerait pas.
+nEUrOO			Membre de la XTeam sans lequel XLib existerait pas.
+
+Loopy, Y0Shi, 
+Marrat Fayzullin,
+et bcp d'autre  pour la documentation technique sur la NES
+
+Ainsi que tout ceux que j'ai pu oublier
+
+9 -> Litérature
+
+[A faire.]
+
+mais lien rapide :
+
+http://www.nesdev.org
+
+10 -> Comment me contacter ?
+s
+Vous pouvez me contacter grace au forum yAronet 
+http://www.yaronet.com
+
+Et surtout grace au lien suivant :
+http://www.yaronet.com/posts.php?sl=&s=2339
+
+Vous pouvez aussi me joindre par mail en m'écrivant à "godzil chez godzil point net"
+Vous pouvez aussi essayer de visiter un de mes sites :
+http://www.godzil.net
+
+--------------------------------------------------------------------------------
+$Id: LisezMoi.txt 17 2007-03-27 09:25:23Z mtrapier $
+--------------------------------------------------------------------------------

+ 7 - 0
doc/TODO.txt

@@ -0,0 +1,7 @@
+07/10/2007  V0.30 part 2
+      - Remerge old PPU debug utilities in real plugin form                  [ ]
+      - Change the make util for UNIX. Maybe cmake ?                         [ ]
+      - Cleanup os/win32 folder                                              [ ]
+      - Thinks about a better svn repository (Maybe a dedibox is a good choice?)
+                                                                             [ ]
+                                                                             

+ 112 - 0
src/NESCarts.c

@@ -0,0 +1,112 @@
+/*
+ *  Cart manager - The TI-NESulator Project
+ *  NESCart.c
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-05-02 18:37:41 +0200 (mer, 02 mai 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/NESCarts.c $
+ *  $Revision: 50 $
+ *
+ */
+ 
+#include "include/NESCarts.h"
+#include "include/mappers/manager.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/* Plateform dependent function */
+void *LoadFilePtr(char * filename);
+
+void DumpCartProperties(FILE *out, NesCart * cart)
+{
+    fprintf(out,
+            "'%s' informations:\n"
+            "   Total ROM Size       : 0x%06X    | Total VROM Size      : 0x%06X\n"
+            "   Mapper ID            : 0x%06X    | Mirroring ?          : %s\n"
+            "   Battery ?            : %s         | 4 Screen ?           : %s   \n"
+            "   PROMBanks start at   : %p  |\n"
+            "   VROMBanks start at   : %p  |\n",
+            cart->FileName,
+            cart->PROMSize,
+            cart->VROMSize,
+            cart->MapperID,
+            cart->Flags & iNES_MIRROR?  "Horizontal" : "Vertical",
+            cart->Flags & iNES_BATTERY? "Yes": "No ",
+            cart->Flags & iNES_4SCREEN? "Yes": "No ",
+            cart->PROMBanks,
+            cart->VROMBanks);
+}
+
+int LoadCart(const char *filename, NesCart * cart) 
+{
+	byte buffer[6];
+    /* Load the cart into memory */
+    cart->File = (byte *)LoadFilePtr(filename);
+    
+    
+    if (cart->File == -1)
+      return -1;
+    
+	sprintf(buffer, "%c%c%c%c", 0x4E, 0x45, 0x53, 0x1A);
+	
+	/* Verify that this is a real iNES valid file */
+	if (memcmp(cart->File, buffer, 4))
+		return -1;
+	
+    if ((cart->File == NULL) || (cart->File == -1))
+        return -1;
+    
+    /* Before go elsewhere, verify that the header is clean !
+           (aka no DiskDude! in it) */
+    if (memcmp(cart->File+7, "DiskDude!", 9) == 0)
+    {
+        printf("\n"
+               "*******************WARNING****************\n"
+               "* The header of this game is not clean   *\n"
+               "* (DiskDude! pollution) I will only use  *\n"
+               "* basic MapperID (mapper 0-15). This can *\n"
+               "* led to unexpected behavior...          *\n"
+               "*                                        *\n"
+               "*        PLEASE CLEAN THIS FILE!         *\n"
+               "******************WARNING*****************\n\n");
+        /* So this rom file is not clean, we can only rely on the "basic" mapperID */
+        cart->MapperID = cart->File[6]>>4;  /* Mapper Type */
+
+    }
+    else
+    {   /* This rom file is clean, we can read the extended MapperID */
+        cart->MapperID = (cart->File[6]>>4)|(cart->File[7]&0xF0);  /* Mapper Type */
+    }
+
+    /* Now fill the structure */
+    cart->FileName = filename;
+
+    cart->PROMSize = cart->File[4] * 16 * 1024;               /* Size of PROM */
+    cart->VROMSize = cart->File[5] *  8 * 1024;               /* Size of VROM */   
+    cart->Flags = cart->File[6] & 0x0F;
+    
+    /* We don't and we will never support trainer-ed ROM */
+    if (cart->Flags & iNES_TRAINER)
+    {
+        printf("\n"
+               "********************ERROR*****************\n"
+               "* This cart have an embedded trainer.    *\n"
+               "* There is NO support for them.          *\n"
+               "* Please use a CLEAN dump if you want    *\n"
+               "* to play this game.                     *\n"
+               "********************ERROR*****************\n\n");
+        return -1; 
+    }
+    
+    cart->PROMBanks = cart->File + 16;                     /* Pointer on the first PROM */
+    
+    cart->VROMBanks = cart->PROMBanks + cart->PROMSize;    /* Pointer on the first VROM */       
+
+    DumpCartProperties(stdout, cart);
+
+    return 0;    
+} 

+ 418 - 0
src/apu/SndAlleg.c

@@ -0,0 +1,418 @@
+/** EMULib Emulation Library *********************************/
+/**                                                         **/
+/**                         SndUnix.c                       **/
+/**                                                         **/
+/** This file contains standard sound generation routines   **/
+/** for Unix using /dev/dsp and /dev/audio.                 **/
+/**                                                         **/
+/** Copyright (C) Marat Fayzullin 1996-2002                 **/
+/**     You are not allowed to distribute this software     **/
+/**     commercially. Please, notify me, if you make any    **/
+/**     changes to this file.                               **/
+/*************************************************************/
+#include "Sound.h"
+
+#include <allegro.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+//#include <unistd.h>
+//#include <fcntl.h>
+#include <pthread.h>
+//#include <sys/ioctl.h>
+    
+#define AUDIO_CONV(A) (128+(A))
+
+
+AUDIOSTREAM *stream;
+
+static pthread_t ThreadID;
+static int SoundFD;
+static int SoundRate    = 0;
+static int MasterVolume = 64;
+static int MasterSwitch = (1<<SND_CHANNELS)-1;
+static int LoopFreq     = 25;
+static int NoiseGen     = 1;
+static int Suspended    = 0;
+
+static struct
+{
+  int Type;                       /* Channel type (SND_*)             */
+  int Freq;                       /* Channel frequency (Hz)           */
+  int Volume;                     /* Channel volume (0..255)          */
+
+  signed char *Data;              /* Wave data (-128..127 each)       */
+  int Length;                     /* Wave length in Data              */
+  int Rate;                       /* Wave playback rate (or 0Hz)      */
+  int Pos;                        /* Wave current position in Data    */  
+
+  int Count;                      /* Phase counter                    */
+} CH[SND_CHANNELS];
+
+static void UnixSetWave(int Channel,signed char *Data,int Length,int Rate);
+static void UnixSetSound(int Channel,int NewType);
+static void UnixDrum(int Type,int Force);
+static void UnixSetChannels(int Volume,int Switch);
+static void UnixSound(int Channel,int NewFreq,int NewVolume);
+
+static int  OpenSoundDevice(int Rate,int Verbose);
+static void *DSPLoop(void *Arg);
+
+/** StopSound() **********************************************/
+/** Temporarily suspend sound.                              **/
+/*************************************************************/
+void StopSound(void) { Suspended=1; }
+
+/** ResumeSound() ********************************************/
+/** Resume sound after StopSound().                         **/
+/*************************************************************/
+void ResumeSound(void) { Suspended=0; }
+
+/** OpenSoundDevice() ****************************************/
+/** Open /dev/dsp with a given level of sound quality.      **/
+/** Returns 0 if failed or sound quality (Mode).            **/
+/*************************************************************/
+static int OpenSoundDevice(int Rate,int Verbose)
+{
+  voice_start(stream->voice);
+  
+  if(Verbose) puts("OK");
+  return(Rate);
+}
+
+/** DSPLoop() ************************************************/
+/** Main loop of the sound server.                          **/
+/*************************************************************/
+static void *DSPLoop(void *Arg)
+{
+  int Wave[SND_BUFSIZE];
+  unsigned char *Buf;
+  register int J,I,K,L,M,N,L1,L2,A1,A2,V;
+  int FreqCount;
+
+  for(J=0;J<SND_CHANNELS;J++)
+  {
+    CH[J].Type   = SND_MELODIC;
+    CH[J].Count  = 0;
+    CH[J].Volume = 0;
+    CH[J].Freq   = 0;
+  }
+
+
+
+  FreqCount=SoundRate/SND_BUFSIZE;
+
+  for(;;)
+  {
+    Buf = get_audio_stream_buffer(stream);
+
+      if (Buf) {
+      FreqCount-=LoopFreq;
+      
+    /* If suspending sound... */
+    if(Suspended)
+    {
+      /* Close sound device */
+      while(Suspended) sleep(1);
+      /* Reopen sound device */
+      SoundRate=OpenSoundDevice(SoundRate,0);
+    }
+
+    /* Waveform generator */
+    for(J=0,M=MasterSwitch;M&&(J<SND_CHANNELS);J++,M>>=1)
+      if(CH[J].Freq&&(V=CH[J].Volume)&&(M&1))
+        switch(CH[J].Type)
+        {
+          case SND_NOISE: /* White Noise */
+            /* For high frequencies, recompute volume */
+            if(CH[J].Freq<=SoundRate) K=0x10000*CH[J].Freq/SoundRate;
+            else { V=V*SoundRate/CH[J].Freq;K=0x10000; }
+            L1=CH[J].Count;
+            V<<=7;
+            for(I=0;I<SND_BUFSIZE;I++)
+            {
+              L1+=K;
+              if(L1&0xFFFF0000)
+              {
+                L1&=0xFFFF;
+                if((NoiseGen<<=1)&0x80000000) NoiseGen^=0x08000001;
+              }
+              Wave[I]+=NoiseGen&1? V:-V;
+            }
+            CH[J].Count=L1;
+            break;
+
+          case SND_WAVE: /* Custom Waveform */
+            /* Waveform data must have correct length! */
+            if(CH[J].Length<=0) break;
+            /* Start counting */
+            K  = CH[J].Rate>0? (SoundRate<<15)/CH[J].Freq/CH[J].Rate
+                             : (SoundRate<<15)/CH[J].Freq/CH[J].Length;
+            L1 = CH[J].Pos%CH[J].Length;
+            L2 = CH[J].Count;
+            A1 = CH[J].Data[L1]*V;
+            /* If expecting interpolation... */
+            if(L2<K)
+            {
+              /* Compute interpolation parameters */
+              A2 = CH[J].Data[(L1+1)%CH[J].Length]*V;
+              L  = (L2>>15)+1;
+              N  = ((K-(L2&0x7FFF))>>15)+1;
+            }
+            /* Add waveform to the buffer */
+            for(I=0;I<SND_BUFSIZE;I++)
+              if(L2<K)
+              {
+                /* Interpolate linearly */
+                Wave[I]+=A1+L*(A2-A1)/N;
+                /* Next waveform step */
+                L2+=0x8000;
+                /* Next interpolation step */
+                L++;
+              }
+              else
+              {
+                L1 = (L1+L2/K)%CH[J].Length;
+                L2 = (L2%K)+0x8000;
+                A1 = CH[J].Data[L1]*V;
+                Wave[I]+=A1;
+                /* If expecting interpolation... */
+                if(L2<K)
+                {
+                  /* Compute interpolation parameters */
+                  A2 = CH[J].Data[(L1+1)%CH[J].Length]*V;
+                  L  = 1;
+                  N  = ((K-L2)>>15)+1;
+                }
+              }
+            /* End counting */
+            CH[J].Pos   = L1;
+            CH[J].Count = L2;
+            break;
+ 
+ 
+          case SND_QS_DU0:
+             /* Do not allow frequencies that are too high */
+            if(CH[J].Freq>=SoundRate/3) break;
+            K=0x10000*CH[J].Freq/SoundRate;
+            L1=CH[J].Count;
+            V<<=7;
+            for(I=0;I<SND_BUFSIZE;I++)
+            {
+              L2=L1+K;
+              Wave[I]+=L1&0x2000?(L2&0x8000? V:0):(L2&0x8000? 0:-V);
+              L1=L2;
+            }
+            CH[J].Count=L1;
+            break;
+            
+          case SND_QS_DU1:
+       /* Do not allow frequencies that are too high */
+            if(CH[J].Freq>=SoundRate/3) break;
+            K=0x10000*CH[J].Freq/SoundRate;
+            L1=CH[J].Count;
+            V<<=7;
+            for(I=0;I<SND_BUFSIZE;I++)
+            {
+              L2=L1+K;
+              Wave[I]+=L1&0x4000?(L2&0x8000? V:0):(L2&0x8000? 0:-V);
+              L1=L2;
+            }
+            CH[J].Count=L1;
+            break;
+            
+          case SND_QS_DU3:
+       /* Do not allow frequencies that are too high */
+            if(CH[J].Freq>=SoundRate/3) break;
+            K=0x10000*CH[J].Freq/SoundRate;
+            L1=CH[J].Count;
+            V<<=7;
+            for(I=0;I<SND_BUFSIZE;I++)
+            {
+              L2=L1+K;
+              Wave[I]+=L1&0xC000?(L2&0x4000? V:0):(L2&0xC000? 0:-V);
+              L1=L2;
+            }
+            CH[J].Count=L1;
+            break;
+            
+          case SND_QS_DU2:
+          case SND_MELODIC: /* Melodic Sound */         
+          default:          /* Default Sound */
+            /* Do not allow frequencies that are too high */
+            if(CH[J].Freq>=SoundRate/3) break;
+            K=0x10000*CH[J].Freq/SoundRate;
+            L1=CH[J].Count;
+            V<<=7;
+            for(I=0;I<SND_BUFSIZE;I++)
+            {
+              L2=L1+K;
+              Wave[I]+=L1&0x8000? (L2&0x8000? V:0):(L2&0x8000? 0:-V);
+              L1=L2;
+            }
+            CH[J].Count=L1;
+            break;
+            
+          case SND_TRIANGLE:          /* Default Sound */
+            /* Do not allow frequencies that are too high */
+            if(CH[J].Freq>=SoundRate/3) break;
+            K=0x10000*CH[J].Freq/SoundRate;
+            L1=CH[J].Count;
+            V<<=7;
+            for(I=0;I<SND_BUFSIZE;I++)
+            {
+              L2=L1+K;
+              Wave[I]+= L1&0x2000?V:-V /*(L2&0x8000? V:0):(L2&0x8000? 0:-V)*/;
+              L1=L2;
+            }
+            CH[J].Count=L1;
+            break;
+        }
+
+    /* Mix and convert waveforms */
+    for(J=0;J<SND_BUFSIZE;J++)
+    {
+      I=(Wave[J]*MasterVolume)>>16;
+      I=I<-128? -128:I>127? 127:I;
+      Buf[J]=AUDIO_CONV(I);
+      Wave[J]=0;
+    }
+    free_audio_stream_buffer(stream);
+    }
+  }
+
+  return(0);
+}
+
+/** InitSound() **********************************************/
+/** Initialize DSP. Returns Rate on success, 0 otherwise.   **/
+/** Mode is 0 to skip initialization (will be silent).      **/
+/*************************************************************/
+int InitSound(int Rate,int Verbose)
+{
+  /* If sound was initialized, kill it */
+  TrashSound();
+
+  /* Silence requested */
+  if(Rate<=0) return(0);
+
+  /* Synthesis rate should be at least 8kHz */
+  if(Rate<8192) Rate=44100;
+
+  /* Initialize things */
+  SoundRate = 0;
+  ThreadID  = 0;
+  Suspended = 0;
+
+  /* Set driver functions */
+  SndDriver.SetSound    = UnixSetSound;
+  SndDriver.Drum        = UnixDrum;
+  SndDriver.SetChannels = UnixSetChannels;
+  SndDriver.Sound       = UnixSound;
+  SndDriver.SetWave     = UnixSetWave;
+
+  if (install_sound(DIGI_AUTODETECT, MIDI_NONE, "") != 0)
+  {
+      fprintf(stderr, "%s!\n", allegro_error);
+      return 1;
+  }
+
+
+  stream = play_audio_stream(SND_BUFSIZE, 8, FALSE, Rate, 255, 128);
+  if (!stream) {
+      fprintf(stderr, "Error creating audio stream!\n");
+      return 1;
+  }
+
+  voice_stop(stream->voice);
+
+  /* Open sound device */
+  if(Verbose) puts("Starting sound server:");
+  if(!(Rate=OpenSoundDevice(Rate,Verbose))) return(0);
+
+  /* Create DSPLoop() thread */
+  if(Verbose) printf("  Creating thread...");
+  if(pthread_create(&ThreadID,0,DSPLoop,0))
+  { if(Verbose) puts("FAILED");return(0); }
+
+  /* Detach the thread */
+  pthread_detach(ThreadID);
+
+  /* Done */
+  if(Verbose) puts("OK");
+  return(SoundRate=Rate);
+}
+
+/** TrashSound() *********************************************/
+/** Shut DSP down.                                          **/
+/*************************************************************/
+void TrashSound(void)
+{
+  StopSound();
+  printf("%s: Kill thread...\n", __func__);
+  if(ThreadID)    pthread_cancel(ThreadID);
+
+  SoundRate = 0;
+  ThreadID  = 0;
+}
+
+/** UnixSound() **********************************************/
+/** Generate sound of given frequency (Hz) and volume       **/
+/** (0..255) via given channel.                             **/
+/*************************************************************/
+void UnixSound(int Channel,int NewFreq,int NewVolume)
+{
+  if((Channel<0)||(Channel>=SND_CHANNELS)) return;
+  if(!NewVolume||!NewFreq) { NewVolume=0;NewFreq=0; }
+
+  CH[Channel].Volume = NewVolume;
+  CH[Channel].Freq   = NewFreq;
+}
+
+/** UnixSetChannels() ****************************************/
+/** Set master volume (0..255) and turn channels on/off.    **/
+/** Each bit in Toggle corresponds to a channel (1=on).     **/
+/*************************************************************/
+void UnixSetChannels(int MVolume,int MSwitch)
+{
+  /* Set new MasterSwitch value */
+  MasterSwitch = MSwitch;
+  MasterVolume = MVolume;
+}
+
+/** UnixSetSound() *******************************************/
+/** Set sound type (SND_NOISE/SND_MELODIC) for a given      **/
+/** channel.                                                **/
+/*************************************************************/
+void UnixSetSound(int Channel,int NewType)
+{
+  if((Channel<0)||(Channel>=SND_CHANNELS)) return;
+  CH[Channel].Type = NewType;
+}
+
+
+/** UnixSetWave() ********************************************/
+/** Set waveform for a given channel. The channel will be   **/
+/** marked with sound type SND_WAVE. Set Rate=0 if you want **/
+/** waveform to be an instrument or set it to the waveform  **/
+/** own playback rate.                                      **/
+/*************************************************************/
+void UnixSetWave(int Channel,signed char *Data,int Length,int Rate)
+{
+  if((Channel<0)||(Channel>=SND_CHANNELS)||(Length<=0)) return;
+
+  CH[Channel].Type   = SND_WAVE;
+  CH[Channel].Length = Length;
+  CH[Channel].Rate   = Rate;
+  CH[Channel].Pos    = 0;
+  CH[Channel].Count  = 0;
+  CH[Channel].Data   = Data;
+}
+
+/** UnixDrum() ***********************************************/
+/** Hit a drum of a given type with given force.            **/
+/*************************************************************/
+void UnixDrum(int Type,int Force)
+{
+  /* This function is currently empty */
+}

+ 554 - 0
src/apu/SndUnixT.c

@@ -0,0 +1,554 @@
+/** EMULib Emulation Library *********************************/
+/**                                                         **/
+/**                         SndUnix.c                       **/
+/**                                                         **/
+/** This file contains standard sound generation routines   **/
+/** for Unix using /dev/dsp and /dev/audio.                 **/
+/**                                                         **/
+/** Copyright (C) Marat Fayzullin 1996-2002                 **/
+/**     You are not allowed to distribute this software     **/
+/**     commercially. Please, notify me, if you make any    **/
+/**     changes to this file.                               **/
+/*************************************************************/
+#ifdef UNIX
+
+#include "Sound.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/ioctl.h>
+
+#ifdef SUN_AUDIO  
+
+#include <sys/audioio.h>
+#include <sys/conf.h>
+#include <stropts.h>
+
+#define AUDIO_CONV(A) (ULAW[0xFF&(128+(A))]) 
+
+static unsigned char ULAW[256] =
+{
+    31,   31,   31,   32,   32,   32,   32,   33,
+    33,   33,   33,   34,   34,   34,   34,   35,
+    35,   35,   35,   36,   36,   36,   36,   37,
+    37,   37,   37,   38,   38,   38,   38,   39,
+    39,   39,   39,   40,   40,   40,   40,   41,
+    41,   41,   41,   42,   42,   42,   42,   43,
+    43,   43,   43,   44,   44,   44,   44,   45,
+    45,   45,   45,   46,   46,   46,   46,   47,
+    47,   47,   47,   48,   48,   49,   49,   50,
+    50,   51,   51,   52,   52,   53,   53,   54,
+    54,   55,   55,   56,   56,   57,   57,   58,
+    58,   59,   59,   60,   60,   61,   61,   62,
+    62,   63,   63,   64,   65,   66,   67,   68,
+    69,   70,   71,   72,   73,   74,   75,   76,
+    77,   78,   79,   81,   83,   85,   87,   89,
+    91,   93,   95,   99,  103,  107,  111,  119,
+   255,  247,  239,  235,  231,  227,  223,  221,
+   219,  217,  215,  213,  211,  209,  207,  206,
+   205,  204,  203,  202,  201,  200,  199,  198,
+   219,  217,  215,  213,  211,  209,  207,  206,
+   205,  204,  203,  202,  201,  200,  199,  198,
+   197,  196,  195,  194,  193,  192,  191,  191,
+   190,  190,  189,  189,  188,  188,  187,  187,
+   186,  186,  185,  185,  184,  184,  183,  183,
+   182,  182,  181,  181,  180,  180,  179,  179,
+   178,  178,  177,  177,  176,  176,  175,  175,
+   175,  175,  174,  174,  174,  174,  173,  173,
+   173,  173,  172,  172,  172,  172,  171,  171,
+   171,  171,  170,  170,  170,  170,  169,  169,
+   169,  169,  168,  168,  168,  168,  167,  167,
+   167,  167,  166,  166,  166,  166,  165,  165,
+   165,  165,  164,  164,  164,  164,  163,  163
+};
+
+#else /* SUN_AUDIO */
+
+#ifdef __FreeBSD__
+#include <machine/soundcard.h>
+#endif
+ 
+#ifdef __NetBSD__
+#include <soundcard.h>
+#endif
+ 
+#ifdef __linux__
+#include <sys/soundcard.h>
+#endif
+    
+#define AUDIO_CONV(A) (128+(A))
+
+#endif /* SUN_AUDIO */
+
+static pthread_t ThreadID;
+static int SoundFD;
+static int SoundRate    = 0;
+static int MasterVolume = 64;
+static int MasterSwitch = (1<<SND_CHANNELS)-1;
+static int LoopFreq     = 25;
+static int NoiseGen     = 1;
+static int Suspended    = 0;
+
+static struct
+{
+  int Type;                       /* Channel type (SND_*)             */
+  int Freq;                       /* Channel frequency (Hz)           */
+  int Volume;                     /* Channel volume (0..255)          */
+
+  signed char *Data;              /* Wave data (-128..127 each)       */
+  int Length;                     /* Wave length in Data              */
+  int Rate;                       /* Wave playback rate (or 0Hz)      */
+  int Pos;                        /* Wave current position in Data    */  
+
+  int Count;                      /* Phase counter                    */
+} CH[SND_CHANNELS];
+
+static void UnixSetWave(int Channel,signed char *Data,int Length,int Rate);
+static void UnixSetSound(int Channel,int NewType);
+static void UnixDrum(int Type,int Force);
+static void UnixSetChannels(int Volume,int Switch);
+static void UnixSound(int Channel,int NewFreq,int NewVolume);
+
+static int  OpenSoundDevice(int Rate,int Verbose);
+static void *DSPLoop(void *Arg);
+
+/** StopSound() **********************************************/
+/** Temporarily suspend sound.                              **/
+/*************************************************************/
+void StopSound(void) { Suspended=1; }
+
+/** ResumeSound() ********************************************/
+/** Resume sound after StopSound().                         **/
+/*************************************************************/
+void ResumeSound(void) { Suspended=0; }
+
+/** OpenSoundDevice() ****************************************/
+/** Open /dev/dsp with a given level of sound quality.      **/
+/** Returns 0 if failed or sound quality (Mode).            **/
+/*************************************************************/
+static int OpenSoundDevice(int Rate,int Verbose)
+{
+  int I,J,K;
+
+#ifdef SUN_AUDIO
+
+  if(Verbose) printf("  Opening /dev/audio...");
+  if((SoundFD=open("/dev/audio",O_WRONLY | O_NONBLOCK))==-1)
+  {
+    if(Verbose) puts("FAILED");
+    return(0);
+  }
+
+  /*
+  ** Sun's specific initialization should be here...
+  ** We assume, that it's set to 8000Hz u-law mono right now.
+  */    
+
+#else /* SUN_AUDIO */
+
+  /* At first, we need to open /dev/dsp: */
+  if(Verbose) printf("  Opening /dev/dsp...");
+  I=((SoundFD=open("/dev/dsp",O_WRONLY))<0);
+
+  /* Set 8-bit sound */
+  if(!I)
+  { 
+    if(Verbose) printf("OK\n  Setting mode: 8bit...");
+    J=AFMT_U8;
+    I=(ioctl(SoundFD,SNDCTL_DSP_SETFMT,&J)<0);
+  }
+
+  /* Set mono sound */
+  if(!I)
+  {
+    if(Verbose) printf("mono...");
+    J=0;
+    I=(ioctl(SoundFD,SNDCTL_DSP_STEREO,&J)<0);
+  }
+
+  /* Set sampling rate */
+  if(!I)
+  {
+    if(Verbose) printf("OK\n  Setting sampling rate: %dHz...",Rate);
+    I=(ioctl(SoundFD,SNDCTL_DSP_SPEED,&Rate)<0);
+    if(Verbose) printf("(got %dHz)...",Rate);
+  } 
+
+  /* Here we set the number of buffers to use */
+  if(!I)
+  { 
+    if(Verbose)
+      printf
+      (
+        "OK\n  Adjusting buffers: %d buffers %d bytes each...",
+        SND_BUFFERS,1<<SND_BITS
+      );
+
+    /* Set buffer length and number of buffers */
+    J=K=SND_BITS|(SND_BUFFERS<<16);
+    I=(ioctl(SoundFD,SNDCTL_DSP_SETFRAGMENT,&J)<0);
+
+    /* Buffer length as n, not 2^n! */
+    if((J&0xFFFF)<16) J=(J&0xFFFF0000)|(1<<(J&0xFFFF));
+    K=(1<<SND_BITS)|(SND_BUFFERS<<16);
+
+    /* If something went wrong... */
+    if(J!=K)
+    {
+      if((J>>16)<SND_BUFFERS)       I=-1;
+      if((J&0xFFFF)!=(1<<SND_BITS)) I=-1;
+    }
+  } 
+
+  /* If something failed, fall out */
+  if(I) { if(Verbose) puts("FAILED");return(0); }
+    
+#endif /* SUN_AUDIO */
+    
+  if(Verbose) puts("OK");
+  return(Rate);
+}
+
+/** DSPLoop() ************************************************/
+/** Main loop of the sound server.                          **/
+/*************************************************************/
+static void *DSPLoop(void *Arg)
+{
+  int Wave[SND_BUFSIZE];
+  unsigned char Buf[SND_BUFSIZE];
+  register int J,I,K,L,M,N,L1,L2,A1,A2,V;
+  int FreqCount;
+
+  for(J=0;J<SND_CHANNELS;J++)
+  {
+    CH[J].Type   = SND_MELODIC;
+    CH[J].Count  = 0;
+    CH[J].Volume = 0;
+    CH[J].Freq   = 0;
+  }
+
+  FreqCount=SoundRate/SND_BUFSIZE;
+  for(;;FreqCount-=LoopFreq)
+  {
+    /* If suspending sound... */
+    if(Suspended)
+    {
+      /* Close sound device */
+#ifndef SUN_AUDIO
+      ioctl(SoundFD,SNDCTL_DSP_RESET);
+#endif
+      close(SoundFD);
+      /* Suspend execution until Suspended=0 */
+      while(Suspended) sleep(1);
+      /* Reopen sound device */
+      SoundRate=OpenSoundDevice(SoundRate,0);
+    }
+
+    /* Waveform generator */
+    for(J=0,M=MasterSwitch;M&&(J<SND_CHANNELS);J++,M>>=1)
+      if(CH[J].Freq&&(V=CH[J].Volume)&&(M&1))
+        switch(CH[J].Type)
+        {
+          case SND_NOISE: /* White Noise */
+            /* For high frequencies, recompute volume */
+            if(CH[J].Freq<=SoundRate) K=0x10000*CH[J].Freq/SoundRate;
+            else { V=V*SoundRate/CH[J].Freq;K=0x10000; }
+            L1=CH[J].Count;
+            V<<=7;
+            for(I=0;I<SND_BUFSIZE;I++)
+            {
+              L1+=K;
+              if(L1&0xFFFF0000)
+              {
+                L1&=0xFFFF;
+                if((NoiseGen<<=1)&0x80000000) NoiseGen^=0x08000001;
+              }
+              Wave[I]+=NoiseGen&1? V:-V;
+            }
+            CH[J].Count=L1;
+            break;
+
+          case SND_WAVE: /* Custom Waveform */
+            /* Waveform data must have correct length! */
+            if(CH[J].Length<=0) break;
+            /* Start counting */
+            K  = CH[J].Rate>0? (SoundRate<<15)/CH[J].Freq/CH[J].Rate
+                             : (SoundRate<<15)/CH[J].Freq/CH[J].Length;
+            L1 = CH[J].Pos%CH[J].Length;
+            L2 = CH[J].Count;
+            A1 = CH[J].Data[L1]*V;
+            /* If expecting interpolation... */
+            if(L2<K)
+            {
+              /* Compute interpolation parameters */
+              A2 = CH[J].Data[(L1+1)%CH[J].Length]*V;
+              L  = (L2>>15)+1;
+              N  = ((K-(L2&0x7FFF))>>15)+1;
+            }
+            /* Add waveform to the buffer */
+            for(I=0;I<SND_BUFSIZE;I++)
+              if(L2<K)
+              {
+                /* Interpolate linearly */
+                Wave[I]+=A1+L*(A2-A1)/N;
+                /* Next waveform step */
+                L2+=0x8000;
+                /* Next interpolation step */
+                L++;
+              }
+              else
+              {
+                L1 = (L1+L2/K)%CH[J].Length;
+                L2 = (L2%K)+0x8000;
+                A1 = CH[J].Data[L1]*V;
+                Wave[I]+=A1;
+                /* If expecting interpolation... */
+                if(L2<K)
+                {
+                  /* Compute interpolation parameters */
+                  A2 = CH[J].Data[(L1+1)%CH[J].Length]*V;
+                  L  = 1;
+                  N  = ((K-L2)>>15)+1;
+                }
+              }
+            /* End counting */
+            CH[J].Pos   = L1;
+            CH[J].Count = L2;
+            break;
+ 
+ 
+          case SND_QS_DU0:
+             /* Do not allow frequencies that are too high */
+            if(CH[J].Freq>=SoundRate/3) break;
+            K=0x10000*CH[J].Freq/SoundRate;
+            L1=CH[J].Count;
+            V<<=7;
+            for(I=0;I<SND_BUFSIZE;I++)
+            {
+              L2=L1+K;
+              Wave[I]+=L1&0x2000?(L2&0x8000? V:0):(L2&0x8000? 0:-V);
+              L1=L2;
+            }
+            CH[J].Count=L1;
+            break;
+            
+          case SND_QS_DU1:
+       /* Do not allow frequencies that are too high */
+            if(CH[J].Freq>=SoundRate/3) break;
+            K=0x10000*CH[J].Freq/SoundRate;
+            L1=CH[J].Count;
+            V<<=7;
+            for(I=0;I<SND_BUFSIZE;I++)
+            {
+              L2=L1+K;
+              Wave[I]+=L1&0x4000?(L2&0x8000? V:0):(L2&0x8000? 0:-V);
+              L1=L2;
+            }
+            CH[J].Count=L1;
+            break;
+            
+          case SND_QS_DU3:
+       /* Do not allow frequencies that are too high */
+            if(CH[J].Freq>=SoundRate/3) break;
+            K=0x10000*CH[J].Freq/SoundRate;
+            L1=CH[J].Count;
+            V<<=7;
+            for(I=0;I<SND_BUFSIZE;I++)
+            {
+              L2=L1+K;
+              Wave[I]+=L1&0xC000?(L2&0x4000? V:0):(L2&0xC000? 0:-V);
+              L1=L2;
+            }
+            CH[J].Count=L1;
+            break;
+            
+          case SND_QS_DU2:
+          case SND_MELODIC: /* Melodic Sound */         
+          default:          /* Default Sound */
+            /* Do not allow frequencies that are too high */
+            if(CH[J].Freq>=SoundRate/3) break;
+            K=0x10000*CH[J].Freq/SoundRate;
+            L1=CH[J].Count;
+            V<<=7;
+            for(I=0;I<SND_BUFSIZE;I++)
+            {
+              L2=L1+K;
+              Wave[I]+=L1&0x8000? (L2&0x8000? V:0):(L2&0x8000? 0:-V);
+              L1=L2;
+            }
+            CH[J].Count=L1;
+            break;
+            
+          case SND_TRIANGLE:          /* Default Sound */
+            /* Do not allow frequencies that are too high */
+            if(CH[J].Freq>=SoundRate/3) break;
+            K=0x10000*CH[J].Freq/SoundRate;
+            L1=CH[J].Count;
+            V<<=7;
+            for(I=0;I<SND_BUFSIZE;I++)
+            {
+              L2=L1+K;
+              Wave[I]+= L1&0x2000?V:-V /*(L2&0x8000? V:0):(L2&0x8000? 0:-V)*/;
+              L1=L2;
+            }
+            CH[J].Count=L1;
+            break;
+        }
+
+    /* Mix and convert waveforms */
+    for(J=0;J<SND_BUFSIZE;J++)
+    {
+      I=(Wave[J]*MasterVolume)>>16;
+      I=I<-128? -128:I>127? 127:I;
+      Buf[J]=AUDIO_CONV(I);
+      Wave[J]=0;
+    }
+
+    if(SoundFD==-1) sleep(1);
+    else
+    {
+#ifdef SUN_AUDIO
+      /* Flush output first, don't care about return status. After this
+      ** write next buffer of audio data. This method produces a horrible 
+      ** click on each buffer :( Any ideas, how to fix this?
+      */
+      ioctl(SoundFD,AUDIO_DRAIN);
+      write(SoundFD,Buf,SND_BUFSIZE);
+#else
+      /* We'll block here until next DMA buffer becomes free. It happens 
+      ** once per (1<<SND_BITS)/SoundRate seconds.
+      */
+      write(SoundFD,Buf,SND_BUFSIZE);
+#endif
+    }
+  }
+
+  return(0);
+}
+
+/** InitSound() **********************************************/
+/** Initialize DSP. Returns Rate on success, 0 otherwise.   **/
+/** Mode is 0 to skip initialization (will be silent).      **/
+/*************************************************************/
+int InitSound(int Rate,int Verbose)
+{
+  /* If sound was initialized, kill it */
+  TrashSound();
+
+  /* Silence requested */
+  if(Rate<=0) return(0);
+
+  /* Synthesis rate should be at least 8kHz */
+  if(Rate<8192) Rate=44100;
+
+  /* Initialize things */
+  SoundRate = 0;
+  SoundFD   = -1;
+  ThreadID  = 0;
+  Suspended = 0;
+
+  /* Set driver functions */
+  SndDriver.SetSound    = UnixSetSound;
+  SndDriver.Drum        = UnixDrum;
+  SndDriver.SetChannels = UnixSetChannels;
+  SndDriver.Sound       = UnixSound;
+  SndDriver.SetWave     = UnixSetWave;
+
+  /* Open sound device */
+  if(Verbose) puts("Starting sound server:");
+  if(!(Rate=OpenSoundDevice(Rate,Verbose))) return(0);
+
+  /* Create DSPLoop() thread */
+  if(Verbose) printf("  Creating thread...");
+  if(pthread_create(&ThreadID,0,DSPLoop,0))
+  { if(Verbose) puts("FAILED");return(0); }
+
+  /* Detach the thread */
+  pthread_detach(ThreadID);
+
+  /* Done */
+  if(Verbose) puts("OK");
+  return(SoundRate=Rate);
+}
+
+/** TrashSound() *********************************************/
+/** Shut DSP down.                                          **/
+/*************************************************************/
+void TrashSound(void)
+{
+  StopSound();
+  printf("%s: Kill thread...\n", __func__);
+  if(ThreadID)    pthread_cancel(ThreadID);
+  printf("%s: close /dev/xxx ...\n", __func__);
+  if(SoundFD!=-1) close(SoundFD);
+
+  SoundRate = 0;
+  SoundFD   = -1;
+  ThreadID  = 0;
+}
+
+/** UnixSound() **********************************************/
+/** Generate sound of given frequency (Hz) and volume       **/
+/** (0..255) via given channel.                             **/
+/*************************************************************/
+void UnixSound(int Channel,int NewFreq,int NewVolume)
+{
+  if((Channel<0)||(Channel>=SND_CHANNELS)) return;
+  if(!NewVolume||!NewFreq) { NewVolume=0;NewFreq=0; }
+
+  CH[Channel].Volume = NewVolume;
+  CH[Channel].Freq   = NewFreq;
+}
+
+/** UnixSetChannels() ****************************************/
+/** Set master volume (0..255) and turn channels on/off.    **/
+/** Each bit in Toggle corresponds to a channel (1=on).     **/
+/*************************************************************/
+void UnixSetChannels(int MVolume,int MSwitch)
+{
+  /* Set new MasterSwitch value */
+  MasterSwitch = MSwitch;
+  MasterVolume = MVolume;
+}
+
+/** UnixSetSound() *******************************************/
+/** Set sound type (SND_NOISE/SND_MELODIC) for a given      **/
+/** channel.                                                **/
+/*************************************************************/
+void UnixSetSound(int Channel,int NewType)
+{
+  if((Channel<0)||(Channel>=SND_CHANNELS)) return;
+  CH[Channel].Type = NewType;
+}
+
+
+/** UnixSetWave() ********************************************/
+/** Set waveform for a given channel. The channel will be   **/
+/** marked with sound type SND_WAVE. Set Rate=0 if you want **/
+/** waveform to be an instrument or set it to the waveform  **/
+/** own playback rate.                                      **/
+/*************************************************************/
+void UnixSetWave(int Channel,signed char *Data,int Length,int Rate)
+{
+  if((Channel<0)||(Channel>=SND_CHANNELS)||(Length<=0)) return;
+
+  CH[Channel].Type   = SND_WAVE;
+  CH[Channel].Length = Length;
+  CH[Channel].Rate   = Rate;
+  CH[Channel].Pos    = 0;
+  CH[Channel].Count  = 0;
+  CH[Channel].Data   = Data;
+}
+
+/** UnixDrum() ***********************************************/
+/** Hit a drum of a given type with given force.            **/
+/*************************************************************/
+void UnixDrum(int Type,int Force)
+{
+  /* This function is currently empty */
+}
+
+#endif /* UNIX */

+ 497 - 0
src/apu/Sound.c

@@ -0,0 +1,497 @@
+/** EMULib Emulation Library *********************************/
+/**                                                         **/
+/**                          Sound.c                        **/
+/**                                                         **/
+/** This file file implements core part of the sound API    **/
+/** and functions needed to log soundtrack into a MIDI      **/
+/** file. See Sound.h for declarations.                     **/
+/**                                                         **/
+/** Copyright (C) Marat Fayzullin 1996-2007                 **/
+/**     You are not allowed to distribute this software     **/
+/**     commercially. Please, notify me, if you make any    **/
+/**     changes to this file.                               **/
+/*************************************************************/
+#include "Sound.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef UNIX
+#include <unistd.h>
+#endif
+
+typedef unsigned char byte;
+typedef unsigned short word;
+
+struct SndDriverStruct SndDriver =
+{
+  (void (*)(int,int))0,
+  (void (*)(int,int))0,
+  (void (*)(int,int))0,
+  (void (*)(int,int,int))0,
+  (void (*)(int,const signed char *,int,int))0,
+  (const signed char *(*)(int))0
+};
+
+static const struct { byte Note;word Wheel; } Freqs[4096] =
+{
+#include "MIDIFreq.h"
+};
+
+static const int Programs[5] =
+{
+  80,  /* SND_MELODIC/SND_RECTANGLE */
+  80,  /* SND_TRIANGLE */
+  122, /* SND_NOISE */
+  122, /* SND_PERIODIC */
+  80   /* SND_WAVE */
+};
+
+static struct
+{
+  int Type;
+  int Note;
+  int Pitch;
+  int Level;
+} CH[MIDI_CHANNELS] =
+{
+  { -1,-1,-1,-1 },
+  { -1,-1,-1,-1 },
+  { -1,-1,-1,-1 },
+  { -1,-1,-1,-1 },
+  { -1,-1,-1,-1 },
+  { -1,-1,-1,-1 },
+  { -1,-1,-1,-1 },
+  { -1,-1,-1,-1 },
+  { -1,-1,-1,-1 },
+  { -1,-1,-1,-1 },
+  { -1,-1,-1,-1 },
+  { -1,-1,-1,-1 },
+  { -1,-1,-1,-1 },
+  { -1,-1,-1,-1 },
+  { -1,-1,-1,-1 },
+  { -1,-1,-1,-1 }
+};
+
+static const char *LogName  = 0;
+static int  Logging   = MIDI_OFF;
+static int  TickCount = 0;
+static int  LastMsg   = -1;
+static int  DrumOn    = 0;
+static FILE *MIDIOut  = 0;
+
+static void MIDISound(int Channel,int Freq,int Volume);
+static void MIDISetSound(int Channel,int Type);
+static void MIDIDrum(int Type,int Force);
+static void MIDIMessage(byte D0,byte D1,byte D2);
+static void NoteOn(byte Channel,byte Note,byte Level);
+static void NoteOff(byte Channel);
+static void WriteDelta(void);
+static void WriteTempo(int Freq);
+
+/** SHIFT() **************************************************/
+/** Make MIDI channel#10 last, as it is normally used for   **/
+/** percussion instruments only and doesn't sound nice.     **/
+/*************************************************************/
+#define SHIFT(Ch) (Ch==15? 9:Ch>8? Ch+1:Ch)
+
+/** Sound() **************************************************/
+/** Generate sound of given frequency (Hz) and volume       **/
+/** (0..255) via given channel. Setting Freq=0 or Volume=0  **/
+/** turns sound off.                                        **/
+/*************************************************************/
+void Sound(int Channel,int Freq,int Volume)
+{
+  if(Channel<0) return;
+  Freq   = Freq<0? 0:Freq;
+  Volume = Volume<0? 0:Volume>255? 255:Volume;
+
+  /* Call sound driver if present */
+  if(SndDriver.Sound) (*SndDriver.Sound)(Channel,Freq,Volume);
+
+  /* Log sound to MIDI file */
+  MIDISound(Channel,Freq,Volume);
+}
+
+/** Drum() ***************************************************/
+/** Hit a drum of given type with given force (0..255).     **/
+/** MIDI drums can be used by ORing their numbers with      **/
+/** SND_MIDI.                                               **/
+/*************************************************************/
+void Drum(int Type,int Force)
+{
+  Force = Force<0? 0:Force>255? 255:Force;
+
+  if(SndDriver.Drum) (*SndDriver.Drum)(Type,Force);
+
+  /* Log drum to MIDI file */
+  MIDIDrum(Type,Force);
+}
+
+/** SetSound() ***********************************************/
+/** Set sound type at a given channel. MIDI instruments can **/
+/** be set directly by ORing their numbers with SND_MIDI.   **/
+/*************************************************************/
+void SetSound(int Channel,int Type)
+{
+  if(Channel<0) return;
+
+  if(SndDriver.SetSound) (*SndDriver.SetSound)(Channel,Type);
+
+  /* Log instrument change to MIDI file */
+  MIDISetSound(Channel,Type);
+}
+
+/** SetChannels() ********************************************/
+/** Set master volume (0..255) and switch channels on/off.  **/
+/** Each channel N has corresponding bit 2^N in Switch. Set **/
+/** or reset this bit to turn the channel on or off.        **/ 
+/*************************************************************/
+void SetChannels(int Volume,int Switch)
+{
+  Volume = Volume<0? 0:Volume>255? 255:Volume;
+
+  if(SndDriver.SetChannels) (*SndDriver.SetChannels)(Volume,Switch);
+}
+
+/** SetWave() ************************************************/
+/** Set waveform for a given channel. The channel will be   **/
+/** marked with sound type SND_WAVE. Set Rate=0 if you want **/
+/** waveform to be an instrument or set it to the waveform  **/
+/** own playback rate.                                      **/
+/*************************************************************/
+void SetWave(int Channel,const signed char *Data,int Length,int Rate)
+{
+  if((Channel<0)||(Length<=0)) return;
+
+  if(SndDriver.SetWave) (*SndDriver.SetWave)(Channel,Data,Length,Rate);
+
+  /* Log instrument change to MIDI file */
+  MIDISetSound(Channel,Rate? -1:SND_MELODIC);
+}
+
+/** GetWave() ************************************************/
+/** Get current read position for the buffer set with the   **/
+/** SetWave() call. Returns 0 if no buffer has been set, or **/
+/** if there is no playrate set (i.e. wave is instrument).  **/
+/*************************************************************/
+const signed char *GetWave(int Channel)
+{
+  return(SndDriver.GetWave? (*SndDriver.GetWave)(Channel):0);
+}
+
+/** InitMIDI() ***********************************************/
+/** Initialize soundtrack logging into MIDI file FileName.  **/
+/** Repeated calls to InitMIDI() will close current MIDI    **/
+/** file and continue logging into a new one.               **/ 
+/*************************************************************/
+void InitMIDI(const char *FileName)
+{
+  int WasLogging;
+
+  /* Must pass a name! */
+  if(!FileName) return;
+
+  /* Memorize logging status */
+  WasLogging=Logging;
+
+  /* If MIDI logging in progress, close current file */
+  if(MIDIOut) TrashMIDI();
+
+  /* Set log file name and ticks/second parameter, no logging yet */
+  LogName   = FileName;
+  Logging   = MIDI_OFF;
+  LastMsg   = -1;
+  TickCount = 0;
+  MIDIOut   = 0;
+  DrumOn    = 0;
+
+  /* If was logging, restart */
+  if(WasLogging) MIDILogging(MIDI_ON);
+}
+
+/** TrashMIDI() **********************************************/
+/** Finish logging soundtrack and close the MIDI file.      **/
+/*************************************************************/
+void TrashMIDI(void)
+{
+  long Length;
+  int J;
+
+  /* If not logging, drop out */
+  if(!MIDIOut) return;
+  /* Turn sound off */
+  for(J=0;J<MIDI_CHANNELS;J++) NoteOff(J);
+  /* End of track */
+  MIDIMessage(0xFF,0x2F,0x00);
+  /* Put track length in file */
+  fseek(MIDIOut,0,SEEK_END);
+  Length=ftell(MIDIOut)-22;
+  fseek(MIDIOut,18,SEEK_SET);
+  fputc((Length>>24)&0xFF,MIDIOut);
+  fputc((Length>>16)&0xFF,MIDIOut);
+  fputc((Length>>8)&0xFF,MIDIOut);
+  fputc(Length&0xFF,MIDIOut);
+
+  /* Done logging */
+  fclose(MIDIOut);
+  Logging   = MIDI_OFF;
+  LastMsg   = -1;
+  TickCount = 0;
+  MIDIOut   = 0;
+}
+
+/** MIDILogging() ********************************************/
+/** Turn soundtrack logging on/off and return its current   **/
+/** status. Possible values of Switch are MIDI_OFF (turn    **/
+/** logging off), MIDI_ON (turn logging on), MIDI_TOGGLE    **/
+/** (toggle logging), and MIDI_QUERY (just return current   **/
+/** state of logging).                                      **/
+/*************************************************************/
+int MIDILogging(int Switch)
+{
+  static const char MThd[] = "MThd\0\0\0\006\0\0\0\1";
+                           /* ID  DataLen   Fmt Trks */
+  static const char MTrk[] = "MTrk\0\0\0\0";
+                           /* ID  TrkLen   */
+  int J,I;
+
+  /* Toggle logging if requested */
+  if(Switch==MIDI_TOGGLE) Switch=!Logging;
+
+  if((Switch==MIDI_ON)||(Switch==MIDI_OFF))
+    if(Switch^Logging)
+    {
+      /* When turning logging off, silence all channels */
+      if(!Switch&&MIDIOut)
+        for(J=0;J<MIDI_CHANNELS;J++) NoteOff(J);
+
+      /* When turning logging on, open MIDI file */
+      if(Switch&&!MIDIOut&&LogName)
+      {
+        /* No messages have been sent yet */
+        LastMsg=-1;
+
+        /* Clear all storage */
+        for(J=0;J<MIDI_CHANNELS;J++)
+          CH[J].Note=CH[J].Pitch=CH[J].Level=-1;
+
+        /* Open new file and write out the header */
+        MIDIOut=fopen(LogName,"wb");
+        if(!MIDIOut) return(MIDI_OFF);
+        if(fwrite(MThd,1,12,MIDIOut)!=12)
+        { fclose(MIDIOut);MIDIOut=0;return(MIDI_OFF); }
+        fputc((MIDI_DIVISIONS>>8)&0xFF,MIDIOut);
+        fputc(MIDI_DIVISIONS&0xFF,MIDIOut);
+        if(fwrite(MTrk,1,8,MIDIOut)!=8)
+        { fclose(MIDIOut);MIDIOut=0;return(MIDI_OFF); }
+
+        /* Write out the tempo */
+        WriteTempo(MIDI_DIVISIONS);
+      }
+
+      /* Turn logging off on failure to open MIDIOut */
+      if(!MIDIOut) Switch=MIDI_OFF;
+
+      /* Assign new switch value */
+      Logging=Switch;
+
+      /* If switching logging on... */
+      if(Switch)
+      {
+        /* Start logging without a pause */
+        TickCount=0;
+
+        /* Write instrument changes */
+        for(J=0;J<MIDI_CHANNELS;J++)
+          if((CH[J].Type>=0)&&(CH[J].Type&0x10000))
+          {
+            I=CH[J].Type&~0x10000;
+            CH[J].Type=-1;
+            MIDISetSound(J,I);
+          }
+      }
+    }
+
+  /* Return current logging status */
+  return(Logging);
+}
+
+/** MIDITicks() **********************************************/
+/** Log N 1ms MIDI ticks.                                   **/
+/*************************************************************/
+void MIDITicks(int N)
+{
+  if(Logging&&MIDIOut&&(N>0)) TickCount+=N;
+}
+
+/** MIDISound() **********************************************/
+/** Set sound frequency (Hz) and volume (0..255) for a      **/
+/** given channel.                                          **/
+/*************************************************************/
+void MIDISound(int Channel,int Freq,int Volume)
+{
+  int MIDIVolume,MIDINote,MIDIWheel;
+
+  /* If logging off, file closed, or invalid channel, drop out */
+  if(!Logging||!MIDIOut||(Channel>=MIDI_CHANNELS-1)||(Channel<0)) return;
+  /* Frequency must be in range */
+  if((Freq<MIDI_MINFREQ)||(Freq>MIDI_MAXFREQ)) Freq=0;
+  /* Volume must be in range */
+  if(Volume<0) Volume=0; else if(Volume>255) Volume=255;
+  /* Instrument number must be valid */
+  if(CH[Channel].Type<0) Freq=0;
+
+  if(!Volume||!Freq) NoteOff(Channel);
+  else
+  {
+    /* SND_TRIANGLE is twice quieter than SND_MELODIC */
+    if(CH[Channel].Type==SND_TRIANGLE) Volume=(Volume+1)/2;
+    /* Compute MIDI note parameters */
+    MIDIVolume = (127*Volume+128)/255;
+    MIDINote   = Freqs[Freq/3].Note;
+    MIDIWheel  = Freqs[Freq/3].Wheel;
+
+    /* Play new note */
+    NoteOn(Channel,MIDINote,MIDIVolume);
+
+    /* Change pitch */
+    if(CH[Channel].Pitch!=MIDIWheel)
+    {
+      MIDIMessage(0xE0+SHIFT(Channel),MIDIWheel&0x7F,(MIDIWheel>>7)&0x7F);
+      CH[Channel].Pitch=MIDIWheel;
+    }
+  }
+}
+
+/** MIDISetSound() *******************************************/
+/** Set sound type for a given channel.                     **/
+/*************************************************************/
+void MIDISetSound(int Channel,int Type)
+{
+  /* Channel must be valid */
+  if((Channel>=MIDI_CHANNELS-1)||(Channel<0)) return;
+
+  /* If instrument changed... */
+  if(CH[Channel].Type!=Type)
+  {
+    /* If logging off or file closed, drop out */
+    if(!Logging||!MIDIOut) CH[Channel].Type=Type|0x10000;
+    else
+    {
+      CH[Channel].Type=Type;
+      if(Type<0) NoteOff(Channel);
+      else
+      {
+        Type=Type&SND_MIDI? (Type&0x7F):Programs[Type%5];
+        MIDIMessage(0xC0+SHIFT(Channel),Type,255);
+      }
+    }
+  }
+}
+
+/** MIDIDrum() ***********************************************/
+/** Hit a drum of a given type with given force.            **/
+/*************************************************************/
+void MIDIDrum(int Type,int Force)
+{
+  /* If logging off or invalid channel, drop out */
+  if(!Logging||!MIDIOut) return;
+  /* The only non-MIDI drum is a click ("Low Wood Block") */
+  Type=Type&DRM_MIDI? (Type&0x7F):77;
+  /* Release previous drum */
+  if(DrumOn) MIDIMessage(0x89,DrumOn,127);
+  /* Hit next drum */
+  if(Type) MIDIMessage(0x99,Type,(Force&0xFF)/2);
+  DrumOn=Type;
+}
+
+/** MIDIMessage() ********************************************/
+/** Write out a MIDI message.                               **/
+/*************************************************************/
+void MIDIMessage(byte D0,byte D1,byte D2)
+{
+  /* Write number of ticks that passed */
+  WriteDelta();
+
+  /* Write out the command */
+  if(D0!=LastMsg) { LastMsg=D0;fputc(D0,MIDIOut); }
+
+  /* Write out the arguments */
+  if(D1<128)
+  {
+    fputc(D1,MIDIOut);
+    if(D2<128) fputc(D2,MIDIOut);
+  }
+}
+
+/** NoteOn() *************************************************/
+/** Turn on a note on a given channel.                      **/
+/*************************************************************/
+void NoteOn(byte Channel,byte Note,byte Level)
+{
+  Note  = Note>0x7F? 0x7F:Note;
+  Level = Level>0x7F? 0x7F:Level;
+
+  if((CH[Channel].Note!=Note)||(CH[Channel].Level!=Level))
+  {
+    if(CH[Channel].Note>=0) NoteOff(Channel);
+    MIDIMessage(0x90+SHIFT(Channel),Note,Level);
+    CH[Channel].Note=Note;
+    CH[Channel].Level=Level;
+  }
+}
+
+/** NoteOff() ************************************************/
+/** Turn off a note on a given channel.                     **/
+/*************************************************************/
+void NoteOff(byte Channel)
+{
+  if(CH[Channel].Note>=0)
+  {
+    MIDIMessage(0x80+SHIFT(Channel),CH[Channel].Note,127);
+    CH[Channel].Note=-1;
+  }
+}
+
+/** WriteDelta() *********************************************/
+/** Write number of ticks since the last MIDI command and   **/
+/** reset the counter.                                      **/
+/*************************************************************/
+void WriteDelta(void)
+{
+  if(TickCount<128) fputc(TickCount,MIDIOut);
+  else
+  {
+    if(TickCount<128*128)
+    {
+      fputc((TickCount>>7)|0x80,MIDIOut);
+      fputc(TickCount&0x7F,MIDIOut);
+    }
+    else
+    {
+      fputc(((TickCount>>14)&0x7F)|0x80,MIDIOut);
+      fputc(((TickCount>>7)&0x7F)|0x80,MIDIOut);
+      fputc(TickCount&0x7F,MIDIOut);
+    }
+  }
+
+  TickCount=0;
+}
+
+/** WriteTempo() *********************************************/
+/** Write out soundtrack tempo (Hz).                        **/
+/*************************************************************/
+void WriteTempo(int Freq)
+{
+  int J;
+
+  J=500000*MIDI_DIVISIONS*2/Freq;
+  WriteDelta();
+  fputc(0xFF,MIDIOut);
+  fputc(0x51,MIDIOut);
+  fputc(0x03,MIDIOut);
+  fputc((J>>16)&0xFF,MIDIOut);
+  fputc((J>>8)&0xFF,MIDIOut);
+  fputc(J&0xFF,MIDIOut);
+}

+ 837 - 0
src/corecpu/Codes.h

@@ -0,0 +1,837 @@
+ /** M6502: portable 6502 emulator ****************************/ 
+ /**                                                         **/ 
+ /**                          Codes.h                        **/ 
+ /**                                                         **/ 
+ /** This file contains implementation for the main table of **/ 
+ /** 6502 commands. It is included from 6502.c.              **/ 
+ /**                                                         **/ 
+ /** Copyright (C) Marat Fayzullin 1996-2002                 **/ 
+ /**               Alex Krasivsky  1996                      **/ 
+ /**     You are not allowed to distribute this software     **/ 
+ /**     commercially. Please, notify me, if you make any    **/ 
+ /**     changes to this file.                               **/ 
+ /*************************************************************/ 
+/*
+ *  $LastChangedDate: 2007-05-31 18:01:41 +0200 (jeu, 31 mai 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/Codes.h $
+ *  $Revision: 57 $
+ */
+ 
+case 0x10:
+if (R->P & N_FLAG)
+    R->PC.W++;
+else
+{
+    M_JR;
+} break;        /* BPL * REL */
+
+case 0x30:
+if (R->P & N_FLAG)
+{
+    M_JR;
+}
+else
+    R->PC.W++;
+break;            /* BMI * REL */
+
+case 0xD0:
+if (R->P & Z_FLAG)
+    R->PC.W++;
+else
+{
+    M_JR;
+} break;        /* BNE * REL */
+
+case 0xF0:
+if (R->P & Z_FLAG)
+{
+    M_JR;
+}
+else
+    R->PC.W++;
+break;            /* BEQ * REL */
+
+case 0x90:
+if (R->P & C_FLAG)
+    R->PC.W++;
+else
+{
+    M_JR;
+} break;        /* BCC * REL */
+
+case 0xB0:
+if (R->P & C_FLAG)
+{
+    M_JR;
+}
+else
+    R->PC.W++;
+break;            /* BCS * REL */
+
+case 0x50:
+if (R->P & V_FLAG)
+    R->PC.W++;
+else
+{
+    M_JR;
+} break;        /* BVC * REL */
+
+case 0x70:
+if (R->P & V_FLAG)
+{
+    M_JR;
+}
+else
+    R->PC.W++;
+break;            /* BVS * REL */
+
+
+ /* RTI */ 
+case 0x40:
+
+M_POP(R->P);
+R->P |= R_FLAG;
+M_POP(R->PC.B.l);
+M_POP(R->PC.B.h);
+
+break;
+
+
+ /* RTS */ 
+case 0x60:
+
+M_POP(R->PC.B.l);
+M_POP(R->PC.B.h);
+R->PC.W++;
+break;
+
+
+ /* JSR $ssss ABS */ 
+case 0x20:
+
+K.B.l = Op6502(R->PC.W++);
+
+K.B.h = Op6502(R->PC.W);
+
+M_PUSH(R->PC.B.h);
+
+M_PUSH(R->PC.B.l);
+
+R->PC = K;
+break;
+
+
+ /* JMP $ssss ABS */ 
+case 0x4C:
+M_LDWORD(K);
+R->PC = K;
+break;
+
+
+ /* JMP ($ssss) ABDINDIR */ 
+case 0x6C:
+
+M_LDWORD(K);
+
+R->PC.B.l = Rd6502(K.W);
+
+K.B.l++;
+
+R->PC.B.h = Rd6502(K.W);
+
+break;
+
+
+ /* BRK */ 
+case 0x00:
+
+R->PC.W++;
+
+M_PUSH(R->PC.B.h);
+M_PUSH(R->PC.B.l);
+
+M_PUSH(R->P | B_FLAG);
+
+R->P = (R->P | I_FLAG) & ~D_FLAG;
+
+R->PC.B.l = Rd6502(0xFFFE);
+
+R->PC.B.h = Rd6502(0xFFFF);
+
+break;
+
+
+ /* CLI */ 
+case 0x58:
+
+if ((R->IRequest != INT_NONE) && (R->P & I_FLAG))
+    
+{
+    
+       R->AfterCLI = 1;
+    
+       R->IBackup = R->ICount;
+    
+       R->ICount = 1;
+    
+} 
+R->P &= ~I_FLAG;
+
+break;
+
+
+ /* PLP */ 
+case 0x28:
+
+M_POP(I);
+
+if ((R->IRequest != INT_NONE) && ((I ^ R->P) & ~I & I_FLAG))
+    
+{
+    
+       R->AfterCLI = 1;
+    
+       R->IBackup = R->ICount;
+    
+       R->ICount = 1;
+    
+} 
+R->P = I | R_FLAG | B_FLAG;
+
+break;
+
+
+case 0x08:
+M_PUSH(R->P);
+break;            /* PHP */
+
+case 0x18:
+R->P &= ~C_FLAG;
+break;            /* CLC */
+
+case 0xB8:
+R->P &= ~V_FLAG;
+break;            /* CLV */
+
+case 0xD8:
+R->P &= ~D_FLAG;
+break;            /* CLD */
+
+case 0x38:
+R->P |= C_FLAG;
+break;            /* SEC */
+
+case 0xF8:
+R->P |= D_FLAG;
+break;            /* SED */
+
+case 0x78:
+R->P |= I_FLAG;
+break;            /* SEI */
+
+case 0x48:
+M_PUSH(R->A);
+break;            /* PHA */
+
+case 0x68:
+M_POP(R->A);
+M_FL(R->A);
+break;            /* PLA */
+
+case 0x98:
+R->A = R->Y;
+M_FL(R->A);
+break;            /* TYA */
+
+case 0xA8:
+R->Y = R->A;
+M_FL(R->Y);
+break;            /* TAY */
+
+case 0xC8:
+R->Y++;
+M_FL(R->Y);
+break;            /* INY */
+
+case 0x88:
+R->Y--;
+M_FL(R->Y);
+break;            /* DEY */
+
+case 0x8A:
+R->A = R->X;
+M_FL(R->A);
+break;            /* TXA */
+
+case 0xAA:
+R->X = R->A;
+M_FL(R->X);
+break;            /* TAX */
+
+case 0xE8:
+R->X++;
+M_FL(R->X);
+break;            /* INX */
+
+case 0xCA:
+R->X--;
+M_FL(R->X);
+break;            /* DEX */
+
+default:          /* In CMOS version unrecognized instructions are concidered as 
+                     NOP */
+case 0xEA:
+break;            /* NOP */
+
+case 0x9A:
+R->S = R->X;
+break;            /* TXS */
+
+case 0xBA:
+R->X = R->S;
+M_FL(R->X);
+break;            /* TSX */
+
+
+case 0x24:
+MR_Zp(I);
+M_BIT(I);
+break;            /* BIT $ss ZP */
+
+case 0x2C:
+MR_Ab(I);
+M_BIT(I);
+break;            /* BIT $ssss ABS */
+
+
+case 0x05:
+MR_Zp(I);
+M_ORA(I);
+break;            /* ORA $ss ZP */
+
+case 0x06:
+MM_Zp(M_ASL);
+break;            /* ASL $ss ZP */
+
+case 0x25:
+MR_Zp(I);
+M_AND(I);
+break;            /* AND $ss ZP */
+
+case 0x26:
+MM_Zp(M_ROL);
+break;            /* ROL $ss ZP */
+
+case 0x45:
+MR_Zp(I);
+M_EOR(I);
+break;            /* EOR $ss ZP */
+
+case 0x46:
+MM_Zp(M_LSR);
+break;            /* LSR $ss ZP */
+
+case 0x65:
+MR_Zp(I);
+M_ADC(I);
+break;            /* ADC $ss ZP */
+
+case 0x66:
+MM_Zp(M_ROR);
+break;            /* ROR $ss ZP */
+
+case 0x84:
+MW_Zp(R->Y);
+break;            /* STY $ss ZP */
+
+case 0x85:
+MW_Zp(R->A);
+break;            /* STA $ss ZP */
+
+case 0x86:
+MW_Zp(R->X);
+break;            /* STX $ss ZP */
+
+case 0xA4:
+MR_Zp(R->Y);
+M_FL(R->Y);
+break;            /* LDY $ss ZP */
+
+case 0xA5:
+MR_Zp(R->A);
+M_FL(R->A);
+break;            /* LDA $ss ZP */
+
+case 0xA6:
+MR_Zp(R->X);
+M_FL(R->X);
+break;            /* LDX $ss ZP */
+
+case 0xC4:
+MR_Zp(I);
+M_CMP(R->Y, I);
+break;            /* CPY $ss ZP */
+
+case 0xC5:
+MR_Zp(I);
+M_CMP(R->A, I);
+break;            /* CMP $ss ZP */
+
+case 0xC6:
+MM_Zp(M_DEC);
+break;            /* DEC $ss ZP */
+
+case 0xE4:
+MR_Zp(I);
+M_CMP(R->X, I);
+break;            /* CPX $ss ZP */
+
+case 0xE5:
+MR_Zp(I);
+M_SBC(I);
+break;            /* SBC $ss ZP */
+
+case 0xE6:
+MM_Zp(M_INC);
+break;            /* INC $ss ZP */
+
+
+case 0x0D:
+MR_Ab(I);
+M_ORA(I);
+break;            /* ORA $ssss ABS */
+
+case 0x0E:
+MM_Ab(M_ASL);
+break;            /* ASL $ssss ABS */
+
+case 0x2D:
+MR_Ab(I);
+M_AND(I);
+break;            /* AND $ssss ABS */
+
+case 0x2E:
+MM_Ab(M_ROL);
+break;            /* ROL $ssss ABS */
+
+case 0x4D:
+MR_Ab(I);
+M_EOR(I);
+break;            /* EOR $ssss ABS */
+
+case 0x4E:
+MM_Ab(M_LSR);
+break;            /* LSR $ssss ABS */
+
+case 0x6D:
+MR_Ab(I);
+M_ADC(I);
+break;            /* ADC $ssss ABS */
+
+case 0x6E:
+MM_Ab(M_ROR);
+break;            /* ROR $ssss ABS */
+
+case 0x8C:
+MW_Ab(R->Y);
+break;            /* STY $ssss ABS */
+
+case 0x8D:
+MW_Ab(R->A);
+break;            /* STA $ssss ABS */
+
+case 0x8E:
+MW_Ab(R->X);
+break;            /* STX $ssss ABS */
+
+case 0xAC:
+MR_Ab(R->Y);
+M_FL(R->Y);
+break;            /* LDY $ssss ABS */
+
+case 0xAD:
+MR_Ab(R->A);
+M_FL(R->A);
+break;            /* LDA $ssss ABS */
+
+case 0xAE:
+MR_Ab(R->X);
+M_FL(R->X);
+break;            /* LDX $ssss ABS */
+
+case 0xCC:
+MR_Ab(I);
+M_CMP(R->Y, I);
+break;            /* CPY $ssss ABS */
+
+case 0xCD:
+MR_Ab(I);
+M_CMP(R->A, I);
+break;            /* CMP $ssss ABS */
+
+case 0xCE:
+MM_Ab(M_DEC);
+break;            /* DEC $ssss ABS */
+
+case 0xEC:
+MR_Ab(I);
+M_CMP(R->X, I);
+break;            /* CPX $ssss ABS */
+
+case 0xED:
+MR_Ab(I);
+M_SBC(I);
+break;            /* SBC $ssss ABS */
+
+case 0xEE:
+MM_Ab(M_INC);
+break;            /* INC $ssss ABS */
+
+
+case 0x09:
+MR_Im(I);
+M_ORA(I);
+break;            /* ORA #$ss IMM */
+
+case 0x29:
+MR_Im(I);
+M_AND(I);
+break;            /* AND #$ss IMM */
+
+case 0x49:
+MR_Im(I);
+M_EOR(I);
+break;            /* EOR #$ss IMM */
+
+case 0x69:
+MR_Im(I);
+M_ADC(I);
+break;            /* ADC #$ss IMM */
+
+case 0xA0:
+MR_Im(R->Y);
+M_FL(R->Y);
+break;            /* LDY #$ss IMM */
+
+case 0xA2:
+MR_Im(R->X);
+M_FL(R->X);
+break;            /* LDX #$ss IMM */
+
+case 0xA9:
+MR_Im(R->A);
+M_FL(R->A);
+break;            /* LDA #$ss IMM */
+
+case 0xC0:
+MR_Im(I);
+M_CMP(R->Y, I);
+break;            /* CPY #$ss IMM */
+
+case 0xC9:
+MR_Im(I);
+M_CMP(R->A, I);
+break;            /* CMP #$ss IMM */
+
+case 0xE0:
+MR_Im(I);
+M_CMP(R->X, I);
+break;            /* CPX #$ss IMM */
+
+case 0xE9:
+MR_Im(I);
+M_SBC(I);
+break;            /* SBC #$ss IMM */
+
+
+case 0x15:
+MR_Zx(I);
+M_ORA(I);
+break;            /* ORA $ss,x ZP,x */
+
+case 0x16:
+MM_Zx(M_ASL);
+break;            /* ASL $ss,x ZP,x */
+
+case 0x35:
+MR_Zx(I);
+M_AND(I);
+break;            /* AND $ss,x ZP,x */
+
+case 0x36:
+MM_Zx(M_ROL);
+break;            /* ROL $ss,x ZP,x */
+
+case 0x55:
+MR_Zx(I);
+M_EOR(I);
+break;            /* EOR $ss,x ZP,x */
+
+case 0x56:
+MM_Zx(M_LSR);
+break;            /* LSR $ss,x ZP,x */
+
+case 0x75:
+MR_Zx(I);
+M_ADC(I);
+break;            /* ADC $ss,x ZP,x */
+
+case 0x76:
+MM_Zx(M_ROR);
+break;            /* ROR $ss,x ZP,x */
+
+case 0x94:
+MW_Zx(R->Y);
+break;            /* STY $ss,x ZP,x */
+
+case 0x95:
+MW_Zx(R->A);
+break;            /* STA $ss,x ZP,x */
+
+case 0x96:
+MW_Zy(R->X);
+break;            /* STX $ss,y ZP,y */
+
+case 0xB4:
+MR_Zx(R->Y);
+M_FL(R->Y);
+break;            /* LDY $ss,x ZP,x */
+
+case 0xB5:
+MR_Zx(R->A);
+M_FL(R->A);
+break;            /* LDA $ss,x ZP,x */
+
+case 0xB6:
+MR_Zy(R->X);
+M_FL(R->X);
+break;            /* LDX $ss,y ZP,y */
+
+case 0xD5:
+MR_Zx(I);
+M_CMP(R->A, I);
+break;            /* CMP $ss,x ZP,x */
+
+case 0xD6:
+MM_Zx(M_DEC);
+break;            /* DEC $ss,x ZP,x */
+
+case 0xF5:
+MR_Zx(I);
+M_SBC(I);
+break;            /* SBC $ss,x ZP,x */
+
+case 0xF6:
+MM_Zx(M_INC);
+break;            /* INC $ss,x ZP,x */
+
+
+case 0x19:
+MR_Ay(I);
+M_ORA(I);
+break;            /* ORA $ssss,y ABS,y */
+
+case 0x1D:
+MR_Ax(I);
+M_ORA(I);
+break;            /* ORA $ssss,x ABS,x */
+
+case 0x1E:
+MM_Ax(M_ASL);
+break;            /* ASL $ssss,x ABS,x */
+
+case 0x39:
+MR_Ay(I);
+M_AND(I);
+break;            /* AND $ssss,y ABS,y */
+
+case 0x3D:
+MR_Ax(I);
+M_AND(I);
+break;            /* AND $ssss,x ABS,x */
+
+case 0x3E:
+MM_Ax(M_ROL);
+break;            /* ROL $ssss,x ABS,x */
+
+case 0x59:
+MR_Ay(I);
+M_EOR(I);
+break;            /* EOR $ssss,y ABS,y */
+
+case 0x5D:
+MR_Ax(I);
+M_EOR(I);
+break;            /* EOR $ssss,x ABS,x */
+
+case 0x5E:
+MM_Ax(M_LSR);
+break;            /* LSR $ssss,x ABS,x */
+
+case 0x79:
+MR_Ay(I);
+M_ADC(I);
+break;            /* ADC $ssss,y ABS,y */
+
+case 0x7D:
+MR_Ax(I);
+M_ADC(I);
+break;            /* ADC $ssss,x ABS,x */
+
+case 0x7E:
+MM_Ax(M_ROR);
+break;            /* ROR $ssss,x ABS,x */
+
+case 0x99:
+MW_Ay(R->A);
+break;            /* STA $ssss,y ABS,y */
+
+case 0x9D:
+MW_Ax(R->A);
+break;            /* STA $ssss,x ABS,x */
+
+case 0xB9:
+MR_Ay(R->A);
+M_FL(R->A);
+break;            /* LDA $ssss,y ABS,y */
+
+case 0xBC:
+MR_Ax(R->Y);
+M_FL(R->Y);
+break;            /* LDY $ssss,x ABS,x */
+
+case 0xBD:
+MR_Ax(R->A);
+M_FL(R->A);
+break;            /* LDA $ssss,x ABS,x */
+
+case 0xBE:
+MR_Ay(R->X);
+M_FL(R->X);
+break;            /* LDX $ssss,y ABS,y */
+
+case 0xD9:
+MR_Ay(I);
+M_CMP(R->A, I);
+break;            /* CMP $ssss,y ABS,y */
+
+case 0xDD:
+MR_Ax(I);
+M_CMP(R->A, I);
+break;            /* CMP $ssss,x ABS,x */
+
+case 0xDE:
+MM_Ax(M_DEC);
+break;            /* DEC $ssss,x ABS,x */
+
+case 0xF9:
+MR_Ay(I);
+M_SBC(I);
+break;            /* SBC $ssss,y ABS,y */
+
+case 0xFD:
+MR_Ax(I);
+M_SBC(I);
+break;            /* SBC $ssss,x ABS,x */
+
+case 0xFE:
+MM_Ax(M_INC);
+break;            /* INC $ssss,x ABS,x */
+
+
+case 0x01:
+MR_Ix(I);
+M_ORA(I);
+break;            /* ORA ($ss,x) INDEXINDIR */
+
+case 0x11:
+MR_Iy(I);
+M_ORA(I);
+break;            /* ORA ($ss),y INDIRINDEX */
+
+case 0x21:
+MR_Ix(I);
+M_AND(I);
+break;            /* AND ($ss,x) INDEXINDIR */
+
+case 0x31:
+MR_Iy(I);
+M_AND(I);
+break;            /* AND ($ss),y INDIRINDEX */
+
+case 0x41:
+MR_Ix(I);
+M_EOR(I);
+break;            /* EOR ($ss,x) INDEXINDIR */
+
+case 0x51:
+MR_Iy(I);
+M_EOR(I);
+break;            /* EOR ($ss),y INDIRINDEX */
+
+case 0x61:
+MR_Ix(I);
+M_ADC(I);
+break;            /* ADC ($ss,x) INDEXINDIR */
+
+case 0x71:
+MR_Iy(I);
+M_ADC(I);
+break;            /* ADC ($ss),y INDIRINDEX */
+
+case 0x81:
+MW_Ix(R->A);
+break;            /* STA ($ss,x) INDEXINDIR */
+
+case 0x91:
+MW_Iy(R->A);
+break;            /* STA ($ss),y INDIRINDEX */
+
+case 0xA1:
+MR_Ix(R->A);
+M_FL(R->A);
+break;            /* LDA ($ss,x) INDEXINDIR */
+
+case 0xB1:
+MR_Iy(R->A);
+M_FL(R->A);
+break;            /* LDA ($ss),y INDIRINDEX */
+
+case 0xC1:
+MR_Ix(I);
+M_CMP(R->A, I);
+break;            /* CMP ($ss,x) INDEXINDIR */
+
+case 0xD1:
+MR_Iy(I);
+M_CMP(R->A, I);
+break;            /* CMP ($ss),y INDIRINDEX */
+
+case 0xE1:
+MR_Ix(I);
+M_SBC(I);
+break;            /* SBC ($ss,x) INDEXINDIR */
+
+case 0xF1:
+MR_Iy(I);
+M_SBC(I);
+break;            /* SBC ($ss),y INDIRINDEX */
+
+
+case 0x0A:
+M_ASL(R->A);
+break;            /* ASL a ACC */
+
+case 0x2A:
+M_ROL(R->A);
+break;            /* ROL a ACC */
+
+case 0x4A:
+M_LSR(R->A);
+break;            /* LSR a ACC */
+
+case 0x6A:
+M_ROR(R->A);
+break;            /* ROR a ACC */

+

+ 480 - 0
src/corecpu/Debug.c

@@ -0,0 +1,480 @@
+/** M6502: portable 6502 emulator ****************************/ 
+/**                                                         **/ 
+/**                         Debug.c                         **/ 
+/**                                                         **/ 
+/** This file contains the built-in debugging routine for   **/ 
+/** the 6502 emulator which is called on each 6502 step     **/ 
+/** when Trap!=0.                                           **/ 
+/**                                                         **/ 
+/** Copyright (C) Marat Fayzullin 1996-1997                 **/ 
+/**               Alex Krasivsky  1996                      **/ 
+/**     You are not allowed to distribute this software     **/ 
+/**     commercially. Please, notify me, if you make any    **/ 
+/**     changes to this file.                               **/ 
+/*************************************************************/ 
+/*
+ *  $LastChangedDate: 2007-04-19 18:18:57 +0200 (jeu, 19 avr 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/Debug.c $
+ *  $Revision: 43 $
+ */
+
+#include "M6502.h"
+#ifdef DEBUG
+#include <allegro.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <ppu/ppu.h>
+
+#include <Sound.h>
+
+#define RDWORD(A) (Rd6502(A+1)*256+Rd6502(A))
+
+extern unsigned char *Memory;
+
+void showlastop();
+
+enum Addressing_Modes
+{
+    Ac = 0, Il, Im, Ab, Zp, Zx, Zy, Ax, Ay, Rl, Ix, Iy, In, No
+};
+
+
+
+static byte *mn[] = 
+{
+    "adc ", "and ", "asl ", "bcc ", "bcs ", "beq ", "bit ", "bmi ", 
+        "bne ", "bpl ", "brk", "bvc ", "bvs ", "clc", "cld", "cli", 
+        "clv", "cmp ", "cpx ", "cpy ", "dec ", "dex", "dey", "inx", 
+        "iny", "eor ", "inc ", "jmp ", "jsr ", "lda ", "nop ", "ldx ", 
+        "ldy ", "lsr ", "ora ", "pha", "php", "pla", "plp", "rol ", 
+        "ror ", "rti", "rts", "sbc ", "sta ", "stx ", "sty ", "sec ", 
+        "sed", "sei", "tax", "tay", "txa", "tya", "tsx", "txs" 
+};
+
+
+
+static byte ad[512] = 
+{
+    10, Il, 34, Ix, No, No, No, No, No, No, 34, Zp, 2, Zp, No, No, 
+        36, Il, 34, Im, 2, Ac, No, No, No, No, 34, Ab, 2, Ab, No, No, 
+        9, Rl, 34, Iy, No, No, No, No, No, No, 34, Zx, 2, Zx, No, No, 
+        13, Il, 34, Ay, No, No, No, No, No, No, 34, Ax, 2, Ax, No, No, 
+        28, Ab, 1, Ix, No, No, No, No, 6, Zp, 1, Zp, 39, Zp, No, No, 
+        38, Il, 1, Im, 39, Ac, No, No, 6, Ab, 1, Ab, 39, Ab, No, No, 
+        7, Rl, 1, Iy, No, No, No, No, No, No, 1, Zx, 39, Zx, No, No, 
+        47, Il, 1, Ay, No, No, No, No, No, No, 1, Ax, 39, Ax, No, No, 
+        41, Il, 25, Ix, No, No, No, No, No, No, 25, Zp, 33, Zp, No, No, 
+        35, Il, 25, Im, 33, Ac, No, No, 27, Ab, 25, Ab, 33, Ab, No, No, 
+        11, Rl, 25, Iy, No, No, No, No, No, No, 25, Zx, 33, Zx, No, No, 
+        15, Il, 25, Ay, No, No, No, No, No, No, 25, Ax, 33, Ax, No, No, 
+        42, Il, 0, Ix, No, No, No, No, No, No, 0, Zp, 40, Zp, No, No, 
+        37, Il, 0, Im, 40, Ac, No, No, 27, In, 0, Ab, 40, Ab, No, No, 
+        12, Rl, 0, Iy, No, No, No, No, No, No, 0, Zx, 40, Zx, No, No, 
+        49, Il, 0, Ay, No, No, No, No, No, No, 0, Ax, 40, Ax, No, No, 
+        No, No, 44, Ix, No, No, No, No, 46, Zp, 44, Zp, 45, Zp, No, No, 
+        22, Il, No, No, 52, Il, No, No, 46, Ab, 44, Ab, 45, Ab, No, No, 
+        3, Rl, 44, Iy, No, No, No, No, 46, Zx, 44, Zx, 45, Zy, No, No, 
+        53, Il, 44, Ay, 55, Il, No, No, No, No, 44, Ax, No, No, No, No, 
+        32, Im, 29, Ix, 31, Im, No, No, 32, Zp, 29, Zp, 31, Zp, No, No, 
+        51, Il, 29, Im, 50, Il, No, No, 32, Ab, 29, Ab, 31, Ab, No, No, 
+        4, Rl, 29, Iy, No, No, No, No, 32, Zx, 29, Zx, 31, Zy, No, No, 
+        16, Il, 29, Ay, 54, Il, No, No, 32, Ax, 29, Ax, 31, Ay, No, No, 
+        19, Im, 17, Ix, No, No, No, No, 19, Zp, 17, Zp, 20, Zp, No, No, 
+        24, Il, 17, Im, 21, Il, No, No, 19, Ab, 17, Ab, 20, Ab, No, No, 
+        8, Rl, 17, Iy, No, No, No, No, No, No, 17, Zx, 20, Zx, No, No, 
+        14, Il, 17, Ay, No, No, No, No, No, No, 17, Ax, 20, Ax, No, No, 
+        18, Im, 43, Ix, No, No, No, No, 18, Zp, 43, Zp, 26, Zp, No, No, 
+        23, Il, 43, Im, 30, Il, No, No, 18, Ab, 43, Ab, 26, Ab, No, No, 
+        5, Rl, 43, Iy, No, No, No, No, No, No, 43, Zx, 26, Zx, No, No, 
+        48, Il, 43, Ay, No, No, No, No, No, No, 43, Ax, 26, Ax, No, No 
+};
+
+
+
+/** DAsm() ****************************************************/ 
+/** This function will disassemble a single command and      **/ 
+/** return the number of bytes disassembled.                 **/ 
+/**************************************************************/ 
+int DAsm(char *S, word A) 
+{
+    
+    byte J;
+    
+       word B, OP, TO;
+       
+       
+       B = A;
+       OP = Rd6502(B++) * 2;
+       
+       
+       switch (ad[OP + 1])
+           
+       {
+           
+       case Ac:
+           sprintf(S, "%s a", mn[ad[OP]]);
+           break;
+           
+       case Il:
+           sprintf(S, "%s", mn[ad[OP]]);
+           break;
+           
+           
+       case Rl:
+           J = Rd6502(B++);
+           TO = A + 2 + ((J < 0x80) ? J : (J - 256));
+           
+           sprintf(S, "%s $%04X", mn[ad[OP]], TO);
+           break;
+           
+           
+       case Im:
+           sprintf(S, "%s #$%02X", mn[ad[OP]], Rd6502(B++));
+           break;
+           
+       case Zp:
+           sprintf(S, "%s $%02X", mn[ad[OP]], Rd6502(B++));
+           break;
+           
+       case Zx:
+           sprintf(S, "%s $%02X,x", mn[ad[OP]], Rd6502(B++));
+           break;
+           
+       case Zy:
+           sprintf(S, "%s $%02X,y", mn[ad[OP]], Rd6502(B++));
+           break;
+           
+       case Ix:
+           sprintf(S, "%s ($%02X,x)", mn[ad[OP]], Rd6502(B++));
+           break;
+           
+       case Iy:
+           sprintf(S, "%s ($%02X),y", mn[ad[OP]], Rd6502(B++));
+           break;
+           
+           
+       case Ab:
+           sprintf(S, "%s $%04X", mn[ad[OP]], RDWORD(B));
+           B += 2;
+           break;
+           
+       case Ax:
+           sprintf(S, "%s $%04X,x", mn[ad[OP]], RDWORD(B));
+           B += 2;
+           break;
+           
+       case Ay:
+           sprintf(S, "%s $%04X,y", mn[ad[OP]], RDWORD(B));
+           B += 2;
+           break;
+           
+       case In:
+           sprintf(S, "%s ($%04X)", mn[ad[OP]], RDWORD(B));
+           B += 2;
+           break;
+           
+           
+       default:
+           sprintf(S, ".db $%02X; <Invalid OPcode>", OP / 2);
+           
+       } 
+       return (B - A);
+       
+} 
+
+
+/** Debug6502() **********************************************/ 
+/** This function should exist if DEBUG is #defined. When   **/ 
+/** Trace!=0, it is called after each command executed by   **/ 
+/** the CPU, and given the 6502 registers. Emulation exits  **/ 
+/** if Debug6502() returns 0.                               **/ 
+/*************************************************************/ 
+byte Debug6502(M6502 * R) 
+{
+    static char FA[8] = "NVRBDIZC";    
+    char S[128];
+    byte F;
+    int J, I;
+    
+    DAsm(S, R->PC.W);
+   
+       printf 
+           (
+           "A:%02X  P:%02X  X:%02X  Y:%02X  S:%04X  PC:%04X  Flags:[", 
+           R->A, R->P, R->X, R->Y, R->S + 0x0100, R->PC.W 
+           );
+       
+       
+       for (J = 0, F = R->P; J < 8; J++, F <<= 1)
+           
+           printf("%c", F & 0x80 ? FA[J] : '.');
+       
+       puts("]");
+       
+       
+       printf 
+           (
+           "AT PC: [%02X - %s]   AT SP: [%02X %02X %02X]\n", 
+           Rd6502(R->PC.W), S, 
+           Rd6502(0x0100 + (byte) (R->S + 1)), 
+           Rd6502(0x0100 + (byte) (R->S + 2)), 
+           Rd6502(0x0100 + (byte) (R->S + 3)) 
+           );
+
+
+       sprintf(S, "");         
+       remove_keyboard();
+
+#ifdef USE_SOUND
+       StopSound();
+#endif
+
+       
+       while (1)
+       {
+           printf("\n[Command,'?']-> ");
+           
+           fflush(stdout);
+           fflush(stdin);
+
+
+           fgets(S, 50, stdin);           
+           
+           for (J = 0; S[J] >= ' '; J++)
+               S[J] = toupper(S[J]);         
+           
+           S[J] = '\0';
+           
+           
+           switch (S[0])
+           {
+           case 'H':
+           case '?':
+               puts("\n***** Built-in 6502 Debugger Commands *****");
+               puts("<CR>       : Break at the next instruction");
+               puts("= <addr>   : Break at addr");
+               puts("+ <offset> : Break at PC + offset");
+               puts("t <addr>   : Set PC to addr");
+               puts("c          : Continue without break");
+               puts("j <addr>   : Continue from addr");
+               puts("m <addr>   : Memory dump at addr");
+               puts("d <addr>   : Disassembly at addr");
+               puts("v          : Show ;interrupt vectors");
+               puts("?,h        : Show this help text");
+               puts("r          : Show Register Status");
+               puts("q          : Exit 6502 emulation");
+               
+               puts("----- TI-NES Specific -----");
+               puts("w          : Dump Memory State");
+               puts("o          : Show PPU registers");
+               puts("p <addr>   : Dump PPU memory at addr");
+               puts("a          : Dump all memory to memory.log");
+               puts("s          : Dump sprite table to sprite.log");
+               puts("n <nb>     : Dump name table <nb> to nt.log");
+               puts("z          : Show lastest opcode executed");
+               puts("i          : SpriteTable Dump");
+               puts("g <nb>     : Get sprite <nb> info");
+               break;
+               
+           case '\0':
+               return (1);
+               
+           case 'Z':
+               showlastop();
+               break;
+
+           case 'W':
+               DumpMemoryState(stdout);
+               break;
+               
+          
+            case 'A':
+               {
+                   
+                   FILE * fpDmpMem;
+                   
+                   if ((fpDmpMem = fopen("memory.log", "wb")) != NULL)
+                       
+                   {
+                       
+                      // fwrite(Memory, 1, 0x8000, fpDmpMem);
+                       //fwrite(mLBank, 1, 0x4000, fpDmpMem);
+                       //fwrite(mUBank, 1, 0x4000, fpDmpMem);
+                       
+                       fclose(fpDmpMem);
+                       
+                   } 
+               } 
+               break;
+               
+               
+               
+               
+               
+           case '=':
+               if (strlen(S) >= 2)
+                   
+               {
+                   sscanf(S + 1, "%hX", &(R->Trap));
+                   R->Trace = 0;
+                   return (1);
+               } 
+               break;
+               
+           case '+':
+               if (strlen(S) >= 2)
+                   
+               {
+                   
+                   sscanf(S + 1, "%hX", &(R->Trap));
+                   
+                   R->Trap += R->PC.W;
+                   R->Trace = 0;
+                   
+                   return (1);
+                   
+               } 
+               break;
+               
+           case 'J':
+               if (strlen(S) >= 2)
+                   
+               {
+                   sscanf(S + 1, "%hX", &(R->PC.W));
+                   R->Trace = 0;
+                   return (1);
+               } 
+               break;
+        
+           case 'T':
+               if (strlen(S) >= 2)
+                   
+               {
+                   sscanf(S + 1, "%hX", &(R->PC.W));
+                   R->Trace = 1;
+               } 
+               break;
+
+           case 'C':
+               R->Trap = 0xFFFF;
+               R->Trace = 0;
+               install_keyboard();
+               
+               //ResumeSound();
+    
+               SetSound(0, SND_RECTANGLE);
+               SetSound(1, SND_RECTANGLE);
+               SetSound(2, SND_TRIANGLE);
+               SetSound(3, SND_NOISE);
+               
+               return (1);
+               
+           case 'Q':
+               return (0);
+               
+               
+           case 'V':
+               puts("\n6502 Interrupt Vectors:");
+               printf("[$FFFC] INIT: $%04X\n", Rd6502(0xFFFC) + 256 * Rd6502(0xFFFD));
+               printf("[$FFFE] IRQ:  $%04X\n", Rd6502(0xFFFE) + 256 * Rd6502(0xFFFF));
+               printf("[$FFFA] NMI:  $%04X\n", Rd6502(0xFFFA) + 256 * Rd6502(0xFFFB));
+               break;
+               
+           case 'M':
+               {
+                   word Addr;
+                   
+                   if (strlen(S) > 1)
+                       sscanf(S + 1, "%hX", &Addr);
+                   else
+                       Addr = R->PC.W;
+                   puts("");
+                   for (J = 0; J < 16; J++)
+                   {
+                       printf("%04X: ", Addr);
+                       for (I = 0; I < 16; I++, Addr++)
+                           
+                           printf("%02X ", Rd6502(Addr));
+                       
+                       printf(" | ");
+                       Addr -= 16;
+                       
+                       for (I = 0; I < 16; I++, Addr++)
+                           
+                           putchar(isprint(Rd6502(Addr)) ? Rd6502(Addr) : '.');
+                       
+                       puts("");
+                       
+                   } 
+               } 
+               break;
+               
+           case 'R':
+               
+               printf 
+                   (
+                   "A:%02X  P:%02X  X:%02X  Y:%02X  S:%04X  PC:%04X  Flags:[", 
+                   R->A, R->P, R->X, R->Y, R->S + 0x0100, R->PC.W 
+                   );
+               
+               
+               for (J = 0, F = R->P; J < 8; J++, F <<= 1)
+                   
+                   printf("%c", F & 0x80 ? FA[J] : '.');
+               
+               puts("]");
+               
+               
+               printf 
+                   (
+                   "AT PC: [%02X - %s]   AT SP: [%02X %02X %02X]\n", 
+                   Rd6502(R->PC.W), S, 
+                   Rd6502(0x0100 + (byte) (R->S + 1)), 
+                   Rd6502(0x0100 + (byte) (R->S + 2)), 
+                   Rd6502(0x0100 + (byte) (R->S + 3)) 
+                   );
+               
+               break;
+               
+               
+           case 'D':
+               
+               {
+                   
+                   word Addr;
+                   
+                   
+                   if (strlen(S) > 1)
+                       sscanf(S + 1, "%hX", &Addr);
+                   else
+                       Addr = R->PC.W;
+                   
+                   puts("");
+                   
+                   for (J = 0; J < 16; J++)
+                       
+                   {
+                       
+                       printf("%04X: ", Addr);
+                       
+                       Addr += DAsm(S, Addr);
+                       
+                       puts(S);
+                       
+                   } 
+               } 
+               break;
+               
+        } 
+    } 
+       
+    /* Continue with emulation */ 
+       return (1);
+       
+} 
+
+
+#endif    /* DEBUG */

+ 317 - 0
src/corecpu/M6502.c

@@ -0,0 +1,317 @@
+/** M6502: portable 6502 emulator ****************************/
+/**                                                         **/
+/**                         M6502.c                         **/
+/**                                                         **/
+/** This file contains implementation for 6502 CPU. Don't   **/
+/** forget to provide Rd6502(), Wr6502(), Loop6502(), and   **/
+/** possibly Op6502() functions to accomodate the emulated  **/
+/** machine's architecture.                                 **/
+/**                                                         **/
+/** Copyright (C) Marat Fayzullin 1996-2002                 **/
+/**               Alex Krasivsky  1996                      **/
+/**     You are not allowed to distribute this software     **/
+/**     commercially. Please, notify me, if you make any    **/   
+/**     changes to this file.                               **/
+/*************************************************************/
+/*
+ *  $LastChangedDate: 2007-04-16 01:55:35 +0200 (lun, 16 avr 2007) $
+ *  $Author: godzil $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/M6502.c $
+ *  $Revision: 39 $
+ */
+ 
+#include "M6502.h"
+#include "Tables.h"
+#include <stdio.h>
+
+/** INLINE ***************************************************/
+/** Different compilers inline C functions differently.     **/
+/*************************************************************/
+#ifdef __GNUC__
+#define INLINE inline
+#else
+#define INLINE static
+#endif
+
+int icount = 0;
+
+/** System-Dependent Stuff ***********************************/
+/** This is system-dependent code put here to speed things  **/
+/** up. It has to stay inlined to be fast.                  **/
+/*************************************************************/
+#ifdef INES
+#define FAST_RDOP
+extern byte *Page[];
+INLINE byte Op6502(register word A) { return(Page[A>>13][A&0x1FFF]); }
+#endif
+
+/** FAST_RDOP ************************************************/
+/** With this #define not present, Rd6502() should perform  **/
+/** the functions of Rd6502().                              **/
+/*************************************************************/
+#ifndef FAST_RDOP
+#define Op6502(A) Rd6502(A)
+#endif
+
+/** Addressing Methods ***************************************/
+/** These macros calculate and return effective addresses.  **/
+/*************************************************************/
+#define MC_Ab(Rg)    M_LDWORD(Rg)
+#define MC_Zp(Rg)       Rg.W=Op6502(R->PC.W++)
+#define MC_Zx(Rg)       Rg.W=(byte)(Op6502(R->PC.W++)+R->X)
+#define MC_Zy(Rg)       Rg.W=(byte)(Op6502(R->PC.W++)+R->Y)
+#define MC_Ax(Rg)    M_LDWORD(Rg);Rg.W+=R->X
+#define MC_Ay(Rg)    M_LDWORD(Rg);Rg.W+=R->Y
+#define MC_Ix(Rg)       K.W=(byte)(Op6502(R->PC.W++)+R->X); \
+            Rg.B.l=Op6502(K.W++);Rg.B.h=Op6502(K.W)
+#define MC_Iy(Rg)       K.W=Op6502(R->PC.W++); \
+            Rg.B.l=Op6502(K.W++);Rg.B.h=Op6502(K.W); \
+            Rg.W+=R->Y
+
+/** Reading From Memory **************************************/
+/** These macros calculate address and read from it.        **/
+/*************************************************************/
+#define MR_Ab(Rg)    MC_Ab(J);Rg=Rd6502(J.W)
+#define MR_Im(Rg)    Rg=Op6502(R->PC.W++)
+#define    MR_Zp(Rg)    MC_Zp(J);Rg=Rd6502(J.W)
+#define MR_Zx(Rg)    MC_Zx(J);Rg=Rd6502(J.W)
+#define MR_Zy(Rg)    MC_Zy(J);Rg=Rd6502(J.W)
+#define    MR_Ax(Rg)    MC_Ax(J);Rg=Rd6502(J.W)
+#define MR_Ay(Rg)    MC_Ay(J);Rg=Rd6502(J.W)
+#define MR_Ix(Rg)    MC_Ix(J);Rg=Rd6502(J.W)
+#define MR_Iy(Rg)    MC_Iy(J);Rg=Rd6502(J.W)
+
+/** Writing To Memory ****************************************/
+/** These macros calculate address and write to it.         **/
+/*************************************************************/
+#define MW_Ab(Rg)    MC_Ab(J);Wr6502(J.W,Rg)
+#define MW_Zp(Rg)    MC_Zp(J);Wr6502(J.W,Rg)
+#define MW_Zx(Rg)    MC_Zx(J);Wr6502(J.W,Rg)
+#define MW_Zy(Rg)    MC_Zy(J);Wr6502(J.W,Rg)
+#define MW_Ax(Rg)    MC_Ax(J);Wr6502(J.W,Rg)
+#define MW_Ay(Rg)    MC_Ay(J);Wr6502(J.W,Rg)
+#define MW_Ix(Rg)    MC_Ix(J);Wr6502(J.W,Rg)
+#define MW_Iy(Rg)    MC_Iy(J);Wr6502(J.W,Rg)
+
+/** Modifying Memory *****************************************/
+/** These macros calculate address and modify it.           **/
+/*************************************************************/
+#define MM_Ab(Cmd)    MC_Ab(J);I=Rd6502(J.W);Cmd(I);Wr6502(J.W,I)
+#define MM_Zp(Cmd)    MC_Zp(J);I=Rd6502(J.W);Cmd(I);Wr6502(J.W,I)
+#define MM_Zx(Cmd)    MC_Zx(J);I=Rd6502(J.W);Cmd(I);Wr6502(J.W,I)
+#define MM_Ax(Cmd)    MC_Ax(J);I=Rd6502(J.W);Cmd(I);Wr6502(J.W,I)
+
+/** Other Macros *********************************************/
+/** Calculating flags, stack, jumps, arithmetics, etc.      **/
+/*************************************************************/
+#define M_FL(Rg)    R->P=(R->P&~(Z_FLAG|N_FLAG))|ZNTable[Rg]
+#define M_LDWORD(Rg)    Rg.B.l=Op6502(R->PC.W++);Rg.B.h=Op6502(R->PC.W++)
+
+#define M_PUSH(Rg)    Wr6502(0x0100|R->S,Rg);R->S--
+#define M_POP(Rg)    R->S++;Rg=Op6502(0x0100|R->S)
+#define M_JR        R->PC.W+=(offset)Op6502(R->PC.W)+1;R->ICount--
+
+#ifdef NO_DECIMAL
+
+#define M_ADC(Rg) \
+  K.W=R->A+Rg+(R->P&C_FLAG); \
+  R->P&=~(N_FLAG|V_FLAG|Z_FLAG|C_FLAG); \
+  R->P|=(~(R->A^Rg)&(R->A^K.B.l)&0x80? V_FLAG:0)| \
+        (K.B.h? C_FLAG:0)|ZNTable[K.B.l]; \
+  R->A=K.B.l
+
+/* Warning! C_FLAG is inverted before SBC and after it */
+#define M_SBC(Rg) \
+  K.W=R->A-Rg-(~R->P&C_FLAG); \
+  R->P&=~(N_FLAG|V_FLAG|Z_FLAG|C_FLAG); \
+  R->P|=((R->A^Rg)&(R->A^K.B.l)&0x80? V_FLAG:0)| \
+        (K.B.h? 0:C_FLAG)|ZNTable[K.B.l]; \
+  R->A=K.B.l
+
+#else /* NO_DECIMAL */
+
+#define M_ADC(Rg) \
+  if(R->P&D_FLAG) \
+  { \
+    K.B.l=(R->A&0x0F)+(Rg&0x0F)+(R->P&C_FLAG); \
+    if(K.B.l>9) K.B.l+=6; \
+    K.B.h=(R->A>>4)+(Rg>>4)+(K.B.l>15? 1:0); \
+    R->A=(K.B.l&0x0F)|(K.B.h<<4); \
+    R->P=(R->P&~C_FLAG)|(K.B.h>15? C_FLAG:0); \
+  } \
+  else \
+  { \
+    K.W=R->A+Rg+(R->P&C_FLAG); \
+    R->P&=~(N_FLAG|V_FLAG|Z_FLAG|C_FLAG); \
+    R->P|=(~(R->A^Rg)&(R->A^K.B.l)&0x80? V_FLAG:0)| \
+          (K.B.h? C_FLAG:0)|ZNTable[K.B.l]; \
+    R->A=K.B.l; \
+  }
+
+/* Warning! C_FLAG is inverted before SBC and after it */
+#define M_SBC(Rg) \
+  if(R->P&D_FLAG) \
+  { \
+    K.B.l=(R->A&0x0F)-(Rg&0x0F)-(~R->P&C_FLAG); \
+    if(K.B.l&0x10) K.B.l-=6; \
+    K.B.h=(R->A>>4)-(Rg>>4)-((K.B.l&0x10)>>4); \
+    if(K.B.h&0x10) K.B.h-=6; \
+    R->A=(K.B.l&0x0F)|(K.B.h<<4); \
+    R->P=(R->P&~C_FLAG)|(K.B.h>15? 0:C_FLAG); \
+  } \
+  else \
+  { \
+    K.W=R->A-Rg-(~R->P&C_FLAG); \
+    R->P&=~(N_FLAG|V_FLAG|Z_FLAG|C_FLAG); \
+    R->P|=((R->A^Rg)&(R->A^K.B.l)&0x80? V_FLAG:0)| \
+          (K.B.h? 0:C_FLAG)|ZNTable[K.B.l]; \
+    R->A=K.B.l; \
+  }
+
+#endif /* NO_DECIMAL */
+
+#define M_CMP(Rg1,Rg2) \
+  K.W=Rg1-Rg2; \
+  R->P&=~(N_FLAG|Z_FLAG|C_FLAG); \
+  R->P|=ZNTable[K.B.l]|(K.B.h? 0:C_FLAG)
+#define M_BIT(Rg) \
+  R->P&=~(N_FLAG|V_FLAG|Z_FLAG); \
+  R->P|=(Rg&(N_FLAG|V_FLAG))|(Rg&R->A? 0:Z_FLAG)
+
+#define M_AND(Rg)    R->A&=Rg;M_FL(R->A)
+#define M_ORA(Rg)    R->A|=Rg;M_FL(R->A)
+#define M_EOR(Rg)    R->A^=Rg;M_FL(R->A)
+#define M_INC(Rg)    Rg++;M_FL(Rg)
+#define M_DEC(Rg)    Rg--;M_FL(Rg)
+
+#define M_ASL(Rg)    R->P&=~C_FLAG;R->P|=Rg>>7;Rg<<=1;M_FL(Rg)
+#define M_LSR(Rg)    R->P&=~C_FLAG;R->P|=Rg&C_FLAG;Rg>>=1;M_FL(Rg)
+#define M_ROL(Rg)    K.B.l=(Rg<<1)|(R->P&C_FLAG); \
+            R->P&=~C_FLAG;R->P|=Rg>>7;Rg=K.B.l; \
+            M_FL(Rg)
+#define M_ROR(Rg)    K.B.l=(Rg>>1)|(R->P<<7); \
+            R->P&=~C_FLAG;R->P|=Rg&C_FLAG;Rg=K.B.l; \
+            M_FL(Rg)
+
+/** Reset6502() **********************************************/
+/** This function can be used to reset the registers before **/
+/** starting execution with Run6502(). It sets registers to **/
+/** their initial values.                                   **/
+/*************************************************************/
+void Reset6502(M6502 *R)
+{
+  R->A=R->X=R->Y=0x00;
+  R->P=Z_FLAG|R_FLAG;
+  R->S=0xFF;
+  R->PC.B.l=Rd6502(0xFFFC);
+  R->PC.B.h=Rd6502(0xFFFD);   
+  R->ICount=R->IPeriod;
+  R->IRequest=INT_NONE;
+  R->AfterCLI=0;
+}
+
+/** Exec6502() ***********************************************/
+/** This function will execute a single 6502 opcode. It     **/
+/** will then return next PC, and current register values   **/
+/** in R.                                                   **/
+/*************************************************************/
+word Exec6502(M6502 *R)
+{
+  register pair J,K;
+  register byte I;
+
+  I=Op6502(R->PC.W++);
+  R->ICount-=Cycles[I];
+  switch(I)
+  {
+#include "Codes.h"
+  }
+
+  /* We are done */
+  return(R->PC.W);
+}
+
+/** Int6502() ************************************************/
+/** This function will generate interrupt of a given type.  **/
+/** INT_NMI will cause a non-maskable interrupt. INT_IRQ    **/
+/** will cause a normal interrupt, unless I_FLAG set in R.  **/
+/*************************************************************/
+void Int6502(M6502 *R,byte Type)
+{
+  register pair J;
+
+  if((Type==INT_NMI)||((Type==INT_IRQ)&&!(R->P&I_FLAG)))
+  {
+    R->ICount-=7;
+    M_PUSH(R->PC.B.h);
+    M_PUSH(R->PC.B.l);
+    M_PUSH(R->P&~B_FLAG);
+    R->P&=~D_FLAG;
+    if(R->IAutoReset&&(Type==R->IRequest)) R->IRequest=INT_NONE;
+    if(Type==INT_NMI) J.W=0xFFFA; else { R->P|=I_FLAG;J.W=0xFFFE; }
+    R->PC.B.l=Rd6502(J.W++);
+    R->PC.B.h=Rd6502(J.W);
+  }
+}
+
+
+/** Run6502() ************************************************/
+/** This function will run 6502 code until Loop6502() call  **/
+/** returns INT_QUIT. It will return the PC at which        **/
+/** emulation stopped, and current register values in R.    **/
+/*************************************************************/
+word Run6502(M6502 *R)
+{
+  register pair J,K;
+  register byte I;
+
+  for(;;)
+  {
+#ifdef DEBUG
+    /* Turn tracing on when reached trap address */
+    if(R->PC.W==R->Trap) R->Trace=1;
+    /* Call single-step debugger, exit if requested */
+    if(R->Trace)
+      if(!Debug6502(R)) return(R->PC.W);
+#endif
+
+    I=Op6502(R->PC.W++);
+    R->ICount-=Cycles[I];
+    
+//#ifdef DEBUG
+//    pushop(I);
+//#endif
+
+    icount++;
+
+    switch(I)
+    {
+#include "Codes.h"
+    }
+
+    /* If cycle counter expired... */
+    if(R->ICount<=0)
+    {
+      /* If we have come after CLI, get INT_? from IRequest */
+      /* Otherwise, get it from the loop handler            */
+      if(R->AfterCLI)
+      {
+        I=R->IRequest;            /* Get pending interrupt     */
+        R->ICount+=R->IBackup-1;  /* Restore the ICount        */
+        R->AfterCLI=0;            /* Done with AfterCLI state  */
+      }
+      else
+      {
+        I=Loop6502(R);            /* Call the periodic handler */
+        R->ICount+=R->IPeriod;    /* Reset the cycle counter   */
+        if(!I) I=R->IRequest;     /* Realize pending interrupt */
+      }
+
+      if(I==INT_QUIT) return(R->PC.W); /* Exit if INT_QUIT     */
+      if(I) Int6502(R,I);              /* Interrupt if needed  */ 
+    }
+  }
+
+  /* Execution stopped */
+  return(R->PC.W);
+}

+ 148 - 0
src/corecpu/M6502.h

@@ -0,0 +1,148 @@
+/** M6502: portable 6502 emulator ****************************/
+/**                                                         **/
+/**                         M6502.h                         **/
+/**                                                         **/
+/** This file contains declarations relevant to emulation   **/
+/** of 6502 CPU.                                            **/
+/**                                                         **/
+/** Copyright (C) Marat Fayzullin 1996-2002                 **/
+/**               Alex Krasivsky  1996                      **/
+/**     You are not allowed to distribute this software     **/
+/**     commercially. Please, notify me, if you make any    **/   
+/**     changes to this file.                               **/
+/*************************************************************/
+/*
+ *  $LastChangedDate: 2007-04-23 18:55:35 +0200 (lun, 23 avr 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/M6502.h $
+ *  $Revision: 45 $
+ */
+
+#ifndef M6502_H
+#define M6502_H
+                               /* Loop6502() returns:        */
+#define INT_NONE  0            /* No interrupt required      */
+#define INT_IRQ   1            /* Standard IRQ interrupt     */
+#define INT_NMI   2            /* Non-maskable interrupt     */
+#define INT_QUIT  3            /* Exit the emulation         */
+
+                               /* 6502 status flags:         */
+#define C_FLAG    0x01         /* 1: Carry occured           */
+#define Z_FLAG    0x02         /* 1: Result is zero          */
+#define I_FLAG    0x04         /* 1: Interrupts disabled     */
+#define D_FLAG    0x08         /* 1: Decimal mode            */
+#define B_FLAG    0x10         /* Break [0 on stk after int] */
+#define R_FLAG    0x20         /* Always 1                   */
+#define V_FLAG    0x40         /* 1: Overflow occured        */
+#define N_FLAG    0x80         /* 1: Result is negative      */
+
+/** Simple Datatypes *****************************************/
+/** NOTICE: sizeof(byte)=1 and sizeof(word)=2               **/
+/*************************************************************/
+#ifndef BYTE_TYPE_DEFINED
+#define BYTE_TYPE_DEFINED
+typedef unsigned char byte;
+#endif
+#ifndef WORD_TYPE_DEFINED
+#define WORD_TYPE_DEFINED
+typedef unsigned short word;
+#endif
+typedef signed char offset;
+
+/** Structured Datatypes *************************************/
+/** NOTICE: #define LSB_FIRST for machines where least      **/
+/**         signifcant byte goes first.                     **/
+/*************************************************************/
+typedef union
+{
+#ifdef LSB_FIRST
+  struct { byte l,h; } B;
+#else
+  struct { byte h,l; } B;
+#endif
+  word W;
+} pair;
+
+typedef struct
+{
+  byte A,P,X,Y,S;     /* CPU registers and program counter   */
+  pair PC;
+
+  int IPeriod,ICount; /* Set IPeriod to number of CPU cycles */
+                      /* between calls to Loop6502()         */
+  byte IRequest;      /* Set to the INT_IRQ when pending IRQ */
+  byte AfterCLI;      /* Private, don't touch                */
+  int IBackup;        /* Private, don't touch                */
+  byte IAutoReset;    /* Set to 1 to autom. reset IRequest   */
+  byte TrapBadOps;    /* Set to 1 to warn of illegal opcodes */
+  word Trap;          /* Set Trap to address to trace from   */
+  byte Trace;         /* Set Trace=1 to start tracing        */
+  void *User;         /* Arbitrary user data (ID,RAM*,etc.)  */
+} M6502;
+
+/** Reset6502() **********************************************/
+/** This function can be used to reset the registers before **/
+/** starting execution with Run6502(). It sets registers to **/
+/** their initial values.                                   **/
+/*************************************************************/
+void Reset6502(register M6502 *R);
+
+/** Exec6502() ***********************************************/
+/** This function will execute a single 6502 opcode. It     **/
+/** will then return next PC, and current register values   **/
+/** in R.                                                   **/
+/*************************************************************/
+word Exec6502(register M6502 *R);
+
+/** Int6502() ************************************************/
+/** This function will generate interrupt of a given type.  **/
+/** INT_NMI will cause a non-maskable interrupt. INT_IRQ    **/
+/** will cause a normal interrupt, unless I_FLAG set in R.  **/
+/*************************************************************/
+void Int6502(register M6502 *R,register byte Type);
+
+/** Run6502() ************************************************/
+/** This function will run 6502 code until Loop6502() call  **/
+/** returns INT_QUIT. It will return the PC at which        **/
+/** emulation stopped, and current register values in R.    **/
+/*************************************************************/
+word Run6502(register M6502 *R);
+
+/** Rd6502()/Wr6502/Op6502() *********************************/
+/** These functions are called when access to RAM occurs.   **/
+/** They allow to control memory access. Op6502 is the same **/
+/** as Rd6502, but used to read *opcodes* only, when many   **/
+/** checks can be skipped to make it fast. It is only       **/
+/** required if there is a #define FAST_RDOP.               **/
+/************************************ TO BE WRITTEN BY USER **/
+void Wr6502(register word Addr,register byte Value);
+byte Rd6502(register word Addr);
+byte Op6502(register word Addr);
+
+/** Debug6502() **********************************************/
+/** This function should exist if DEBUG is #defined. When   **/
+/** Trace!=0, it is called after each command executed by   **/
+/** the CPU, and given the 6502 registers. Emulation exits  **/
+/** if Debug6502() returns 0.                               **/
+/*************************************************************/
+byte Debug6502(register M6502 *R);
+
+/** Loop6502() ***********************************************/
+/** 6502 emulation calls this function periodically to      **/
+/** check if the system hardware requires any interrupts.   **/
+/** This function must return one of following values:      **/
+/** INT_NONE, INT_IRQ, INT_NMI, or INT_QUIT to exit the     **/
+/** emulation loop.                                         **/
+/************************************ TO BE WRITTEN BY USER **/
+byte Loop6502(register M6502 *R);
+
+/** Patch6502() **********************************************/
+/** Emulation calls this function when it encounters an     **/
+/** unknown opcode. This can be used to patch the code to   **/
+/** emulate BIOS calls, such as disk and tape access. The   **/
+/** function should return 1 if the exception was handled,  **/
+/** or 0 if the opcode was truly illegal.                   **/
+/************************************ TO BE WRITTEN BY USER **/
+byte Patch6502(register byte Op,register M6502 *R);
+
+#endif /* M6502_H */

+ 71 - 0
src/corecpu/Tables.h

@@ -0,0 +1,71 @@
+ /** M6502: portable 6502 emulator ****************************/ 
+ /**                                                         **/ 
+ /**                          Tables.h                       **/ 
+ /**                                                         **/ 
+ /** This file contains tables of used by 6502 emulation to  **/ 
+ /** compute NEGATIVE and ZERO flags. There are also timing  **/ 
+ /** tables for 6502 opcodes. This file is included from     **/ 
+ /** 6502.c.                                                 **/ 
+ /**                                                         **/ 
+ /** Copyright (C) Marat Fayzullin 1996-2002                 **/ 
+ /**     You are not allowed to distribute this software     **/ 
+ /**     commercially. Please, notify me, if you make any    **/ 
+ /**     changes to this file.                               **/ 
+ /*************************************************************/ 
+/*
+ *  $LastChangedDate: 2007-05-24 15:07:13 +0200 (jeu, 24 mai 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/Tables.h $
+ *  $Revision: 52 $
+ */
+ 
+static byte Cycles[256] = 
+{
+       7, 6, 2, 1, 5, 3, 5, 5, 3, 2, 2, 1, 6, 4, 6, 2, 
+       2, 5, 5, 1, 5, 4, 6, 5, 2, 4, 2, 1, 6, 4, 6, 2, 
+       6, 6, 2, 1, 3, 3, 5, 5, 4, 2, 2, 1, 4, 4, 6, 2, 
+       2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 2, 1, 4, 4, 6, 2, 
+       6, 6, 2, 1, 3, 3, 5, 5, 3, 2, 2, 1, 3, 4, 6, 2, 
+       2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 3, 1, 8, 4, 6, 2, 
+       6, 6, 2, 1, 3, 3, 5, 5, 4, 2, 2, 1, 6, 4, 6, 2, 
+       2, 5, 5, 1, 4, 4, 6, 5, 5, 4, 4, 1, 6, 4, 6, 2, 
+       3, 6, 2, 1, 3, 3, 3, 5, 2, 2, 2, 1, 4, 4, 4, 2, 
+       2, 6, 5, 1, 4, 4, 4, 5, 2, 5, 2, 1, 4, 5, 5, 2, 
+       2, 6, 2, 1, 3, 3, 3, 5, 2, 2, 2, 1, 4, 4, 4, 2, 
+       2, 5, 5, 1, 4, 4, 4, 5, 2, 4, 2, 1, 4, 4, 4, 2, 
+       2, 6, 2, 1, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 2, 
+       2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 3, 2, 4, 4, 6, 2, 
+       2, 6, 2, 1, 3, 3, 5, 5, 2, 2, 2, 1, 4, 4, 6, 2, 
+       2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 4, 1, 4, 4, 6, 2
+};
+
+
+byte ZNTable[256] = 
+{
+    
+       Z_FLAG, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, 
+       N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, 
+       N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, 
+       N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, 
+       N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, 
+       N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, 
+       N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, 
+       N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, 
+       N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, 
+       N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, 
+       N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, 
+       N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, 
+       N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, 
+       N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, 
+       N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, 
+       N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, N_FLAG, 
+};
+

+ 1038 - 0
src/include/MIDIFreq.h

@@ -0,0 +1,1038 @@
+/** EMULib Emulation Library *********************************/
+/**                                                         **/
+/**                       MIDIFreq.h                        **/
+/**                                                         **/
+/** This file contains a table to convert sound frequencies **/
+/** into MIDI octave/note numbers. It is included from the  **/
+/** Sound.c and SndWin.c files.                             **/
+/**                                                         **/
+/** Copyright (C) Marat Fayzullin 1996-2007                 **/
+/**     You are not allowed to distribute this software     **/
+/**     commercially. Please, notify me, if you make any    **/
+/**     changes to this file.                               **/
+/*************************************************************/
+
+{ -36, 8063 },{ -12, 8063 },{  -3, 8298 },{   3, 8358 },
+{   8, 8201 },{  12, 8063 },{  15, 8054 },{  17, 8238 },
+{  19, 8311 },{  21, 8298 },{  23, 8217 },{  25, 8080 },
+{  26, 8238 },{  27, 8358 },{  29, 8102 },{  30, 8159 },
+{  31, 8191 },{  32, 8201 },{  33, 8191 },{  34, 8164 },
+{  35, 8121 },{  36, 8063 },{  36, 8334 },{  37, 8251 },
+{  38, 8158 },{  39, 8054 },{  39, 8283 },{  40, 8162 },
+{  41, 8033 },{  41, 8238 },{  42, 8095 },{  42, 8287 },
+{  43, 8131 },{  43, 8311 },{  44, 8144 },{  44, 8314 },
+{  45, 8137 },{  45, 8298 },{  46, 8113 },{  46, 8265 },
+{  47, 8072 },{  47, 8217 },{  47, 8358 },{  48, 8155 },
+{  48, 8289 },{  49, 8080 },{  49, 8209 },{  49, 8335 },
+{  50, 8117 },{  50, 8238 },{  50, 8357 },{  51, 8132 },
+{  51, 8246 },{  51, 8358 },{  52, 8126 },{  52, 8234 },
+{  52, 8340 },{  53, 8102 },{  53, 8204 },{  53, 8305 },
+{  54, 8062 },{  54, 8159 },{  54, 8255 },{  54, 8349 },
+{  55, 8100 },{  55, 8191 },{  55, 8281 },{  56, 8028 },
+{  56, 8115 },{  56, 8201 },{  56, 8286 },{  57, 8028 },
+{  57, 8110 },{  57, 8191 },{  57, 8271 },{  57, 8350 },
+{  58, 8087 },{  58, 8164 },{  58, 8240 },{  58, 8315 },
+{  59, 8047 },{  59, 8121 },{  59, 8193 },{  59, 8264 },
+{  59, 8335 },{  60, 8063 },{  60, 8132 },{  60, 8200 },
+{  60, 8267 },{  60, 8334 },{  61, 8058 },{  61, 8123 },
+{  61, 8188 },{  61, 8251 },{  61, 8314 },{  62, 8035 },
+{  62, 8097 },{  62, 8158 },{  62, 8218 },{  62, 8278 },
+{  62, 8337 },{  63, 8054 },{  63, 8112 },{  63, 8170 },
+{  63, 8227 },{  63, 8283 },{  63, 8339 },{  64, 8053 },
+{  64, 8108 },{  64, 8162 },{  64, 8216 },{  64, 8269 },
+{  64, 8322 },{  65, 8033 },{  65, 8085 },{  65, 8137 },
+{  65, 8188 },{  65, 8238 },{  65, 8288 },{  65, 8338 },
+{  66, 8046 },{  66, 8095 },{  66, 8143 },{  66, 8191 },
+{  66, 8239 },{  66, 8287 },{  66, 8334 },{  67, 8039 },
+{  67, 8085 },{  67, 8131 },{  67, 8176 },{  67, 8222 },
+{  67, 8266 },{  67, 8311 },{  67, 8355 },{  68, 8057 },
+{  68, 8101 },{  68, 8144 },{  68, 8187 },{  68, 8230 },
+{  68, 8272 },{  68, 8314 },{  68, 8356 },{  69, 8056 },
+{  69, 8097 },{  69, 8137 },{  69, 8178 },{  69, 8218 },
+{  69, 8258 },{  69, 8298 },{  69, 8337 },{  70, 8035 },
+{  70, 8074 },{  70, 8113 },{  70, 8151 },{  70, 8189 },
+{  70, 8227 },{  70, 8265 },{  70, 8302 },{  70, 8340 },
+{  71, 8035 },{  71, 8072 },{  71, 8108 },{  71, 8145 },
+{  71, 8181 },{  71, 8217 },{  71, 8252 },{  71, 8288 },
+{  71, 8323 },{  71, 8358 },{  72, 8051 },{  72, 8086 },
+{  72, 8120 },{  72, 8155 },{  72, 8189 },{  72, 8222 },
+{  72, 8256 },{  72, 8289 },{  72, 8323 },{  72, 8356 },
+{  73, 8047 },{  73, 8080 },{  73, 8112 },{  73, 8145 },
+{  73, 8177 },{  73, 8209 },{  73, 8241 },{  73, 8272 },
+{  73, 8304 },{  73, 8335 },{  74, 8025 },{  74, 8056 },
+{  74, 8087 },{  74, 8117 },{  74, 8148 },{  74, 8178 },
+{  74, 8208 },{  74, 8238 },{  74, 8268 },{  74, 8298 },
+{  74, 8327 },{  74, 8357 },{  75, 8045 },{  75, 8074 },
+{  75, 8103 },{  75, 8132 },{  75, 8160 },{  75, 8189 },
+{  75, 8217 },{  75, 8246 },{  75, 8274 },{  75, 8302 },
+{  75, 8330 },{  75, 8358 },{  76, 8044 },{  76, 8071 },
+{  76, 8099 },{  76, 8126 },{  76, 8153 },{  76, 8180 },
+{  76, 8207 },{  76, 8234 },{  76, 8260 },{  76, 8287 },
+{  76, 8313 },{  76, 8340 },{  77, 8024 },{  77, 8051 },
+{  77, 8076 },{  77, 8102 },{  77, 8128 },{  77, 8154 },
+{  77, 8179 },{  77, 8204 },{  77, 8230 },{  77, 8255 },
+{  77, 8280 },{  77, 8305 },{  77, 8330 },{  77, 8354 },
+{  78, 8038 },{  78, 8062 },{  78, 8087 },{  78, 8111 },
+{  78, 8135 },{  78, 8159 },{  78, 8183 },{  78, 8207 },
+{  78, 8231 },{  78, 8255 },{  78, 8279 },{  78, 8302 },
+{  78, 8326 },{  78, 8349 },{  79, 8031 },{  79, 8054 },
+{  79, 8077 },{  79, 8100 },{  79, 8123 },{  79, 8146 },
+{  79, 8169 },{  79, 8191 },{  79, 8214 },{  79, 8237 },
+{  79, 8259 },{  79, 8281 },{  79, 8303 },{  79, 8326 },
+{  79, 8348 },{  80, 8028 },{  80, 8050 },{  80, 8072 },
+{  80, 8094 },{  80, 8115 },{  80, 8137 },{  80, 8159 },
+{  80, 8180 },{  80, 8201 },{  80, 8223 },{  80, 8244 },
+{  80, 8265 },{  80, 8286 },{  80, 8307 },{  80, 8328 },
+{  80, 8349 },{  81, 8028 },{  81, 8049 },{  81, 8069 },
+{  81, 8090 },{  81, 8110 },{  81, 8131 },{  81, 8151 },
+{  81, 8171 },{  81, 8191 },{  81, 8212 },{  81, 8232 },
+{  81, 8252 },{  81, 8271 },{  81, 8291 },{  81, 8311 },
+{  81, 8331 },{  81, 8350 },{  82, 8029 },{  82, 8048 },
+{  82, 8068 },{  82, 8087 },{  82, 8106 },{  82, 8126 },
+{  82, 8145 },{  82, 8164 },{  82, 8183 },{  82, 8202 },
+{  82, 8221 },{  82, 8240 },{  82, 8259 },{  82, 8277 },
+{  82, 8296 },{  82, 8315 },{  82, 8333 },{  82, 8352 },
+{  83, 8029 },{  83, 8047 },{  83, 8066 },{  83, 8084 },
+{  83, 8102 },{  83, 8121 },{  83, 8139 },{  83, 8157 },
+{  83, 8175 },{  83, 8193 },{  83, 8211 },{  83, 8229 },
+{  83, 8246 },{  83, 8264 },{  83, 8282 },{  83, 8299 },
+{  83, 8317 },{  83, 8335 },{  83, 8352 },{  84, 8028 },
+{  84, 8046 },{  84, 8063 },{  84, 8080 },{  84, 8097 },
+{  84, 8115 },{  84, 8132 },{  84, 8149 },{  84, 8166 },
+{  84, 8183 },{  84, 8200 },{  84, 8217 },{  84, 8234 },
+{  84, 8250 },{  84, 8267 },{  84, 8284 },{  84, 8300 },
+{  84, 8317 },{  84, 8334 },{  84, 8350 },{  85, 8025 },
+{  85, 8042 },{  85, 8058 },{  85, 8074 },{  85, 8091 },
+{  85, 8107 },{  85, 8123 },{  85, 8139 },{  85, 8155 },
+{  85, 8171 },{  85, 8188 },{  85, 8203 },{  85, 8219 },
+{  85, 8235 },{  85, 8251 },{  85, 8267 },{  85, 8283 },
+{  85, 8298 },{  85, 8314 },{  85, 8330 },{  85, 8345 },
+{  85, 8361 },{  86, 8035 },{  86, 8051 },{  86, 8066 },
+{  86, 8081 },{  86, 8097 },{  86, 8112 },{  86, 8127 },
+{  86, 8143 },{  86, 8158 },{  86, 8173 },{  86, 8188 },
+{  86, 8203 },{  86, 8218 },{  86, 8233 },{  86, 8248 },
+{  86, 8263 },{  86, 8278 },{  86, 8293 },{  86, 8308 },
+{  86, 8322 },{  86, 8337 },{  86, 8352 },{  87, 8025 },
+{  87, 8040 },{  87, 8054 },{  87, 8069 },{  87, 8084 },
+{  87, 8098 },{  87, 8112 },{  87, 8127 },{  87, 8141 },
+{  87, 8156 },{  87, 8170 },{  87, 8184 },{  87, 8198 },
+{  87, 8213 },{  87, 8227 },{  87, 8241 },{  87, 8255 },
+{  87, 8269 },{  87, 8283 },{  87, 8297 },{  87, 8311 },
+{  87, 8325 },{  87, 8339 },{  87, 8353 },{  88, 8025 },
+{  88, 8039 },{  88, 8053 },{  88, 8067 },{  88, 8081 },
+{  88, 8094 },{  88, 8108 },{  88, 8121 },{  88, 8135 },
+{  88, 8149 },{  88, 8162 },{  88, 8176 },{  88, 8189 },
+{  88, 8203 },{  88, 8216 },{  88, 8229 },{  88, 8243 },
+{  88, 8256 },{  88, 8269 },{  88, 8283 },{  88, 8296 },
+{  88, 8309 },{  88, 8322 },{  88, 8335 },{  88, 8348 },
+{  88, 8361 },{  89, 8033 },{  89, 8046 },{  89, 8059 },
+{  89, 8072 },{  89, 8085 },{  89, 8098 },{  89, 8111 },
+{  89, 8124 },{  89, 8137 },{  89, 8149 },{  89, 8162 },
+{  89, 8175 },{  89, 8188 },{  89, 8200 },{  89, 8213 },
+{  89, 8226 },{  89, 8238 },{  89, 8251 },{  89, 8263 },
+{  89, 8276 },{  89, 8288 },{  89, 8301 },{  89, 8313 },
+{  89, 8326 },{  89, 8338 },{  89, 8350 },{  90, 8021 },
+{  90, 8034 },{  90, 8046 },{  90, 8058 },{  90, 8070 },
+{  90, 8083 },{  90, 8095 },{  90, 8107 },{  90, 8119 },
+{  90, 8131 },{  90, 8143 },{  90, 8155 },{  90, 8167 },
+{  90, 8179 },{  90, 8191 },{  90, 8203 },{  90, 8215 },
+{  90, 8227 },{  90, 8239 },{  90, 8251 },{  90, 8263 },
+{  90, 8275 },{  90, 8287 },{  90, 8298 },{  90, 8310 },
+{  90, 8322 },{  90, 8334 },{  90, 8345 },{  90, 8357 },
+{  91, 8027 },{  91, 8039 },{  91, 8050 },{  91, 8062 },
+{  91, 8073 },{  91, 8085 },{  91, 8096 },{  91, 8108 },
+{  91, 8119 },{  91, 8131 },{  91, 8142 },{  91, 8154 },
+{  91, 8165 },{  91, 8176 },{  91, 8188 },{  91, 8199 },
+{  91, 8210 },{  91, 8222 },{  91, 8233 },{  91, 8244 },
+{  91, 8255 },{  91, 8266 },{  91, 8278 },{  91, 8289 },
+{  91, 8300 },{  91, 8311 },{  91, 8322 },{  91, 8333 },
+{  91, 8344 },{  91, 8355 },{  92, 8025 },{  92, 8036 },
+{  92, 8047 },{  92, 8057 },{  92, 8068 },{  92, 8079 },
+{  92, 8090 },{  92, 8101 },{  92, 8112 },{  92, 8123 },
+{  92, 8133 },{  92, 8144 },{  92, 8155 },{  92, 8166 },
+{  92, 8176 },{  92, 8187 },{  92, 8198 },{  92, 8208 },
+{  92, 8219 },{  92, 8230 },{  92, 8240 },{  92, 8251 },
+{  92, 8261 },{  92, 8272 },{  92, 8282 },{  92, 8293 },
+{  92, 8303 },{  92, 8314 },{  92, 8324 },{  92, 8335 },
+{  92, 8345 },{  92, 8356 },{  93, 8025 },{  93, 8035 },
+{  93, 8045 },{  93, 8056 },{  93, 8066 },{  93, 8076 },
+{  93, 8086 },{  93, 8097 },{  93, 8107 },{  93, 8117 },
+{  93, 8127 },{  93, 8137 },{  93, 8148 },{  93, 8158 },
+{  93, 8168 },{  93, 8178 },{  93, 8188 },{  93, 8198 },
+{  93, 8208 },{  93, 8218 },{  93, 8228 },{  93, 8238 },
+{  93, 8248 },{  93, 8258 },{  93, 8268 },{  93, 8278 },
+{  93, 8288 },{  93, 8298 },{  93, 8308 },{  93, 8318 },
+{  93, 8327 },{  93, 8337 },{  93, 8347 },{  93, 8357 },
+{  94, 8025 },{  94, 8035 },{  94, 8045 },{  94, 8055 },
+{  94, 8064 },{  94, 8074 },{  94, 8084 },{  94, 8093 },
+{  94, 8103 },{  94, 8113 },{  94, 8122 },{  94, 8132 },
+{  94, 8142 },{  94, 8151 },{  94, 8161 },{  94, 8170 },
+{  94, 8180 },{  94, 8189 },{  94, 8199 },{  94, 8208 },
+{  94, 8218 },{  94, 8227 },{  94, 8237 },{  94, 8246 },
+{  94, 8256 },{  94, 8265 },{  94, 8274 },{  94, 8284 },
+{  94, 8293 },{  94, 8302 },{  94, 8312 },{  94, 8321 },
+{  94, 8330 },{  94, 8340 },{  94, 8349 },{  94, 8358 },
+{  95, 8026 },{  95, 8035 },{  95, 8044 },{  95, 8054 },
+{  95, 8063 },{  95, 8072 },{  95, 8081 },{  95, 8090 },
+{  95, 8099 },{  95, 8108 },{  95, 8118 },{  95, 8127 },
+{  95, 8136 },{  95, 8145 },{  95, 8154 },{  95, 8163 },
+{  95, 8172 },{  95, 8181 },{  95, 8190 },{  95, 8199 },
+{  95, 8208 },{  95, 8217 },{  95, 8226 },{  95, 8235 },
+{  95, 8243 },{  95, 8252 },{  95, 8261 },{  95, 8270 },
+{  95, 8279 },{  95, 8288 },{  95, 8297 },{  95, 8305 },
+{  95, 8314 },{  95, 8323 },{  95, 8332 },{  95, 8340 },
+{  95, 8349 },{  95, 8358 },{  96, 8025 },{  96, 8034 },
+{  96, 8043 },{  96, 8051 },{  96, 8060 },{  96, 8069 },
+{  96, 8077 },{  96, 8086 },{  96, 8095 },{  96, 8103 },
+{  96, 8112 },{  96, 8120 },{  96, 8129 },{  96, 8137 },
+{  96, 8146 },{  96, 8155 },{  96, 8163 },{  96, 8172 },
+{  96, 8180 },{  96, 8189 },{  96, 8197 },{  96, 8205 },
+{  96, 8214 },{  96, 8222 },{  96, 8231 },{  96, 8239 },
+{  96, 8248 },{  96, 8256 },{  96, 8264 },{  96, 8273 },
+{  96, 8281 },{  96, 8289 },{  96, 8298 },{  96, 8306 },
+{  96, 8314 },{  96, 8323 },{  96, 8331 },{  96, 8339 },
+{  96, 8347 },{  96, 8356 },{  97, 8023 },{  97, 8031 },
+{  97, 8039 },{  97, 8047 },{  97, 8055 },{  97, 8064 },
+{  97, 8072 },{  97, 8080 },{  97, 8088 },{  97, 8096 },
+{  97, 8104 },{  97, 8112 },{  97, 8120 },{  97, 8129 },
+{  97, 8137 },{  97, 8145 },{  97, 8153 },{  97, 8161 },
+{  97, 8169 },{  97, 8177 },{  97, 8185 },{  97, 8193 },
+{  97, 8201 },{  97, 8209 },{  97, 8217 },{  97, 8225 },
+{  97, 8233 },{  97, 8241 },{  97, 8249 },{  97, 8256 },
+{  97, 8264 },{  97, 8272 },{  97, 8280 },{  97, 8288 },
+{  97, 8296 },{  97, 8304 },{  97, 8312 },{  97, 8319 },
+{  97, 8327 },{  97, 8335 },{  97, 8343 },{  97, 8351 },
+{  97, 8358 },{  98, 8025 },{  98, 8033 },{  98, 8040 },
+{  98, 8048 },{  98, 8056 },{  98, 8063 },{  98, 8071 },
+{  98, 8079 },{  98, 8087 },{  98, 8094 },{  98, 8102 },
+{  98, 8110 },{  98, 8117 },{  98, 8125 },{  98, 8132 },
+{  98, 8140 },{  98, 8148 },{  98, 8155 },{  98, 8163 },
+{  98, 8170 },{  98, 8178 },{  98, 8186 },{  98, 8193 },
+{  98, 8201 },{  98, 8208 },{  98, 8216 },{  98, 8223 },
+{  98, 8231 },{  98, 8238 },{  98, 8246 },{  98, 8253 },
+{  98, 8261 },{  98, 8268 },{  98, 8276 },{  98, 8283 },
+{  98, 8290 },{  98, 8298 },{  98, 8305 },{  98, 8313 },
+{  98, 8320 },{  98, 8327 },{  98, 8335 },{  98, 8342 },
+{  98, 8349 },{  98, 8357 },{  99, 8023 },{  99, 8030 },
+{  99, 8037 },{  99, 8045 },{  99, 8052 },{  99, 8059 },
+{  99, 8067 },{  99, 8074 },{  99, 8081 },{  99, 8088 },
+{  99, 8096 },{  99, 8103 },{  99, 8110 },{  99, 8117 },
+{  99, 8125 },{  99, 8132 },{  99, 8139 },{  99, 8146 },
+{  99, 8153 },{  99, 8160 },{  99, 8168 },{  99, 8175 },
+{  99, 8182 },{  99, 8189 },{  99, 8196 },{  99, 8203 },
+{  99, 8210 },{  99, 8217 },{  99, 8225 },{  99, 8232 },
+{  99, 8239 },{  99, 8246 },{  99, 8253 },{  99, 8260 },
+{  99, 8267 },{  99, 8274 },{  99, 8281 },{  99, 8288 },
+{  99, 8295 },{  99, 8302 },{  99, 8309 },{  99, 8316 },
+{  99, 8323 },{  99, 8330 },{  99, 8337 },{  99, 8344 },
+{  99, 8351 },{  99, 8358 },{ 100, 8023 },{ 100, 8030 },
+{ 100, 8037 },{ 100, 8044 },{ 100, 8051 },{ 100, 8058 },
+{ 100, 8065 },{ 100, 8071 },{ 100, 8078 },{ 100, 8085 },
+{ 100, 8092 },{ 100, 8099 },{ 100, 8106 },{ 100, 8112 },
+{ 100, 8119 },{ 100, 8126 },{ 100, 8133 },{ 100, 8140 },
+{ 100, 8146 },{ 100, 8153 },{ 100, 8160 },{ 100, 8167 },
+{ 100, 8173 },{ 100, 8180 },{ 100, 8187 },{ 100, 8194 },
+{ 100, 8200 },{ 100, 8207 },{ 100, 8214 },{ 100, 8220 },
+{ 100, 8227 },{ 100, 8234 },{ 100, 8240 },{ 100, 8247 },
+{ 100, 8254 },{ 100, 8260 },{ 100, 8267 },{ 100, 8274 },
+{ 100, 8280 },{ 100, 8287 },{ 100, 8294 },{ 100, 8300 },
+{ 100, 8307 },{ 100, 8313 },{ 100, 8320 },{ 100, 8326 },
+{ 100, 8333 },{ 100, 8340 },{ 100, 8346 },{ 100, 8353 },
+{ 100, 8359 },{ 101, 8024 },{ 101, 8031 },{ 101, 8037 },
+{ 101, 8044 },{ 101, 8051 },{ 101, 8057 },{ 101, 8063 },
+{ 101, 8070 },{ 101, 8076 },{ 101, 8083 },{ 101, 8089 },
+{ 101, 8096 },{ 101, 8102 },{ 101, 8109 },{ 101, 8115 },
+{ 101, 8122 },{ 101, 8128 },{ 101, 8134 },{ 101, 8141 },
+{ 101, 8147 },{ 101, 8154 },{ 101, 8160 },{ 101, 8166 },
+{ 101, 8173 },{ 101, 8179 },{ 101, 8185 },{ 101, 8192 },
+{ 101, 8198 },{ 101, 8204 },{ 101, 8211 },{ 101, 8217 },
+{ 101, 8223 },{ 101, 8230 },{ 101, 8236 },{ 101, 8242 },
+{ 101, 8249 },{ 101, 8255 },{ 101, 8261 },{ 101, 8267 },
+{ 101, 8274 },{ 101, 8280 },{ 101, 8286 },{ 101, 8292 },
+{ 101, 8299 },{ 101, 8305 },{ 101, 8311 },{ 101, 8317 },
+{ 101, 8324 },{ 101, 8330 },{ 101, 8336 },{ 101, 8342 },
+{ 101, 8348 },{ 101, 8354 },{ 101, 8361 },{ 102, 8025 },
+{ 102, 8032 },{ 102, 8038 },{ 102, 8044 },{ 102, 8050 },
+{ 102, 8056 },{ 102, 8062 },{ 102, 8068 },{ 102, 8075 },
+{ 102, 8081 },{ 102, 8087 },{ 102, 8093 },{ 102, 8099 },
+{ 102, 8105 },{ 102, 8111 },{ 102, 8117 },{ 102, 8123 },
+{ 102, 8129 },{ 102, 8135 },{ 102, 8141 },{ 102, 8147 },
+{ 102, 8153 },{ 102, 8159 },{ 102, 8165 },{ 102, 8171 },
+{ 102, 8177 },{ 102, 8183 },{ 102, 8189 },{ 102, 8195 },
+{ 102, 8201 },{ 102, 8207 },{ 102, 8213 },{ 102, 8219 },
+{ 102, 8225 },{ 102, 8231 },{ 102, 8237 },{ 102, 8243 },
+{ 102, 8249 },{ 102, 8255 },{ 102, 8261 },{ 102, 8267 },
+{ 102, 8273 },{ 102, 8279 },{ 102, 8285 },{ 102, 8290 },
+{ 102, 8296 },{ 102, 8302 },{ 102, 8308 },{ 102, 8314 },
+{ 102, 8320 },{ 102, 8326 },{ 102, 8332 },{ 102, 8337 },
+{ 102, 8343 },{ 102, 8349 },{ 102, 8355 },{ 102, 8361 },
+{ 103, 8025 },{ 103, 8031 },{ 103, 8037 },{ 103, 8043 },
+{ 103, 8048 },{ 103, 8054 },{ 103, 8060 },{ 103, 8066 },
+{ 103, 8072 },{ 103, 8077 },{ 103, 8083 },{ 103, 8089 },
+{ 103, 8095 },{ 103, 8100 },{ 103, 8106 },{ 103, 8112 },
+{ 103, 8118 },{ 103, 8123 },{ 103, 8129 },{ 103, 8135 },
+{ 103, 8140 },{ 103, 8146 },{ 103, 8152 },{ 103, 8157 },
+{ 103, 8163 },{ 103, 8169 },{ 103, 8174 },{ 103, 8180 },
+{ 103, 8186 },{ 103, 8191 },{ 103, 8197 },{ 103, 8203 },
+{ 103, 8208 },{ 103, 8214 },{ 103, 8220 },{ 103, 8225 },
+{ 103, 8231 },{ 103, 8237 },{ 103, 8242 },{ 103, 8248 },
+{ 103, 8253 },{ 103, 8259 },{ 103, 8265 },{ 103, 8270 },
+{ 103, 8276 },{ 103, 8281 },{ 103, 8287 },{ 103, 8292 },
+{ 103, 8298 },{ 103, 8303 },{ 103, 8309 },{ 103, 8315 },
+{ 103, 8320 },{ 103, 8326 },{ 103, 8331 },{ 103, 8337 },
+{ 103, 8342 },{ 103, 8348 },{ 103, 8353 },{ 103, 8359 },
+{ 104, 8023 },{ 104, 8028 },{ 104, 8034 },{ 104, 8039 },
+{ 104, 8045 },{ 104, 8050 },{ 104, 8056 },{ 104, 8061 },
+{ 104, 8067 },{ 104, 8072 },{ 104, 8077 },{ 104, 8083 },
+{ 104, 8088 },{ 104, 8094 },{ 104, 8099 },{ 104, 8105 },
+{ 104, 8110 },{ 104, 8115 },{ 104, 8121 },{ 104, 8126 },
+{ 104, 8132 },{ 104, 8137 },{ 104, 8142 },{ 104, 8148 },
+{ 104, 8153 },{ 104, 8159 },{ 104, 8164 },{ 104, 8169 },
+{ 104, 8175 },{ 104, 8180 },{ 104, 8185 },{ 104, 8191 },
+{ 104, 8196 },{ 104, 8201 },{ 104, 8207 },{ 104, 8212 },
+{ 104, 8217 },{ 104, 8223 },{ 104, 8228 },{ 104, 8233 },
+{ 104, 8238 },{ 104, 8244 },{ 104, 8249 },{ 104, 8254 },
+{ 104, 8260 },{ 104, 8265 },{ 104, 8270 },{ 104, 8275 },
+{ 104, 8281 },{ 104, 8286 },{ 104, 8291 },{ 104, 8296 },
+{ 104, 8302 },{ 104, 8307 },{ 104, 8312 },{ 104, 8317 },
+{ 104, 8323 },{ 104, 8328 },{ 104, 8333 },{ 104, 8338 },
+{ 104, 8343 },{ 104, 8349 },{ 104, 8354 },{ 104, 8359 },
+{ 105, 8023 },{ 105, 8028 },{ 105, 8033 },{ 105, 8038 },
+{ 105, 8044 },{ 105, 8049 },{ 105, 8054 },{ 105, 8059 },
+{ 105, 8064 },{ 105, 8069 },{ 105, 8074 },{ 105, 8080 },
+{ 105, 8085 },{ 105, 8090 },{ 105, 8095 },{ 105, 8100 },
+{ 105, 8105 },{ 105, 8110 },{ 105, 8115 },{ 105, 8120 },
+{ 105, 8126 },{ 105, 8131 },{ 105, 8136 },{ 105, 8141 },
+{ 105, 8146 },{ 105, 8151 },{ 105, 8156 },{ 105, 8161 },
+{ 105, 8166 },{ 105, 8171 },{ 105, 8176 },{ 105, 8181 },
+{ 105, 8186 },{ 105, 8191 },{ 105, 8196 },{ 105, 8201 },
+{ 105, 8206 },{ 105, 8212 },{ 105, 8217 },{ 105, 8222 },
+{ 105, 8227 },{ 105, 8232 },{ 105, 8237 },{ 105, 8242 },
+{ 105, 8247 },{ 105, 8252 },{ 105, 8257 },{ 105, 8261 },
+{ 105, 8266 },{ 105, 8271 },{ 105, 8276 },{ 105, 8281 },
+{ 105, 8286 },{ 105, 8291 },{ 105, 8296 },{ 105, 8301 },
+{ 105, 8306 },{ 105, 8311 },{ 105, 8316 },{ 105, 8321 },
+{ 105, 8326 },{ 105, 8331 },{ 105, 8336 },{ 105, 8341 },
+{ 105, 8345 },{ 105, 8350 },{ 105, 8355 },{ 105, 8360 },
+{ 106, 8024 },{ 106, 8029 },{ 106, 8034 },{ 106, 8038 },
+{ 106, 8043 },{ 106, 8048 },{ 106, 8053 },{ 106, 8058 },
+{ 106, 8063 },{ 106, 8068 },{ 106, 8072 },{ 106, 8077 },
+{ 106, 8082 },{ 106, 8087 },{ 106, 8092 },{ 106, 8097 },
+{ 106, 8101 },{ 106, 8106 },{ 106, 8111 },{ 106, 8116 },
+{ 106, 8121 },{ 106, 8126 },{ 106, 8130 },{ 106, 8135 },
+{ 106, 8140 },{ 106, 8145 },{ 106, 8150 },{ 106, 8154 },
+{ 106, 8159 },{ 106, 8164 },{ 106, 8169 },{ 106, 8173 },
+{ 106, 8178 },{ 106, 8183 },{ 106, 8188 },{ 106, 8192 },
+{ 106, 8197 },{ 106, 8202 },{ 106, 8207 },{ 106, 8211 },
+{ 106, 8216 },{ 106, 8221 },{ 106, 8226 },{ 106, 8230 },
+{ 106, 8235 },{ 106, 8240 },{ 106, 8245 },{ 106, 8249 },
+{ 106, 8254 },{ 106, 8259 },{ 106, 8263 },{ 106, 8268 },
+{ 106, 8273 },{ 106, 8277 },{ 106, 8282 },{ 106, 8287 },
+{ 106, 8291 },{ 106, 8296 },{ 106, 8301 },{ 106, 8305 },
+{ 106, 8310 },{ 106, 8315 },{ 106, 8319 },{ 106, 8324 },
+{ 106, 8329 },{ 106, 8333 },{ 106, 8338 },{ 106, 8343 },
+{ 106, 8347 },{ 106, 8352 },{ 106, 8357 },{ 106, 8361 },
+{ 107, 8024 },{ 107, 8029 },{ 107, 8034 },{ 107, 8038 },
+{ 107, 8043 },{ 107, 8047 },{ 107, 8052 },{ 107, 8057 },
+{ 107, 8061 },{ 107, 8066 },{ 107, 8070 },{ 107, 8075 },
+{ 107, 8080 },{ 107, 8084 },{ 107, 8089 },{ 107, 8093 },
+{ 107, 8098 },{ 107, 8102 },{ 107, 8107 },{ 107, 8111 },
+{ 107, 8116 },{ 107, 8121 },{ 107, 8125 },{ 107, 8130 },
+{ 107, 8134 },{ 107, 8139 },{ 107, 8143 },{ 107, 8148 },
+{ 107, 8152 },{ 107, 8157 },{ 107, 8161 },{ 107, 8166 },
+{ 107, 8170 },{ 107, 8175 },{ 107, 8179 },{ 107, 8184 },
+{ 107, 8188 },{ 107, 8193 },{ 107, 8197 },{ 107, 8202 },
+{ 107, 8206 },{ 107, 8211 },{ 107, 8215 },{ 107, 8220 },
+{ 107, 8224 },{ 107, 8229 },{ 107, 8233 },{ 107, 8237 },
+{ 107, 8242 },{ 107, 8246 },{ 107, 8251 },{ 107, 8255 },
+{ 107, 8260 },{ 107, 8264 },{ 107, 8269 },{ 107, 8273 },
+{ 107, 8277 },{ 107, 8282 },{ 107, 8286 },{ 107, 8291 },
+{ 107, 8295 },{ 107, 8299 },{ 107, 8304 },{ 107, 8308 },
+{ 107, 8313 },{ 107, 8317 },{ 107, 8321 },{ 107, 8326 },
+{ 107, 8330 },{ 107, 8335 },{ 107, 8339 },{ 107, 8343 },
+{ 107, 8348 },{ 107, 8352 },{ 107, 8356 },{ 107, 8361 },
+{ 108, 8024 },{ 108, 8028 },{ 108, 8033 },{ 108, 8037 },
+{ 108, 8041 },{ 108, 8046 },{ 108, 8050 },{ 108, 8054 },
+{ 108, 8059 },{ 108, 8063 },{ 108, 8067 },{ 108, 8072 },
+{ 108, 8076 },{ 108, 8080 },{ 108, 8085 },{ 108, 8089 },
+{ 108, 8093 },{ 108, 8097 },{ 108, 8102 },{ 108, 8106 },
+{ 108, 8110 },{ 108, 8115 },{ 108, 8119 },{ 108, 8123 },
+{ 108, 8128 },{ 108, 8132 },{ 108, 8136 },{ 108, 8140 },
+{ 108, 8145 },{ 108, 8149 },{ 108, 8153 },{ 108, 8157 },
+{ 108, 8162 },{ 108, 8166 },{ 108, 8170 },{ 108, 8174 },
+{ 108, 8179 },{ 108, 8183 },{ 108, 8187 },{ 108, 8191 },
+{ 108, 8196 },{ 108, 8200 },{ 108, 8204 },{ 108, 8208 },
+{ 108, 8213 },{ 108, 8217 },{ 108, 8221 },{ 108, 8225 },
+{ 108, 8229 },{ 108, 8234 },{ 108, 8238 },{ 108, 8242 },
+{ 108, 8246 },{ 108, 8250 },{ 108, 8255 },{ 108, 8259 },
+{ 108, 8263 },{ 108, 8267 },{ 108, 8271 },{ 108, 8275 },
+{ 108, 8280 },{ 108, 8284 },{ 108, 8288 },{ 108, 8292 },
+{ 108, 8296 },{ 108, 8300 },{ 108, 8305 },{ 108, 8309 },
+{ 108, 8313 },{ 108, 8317 },{ 108, 8321 },{ 108, 8325 },
+{ 108, 8330 },{ 108, 8334 },{ 108, 8338 },{ 108, 8342 },
+{ 108, 8346 },{ 108, 8350 },{ 108, 8354 },{ 108, 8358 },
+{ 109, 8021 },{ 109, 8025 },{ 109, 8029 },{ 109, 8034 },
+{ 109, 8038 },{ 109, 8042 },{ 109, 8046 },{ 109, 8050 },
+{ 109, 8054 },{ 109, 8058 },{ 109, 8062 },{ 109, 8066 },
+{ 109, 8070 },{ 109, 8074 },{ 109, 8079 },{ 109, 8083 },
+{ 109, 8087 },{ 109, 8091 },{ 109, 8095 },{ 109, 8099 },
+{ 109, 8103 },{ 109, 8107 },{ 109, 8111 },{ 109, 8115 },
+{ 109, 8119 },{ 109, 8123 },{ 109, 8127 },{ 109, 8131 },
+{ 109, 8135 },{ 109, 8139 },{ 109, 8143 },{ 109, 8147 },
+{ 109, 8151 },{ 109, 8155 },{ 109, 8159 },{ 109, 8163 },
+{ 109, 8167 },{ 109, 8171 },{ 109, 8175 },{ 109, 8179 },
+{ 109, 8184 },{ 109, 8188 },{ 109, 8192 },{ 109, 8195 },
+{ 109, 8199 },{ 109, 8203 },{ 109, 8207 },{ 109, 8211 },
+{ 109, 8215 },{ 109, 8219 },{ 109, 8223 },{ 109, 8227 },
+{ 109, 8231 },{ 109, 8235 },{ 109, 8239 },{ 109, 8243 },
+{ 109, 8247 },{ 109, 8251 },{ 109, 8255 },{ 109, 8259 },
+{ 109, 8263 },{ 109, 8267 },{ 109, 8271 },{ 109, 8275 },
+{ 109, 8279 },{ 109, 8283 },{ 109, 8287 },{ 109, 8291 },
+{ 109, 8295 },{ 109, 8298 },{ 109, 8302 },{ 109, 8306 },
+{ 109, 8310 },{ 109, 8314 },{ 109, 8318 },{ 109, 8322 },
+{ 109, 8326 },{ 109, 8330 },{ 109, 8334 },{ 109, 8338 },
+{ 109, 8341 },{ 109, 8345 },{ 109, 8349 },{ 109, 8353 },
+{ 109, 8357 },{ 109, 8361 },{ 110, 8023 },{ 110, 8027 },
+{ 110, 8031 },{ 110, 8035 },{ 110, 8039 },{ 110, 8043 },
+{ 110, 8047 },{ 110, 8051 },{ 110, 8054 },{ 110, 8058 },
+{ 110, 8062 },{ 110, 8066 },{ 110, 8070 },{ 110, 8074 },
+{ 110, 8078 },{ 110, 8081 },{ 110, 8085 },{ 110, 8089 },
+{ 110, 8093 },{ 110, 8097 },{ 110, 8101 },{ 110, 8104 },
+{ 110, 8108 },{ 110, 8112 },{ 110, 8116 },{ 110, 8120 },
+{ 110, 8124 },{ 110, 8127 },{ 110, 8131 },{ 110, 8135 },
+{ 110, 8139 },{ 110, 8143 },{ 110, 8146 },{ 110, 8150 },
+{ 110, 8154 },{ 110, 8158 },{ 110, 8162 },{ 110, 8165 },
+{ 110, 8169 },{ 110, 8173 },{ 110, 8177 },{ 110, 8181 },
+{ 110, 8184 },{ 110, 8188 },{ 110, 8192 },{ 110, 8196 },
+{ 110, 8199 },{ 110, 8203 },{ 110, 8207 },{ 110, 8211 },
+{ 110, 8214 },{ 110, 8218 },{ 110, 8222 },{ 110, 8226 },
+{ 110, 8229 },{ 110, 8233 },{ 110, 8237 },{ 110, 8241 },
+{ 110, 8244 },{ 110, 8248 },{ 110, 8252 },{ 110, 8256 },
+{ 110, 8259 },{ 110, 8263 },{ 110, 8267 },{ 110, 8271 },
+{ 110, 8274 },{ 110, 8278 },{ 110, 8282 },{ 110, 8285 },
+{ 110, 8289 },{ 110, 8293 },{ 110, 8297 },{ 110, 8300 },
+{ 110, 8304 },{ 110, 8308 },{ 110, 8311 },{ 110, 8315 },
+{ 110, 8319 },{ 110, 8322 },{ 110, 8326 },{ 110, 8330 },
+{ 110, 8334 },{ 110, 8337 },{ 110, 8341 },{ 110, 8345 },
+{ 110, 8348 },{ 110, 8352 },{ 110, 8356 },{ 110, 8359 },
+{ 111, 8022 },{ 111, 8025 },{ 111, 8029 },{ 111, 8033 },
+{ 111, 8036 },{ 111, 8040 },{ 111, 8044 },{ 111, 8047 },
+{ 111, 8051 },{ 111, 8054 },{ 111, 8058 },{ 111, 8062 },
+{ 111, 8065 },{ 111, 8069 },{ 111, 8073 },{ 111, 8076 },
+{ 111, 8080 },{ 111, 8084 },{ 111, 8087 },{ 111, 8091 },
+{ 111, 8094 },{ 111, 8098 },{ 111, 8102 },{ 111, 8105 },
+{ 111, 8109 },{ 111, 8112 },{ 111, 8116 },{ 111, 8120 },
+{ 111, 8123 },{ 111, 8127 },{ 111, 8131 },{ 111, 8134 },
+{ 111, 8138 },{ 111, 8141 },{ 111, 8145 },{ 111, 8148 },
+{ 111, 8152 },{ 111, 8156 },{ 111, 8159 },{ 111, 8163 },
+{ 111, 8166 },{ 111, 8170 },{ 111, 8174 },{ 111, 8177 },
+{ 111, 8181 },{ 111, 8184 },{ 111, 8188 },{ 111, 8191 },
+{ 111, 8195 },{ 111, 8198 },{ 111, 8202 },{ 111, 8206 },
+{ 111, 8209 },{ 111, 8213 },{ 111, 8216 },{ 111, 8220 },
+{ 111, 8223 },{ 111, 8227 },{ 111, 8230 },{ 111, 8234 },
+{ 111, 8237 },{ 111, 8241 },{ 111, 8245 },{ 111, 8248 },
+{ 111, 8252 },{ 111, 8255 },{ 111, 8259 },{ 111, 8262 },
+{ 111, 8266 },{ 111, 8269 },{ 111, 8273 },{ 111, 8276 },
+{ 111, 8280 },{ 111, 8283 },{ 111, 8287 },{ 111, 8290 },
+{ 111, 8294 },{ 111, 8297 },{ 111, 8301 },{ 111, 8304 },
+{ 111, 8308 },{ 111, 8311 },{ 111, 8315 },{ 111, 8318 },
+{ 111, 8322 },{ 111, 8325 },{ 111, 8329 },{ 111, 8332 },
+{ 111, 8336 },{ 111, 8339 },{ 111, 8343 },{ 111, 8346 },
+{ 111, 8349 },{ 111, 8353 },{ 111, 8356 },{ 111, 8360 },
+{ 112, 8022 },{ 112, 8025 },{ 112, 8029 },{ 112, 8032 },
+{ 112, 8036 },{ 112, 8039 },{ 112, 8043 },{ 112, 8046 },
+{ 112, 8050 },{ 112, 8053 },{ 112, 8056 },{ 112, 8060 },
+{ 112, 8063 },{ 112, 8067 },{ 112, 8070 },{ 112, 8074 },
+{ 112, 8077 },{ 112, 8081 },{ 112, 8084 },{ 112, 8087 },
+{ 112, 8091 },{ 112, 8094 },{ 112, 8098 },{ 112, 8101 },
+{ 112, 8104 },{ 112, 8108 },{ 112, 8111 },{ 112, 8115 },
+{ 112, 8118 },{ 112, 8121 },{ 112, 8125 },{ 112, 8128 },
+{ 112, 8132 },{ 112, 8135 },{ 112, 8138 },{ 112, 8142 },
+{ 112, 8145 },{ 112, 8149 },{ 112, 8152 },{ 112, 8155 },
+{ 112, 8159 },{ 112, 8162 },{ 112, 8166 },{ 112, 8169 },
+{ 112, 8172 },{ 112, 8176 },{ 112, 8179 },{ 112, 8182 },
+{ 112, 8186 },{ 112, 8189 },{ 112, 8192 },{ 112, 8196 },
+{ 112, 8199 },{ 112, 8203 },{ 112, 8206 },{ 112, 8209 },
+{ 112, 8213 },{ 112, 8216 },{ 112, 8219 },{ 112, 8223 },
+{ 112, 8226 },{ 112, 8229 },{ 112, 8233 },{ 112, 8236 },
+{ 112, 8239 },{ 112, 8243 },{ 112, 8246 },{ 112, 8249 },
+{ 112, 8253 },{ 112, 8256 },{ 112, 8259 },{ 112, 8263 },
+{ 112, 8266 },{ 112, 8269 },{ 112, 8273 },{ 112, 8276 },
+{ 112, 8279 },{ 112, 8283 },{ 112, 8286 },{ 112, 8289 },
+{ 112, 8292 },{ 112, 8296 },{ 112, 8299 },{ 112, 8302 },
+{ 112, 8306 },{ 112, 8309 },{ 112, 8312 },{ 112, 8316 },
+{ 112, 8319 },{ 112, 8322 },{ 112, 8325 },{ 112, 8329 },
+{ 112, 8332 },{ 112, 8335 },{ 112, 8339 },{ 112, 8342 },
+{ 112, 8345 },{ 112, 8348 },{ 112, 8352 },{ 112, 8355 },
+{ 112, 8358 },{ 112, 8361 },{ 113, 8023 },{ 113, 8027 },
+{ 113, 8030 },{ 113, 8033 },{ 113, 8036 },{ 113, 8040 },
+{ 113, 8043 },{ 113, 8046 },{ 113, 8049 },{ 113, 8053 },
+{ 113, 8056 },{ 113, 8059 },{ 113, 8062 },{ 113, 8066 },
+{ 113, 8069 },{ 113, 8072 },{ 113, 8075 },{ 113, 8079 },
+{ 113, 8082 },{ 113, 8085 },{ 113, 8088 },{ 113, 8092 },
+{ 113, 8095 },{ 113, 8098 },{ 113, 8101 },{ 113, 8104 },
+{ 113, 8108 },{ 113, 8111 },{ 113, 8114 },{ 113, 8117 },
+{ 113, 8120 },{ 113, 8124 },{ 113, 8127 },{ 113, 8130 },
+{ 113, 8133 },{ 113, 8137 },{ 113, 8140 },{ 113, 8143 },
+{ 113, 8146 },{ 113, 8149 },{ 113, 8153 },{ 113, 8156 },
+{ 113, 8159 },{ 113, 8162 },{ 113, 8165 },{ 113, 8168 },
+{ 113, 8172 },{ 113, 8175 },{ 113, 8178 },{ 113, 8181 },
+{ 113, 8184 },{ 113, 8188 },{ 113, 8191 },{ 113, 8194 },
+{ 113, 8197 },{ 113, 8200 },{ 113, 8203 },{ 113, 8207 },
+{ 113, 8210 },{ 113, 8213 },{ 113, 8216 },{ 113, 8219 },
+{ 113, 8222 },{ 113, 8226 },{ 113, 8229 },{ 113, 8232 },
+{ 113, 8235 },{ 113, 8238 },{ 113, 8241 },{ 113, 8244 },
+{ 113, 8248 },{ 113, 8251 },{ 113, 8254 },{ 113, 8257 },
+{ 113, 8260 },{ 113, 8263 },{ 113, 8266 },{ 113, 8270 },
+{ 113, 8273 },{ 113, 8276 },{ 113, 8279 },{ 113, 8282 },
+{ 113, 8285 },{ 113, 8288 },{ 113, 8291 },{ 113, 8295 },
+{ 113, 8298 },{ 113, 8301 },{ 113, 8304 },{ 113, 8307 },
+{ 113, 8310 },{ 113, 8313 },{ 113, 8316 },{ 113, 8319 },
+{ 113, 8322 },{ 113, 8326 },{ 113, 8329 },{ 113, 8332 },
+{ 113, 8335 },{ 113, 8338 },{ 113, 8341 },{ 113, 8344 },
+{ 113, 8347 },{ 113, 8350 },{ 113, 8353 },{ 113, 8357 },
+{ 113, 8360 },{ 114, 8021 },{ 114, 8024 },{ 114, 8028 },
+{ 114, 8031 },{ 114, 8034 },{ 114, 8037 },{ 114, 8040 },
+{ 114, 8043 },{ 114, 8046 },{ 114, 8049 },{ 114, 8052 },
+{ 114, 8055 },{ 114, 8058 },{ 114, 8061 },{ 114, 8064 },
+{ 114, 8067 },{ 114, 8070 },{ 114, 8074 },{ 114, 8077 },
+{ 114, 8080 },{ 114, 8083 },{ 114, 8086 },{ 114, 8089 },
+{ 114, 8092 },{ 114, 8095 },{ 114, 8098 },{ 114, 8101 },
+{ 114, 8104 },{ 114, 8107 },{ 114, 8110 },{ 114, 8113 },
+{ 114, 8116 },{ 114, 8119 },{ 114, 8122 },{ 114, 8125 },
+{ 114, 8128 },{ 114, 8131 },{ 114, 8134 },{ 114, 8137 },
+{ 114, 8140 },{ 114, 8143 },{ 114, 8146 },{ 114, 8149 },
+{ 114, 8152 },{ 114, 8155 },{ 114, 8158 },{ 114, 8161 },
+{ 114, 8164 },{ 114, 8167 },{ 114, 8170 },{ 114, 8173 },
+{ 114, 8176 },{ 114, 8179 },{ 114, 8182 },{ 114, 8185 },
+{ 114, 8188 },{ 114, 8191 },{ 114, 8194 },{ 114, 8197 },
+{ 114, 8200 },{ 114, 8203 },{ 114, 8206 },{ 114, 8209 },
+{ 114, 8212 },{ 114, 8215 },{ 114, 8218 },{ 114, 8221 },
+{ 114, 8224 },{ 114, 8227 },{ 114, 8230 },{ 114, 8233 },
+{ 114, 8236 },{ 114, 8239 },{ 114, 8242 },{ 114, 8245 },
+{ 114, 8248 },{ 114, 8251 },{ 114, 8254 },{ 114, 8257 },
+{ 114, 8260 },{ 114, 8263 },{ 114, 8266 },{ 114, 8269 },
+{ 114, 8272 },{ 114, 8275 },{ 114, 8278 },{ 114, 8281 },
+{ 114, 8284 },{ 114, 8287 },{ 114, 8289 },{ 114, 8292 },
+{ 114, 8295 },{ 114, 8298 },{ 114, 8301 },{ 114, 8304 },
+{ 114, 8307 },{ 114, 8310 },{ 114, 8313 },{ 114, 8316 },
+{ 114, 8319 },{ 114, 8322 },{ 114, 8325 },{ 114, 8328 },
+{ 114, 8331 },{ 114, 8334 },{ 114, 8336 },{ 114, 8339 },
+{ 114, 8342 },{ 114, 8345 },{ 114, 8348 },{ 114, 8351 },
+{ 114, 8354 },{ 114, 8357 },{ 114, 8360 },{ 115, 8021 },
+{ 115, 8024 },{ 115, 8027 },{ 115, 8030 },{ 115, 8033 },
+{ 115, 8036 },{ 115, 8039 },{ 115, 8042 },{ 115, 8045 },
+{ 115, 8047 },{ 115, 8050 },{ 115, 8053 },{ 115, 8056 },
+{ 115, 8059 },{ 115, 8062 },{ 115, 8065 },{ 115, 8068 },
+{ 115, 8071 },{ 115, 8073 },{ 115, 8076 },{ 115, 8079 },
+{ 115, 8082 },{ 115, 8085 },{ 115, 8088 },{ 115, 8091 },
+{ 115, 8094 },{ 115, 8096 },{ 115, 8099 },{ 115, 8102 },
+{ 115, 8105 },{ 115, 8108 },{ 115, 8111 },{ 115, 8114 },
+{ 115, 8117 },{ 115, 8119 },{ 115, 8122 },{ 115, 8125 },
+{ 115, 8128 },{ 115, 8131 },{ 115, 8134 },{ 115, 8137 },
+{ 115, 8139 },{ 115, 8142 },{ 115, 8145 },{ 115, 8148 },
+{ 115, 8151 },{ 115, 8154 },{ 115, 8157 },{ 115, 8159 },
+{ 115, 8162 },{ 115, 8165 },{ 115, 8168 },{ 115, 8171 },
+{ 115, 8174 },{ 115, 8176 },{ 115, 8179 },{ 115, 8182 },
+{ 115, 8185 },{ 115, 8188 },{ 115, 8191 },{ 115, 8193 },
+{ 115, 8196 },{ 115, 8199 },{ 115, 8202 },{ 115, 8205 },
+{ 115, 8207 },{ 115, 8210 },{ 115, 8213 },{ 115, 8216 },
+{ 115, 8219 },{ 115, 8222 },{ 115, 8224 },{ 115, 8227 },
+{ 115, 8230 },{ 115, 8233 },{ 115, 8236 },{ 115, 8238 },
+{ 115, 8241 },{ 115, 8244 },{ 115, 8247 },{ 115, 8250 },
+{ 115, 8252 },{ 115, 8255 },{ 115, 8258 },{ 115, 8261 },
+{ 115, 8264 },{ 115, 8266 },{ 115, 8269 },{ 115, 8272 },
+{ 115, 8275 },{ 115, 8278 },{ 115, 8280 },{ 115, 8283 },
+{ 115, 8286 },{ 115, 8289 },{ 115, 8291 },{ 115, 8294 },
+{ 115, 8297 },{ 115, 8300 },{ 115, 8303 },{ 115, 8305 },
+{ 115, 8308 },{ 115, 8311 },{ 115, 8314 },{ 115, 8316 },
+{ 115, 8319 },{ 115, 8322 },{ 115, 8325 },{ 115, 8327 },
+{ 115, 8330 },{ 115, 8333 },{ 115, 8336 },{ 115, 8338 },
+{ 115, 8341 },{ 115, 8344 },{ 115, 8347 },{ 115, 8350 },
+{ 115, 8352 },{ 115, 8355 },{ 115, 8358 },{ 115, 8361 },
+{ 116, 8022 },{ 116, 8025 },{ 116, 8027 },{ 116, 8030 },
+{ 116, 8033 },{ 116, 8036 },{ 116, 8038 },{ 116, 8041 },
+{ 116, 8044 },{ 116, 8047 },{ 116, 8049 },{ 116, 8052 },
+{ 116, 8055 },{ 116, 8057 },{ 116, 8060 },{ 116, 8063 },
+{ 116, 8066 },{ 116, 8068 },{ 116, 8071 },{ 116, 8074 },
+{ 116, 8077 },{ 116, 8079 },{ 116, 8082 },{ 116, 8085 },
+{ 116, 8087 },{ 116, 8090 },{ 116, 8093 },{ 116, 8096 },
+{ 116, 8098 },{ 116, 8101 },{ 116, 8104 },{ 116, 8106 },
+{ 116, 8109 },{ 116, 8112 },{ 116, 8115 },{ 116, 8117 },
+{ 116, 8120 },{ 116, 8123 },{ 116, 8125 },{ 116, 8128 },
+{ 116, 8131 },{ 116, 8133 },{ 116, 8136 },{ 116, 8139 },
+{ 116, 8142 },{ 116, 8144 },{ 116, 8147 },{ 116, 8150 },
+{ 116, 8152 },{ 116, 8155 },{ 116, 8158 },{ 116, 8160 },
+{ 116, 8163 },{ 116, 8166 },{ 116, 8168 },{ 116, 8171 },
+{ 116, 8174 },{ 116, 8176 },{ 116, 8179 },{ 116, 8182 },
+{ 116, 8184 },{ 116, 8187 },{ 116, 8190 },{ 116, 8192 },
+{ 116, 8195 },{ 116, 8198 },{ 116, 8200 },{ 116, 8203 },
+{ 116, 8206 },{ 116, 8208 },{ 116, 8211 },{ 116, 8214 },
+{ 116, 8216 },{ 116, 8219 },{ 116, 8222 },{ 116, 8224 },
+{ 116, 8227 },{ 116, 8230 },{ 116, 8232 },{ 116, 8235 },
+{ 116, 8238 },{ 116, 8240 },{ 116, 8243 },{ 116, 8246 },
+{ 116, 8248 },{ 116, 8251 },{ 116, 8253 },{ 116, 8256 },
+{ 116, 8259 },{ 116, 8261 },{ 116, 8264 },{ 116, 8267 },
+{ 116, 8269 },{ 116, 8272 },{ 116, 8275 },{ 116, 8277 },
+{ 116, 8280 },{ 116, 8282 },{ 116, 8285 },{ 116, 8288 },
+{ 116, 8290 },{ 116, 8293 },{ 116, 8296 },{ 116, 8298 },
+{ 116, 8301 },{ 116, 8303 },{ 116, 8306 },{ 116, 8309 },
+{ 116, 8311 },{ 116, 8314 },{ 116, 8316 },{ 116, 8319 },
+{ 116, 8322 },{ 116, 8324 },{ 116, 8327 },{ 116, 8330 },
+{ 116, 8332 },{ 116, 8335 },{ 116, 8337 },{ 116, 8340 },
+{ 116, 8343 },{ 116, 8345 },{ 116, 8348 },{ 116, 8350 },
+{ 116, 8353 },{ 116, 8356 },{ 116, 8358 },{ 116, 8361 },
+{ 117, 8022 },{ 117, 8025 },{ 117, 8027 },{ 117, 8030 },
+{ 117, 8032 },{ 117, 8035 },{ 117, 8037 },{ 117, 8040 },
+{ 117, 8043 },{ 117, 8045 },{ 117, 8048 },{ 117, 8050 },
+{ 117, 8053 },{ 117, 8056 },{ 117, 8058 },{ 117, 8061 },
+{ 117, 8063 },{ 117, 8066 },{ 117, 8068 },{ 117, 8071 },
+{ 117, 8074 },{ 117, 8076 },{ 117, 8079 },{ 117, 8081 },
+{ 117, 8084 },{ 117, 8086 },{ 117, 8089 },{ 117, 8092 },
+{ 117, 8094 },{ 117, 8097 },{ 117, 8099 },{ 117, 8102 },
+{ 117, 8104 },{ 117, 8107 },{ 117, 8109 },{ 117, 8112 },
+{ 117, 8115 },{ 117, 8117 },{ 117, 8120 },{ 117, 8122 },
+{ 117, 8125 },{ 117, 8127 },{ 117, 8130 },{ 117, 8132 },
+{ 117, 8135 },{ 117, 8137 },{ 117, 8140 },{ 117, 8143 },
+{ 117, 8145 },{ 117, 8148 },{ 117, 8150 },{ 117, 8153 },
+{ 117, 8155 },{ 117, 8158 },{ 117, 8160 },{ 117, 8163 },
+{ 117, 8165 },{ 117, 8168 },{ 117, 8170 },{ 117, 8173 },
+{ 117, 8175 },{ 117, 8178 },{ 117, 8180 },{ 117, 8183 },
+{ 117, 8186 },{ 117, 8188 },{ 117, 8191 },{ 117, 8193 },
+{ 117, 8196 },{ 117, 8198 },{ 117, 8201 },{ 117, 8203 },
+{ 117, 8206 },{ 117, 8208 },{ 117, 8211 },{ 117, 8213 },
+{ 117, 8216 },{ 117, 8218 },{ 117, 8221 },{ 117, 8223 },
+{ 117, 8226 },{ 117, 8228 },{ 117, 8231 },{ 117, 8233 },
+{ 117, 8236 },{ 117, 8238 },{ 117, 8241 },{ 117, 8243 },
+{ 117, 8246 },{ 117, 8248 },{ 117, 8251 },{ 117, 8253 },
+{ 117, 8256 },{ 117, 8258 },{ 117, 8261 },{ 117, 8263 },
+{ 117, 8266 },{ 117, 8268 },{ 117, 8271 },{ 117, 8273 },
+{ 117, 8276 },{ 117, 8278 },{ 117, 8281 },{ 117, 8283 },
+{ 117, 8285 },{ 117, 8288 },{ 117, 8290 },{ 117, 8293 },
+{ 117, 8295 },{ 117, 8298 },{ 117, 8300 },{ 117, 8303 },
+{ 117, 8305 },{ 117, 8308 },{ 117, 8310 },{ 117, 8313 },
+{ 117, 8315 },{ 117, 8318 },{ 117, 8320 },{ 117, 8323 },
+{ 117, 8325 },{ 117, 8327 },{ 117, 8330 },{ 117, 8332 },
+{ 117, 8335 },{ 117, 8337 },{ 117, 8340 },{ 117, 8342 },
+{ 117, 8345 },{ 117, 8347 },{ 117, 8350 },{ 117, 8352 },
+{ 117, 8354 },{ 117, 8357 },{ 117, 8359 },{ 117, 8362 },
+{ 118, 8023 },{ 118, 8025 },{ 118, 8028 },{ 118, 8030 },
+{ 118, 8033 },{ 118, 8035 },{ 118, 8038 },{ 118, 8040 },
+{ 118, 8042 },{ 118, 8045 },{ 118, 8047 },{ 118, 8050 },
+{ 118, 8052 },{ 118, 8055 },{ 118, 8057 },{ 118, 8060 },
+{ 118, 8062 },{ 118, 8064 },{ 118, 8067 },{ 118, 8069 },
+{ 118, 8072 },{ 118, 8074 },{ 118, 8077 },{ 118, 8079 },
+{ 118, 8081 },{ 118, 8084 },{ 118, 8086 },{ 118, 8089 },
+{ 118, 8091 },{ 118, 8093 },{ 118, 8096 },{ 118, 8098 },
+{ 118, 8101 },{ 118, 8103 },{ 118, 8106 },{ 118, 8108 },
+{ 118, 8110 },{ 118, 8113 },{ 118, 8115 },{ 118, 8118 },
+{ 118, 8120 },{ 118, 8122 },{ 118, 8125 },{ 118, 8127 },
+{ 118, 8130 },{ 118, 8132 },{ 118, 8134 },{ 118, 8137 },
+{ 118, 8139 },{ 118, 8142 },{ 118, 8144 },{ 118, 8146 },
+{ 118, 8149 },{ 118, 8151 },{ 118, 8154 },{ 118, 8156 },
+{ 118, 8158 },{ 118, 8161 },{ 118, 8163 },{ 118, 8165 },
+{ 118, 8168 },{ 118, 8170 },{ 118, 8173 },{ 118, 8175 },
+{ 118, 8177 },{ 118, 8180 },{ 118, 8182 },{ 118, 8185 },
+{ 118, 8187 },{ 118, 8189 },{ 118, 8192 },{ 118, 8194 },
+{ 118, 8196 },{ 118, 8199 },{ 118, 8201 },{ 118, 8204 },
+{ 118, 8206 },{ 118, 8208 },{ 118, 8211 },{ 118, 8213 },
+{ 118, 8215 },{ 118, 8218 },{ 118, 8220 },{ 118, 8223 },
+{ 118, 8225 },{ 118, 8227 },{ 118, 8230 },{ 118, 8232 },
+{ 118, 8234 },{ 118, 8237 },{ 118, 8239 },{ 118, 8241 },
+{ 118, 8244 },{ 118, 8246 },{ 118, 8248 },{ 118, 8251 },
+{ 118, 8253 },{ 118, 8256 },{ 118, 8258 },{ 118, 8260 },
+{ 118, 8263 },{ 118, 8265 },{ 118, 8267 },{ 118, 8270 },
+{ 118, 8272 },{ 118, 8274 },{ 118, 8277 },{ 118, 8279 },
+{ 118, 8281 },{ 118, 8284 },{ 118, 8286 },{ 118, 8288 },
+{ 118, 8291 },{ 118, 8293 },{ 118, 8295 },{ 118, 8298 },
+{ 118, 8300 },{ 118, 8302 },{ 118, 8305 },{ 118, 8307 },
+{ 118, 8309 },{ 118, 8312 },{ 118, 8314 },{ 118, 8316 },
+{ 118, 8319 },{ 118, 8321 },{ 118, 8323 },{ 118, 8326 },
+{ 118, 8328 },{ 118, 8330 },{ 118, 8333 },{ 118, 8335 },
+{ 118, 8337 },{ 118, 8340 },{ 118, 8342 },{ 118, 8344 },
+{ 118, 8347 },{ 118, 8349 },{ 118, 8351 },{ 118, 8353 },
+{ 118, 8356 },{ 118, 8358 },{ 118, 8360 },{ 119, 8021 },
+{ 119, 8024 },{ 119, 8026 },{ 119, 8028 },{ 119, 8031 },
+{ 119, 8033 },{ 119, 8035 },{ 119, 8038 },{ 119, 8040 },
+{ 119, 8042 },{ 119, 8044 },{ 119, 8047 },{ 119, 8049 },
+{ 119, 8051 },{ 119, 8054 },{ 119, 8056 },{ 119, 8058 },
+{ 119, 8060 },{ 119, 8063 },{ 119, 8065 },{ 119, 8067 },
+{ 119, 8070 },{ 119, 8072 },{ 119, 8074 },{ 119, 8077 },
+{ 119, 8079 },{ 119, 8081 },{ 119, 8083 },{ 119, 8086 },
+{ 119, 8088 },{ 119, 8090 },{ 119, 8093 },{ 119, 8095 },
+{ 119, 8097 },{ 119, 8099 },{ 119, 8102 },{ 119, 8104 },
+{ 119, 8106 },{ 119, 8108 },{ 119, 8111 },{ 119, 8113 },
+{ 119, 8115 },{ 119, 8118 },{ 119, 8120 },{ 119, 8122 },
+{ 119, 8124 },{ 119, 8127 },{ 119, 8129 },{ 119, 8131 },
+{ 119, 8133 },{ 119, 8136 },{ 119, 8138 },{ 119, 8140 },
+{ 119, 8142 },{ 119, 8145 },{ 119, 8147 },{ 119, 8149 },
+{ 119, 8152 },{ 119, 8154 },{ 119, 8156 },{ 119, 8158 },
+{ 119, 8161 },{ 119, 8163 },{ 119, 8165 },{ 119, 8167 },
+{ 119, 8170 },{ 119, 8172 },{ 119, 8174 },{ 119, 8176 },
+{ 119, 8179 },{ 119, 8181 },{ 119, 8183 },{ 119, 8185 },
+{ 119, 8188 },{ 119, 8190 },{ 119, 8192 },{ 119, 8194 },
+{ 119, 8197 },{ 119, 8199 },{ 119, 8201 },{ 119, 8203 },
+{ 119, 8205 },{ 119, 8208 },{ 119, 8210 },{ 119, 8212 },
+{ 119, 8214 },{ 119, 8217 },{ 119, 8219 },{ 119, 8221 },
+{ 119, 8223 },{ 119, 8226 },{ 119, 8228 },{ 119, 8230 },
+{ 119, 8232 },{ 119, 8235 },{ 119, 8237 },{ 119, 8239 },
+{ 119, 8241 },{ 119, 8243 },{ 119, 8246 },{ 119, 8248 },
+{ 119, 8250 },{ 119, 8252 },{ 119, 8255 },{ 119, 8257 },
+{ 119, 8259 },{ 119, 8261 },{ 119, 8263 },{ 119, 8266 },
+{ 119, 8268 },{ 119, 8270 },{ 119, 8272 },{ 119, 8274 },
+{ 119, 8277 },{ 119, 8279 },{ 119, 8281 },{ 119, 8283 },
+{ 119, 8286 },{ 119, 8288 },{ 119, 8290 },{ 119, 8292 },
+{ 119, 8294 },{ 119, 8297 },{ 119, 8299 },{ 119, 8301 },
+{ 119, 8303 },{ 119, 8305 },{ 119, 8308 },{ 119, 8310 },
+{ 119, 8312 },{ 119, 8314 },{ 119, 8316 },{ 119, 8319 },
+{ 119, 8321 },{ 119, 8323 },{ 119, 8325 },{ 119, 8327 },
+{ 119, 8330 },{ 119, 8332 },{ 119, 8334 },{ 119, 8336 },
+{ 119, 8338 },{ 119, 8340 },{ 119, 8343 },{ 119, 8345 },
+{ 119, 8347 },{ 119, 8349 },{ 119, 8351 },{ 119, 8354 },
+{ 119, 8356 },{ 119, 8358 },{ 119, 8360 },{ 120, 8021 },
+{ 120, 8023 },{ 120, 8025 },{ 120, 8027 },{ 120, 8030 },
+{ 120, 8032 },{ 120, 8034 },{ 120, 8036 },{ 120, 8038 },
+{ 120, 8041 },{ 120, 8043 },{ 120, 8045 },{ 120, 8047 },
+{ 120, 8049 },{ 120, 8051 },{ 120, 8054 },{ 120, 8056 },
+{ 120, 8058 },{ 120, 8060 },{ 120, 8062 },{ 120, 8064 },
+{ 120, 8067 },{ 120, 8069 },{ 120, 8071 },{ 120, 8073 },
+{ 120, 8075 },{ 120, 8077 },{ 120, 8080 },{ 120, 8082 },
+{ 120, 8084 },{ 120, 8086 },{ 120, 8088 },{ 120, 8090 },
+{ 120, 8092 },{ 120, 8095 },{ 120, 8097 },{ 120, 8099 },
+{ 120, 8101 },{ 120, 8103 },{ 120, 8105 },{ 120, 8108 },
+{ 120, 8110 },{ 120, 8112 },{ 120, 8114 },{ 120, 8116 },
+{ 120, 8118 },{ 120, 8120 },{ 120, 8123 },{ 120, 8125 },
+{ 120, 8127 },{ 120, 8129 },{ 120, 8131 },{ 120, 8133 },
+{ 120, 8135 },{ 120, 8137 },{ 120, 8140 },{ 120, 8142 },
+{ 120, 8144 },{ 120, 8146 },{ 120, 8148 },{ 120, 8150 },
+{ 120, 8152 },{ 120, 8155 },{ 120, 8157 },{ 120, 8159 },
+{ 120, 8161 },{ 120, 8163 },{ 120, 8165 },{ 120, 8167 },
+{ 120, 8169 },{ 120, 8172 },{ 120, 8174 },{ 120, 8176 },
+{ 120, 8178 },{ 120, 8180 },{ 120, 8182 },{ 120, 8184 },
+{ 120, 8186 },{ 120, 8189 },{ 120, 8191 },{ 120, 8193 },
+{ 120, 8195 },{ 120, 8197 },{ 120, 8199 },{ 120, 8201 },
+{ 120, 8203 },{ 120, 8205 },{ 120, 8208 },{ 120, 8210 },
+{ 120, 8212 },{ 120, 8214 },{ 120, 8216 },{ 120, 8218 },
+{ 120, 8220 },{ 120, 8222 },{ 120, 8224 },{ 120, 8227 },
+{ 120, 8229 },{ 120, 8231 },{ 120, 8233 },{ 120, 8235 },
+{ 120, 8237 },{ 120, 8239 },{ 120, 8241 },{ 120, 8243 },
+{ 120, 8245 },{ 120, 8248 },{ 120, 8250 },{ 120, 8252 },
+{ 120, 8254 },{ 120, 8256 },{ 120, 8258 },{ 120, 8260 },
+{ 120, 8262 },{ 120, 8264 },{ 120, 8266 },{ 120, 8269 },
+{ 120, 8271 },{ 120, 8273 },{ 120, 8275 },{ 120, 8277 },
+{ 120, 8279 },{ 120, 8281 },{ 120, 8283 },{ 120, 8285 },
+{ 120, 8287 },{ 120, 8289 },{ 120, 8291 },{ 120, 8294 },
+{ 120, 8296 },{ 120, 8298 },{ 120, 8300 },{ 120, 8302 },
+{ 120, 8304 },{ 120, 8306 },{ 120, 8308 },{ 120, 8310 },
+{ 120, 8312 },{ 120, 8314 },{ 120, 8316 },{ 120, 8318 },
+{ 120, 8321 },{ 120, 8323 },{ 120, 8325 },{ 120, 8327 },
+{ 120, 8329 },{ 120, 8331 },{ 120, 8333 },{ 120, 8335 },
+{ 120, 8337 },{ 120, 8339 },{ 120, 8341 },{ 120, 8343 },
+{ 120, 8345 },{ 120, 8347 },{ 120, 8349 },{ 120, 8352 },
+{ 120, 8354 },{ 120, 8356 },{ 120, 8358 },{ 120, 8360 },
+{ 120, 8362 },{ 121, 8023 },{ 121, 8025 },{ 121, 8027 },
+{ 121, 8029 },{ 121, 8031 },{ 121, 8033 },{ 121, 8035 },
+{ 121, 8037 },{ 121, 8039 },{ 121, 8041 },{ 121, 8043 },
+{ 121, 8045 },{ 121, 8047 },{ 121, 8049 },{ 121, 8051 },
+{ 121, 8053 },{ 121, 8055 },{ 121, 8057 },{ 121, 8059 },
+{ 121, 8062 },{ 121, 8064 },{ 121, 8066 },{ 121, 8068 },
+{ 121, 8070 },{ 121, 8072 },{ 121, 8074 },{ 121, 8076 },
+{ 121, 8078 },{ 121, 8080 },{ 121, 8082 },{ 121, 8084 },
+{ 121, 8086 },{ 121, 8088 },{ 121, 8090 },{ 121, 8092 },
+{ 121, 8094 },{ 121, 8096 },{ 121, 8098 },{ 121, 8100 },
+{ 121, 8102 },{ 121, 8104 },{ 121, 8106 },{ 121, 8108 },
+{ 121, 8110 },{ 121, 8112 },{ 121, 8114 },{ 121, 8116 },
+{ 121, 8118 },{ 121, 8120 },{ 121, 8122 },{ 121, 8125 },
+{ 121, 8127 },{ 121, 8129 },{ 121, 8131 },{ 121, 8133 },
+{ 121, 8135 },{ 121, 8137 },{ 121, 8139 },{ 121, 8141 },
+{ 121, 8143 },{ 121, 8145 },{ 121, 8147 },{ 121, 8149 },
+{ 121, 8151 },{ 121, 8153 },{ 121, 8155 },{ 121, 8157 },
+{ 121, 8159 },{ 121, 8161 },{ 121, 8163 },{ 121, 8165 },
+{ 121, 8167 },{ 121, 8169 },{ 121, 8171 },{ 121, 8173 },
+{ 121, 8175 },{ 121, 8177 },{ 121, 8179 },{ 121, 8181 },
+{ 121, 8183 },{ 121, 8185 },{ 121, 8187 },{ 121, 8189 },
+{ 121, 8191 },{ 121, 8193 },{ 121, 8195 },{ 121, 8197 },
+{ 121, 8199 },{ 121, 8201 },{ 121, 8203 },{ 121, 8205 },
+{ 121, 8207 },{ 121, 8209 },{ 121, 8211 },{ 121, 8213 },
+{ 121, 8215 },{ 121, 8217 },{ 121, 8219 },{ 121, 8221 },
+{ 121, 8223 },{ 121, 8225 },{ 121, 8227 },{ 121, 8229 },
+{ 121, 8231 },{ 121, 8233 },{ 121, 8235 },{ 121, 8237 },
+{ 121, 8239 },{ 121, 8241 },{ 121, 8243 },{ 121, 8245 },
+{ 121, 8247 },{ 121, 8249 },{ 121, 8251 },{ 121, 8252 },
+{ 121, 8254 },{ 121, 8256 },{ 121, 8258 },{ 121, 8260 },
+{ 121, 8262 },{ 121, 8264 },{ 121, 8266 },{ 121, 8268 },
+{ 121, 8270 },{ 121, 8272 },{ 121, 8274 },{ 121, 8276 },
+{ 121, 8278 },{ 121, 8280 },{ 121, 8282 },{ 121, 8284 },
+{ 121, 8286 },{ 121, 8288 },{ 121, 8290 },{ 121, 8292 },
+{ 121, 8294 },{ 121, 8296 },{ 121, 8298 },{ 121, 8300 },
+{ 121, 8302 },{ 121, 8304 },{ 121, 8306 },{ 121, 8308 },
+{ 121, 8310 },{ 121, 8312 },{ 121, 8313 },{ 121, 8315 },
+{ 121, 8317 },{ 121, 8319 },{ 121, 8321 },{ 121, 8323 },
+{ 121, 8325 },{ 121, 8327 },{ 121, 8329 },{ 121, 8331 },
+{ 121, 8333 },{ 121, 8335 },{ 121, 8337 },{ 121, 8339 },
+{ 121, 8341 },{ 121, 8343 },{ 121, 8345 },{ 121, 8347 },
+{ 121, 8349 },{ 121, 8351 },{ 121, 8353 },{ 121, 8354 },
+{ 121, 8356 },{ 121, 8358 },{ 121, 8360 },{ 122, 8021 },
+{ 122, 8023 },{ 122, 8025 },{ 122, 8027 },{ 122, 8029 },
+{ 122, 8031 },{ 122, 8033 },{ 122, 8034 },{ 122, 8036 },
+{ 122, 8038 },{ 122, 8040 },{ 122, 8042 },{ 122, 8044 },
+{ 122, 8046 },{ 122, 8048 },{ 122, 8050 },{ 122, 8052 },
+{ 122, 8054 },{ 122, 8056 },{ 122, 8058 },{ 122, 8060 },
+{ 122, 8062 },{ 122, 8063 },{ 122, 8065 },{ 122, 8067 },
+{ 122, 8069 },{ 122, 8071 },{ 122, 8073 },{ 122, 8075 },
+{ 122, 8077 },{ 122, 8079 },{ 122, 8081 },{ 122, 8083 },
+{ 122, 8085 },{ 122, 8087 },{ 122, 8088 },{ 122, 8090 },
+{ 122, 8092 },{ 122, 8094 },{ 122, 8096 },{ 122, 8098 },
+{ 122, 8100 },{ 122, 8102 },{ 122, 8104 },{ 122, 8106 },
+{ 122, 8108 },{ 122, 8110 },{ 122, 8111 },{ 122, 8113 },
+{ 122, 8115 },{ 122, 8117 },{ 122, 8119 },{ 122, 8121 },
+{ 122, 8123 },{ 122, 8125 },{ 122, 8127 },{ 122, 8129 },
+{ 122, 8131 },{ 122, 8132 },{ 122, 8134 },{ 122, 8136 },
+{ 122, 8138 },{ 122, 8140 },{ 122, 8142 },{ 122, 8144 },
+{ 122, 8146 },{ 122, 8148 },{ 122, 8150 },{ 122, 8151 },
+{ 122, 8153 },{ 122, 8155 },{ 122, 8157 },{ 122, 8159 },
+{ 122, 8161 },{ 122, 8163 },{ 122, 8165 },{ 122, 8167 },
+{ 122, 8169 },{ 122, 8170 },{ 122, 8172 },{ 122, 8174 },
+{ 122, 8176 },{ 122, 8178 },{ 122, 8180 },{ 122, 8182 },
+{ 122, 8184 },{ 122, 8186 },{ 122, 8187 },{ 122, 8189 },
+{ 122, 8191 },{ 122, 8193 },{ 122, 8195 },{ 122, 8197 },
+{ 122, 8199 },{ 122, 8201 },{ 122, 8203 },{ 122, 8204 },
+{ 122, 8206 },{ 122, 8208 },{ 122, 8210 },{ 122, 8212 },
+{ 122, 8214 },{ 122, 8216 },{ 122, 8218 },{ 122, 8219 },
+{ 122, 8221 },{ 122, 8223 },{ 122, 8225 },{ 122, 8227 },
+{ 122, 8229 },{ 122, 8231 },{ 122, 8233 },{ 122, 8234 },
+{ 122, 8236 },{ 122, 8238 },{ 122, 8240 },{ 122, 8242 },
+{ 122, 8244 },{ 122, 8246 },{ 122, 8248 },{ 122, 8249 },
+{ 122, 8251 },{ 122, 8253 },{ 122, 8255 },{ 122, 8257 },
+{ 122, 8259 },{ 122, 8261 },{ 122, 8262 },{ 122, 8264 },
+{ 122, 8266 },{ 122, 8268 },{ 122, 8270 },{ 122, 8272 },
+{ 122, 8274 },{ 122, 8276 },{ 122, 8277 },{ 122, 8279 },
+{ 122, 8281 },{ 122, 8283 },{ 122, 8285 },{ 122, 8287 },
+{ 122, 8289 },{ 122, 8290 },{ 122, 8292 },{ 122, 8294 },
+{ 122, 8296 },{ 122, 8298 },{ 122, 8300 },{ 122, 8301 },
+{ 122, 8303 },{ 122, 8305 },{ 122, 8307 },{ 122, 8309 },
+{ 122, 8311 },{ 122, 8313 },{ 122, 8314 },{ 122, 8316 },
+{ 122, 8318 },{ 122, 8320 },{ 122, 8322 },{ 122, 8324 },
+{ 122, 8326 },{ 122, 8327 },{ 122, 8329 },{ 122, 8331 },
+{ 122, 8333 },{ 122, 8335 },{ 122, 8337 },{ 122, 8338 },
+{ 122, 8340 },{ 122, 8342 },{ 122, 8344 },{ 122, 8346 },
+{ 122, 8348 },{ 122, 8349 },{ 122, 8351 },{ 122, 8353 },
+{ 122, 8355 },{ 122, 8357 },{ 122, 8359 },{ 122, 8360 },
+{ 123, 8021 },{ 123, 8023 },{ 123, 8025 },{ 123, 8026 },
+{ 123, 8028 },{ 123, 8030 },{ 123, 8032 },{ 123, 8034 },
+{ 123, 8036 },{ 123, 8037 },{ 123, 8039 },{ 123, 8041 },
+{ 123, 8043 },{ 123, 8045 },{ 123, 8047 },{ 123, 8048 },
+{ 123, 8050 },{ 123, 8052 },{ 123, 8054 },{ 123, 8056 },
+{ 123, 8057 },{ 123, 8059 },{ 123, 8061 },{ 123, 8063 },
+{ 123, 8065 },{ 123, 8067 },{ 123, 8068 },{ 123, 8070 },
+{ 123, 8072 },{ 123, 8074 },{ 123, 8076 },{ 123, 8077 },
+{ 123, 8079 },{ 123, 8081 },{ 123, 8083 },{ 123, 8085 },
+{ 123, 8087 },{ 123, 8088 },{ 123, 8090 },{ 123, 8092 },
+{ 123, 8094 },{ 123, 8096 },{ 123, 8097 },{ 123, 8099 },
+{ 123, 8101 },{ 123, 8103 },{ 123, 8105 },{ 123, 8106 },
+{ 123, 8108 },{ 123, 8110 },{ 123, 8112 },{ 123, 8114 },
+{ 123, 8115 },{ 123, 8117 },{ 123, 8119 },{ 123, 8121 },
+{ 123, 8123 },{ 123, 8125 },{ 123, 8126 },{ 123, 8128 },
+{ 123, 8130 },{ 123, 8132 },{ 123, 8134 },{ 123, 8135 },
+{ 123, 8137 },{ 123, 8139 },{ 123, 8141 },{ 123, 8142 },
+{ 123, 8144 },{ 123, 8146 },{ 123, 8148 },{ 123, 8150 },
+{ 123, 8151 },{ 123, 8153 },{ 123, 8155 },{ 123, 8157 },
+{ 123, 8159 },{ 123, 8160 },{ 123, 8162 },{ 123, 8164 },
+{ 123, 8166 },{ 123, 8168 },{ 123, 8169 },{ 123, 8171 },
+{ 123, 8173 },{ 123, 8175 },{ 123, 8177 },{ 123, 8178 },
+{ 123, 8180 },{ 123, 8182 },{ 123, 8184 },{ 123, 8185 },
+{ 123, 8187 },{ 123, 8189 },{ 123, 8191 },{ 123, 8193 },
+{ 123, 8194 },{ 123, 8196 },{ 123, 8198 },{ 123, 8200 },
+{ 123, 8201 },{ 123, 8203 },{ 123, 8205 },{ 123, 8207 },
+{ 123, 8209 },{ 123, 8210 },{ 123, 8212 },{ 123, 8214 },
+{ 123, 8216 },{ 123, 8217 },{ 123, 8219 },{ 123, 8221 },
+{ 123, 8223 },{ 123, 8225 },{ 123, 8226 },{ 123, 8228 },
+{ 123, 8230 },{ 123, 8232 },{ 123, 8233 },{ 123, 8235 },
+{ 123, 8237 },{ 123, 8239 },{ 123, 8240 },{ 123, 8242 },
+{ 123, 8244 },{ 123, 8246 },{ 123, 8247 },{ 123, 8249 },
+{ 123, 8251 },{ 123, 8253 },{ 123, 8255 },{ 123, 8256 },
+{ 123, 8258 },{ 123, 8260 },{ 123, 8262 },{ 123, 8263 },
+{ 123, 8265 },{ 123, 8267 },{ 123, 8269 },{ 123, 8270 },
+{ 123, 8272 },{ 123, 8274 },{ 123, 8276 },{ 123, 8277 },
+{ 123, 8279 },{ 123, 8281 },{ 123, 8283 },{ 123, 8284 },
+{ 123, 8286 },{ 123, 8288 },{ 123, 8290 },{ 123, 8291 },
+{ 123, 8293 },{ 123, 8295 },{ 123, 8297 },{ 123, 8298 },
+{ 123, 8300 },{ 123, 8302 },{ 123, 8304 },{ 123, 8305 },
+{ 123, 8307 },{ 123, 8309 },{ 123, 8311 },{ 123, 8312 },
+{ 123, 8314 },{ 123, 8316 },{ 123, 8318 },{ 123, 8319 },
+{ 123, 8321 },{ 123, 8323 },{ 123, 8325 },{ 123, 8326 },
+{ 123, 8328 },{ 123, 8330 },{ 123, 8332 },{ 123, 8333 },
+{ 123, 8335 },{ 123, 8337 },{ 123, 8338 },{ 123, 8340 },
+{ 123, 8342 },{ 123, 8344 },{ 123, 8345 },{ 123, 8347 },
+{ 123, 8349 },{ 123, 8351 },{ 123, 8352 },{ 123, 8354 },
+{ 123, 8356 },{ 123, 8358 },{ 123, 8359 },{ 123, 8361 },
+{ 124, 8021 },{ 124, 8023 },{ 124, 8025 },{ 124, 8027 },
+{ 124, 8028 },{ 124, 8030 },{ 124, 8032 },{ 124, 8034 },
+{ 124, 8035 },{ 124, 8037 },{ 124, 8039 },{ 124, 8040 },
+{ 124, 8042 },{ 124, 8044 },{ 124, 8046 },{ 124, 8047 },
+{ 124, 8049 },{ 124, 8051 },{ 124, 8052 },{ 124, 8054 },
+{ 124, 8056 },{ 124, 8058 },{ 124, 8059 },{ 124, 8061 },
+{ 124, 8063 },{ 124, 8065 },{ 124, 8066 },{ 124, 8068 },
+{ 124, 8070 },{ 124, 8071 },{ 124, 8073 },{ 124, 8075 },
+{ 124, 8077 },{ 124, 8078 },{ 124, 8080 },{ 124, 8082 },
+{ 124, 8083 },{ 124, 8085 },{ 124, 8087 },{ 124, 8089 },
+{ 124, 8090 },{ 124, 8092 },{ 124, 8094 },{ 124, 8095 },
+{ 124, 8097 },{ 124, 8099 },{ 124, 8100 },{ 124, 8102 },
+{ 124, 8104 },{ 124, 8106 },{ 124, 8107 },{ 124, 8109 },
+{ 124, 8111 },{ 124, 8112 },{ 124, 8114 },{ 124, 8116 },
+{ 124, 8118 },{ 124, 8119 },{ 124, 8121 },{ 124, 8123 },
+{ 124, 8124 },{ 124, 8126 },{ 124, 8128 },{ 124, 8129 },
+{ 124, 8131 },{ 124, 8133 },{ 124, 8134 },{ 124, 8136 },
+{ 124, 8138 },{ 124, 8140 },{ 124, 8141 },{ 124, 8143 },
+{ 124, 8145 },{ 124, 8146 },{ 124, 8148 },{ 124, 8150 },
+{ 124, 8151 },{ 124, 8153 },{ 124, 8155 },{ 124, 8157 },
+{ 124, 8158 },{ 124, 8160 },{ 124, 8162 },{ 124, 8163 },
+{ 124, 8165 },{ 124, 8167 },{ 124, 8168 },{ 124, 8170 },
+{ 124, 8172 },{ 124, 8173 },{ 124, 8175 },{ 124, 8177 },
+{ 124, 8178 },{ 124, 8180 },{ 124, 8182 },{ 124, 8184 },
+{ 124, 8185 },{ 124, 8187 },{ 124, 8189 },{ 124, 8190 },
+{ 124, 8192 },{ 124, 8194 },{ 124, 8195 },{ 124, 8197 },
+{ 124, 8199 },{ 124, 8200 },{ 124, 8202 },{ 124, 8204 },
+{ 124, 8205 },{ 124, 8207 },{ 124, 8209 },{ 124, 8210 },
+{ 124, 8212 },{ 124, 8214 },{ 124, 8215 },{ 124, 8217 },
+{ 124, 8219 },{ 124, 8220 },{ 124, 8222 },{ 124, 8224 },
+{ 124, 8225 },{ 124, 8227 },{ 124, 8229 },{ 124, 8230 },
+{ 124, 8232 },{ 124, 8234 },{ 124, 8235 },{ 124, 8237 },
+{ 124, 8239 },{ 124, 8240 },{ 124, 8242 },{ 124, 8244 },
+{ 124, 8245 },{ 124, 8247 },{ 124, 8249 },{ 124, 8250 },
+{ 124, 8252 },{ 124, 8254 },{ 124, 8255 },{ 124, 8257 },
+{ 124, 8259 },{ 124, 8260 },{ 124, 8262 },{ 124, 8264 },
+{ 124, 8265 },{ 124, 8267 },{ 124, 8269 },{ 124, 8270 },
+{ 124, 8272 },{ 124, 8274 },{ 124, 8275 },{ 124, 8277 },
+{ 124, 8279 },{ 124, 8280 },{ 124, 8282 },{ 124, 8284 },
+{ 124, 8285 },{ 124, 8287 },{ 124, 8289 },{ 124, 8290 },
+{ 124, 8292 },{ 124, 8294 },{ 124, 8295 },{ 124, 8297 },
+{ 124, 8299 },{ 124, 8300 },{ 124, 8302 },{ 124, 8303 },
+{ 124, 8305 },{ 124, 8307 },{ 124, 8308 },{ 124, 8310 },
+{ 124, 8312 },{ 124, 8313 },{ 124, 8315 },{ 124, 8317 },
+{ 124, 8318 },{ 124, 8320 },{ 124, 8322 },{ 124, 8323 },
+{ 124, 8325 },{ 124, 8326 },{ 124, 8328 },{ 124, 8330 },
+{ 124, 8331 },{ 124, 8333 },{ 124, 8335 },{ 124, 8336 },
+{ 124, 8338 },{ 124, 8340 },{ 124, 8341 },{ 124, 8343 },
+{ 124, 8345 },{ 124, 8346 },{ 124, 8348 },{ 124, 8349 },
+{ 124, 8351 },{ 124, 8353 },{ 124, 8354 },{ 124, 8356 },
+{ 124, 8358 },{ 124, 8359 },{ 124, 8361 },{ 125, 8021 },
+{ 125, 8023 },{ 125, 8024 },{ 125, 8026 },{ 125, 8028 },
+{ 125, 8029 },{ 125, 8031 },{ 125, 8033 },{ 125, 8034 },
+{ 125, 8036 },{ 125, 8037 },{ 125, 8039 },{ 125, 8041 },
+{ 125, 8042 },{ 125, 8044 },{ 125, 8046 },{ 125, 8047 },
+{ 125, 8049 },{ 125, 8051 },{ 125, 8052 },{ 125, 8054 },
+{ 125, 8055 },{ 125, 8057 },{ 125, 8059 },{ 125, 8060 },
+{ 125, 8062 },{ 125, 8063 },{ 125, 8065 },{ 125, 8067 },
+{ 125, 8068 },{ 125, 8070 },{ 125, 8072 },{ 125, 8073 },
+{ 125, 8075 },{ 125, 8076 },{ 125, 8078 },{ 125, 8080 },
+{ 125, 8081 },{ 125, 8083 },{ 125, 8085 },{ 125, 8086 },
+{ 125, 8088 },{ 125, 8089 },{ 125, 8091 },{ 125, 8093 },
+{ 125, 8094 },{ 125, 8096 },{ 125, 8097 },{ 125, 8099 },
+{ 125, 8101 },{ 125, 8102 },{ 125, 8104 },{ 125, 8105 },
+{ 125, 8107 },{ 125, 8109 },{ 125, 8110 },{ 125, 8112 },
+{ 125, 8114 },{ 125, 8115 },{ 125, 8117 },{ 125, 8118 },
+{ 125, 8120 },{ 125, 8122 },{ 125, 8123 },{ 125, 8125 },
+{ 125, 8126 },{ 125, 8128 },{ 125, 8130 },{ 125, 8131 },
+{ 125, 8133 },{ 125, 8134 },{ 125, 8136 },{ 125, 8138 },
+{ 125, 8139 },{ 125, 8141 },{ 125, 8142 },{ 125, 8144 },
+{ 125, 8146 },{ 125, 8147 },{ 125, 8149 },{ 125, 8150 },
+{ 125, 8152 },{ 125, 8154 },{ 125, 8155 },{ 125, 8157 },
+{ 125, 8158 },{ 125, 8160 },{ 125, 8162 },{ 125, 8163 },
+{ 125, 8165 },{ 125, 8166 },{ 125, 8168 },{ 125, 8170 },
+{ 125, 8171 },{ 125, 8173 },{ 125, 8174 },{ 125, 8176 },
+{ 125, 8177 },{ 125, 8179 },{ 125, 8181 },{ 125, 8182 },
+{ 125, 8184 },{ 125, 8185 },{ 125, 8187 },{ 125, 8189 },
+{ 125, 8190 },{ 125, 8192 },{ 125, 8193 },{ 125, 8195 },
+{ 125, 8197 },{ 125, 8198 },{ 125, 8200 },{ 125, 8201 },
+{ 125, 8203 },{ 125, 8204 },{ 125, 8206 },{ 125, 8208 },
+{ 125, 8209 },{ 125, 8211 },{ 125, 8212 },{ 125, 8214 },
+{ 125, 8216 },{ 125, 8217 },{ 125, 8219 },{ 125, 8220 },
+{ 125, 8222 },{ 125, 8223 },{ 125, 8225 },{ 125, 8227 },
+{ 125, 8228 },{ 125, 8230 },{ 125, 8231 },{ 125, 8233 },
+{ 125, 8234 },{ 125, 8236 },{ 125, 8238 },{ 125, 8239 },
+{ 125, 8241 },{ 125, 8242 },{ 125, 8244 },{ 125, 8245 },
+{ 125, 8247 },{ 125, 8249 },{ 125, 8250 },{ 125, 8252 },
+{ 125, 8253 },{ 125, 8255 },{ 125, 8256 },{ 125, 8258 },
+{ 125, 8260 },{ 125, 8261 },{ 125, 8263 },{ 125, 8264 },
+{ 125, 8266 },{ 125, 8267 },{ 125, 8269 },{ 125, 8271 },
+{ 125, 8272 },{ 125, 8274 },{ 125, 8275 },{ 125, 8277 },
+{ 125, 8278 },{ 125, 8280 },{ 125, 8281 },{ 125, 8283 },
+{ 125, 8285 },{ 125, 8286 },{ 125, 8288 },{ 125, 8289 },
+{ 125, 8291 },{ 125, 8292 },{ 125, 8294 },{ 125, 8296 },
+{ 125, 8297 },{ 125, 8299 },{ 125, 8300 },{ 125, 8302 },
+{ 125, 8303 },{ 125, 8305 },{ 125, 8306 },{ 125, 8308 },
+{ 125, 8310 },{ 125, 8311 },{ 125, 8313 },{ 125, 8314 },
+{ 125, 8316 },{ 125, 8317 },{ 125, 8319 },{ 125, 8320 },
+{ 125, 8322 },{ 125, 8324 },{ 125, 8325 },{ 125, 8327 },
+{ 125, 8328 },{ 125, 8330 },{ 125, 8331 },{ 125, 8333 },
+{ 125, 8334 },{ 125, 8336 },{ 125, 8337 },{ 125, 8339 },
+{ 125, 8341 },{ 125, 8342 },{ 125, 8344 },{ 125, 8345 },
+{ 125, 8347 },{ 125, 8348 },{ 125, 8350 },{ 125, 8351 },
+{ 125, 8353 },{ 125, 8354 },{ 125, 8356 },{ 125, 8358 },
+{ 125, 8359 },{ 125, 8361 },{ 126, 8021 },{ 126, 8022 },
+{ 126, 8024 },{ 126, 8025 },{ 126, 8027 },{ 126, 8029 },
+{ 126, 8030 },{ 126, 8032 },{ 126, 8033 },{ 126, 8035 },
+{ 126, 8036 },{ 126, 8038 },{ 126, 8039 },{ 126, 8041 },
+{ 126, 8042 },{ 126, 8044 },{ 126, 8045 },{ 126, 8047 },
+{ 126, 8049 },{ 126, 8050 },{ 126, 8052 },{ 126, 8053 },
+{ 126, 8055 },{ 126, 8056 },{ 126, 8058 },{ 126, 8059 },
+{ 126, 8061 },{ 126, 8062 },{ 126, 8064 },{ 126, 8065 },
+{ 126, 8067 },{ 126, 8068 },{ 126, 8070 },{ 126, 8071 },
+{ 126, 8073 },{ 126, 8075 },{ 126, 8076 },{ 126, 8078 },
+{ 126, 8079 },{ 126, 8081 },{ 126, 8082 },{ 126, 8084 },
+{ 126, 8085 },{ 126, 8087 },{ 126, 8088 },{ 126, 8090 },
+{ 126, 8091 },{ 126, 8093 },{ 126, 8094 },{ 126, 8096 },
+{ 126, 8097 },{ 126, 8099 },{ 126, 8100 },{ 126, 8102 },
+{ 126, 8103 },{ 126, 8105 },{ 126, 8107 },{ 126, 8108 },
+{ 126, 8110 },{ 126, 8111 },{ 126, 8113 },{ 126, 8114 },
+{ 126, 8116 },{ 126, 8117 },{ 126, 8119 },{ 126, 8120 },
+{ 126, 8122 },{ 126, 8123 },{ 126, 8125 },{ 126, 8126 },
+{ 126, 8128 },{ 126, 8129 },{ 126, 8131 },{ 126, 8132 },
+{ 126, 8134 },{ 126, 8135 },{ 126, 8137 },{ 126, 8138 },
+{ 126, 8140 },{ 126, 8141 },{ 126, 8143 },{ 126, 8144 },
+{ 126, 8146 },{ 126, 8147 },{ 126, 8149 },{ 126, 8150 },
+{ 126, 8152 },{ 126, 8153 },{ 126, 8155 },{ 126, 8156 },
+{ 126, 8158 },{ 126, 8159 },{ 126, 8161 },{ 126, 8162 },
+{ 126, 8164 },{ 126, 8165 },{ 126, 8167 },{ 126, 8168 },
+{ 126, 8170 },{ 126, 8171 },{ 126, 8173 },{ 126, 8174 },
+{ 126, 8176 },{ 126, 8177 },{ 126, 8179 },{ 126, 8180 },
+{ 126, 8182 },{ 126, 8183 },{ 126, 8185 },{ 126, 8186 },
+{ 126, 8188 },{ 126, 8189 },{ 126, 8191 },{ 126, 8192 },
+{ 126, 8194 },{ 126, 8195 },{ 126, 8197 },{ 126, 8198 },
+{ 126, 8200 },{ 126, 8201 },{ 126, 8203 },{ 126, 8204 },
+{ 126, 8206 },{ 126, 8207 },{ 126, 8209 },{ 126, 8210 },
+{ 126, 8212 },{ 126, 8213 },{ 126, 8215 },{ 126, 8216 },
+{ 126, 8218 },{ 126, 8219 },{ 126, 8221 },{ 126, 8222 },
+{ 126, 8224 },{ 126, 8225 },{ 126, 8227 },{ 126, 8228 },
+{ 126, 8230 },{ 126, 8231 },{ 126, 8233 },{ 126, 8234 },
+{ 126, 8236 },{ 126, 8237 },{ 126, 8239 },{ 126, 8240 },
+{ 126, 8242 },{ 126, 8243 },{ 126, 8245 },{ 126, 8246 },
+{ 126, 8248 },{ 126, 8249 },{ 126, 8251 },{ 126, 8252 },
+{ 126, 8254 },{ 126, 8255 },{ 126, 8257 },{ 126, 8258 },
+{ 126, 8259 },{ 126, 8261 },{ 126, 8262 },{ 126, 8264 },
+{ 126, 8265 },{ 126, 8267 },{ 126, 8268 },{ 126, 8270 },
+{ 126, 8271 },{ 126, 8273 },{ 126, 8274 },{ 126, 8276 },
+{ 126, 8277 },{ 126, 8279 },{ 126, 8280 },{ 126, 8282 },
+{ 126, 8283 },{ 126, 8285 },{ 126, 8286 },{ 126, 8288 },
+{ 126, 8289 },{ 126, 8290 },{ 126, 8292 },{ 126, 8293 },
+{ 126, 8295 },{ 126, 8296 },{ 126, 8298 },{ 126, 8299 },
+{ 126, 8301 },{ 126, 8302 },{ 126, 8304 },{ 126, 8305 },
+{ 126, 8307 },{ 126, 8308 },{ 126, 8310 },{ 126, 8311 },
+{ 126, 8313 },{ 126, 8314 },{ 126, 8315 },{ 126, 8317 },
+{ 126, 8318 },{ 126, 8320 },{ 126, 8321 },{ 126, 8323 },
+{ 126, 8324 },{ 126, 8326 },{ 126, 8327 },{ 126, 8329 },
+{ 126, 8330 },{ 126, 8332 },{ 126, 8333 },{ 126, 8334 },
+{ 126, 8336 },{ 126, 8337 },{ 126, 8339 },{ 126, 8340 },
+{ 126, 8342 },{ 126, 8343 },{ 126, 8345 },{ 126, 8346 },
+{ 126, 8348 },{ 126, 8349 },{ 126, 8351 },{ 126, 8352 },
+{ 126, 8353 },{ 126, 8355 },{ 126, 8356 },{ 126, 8358 },
+{ 126, 8359 },{ 126, 8361 },{ 127, 8021 },{ 127, 8022 },
+{ 127, 8024 },{ 127, 8025 },{ 127, 8027 },{ 127, 8028 },
+{ 127, 8030 },{ 127, 8031 },{ 127, 8032 },{ 127, 8034 },
+{ 127, 8035 },{ 127, 8037 },{ 127, 8038 },{ 127, 8040 },
+{ 127, 8041 },{ 127, 8043 },{ 127, 8044 },{ 127, 8046 },
+{ 127, 8047 },{ 127, 8048 },{ 127, 8050 },{ 127, 8051 },
+{ 127, 8053 },{ 127, 8054 },{ 127, 8056 },{ 127, 8057 },
+{ 127, 8059 },{ 127, 8060 },{ 127, 8061 },{ 127, 8063 },
+{ 127, 8064 },{ 127, 8066 },{ 127, 8067 },{ 127, 8069 }

+ 39 - 0
src/include/NESCarts.h

@@ -0,0 +1,39 @@
+/*
+ *  Cart manager - The TI-NESulator Project
+ *  NESCart.h
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-04-16 01:55:35 +0200 (lun, 16 avr 2007) $
+ *  $Author: godzil $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/NESCarts.h $
+ *  $Revision: 39 $
+ */
+
+#ifndef NESCARTS_H
+#define NESCARTS_H
+
+#include "types.h"
+
+#define iNES_MIRROR  0x01
+#define iNES_BATTERY 0x02
+#define iNES_TRAINER 0x04
+#define iNES_4SCREEN 0x08
+
+typedef struct NesCart_
+{  
+    unsigned long PROMSize, /* Size of PROM */
+                  VROMSize; /* Size of VROM */
+    char MapperID;          /* Mapper Type */
+    byte Flags;
+    char *FileName;
+    byte *File;             /* Pointer on the file in memory */
+    byte *PROMBanks;        /* Pointer on the first PROM */
+    byte *VROMBanks;        /* Pointer on the first VROM */
+} NesCart;
+
+void DumpCartProperties();
+int LoadCart(const char *filename, NesCart * cart);
+
+#endif

+ 232 - 0
src/include/Sound.h

@@ -0,0 +1,232 @@
+/** EMULib Emulation Library *********************************/
+/**                                                         **/
+/**                          Sound.h                        **/
+/**                                                         **/
+/** This file defines standard sound generation API and     **/
+/** functions needed to log soundtrack into a MIDI file.    **/
+/** See Sound.c and the sound drivers for the code.         **/ 
+/**                                                         **/
+/** Copyright (C) Marat Fayzullin 1996-2007                 **/
+/**     You are not allowed to distribute this software     **/
+/**     commercially. Please, notify me, if you make any    **/
+/**     changes to this file.                               **/
+/*************************************************************/
+
+#ifndef SOUND_H
+#define SOUND_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+                               /* SetSound() arguments:      */
+#define SND_MELODIC     0      /* Melodic sound (default)    */
+#define SND_RECTANGLE   0      /* Rectangular wave           */
+#define SND_QS_DU0        5
+#define SND_QS_DU1        6
+#define SND_QS_DU2        7
+#define SND_QS_DU3        8
+#define SND_TRIANGLE    1      /* Triangular wave (1/2 rect.)*/
+#define SND_NOISE       2      /* White noise                */
+#define SND_PERIODIC    3      /* Periodic noise (not im-ed) */
+#define SND_WAVE        4      /* Wave sound set by SetWave()*/
+#define SND_MIDI        0x100  /* MIDI instrument (ORable)   */
+
+                               /* Drum() arguments:          */
+#define DRM_CLICK       0      /* Click (default)            */
+#define DRM_MIDI        0x100  /* MIDI drum (ORable)         */
+
+                               /* MIDI characteristics:      */
+#define MIDI_CHANNELS   16     /* Number of MIDI channels    */
+#define MIDI_MINFREQ    9      /* Min MIDI frequency (Hz)    */
+#define MIDI_MAXFREQ    12285  /* Max MIDI frequency (Hz)    */
+#define MIDI_DIVISIONS  1000   /* Number of ticks per second */
+
+                               /* MIDILogging() arguments:   */
+#define MIDI_OFF        0      /* Turn MIDI logging off      */
+#define MIDI_ON         1      /* Turn MIDI logging on       */
+#define MIDI_TOGGLE     2      /* Toggle MIDI logging        */
+#define MIDI_QUERY      3      /* Query MIDI logging status  */
+
+/** TrashSound() *********************************************/
+/** Shut down sound driver. Each driver implements its own  **/
+/** TrashSound() function.                                  **/
+/*************************************************************/
+void TrashSound(void);
+
+/** Sound() **************************************************/
+/** Generate sound of given frequency (Hz) and volume       **/
+/** (0..255) via given channel. Setting Freq=0 or Volume=0  **/
+/** turns sound off.                                        **/
+/*************************************************************/
+void Sound(int Channel,int Freq,int Volume);
+
+/** Drum() ***************************************************/
+/** Hit a drum of given type with given force (0..255).     **/
+/** MIDI drums can be used by ORing their numbers with      **/
+/** SND_MIDI.                                               **/
+/*************************************************************/
+void Drum(int Type,int Force);
+
+/** SetSound() ***********************************************/
+/** Set sound type at a given channel. MIDI instruments can **/
+/** be set directly by ORing their numbers with SND_MIDI.   **/
+/*************************************************************/
+void SetSound(int Channel,int NewType);
+
+/** SetChannels() ********************************************/
+/** Set master volume (0..255) and switch channels on/off.  **/
+/** Each channel N has corresponding bit 2^N in Switch. Set **/
+/** or reset this bit to turn the channel on or off.        **/ 
+/*************************************************************/
+void SetChannels(int Volume,int Switch);
+
+/** SetWave() ************************************************/
+/** Set waveform for a given channel. The channel will be   **/
+/** marked with sound type SND_WAVE. Set Rate=0 if you want **/
+/** waveform to be an instrument or set it to the waveform  **/
+/** own playback rate.                                      **/
+/*************************************************************/
+void SetWave(int Channel,const signed char *Data,int Length,int Rate);
+
+/** GetWave() ************************************************/
+/** Get current read position for the buffer set with the   **/
+/** SetWave() call. Returns 0 if no buffer has been set, or **/
+/** if there is no playrate set (i.e. wave is instrument).  **/
+/*************************************************************/
+const signed char *GetWave(int Channel);
+
+/** InitMIDI() ***********************************************/
+/** Initialize soundtrack logging into MIDI file FileName.  **/
+/** Repeated calls to InitMIDI() will close current MIDI    **/
+/** file and continue logging into a new one.               **/ 
+/*************************************************************/
+void InitMIDI(const char *FileName);
+
+/** TrashMIDI() **********************************************/
+/** Finish logging soundtrack and close the MIDI file.      **/
+/*************************************************************/
+void TrashMIDI(void);
+
+/** MIDILogging() ********************************************/
+/** Turn soundtrack logging on/off and return its current   **/
+/** status. Possible values of Switch are MIDI_OFF (turn    **/
+/** logging off), MIDI_ON (turn logging on), MIDI_TOGGLE    **/
+/** (toggle logging), and MIDI_QUERY (just return current   **/
+/** state of logging).                                      **/
+/*************************************************************/
+int MIDILogging(int Switch);
+
+/** MIDITicks() **********************************************/
+/** Log N 1ms MIDI ticks.                                   **/
+/*************************************************************/
+void MIDITicks(int N);
+
+//#ifdef UNIX
+#define SND_CHANNELS    16   /* Number of channels           */
+#define SND_SAMPLESIZE  256  /* Max. SetWave() sample size   */
+#define SND_BUFSIZE     256  /* Buffer size, <= 2^SND_BITS   */
+#define SND_BITS        8    /* Number of bits in a fragment */
+#define SND_BUFFERS     64   /* Number of fragments, >= 2    */
+    /* Bigger value results in better behaviour on loaded    */
+    /* but output gets more delayed.                         */
+
+/** InitSound() **********************************************/
+/** Initialize Unix sound driver with given synthesis rate. **/
+/** Returns Rate on success, 0 otherwise. Pass Rate=0 to    **/
+/** skip initialization and be silent. Pass Verbose!=0 to   **/
+/** see initialization messages.                            **/
+/*************************************************************/
+#warning You Suck !
+int InitSound(int Rate,int Verbose);
+
+/** StopSound() **********************************************/
+/** Temporarily suspend sound.                              **/
+/*************************************************************/
+void StopSound(void);
+
+/** ResumeSound() ********************************************/
+/** Resume sound after StopSound().                         **/
+/*************************************************************/
+void ResumeSound(void);
+//#endif /* UNIX */
+
+#ifdef MSDOS
+#define SND_CHANNELS    16     /* Number of sound channels   */
+#define OPL_CHANNELS    7      /* Number of Adlib channels   */
+#define SND_SAMPLESIZE  256    /* Max. SetWave() sample size */
+#define SND_BUFSIZE     512    /* Buffer size for DMA        */
+#define SND_MAXDELAY    10     /* Maximal sound delay 1/n s  */
+
+/** InitSound() **********************************************/
+/** Initialize sound. Returns Rate on success, 0 otherwise. **/
+/** Rate=0 to skip initialization (will be silent).         **/
+/*************************************************************/
+int InitSound(unsigned int Rate,unsigned int Latency);
+#endif /* MSDOS */
+
+#ifdef WINDOWS
+#define SND_CHANNELS    16     /* Number of channels         */
+#define SND_SAMPLESIZE  256    /* Max. SetWave() sample size */
+#define SND_BUFSIZE     512    /* Size of a wave buffer      */
+#define SND_BUFFERS     32     /* Number of wave buffers     */
+
+#include <Windows.h>
+
+/** InitSound() **********************************************/
+/** Initialize Windows sound driver with given synthesis    **/
+/** rate. Returns Rate on success, 0 otherwise. Pass Rate=0 **/
+/** to skip initialization and be silent. Pass Rate=1 to    **/
+/** use MIDI (midiOut). Pass Rate=8192..44100 to use wave   **/
+/** synthesis (waveOut). Number of wave synthesis buffers   **/
+/** must be in 2..SND_BUFFERS range.                        **/
+/*************************************************************/
+unsigned int InitSound(unsigned int Rate,unsigned int Delay);
+#endif /* WINDOWS */
+
+
+#if 0
+   
+#ifndef MSDOS
+#ifndef WINDOWS
+#ifndef UNIX
+#define SND_CHANNELS MIDI_CHANNELS         /* Default number */
+#endif
+#endif
+#endif
+
+/** InitSound() **********************************************/
+/** Initialize Series60 sound driver with given synthesis   **/
+/** rate. Returns Rate on success, 0 otherwise. Pass Rate=0 **/
+/** to skip initialization and be silent.                   **/
+/*************************************************************/
+unsigned int InitSound(unsigned int Rate,unsigned int Delay);
+#endif
+
+/** RenderAudio() ********************************************/
+/** Render given number of melodic sound samples. Returns   **/
+/** number of samples actually rendered.                    **/
+/*************************************************************/
+unsigned int RenderAudio(unsigned int Samples);
+
+/** SndDriver ************************************************/
+/** Each sound driver should fill this structure with       **/
+/** pointers to hardware-dependent handlers. This has to be **/
+/** done inside the InitSound() function.                   **/
+/*************************************************************/
+struct SndDriverStruct
+{
+  void (*SetSound)(int Channel,int NewType);
+  void (*Drum)(int Type,int Force);
+  void (*SetChannels)(int Volume,int Switch);
+  void (*Sound)(int Channel,int NewFreq,int NewVolume);
+  void (*SetWave)(int Channel,const signed char *Data,int Length,int Freq);
+  const signed char *(*GetWave)(int Channel);
+};
+extern struct SndDriverStruct SndDriver;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SOUND_H */

+ 60 - 0
src/include/mappers/manager.h

@@ -0,0 +1,60 @@
+/*
+ *  Mappers manager & facilities - The TI-NESulator Project
+ *  mappers.h
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate$
+ *  $Author$
+ *  $HeadURL$
+ *  $Revision$
+ *
+ */
+
+#ifndef MAPPERS_H
+#define MAPPERS_H
+
+#include <types.h>
+#include <stdio.h>
+#include <NESCarts.h>
+
+typedef int (*MapperInit)      (NesCart * cart);
+typedef int (*MapperWriteHook) (register unsigned short Addr, 
+                                register unsigned char Value);
+typedef int (*MapperIRQ)       (int cycledone);
+typedef void (*MapperDump)     ();
+
+#ifdef __TINES_MAPPERS__
+
+extern NesCart *Cart;
+
+/* Available functions for mappers */
+#define GETLAST08KBANK(c) ((c->PROMSize>>13)-1)
+#define GETLAST16KBANK(c) ((c->PROMSize>>14)-1)
+#define GETLAST32KBANK(c) ((c->PROMSize>>15)-1)
+
+void map_sram();     /* Map SRAM   */
+void unmap_sram();   /* Unmap SRAM */
+
+void set_vrom_bank_1k(unsigned short addr,int slot);
+void set_vrom_bank_2k(unsigned short addr,int slot);
+void set_vrom_bank_4k(unsigned short addr,int slot);
+void set_vrom_bank_8k(unsigned short addr, int slot);
+
+void set_prom_bank_8k(unsigned short addr,int slot);
+void set_prom_bank_16k(unsigned short addr,int slot);
+void set_prom_bank_32k(unsigned short addr,int slot);
+
+#else /* __TINES_MAPPERS__ */
+
+/* Available functions outside of mappers */
+
+       void mapper_list       ();
+       int  mapper_init       (NesCart *cart);
+extern int  (*mapper_irqloop) (int cyclodone);
+extern void (*mapper_dump)    (FILE *fp);
+
+#endif /* __TINES_MAPPERS__ */
+
+#endif

+ 68 - 0
src/include/memory/manager.h

@@ -0,0 +1,68 @@
+/*
+ *  6502 Memory manager - The TI-NESulator Project
+ *  memory.h - Taken from the Quick6502 project
+ *
+ *  Created by Manoël Trapier on 18/09/06.
+ *  Copyright 2003-2007 986 Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-04-05 16:30:20 +0200 (jeu, 05 avr 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/memory.h $
+ *  $Revision: 31 $
+ *
+ */
+#ifndef MEMORY_H
+#define MEMORY_H
+
+#include "types.h"
+
+#define ATTR_PAGE_HAVE_RDHOOK    0x20
+#define ATTR_PAGE_HAVE_WRHOOK    0x10
+#define ATTR_PAGE_WRITEABLE      0x08
+#define ATTR_PAGE_READABLE       0x04
+#define ATTR_PAGE_GHOST          0x02
+#define ATTR_PAGE_MAPPED         0x01
+
+typedef byte (*func_rdhook)(byte /* addr */);
+typedef void (*func_wrhook)(byte addr, byte data);
+
+/* Functions to manage pages data */
+void set_page_ptr(byte page, byte *ptr);
+void set_page_ptr_1k(byte page, byte *ptr);
+void set_page_ptr_2k(byte page, byte *ptr);
+void set_page_ptr_4k(byte page, byte *ptr);
+void set_page_ptr_8k(byte page, byte *ptr);
+void set_page_ptr_16k(byte page, byte *ptr);
+void set_page_ptr_32k(byte page, byte *ptr);
+
+byte *get_page_ptr(byte page);
+
+
+/* Functions to set pages attributes */
+
+void set_page_rd_hook(byte page, func_rdhook func);    
+
+void set_page_wr_hook(byte page, func_wrhook func);
+
+void set_page_readable(byte page, bool value);
+
+void set_page_writeable(byte page, bool value);
+
+void set_page_ghost(byte page, bool value, byte ghost);
+
+byte get_page_attributes(byte page);
+
+func_rdhook get_page_rdhook(byte page);
+
+func_wrhook get_page_wrhook(byte page);
+
+/* Generalist functions */
+
+void InitMemory();
+
+byte ReadMemory(byte page, byte addr);
+void WriteMemory(byte page, byte addr, byte value);
+
+void DumpMemoryState();
+
+#endif

+ 36 - 0
src/include/paddle.h

@@ -0,0 +1,36 @@
+/*
+ *  Paddle manager - The TI-NESulator Project
+ *  paddle.h
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-04-16 01:55:35 +0200 (lun, 16 avr 2007) $
+ *  $Author: godzil $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/paddle.h $
+ *  $Revision: 39 $
+ *
+ */
+
+#ifndef PADDLE_H
+#define PADDLE_H
+
+typedef struct Paddle_ 
+{
+    
+    unsigned char Bit;
+    
+    unsigned char LastWrite;
+    
+} Paddle;
+
+
+unsigned char ReadPaddle(Paddle * pdl);
+
+
+void InitPaddle(Paddle * pdl);
+
+void WritePaddle(Paddle * pdl, unsigned char val);
+
+
+#endif

+ 261 - 0
src/include/palette.h

@@ -0,0 +1,261 @@
+/* Generated data file from file 'stdin' */
+
+
+PALETTE basicPalette = {
+       { 0x1E, 0x1E, 0x1E, 0x07 },
+       { 0x03, 0x09, 0x28, 0xB7 },
+       { 0x0A, 0x04, 0x2B, 0x0D },
+       { 0x17, 0x02, 0x28, 0x00 },
+       { 0x22, 0x00, 0x1D, 0xB7 },
+       { 0x24, 0x01, 0x0A, 0xB7 },
+       { 0x24, 0x04, 0x02, 0xB7 },
+       { 0x1B, 0x09, 0x01, 0xB7 },
+       { 0x11, 0x10, 0x01, 0x00 },
+       { 0x05, 0x15, 0x01, 0x00 },
+       { 0x01, 0x17, 0x02, 0xB7 },
+       { 0x00, 0x14, 0x0B, 0xBF },
+       { 0x01, 0x11, 0x1B, 0xBF },
+       { 0x00, 0x00, 0x00, 0x00 },
+       { 0x00, 0x00, 0x00, 0x00 },
+       { 0x00, 0x00, 0x00, 0xB7 },
+       { 0x2F, 0x30, 0x2F, 0xB7 },
+       { 0x05, 0x1A, 0x37, 0xBF },
+       { 0x12, 0x10, 0x3B, 0xBF },
+       { 0x23, 0x09, 0x38, 0xB7 },
+       { 0x31, 0x06, 0x2E, 0xBF },
+       { 0x35, 0x08, 0x18, 0xB7 },
+       { 0x35, 0x0D, 0x08, 0x00 },
+       { 0x2D, 0x16, 0x03, 0xB7 },
+       { 0x22, 0x1E, 0x01, 0x00 },
+       { 0x0E, 0x24, 0x01, 0x00 },
+       { 0x05, 0x26, 0x06, 0x00 },
+       { 0x02, 0x26, 0x16, 0xB7 },
+       { 0x02, 0x22, 0x2A, 0xBF },
+       { 0x0B, 0x0B, 0x0B, 0xBF },
+       { 0x00, 0x00, 0x00, 0xB7 },
+       { 0x00, 0x00, 0x00, 0x00 },
+       { 0x3D, 0x3D, 0x3E, 0x00 },
+       { 0x12, 0x2C, 0x3E, 0xB7 },
+       { 0x22, 0x25, 0x3F, 0xB7 },
+       { 0x30, 0x1E, 0x3E, 0xB7 },
+       { 0x3A, 0x1B, 0x3B, 0xBF },
+       { 0x3D, 0x1D, 0x2E, 0xB7 },
+       { 0x3D, 0x20, 0x1B, 0xB7 },
+       { 0x3B, 0x28, 0x11, 0x07 },
+       { 0x35, 0x30, 0x08, 0x01 },
+       { 0x26, 0x35, 0x08, 0x00 },
+       { 0x14, 0x37, 0x11, 0x00 },
+       { 0x0F, 0x37, 0x25, 0x00 },
+       { 0x0A, 0x36, 0x37, 0xB7 },
+       { 0x18, 0x19, 0x19, 0xB7 },
+       { 0x01, 0x01, 0x01, 0xB7 },
+       { 0x01, 0x01, 0x01, 0xB7 },
+       { 0x3E, 0x3E, 0x3E, 0xB7 },
+       { 0x2D, 0x37, 0x3E, 0xB7 },
+       { 0x33, 0x34, 0x3F, 0x10 },
+       { 0x37, 0x31, 0x3E, 0x00 },
+       { 0x3C, 0x30, 0x3D, 0x00 },
+       { 0x3D, 0x30, 0x38, 0x00 },
+       { 0x3D, 0x33, 0x30, 0x00 },
+       { 0x3D, 0x36, 0x2B, 0x00 },
+       { 0x3B, 0x39, 0x26, 0x00 },
+       { 0x34, 0x3B, 0x27, 0x00 },
+       { 0x2E, 0x3C, 0x2D, 0x00 },
+       { 0x2C, 0x3C, 0x36, 0x00 },
+       { 0x29, 0x3C, 0x3C, 0x00 },
+       { 0x32, 0x31, 0x32, 0xB7 },
+       { 0x01, 0x01, 0x01, 0xB7 },
+       { 0x01, 0x01, 0x01, 0xB7 },
+       { 0x1E, 0x1E, 0x1E, 0xB7 },
+       { 0x03, 0x09, 0x28, 0xB7 },
+       { 0x0A, 0x04, 0x2B, 0xBF },
+       { 0x17, 0x02, 0x28, 0xB7 },
+       { 0x22, 0x00, 0x1D, 0xB7 },
+       { 0x24, 0x01, 0x0A, 0xB7 },
+       { 0x24, 0x04, 0x02, 0x00 },
+       { 0x1B, 0x09, 0x01, 0x00 },
+       { 0x11, 0x10, 0x01, 0x00 },
+       { 0x05, 0x15, 0x01, 0xB7 },
+       { 0x01, 0x17, 0x02, 0x00 },
+       { 0x00, 0x14, 0x0B, 0xB7 },
+       { 0x01, 0x11, 0x1B, 0x50 },
+       { 0x00, 0x00, 0x00, 0x00 },
+       { 0x00, 0x00, 0x00, 0x00 },
+       { 0x00, 0x00, 0x00, 0xB7 },
+       { 0x2F, 0x30, 0x2F, 0xBF },
+       { 0x05, 0x1A, 0x37, 0xB7 },
+       { 0x12, 0x10, 0x3B, 0xB7 },
+       { 0x23, 0x09, 0x38, 0x20 },
+       { 0x31, 0x06, 0x2E, 0xBF },
+       { 0x35, 0x08, 0x18, 0xB7 },
+       { 0x35, 0x0D, 0x08, 0xBF },
+       { 0x2D, 0x16, 0x03, 0xBF },
+       { 0x22, 0x1E, 0x01, 0xB7 },
+       { 0x0E, 0x24, 0x01, 0xBF },
+       { 0x05, 0x26, 0x06, 0xB7 },
+       { 0x02, 0x26, 0x16, 0xBF },
+       { 0x02, 0x22, 0x2A, 0xBF },
+       { 0x0B, 0x0B, 0x0B, 0x00 },
+       { 0x00, 0x00, 0x00, 0x00 },
+       { 0x00, 0x00, 0x00, 0xB7 },
+       { 0x3D, 0x3D, 0x3E, 0xBF },
+       { 0x12, 0x2C, 0x3E, 0x53 },
+       { 0x22, 0x25, 0x3F, 0x54 },
+       { 0x30, 0x1E, 0x3E, 0x46 },
+       { 0x3A, 0x1B, 0x3B, 0x31 },
+       { 0x3D, 0x1D, 0x2E, 0x3A },
+       { 0x3D, 0x20, 0x1B, 0x32 },
+       { 0x3B, 0x28, 0x11, 0x54 },
+       { 0x35, 0x30, 0x08, 0x30 },
+       { 0x26, 0x35, 0x08, 0x00 },
+       { 0x14, 0x37, 0x11, 0x00 },
+       { 0x0F, 0x37, 0x25, 0x00 },
+       { 0x0A, 0x36, 0x37, 0x00 },
+       { 0x18, 0x19, 0x19, 0x00 },
+       { 0x01, 0x01, 0x01, 0x00 },
+       { 0x01, 0x01, 0x01, 0x00 },
+       { 0x3E, 0x3E, 0x3E, 0x00 },
+       { 0x2D, 0x37, 0x3E, 0x00 },
+       { 0x33, 0x34, 0x3F, 0x00 },
+       { 0x37, 0x31, 0x3E, 0x00 },
+       { 0x3C, 0x30, 0x3D, 0x00 },
+       { 0x3D, 0x30, 0x38, 0x00 },
+       { 0x3D, 0x33, 0x30, 0xB7 },
+       { 0x3D, 0x36, 0x2B, 0xB7 },
+       { 0x3B, 0x39, 0x26, 0x00 },
+       { 0x34, 0x3B, 0x27, 0xBF },
+       { 0x2E, 0x3C, 0x2D, 0xB7 },
+       { 0x2C, 0x3C, 0x36, 0xB7 },
+       { 0x29, 0x3C, 0x3C, 0xB7 },
+       { 0x32, 0x31, 0x32, 0xB7 },
+       { 0x01, 0x01, 0x01, 0xB7 },
+       { 0x01, 0x01, 0x01, 0xB7 },
+       { 0x1E, 0x1E, 0x1E, 0x07 },
+       { 0x03, 0x09, 0x28, 0xB7 },
+       { 0x0A, 0x04, 0x2B, 0x0D },
+       { 0x17, 0x02, 0x28, 0x00 },
+       { 0x22, 0x00, 0x1D, 0xB7 },
+       { 0x24, 0x01, 0x0A, 0xB7 },
+       { 0x24, 0x04, 0x02, 0xB7 },
+       { 0x1B, 0x09, 0x01, 0xB7 },
+       { 0x11, 0x10, 0x01, 0x00 },
+       { 0x05, 0x15, 0x01, 0x00 },
+       { 0x01, 0x17, 0x02, 0xB7 },
+       { 0x00, 0x14, 0x0B, 0x00 },
+       { 0x01, 0x11, 0x1B, 0xB7 },
+       { 0x00, 0x00, 0x00, 0xB7 },
+       { 0x00, 0x00, 0x00, 0xB7 },
+       { 0x00, 0x00, 0x00, 0xB7 },
+       { 0x2F, 0x30, 0x2F, 0xB7 },
+       { 0x05, 0x1A, 0x37, 0xBF },
+       { 0x12, 0x10, 0x3B, 0xB7 },
+       { 0x23, 0x09, 0x38, 0xB7 },
+       { 0x31, 0x06, 0x2E, 0x00 },
+       { 0x35, 0x08, 0x18, 0xBF },
+       { 0x35, 0x0D, 0x08, 0xB7 },
+       { 0x2D, 0x16, 0x03, 0xB7 },
+       { 0x22, 0x1E, 0x01, 0xB7 },
+       { 0x0E, 0x24, 0x01, 0x00 },
+       { 0x05, 0x26, 0x06, 0x00 },
+       { 0x02, 0x26, 0x16, 0xB7 },
+       { 0x02, 0x22, 0x2A, 0x06 },
+       { 0x0B, 0x0B, 0x0B, 0xB7 },
+       { 0x00, 0x00, 0x00, 0x00 },
+       { 0x00, 0x00, 0x00, 0x00 },
+       { 0x3D, 0x3D, 0x3E, 0xB7 },
+       { 0x12, 0x2C, 0x3E, 0xB7 },
+       { 0x22, 0x25, 0x3F, 0xB7 },
+       { 0x30, 0x1E, 0x3E, 0xB7 },
+       { 0x3A, 0x1B, 0x3B, 0x00 },
+       { 0x3D, 0x1D, 0x2E, 0x00 },
+       { 0x3D, 0x20, 0x1B, 0xB7 },
+       { 0x3B, 0x28, 0x11, 0xB7 },
+       { 0x35, 0x30, 0x08, 0x03 },
+       { 0x26, 0x35, 0x08, 0xB7 },
+       { 0x14, 0x37, 0x11, 0xBF },
+       { 0x0F, 0x37, 0x25, 0x00 },
+       { 0x0A, 0x36, 0x37, 0xB7 },
+       { 0x18, 0x19, 0x19, 0xB7 },
+       { 0x01, 0x01, 0x01, 0xB7 },
+       { 0x01, 0x01, 0x01, 0xB7 },
+       { 0x3E, 0x3E, 0x3E, 0x00 },
+       { 0x2D, 0x37, 0x3E, 0x00 },
+       { 0x33, 0x34, 0x3F, 0xB7 },
+       { 0x37, 0x31, 0x3E, 0xB7 },
+       { 0x3C, 0x30, 0x3D, 0x00 },
+       { 0x3D, 0x30, 0x38, 0x00 },
+       { 0x3D, 0x33, 0x30, 0xB7 },
+       { 0x3D, 0x36, 0x2B, 0xB7 },
+       { 0x3B, 0x39, 0x26, 0xB7 },
+       { 0x34, 0x3B, 0x27, 0xBF },
+       { 0x2E, 0x3C, 0x2D, 0xBF },
+       { 0x2C, 0x3C, 0x36, 0xB7 },
+       { 0x29, 0x3C, 0x3C, 0xBF },
+       { 0x32, 0x31, 0x32, 0xB7 },
+       { 0x01, 0x01, 0x01, 0x00 },
+       { 0x01, 0x01, 0x01, 0xB7 },
+       { 0x1E, 0x1E, 0x1E, 0xB7 },
+       { 0x03, 0x09, 0x28, 0x08 },
+       { 0x0A, 0x04, 0x2B, 0xBF },
+       { 0x17, 0x02, 0x28, 0xB7 },
+       { 0x22, 0x00, 0x1D, 0x08 },
+       { 0x24, 0x01, 0x0A, 0xB7 },
+       { 0x24, 0x04, 0x02, 0x08 },
+       { 0x1B, 0x09, 0x01, 0xB7 },
+       { 0x11, 0x10, 0x01, 0x00 },
+       { 0x05, 0x15, 0x01, 0xBF },
+       { 0x01, 0x17, 0x02, 0xB7 },
+       { 0x00, 0x14, 0x0B, 0xB7 },
+       { 0x01, 0x11, 0x1B, 0x08 },
+       { 0x00, 0x00, 0x00, 0xB7 },
+       { 0x00, 0x00, 0x00, 0xB7 },
+       { 0x00, 0x00, 0x00, 0x08 },
+       { 0x2F, 0x30, 0x2F, 0x01 },
+       { 0x05, 0x1A, 0x37, 0x08 },
+       { 0x12, 0x10, 0x3B, 0x0D },
+       { 0x23, 0x09, 0x38, 0x00 },
+       { 0x31, 0x06, 0x2E, 0xB7 },
+       { 0x35, 0x08, 0x18, 0xB7 },
+       { 0x35, 0x0D, 0x08, 0xB7 },
+       { 0x2D, 0x16, 0x03, 0xB7 },
+       { 0x22, 0x1E, 0x01, 0x00 },
+       { 0x0E, 0x24, 0x01, 0x00 },
+       { 0x05, 0x26, 0x06, 0xB7 },
+       { 0x02, 0x26, 0x16, 0x00 },
+       { 0x02, 0x22, 0x2A, 0xB7 },
+       { 0x0B, 0x0B, 0x0B, 0xB7 },
+       { 0x00, 0x00, 0x00, 0x08 },
+       { 0x00, 0x00, 0x00, 0xB7 },
+       { 0x3D, 0x3D, 0x3E, 0xB7 },
+       { 0x12, 0x2C, 0x3E, 0xBF },
+       { 0x22, 0x25, 0x3F, 0xBF },
+       { 0x30, 0x1E, 0x3E, 0xB7 },
+       { 0x3A, 0x1B, 0x3B, 0xBF },
+       { 0x3D, 0x1D, 0x2E, 0xB7 },
+       { 0x3D, 0x20, 0x1B, 0x00 },
+       { 0x3B, 0x28, 0x11, 0xB7 },
+       { 0x35, 0x30, 0x08, 0x00 },
+       { 0x26, 0x35, 0x08, 0x00 },
+       { 0x14, 0x37, 0x11, 0x00 },
+       { 0x0F, 0x37, 0x25, 0xB7 },
+       { 0x0A, 0x36, 0x37, 0xB7 },
+       { 0x18, 0x19, 0x19, 0x00 },
+       { 0x01, 0x01, 0x01, 0x00 },
+       { 0x01, 0x01, 0x01, 0x00 },
+       { 0x3E, 0x3E, 0x3E, 0x00 },
+       { 0x2D, 0x37, 0x3E, 0xB7 },
+       { 0x33, 0x34, 0x3F, 0x00 },
+       { 0x37, 0x31, 0x3E, 0xB7 },
+       { 0x3C, 0x30, 0x3D, 0xBF },
+       { 0x3D, 0x30, 0x38, 0xB7 },
+       { 0x3D, 0x33, 0x30, 0x08 },
+       { 0x3D, 0x36, 0x2B, 0x01 },
+       { 0x3B, 0x39, 0x26, 0x01 },
+       { 0x34, 0x3B, 0x27, 0x00 },
+       { 0x2E, 0x3C, 0x2D, 0xB7 },
+       { 0x2C, 0x3C, 0x36, 0xB7 },
+       { 0x29, 0x3C, 0x3C, 0x08 },
+       { 0x32, 0x31, 0x32, 0xB7 },
+       { 0x01, 0x01, 0x01, 0x08 },
+       { 0x01, 0x01, 0x01, 0xBF }
+      };

+ 46 - 0
src/include/plugins/manager.h

@@ -0,0 +1,46 @@
+/*
+ *  Plugins manager - The TI-NESulator Project
+ *  plugins.h
+ *
+ *  Created by Manoel TRAPIER on 02/04/07.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate$
+ *  $Author$
+ *  $HeadURL$
+ *  $Revision$
+ *
+ */
+
+#ifndef PLUGINS_H 
+#define PLUGINS_H 
+
+#include <types.h>
+
+/* Function pointer for prototyping function that plugins may export */
+typedef int (*PluginInit) (void);
+typedef int (*PluginDeinit) (void);
+typedef void (*PluginKeypress) (void);
+
+#ifdef __TINES_PLUGINS__
+
+/* Available functions for plugins */
+int plugin_install_keypressHandler(byte key, PluginKeypress);
+int plugin_remove_keypressHandler(byte key, PluginKeypress);
+
+#else /* __TINES_PLUGINS__ */
+
+/* Available functions outside of plugins */
+int plugin_keypress(byte key);
+
+/* Real Prototype: TBD */
+void plugin_list();
+
+int plugin_load(int id);
+
+int plugin_unload(int id);
+
+
+#endif /* __TINES_PLUGINS__ */
+
+#endif /* PLUGINS_H */

+ 24 - 0
src/include/ppu/ppu.debug.h

@@ -0,0 +1,24 @@
+/*
+ *  PPU debug utilities - The TI-NESulator Project
+ *  ppu.debug.h
+ *
+ *  Created by Manoël Trapier on 12/04/07.
+ *  Copyright 2003-2007 986 Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-05-24 15:11:55 +0200 (jeu, 24 mai 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/ppu/ppu.debug.h $
+ *  $Revision: 53 $
+ *
+ */
+
+#ifdef __TINES_PPU_INTERNAL__
+
+void ppu_dumpPalette(int x, int y);
+void ppu_dumpPattern(int xd, int yd);
+void ppu_dumpNameTable(int xd, int yd);
+void ppu_dumpAttributeTable(int xd, int yd);
+
+#else
+#error Must only be included inside the PPU code
+#endif

+ 68 - 0
src/include/ppu/ppu.h

@@ -0,0 +1,68 @@
+/*
+ *  PPU emulation - The TI-NESulator Project
+ *  ppu.h
+ *  
+ *  Define and emulate the PPU (Picture Processing Unit) of the real NES
+ * 
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-04-26 18:47:34 +0200 (jeu, 26 avr 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/ppu.h $
+ *  $Revision: 46 $
+ *
+ */
+
+#ifndef PPU_H
+#define PPU_H
+
+#include <types.h>
+
+typedef struct PPU_Sprite_
+{
+    byte y;
+    byte tileid;
+    byte flags;
+    byte x;
+} PPU_Sprite;
+
+/*
+PPU must be initialized after memory initialisation..
+*/
+int ppu_init();
+
+int ppu_hblank(int scanline);
+
+byte ppu_readReg(byte id);
+
+void ppu_writeReg(byte id, byte val);
+
+void ppu_fillSprRamDMA(byte value);
+
+#define PPU_MIRROR_HORIZTAL 0
+#define PPU_MIRROR_VERTICAL 1
+
+#define PPU_SCREEN_000 0
+#define PPU_SCREEN_400 1
+#define PPU_SCREEN_800 2
+#define PPU_SCREEN_C00 3
+
+#define PPU_SCMODE_SINGLE 0
+#define PPU_SCMODE_NORMAL 1
+#define PPU_SCMODE_FOURSC 2
+
+void ppu_setMirroring(byte direction);
+void ppu_setSingleScreen(byte screen);
+void ppu_setScreenMode(byte mode);
+
+
+PPU_Sprite ppu_getSprite(unsigned short i);
+
+unsigned char ppu_memoryRead(byte page, byte addr);
+void          ppu_memoryWrite(byte page, byte addr, byte value);
+
+void ppu_debugSprites();
+void ppu_debugColor();
+
+#endif

+ 32 - 0
src/include/ppu/ppu.memory.h

@@ -0,0 +1,32 @@
+/*
+ *  PPU Memory manager - The TI-NESulator Project
+ *  ppu.memory.h - Inspired from the memory manager of the Quick6502 Project.
+ *
+ *  Created by Manoël Trapier on 12/04/07.
+ *  Copyright 2003-2007 986 Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-05-24 15:11:55 +0200 (jeu, 24 mai 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/ppu/ppu.memory.h $
+ *  $Revision: 53 $
+ *
+ */
+
+#ifdef __TINES_PPU_INTERNAL__
+
+int ppu_initMemory();
+
+void ppu_setPagePtr  (byte page, byte *ptr);
+void ppu_setPagePtr1k(byte page, byte *ptr);
+void ppu_setPagePtr2k(byte page, byte *ptr);
+void ppu_setPagePtr4k(byte page, byte *ptr);
+void ppu_setPagePtr8k(byte page, byte *ptr);
+
+void ppu_memoryDumpState(FILE *fp);
+
+byte ppu_readMemory(byte page, byte addr);
+void ppu_writeMemory(byte page, byte addr, byte value);
+
+#else
+#error Must only be included inside the PPU code
+#endif

+ 28 - 0
src/include/types.h

@@ -0,0 +1,28 @@
+/*
+ *  Base type definitions - The TI-NESulator Project
+ *  types.h - Taken from the Quick6502 project
+ *
+ *  Created by Manoël Trapier on 18/09/06.
+ *  Copyright 2003-2007 986 Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-03-28 15:50:50 +0200 (mer, 28 mar 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/types.h $
+ *  $Revision: 25 $
+ *
+ */
+
+#ifndef TYPES_H
+#define TYPES_H
+
+#ifndef BYTE_TYPE_DEFINED
+#define BYTE_TYPE_DEFINED
+typedef unsigned char byte;
+#endif
+
+typedef unsigned char bool;
+
+#define true  (0)
+#define false (!true)
+
+#endif

+ 1232 - 0
src/main.c

@@ -0,0 +1,1232 @@
+/*
+ *  Main application source file - The TI-NESulator Project
+ *  main.c
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-05-31 18:02:16 +0200 (jeu, 31 mai 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/main.c $
+ *  $Revision: 58 $
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <time.h>
+
+#ifndef WIN32
+
+//#include <Allegro/allegro.h>
+#include <allegro.h>
+
+#else
+
+#define USE_CONSOLE
+#include <allegro.h>
+
+#endif
+
+#if ISPAL && !ISNTSC
+//#define VBLANK_TIME 70
+//#define HBLANK_TIME 261
+int VBLANK_TIME     = 70;
+int HBLANK_TIME     = 140;
+double APU_BASEFREQ = 1.7734474;
+#elif !ISPAL && ISNTSC
+int VBLANK_TIME     = 20;
+int HBLANK_TIME     = 119;
+double APU_BASEFREQ = 1.7897725;
+//#define VBLANK_TIME 20
+//#define HBLANK_TIME 260
+#else
+#error Cannot use ISPAL with ISNTSC together !
+#endif
+
+/* Should find something better for sharing the copu interface... */
+
+#include <corecpu/M6502.h>
+#include <ppu/ppu.h>
+#include <NESCarts.h>
+#include <paddle.h>
+
+#include <mappers/manager.h>
+
+#include <memory/manager.h>
+
+#include <plugins/manager.h>
+
+#ifdef USE_SOUND
+#include <Sound.h>
+#endif
+
+#include <palette.h>
+
+#define V_MAJOR 0
+#define V_MINOR 29
+
+#define VS_ID              "$Id: main.c 58 2007-05-31 16:02:16Z mtrapier $"
+#define VS_REVISION        "$Revision: 58 $"
+#define VS_LASTCHANGEDDATE "$LastChangedDate: 2007-05-31 18:02:16 +0200 (jeu, 31 mai 2007) $"
+#define VS_HEADURL         "$HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/main.c $"
+#define VS_AUTHOR          "$Author: mtrapier $"
+
+#define MAXLASTOP 42
+
+word latestop[MAXLASTOP];
+
+M6502 MainCPU;
+
+BITMAP *Buffer;
+
+byte *FDSRom;
+byte *FDSRam;
+
+
+/* Command line options */
+byte START_DEBUG = 0;
+byte START_WITH_FDS = 0;
+char *CART_FILENAME = NULL;
+char *PALETTE_FILENAME = NULL;
+
+#define fatal(s) { printf("%s",s); exit(-1); }
+
+Paddle P1, P2;
+
+unsigned short ScanLine;
+
+volatile int frame = 0;
+volatile extern int icount;
+
+char MapperWantIRQ = 0;
+
+char WantClosing = 0;
+
+struct timeval timeStart;
+struct timeval timeEnd;
+
+volatile unsigned long FPS, IPS;
+
+PALETTE pal;
+
+short IRQScanHit = -1;
+
+NesCart *Cart;
+
+/* palette */
+unsigned long ColorPalette[ 8 * 63 ];
+
+#define SET_RGB(r,g,b) ((((r<<8)|g)<<8)|b)|0xFF000000
+
+void CloseHook(void)
+{
+   WantClosing = 1;
+}
+
+void ips_fps_counter(void)
+{
+   FPS = frame;
+   IPS = icount;
+   frame = 0;
+   icount = 0;
+}
+
+END_OF_FUNCTION(ips_fps_counter);
+
+void SaveSaveRam(char *name)
+{
+   FILE *fp;
+   int i;
+   char fname[512];
+   //byte car;
+   strcpy(fname, name);
+   strcat(fname, ".svt");
+   if ((fp = fopen(fname, "wb")))
+   {
+      printf("Saving savestate '%s'\n", fname);
+      for( i = 0x60; i < 0x80; i++)
+      {
+         fwrite(get_page_ptr(i), 1, 0x100, fp);
+      }
+      
+      fclose(fp);
+   }
+}
+
+void LoadSaveRam(char *name)
+{
+   FILE *fp;
+   int i;
+   char fname[512];
+   
+   strcpy(fname, name);
+   strcat(fname, ".svt");
+   if ((fp = fopen(fname, "rb")))
+   {
+      printf("Loading savestate '%s'\n", fname);
+      for( i = 0x60; i < 0x80; i++)
+      {
+         fread(get_page_ptr(i), 1, 0x0100, fp);
+      }    
+      fclose(fp);
+      
+   }
+}
+
+
+void LoadPalette(char *filename, PALETTE pal)
+{
+   FILE *fp;
+   
+   unsigned char r, v, b, i;
+   printf("%s: try to load pallette file '%s'", __func__, filename);
+   if ((fp = fopen(filename, "rb")) != NULL)
+   {
+      
+      for (i = 0; i < 64; i++)
+      {
+         
+         fread(&r, 1, 1, fp);
+         fread(&v, 1, 1, fp);
+         fread(&b, 1, 1, fp);
+         
+         /*            r = (r * 64) / 255;
+          v = (v * 64) / 255;
+          b = (b * 64) / 255;*/
+         
+         
+#ifdef USE_24BITS            
+         ColorPalette[i + (0 * 63)] = SET_RGB(r,v,b);
+         
+         /* Red emphase */
+         ColorPalette[i + (1 * 63)] = SET_RGB(r + 10, v - 05, b - 05);
+         
+         /* Green emphase */
+         ColorPalette[i + (2 * 63)] = SET_RGB(r - 05, v + 10, b - 05);
+         
+         /* Red + green emphase */
+         ColorPalette[i + (3 * 63)] = SET_RGB(r + 05, v + 05, b - 10);
+         
+         /* Blue emphase */
+         ColorPalette[i + (4 * 63)] = SET_RGB(r - 05, v - 05, b + 10);
+         
+         /* Red + blue emphase */
+         ColorPalette[i + (5 * 63)] = SET_RGB(r + 05, v - 10, b + 05);
+         
+         /* Blue + green emphase */
+         ColorPalette[i + (6 * 63)] = SET_RGB(r - 10, v + 05, b + 05);
+         
+         /* Red + Green + Blue emphase */
+         ColorPalette[i + (7 * 63)] = SET_RGB(r + 00, v + 00, b + 00);*/
+#else /* Else Use 8Bits */
+         pal[i].r = r;
+         pal[i].g = v;
+         pal[i].b = b;
+         
+         pal[i + 64].r = r;
+         pal[i + 64].g = v;
+         pal[i + 64].b = b;
+         
+         pal[i + 128].r = r;
+         pal[i + 128].g = v;
+         pal[i + 128].b = b;
+         
+         pal[i + 192].r = r;
+         pal[i + 192].g = v;
+         pal[i + 192].b = b;
+#endif
+      }
+      fclose(fp);
+      printf(" [ OK ]\n");
+   }
+   else
+   {
+      printf("Error loading palette '%s'!\n", filename);
+      exit(-1);
+   }
+}
+
+int DAsm(char *S, word A);
+
+int oppos = 0;
+
+void pushop(word op)
+{
+   latestop[oppos] = op;
+   //  printf("%d\n", oppos);  
+   oppos = (oppos+1)%42;
+}
+
+void showlastop(FILE *fp)
+{
+#ifdef DEBUG
+   int i,j;
+   char S[256];
+   i = oppos;
+   do
+   {
+      j=(DAsm(S,latestop[i])-1);
+      fprintf(fp, "0x%04X : %s\n", MainCPU.PC.W,S);
+      i = (i+1)%42;
+   }
+   while(i != oppos);
+#endif
+}
+
+void *signalhandler(int sig)
+{
+   static int state=0;
+   M6502 *R = &MainCPU;
+   byte F;
+   int J, I;
+   static char FA[8] = "NVRBDIZC";    
+   char S[128];
+   char name[512];
+   static FILE *fp = NULL;
+   sprintf(name, "crashdump-%d.txt", time(NULL));
+   if (state != 0)
+   {
+      fprintf(stderr, "\n\n\nCrashed within signal!\nEmergency exit\n");
+      exit(42);
+   }
+   state = 1;
+   
+   if (fp == NULL)
+      fp = fopen(name, "wt");
+   
+   state = 2;
+   
+   if (fp) fprintf(stderr,
+                   "\n\n\n\n\n"
+                   "#sick# TI-NESulator %d.%d #sick#\n"
+                   "see %s for more information",
+                   V_MAJOR,
+                   V_MINOR,
+                   name);
+   
+   if (!fp) fp = stderr;
+   
+   fprintf(fp,"\n\n\n\n\n"
+           "#sick# TI-NESulator %d.%d #sick# signal: ",
+           V_MAJOR,
+           V_MINOR);
+   switch(sig)
+   {
+      default:
+      case SIGABRT: fprintf(fp,"Abnormal termination"); break;
+      case SIGILL:  fprintf(fp,"Illegal instruction"); break;
+      case SIGINT:  fprintf(fp,"CTRL+C signal"); break;
+      case SIGSEGV: fprintf(fp,"Segmentation fault"); break;
+      case SIGTERM: fprintf(fp,"Termination request"); break;
+   }
+   fprintf(fp,"\nAn error occured during the excution.\n Crash report information :\n");
+#ifdef DEBUG
+   DAsm(S, R->PC.W);
+#endif
+   fprintf(fp, "CPU: A:%02X  P:%02X  X:%02X  Y:%02X  S:%04X  PC:%04X  Flags:[", 
+           R->A, R->P, R->X, R->Y, R->S + 0x0100, R->PC.W);
+   for (J = 0, F = R->P; J < 8; J++, F <<= 1)
+      fprintf(fp, "%c", F & 0x80 ? FA[J] : '.');
+   fprintf(fp, "]\nCPU: ");
+   fprintf(fp, "AT PC: [%02X - %s]   AT SP: [%02X %02X %02X]\nLast execution :\n", 
+           Rd6502(R->PC.W), S, 
+           Rd6502(0x0100 + (byte) (R->S + 1)), 
+           Rd6502(0x0100 + (byte) (R->S + 2)), 
+           Rd6502(0x0100 + (byte) (R->S + 3)));
+   showlastop(fp);
+   //       fprintf(fp, "PPU: CR1: 0x%02X (NT:%d AI:%d SP:%d BP:%d SS:%d NMI:%d)\n",ppu.ControlRegister1.b, ppu.ControlRegister1.s.NameTblAddr, ppu.ControlRegister1.s.AddrIncrmt, ppu.ControlRegister1.s.SptPattern, ppu.ControlRegister1.s.BgPattern, ppu.ControlRegister1.s.SpriteSize, ppu.ControlRegister1.s.VBlank_NMI);
+   //       fprintf(fp, "PPU: CR2: 0x%02X (FBC/CI:%d SV:%d BV:%d SC:%d BC:%d DT:%d)\n",ppu.ControlRegister2.b,ppu.ControlRegister2.s.Colour,ppu.ControlRegister2.s.SpriteVisibility,ppu.ControlRegister2.s.BgVisibility,ppu.ControlRegister2.s.SpriteClipping,ppu.ControlRegister2.s.BgClipping,ppu.ControlRegister2.s.DisplayType);
+   //       fprintf(fp, "PPU: SR: 0x%02X (VB:%d S0:%d SSC:%d VWF:%d)\n", ppu.StatusRegister.b,ppu.StatusRegister.s.VBlankOccur,ppu.StatusRegister.s.Sprite0Occur,ppu.StatusRegister.s.SprtCount,ppu.StatusRegister.s.VRAMProtect);
+   //       fprintf(fp, "PPU: M:%d ST:%d VRAMPtr:0x%04X T:0x%04X\n",ppu.MirrorDir,ppu.ScreenType,ppu.VRAMAddrReg2.W,ppu.TmpVRamPtr);
+   
+   mapper_dump(fp);
+   
+   for(I = 0; I < 0xFFFF; I += 0x10)
+      fprintf(fp, "%04X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X | %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",
+              I,
+              Rd6502(I+0x00), Rd6502(I+0x01), Rd6502(I+0x02), Rd6502(I+0x03),
+              Rd6502(I+0x04), Rd6502(I+0x05), Rd6502(I+0x06), Rd6502(I+0x07),
+              Rd6502(I+0x08), Rd6502(I+0x09), Rd6502(I+0x0A), Rd6502(I+0x0B),
+              Rd6502(I+0x0C), Rd6502(I+0x0D), Rd6502(I+0x0E), Rd6502(I+0x0F),
+              // --- //
+              isprint(Rd6502(I+0x00))?Rd6502(I+0x00):'_',
+              isprint(Rd6502(I+0x01))?Rd6502(I+0x01):'_',
+              isprint(Rd6502(I+0x02))?Rd6502(I+0x02):'_',
+              isprint(Rd6502(I+0x03))?Rd6502(I+0x03):'_',
+              isprint(Rd6502(I+0x04))?Rd6502(I+0x04):'_',
+              isprint(Rd6502(I+0x05))?Rd6502(I+0x05):'_',
+              isprint(Rd6502(I+0x06))?Rd6502(I+0x06):'_',
+              isprint(Rd6502(I+0x07))?Rd6502(I+0x07):'_',
+              isprint(Rd6502(I+0x08))?Rd6502(I+0x08):'_',
+              isprint(Rd6502(I+0x09))?Rd6502(I+0x09):'_',
+              isprint(Rd6502(I+0x0A))?Rd6502(I+0x0A):'_',
+              isprint(Rd6502(I+0x0B))?Rd6502(I+0x0B):'_',
+              isprint(Rd6502(I+0x0C))?Rd6502(I+0x0C):'_',
+              isprint(Rd6502(I+0x0D))?Rd6502(I+0x0D):'_',
+              isprint(Rd6502(I+0x0E))?Rd6502(I+0x0E):'_',
+              isprint(Rd6502(I+0x0F))?Rd6502(I+0x0F):'_');
+   
+   DumpMemoryState(fp);
+   
+   fprintf(stderr, "\nPlease join this informations when submiting crash report\n");
+   if (fp != stderr) fclose(fp);
+   //getchar();
+   exit(-42);
+}
+
+byte Page40[256];
+
+void WrHook4000Multiplexer(byte addr, byte value)
+{
+   static byte SQ1V = 0;
+   static byte SQ2V = 0;
+   static byte TRIV = 0;
+   static byte NOIV = 0;
+   
+   static unsigned short SQ1P = 0;
+   static unsigned short SQ2P = 0;
+   static unsigned short TRIP = 0;
+   static unsigned short NOIP = 0;
+   
+   static byte Sq1_reg0 = 0;
+   static byte Sq1_reg1 = 0;
+   static byte Sq1_reg2 = 0;
+   static byte Sq1_reg3 = 0;
+   
+   static byte Sq2_reg0 = 0;
+   static byte Sq2_reg1 = 0;
+   static byte Sq2_reg2 = 0;
+   static byte Sq2_reg3 = 0;
+   
+   double SQ;
+   switch(addr)
+   {
+#ifdef USE_SOUND    
+      case 0x00: /* DDLE NNNN */
+         Sq1_reg0 = value;
+         if (Sq1_reg0 & 0x10)
+         {
+            SQ1V = 0x0F/*(0x04+(value&0x0F))& 0x0F*/;
+         }
+         else
+         {
+            SQ1V = value&0x0F;
+         }
+         
+         break;
+         case 0x01: /* EPPP NSSS */        
+         Sq1_reg1 = value;
+         break;
+         case 0x02:
+         /*printf("Sq1 reg0: 0x%02X - duty:0x%X loop:0x%X env:0x%X vol:0x%X\n", 
+          Sq1_reg0,
+          (Sq1_reg0&0xC0)>>6,
+          (Sq1_reg0&0x20)>>5,
+          (Sq1_reg0&0x10)>>4,
+          Sq1_reg0&0x0F);
+          printf("Sq1 reg1: 0x%02X - sweep:0x%X period:0x%X neg:0x%X shift:0x%X\n", 
+          Sq1_reg1,
+          (Sq1_reg1&0x80)>>8,
+          (Sq1_reg1&0x70)>>4,
+          (Sq1_reg1&0x08)>>3,
+          Sq1_reg1&0x07);
+          printf("Sq1 reg2: 0x%02X\n", value);               
+          printf("Sq1 reg3: 0x%02X\n", Sq1_reg3);*/
+         SQ1P = value | ((Sq1_reg3&0x7) << 8);
+         SQ = APU_BASEFREQ * 1000 * 1000 / (SQ1P+1 /*+ 
+          (Sq1_reg1&0x80)?0:( (Sq1_reg1&0x08)?(SQ1P>>(Sq1_reg1&0x07)):(SQ1P<<(Sq1_reg1&0x07)) )*/);
+         SetSound(0,SND_MELODIC);
+         
+         //printf("SQ1V = %d - SQ = %f - SQ1P = %d\n", SQ1V, SQ, SQ1P);
+         
+         
+         //        { FILE *fp = fopen("sound.log", "at"); fprintf(fp, "%d %d %d\n", 0, SQ1P, SQ1V); fclose(fp); }
+         
+         Sound(0, (int) SQ/22, (0xFF/0x0F) * SQ1V);
+         
+         //        printf("40%02X: 0x%02X (SQ1P:%d SQ:%f (%d))\n", addr, value, SQ1P, SQ, (int) SQ);
+         Sq1_reg2 = value;
+         break;
+         
+         case 0x03:
+         Sq1_reg3 = value;
+         SQ1P = Sq1_reg2 | ((value&0x7) << 8);
+         SQ = APU_BASEFREQ * 1000 * 1000 / (SQ1P+1 /*+ 
+          (Sq1_reg1&0x80)?0:( (Sq1_reg1&0x08)?(SQ1P>>(Sq1_reg1&0x07)):(SQ1P<<(Sq1_reg1&0x07)) )*/);            
+         //        { FILE *fp = fopen("sound.log", "at"); fprintf(fp, "%d %d %d\n", 0, SQ1P, SQ1V); fclose(fp); }
+         Sound(0, (int) SQ/22, (0xFF/0x0F) * SQ1V);      
+         break;
+         
+         
+         
+         case 0x04: 
+         Sq2_reg0 = value;
+         if (Sq2_reg0 & 0x10)
+         {
+            SQ2V = 0x0F;
+            //SQ2V = (0x04+(value&0x0F))& 0x0F;
+         }
+         else
+         {
+            SQ2V = value&0x0F;        
+         }
+         
+         break;
+         case 0x05:
+         Sq2_reg1 = value;
+         break;
+         
+         case 0x06:
+         Sq2_reg2 = value;
+         
+         SQ2P = Sq2_reg2 | ((Sq2_reg3&0x7) << 8);
+         
+         SQ = APU_BASEFREQ * 1000 * 1000 / (SQ2P+1 /*+ 
+          (Sq2_reg1&0x80)?0:( (Sq2_reg1&0x08)?(SQ2P>>(Sq2_reg1&0x07)):(SQ2P<<(Sq2_reg1&0x07)) )*/);
+         
+         /*       printf("Sq2 reg0: 0x%02X - duty:0x%X loop:0x%X env:0x%X vol:0x%X\n", 
+          Sq2_reg0,
+          (Sq2_reg0&0xC0)>>6,
+          (Sq2_reg0&0x20)>>5,
+          (Sq2_reg0&0x10)>>4,
+          Sq2_reg0&0x0F);
+          printf("Sq2 reg1: 0x%02X - sweep:0x%X period:0x%X neg:0x%X shift:0x%X\n", 
+          Sq2_reg1,
+          (Sq2_reg1&0x80)>>8,
+          (Sq2_reg1&0x70)>>4,
+          (Sq2_reg1&0x08)>>3,
+          Sq2_reg1&0x07);
+          printf("Sq2 reg2: 0x%02X\n", value);               
+          printf("Sq2 reg3: 0x%02X\n", Sq2_reg3);
+          printf("SQ2V = %d - SQ = %f - SQ2P = %d\n", SQ2V, SQ, SQ2P);*/
+         
+         //        { FILE *fp = fopen("sound.log", "at"); fprintf(fp, "%d %d %d\n", 1, SQ2P, SQ2V); fclose(fp); }
+         Sound(1, (int) SQ/22, (0xFF/0x0F) * SQ2V);
+         break;
+         
+         case 0x07:
+         Sq2_reg3 = value;
+         
+         SQ2P = Sq2_reg2 | ((Sq2_reg3&0x7) << 8);
+         //SQ2P = (SQ2P & 0x00FF) | ((value&0x7) << 8);
+         SQ = APU_BASEFREQ * 1000 * 1000 / (SQ2P+1 /*+ 
+          (Sq2_reg1&0x80)?0:( (Sq2_reg1&0x08)?(SQ2P>>(Sq2_reg1&0x07)):(SQ2P<<(Sq2_reg1&0x07)) )*/);
+         //        { FILE *fp = fopen("sound.log", "at"); fprintf(fp, "%d %d %d\n", 1, SQ2P, SQ2V); fclose(fp); }        
+         Sound(1, (int) SQ/22, (0xFF/0x0F) * SQ2V);        
+         break;
+         
+         case 0x0A:
+         TRIP = (TRIP & 0xFF00) | value;
+         SQ = APU_BASEFREQ * 1000 * 1000 / TRIP;
+         //        { FILE *fp = fopen("sound.log", "at"); fprintf(fp, "%d %d %d\n", 2, TRIP, 255); fclose(fp); }
+         Sound(2, (int) SQ/22, 127);
+         break;
+         
+         case 0x0B:
+         TRIP = (TRIP & 0x00FF) | ((value&0x7) << 8);;
+         SQ = APU_BASEFREQ * 1000 * 1000 / TRIP;
+         //       { FILE *fp = fopen("sound.log", "at"); fprintf(fp, "%d %d %d\n", 2, TRIP, 255); fclose(fp); }
+         
+         Sound(2, (int) SQ/22, 127);
+         break;
+         
+         /*    case 0x0C:
+          NOIV = value & 0x0F;
+          break;
+          
+          case 0x0E:
+          NOIP = value & 0x0F;
+          SQ = APU_BASEFREQ * 1000 * 1000 / NOIP;
+          //       { FILE *fp = fopen("sound.log", "at"); fprintf(fp, "%d %d %d\n", 2, TRIP, 255); fclose(fp); }
+          SetSound(3, SND_NOISE);
+          Sound(3, (int) SQ/22, (0xFF/0x0F) * NOIV);
+          break;
+          case 0x0F:
+          
+          break;*/
+         case 0x15:
+         /* DMC, Noise, Triangle, Sq 2, Sq 1 */
+         //SetChannels(0, (value&0x01)?0x01:0);
+         /*        printf("40%02X: 0x%02X [%c%c%c%c%c]\n", addr, value,
+          (value&0x10)?'d':'.',
+          (value&0x08)?'n':'.',
+          (value&0x04)?'t':'.',
+          (value&0x02)?'2':'.',
+          (value&0x01)?'1':'.');*/
+         
+         break; 
+#endif   
+         case 0x14:
+         ppu_fillSprRamDMA(value);
+         break;
+         
+         case 0x16:
+         WritePaddle(&P1, value);
+         //WritePaddle(&P2, value);
+         break;
+         
+         case 0x17:
+         //        printf("40%02X: 0x%02X\n", addr, value);       
+         
+         break;
+         //    default:
+         //Page40[addr] = value;
+         // printf("40%02X: 0x%02X\n", addr, value);       
+         //        printf("pAPU: 0x%X @ 0x40%X\n", value, addr);
+   }
+   
+}
+
+byte RdHook4000Multiplexer(byte addr)
+{
+   byte ret;
+   switch(addr)
+   {
+      case 0x16:
+         ret = ReadPaddle(&P1);
+         break;
+         
+      case 0x17:
+         ret = 0x40;
+         break;
+         
+      case 0x15:
+         ret = 0x1F;
+      default:
+         ret = 0x42;
+   }   
+   return ret;
+}
+
+void printUsage(int argc, char *argv[])
+{
+   printf("Usage : %s game.nes [-p number][-f][-b filename.pal][ filename.nes\n"
+          "   -p: to add plugin 'number'\n"
+          "   -f: to start in FDS mode\n"
+          "   -d: to start directily into the debugguer\n"
+          "   -b: to use palette file 'filename.pal'\n",
+          argv[0]);
+   exit(0);
+}
+
+int main(int argc, char *argv[])
+{    
+   int i;
+   unsigned char j, k;
+   unsigned char *MemoryPage;
+   
+   /* Here we will fill the memory */
+   /*
+    --------------------------------------- $10000
+    Upper Bank of Cartridge ROM
+    --------------------------------------- $C000
+    Lower Bank of Cartridge ROM
+    --------------------------------------- $8000
+    Cartridge RAM (may be battery-backed)
+    --------------------------------------- $6000
+    Expansion Modules
+    --------------------------------------- $5000
+    Input/Output
+    --------------------------------------- $2000
+    2kB Internal RAM, mirrored 4 times
+    --------------------------------------- $0000
+    */
+   
+   /* Print the banner */
+   printf("--------------------------------------------------------------------------------\n"
+          "Welcome to TI-NESulator v%d.%d - by Godzil\n"
+          "Copyright 2003-2007 TRAPIER Manoel (godzil@godzil.net)\n"
+          "%s\n%s\n%s\n"
+          "--------------------------------------------------------------------------------\n\n", 
+          V_MAJOR,
+          V_MINOR,
+          VS_REVISION,
+          VS_LASTCHANGEDDATE,
+          VS_AUTHOR);
+   
+   printf("Install signal handlers...\t[");
+   //    signal(SIGABRT,signalhandler);
+   printf("A");
+   //    signal(SIGILL,signalhandler);
+   printf("I");
+   /*signal(SIGINT,signalhandler);*/
+   printf(".");
+   //    signal(SIGSEGV,signalhandler);
+   printf("S");
+   //    signal(SIGTERM,signalhandler);
+   printf("T]\n");
+   
+   /*  */
+   printf("Initialize memory...\t\t");
+   InitMemory();
+   printf("[ OK ]\n");
+   printf("Parsing parameters (%d)...\n", argc);
+   /* Now we use a real argument parser ! */
+   for(i = 1 ; (i < argc) && (argv[i][0]=='-'); i++)
+   {
+      switch(argv[i][1])
+      {
+         default: /* Option not recognized */
+         case 'h': /* ask for help */
+            printUsage(argc, argv);
+            break;
+            
+         case 'p': 
+            if (atoi(argv[i+1]) != 0)
+            {
+               printf("-Load plugin #%d...\n", atoi(argv[i+1]));
+               if ( plugin_load(atoi(argv[i+1])) == -1)
+               {
+                  plugin_list();
+                  exit(0);
+               }
+               i++;
+            }
+            else
+            {
+               plugin_list();
+               exit(0);
+            }
+            break;
+            
+            case 'f':
+            printf("-Start with fds!\n");
+            START_WITH_FDS = 1;
+            break;
+            
+            case 'd':
+            printf("-Start with debug!\n");
+            START_DEBUG = 1;
+            break;
+            
+            case 'b':
+            printf("-Palette file is %s\n", argv[i+1]);
+            PALETTE_FILENAME = argv[i+1];
+            i++;
+            break;      
+      }
+      
+   }
+   
+   CART_FILENAME = argv[argc-1];
+   
+   if (CART_FILENAME == NULL)
+      printUsage(argc, argv);
+   
+   printf("Allocating 6502 memory\t\t");
+   
+   /* Allocating first 0x7FF memory */
+   MemoryPage = (unsigned char *)malloc (0x800);
+   set_page_ptr_2k(0,MemoryPage);
+   for (i = 0; i < 0x08; i++)
+   {
+      set_page_readable(i, true);
+      set_page_writeable(i, true);
+   }
+   
+   /* Set ghost starting from 0x800 */
+   set_page_ghost(0x08,true,0x00);
+   set_page_ghost(0x09,true,0x01);
+   set_page_ghost(0x0A,true,0x02);
+   set_page_ghost(0x0B,true,0x03);
+   set_page_ghost(0x0C,true,0x04);
+   set_page_ghost(0x0D,true,0x05);
+   set_page_ghost(0x0E,true,0x06);
+   set_page_ghost(0x0F,true,0x07);
+   
+   /* Set ghost starting from 0x1000 */
+   set_page_ghost(0x10,true,0x00);
+   set_page_ghost(0x11,true,0x01);
+   set_page_ghost(0x12,true,0x02);
+   set_page_ghost(0x13,true,0x03);
+   set_page_ghost(0x14,true,0x04);
+   set_page_ghost(0x15,true,0x05);
+   set_page_ghost(0x16,true,0x06);
+   set_page_ghost(0x17,true,0x07);
+   
+   /* Set ghost starting from 0x1800 */
+   set_page_ghost(0x18,true,0x00);
+   set_page_ghost(0x19,true,0x01);
+   set_page_ghost(0x1A,true,0x02);
+   set_page_ghost(0x1B,true,0x03);
+   set_page_ghost(0x1C,true,0x04);
+   set_page_ghost(0x1D,true,0x05);
+   set_page_ghost(0x1E,true,0x06);
+   set_page_ghost(0x1F,true,0x07);
+   
+   /* Set 0x4000 registers */
+   
+   /* "hack" : only page $40 is used by multiple devices, we need to multiplexe
+    it*/
+   set_page_wr_hook(0x40, WrHook4000Multiplexer);
+   set_page_rd_hook(0x40, RdHook4000Multiplexer);
+   
+   set_page_readable(0x40, true);
+   set_page_writeable(0x40, true);
+   
+   /* Exp ROM : Nothing to do actually */
+   
+   /* SRAM (0x6000 : 0x2000 bytes ) */
+   MemoryPage = (unsigned char *)malloc (0x2000);
+   
+   set_page_ptr_8k(0x60, MemoryPage);
+   
+   /* ROM ptr will be set by mapper */
+   /* But we will set the readable bit */
+   for (i = 0x80; i < 0x100; i++)
+   {
+      set_page_readable(i, true);
+      set_page_writeable(i, false);
+   }
+   
+   printf("[ OK ]\n");
+   
+#define Value(s) (((s%0xFF) + (rand()%0xFF-128) )%0xFF)
+   
+   printf("Testing memory validity...\n");
+   
+   map_sram();
+   
+   for(i = 0x0000; i < 0x2000; i ++)
+   {    
+      j = Value(i);    
+      MemoryPage[i] = j;
+   }
+   
+   for(i = 0x6000; i < 0x8000; i ++)
+   {    
+      if (MemoryPage[i-0x6000] != (k = Rd6502(i)))
+         printf("Error RdRead @ 0x%X [should:%d,is:%d]\n", i, MemoryPage[i-0x6000], k);
+      if (MemoryPage[i-0x6000] != (k = Op6502(i)))
+         printf("Error OpRead @ 0x%X [should:%d,is:%d]\n", i, MemoryPage[i-0x6000], k);
+   }    
+   
+   printf("Testing memory... (<0x2000)\n");
+   for( i = 0 ; i < 0x2000 ; i++ ) {
+      j = Value(i);
+      Wr6502(i, j);
+      if ((k=Rd6502(i)) != j)
+         printf("Error read/write @ 0x%X [w:%d,r:%d]\n", i, j, k);
+      if ((k=Op6502(i)) != j)
+         printf("Error opcode @ 0x%X [w:%d,r:%d]\n", i, j, k);
+   }
+   
+   printf("Testing memory... (0x6000-0x8000)\n");
+   for(i=0x6000;i<0x8000;i++) {
+      j = Value(i);
+      Wr6502(i, j);
+      if ((k=Rd6502(i)) != j)
+         printf("Error read/write @ 0x%X [w:%d,r:%d]\n", i, j, k);
+      if ((k=Op6502(i)) != j)
+         printf("Error opcode @ 0x%X [w:%d,r:%d]\n", i, j, k);
+   }
+   
+   printf("Reseting main RAM...\t\t");
+   //Force the stack to be empty of zero
+   for( i = 0x100 ; i < 0x200 ; i++ ) {
+      Wr6502(i, 0x00);
+   }
+   printf("[ OK ]\n");
+   
+   if (START_WITH_FDS)
+   {
+      int fd;
+      printf("Loading FDS ROM...\t\t");
+      
+      fd = open("../data/disksys.rom", O_RDONLY);
+      
+      FDSRom = mmap(NULL, 8*1024, PROT_READ, MAP_PRIVATE, fd, 0);
+      printf("%p [ OK ]\n", FDSRom);
+      close(fd);
+      
+      set_page_ptr_8k(0xE0, FDSRom);
+      
+      printf("Allocating FDS RAM...\t\t");
+      
+      FDSRam = (byte*) malloc( (8+16) * 1024);
+      
+      if (FDSRam == NULL)
+         fatal("Allocation error\n");
+      
+      for (i = 0x80; i < 0xE0; i++)
+      {
+         set_page_ptr(i, FDSRam + (i*0x100));
+         set_page_readable(i, true);
+         set_page_writeable(i, true);
+      }
+      
+      Cart->MapperID = 100;
+   }
+   else
+   {
+      Cart = malloc( sizeof (NesCart));
+      if (Cart == NULL)
+         fatal("Memory allocation error...\n");
+      printf("Please Wait while loading %s cartridge...\n", CART_FILENAME);
+      if (LoadCart(CART_FILENAME, Cart) != 0)
+         fatal("Loading error...\n");
+      
+      if (Cart->Flags & iNES_BATTERY)
+      {
+         LoadSaveRam(CART_FILENAME);
+      }        
+      
+   }
+   
+   unmap_sram();
+   
+   InitPaddle(&P1);
+   
+   printf("Initializing Allegro...\t\t");
+   allegro_init();
+   install_timer();
+   install_keyboard();
+   printf("[ OK ]\n");    
+   printf("Set graphic mode...\t\t");
+   set_color_depth(8);
+   set_gfx_mode(GFX_AUTODETECT_WINDOWED, 512 + 256, 480, 512 + 256, 480);
+   Buffer = create_bitmap(512 + 256, 480);
+   clear_to_color(Buffer, 0x0D);
+   
+   set_close_button_callback(CloseHook);
+   set_window_title("TI-NESulator");
+   
+   printf("[ OK ]\n");
+   
+   printf("Init PPU...\n");
+   
+   if (ppu_init() != 0)
+      fatal("PPU Initialisation error..\n");   
+   
+   //DumpMemoryState();
+   if (Cart->Flags & iNES_4SCREEN)
+   {
+      ppu_setScreenMode(PPU_SCMODE_FOURSC);
+   }
+   else
+   {       
+      ppu_setScreenMode(PPU_SCMODE_NORMAL);
+      ppu_setMirroring((Cart->Flags & iNES_MIRROR)?PPU_MIRROR_VERTICAL:PPU_MIRROR_HORIZTAL);
+   }
+   
+   printf("Init mapper...\t\t\t");
+   if (mapper_init(Cart) == -1)
+      return -1;
+   printf("[ OK ]\n");
+   
+   if (PALETTE_FILENAME == NULL)
+   {
+      set_palette(basicPalette);
+   }
+   else
+   {
+      LoadPalette(PALETTE_FILENAME, pal);    
+      set_palette(pal);
+   }
+   
+   /*  for(i = 0; i < 256; i++)
+    {
+    r = (r * 64) / 255;
+    v = (v * 64) / 255;
+    b = (b * 64) / 255;
+    
+    pal[i].r = r;
+    pal[i].g = v;
+    pal[i].b = b;
+    
+    pal[i + 64].r = r;
+    pal[i + 64].g = v;
+    pal[i + 64].b = b;
+    
+    pal[i + 128].r = r;
+    pal[i + 128].g = v;
+    pal[i + 128].b = b;
+    
+    pal[i + 192].r = r;
+    pal[i + 192].g = v;
+    pal[i + 192].b = b;
+    
+    printf("       { 0x%02X, 0x%02X, 0x%02X, 0x%02X },\n",
+    (basicPalette[i].r * 64) / 255,
+    (basicPalette[i].g * 64) / 255,
+    (basicPalette[i].b * 64) / 255,
+    basicPalette[i].filler);
+    printf("       { 0x%02X, 0x%02X, 0x%02X, 0x%02X },\n",
+    pal[i].r,
+    pal[i].g,
+    pal[i].b,
+    pal[i].filler);
+    }
+    
+    exit(0);*/
+   
+#ifdef USE_SOUND
+   InitSound(48000,!0);
+   
+   SetSound(0, SND_RECTANGLE);
+   SetSound(1, SND_RECTANGLE);
+   SetSound(2, SND_TRIANGLE);
+   SetSound(3, SND_NOISE);
+#endif
+   
+   
+   /*   short val = 0xCE;
+    
+    while(1)
+    {  
+    
+    Wr6502(0x4000, 0x74);
+    Wr6502(0x4001, 0x68);
+    Wr6502(0x4002, val & 0xFF);
+    Wr6502(0x4003, 0x08 | ((val & 0x700) >> 8));
+    
+    
+    if (key[KEY_UP])
+    val++;
+    if (key[KEY_DOWN])
+    val--;   
+    
+    usleep(500);     
+    }
+    
+    
+    exit(0);*/
+   
+   /*    int K, L1, V = 127, L2, I;
+    
+    if(440>=48000/3) printf("Pwet\n");
+    K=48000/440;
+    L1=0;
+    V<<=7;
+    for(I=0;I<SND_BUFSIZE;I++)
+    {
+    L2=L1+K;
+    printf("L1:%d L2:%d K:%d V:%d\n", L1, L2, K, V);
+    //L1&0x8000?(L2&0x8000? V:0):(L2&0x8000? 0:-V)
+    //printf("Wave[%d] = %d\n", I,  L1&0x8000?(L2&0x8000? V:0):(L2&0x8000? 0:-V));
+    L1=L2;
+    }
+    printf("Phaser: %d\n", L1);
+    
+    
+    exit(0);*/
+   
+   printf("Press ESC to pause emulation and jump to debugguer\n");
+   install_int(ips_fps_counter, 1000);
+   ScanLine = 0;
+   
+   //Do a loop every HBlank
+   MainCPU.IPeriod = HBLANK_TIME;
+   //MainCPU.IPeriod = 260;
+   
+   Reset6502(&MainCPU);
+   
+   MainCPU.Trace = 0;
+   
+   if (START_DEBUG)
+      MainCPU.Trace = 1;
+   
+   gettimeofday(&timeStart, NULL);
+   
+   Run6502(&MainCPU);
+   
+   if (Cart->Flags & iNES_BATTERY)
+   {
+      SaveSaveRam(CART_FILENAME);
+   }
+   return 0;
+}
+END_OF_MAIN()
+
+/** Rd6502()/Wr6502/Op6502() *********************************/
+/** These functions are called when access to RAM occurs.   **/
+/** They allow to control memory access. Op6502 is the same **/
+/** as Rd6502, but used to read *opcodes* only, when many   **/
+/** checks can be skipped to make it fast. It is only       **/
+/** required if there is a #define FAST_RDOP.               **/
+/************************************ TO BE WRITTEN BY USER **/
+void Wr6502(register word Addr, register byte Value)
+{            /* Write to memory */
+   WriteMemory((Addr&0xFF00)>>8,Addr&0x00FF, Value);
+}
+
+byte Rd6502(register word Addr)
+{            /* Read memory for normal use */
+   return ReadMemory((Addr&0xFF00)>>8,Addr&0x00FF);
+   
+}
+
+extern byte *memory_pages[0xFF];
+byte Op6502(register word Addr)
+{            /* Read OpCodes */
+   byte *ptr;
+   return (ptr = memory_pages[(Addr&0xFF00)>>8])>1?ptr[Addr&0x00FF]:0;
+   
+   //return ReadMemory((Addr&0xFF00)>>8,Addr&0x00FF);
+}
+
+/** Loop6502() ***********************************************/
+/** 6502 emulation calls this function periodically to      **/
+/** check if the system hardware requires any interrupts.   **/
+/** This function must return one of following values:      **/
+/** INT_NONE, INT_IRQ, INT_NMI, or INT_QUIT to exit the     **/
+/** emulation loop.                                         **/
+/************************************ TO BE WRITTEN BY USER **/
+byte Loop6502(register M6502 * R)
+{
+   byte ret;
+   short skey; 
+   printf("pouic\n");
+   long WaitTime;
+   static long delta=0;
+   
+   ret = INT_NONE;
+   
+   if ( mapper_irqloop (ScanLine) )
+   {
+      ret = INT_IRQ;
+      IRQScanHit = ScanLine;
+   }
+   
+   if ( MapperWantIRQ == 1)
+   {
+      MapperWantIRQ = 0;
+      ret = INT_IRQ;
+   }
+   
+   if ( ppu_hblank(ScanLine) != 0 )
+   {
+      /*        if (ret == INT_IRQ)
+       MapperWantIRQ = 1;*/
+      ret = INT_NMI;        
+   }
+   
+   if (ScanLine == 241)
+      frame++;  
+   
+   //if (ScanLine >= (241 + VBLANK_TIME))
+   if (ScanLine >= 262)
+   {   /* End of VBlank Time */
+      /* Sync at 60FPS */
+      /* Get current time in microseconds */
+      gettimeofday(&timeEnd, NULL);     
+      
+      /* Calculate the waiting time, 16666 is the time of one frame in microseconds at a 60Hz rate) */
+      WaitTime = (timeEnd.tv_sec) - (timeStart.tv_sec);           
+      WaitTime *= 1000000;
+      WaitTime += (timeEnd.tv_usec - timeStart.tv_usec);
+      WaitTime = 16666 - WaitTime + delta;
+      
+      
+      /* If we press Page Up, we dont we to accelerate "time" */
+      if (!key[KEY_PGUP])
+         if ((WaitTime >= 0) && (WaitTime < 100000))
+            usleep(WaitTime);
+      //usleep(WaitTime<0?0:(WaitTime>100000?0:WaitTime));
+      
+      /* Now get the time after sleep */
+      gettimeofday(&timeStart, NULL);
+      
+      /* Now calculate How many microseconds we really spend in sleep and 
+       calculate a delta for next iteration */
+      delta = (timeStart.tv_sec) - (timeEnd.tv_sec);           
+      delta *= 1000000;
+      delta += (timeStart.tv_usec - timeEnd.tv_usec);
+      delta = WaitTime - delta;
+      
+      /* To avoid strange time warp when stoping emulation or using acceleration a lot */
+      if ((delta > 10000) || (delta < -10000)) 
+         delta = 0;
+      
+      ScanLine = 0;
+   }
+   else
+      
+      ScanLine++;
+   
+   if (keypressed())
+   {
+      skey = (readkey() & 0xFF);
+      if (skey == 27)
+         R->Trace = 1;
+      
+      if (skey == '9')
+      {
+         VBLANK_TIME += 1;
+         printf("VBLT: %d\n", VBLANK_TIME);
+      }
+      
+      if (skey == '6')
+      {
+         VBLANK_TIME -= 1;
+         printf("VBLT: %d\n", VBLANK_TIME);            
+      }
+      
+      if (skey == '7')
+      {
+         HBLANK_TIME += 1;
+         printf("HBLT: %d\n", HBLANK_TIME);  
+         MainCPU.IPeriod = HBLANK_TIME;          
+      }
+      
+      if (skey == '4')
+      {
+         HBLANK_TIME -= 1;
+         printf("HBLT: %d\n", HBLANK_TIME);            
+         MainCPU.IPeriod = HBLANK_TIME;
+      }
+      
+      //        if ((skey == '&') || (skey == '1'))
+      //          ppu.ForceBgVisibility = ~ppu.ForceBgVisibility;
+      //        if ((skey == 'ª') || (skey == '2'))
+      //          ppu.ForceSpriteVisibility = ~ppu.ForceSpriteVisibility;
+      
+      //        if ((skey == '"') || (skey == '3'))
+      //          ppu.DisplayNameTables = ~ppu.DisplayNameTables;
+      
+      //        if ((skey == '\'') || (skey == '4'))
+      //          ppu.DisplayAttributeTable = ~ppu.DisplayAttributeTable;
+      
+      //        if ((skey == '(') || (skey == '5'))
+      //          ppu.DisplayPalette = ~ppu.DisplayPalette;
+      
+      //        if ((skey == '-') || (skey == 'fl') || (skey == '6'))        
+      //          ppu.DisplayVRAM = ~ppu.DisplayVRAM;
+      
+      if ((skey == 'r') || (skey == 'R'))
+      {
+         //Reset6502(R);
+         MainCPU.PC.B.l=Rd6502(0xFFFC);
+         MainCPU.PC.B.h=Rd6502(0xFFFD);   
+         
+      }
+      
+      plugin_keypress(skey);
+      
+   }
+   
+   if (WantClosing == 1)
+   {
+      ret = INT_QUIT;
+   }
+   return ret;
+}
+
+/** Patch6502() **********************************************/
+/** Emulation calls this function when it encounters an     **/
+/** unknown opcode. This can be used to patch the code to   **/
+/** emulate BIOS calls, such as disk and tape access. The   **/
+/** function should return 1 if the exception was handled,  **/
+/** or 0 if the opcode was truly illegal.                   **/
+/************************************ TO BE WRITTEN BY USER **/
+byte Patch6502(register byte Op, register M6502 * R)
+{
+   //printf("Invalid Opcode : 0x%X @ 0x%04X !\n", Op, R->PC.W);
+   return 1;
+}

+ 62 - 0
src/mappersmanager/manager.c

@@ -0,0 +1,62 @@
+/*
+ *  manager.c
+ *  TI-NESulator.X
+ *
+ *  Created by Manoël Trapier on 07/10/07.
+ *  Copyright 2007 986 Corp. All rights reserved.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <mappers/manager.h>
+
+MapperIRQ       mapper_irqloop;
+MapperDump      mapper_dump;
+MapperWriteHook mapper_hook;
+
+typedef struct Mapper_
+{   
+   byte id;
+   byte *name;
+
+   MapperInit      init;
+   MapperIRQ       irq;
+   MapperDump      dump;
+   
+} Mapper;
+
+#include "mappers_list.h"
+
+void mapper_list ()
+{
+   Mapper *ptr = &(Mappers[0]);
+   printf("Available mapers:\n");
+   while(ptr->name != NULL)
+   {
+      printf("%d - %s\n", ptr->id, ptr->name);
+      ptr++;
+   }
+}
+
+int mapper_init (NesCart *cart)
+{
+   Mapper *ptr = &(Mappers[0]);
+   printf ("Search for a compatible mapper ID #%X:\n", cart->MapperID);
+   while (ptr->name != NULL)
+   {
+      if (ptr->id == cart->MapperID)
+      {
+         printf ("Found mapper ID #%X - '%s'\n", ptr->id, ptr->name);
+         ptr->init (cart);
+         
+         mapper_irqloop = ptr->irq;
+         mapper_dump    = ptr->dump;
+         
+         return 0;
+      }
+      ptr++;
+   }
+   return -1;
+}

+ 361 - 0
src/mappersmanager/mappers/mmc1.c

@@ -0,0 +1,361 @@
+/*
+ *  MMC1 Mapper - The TI-NESulator Project
+ *  mmc1.h
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-05-02 18:37:41 +0200 (mer, 02 mai 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/mmc1.h $
+ *  $Revision: 50 $
+ *
+ */
+
+#include "norom.h"
+
+#include <ppu/ppu.h>
+#include <memory/manager.h>
+
+unsigned char MMC1_reg0;
+
+unsigned char MMC1_reg1;
+
+unsigned char MMC1_reg2;
+
+unsigned char MMC1_reg3;
+
+unsigned char mmc1_CurrentBank;
+
+#define MMC1_R0_MIRROR    0x01
+#define MMC1_R0_ONESCREEN  0x02
+#define MMC1_R0_PRGAREA   0x04
+#define MMC1_R0_PRGSIZE   0x08
+#define MMC1_R0_VROMSW    0x10
+#define MMC1_R0_RESET     0x80
+
+#define MMC1_R1_VROMB1    0x0F
+#define MMC1_R1_256KSEL   0x10
+#define MMC1_R1_RESET     0x80
+
+#define MMC1_R2_VROMB2    0x0F
+#define MMC1_R2_256KSEL   0x10
+#define MMC1_R2_RESET     0x80
+
+#define MMC1_R3_VROMB2    0x0F
+#define MMC1_R3_SAVECE    0x10
+#define MMC1_R3_RESET     0x80
+
+
+#define MMC1_REG0_DEFAULT MMC1_R0_PRGSIZE | MMC1_R0_PRGAREA
+#define MMC1_REG1_DEFAULT 0
+#define MMC1_REG2_DEFAULT 0
+#define MMC1_REG3_DEFAULT 0
+
+
+void mmc1_MapperWriteReg0(register byte Addr, register byte Value);
+void mmc1_MapperWriteReg1(register byte Addr, register byte Value);
+void mmc1_MapperWriteReg2(register byte Addr, register byte Value);
+void mmc1_MapperWriteReg3(register byte Addr, register byte Value);
+
+void mmc1_MapperDump(FILE *fp)
+{
+    fprintf(fp,"MMC1: r0:0x%02X r1:0x%02X r2:0x%02X r3:0x%02X\n",MMC1_reg0,MMC1_reg1,MMC1_reg2,MMC1_reg3);
+}
+
+int mmc1_InitMapper(NesCart * cart) 
+{
+    int i;
+    
+       MMC1_reg0 = MMC1_REG0_DEFAULT;
+       
+       MMC1_reg1 = MMC1_REG1_DEFAULT;
+       
+       MMC1_reg2 = MMC1_REG2_DEFAULT;
+       
+       MMC1_reg3 = MMC1_REG3_DEFAULT;
+        
+       set_prom_bank_16k(0x8000,0);
+       set_prom_bank_16k(0xC000, GETLAST16KBANK(cart));
+       
+       mmc1_CurrentBank = 0;
+       
+       if (cart->VROMSize > 0)
+           set_vrom_bank_4k(0x0000,0);
+
+       
+       /* Mapper should register itself for write hook */
+    for (i = 0x80; i < 0xA0 ; i++)
+    {
+       set_page_wr_hook(i, mmc1_MapperWriteReg0);
+       set_page_writeable(i, true);       
+    } 
+    for (i = 0xA0; i < 0xC0 ; i++)
+    {
+       set_page_wr_hook(i, mmc1_MapperWriteReg1);
+       set_page_writeable(i, true);       
+    } 
+    for (i = 0xC0; i < 0xE0 ; i++)
+    {
+       set_page_wr_hook(i, mmc1_MapperWriteReg2);
+       set_page_writeable(i, true);       
+    } 
+    for (i = 0xE0; i < 0x100 ; i++)
+    {
+       set_page_wr_hook(i, mmc1_MapperWriteReg3);
+       set_page_writeable(i, true);       
+    }
+    
+    return 0;
+       
+} 
+
+
+/*
+Reg 0
+8 : 1000
+9 : 1001
+
+        Reg 1
+        A : 1010
+        B : 1011
+        
+          Reg 2
+          C : 1100
+          D : 1101
+          
+            Reg 3
+            E : 1110
+            F : 1111
+            
+              ((Addr & 0x6000) >> 13) <- port number
+*/ 
+#define MMC1_GetReg(a) ((a & 0x6000) >> 13)
+/* (Val & 0x01) recuperation du bit */ 
+#define MMC1_GetBit(v) (v & 0x01)
+/* ( ( b & (1 << Bit)) | (v << Bit) ) Ajout du bit */ 
+#define MMC1_AddBit(b,v) ( ( b & ~(1 << Bit)) | (v << Bit) )
+
+void mmc1_ApplyReg0Mod() 
+{
+    
+    static unsigned char OldSwitchArea = MMC1_R0_PRGAREA;
+    
+    
+       
+    //printf("Change to reg0 done ! (0x%x)\n\tMiror flag : %d\n\tOneScreen Flag : %d\n\tPRG Size : %d\n\tPRG Area : %d\n\tVROM Switch size : %d\n", MMC1_reg0, MMC1_reg0 & MMC1_R0_MIRROR, MMC1_reg0 & MMC1_R0_ONESCREEN, MMC1_reg0 & MMC1_R0_PRGAREA, MMC1_reg0 & MMC1_R0_PRGSIZE, MMC1_reg0 & MMC1_R0_VROMSW);
+        
+    switch (MMC1_reg0 & 0x03)
+    {
+    case 0:
+        ppu_setScreenMode(PPU_SCMODE_SINGLE);
+        ppu_setSingleScreen(PPU_SCREEN_000);
+        break;
+    case 1:
+        ppu_setScreenMode(PPU_SCMODE_SINGLE);
+        ppu_setSingleScreen(PPU_SCREEN_400);
+        break;
+    case 2:
+        ppu_setScreenMode(PPU_SCMODE_NORMAL);
+        ppu_setMirroring(PPU_MIRROR_VERTICAL);
+        break;
+    case 3:
+        ppu_setScreenMode(PPU_SCMODE_NORMAL);
+        ppu_setMirroring(PPU_MIRROR_HORIZTAL);
+        break;
+    }
+       
+    if ( (OldSwitchArea != (MMC1_reg0 & MMC1_R0_PRGAREA)) && ((MMC1_reg0 & MMC1_R0_PRGSIZE) != 0 ) )
+    {
+           
+        if ((MMC1_reg0 & MMC1_R0_PRGAREA) != 0)
+        {        /* 0x8000 area */
+            set_prom_bank_16k(0x8000,mmc1_CurrentBank);
+            set_prom_bank_16k(0xC000, GETLAST16KBANK(Cart));
+        } 
+        else
+        {        /* 0xC000 area */
+    
+            set_prom_bank_16k(0x8000,0);
+            set_prom_bank_16k(0xC000,mmc1_CurrentBank);
+
+        } 
+           
+        OldSwitchArea = (MMC1_reg0 & MMC1_R0_PRGAREA);
+    }
+       
+} 
+
+int VROMBankNb;
+unsigned char Bit = 0;
+unsigned char BitBuf = 0;
+
+void mmc1_MapperWriteReg0(register byte Addr, register byte Value) 
+{
+    if (Value & 0x80) 
+    {   
+        MMC1_reg0 = MMC1_REG0_DEFAULT;
+        printf("MMC1: Reg0 Reset occured !\n");
+        mmc1_ApplyReg0Mod();
+    }
+    else
+    {
+        if (Bit < 4)
+        {        /* Pas encore ecrit les 5 bits  */
+            BitBuf = MMC1_AddBit(BitBuf, MMC1_GetBit(Value));
+            Bit++;
+        } 
+        else
+        {
+            BitBuf = MMC1_AddBit(BitBuf, MMC1_GetBit(Value));
+            Bit = 0;
+            
+            MMC1_reg0 = BitBuf;
+            
+            mmc1_ApplyReg0Mod();
+        }
+    }               
+}
+
+void mmc1_MapperWriteReg1(register byte Addr, register byte Value) 
+{
+    if (Value & 0x80) 
+    {   
+        MMC1_reg1 = MMC1_REG1_DEFAULT;
+        printf("MMC1: Reg1 Reset occured !\n");
+    }
+    else
+    {
+        if (Bit < 4)
+        {        /* Pas encore ecrit les 5 bits  */
+            BitBuf = MMC1_AddBit(BitBuf, MMC1_GetBit(Value));
+            Bit++;
+        } 
+        else
+        {
+            BitBuf = MMC1_AddBit(BitBuf, MMC1_GetBit(Value));
+            Bit = 0;
+            
+            MMC1_reg1 = BitBuf;
+                        
+            VROMBankNb = (MMC1_reg1 /* & MMC1_R1_VROMB1 */ );
+
+            if (Cart->VROMSize == 0)
+            {
+                printf("Try to change VROM but with didn't have any VROM ! [%04X]\n", VROMBankNb);
+                return;
+            }
+                
+            if ( (MMC1_reg0 & MMC1_R0_VROMSW) != 0 )
+            {    /* 4K vram */
+                //printf("Switching VROM at 0x0000 to 4k bank %d\n", VROMBankNb);
+                set_vrom_bank_4k(0x0000,VROMBankNb);
+            }
+            else
+            {    /* 8K vram */
+                //printf("Switching VROM at 0x0000 to 8k bank %d\n", VROMBankNb>>1);
+                set_vrom_bank_8k(0x0000,VROMBankNb>>1);
+            }
+        }
+    }
+}
+
+void mmc1_MapperWriteReg2(register byte Addr, register byte Value) 
+{
+    if (Value & 0x80) 
+    {   
+        MMC1_reg2 = MMC1_REG2_DEFAULT;
+        printf("MMC1: Reg2 Reset occured !\n");
+    }
+    else
+    {
+        if (Bit < 4)
+        {        /* Pas encore ecrit les 5 bits  */
+            BitBuf = MMC1_AddBit(BitBuf, MMC1_GetBit(Value));
+            Bit++;
+        } 
+        else
+        {
+            BitBuf = MMC1_AddBit(BitBuf, MMC1_GetBit(Value));
+            Bit = 0;
+
+            MMC1_reg2 = BitBuf;
+            
+            VROMBankNb = (MMC1_reg2 /* & MMC1_R2_VROMB2 */ );
+            
+            //printf("Want to switch VROM at 0x1000 to 4k bank %d\n", VROMBankNb);
+            if (Cart->VROMSize == 0)
+            {
+                //printf(": No\n");
+                return;
+            }
+            
+            if ( (MMC1_reg0 & MMC1_R0_VROMSW) != 0 )
+            {    /* 4K vram */
+                //printf("Switching VROM at 0x1000 to 4k bank %d\n", VROMBankNb);
+                set_vrom_bank_4k(0x1000,VROMBankNb);
+            }
+            else
+            {    /* 8K vram */
+            //       printf("Switching VROM at 0x1000 to 8k bank %d\n", VROMBankNb>>1);
+            //       set_vrom_bank_8k(0x1000,VROMBankNb>>1);
+            }
+        }
+    }
+}
+
+void mmc1_MapperWriteReg3(register byte Addr, register byte Value) 
+{
+    if (Value & 0x80) 
+    {   
+        MMC1_reg3 = MMC1_REG3_DEFAULT;
+        printf("MMC1: Reg3 Reset occured !\n");
+    }
+    else
+    {
+        if (Bit < 4)
+        {        /* Pas encore ecrit les 5 bits  */
+            BitBuf = MMC1_AddBit(BitBuf, MMC1_GetBit(Value));
+            Bit++;
+        } 
+        else
+        {
+            BitBuf = MMC1_AddBit(BitBuf, MMC1_GetBit(Value));
+            Bit = 0;
+        
+            MMC1_reg3 = BitBuf;
+            
+            if (MMC1_reg3<<14 > Cart->PROMSize)
+                return;
+                
+            if ( (MMC1_reg0 & MMC1_R0_PRGSIZE) != 0 )
+            {    /* 16K Switch */
+                if ( (MMC1_reg0 & MMC1_R0_PRGAREA) != 0 )
+                {    /* 0x8000 switch */
+                    set_prom_bank_16k(0x8000,MMC1_reg3);
+                    //printf("LowBank is now %d ( 0x%p )\n", MMC1_reg3, mLBank);
+                }
+                else
+                {    /* 0xC000 switch */
+                    set_prom_bank_16k(0xC000,MMC1_reg3);
+                    //printf("HighBank is now %d ( 0x%p )\n", MMC1_reg3, mUBank);
+                }
+            }
+            else
+            {    /* 32K Switch */
+                set_prom_bank_32k(0x8000,MMC1_reg3>>1);
+            }
+            
+            if ( ( MMC1_reg3 & MMC1_R3_SAVECE ) != 0)   
+            {
+                unmap_sram();
+            }
+            else
+            {
+                map_sram();
+            }
+        }
+    }
+}    
+        
+        //printf("MMC1: Debug (Reg:%d,Val:0x%02X,reg0:0x%02X,reg1:0x%02X,reg2:0x%02X,reg3:0x%02X)\n", MMC1_GetReg(Addr), Value, MMC1_reg0, MMC1_reg1, MMC1_reg2, MMC1_reg3);

+ 16 - 0
src/mappersmanager/mappers/mmc1.h

@@ -0,0 +1,16 @@
+/*
+ *  mmc1.h
+ *  TI-NESulator.X
+ *
+ *  Created by Manoël Trapier on 02/12/07.
+ *  Copyright 2007 986 Corp. All rights reserved.
+ *
+ */
+
+#define __TINES_MAPPERS__
+#include <mappers/manager.h>
+
+int mmc1_InitMapper  (NesCart *cart);
+int mmc1_MapperIRQ   (int cycledone);
+void mmc1_MapperDump ();
+void mmc1_MapperWriteHook(register byte Addr, register byte Value); 

+ 48 - 0
src/mappersmanager/mappers/norom.c

@@ -0,0 +1,48 @@
+/*
+ *  norom.c
+ *  TI-NESulator.X
+ *
+ *  Created by Manoël Trapier on 25/10/07.
+ *  Copyright 2007 986 Corp. All rights reserved.
+ *
+ */
+
+#include "norom.h"
+
+int norom_InitMapper(NesCart *cart) 
+{
+   int i;
+      
+   set_page_ptr_16k(0x80, cart->PROMBanks); 
+   
+   /* mUBank = 0xC000 */   
+   if (cart->PROMSize > (16*1024))
+   {
+      set_prom_bank_16k(0xC000, 1);
+   }
+   else        
+   {
+      set_prom_bank_16k(0xC000, 0);
+   }
+   
+   if (cart->VROMSize > 0)
+      set_vrom_bank_8k(0x2000, 0);
+   
+   return 0;
+}
+
+int norom_MapperIRQ(int cycledone)
+{
+   return 0;
+}
+
+void norom_MapperWriteHook(register byte Addr, register byte Value)
+{
+   /* Nothing to do */
+   return;
+}
+
+void norom_MapperDump (FILE *fp)
+{
+   fprintf(fp, "norom mapper have nothing to dump");
+}

+ 16 - 0
src/mappersmanager/mappers/norom.h

@@ -0,0 +1,16 @@
+/*
+ *  norom.h
+ *  TI-NESulator.X
+ *
+ *  Created by Manoël Trapier on 25/10/07.
+ *  Copyright 2007 986 Corp. All rights reserved.
+ *
+ */
+
+#define __TINES_MAPPERS__
+#include <mappers/manager.h>
+
+int norom_InitMapper  (NesCart *cart);
+int norom_MapperIRQ   (int cycledone);
+void norom_MapperDump ();
+void norom_MapperWriteHook(register byte Addr, register byte Value);

+ 63 - 0
src/mappersmanager/mappers/temp/aorom.c

@@ -0,0 +1,63 @@
+/*
+ *  AOROM Mapper - The TI-NESulator Project
+ *  aorom.h
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-04-26 18:47:34 +0200 (jeu, 26 avr 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/aorom.h $
+ *  $Revision: 46 $
+ *
+ */
+
+unsigned char aorom_load_bank;
+
+void aorom_MapperWriteHook(register byte Addr, register byte Value);
+
+extern byte *ppu_mem_nameTables;
+
+int aorom_InitMapper(NesCart * cart) 
+{    
+    int i;
+    
+    set_prom_bank_32k(0x8000,0);
+  
+    ppu_setScreenMode(PPU_SCMODE_SINGLE);
+    
+    aorom_load_bank = 0;
+
+    /* Register the write hook */
+    for (i = 0x80; i < 0x100; i++)
+    {
+        set_page_wr_hook(i, aorom_MapperWriteHook);
+        set_page_writeable(i, true);
+    }
+    
+
+    return 0;
+    
+} 
+
+void aorom_MapperWriteHook(register byte Addr, register byte Value) 
+{
+    int BankNb;
+
+        if (Value & (1 << 4))
+            ppu_setSingleScreen(PPU_SCREEN_000);
+        else
+            ppu_setSingleScreen(PPU_SCREEN_400);
+
+        BankNb = Value & 0x0F;
+
+        aorom_load_bank = BankNb;
+
+        //printf("aorom: Asking bank %d (giving %d & %d) - mirror is %d\n",BankNb,BankNb,(Value<<1)+1,Value&0x0F);
+        set_prom_bank_32k(0x8000,BankNb);
+} 
+
+void aorom_MapperDump(FILE *fp)
+{
+    fprintf(fp,"aorom: bank:%d\n",aorom_load_bank);
+}

+ 47 - 0
src/mappersmanager/mappers/temp/cnrom.c

@@ -0,0 +1,47 @@
+/*
+ *  CNROM Mapper - The TI-NESulator Project
+ *  cnrom.h
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-04-16 01:55:35 +0200 (lun, 16 avr 2007) $
+ *  $Author: godzil $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/cnrom.h $
+ *  $Revision: 39 $
+ *
+ */
+
+unsigned char cnrom_load_bank;
+
+void cnrom_MapperWriteHook(register byte Addr, register byte Value);
+
+int cnrom_InitMapper(NesCart * cart) 
+{
+    int i;
+    
+    set_prom_bank_16k(0x8000,  0);
+    set_prom_bank_16k(0xC000, GETLAST16KBANK(cart)); /* Set the last one */
+    cnrom_load_bank = 0;
+    
+    /* Register the write hook */
+    for (i = 0x80; i < 0x100; i++)
+    {
+        set_page_wr_hook(i, cnrom_MapperWriteHook);
+        set_page_writeable(i, true);
+    }
+    
+    return 0;
+} 
+
+
+void cnrom_MapperWriteHook(register byte Addr, register byte Value) 
+{
+    set_prom_bank_16k(0x8000,Value);
+    cnrom_load_bank = Value;
+}
+ 
+void cnrom_MapperDump(FILE *fp)
+{
+    fprintf(fp,"cnrom: bank:%d\n",cnrom_load_bank);
+}

+ 35 - 0
src/mappersmanager/mappers/temp/genericmapper.c

@@ -0,0 +1,35 @@
+/*
+ *  Generic mapper implementation - The TI-NESulator Project
+ *  genericmapper.h
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-04-16 01:55:35 +0200 (lun, 16 avr 2007) $
+ *  $Author: godzil $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/genericmapper.h $
+ *  $Revision: 39 $
+ *
+ */
+ 
+int _InitMapper(NesCart * cart) 
+{
+    
+    set_prom_bank_16k(0xC000,0);
+    set_prom_bank_16k(0x8000,-1);
+    
+    return 0;
+    
+} 
+
+int _MapperWriteHook(register word Addr, register byte Value) 
+{
+    
+    if (Addr > 0x7FFF)    /* Try to write to the rom */
+    {
+        set_vrom_bank_8k(0x0000,Value) 
+        
+        return 1;
+    } 
+    return 0;
+} 

+ 125 - 0
src/mappersmanager/mappers/temp/iremh3001.c

@@ -0,0 +1,125 @@
+/*
+ *  IREMH3001 Mapper - The TI-NESulator Project
+ *  iremh3001.h
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-04-16 01:55:35 +0200 (lun, 16 avr 2007) $
+ *  $Author: godzil $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/iremh3001.h $
+ *  $Revision: 39 $
+ *
+ */
+
+unsigned short iremh3001_prom_slot[3];
+
+unsigned short iremh3001_vrom_slot[8];
+
+int iremh3001_InitMapper(NesCart * cart) 
+{
+    
+    set_prom_bank_16k(0x8000, 0);
+    set_prom_bank_16k(0xC000, GETLAST16KBANK(cart));
+    
+    iremh3001_prom_slot[0] = 0;
+    iremh3001_prom_slot[1] = 1;
+    iremh3001_prom_slot[2] = GETLAST16KBANK(cart);
+
+    set_vrom_bank_8k(0x0000,4);
+
+    iremh3001_vrom_slot[0] = 0;
+    iremh3001_vrom_slot[1] = 0;
+    iremh3001_vrom_slot[2] = 0;
+    iremh3001_vrom_slot[3] = 0;
+    iremh3001_vrom_slot[4] = 0;
+    iremh3001_vrom_slot[5] = 0;
+    iremh3001_vrom_slot[6] = 0;
+    iremh3001_vrom_slot[7] = 0;
+
+    return 0;
+    
+} 
+
+int iremh3001_MapperWriteHook(register word Addr, register byte Value) 
+{
+
+  switch(Addr)
+  {
+  case 0x8000: /* Set 8k PROM @ 8000 */
+    printf("iremh3001: %X: change PROM to %d[%X]\n", Addr, Value, Value);
+    set_prom_bank_8k(0x8000, Value);
+    iremh3001_prom_slot[0] = Value;
+    break;
+
+  case 0x9003: /* Mirroring ??? */
+    printf("iremh3001: Mirroring[0x%X:%d] ?\n", Value, Value);
+    break;
+
+  case 0x9005: /* IRQ ??? */
+    printf("iremh3001: IRQ[0x%X:%d] ?\n", Value, Value);
+    break;
+
+  case 0x9006: /* IRQ ??? */
+    printf("iremh3001: IRQ[0x%X:%d] ?\n", Value, Value);
+    break;
+
+  case 0xA000: /* Set 8k PROM @ A000 */
+    printf("iremh3001: %X: change PROM to %d[%X]\n", Addr, Value, Value);
+    set_prom_bank_8k(0xA000, Value);
+    iremh3001_prom_slot[1] = Value;
+    break;
+
+  case 0xB000: /* Set 1k VROM @ 0000 */
+  case 0xB001: /* Set 1k VROM @ 0400 */
+  case 0xB002: /* Set 1k VROM @ 0800 */
+  case 0xB003: /* Set 1k VROM @ 0C00 */
+  case 0xB004: /* Set 1k VROM @ 1000 */
+  case 0xB005: /* Set 1k VROM @ 1400 */
+  case 0xB006: /* Set 1k VROM @ 1800 */
+  case 0xB007: /* Set 1k VROM @ 1C00 */
+    printf("iremh3001: %X: change VROM to %d[%X]\n", (Addr&0x0F)<<10, Value, Value);
+    set_vrom_bank_1k((Addr&0xF)<<10, Value);
+    iremh3001_vrom_slot[Addr&0x0F] = Value;
+    break;
+
+  case 0xC000: /* Set 8k PROM @ C000 */
+    printf("iremh3001: %X: change PROM to %d[%X]\n", Addr, Value, Value);
+    set_prom_bank_8k(0xC000, Value);
+    iremh3001_prom_slot[2] = Value;
+    break;
+
+  default:
+    printf("@:%X -- V:%X", Addr, Value);
+    return 0;
+
+  }
+
+  return 1;
+} 
+
+void iremh3001_MapperDump(FILE *fp)
+{
+    fprintf(fp,"iremh3001: prom: $8000:%d $A000:%d $C000:%d\n",
+        iremh3001_prom_slot[0], 
+        iremh3001_prom_slot[1],
+        iremh3001_prom_slot[2]);
+
+    fprintf(fp,"iremh3001: vrom: $0000:%d $0400:%d $0800:%d $0C00:%d\n" \
+           "                 $1000:%d $1400:%d $1800:%d $1C00:%d\n",
+        iremh3001_vrom_slot[0], 
+        iremh3001_vrom_slot[1],
+        iremh3001_vrom_slot[2], 
+        iremh3001_vrom_slot[3],
+        iremh3001_vrom_slot[4], 
+        iremh3001_vrom_slot[5],
+        iremh3001_vrom_slot[6],
+        iremh3001_prom_slot[7]);
+}
+
+
+int iremh3001_MapperIRQ(int cycledone)
+{
+
+  return 0;
+}

+ 317 - 0
src/mappersmanager/mappers/temp/mmc3.c

@@ -0,0 +1,317 @@
+/*
+ *  MMC3 Mapper - The TI-NESulator Project
+ *  mmc3.h
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-05-02 18:37:41 +0200 (mer, 02 mai 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/mmc3.h $
+ *  $Revision: 50 $
+ *
+ */
+
+unsigned short mmc3_command;
+
+unsigned char mmc3_irq_counter;
+unsigned char mmc3_irq_counter_reload;
+unsigned char mmc3_irq_enable;
+
+unsigned short mmc3_first_prom_page;
+unsigned short mmc3_second_prom_page;
+
+unsigned char  mmc3_use_xor;
+unsigned char mmc3_last_vrom[6];
+
+unsigned char mmc3_last_prom[2];
+unsigned char mmc3_last_prom_switch;
+
+unsigned short dummy;
+
+void mmc3_MapperWrite80Hook(byte addr, byte value);
+void mmc3_MapperWriteA0Hook(byte addr, byte value);
+void mmc3_MapperWriteC0Hook(byte addr, byte value);
+void mmc3_MapperWriteE0Hook(byte addr, byte value);
+
+void mmc3_MapperDump(FILE *fp)
+{
+    fprintf(fp,"MMC3: CMD:%d IC:%d IR:%d IE:%d FPP:0x%04X SPP:0x%04X UX:%d\n",mmc3_command,mmc3_irq_counter,mmc3_irq_counter_reload,mmc3_irq_enable,mmc3_first_prom_page,mmc3_second_prom_page,mmc3_use_xor);
+    fprintf(fp,"MMC3: LV0:%d LV1:%d LV2:%d LV3:%d LV4:%d LV5:%d\n",mmc3_last_vrom[0],mmc3_last_vrom[1],mmc3_last_vrom[2],mmc3_last_vrom[3],mmc3_last_vrom[4],mmc3_last_vrom[5]);
+    fprintf(fp,"MMC3: LP0:%d LP1:%d LPS:%d\n",mmc3_last_prom[0],mmc3_last_prom[1],mmc3_last_prom_switch);
+}
+
+int mmc3_InitMapper(NesCart * cart) 
+{
+    int i;
+
+    set_prom_bank_16k(0x8000, 0);
+    set_prom_bank_16k(0xC000, GETLAST16KBANK(cart));
+
+    if ( Cart->VROMSize > 0)
+    {
+         set_vrom_bank_8k(0, 0x0000);
+    }
+    
+    mmc3_command = -1;
+
+    mmc3_irq_counter = -1;
+    mmc3_irq_enable = 1;
+    mmc3_irq_counter_reload = 0;
+    
+    mmc3_use_xor = 0x42;
+    
+    mmc3_last_prom_switch = 0x42;
+    
+    mmc3_last_prom[0] = 0;
+    mmc3_last_prom[1] = 1;
+    
+    mmc3_last_vrom[0] = 0;
+    mmc3_last_vrom[1] = 2;
+    mmc3_last_vrom[2] = 3;
+    mmc3_last_vrom[3] = 4;
+    mmc3_last_vrom[4] = 5;
+    mmc3_last_vrom[5] = 6;
+    
+    mmc3_first_prom_page = 0x8000;
+    mmc3_second_prom_page = 0xA000; 
+    //mmc3_first_prom_page = 0; // Set it to 0x8000
+
+    
+    /* Register mapper write hook */
+    set_page_wr_hook(0x80, mmc3_MapperWrite80Hook);
+    set_page_writeable(0x80, true);
+    
+    set_page_wr_hook(0xA0, mmc3_MapperWriteA0Hook);
+    set_page_writeable(0xA0, true);
+    
+    set_page_wr_hook(0xC0, mmc3_MapperWriteC0Hook);
+    set_page_writeable(0xC0, true);
+    
+    set_page_wr_hook(0xE0, mmc3_MapperWriteE0Hook);
+    set_page_writeable(0xE0, true);
+
+    return 0;
+    
+} 
+
+void mmc3_MapperWrite80Hook(byte addr, byte Value)
+{
+    //printf("%s(0x%02X, 0x%02X)\n", __func__, addr, Value);
+    if (addr > 0x01)
+        return;
+
+    if (!addr)
+    {
+
+        if ((Cart->VROMSize > 0) && ( mmc3_use_xor != (Value & 0x80) ))
+        {
+    		if (Value & 0x80)
+    		{
+    			set_vrom_bank_1k(0x0000, mmc3_last_vrom[2]);
+    			set_vrom_bank_1k(0x0400, mmc3_last_vrom[3]);
+    			set_vrom_bank_1k(0x0800, mmc3_last_vrom[4]);
+    			set_vrom_bank_1k(0x0C00, mmc3_last_vrom[5]);
+    			set_vrom_bank_2k(0x1000, mmc3_last_vrom[0]>>1);
+    			set_vrom_bank_2k(0x1800, mmc3_last_vrom[1]>>1);
+    			//chr4,chr5,chr6,chr7,chr01,chr01+1,chr23,chr23+1
+    		}
+    		else
+    		{
+    		    set_vrom_bank_2k(0x0000, mmc3_last_vrom[0]>>1);
+    			set_vrom_bank_2k(0x0800, mmc3_last_vrom[1]>>1);
+    			set_vrom_bank_1k(0x1000, mmc3_last_vrom[2]);
+    			set_vrom_bank_1k(0x1400, mmc3_last_vrom[3]);
+    			set_vrom_bank_1k(0x1800, mmc3_last_vrom[4]);
+    			set_vrom_bank_1k(0x1C00, mmc3_last_vrom[5]);
+    			//chr01,chr01+1,chr23,chr23+1,chr4,chr5,chr6,chr7
+    		}
+   			mmc3_use_xor = (Value & 0x80);
+    	}
+    	
+		
+		if (mmc3_last_prom_switch != (Value & 0x40))
+		{
+    		if (!(Value & 0x40))
+    		{
+		        printf("MMC3: Switch -> 8/A\n");
+    		    mmc3_first_prom_page = 0x8000;
+    		    mmc3_second_prom_page = 0xA000;
+    		    
+    		    set_prom_bank_8k(mmc3_first_prom_page, mmc3_last_prom[0]);
+    		    set_prom_bank_8k(mmc3_second_prom_page, mmc3_last_prom[1]);
+    		    
+    		    set_prom_bank_8k(0xC000, GETLAST08KBANK(Cart)-1);
+    		    //set_prom_bank_8k(0xE000, GETLAST08KBANK(cart));
+    			//prg_bank(prg0,prg1,max_prg-1,max_prg);
+    		}
+    		else
+    		{
+    		    printf("MMC3: Switch -> C/A\n");
+    		    mmc3_first_prom_page = 0xC000;
+    		    mmc3_second_prom_page = 0xA000;
+    		
+    		    set_prom_bank_8k(mmc3_first_prom_page, mmc3_last_prom[0]);
+    		    set_prom_bank_8k(mmc3_second_prom_page, mmc3_last_prom[1]);
+    		    
+    		    set_prom_bank_8k(0x8000, GETLAST08KBANK(Cart)-1);
+    
+        	
+    			//prg_bank(max_prg-1,prg1,prg0,max_prg);
+    		}
+  			mmc3_last_prom_switch = (Value & 0x40);	
+    	}
+		mmc3_command = Value & 0x07;
+		
+		
+
+    } else { /* 8001 */
+		switch (mmc3_command)
+		{
+		case 0: 
+		case 1: 
+		case 2:
+		case 3: 
+		case 4: 
+		case 5:  
+		    if (Cart->VROMSize == 0)
+		        return;
+
+		    mmc3_last_vrom[mmc3_command] = Value;
+		    
+		    if (mmc3_use_xor)
+    		{
+    			set_vrom_bank_1k(0x0000, mmc3_last_vrom[2]);
+    			set_vrom_bank_1k(0x0400, mmc3_last_vrom[3]);
+    			set_vrom_bank_1k(0x0800, mmc3_last_vrom[4]);
+    			set_vrom_bank_1k(0x0C00, mmc3_last_vrom[5]);
+    			set_vrom_bank_2k(0x1000, mmc3_last_vrom[0]>>1);
+    			set_vrom_bank_2k(0x1800, mmc3_last_vrom[1]>>1);
+    			//chr4,chr5,chr6,chr7,chr01,chr01+1,chr23,chr23+1
+    		}
+    		else
+    		{
+    		    set_vrom_bank_2k(0x0000, mmc3_last_vrom[0]>>1);
+    			set_vrom_bank_2k(0x0800, mmc3_last_vrom[1]>>1);
+    			set_vrom_bank_1k(0x1000, mmc3_last_vrom[2]);
+    			set_vrom_bank_1k(0x1400, mmc3_last_vrom[3]);
+    			set_vrom_bank_1k(0x1800, mmc3_last_vrom[4]);
+    			set_vrom_bank_1k(0x1C00, mmc3_last_vrom[5]);
+    			//chr01,chr01+1,chr23,chr23+1,chr4,chr5,chr6,chr7
+    		}
+		    
+			break;
+			
+		case 6: 
+		    mmc3_last_prom[0] = Value;
+            set_prom_bank_8k(mmc3_first_prom_page, mmc3_last_prom[0]);
+			break;
+			
+		case 7:
+		    mmc3_last_prom[1] = Value;
+            set_prom_bank_8k(mmc3_second_prom_page, mmc3_last_prom[1]);
+			break;
+			
+		}
+		
+        /*if(mmc3_use_xor)
+    	    chr_bank(chr4,chr5,chr6,chr7,chr01,chr01+1,chr23,chr23+1);
+        else
+    		chr_bank(chr01,chr01+1,chr23,chr23+1,chr4,chr5,chr6,chr7);*/
+    }
+
+
+}
+
+void mmc3_MapperWriteA0Hook(byte addr, byte Value)
+{
+    //printf("%s(0x%02X, 0x%02X)\n", __func__, addr, Value);
+    if (addr > 0x01)
+        return;
+    
+    if (!addr)    
+    {    
+      //printf("MMC3: Select mirroring (0xA000) : 0x%X\n",Value);
+      
+      if (Value & 0x1)
+        ppu_setMirroring(PPU_MIRROR_HORIZTAL);
+      else
+        ppu_setMirroring(PPU_MIRROR_VERTICAL);
+
+    } 
+    else
+    {
+      //printf("MMC3: SaveRAM Toggle (0xA001) : 0x%X\n",Value);
+      if (Value)
+          map_sram();
+      else
+          unmap_sram();
+    }
+      
+}
+
+extern unsigned short ScanLine;
+
+void mmc3_MapperWriteC0Hook(byte addr, byte Value)
+{
+    //printf("%s(0x%02X, 0x%02X)\n", __func__, addr, Value);
+    if (addr > 0x01)
+        return;
+        
+    if (!addr)
+    {
+     mmc3_irq_counter_reload = Value;
+     mmc3_irq_counter = Value;
+     //printf("MMC3 IRQ[%d]: SetIRQ reload to %d\n", ScanLine, Value);
+     
+    }else{ /* C001 */
+      //printf("MMC3: New tmp IRQ value (0xC001) : 0x%X\n",Value);
+      //printf("MMC3 IRQ[%d]: Reset IRQ counter to val %d [Value = %d]\n", ScanLine, mmc3_irq_counter_reload, Value);
+      mmc3_irq_counter = Value;
+    }
+}
+
+void mmc3_MapperWriteE0Hook(byte addr, byte Value)
+{
+    //printf("%s(0x%02X, 0x%02X)\n", __func__, addr, Value);
+    if (addr > 0x01)
+        return;
+        
+    if (!addr)
+    {
+    //printf("MMC3: Writing to 0xE001 : 0x%X\n",Value);
+      //printf("MMC3 IRQ[%d]: IRQ disabled\n", ScanLine);
+      mmc3_irq_enable = 0;
+      //MapperWantIRQ = 1;
+      // Add a way to raise an IRQ
+      
+     }else{ /* E001 */
+      //printf("MMC3: Writing to 0xE001 : 0x%X\n",Value);
+      //printf("MMC3: IRQ Enabled (value : %d)\n",mmc3_irq_counter);
+      //printf("MMC3 IRQ[%d]: Enable IRQ\nr", ScanLine);
+      mmc3_irq_enable = 1;
+    }
+}
+
+int mmc3_MapperIRQ(int cycledone)
+{
+ if (((cycledone > 0) && (cycledone < 241)) /*&&
+      (ppu.ControlRegister2.b & (PPU_CR2_BGVISIBILITY | PPU_CR2_SPRTVISIBILITY)) == (PPU_CR2_BGVISIBILITY | PPU_CR2_SPRTVISIBILITY)*/)
+  {
+
+    if ((mmc3_irq_counter --) > 0 )return 0;   
+    
+    
+    /* Load next counter position */
+    mmc3_irq_counter = mmc3_irq_counter_reload;
+    
+    if (mmc3_irq_enable == 0) return 0;
+    
+    mmc3_irq_enable = 0;
+    
+    //printf("MMC3 IRQ[%d]: Tick next at %d\n", ScanLine, mmc3_irq_counter_reload);
+    
+    return 1;
+  }
+  return 0;
+}

+ 137 - 0
src/mappersmanager/mappers/temp/mmc4.c

@@ -0,0 +1,137 @@
+/*
+ *  MMC4 Mapper - The TI-NESulator Project
+ *  mmc4.h
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-05-31 18:00:41 +0200 (jeu, 31 mai 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/mmc4.h $
+ *  $Revision: 56 $
+ *
+ */
+
+byte mmc4_RegA;
+byte mmc4_RegB;
+byte mmc4_RegC;
+byte mmc4_RegD;
+byte mmc4_RegE;
+byte mmc4_RegF;
+
+#ifdef DEBUG
+#define LOG 
+//printf
+#else
+#define LOG 
+#endif
+
+
+void mmc4_MapperWriteRegA(register byte Addr, register byte Value)
+{
+    LOG("%s(%02X, %02X)\n", __func__, Addr, Value);
+    mmc4_RegA = Value;
+    
+    set_prom_bank_16k(0x8000, Value & 0x0F);
+    
+}
+
+void mmc4_MapperWriteRegB(register byte Addr, register byte Value)
+{
+    LOG("%s(%02X, %02X)\n", __func__, Addr, Value);
+    mmc4_RegB = Value;    
+    
+    set_vrom_bank_4k(0x0000, Value & 0x1F);
+}
+
+void mmc4_MapperWriteRegC(register byte Addr, register byte Value)
+{
+    LOG("%s(%02X, %02X)\n", __func__, Addr, Value);
+    mmc4_RegC = Value;    
+    set_vrom_bank_4k(0x0000, Value & 0x1F);
+}
+
+void mmc4_MapperWriteRegD(register byte Addr, register byte Value)
+{
+    LOG("%s(%02X, %02X)\n", __func__, Addr, Value);
+    mmc4_RegD = Value;    
+    set_vrom_bank_4k(0x1000, Value & 0x1F);
+}
+
+void mmc4_MapperWriteRegE(register byte Addr, register byte Value)
+{
+    LOG("%s(%02X, %02X)\n", __func__, Addr, Value);
+    mmc4_RegE = Value;  
+    set_vrom_bank_4k(0x1000, Value & 0x1F);  
+}
+
+void mmc4_MapperWriteRegF(register byte Addr, register byte Value)
+{
+    LOG("%s(%02X, %02X)\n", __func__, Addr, Value);
+    mmc4_RegF = Value;    
+    if (Value & 0x01)
+        ppu_setMirroring(PPU_MIRROR_HORIZTAL);
+    else
+        ppu_setMirroring(PPU_MIRROR_VERTICAL);
+}
+
+
+void mmc4_MapperDump(FILE *fp)
+{
+    
+}
+
+int mmc4_InitMapper(NesCart * cart) 
+{
+    int i;
+    
+    set_prom_bank_16k(0x8000,0);
+    set_prom_bank_16k(0xC000, GETLAST16KBANK(cart));
+
+    if (cart->VROMSize > 0)
        set_vrom_bank_8k(0x0000,0);
+       
+    /* Mapper should register itself for write hook */
+    for (i = 0xA0; i < 0xB0 ; i++)
+    {
+       set_page_wr_hook(i, mmc4_MapperWriteRegA);
+       set_page_writeable(i, true);       
+    } 
+    for (i = 0xB0; i < 0xC0 ; i++)
+    {
+       set_page_wr_hook(i, mmc4_MapperWriteRegB);
+       set_page_writeable(i, true);       
+    }
+    for (i = 0xC0; i < 0xD0 ; i++)
+    {
+       set_page_wr_hook(i, mmc4_MapperWriteRegC);
+       set_page_writeable(i, true);       
+    } 
+    for (i = 0xD0; i < 0xE0 ; i++)
+    {
+       set_page_wr_hook(i, mmc4_MapperWriteRegD);
+       set_page_writeable(i, true);       
+    } 
+    for (i = 0xE0; i < 0xF0 ; i++)
+    {
+       set_page_wr_hook(i, mmc4_MapperWriteRegE);
+       set_page_writeable(i, true);       
+    }
+    for (i = 0xF0; i < 0x100 ; i++)
+    {
+       set_page_wr_hook(i, mmc4_MapperWriteRegF);
+       set_page_writeable(i, true);       
+    }
+    
+    for (i = 0x60; i < 0x80 ; i++)
+    {
+       set_page_writeable(i, true);
+       set_page_readable(i, true);
+    }
+    
+    //ppu_setScreenMode(PPU_SCMODE_NORMAL);
+    //ppu_setMirroring(PPU_MIRROR_HORIZTAL);
+    
+    return 0;
+       
+}
+

+ 51 - 0
src/mappersmanager/mappers/temp/unrom.c

@@ -0,0 +1,51 @@
+/*
+ *  UNROM Mapper - The TI-NESulator Project
+ *  unrom.h
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-04-16 01:55:35 +0200 (lun, 16 avr 2007) $
+ *  $Author: godzil $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/unrom.h $
+ *  $Revision: 39 $
+ *
+ */
+ 
+unsigned char unrom_load_vbank;
+
+void unrom_MapperWriteHook(register byte Addr, register byte Value);
+
+int unrom_InitMapper(NesCart * cart) 
+{
+    int i;
+    
+    set_prom_bank_16k(0xC000, 0);
+    set_prom_bank_16k(0x8000, GETLAST16KBANK(cart)); /* Set the last one */
+
+    if (Cart->VROMSize > 0)
+        set_vrom_bank_8k(0x0000,0);
+
+    unrom_load_vbank = 0;
+    
+    /* Register the write hook */
+    for (i = 0x80; i < 0x100; i++)
+    {
+        set_page_wr_hook(i, unrom_MapperWriteHook);
+        set_page_writeable(i, true);
+    }
+    
+    return 0;
+} 
+
+
+void unrom_MapperWriteHook(register byte Addr, register byte Value) 
+{    
+    set_vrom_bank_8k(0x0000,Value);
+    unrom_load_vbank = Value;
+} 
+
+void unrom_MapperDump(FILE *fp)
+{
+    fprintf(fp,"unrom: vbank:%d\n",unrom_load_vbank);
+}

+ 20 - 0
src/mappersmanager/mappers_list.h

@@ -0,0 +1,20 @@
+/*
+ *  mappers_list.h
+ *  TI-NESulator.X
+ *
+ *  Created by Manoël Trapier on 25/10/07.
+ *  Copyright 2007 986 Corp. All rights reserved.
+ *
+ */
+
+/* This file could be generated from the mappers directory... */
+#include "mappers/norom.h"
+#include "mappers/mmc1.h"
+
+Mapper Mappers[] = {
+{ 0, "No Mapper", norom_InitMapper, norom_MapperIRQ, norom_MapperDump },
+{ 1, "MMC1", mmc1_InitMapper, norom_MapperIRQ, mmc1_MapperDump },   
+
+   /* EOL tag */
+{ 0, NULL, NULL, NULL, NULL }
+};

+ 63 - 0
src/mappersmanager/unused/aorom.h

@@ -0,0 +1,63 @@
+/*
+ *  AOROM Mapper - The TI-NESulator Project
+ *  aorom.h
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-04-26 18:47:34 +0200 (jeu, 26 avr 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/aorom.h $
+ *  $Revision: 46 $
+ *
+ */
+
+unsigned char aorom_load_bank;
+
+void aorom_MapperWriteHook(register byte Addr, register byte Value);
+
+extern byte *ppu_mem_nameTables;
+
+int aorom_InitMapper(NesCart * cart) 
+{    
+    int i;
+    
+    set_prom_bank_32k(0x8000,0);
+  
+    ppu_setScreenMode(PPU_SCMODE_SINGLE);
+    
+    aorom_load_bank = 0;
+
+    /* Register the write hook */
+    for (i = 0x80; i < 0x100; i++)
+    {
+        set_page_wr_hook(i, aorom_MapperWriteHook);
+        set_page_writeable(i, true);
+    }
+    
+
+    return 0;
+    
+} 
+
+void aorom_MapperWriteHook(register byte Addr, register byte Value) 
+{
+    int BankNb;
+
+        if (Value & (1 << 4))
+            ppu_setSingleScreen(PPU_SCREEN_000);
+        else
+            ppu_setSingleScreen(PPU_SCREEN_400);
+
+        BankNb = Value & 0x0F;
+
+        aorom_load_bank = BankNb;
+
+        //printf("aorom: Asking bank %d (giving %d & %d) - mirror is %d\n",BankNb,BankNb,(Value<<1)+1,Value&0x0F);
+        set_prom_bank_32k(0x8000,BankNb);
+} 
+
+void aorom_MapperDump(FILE *fp)
+{
+    fprintf(fp,"aorom: bank:%d\n",aorom_load_bank);
+}

+ 47 - 0
src/mappersmanager/unused/cnrom.h

@@ -0,0 +1,47 @@
+/*
+ *  CNROM Mapper - The TI-NESulator Project
+ *  cnrom.h
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-04-16 01:55:35 +0200 (lun, 16 avr 2007) $
+ *  $Author: godzil $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/cnrom.h $
+ *  $Revision: 39 $
+ *
+ */
+
+unsigned char cnrom_load_bank;
+
+void cnrom_MapperWriteHook(register byte Addr, register byte Value);
+
+int cnrom_InitMapper(NesCart * cart) 
+{
+    int i;
+    
+    set_prom_bank_16k(0x8000,  0);
+    set_prom_bank_16k(0xC000, GETLAST16KBANK(cart)); /* Set the last one */
+    cnrom_load_bank = 0;
+    
+    /* Register the write hook */
+    for (i = 0x80; i < 0x100; i++)
+    {
+        set_page_wr_hook(i, cnrom_MapperWriteHook);
+        set_page_writeable(i, true);
+    }
+    
+    return 0;
+} 
+
+
+void cnrom_MapperWriteHook(register byte Addr, register byte Value) 
+{
+    set_prom_bank_16k(0x8000,Value);
+    cnrom_load_bank = Value;
+}
+ 
+void cnrom_MapperDump(FILE *fp)
+{
+    fprintf(fp,"cnrom: bank:%d\n",cnrom_load_bank);
+}

+ 35 - 0
src/mappersmanager/unused/genericmapper.h

@@ -0,0 +1,35 @@
+/*
+ *  Generic mapper implementation - The TI-NESulator Project
+ *  genericmapper.h
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-04-16 01:55:35 +0200 (lun, 16 avr 2007) $
+ *  $Author: godzil $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/genericmapper.h $
+ *  $Revision: 39 $
+ *
+ */
+ 
+int _InitMapper(NesCart * cart) 
+{
+    
+    set_prom_bank_16k(0xC000,0);
+    set_prom_bank_16k(0x8000,-1);
+    
+    return 0;
+    
+} 
+
+int _MapperWriteHook(register word Addr, register byte Value) 
+{
+    
+    if (Addr > 0x7FFF)    /* Try to write to the rom */
+    {
+        set_vrom_bank_8k(0x0000,Value) 
+        
+        return 1;
+    } 
+    return 0;
+} 

+ 125 - 0
src/mappersmanager/unused/iremh3001.h

@@ -0,0 +1,125 @@
+/*
+ *  IREMH3001 Mapper - The TI-NESulator Project
+ *  iremh3001.h
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-04-16 01:55:35 +0200 (lun, 16 avr 2007) $
+ *  $Author: godzil $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/iremh3001.h $
+ *  $Revision: 39 $
+ *
+ */
+
+unsigned short iremh3001_prom_slot[3];
+
+unsigned short iremh3001_vrom_slot[8];
+
+int iremh3001_InitMapper(NesCart * cart) 
+{
+    
+    set_prom_bank_16k(0x8000, 0);
+    set_prom_bank_16k(0xC000, GETLAST16KBANK(cart));
+    
+    iremh3001_prom_slot[0] = 0;
+    iremh3001_prom_slot[1] = 1;
+    iremh3001_prom_slot[2] = GETLAST16KBANK(cart);
+
+    set_vrom_bank_8k(0x0000,4);
+
+    iremh3001_vrom_slot[0] = 0;
+    iremh3001_vrom_slot[1] = 0;
+    iremh3001_vrom_slot[2] = 0;
+    iremh3001_vrom_slot[3] = 0;
+    iremh3001_vrom_slot[4] = 0;
+    iremh3001_vrom_slot[5] = 0;
+    iremh3001_vrom_slot[6] = 0;
+    iremh3001_vrom_slot[7] = 0;
+
+    return 0;
+    
+} 
+
+int iremh3001_MapperWriteHook(register word Addr, register byte Value) 
+{
+
+  switch(Addr)
+  {
+  case 0x8000: /* Set 8k PROM @ 8000 */
+    printf("iremh3001: %X: change PROM to %d[%X]\n", Addr, Value, Value);
+    set_prom_bank_8k(0x8000, Value);
+    iremh3001_prom_slot[0] = Value;
+    break;
+
+  case 0x9003: /* Mirroring ??? */
+    printf("iremh3001: Mirroring[0x%X:%d] ?\n", Value, Value);
+    break;
+
+  case 0x9005: /* IRQ ??? */
+    printf("iremh3001: IRQ[0x%X:%d] ?\n", Value, Value);
+    break;
+
+  case 0x9006: /* IRQ ??? */
+    printf("iremh3001: IRQ[0x%X:%d] ?\n", Value, Value);
+    break;
+
+  case 0xA000: /* Set 8k PROM @ A000 */
+    printf("iremh3001: %X: change PROM to %d[%X]\n", Addr, Value, Value);
+    set_prom_bank_8k(0xA000, Value);
+    iremh3001_prom_slot[1] = Value;
+    break;
+
+  case 0xB000: /* Set 1k VROM @ 0000 */
+  case 0xB001: /* Set 1k VROM @ 0400 */
+  case 0xB002: /* Set 1k VROM @ 0800 */
+  case 0xB003: /* Set 1k VROM @ 0C00 */
+  case 0xB004: /* Set 1k VROM @ 1000 */
+  case 0xB005: /* Set 1k VROM @ 1400 */
+  case 0xB006: /* Set 1k VROM @ 1800 */
+  case 0xB007: /* Set 1k VROM @ 1C00 */
+    printf("iremh3001: %X: change VROM to %d[%X]\n", (Addr&0x0F)<<10, Value, Value);
+    set_vrom_bank_1k((Addr&0xF)<<10, Value);
+    iremh3001_vrom_slot[Addr&0x0F] = Value;
+    break;
+
+  case 0xC000: /* Set 8k PROM @ C000 */
+    printf("iremh3001: %X: change PROM to %d[%X]\n", Addr, Value, Value);
+    set_prom_bank_8k(0xC000, Value);
+    iremh3001_prom_slot[2] = Value;
+    break;
+
+  default:
+    printf("@:%X -- V:%X", Addr, Value);
+    return 0;
+
+  }
+
+  return 1;
+} 
+
+void iremh3001_MapperDump(FILE *fp)
+{
+    fprintf(fp,"iremh3001: prom: $8000:%d $A000:%d $C000:%d\n",
+        iremh3001_prom_slot[0], 
+        iremh3001_prom_slot[1],
+        iremh3001_prom_slot[2]);
+
+    fprintf(fp,"iremh3001: vrom: $0000:%d $0400:%d $0800:%d $0C00:%d\n" \
+           "                 $1000:%d $1400:%d $1800:%d $1C00:%d\n",
+        iremh3001_vrom_slot[0], 
+        iremh3001_vrom_slot[1],
+        iremh3001_vrom_slot[2], 
+        iremh3001_vrom_slot[3],
+        iremh3001_vrom_slot[4], 
+        iremh3001_vrom_slot[5],
+        iremh3001_vrom_slot[6],
+        iremh3001_prom_slot[7]);
+}
+
+
+int iremh3001_MapperIRQ(int cycledone)
+{
+
+  return 0;
+}

+ 355 - 0
src/mappersmanager/unused/mmc1.h

@@ -0,0 +1,355 @@
+/*
+ *  MMC1 Mapper - The TI-NESulator Project
+ *  mmc1.h
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-05-02 18:37:41 +0200 (mer, 02 mai 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/mmc1.h $
+ *  $Revision: 50 $
+ *
+ */
+unsigned char MMC1_reg0;
+
+unsigned char MMC1_reg1;
+
+unsigned char MMC1_reg2;
+
+unsigned char MMC1_reg3;
+
+unsigned char mmc1_CurrentBank;
+
+#define MMC1_R0_MIRROR    0x01
+#define MMC1_R0_ONESCREEN  0x02
+#define MMC1_R0_PRGAREA   0x04
+#define MMC1_R0_PRGSIZE   0x08
+#define MMC1_R0_VROMSW    0x10
+#define MMC1_R0_RESET     0x80
+
+#define MMC1_R1_VROMB1    0x0F
+#define MMC1_R1_256KSEL   0x10
+#define MMC1_R1_RESET     0x80
+
+#define MMC1_R2_VROMB2    0x0F
+#define MMC1_R2_256KSEL   0x10
+#define MMC1_R2_RESET     0x80
+
+#define MMC1_R3_VROMB2    0x0F
+#define MMC1_R3_SAVECE    0x10
+#define MMC1_R3_RESET     0x80
+
+
+#define MMC1_REG0_DEFAULT MMC1_R0_PRGSIZE | MMC1_R0_PRGAREA
+#define MMC1_REG1_DEFAULT 0
+#define MMC1_REG2_DEFAULT 0
+#define MMC1_REG3_DEFAULT 0
+
+
+void mmc1_MapperWriteReg0(register byte Addr, register byte Value);
+void mmc1_MapperWriteReg1(register byte Addr, register byte Value);
+void mmc1_MapperWriteReg2(register byte Addr, register byte Value);
+void mmc1_MapperWriteReg3(register byte Addr, register byte Value);
+
+void mmc1_MapperDump(FILE *fp)
+{
+    fprintf(fp,"MMC1: r0:0x%02X r1:0x%02X r2:0x%02X r3:0x%02X\n",MMC1_reg0,MMC1_reg1,MMC1_reg2,MMC1_reg3);
+}
+
+int mmc1_InitMapper(NesCart * cart) 
+{
+    int i;
+    
+       MMC1_reg0 = MMC1_REG0_DEFAULT;
+       
+       MMC1_reg1 = MMC1_REG1_DEFAULT;
+       
+       MMC1_reg2 = MMC1_REG2_DEFAULT;
+       
+       MMC1_reg3 = MMC1_REG3_DEFAULT;
+        
+       set_prom_bank_16k(0x8000,0);
+       set_prom_bank_16k(0xC000, GETLAST16KBANK(cart));
+       
+       mmc1_CurrentBank = 0;
+       
+       if (cart->VROMSize > 0)
+           set_vrom_bank_4k(0x0000,0);
+
+       
+       /* Mapper should register itself for write hook */
+    for (i = 0x80; i < 0xA0 ; i++)
+    {
+       set_page_wr_hook(i, mmc1_MapperWriteReg0);
+       set_page_writeable(i, true);       
+    } 
+    for (i = 0xA0; i < 0xC0 ; i++)
+    {
+       set_page_wr_hook(i, mmc1_MapperWriteReg1);
+       set_page_writeable(i, true);       
+    } 
+    for (i = 0xC0; i < 0xE0 ; i++)
+    {
+       set_page_wr_hook(i, mmc1_MapperWriteReg2);
+       set_page_writeable(i, true);       
+    } 
+    for (i = 0xE0; i < 0x100 ; i++)
+    {
+       set_page_wr_hook(i, mmc1_MapperWriteReg3);
+       set_page_writeable(i, true);       
+    }
+    
+    return 0;
+       
+} 
+
+
+/*
+Reg 0
+8 : 1000
+9 : 1001
+
+        Reg 1
+        A : 1010
+        B : 1011
+        
+          Reg 2
+          C : 1100
+          D : 1101
+          
+            Reg 3
+            E : 1110
+            F : 1111
+            
+              ((Addr & 0x6000) >> 13) <- port number
+*/ 
+#define MMC1_GetReg(a) ((a & 0x6000) >> 13)
+/* (Val & 0x01) recuperation du bit */ 
+#define MMC1_GetBit(v) (v & 0x01)
+/* ( ( b & (1 << Bit)) | (v << Bit) ) Ajout du bit */ 
+#define MMC1_AddBit(b,v) ( ( b & ~(1 << Bit)) | (v << Bit) )
+
+void mmc1_ApplyReg0Mod() 
+{
+    
+    static unsigned char OldSwitchArea = MMC1_R0_PRGAREA;
+    
+    
+       
+    //printf("Change to reg0 done ! (0x%x)\n\tMiror flag : %d\n\tOneScreen Flag : %d\n\tPRG Size : %d\n\tPRG Area : %d\n\tVROM Switch size : %d\n", MMC1_reg0, MMC1_reg0 & MMC1_R0_MIRROR, MMC1_reg0 & MMC1_R0_ONESCREEN, MMC1_reg0 & MMC1_R0_PRGAREA, MMC1_reg0 & MMC1_R0_PRGSIZE, MMC1_reg0 & MMC1_R0_VROMSW);
+        
+    switch (MMC1_reg0 & 0x03)
+    {
+    case 0:
+        ppu_setScreenMode(PPU_SCMODE_SINGLE);
+        ppu_setSingleScreen(PPU_SCREEN_000);
+        break;
+    case 1:
+        ppu_setScreenMode(PPU_SCMODE_SINGLE);
+        ppu_setSingleScreen(PPU_SCREEN_400);
+        break;
+    case 2:
+        ppu_setScreenMode(PPU_SCMODE_NORMAL);
+        ppu_setMirroring(PPU_MIRROR_VERTICAL);
+        break;
+    case 3:
+        ppu_setScreenMode(PPU_SCMODE_NORMAL);
+        ppu_setMirroring(PPU_MIRROR_HORIZTAL);
+        break;
+    }
+       
+    if ( (OldSwitchArea != (MMC1_reg0 & MMC1_R0_PRGAREA)) && ((MMC1_reg0 & MMC1_R0_PRGSIZE) != 0 ) )
+    {
+           
+        if ((MMC1_reg0 & MMC1_R0_PRGAREA) != 0)
+        {        /* 0x8000 area */
+            set_prom_bank_16k(0x8000,mmc1_CurrentBank);
+            set_prom_bank_16k(0xC000, GETLAST16KBANK(Cart));
+        } 
+        else
+        {        /* 0xC000 area */
+    
+            set_prom_bank_16k(0x8000,0);
+            set_prom_bank_16k(0xC000,mmc1_CurrentBank);
+
+        } 
+           
+        OldSwitchArea = (MMC1_reg0 & MMC1_R0_PRGAREA);
+    }
+       
+} 
+
+int VROMBankNb;
+unsigned char Bit = 0;
+unsigned char BitBuf = 0;
+
+void mmc1_MapperWriteReg0(register byte Addr, register byte Value) 
+{
+    if (Value & 0x80) 
+    {   
+        MMC1_reg0 = MMC1_REG0_DEFAULT;
+        printf("MMC1: Reg0 Reset occured !\n");
+        mmc1_ApplyReg0Mod();
+    }
+    else
+    {
+        if (Bit < 4)
+        {        /* Pas encore ecrit les 5 bits  */
+            BitBuf = MMC1_AddBit(BitBuf, MMC1_GetBit(Value));
+            Bit++;
+        } 
+        else
+        {
+            BitBuf = MMC1_AddBit(BitBuf, MMC1_GetBit(Value));
+            Bit = 0;
+            
+            MMC1_reg0 = BitBuf;
+            
+            mmc1_ApplyReg0Mod();
+        }
+    }               
+}
+
+void mmc1_MapperWriteReg1(register byte Addr, register byte Value) 
+{
+    if (Value & 0x80) 
+    {   
+        MMC1_reg1 = MMC1_REG1_DEFAULT;
+        printf("MMC1: Reg1 Reset occured !\n");
+    }
+    else
+    {
+        if (Bit < 4)
+        {        /* Pas encore ecrit les 5 bits  */
+            BitBuf = MMC1_AddBit(BitBuf, MMC1_GetBit(Value));
+            Bit++;
+        } 
+        else
+        {
+            BitBuf = MMC1_AddBit(BitBuf, MMC1_GetBit(Value));
+            Bit = 0;
+            
+            MMC1_reg1 = BitBuf;
+                        
+            VROMBankNb = (MMC1_reg1 /* & MMC1_R1_VROMB1 */ );
+
+            if (Cart->VROMSize == 0)
+            {
+                printf("Try to change VROM but with didn't have any VROM ! [%04X]\n", VROMBankNb);
+                return;
+            }
+                
+            if ( (MMC1_reg0 & MMC1_R0_VROMSW) != 0 )
+            {    /* 4K vram */
+                //printf("Switching VROM at 0x0000 to 4k bank %d\n", VROMBankNb);
+                set_vrom_bank_4k(0x0000,VROMBankNb);
+            }
+            else
+            {    /* 8K vram */
+                //printf("Switching VROM at 0x0000 to 8k bank %d\n", VROMBankNb>>1);
+                set_vrom_bank_8k(0x0000,VROMBankNb>>1);
+            }
+        }
+    }
+}
+
+void mmc1_MapperWriteReg2(register byte Addr, register byte Value) 
+{
+    if (Value & 0x80) 
+    {   
+        MMC1_reg2 = MMC1_REG2_DEFAULT;
+        printf("MMC1: Reg2 Reset occured !\n");
+    }
+    else
+    {
+        if (Bit < 4)
+        {        /* Pas encore ecrit les 5 bits  */
+            BitBuf = MMC1_AddBit(BitBuf, MMC1_GetBit(Value));
+            Bit++;
+        } 
+        else
+        {
+            BitBuf = MMC1_AddBit(BitBuf, MMC1_GetBit(Value));
+            Bit = 0;
+
+            MMC1_reg2 = BitBuf;
+            
+            VROMBankNb = (MMC1_reg2 /* & MMC1_R2_VROMB2 */ );
+            
+            //printf("Want to switch VROM at 0x1000 to 4k bank %d\n", VROMBankNb);
+            if (Cart->VROMSize == 0)
+            {
+                //printf(": No\n");
+                return;
+            }
+            
+            if ( (MMC1_reg0 & MMC1_R0_VROMSW) != 0 )
+            {    /* 4K vram */
+                //printf("Switching VROM at 0x1000 to 4k bank %d\n", VROMBankNb);
+                set_vrom_bank_4k(0x1000,VROMBankNb);
+            }
+            else
+            {    /* 8K vram */
+            //       printf("Switching VROM at 0x1000 to 8k bank %d\n", VROMBankNb>>1);
+            //       set_vrom_bank_8k(0x1000,VROMBankNb>>1);
+            }
+        }
+    }
+}
+
+void mmc1_MapperWriteReg3(register byte Addr, register byte Value) 
+{
+    if (Value & 0x80) 
+    {   
+        MMC1_reg3 = MMC1_REG3_DEFAULT;
+        printf("MMC1: Reg3 Reset occured !\n");
+    }
+    else
+    {
+        if (Bit < 4)
+        {        /* Pas encore ecrit les 5 bits  */
+            BitBuf = MMC1_AddBit(BitBuf, MMC1_GetBit(Value));
+            Bit++;
+        } 
+        else
+        {
+            BitBuf = MMC1_AddBit(BitBuf, MMC1_GetBit(Value));
+            Bit = 0;
+        
+            MMC1_reg3 = BitBuf;
+            
+            if (MMC1_reg3<<14 > Cart->PROMSize)
+                return;
+                
+            if ( (MMC1_reg0 & MMC1_R0_PRGSIZE) != 0 )
+            {    /* 16K Switch */
+                if ( (MMC1_reg0 & MMC1_R0_PRGAREA) != 0 )
+                {    /* 0x8000 switch */
+                    set_prom_bank_16k(0x8000,MMC1_reg3);
+                    //printf("LowBank is now %d ( 0x%p )\n", MMC1_reg3, mLBank);
+                }
+                else
+                {    /* 0xC000 switch */
+                    set_prom_bank_16k(0xC000,MMC1_reg3);
+                    //printf("HighBank is now %d ( 0x%p )\n", MMC1_reg3, mUBank);
+                }
+            }
+            else
+            {    /* 32K Switch */
+                set_prom_bank_32k(0x8000,MMC1_reg3>>1);
+            }
+            
+            if ( ( MMC1_reg3 & MMC1_R3_SAVECE ) != 0)   
+            {
+                unmap_sram();
+            }
+            else
+            {
+                map_sram();
+            }
+        }
+    }
+}    
+        
+        //printf("MMC1: Debug (Reg:%d,Val:0x%02X,reg0:0x%02X,reg1:0x%02X,reg2:0x%02X,reg3:0x%02X)\n", MMC1_GetReg(Addr), Value, MMC1_reg0, MMC1_reg1, MMC1_reg2, MMC1_reg3);

+ 317 - 0
src/mappersmanager/unused/mmc3.h

@@ -0,0 +1,317 @@
+/*
+ *  MMC3 Mapper - The TI-NESulator Project
+ *  mmc3.h
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-05-02 18:37:41 +0200 (mer, 02 mai 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/mmc3.h $
+ *  $Revision: 50 $
+ *
+ */
+
+unsigned short mmc3_command;
+
+unsigned char mmc3_irq_counter;
+unsigned char mmc3_irq_counter_reload;
+unsigned char mmc3_irq_enable;
+
+unsigned short mmc3_first_prom_page;
+unsigned short mmc3_second_prom_page;
+
+unsigned char  mmc3_use_xor;
+unsigned char mmc3_last_vrom[6];
+
+unsigned char mmc3_last_prom[2];
+unsigned char mmc3_last_prom_switch;
+
+unsigned short dummy;
+
+void mmc3_MapperWrite80Hook(byte addr, byte value);
+void mmc3_MapperWriteA0Hook(byte addr, byte value);
+void mmc3_MapperWriteC0Hook(byte addr, byte value);
+void mmc3_MapperWriteE0Hook(byte addr, byte value);
+
+void mmc3_MapperDump(FILE *fp)
+{
+    fprintf(fp,"MMC3: CMD:%d IC:%d IR:%d IE:%d FPP:0x%04X SPP:0x%04X UX:%d\n",mmc3_command,mmc3_irq_counter,mmc3_irq_counter_reload,mmc3_irq_enable,mmc3_first_prom_page,mmc3_second_prom_page,mmc3_use_xor);
+    fprintf(fp,"MMC3: LV0:%d LV1:%d LV2:%d LV3:%d LV4:%d LV5:%d\n",mmc3_last_vrom[0],mmc3_last_vrom[1],mmc3_last_vrom[2],mmc3_last_vrom[3],mmc3_last_vrom[4],mmc3_last_vrom[5]);
+    fprintf(fp,"MMC3: LP0:%d LP1:%d LPS:%d\n",mmc3_last_prom[0],mmc3_last_prom[1],mmc3_last_prom_switch);
+}
+
+int mmc3_InitMapper(NesCart * cart) 
+{
+    int i;
+
+    set_prom_bank_16k(0x8000, 0);
+    set_prom_bank_16k(0xC000, GETLAST16KBANK(cart));
+
+    if ( Cart->VROMSize > 0)
+    {
+         set_vrom_bank_8k(0, 0x0000);
+    }
+    
+    mmc3_command = -1;
+
+    mmc3_irq_counter = -1;
+    mmc3_irq_enable = 1;
+    mmc3_irq_counter_reload = 0;
+    
+    mmc3_use_xor = 0x42;
+    
+    mmc3_last_prom_switch = 0x42;
+    
+    mmc3_last_prom[0] = 0;
+    mmc3_last_prom[1] = 1;
+    
+    mmc3_last_vrom[0] = 0;
+    mmc3_last_vrom[1] = 2;
+    mmc3_last_vrom[2] = 3;
+    mmc3_last_vrom[3] = 4;
+    mmc3_last_vrom[4] = 5;
+    mmc3_last_vrom[5] = 6;
+    
+    mmc3_first_prom_page = 0x8000;
+    mmc3_second_prom_page = 0xA000; 
+    //mmc3_first_prom_page = 0; // Set it to 0x8000
+
+    
+    /* Register mapper write hook */
+    set_page_wr_hook(0x80, mmc3_MapperWrite80Hook);
+    set_page_writeable(0x80, true);
+    
+    set_page_wr_hook(0xA0, mmc3_MapperWriteA0Hook);
+    set_page_writeable(0xA0, true);
+    
+    set_page_wr_hook(0xC0, mmc3_MapperWriteC0Hook);
+    set_page_writeable(0xC0, true);
+    
+    set_page_wr_hook(0xE0, mmc3_MapperWriteE0Hook);
+    set_page_writeable(0xE0, true);
+
+    return 0;
+    
+} 
+
+void mmc3_MapperWrite80Hook(byte addr, byte Value)
+{
+    //printf("%s(0x%02X, 0x%02X)\n", __func__, addr, Value);
+    if (addr > 0x01)
+        return;
+
+    if (!addr)
+    {
+
+        if ((Cart->VROMSize > 0) && ( mmc3_use_xor != (Value & 0x80) ))
+        {
+    		if (Value & 0x80)
+    		{
+    			set_vrom_bank_1k(0x0000, mmc3_last_vrom[2]);
+    			set_vrom_bank_1k(0x0400, mmc3_last_vrom[3]);
+    			set_vrom_bank_1k(0x0800, mmc3_last_vrom[4]);
+    			set_vrom_bank_1k(0x0C00, mmc3_last_vrom[5]);
+    			set_vrom_bank_2k(0x1000, mmc3_last_vrom[0]>>1);
+    			set_vrom_bank_2k(0x1800, mmc3_last_vrom[1]>>1);
+    			//chr4,chr5,chr6,chr7,chr01,chr01+1,chr23,chr23+1
+    		}
+    		else
+    		{
+    		    set_vrom_bank_2k(0x0000, mmc3_last_vrom[0]>>1);
+    			set_vrom_bank_2k(0x0800, mmc3_last_vrom[1]>>1);
+    			set_vrom_bank_1k(0x1000, mmc3_last_vrom[2]);
+    			set_vrom_bank_1k(0x1400, mmc3_last_vrom[3]);
+    			set_vrom_bank_1k(0x1800, mmc3_last_vrom[4]);
+    			set_vrom_bank_1k(0x1C00, mmc3_last_vrom[5]);
+    			//chr01,chr01+1,chr23,chr23+1,chr4,chr5,chr6,chr7
+    		}
+   			mmc3_use_xor = (Value & 0x80);
+    	}
+    	
+		
+		if (mmc3_last_prom_switch != (Value & 0x40))
+		{
+    		if (!(Value & 0x40))
+    		{
+		        printf("MMC3: Switch -> 8/A\n");
+    		    mmc3_first_prom_page = 0x8000;
+    		    mmc3_second_prom_page = 0xA000;
+    		    
+    		    set_prom_bank_8k(mmc3_first_prom_page, mmc3_last_prom[0]);
+    		    set_prom_bank_8k(mmc3_second_prom_page, mmc3_last_prom[1]);
+    		    
+    		    set_prom_bank_8k(0xC000, GETLAST08KBANK(Cart)-1);
+    		    //set_prom_bank_8k(0xE000, GETLAST08KBANK(cart));
+    			//prg_bank(prg0,prg1,max_prg-1,max_prg);
+    		}
+    		else
+    		{
+    		    printf("MMC3: Switch -> C/A\n");
+    		    mmc3_first_prom_page = 0xC000;
+    		    mmc3_second_prom_page = 0xA000;
+    		
+    		    set_prom_bank_8k(mmc3_first_prom_page, mmc3_last_prom[0]);
+    		    set_prom_bank_8k(mmc3_second_prom_page, mmc3_last_prom[1]);
+    		    
+    		    set_prom_bank_8k(0x8000, GETLAST08KBANK(Cart)-1);
+    
+        	
+    			//prg_bank(max_prg-1,prg1,prg0,max_prg);
+    		}
+  			mmc3_last_prom_switch = (Value & 0x40);	
+    	}
+		mmc3_command = Value & 0x07;
+		
+		
+
+    } else { /* 8001 */
+		switch (mmc3_command)
+		{
+		case 0: 
+		case 1: 
+		case 2:
+		case 3: 
+		case 4: 
+		case 5:  
+		    if (Cart->VROMSize == 0)
+		        return;
+
+		    mmc3_last_vrom[mmc3_command] = Value;
+		    
+		    if (mmc3_use_xor)
+    		{
+    			set_vrom_bank_1k(0x0000, mmc3_last_vrom[2]);
+    			set_vrom_bank_1k(0x0400, mmc3_last_vrom[3]);
+    			set_vrom_bank_1k(0x0800, mmc3_last_vrom[4]);
+    			set_vrom_bank_1k(0x0C00, mmc3_last_vrom[5]);
+    			set_vrom_bank_2k(0x1000, mmc3_last_vrom[0]>>1);
+    			set_vrom_bank_2k(0x1800, mmc3_last_vrom[1]>>1);
+    			//chr4,chr5,chr6,chr7,chr01,chr01+1,chr23,chr23+1
+    		}
+    		else
+    		{
+    		    set_vrom_bank_2k(0x0000, mmc3_last_vrom[0]>>1);
+    			set_vrom_bank_2k(0x0800, mmc3_last_vrom[1]>>1);
+    			set_vrom_bank_1k(0x1000, mmc3_last_vrom[2]);
+    			set_vrom_bank_1k(0x1400, mmc3_last_vrom[3]);
+    			set_vrom_bank_1k(0x1800, mmc3_last_vrom[4]);
+    			set_vrom_bank_1k(0x1C00, mmc3_last_vrom[5]);
+    			//chr01,chr01+1,chr23,chr23+1,chr4,chr5,chr6,chr7
+    		}
+		    
+			break;
+			
+		case 6: 
+		    mmc3_last_prom[0] = Value;
+            set_prom_bank_8k(mmc3_first_prom_page, mmc3_last_prom[0]);
+			break;
+			
+		case 7:
+		    mmc3_last_prom[1] = Value;
+            set_prom_bank_8k(mmc3_second_prom_page, mmc3_last_prom[1]);
+			break;
+			
+		}
+		
+        /*if(mmc3_use_xor)
+    	    chr_bank(chr4,chr5,chr6,chr7,chr01,chr01+1,chr23,chr23+1);
+        else
+    		chr_bank(chr01,chr01+1,chr23,chr23+1,chr4,chr5,chr6,chr7);*/
+    }
+
+
+}
+
+void mmc3_MapperWriteA0Hook(byte addr, byte Value)
+{
+    //printf("%s(0x%02X, 0x%02X)\n", __func__, addr, Value);
+    if (addr > 0x01)
+        return;
+    
+    if (!addr)    
+    {    
+      //printf("MMC3: Select mirroring (0xA000) : 0x%X\n",Value);
+      
+      if (Value & 0x1)
+        ppu_setMirroring(PPU_MIRROR_HORIZTAL);
+      else
+        ppu_setMirroring(PPU_MIRROR_VERTICAL);
+
+    } 
+    else
+    {
+      //printf("MMC3: SaveRAM Toggle (0xA001) : 0x%X\n",Value);
+      if (Value)
+          map_sram();
+      else
+          unmap_sram();
+    }
+      
+}
+
+extern unsigned short ScanLine;
+
+void mmc3_MapperWriteC0Hook(byte addr, byte Value)
+{
+    //printf("%s(0x%02X, 0x%02X)\n", __func__, addr, Value);
+    if (addr > 0x01)
+        return;
+        
+    if (!addr)
+    {
+     mmc3_irq_counter_reload = Value;
+     mmc3_irq_counter = Value;
+     //printf("MMC3 IRQ[%d]: SetIRQ reload to %d\n", ScanLine, Value);
+     
+    }else{ /* C001 */
+      //printf("MMC3: New tmp IRQ value (0xC001) : 0x%X\n",Value);
+      //printf("MMC3 IRQ[%d]: Reset IRQ counter to val %d [Value = %d]\n", ScanLine, mmc3_irq_counter_reload, Value);
+      mmc3_irq_counter = Value;
+    }
+}
+
+void mmc3_MapperWriteE0Hook(byte addr, byte Value)
+{
+    //printf("%s(0x%02X, 0x%02X)\n", __func__, addr, Value);
+    if (addr > 0x01)
+        return;
+        
+    if (!addr)
+    {
+    //printf("MMC3: Writing to 0xE001 : 0x%X\n",Value);
+      //printf("MMC3 IRQ[%d]: IRQ disabled\n", ScanLine);
+      mmc3_irq_enable = 0;
+      //MapperWantIRQ = 1;
+      // Add a way to raise an IRQ
+      
+     }else{ /* E001 */
+      //printf("MMC3: Writing to 0xE001 : 0x%X\n",Value);
+      //printf("MMC3: IRQ Enabled (value : %d)\n",mmc3_irq_counter);
+      //printf("MMC3 IRQ[%d]: Enable IRQ\nr", ScanLine);
+      mmc3_irq_enable = 1;
+    }
+}
+
+int mmc3_MapperIRQ(int cycledone)
+{
+ if (((cycledone > 0) && (cycledone < 241)) /*&&
+      (ppu.ControlRegister2.b & (PPU_CR2_BGVISIBILITY | PPU_CR2_SPRTVISIBILITY)) == (PPU_CR2_BGVISIBILITY | PPU_CR2_SPRTVISIBILITY)*/)
+  {
+
+    if ((mmc3_irq_counter --) > 0 )return 0;   
+    
+    
+    /* Load next counter position */
+    mmc3_irq_counter = mmc3_irq_counter_reload;
+    
+    if (mmc3_irq_enable == 0) return 0;
+    
+    mmc3_irq_enable = 0;
+    
+    //printf("MMC3 IRQ[%d]: Tick next at %d\n", ScanLine, mmc3_irq_counter_reload);
+    
+    return 1;
+  }
+  return 0;
+}

+ 137 - 0
src/mappersmanager/unused/mmc4.h

@@ -0,0 +1,137 @@
+/*
+ *  MMC4 Mapper - The TI-NESulator Project
+ *  mmc4.h
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-05-31 18:00:41 +0200 (jeu, 31 mai 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/mmc4.h $
+ *  $Revision: 56 $
+ *
+ */
+
+byte mmc4_RegA;
+byte mmc4_RegB;
+byte mmc4_RegC;
+byte mmc4_RegD;
+byte mmc4_RegE;
+byte mmc4_RegF;
+
+#ifdef DEBUG
+#define LOG 
+//printf
+#else
+#define LOG 
+#endif
+
+
+void mmc4_MapperWriteRegA(register byte Addr, register byte Value)
+{
+    LOG("%s(%02X, %02X)\n", __func__, Addr, Value);
+    mmc4_RegA = Value;
+    
+    set_prom_bank_16k(0x8000, Value & 0x0F);
+    
+}
+
+void mmc4_MapperWriteRegB(register byte Addr, register byte Value)
+{
+    LOG("%s(%02X, %02X)\n", __func__, Addr, Value);
+    mmc4_RegB = Value;    
+    
+    set_vrom_bank_4k(0x0000, Value & 0x1F);
+}
+
+void mmc4_MapperWriteRegC(register byte Addr, register byte Value)
+{
+    LOG("%s(%02X, %02X)\n", __func__, Addr, Value);
+    mmc4_RegC = Value;    
+    set_vrom_bank_4k(0x0000, Value & 0x1F);
+}
+
+void mmc4_MapperWriteRegD(register byte Addr, register byte Value)
+{
+    LOG("%s(%02X, %02X)\n", __func__, Addr, Value);
+    mmc4_RegD = Value;    
+    set_vrom_bank_4k(0x1000, Value & 0x1F);
+}
+
+void mmc4_MapperWriteRegE(register byte Addr, register byte Value)
+{
+    LOG("%s(%02X, %02X)\n", __func__, Addr, Value);
+    mmc4_RegE = Value;  
+    set_vrom_bank_4k(0x1000, Value & 0x1F);  
+}
+
+void mmc4_MapperWriteRegF(register byte Addr, register byte Value)
+{
+    LOG("%s(%02X, %02X)\n", __func__, Addr, Value);
+    mmc4_RegF = Value;    
+    if (Value & 0x01)
+        ppu_setMirroring(PPU_MIRROR_HORIZTAL);
+    else
+        ppu_setMirroring(PPU_MIRROR_VERTICAL);
+}
+
+
+void mmc4_MapperDump(FILE *fp)
+{
+    
+}
+
+int mmc4_InitMapper(NesCart * cart) 
+{
+    int i;
+    
+    set_prom_bank_16k(0x8000,0);
+    set_prom_bank_16k(0xC000, GETLAST16KBANK(cart));
+
+    if (cart->VROMSize > 0)
        set_vrom_bank_8k(0x0000,0);
+       
+    /* Mapper should register itself for write hook */
+    for (i = 0xA0; i < 0xB0 ; i++)
+    {
+       set_page_wr_hook(i, mmc4_MapperWriteRegA);
+       set_page_writeable(i, true);       
+    } 
+    for (i = 0xB0; i < 0xC0 ; i++)
+    {
+       set_page_wr_hook(i, mmc4_MapperWriteRegB);
+       set_page_writeable(i, true);       
+    }
+    for (i = 0xC0; i < 0xD0 ; i++)
+    {
+       set_page_wr_hook(i, mmc4_MapperWriteRegC);
+       set_page_writeable(i, true);       
+    } 
+    for (i = 0xD0; i < 0xE0 ; i++)
+    {
+       set_page_wr_hook(i, mmc4_MapperWriteRegD);
+       set_page_writeable(i, true);       
+    } 
+    for (i = 0xE0; i < 0xF0 ; i++)
+    {
+       set_page_wr_hook(i, mmc4_MapperWriteRegE);
+       set_page_writeable(i, true);       
+    }
+    for (i = 0xF0; i < 0x100 ; i++)
+    {
+       set_page_wr_hook(i, mmc4_MapperWriteRegF);
+       set_page_writeable(i, true);       
+    }
+    
+    for (i = 0x60; i < 0x80 ; i++)
+    {
+       set_page_writeable(i, true);
+       set_page_readable(i, true);
+    }
+    
+    //ppu_setScreenMode(PPU_SCMODE_NORMAL);
+    //ppu_setMirroring(PPU_MIRROR_HORIZTAL);
+    
+    return 0;
+       
+}
+

+ 51 - 0
src/mappersmanager/unused/unrom.h

@@ -0,0 +1,51 @@
+/*
+ *  UNROM Mapper - The TI-NESulator Project
+ *  unrom.h
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-04-16 01:55:35 +0200 (lun, 16 avr 2007) $
+ *  $Author: godzil $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/unrom.h $
+ *  $Revision: 39 $
+ *
+ */
+ 
+unsigned char unrom_load_vbank;
+
+void unrom_MapperWriteHook(register byte Addr, register byte Value);
+
+int unrom_InitMapper(NesCart * cart) 
+{
+    int i;
+    
+    set_prom_bank_16k(0xC000, 0);
+    set_prom_bank_16k(0x8000, GETLAST16KBANK(cart)); /* Set the last one */
+
+    if (Cart->VROMSize > 0)
+        set_vrom_bank_8k(0x0000,0);
+
+    unrom_load_vbank = 0;
+    
+    /* Register the write hook */
+    for (i = 0x80; i < 0x100; i++)
+    {
+        set_page_wr_hook(i, unrom_MapperWriteHook);
+        set_page_writeable(i, true);
+    }
+    
+    return 0;
+} 
+
+
+void unrom_MapperWriteHook(register byte Addr, register byte Value) 
+{    
+    set_vrom_bank_8k(0x0000,Value);
+    unrom_load_vbank = Value;
+} 
+
+void unrom_MapperDump(FILE *fp)
+{
+    fprintf(fp,"unrom: vbank:%d\n",unrom_load_vbank);
+}

+ 118 - 0
src/mappersmanager/utils.c

@@ -0,0 +1,118 @@
+/*
+ *  Mapper facilities - The TI-NESulator Project
+ *  mappers.c
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-05-31 18:00:41 +0200 (jeu, 31 mai 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/mappers.c $
+ *  $Revision: 56 $
+ *
+ */ 
+
+//#define DEBUG_VROM_BANK_SWITCH
+//#define DEBUG_PROM_BANK_SWITCH
+
+#include <stdio.h>
+#include <string.h>
+
+#include <NESCarts.h>
+#include <ppu/ppu.h>
+#include <mappers/manager.h>
+#include <memory/manager.h>
+
+extern NesCart *Cart;
+
+extern char MapperWantIRQ;
+
+/*
+ Here are some fonction useful for mappers
+ */
+
+void set_vrom_bank_1k(unsigned short addr,int slot)
+{
+#ifdef DEBUG_VROM_BANK_SWITCH
+    printf("Change vrom 1k bank 0x%X to slot %d\n",addr,slot);
+#endif
+    ppu_setPagePtr1k((addr>>8)&0xFF, Cart->VROMBanks + (slot * 1024));
+//    memcpy(ppu.Memory+addr, Cart->VROMBanks + (slot * 1024), 0x0400);
+}
+
+void set_vrom_bank_2k(unsigned short addr,int slot)
+{
+#ifdef DEBUG_VROM_BANK_SWITCH
+    printf("Change vrom 2k bank 0x%X to slot %d\n",addr,slot);
+#endif
+    ppu_setPagePtr2k((addr>>8)&0xFF, Cart->VROMBanks + (slot * 2 * 1024));
+//    memcpy(ppu.Memory+addr, Cart->VROMBanks + (slot * 2 * 1024), 0x0800);
+}
+
+void set_vrom_bank_4k(unsigned short addr,int slot)
+{
+#ifdef DEBUG_VROM_BANK_SWITCH
+    printf("Change vrom 4k bank 0x%X to slot %d\n",addr,slot);
+#endif
+    ppu_setPagePtr4k((addr>>8)&0xFF, Cart->VROMBanks + (slot * 4 * 1024));
+//    memcpy(ppu.Memory+addr, Cart->VROMBanks + (slot * 4 * 1024), 0x1000);
+}
+
+void set_vrom_bank_8k(unsigned short addr, int slot)
+{
+#ifdef DEBUG_VROM_BANK_SWITCH
+    printf("Change vrom 8k bank 0x%X to slot %d\n",addr,slot);
+#endif
+    ppu_setPagePtr8k(0x00, Cart->VROMBanks + (slot * 8 * 1024));
+//    memcpy(ppu.Memory, Cart->VROMBanks + (slot * 8 * 1024) , 0x2000);    
+}
+
+/*-----------*/
+
+void set_prom_bank_8k(unsigned short addr,int slot)
+{
+#ifdef DEBUG_PROM_BANK_SWITCH
+    printf("Change prom 8k bank 0x%X to slot %d\n",addr,slot);
+#endif
+    set_page_ptr_8k(addr >> 8, Cart->PROMBanks + (slot * 8 * 1024));
+}
+
+void set_prom_bank_16k(unsigned short addr,int slot)
+{
+#ifdef DEBUG_PROM_BANK_SWITCH
+    printf("Change prom 16k bank @ 0x%X [0x%X] to slot 0x%X\n",addr, addr>>8,slot);
+#endif
+    set_page_ptr_16k(addr >> 8, Cart->PROMBanks + (slot * 16 * 1024));
+}
+
+void set_prom_bank_32k(unsigned short addr,int slot)
+{ /* addr may not be different from 0x8000 !*/
+  /* Anyway I don't use it */
+#ifdef DEBUG_PROM_BANK_SWITCH
+    printf("Change prom 32k bank 0x%X to slot %d\n",addr,slot);
+#endif
+    set_page_ptr_32k(addr >> 8, Cart->PROMBanks + (slot * 32 * 1024));
+/*    set_page_ptr_16k(0x80, Cart->PROMBanks[(slot<<1)]);
+    set_page_ptr_16k(0xC0, Cart->PROMBanks[(slot<<1)+1]);*/
+}
+
+
+void map_sram()
+{
+    int i;
+    for (i = 0x60; i < 0x80; i++)
+    {
+        set_page_readable(i,true);
+        set_page_writeable(i,true);
+    }
+}
+
+void unmap_sram()
+{
+    int i;
+    for (i = 0x60; i < 0x80; i++)
+    {
+        set_page_readable(i,false);
+        set_page_writeable(i,false);
+    }
+}

+ 275 - 0
src/memorymanager/memory.c

@@ -0,0 +1,275 @@
+/*
+ *  6502 Memory manager - The TI-NESulator Project
+ *  memory.c - Taken from the Quick6502 project
+ *
+ *  Created by Manoël Trapier on 18/09/06.
+ *  Copyright 2003-2007 986 Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-05-02 18:37:41 +0200 (mer, 02 mai 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/memory.c $
+ *  $Revision: 50 $
+ *
+ */
+
+ 
+#include <stdio.h>
+#include "types.h"
+#include "../include/memory/manager.h"
+
+/* Private structures */
+
+#define KBYTE * (1024)
+
+/* 
+* What inside memory manager:
+ *
+ * Table of attributes
+ * Table of original page ptr
+ * Table of moded page ptr
+ * 
+ */
+
+/* Private data */
+
+byte *memory_pages[0x100];
+byte memory_pages_attr[0x100];
+
+func_rdhook rdh_table[0x100];
+func_wrhook wrh_table[0x100];
+
+//#define DEBUG
+#undef DEBUG
+
+#ifdef DEBUG
+#define LOG(s) s
+#else
+#define LOG(s) {}
+#endif
+
+/* Public functions */
+void set_page_ptr(byte page, byte *ptr)
+{
+    LOG(printf("Set page 0x%X to ptr %p\n", page, ptr));
+    memory_pages[page] = ptr;
+}
+
+void set_page_ptr_1k(byte page, byte *ptr)
+{   /* 1k = 4 * 256 */
+    LOG(printf("Set page(1k) 0x%X to ptr %p\n", page, ptr));
+    memory_pages[page + 0] = ptr;
+    memory_pages[page + 1] = ptr +  0x100;
+    memory_pages[page + 2] = ptr + (0x100 * 2);
+    memory_pages[page + 3] = ptr + (0x100 * 3);
+}
+
+void set_page_ptr_2k(byte page, byte *ptr)
+{
+    LOG(printf("Set page(2k) 0x%X to ptr %p\n", page, ptr));
+    memory_pages[page + 0] = ptr;
+    memory_pages[page + 1] = ptr +  0x100;
+    memory_pages[page + 2] = ptr + (0x100 * 2);
+    memory_pages[page + 3] = ptr + (0x100 * 3);
+    memory_pages[page + 4] = ptr + (0x100 * 4);
+    memory_pages[page + 5] = ptr + (0x100 * 5);
+    memory_pages[page + 6] = ptr + (0x100 * 6);
+    memory_pages[page + 7] = ptr + (0x100 * 7);
+}
+
+void set_page_ptr_4k(byte page, byte *ptr)
+{
+    LOG(printf("Set page(4k) 0x%X to ptr %p\n", page, ptr));
+    set_page_ptr_2k(page, ptr);
+    set_page_ptr_2k(page+((4 KBYTE / 256) / 2), ptr + 2 KBYTE);    
+}
+
+void set_page_ptr_8k(byte page, byte *ptr)
+{
+    LOG(printf("Set page(8k) 0x%X to ptr %p\n", page, ptr));
+    set_page_ptr_4k(page, ptr);
+    set_page_ptr_4k(page+((8 KBYTE / 256) / 2), ptr + 4 KBYTE);        
+}
+
+void set_page_ptr_16k(byte page, byte *ptr)
+{
+    set_page_ptr_8k(page, ptr);
+    set_page_ptr_8k(page+((16 KBYTE / 256) / 2), ptr + 8 KBYTE);    
+}
+
+void set_page_ptr_32k(byte page, byte *ptr)
+{
+    set_page_ptr_16k(page, ptr);
+    set_page_ptr_16k(page+((32 KBYTE / 256) / 2), ptr + 16 KBYTE);    
+}
+
+byte *get_page_ptr(byte page)
+{
+    return memory_pages[page];
+}
+
+
+/* Functions to set pages attributes */
+
+void set_page_rd_hook(byte page, func_rdhook func)
+{
+    if (func == NULL)
+    {
+        memory_pages_attr[page] &= (~ATTR_PAGE_HAVE_RDHOOK);
+        if (memory_pages[page] == (byte *)0x01)
+            memory_pages[page] = NULL;
+    }
+    else
+    {
+        memory_pages_attr[page] |= ATTR_PAGE_HAVE_RDHOOK;
+        if (memory_pages[page] == NULL)
+            memory_pages[page] = (byte *)0x01;
+    }
+    
+    rdh_table[page] = func;
+}
+
+void set_page_wr_hook(byte page, func_wrhook func)
+{
+    if (func == NULL)
+    {
+        memory_pages_attr[page] &= (~ATTR_PAGE_HAVE_WRHOOK);
+        if (memory_pages[page] == (byte*)0x01)
+            memory_pages[page] = NULL;
+
+    }
+    else
+    {
+        memory_pages_attr[page] |= ATTR_PAGE_HAVE_WRHOOK;
+        if (memory_pages[page] == NULL)
+            memory_pages[page] = (byte *)0x01;
+    }
+    
+    wrh_table[page] = func;    
+}
+
+void set_page_readable(byte page, bool value)
+{
+    if (value == true)
+        memory_pages_attr[page] |= ATTR_PAGE_READABLE;
+    else
+        memory_pages_attr[page] &= (~ATTR_PAGE_READABLE);    
+}
+
+void set_page_writeable(byte page, bool value)
+{
+    if (value == true)
+        memory_pages_attr[page] |= ATTR_PAGE_WRITEABLE;
+    else
+        memory_pages_attr[page] &= (~ATTR_PAGE_WRITEABLE);        
+}
+
+void set_page_ghost(byte page, bool value, byte ghost)
+{
+    if (value == true)
+    {
+        memory_pages_attr[page] = memory_pages_attr[ghost];
+        rdh_table[page] = rdh_table[ghost];
+        wrh_table[page] = wrh_table[ghost];
+        memory_pages[page] = memory_pages[ghost];
+    }
+}
+
+byte get_page_attributes(byte page)
+{
+    return memory_pages_attr[page];
+}
+
+func_rdhook get_page_rdhook(byte page)
+{
+    if (memory_pages_attr[page] & ATTR_PAGE_HAVE_RDHOOK)
+        return rdh_table[page];
+    
+    return NULL;
+}
+
+func_wrhook get_page_wrhook(byte page)
+{
+    if (memory_pages_attr[page] & ATTR_PAGE_HAVE_WRHOOK)
+        return wrh_table[page];
+    
+    return NULL;
+}
+
+
+
+
+byte ReadMemory(byte page, byte addr)
+{
+    static byte LastRetByte = 0xA5;
+    byte *page_ptr;
+    byte attributes;
+    LOG(printf("Read @ 0x%X-%X\n", page, addr));
+    /* Est-ce que la page est mappé ? && Est-ce que la page est "readable" ? */
+    if ((page_ptr = memory_pages[page]) &&
+          ( (attributes = memory_pages_attr[page]) & ATTR_PAGE_READABLE) )
+    {
+        LOG(printf("Page is non null & readable\n"));
+        if ( attributes & ATTR_PAGE_HAVE_RDHOOK )
+            return ( LastRetByte = rdh_table[page](addr) );
+        else
+            return ( LastRetByte = page_ptr[addr] );
+    }
+    //printf("Trying to read @ 0x%X-%X\n", page, addr);
+    return LastRetByte;
+}
+
+void WriteMemory(byte page, byte addr, byte value)
+{
+    byte *page_ptr;
+    byte attributes;
+    LOG(printf("Write 0x%x @ 0x%X-%X\n", value, page, addr));
+    /* Est-ce que la page est mappé ? && Est-ce que la page est "writable" ? */
+    if ( (page_ptr = memory_pages[page]) &&
+         ( (attributes = memory_pages_attr[page]) & ATTR_PAGE_WRITEABLE) )
+    {
+        if ( attributes & ATTR_PAGE_HAVE_WRHOOK )
+        {
+#ifdef DETECT_BUS_CONFLICT
+            if ((page >= 0x80) && (memory_pages[page][addr] != value))
+                printf("WriteHook: bus conflict at %02X%02X rom:%02X write:%02X\n", page, addr, memory_pages[page][addr], value);
+#endif
+            wrh_table[page](addr, value);
+        }
+        else
+            page_ptr[addr] = value;
+    }
+    else { printf("Trying to write 0x%X @ 0x%X-%X\n", value, page, addr); }
+}
+
+void DumpMemoryState(FILE *fp)
+{
+    int i;
+    
+    for (i = 0x00; i < 0x100; i++)
+    {
+        fprintf(fp,
+                "Page 0x%02X : [%c%c%c%c%c%c] RdH:%p WrH:%p ptr:%p\n",
+                i,
+                (memory_pages_attr[i]&ATTR_PAGE_HAVE_RDHOOK)?'r':'.',
+                (memory_pages_attr[i]&ATTR_PAGE_HAVE_WRHOOK)?'w':'.',
+                (memory_pages_attr[i]&ATTR_PAGE_READABLE)?'R':'.',
+                (memory_pages_attr[i]&ATTR_PAGE_WRITEABLE)?'W':'.',                        
+                (memory_pages_attr[i]&ATTR_PAGE_GHOST)?'G':'.',
+                (memory_pages_attr[i]&ATTR_PAGE_MAPPED)?'M':'.',
+                rdh_table[i],
+                wrh_table[i],
+                memory_pages[i]
+                );
+    }
+}
+
+void InitMemory()
+{
+    int page;
+    
+    for(page = 0 ; page < 0x100 ; page++)
+    {
+        set_page_ptr(page,NULL);
+        memory_pages_attr[page] = 0x00;
+    }
+}

+ 31 - 0
src/os/unix/loadfile.c

@@ -0,0 +1,31 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+#include <sys/mman.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <fcntl.h>
+
+
+/* Map a file in memory */
+void *LoadFilePtr(char * filename)
+{
+	int fd;
+	void *RetPtr = NULL;
+	struct stat FileStat;
+
+	fd = open(filename, O_RDONLY);
+
+	fstat(fd, &FileStat);
+
+	RetPtr = mmap(NULL, FileStat.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
+	
+	close(fd);
+
+	return RetPtr;
+}

+ 121 - 0
src/paddle.c

@@ -0,0 +1,121 @@
+/*
+ *  Paddle manager - The TI-NESulator Project
+ *  paddle.c
+ *
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-05-02 18:37:41 +0200 (mer, 02 mai 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/paddle.c $
+ *  $Revision: 50 $
+ *
+ */
+
+#include <allegro.h>
+#include "paddle.h"
+
+void InitPaddle(Paddle * pdl) 
+{
+    pdl->Bit = 1;
+    pdl->LastWrite = 0;
+} 
+
+
+void WritePaddle(Paddle *pdl, unsigned char val) 
+{
+    if ((pdl->LastWrite == 1) && (val == 0))
+        InitPaddle(pdl);
+    
+       pdl->LastWrite = val;
+} 
+unsigned char ReadPaddle(Paddle * pdl) 
+{
+    switch (pdl->Bit++)    
+    {
+        
+        case 1:
+        if (key[KEY_Z])
+            
+            return 0x41;
+        
+           break;
+        
+    case 2:
+        
+           if (key[KEY_X])
+            
+               return 0x41;
+        
+           break;
+        
+    case 3:
+        
+           if (key[KEY_P])
+            
+               return 0x41;
+        
+           break;
+        
+    case 4:
+        
+           if (key[KEY_ENTER])
+            
+               return 0x41;
+        
+           break;
+        
+    case 5:
+        
+           if (key[KEY_UP])
+            
+               return 0x41;
+        
+           break;
+        
+    case 6:
+        
+           if (key[KEY_DOWN])
+            
+               return 0x41;
+        
+           break;
+        
+    case 7:
+        
+           if (key[KEY_LEFT])
+            
+               return 0x41;
+        
+           break;
+        
+    case 8:
+        
+           if (key[KEY_RIGHT])
+            
+               return 0x41;
+        
+           break;
+        
+    case 20:
+        
+           return 0x41;
+        
+           break;
+        
+    case 24:
+        
+           pdl->Bit = 1;
+        
+           return 0x40;
+        
+    default:
+        
+           return 0x40;
+        
+           break;
+        
+    } 
+       return 0x40;
+    
+} 

+ 136 - 0
src/pluginsmanager/manager.c

@@ -0,0 +1,136 @@
+/*
+ *  Plugins manager - The TI-NESulator Project
+ *  plugins.c
+ *
+ *  Created by Manoel TRAPIER on 02/04/07.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate$
+ *  $Author$
+ *  $HeadURL$
+ *  $Revision$
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <plugins/manager.h>
+
+typedef struct Plugin_
+{   
+    byte *name;
+    
+    PluginInit init;
+    PluginDeinit deinit;
+
+} Plugin;
+
+typedef struct KeyHandler_
+{
+    byte key;
+    
+    PluginKeypress func;
+    
+    struct KeyHandler_ *next;
+    
+} KeyHandler;
+
+KeyHandler *keyHandlersList = NULL;
+
+#include "plugins_list.h"
+
+void plugin_list()
+{
+    Plugin *ptr = &(Plugins[0]);
+    int i = 1;
+    printf("Available plugins:\n");
+    while(ptr->name != NULL)
+    {
+        printf("%d - %s\n", i, ptr->name);
+        ptr++; i++;
+    }
+}
+
+int plugin_load(int id)
+{
+    Plugin *ptr = &(Plugins[0]);
+    
+    for ( ; id == 0 && ptr != NULL; id -- )
+        ptr ++;
+
+    if (ptr == NULL)
+        return -1;
+
+    return ptr->init();
+}
+
+int plugin_unload(int id)
+{
+    Plugin *ptr = &(Plugins[0]);
+    
+    for ( ; id == 0 && ptr != NULL; id -- )
+        ptr ++;
+
+    if (ptr == NULL)
+        return -1;
+
+    return ptr->deinit();
+}
+
+
+/* Available functions for plugins */
+int plugin_install_keypressHandler(byte key, PluginKeypress func)
+{
+    KeyHandler *ptr;
+    
+    if (keyHandlersList == NULL)
+    {
+        keyHandlersList = (KeyHandler*) malloc(sizeof(KeyHandler));
+        
+        keyHandlersList->key = key;
+        keyHandlersList->func = func;
+        keyHandlersList->next = NULL;
+    }
+    else
+    { 
+        ptr = keyHandlersList;
+        
+        while(ptr->next != NULL)
+            ptr = ptr->next;
+        
+        ptr->next = (KeyHandler*) malloc(sizeof(KeyHandler));
+        
+        ptr = ptr->next;
+        
+        ptr->key = key;
+        ptr->func = func;
+        ptr->next = NULL;
+
+    }
+    
+    return 0;
+}
+
+int plugin_remove_keypressHandler(byte key, PluginKeypress func)
+{   /* actually do nothing, we cant remove plugin online */
+    return 0;
+}
+
+
+/* Available functions outside of plugins */
+int plugin_keypress(byte key)
+{
+    KeyHandler *ptr = keyHandlersList;
+    
+    while(ptr != NULL)
+    {
+        if (ptr->key == key)
+        {
+            ptr->func();
+        }
+        ptr = ptr->next;
+    }
+    
+    return 0;
+}

+ 802 - 0
src/pluginsmanager/plugins/gamegenie.c

@@ -0,0 +1,802 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <allegro.h>
+
+#define __TINES_PLUGINS__
+#include <plugins/manager.h>
+
+#include <memory/manager.h>
+#include <types.h>
+#include "allegro.h"
+
+typedef enum gg_States_
+{
+    GG_S00_MAIN_STATE = 0,
+    GG_S01_SEARCH_VALUE,
+    GG_S02_SEARCH_BAR
+} gg_States;
+
+/* Actual State Machine state */
+gg_States gg_state = GG_S00_MAIN_STATE;
+
+/* Own representation of memory */
+byte gg_MainRAM[0x800];
+byte gg_OldMainRAM[0x800];
+byte gg_SRAM[0x2000];
+
+/* Field used to now which byte are currently marked as pertinent or not */
+byte gg_use_MainRAM[0x800];
+byte gg_use_SRAM[0x2000];
+
+int gg_ResultNumber;
+
+byte gg_PatchUsed[10];
+byte gg_PatchedPage[10];
+byte gg_PatchedAddr[10];
+byte gg_PatchedValue[10];
+func_rdhook gg_rdhookPtr[10];
+
+#define GG_RDHOOKPATCH(d) \
+byte gg_RdHookPatch##d(byte addr) \
+{ \
+    if (addr == gg_PatchedAddr[d]) \
+    { \
+        return gg_PatchedValue[d]; \
+    } \
+    else \
+    { \
+        if (gg_rdhookPtr[d] != NULL) \
+            return gg_rdhookPtr[d](addr); \
+        else \
+            return (get_page_ptr(gg_PatchedPage[d])[addr]); \
+    } \
+}
+
+#define GG_MAX_PATCH 10
+
+/* Defines the rdhook patches */
+GG_RDHOOKPATCH(0)
+GG_RDHOOKPATCH(1)
+GG_RDHOOKPATCH(2)
+GG_RDHOOKPATCH(3)
+GG_RDHOOKPATCH(4)
+GG_RDHOOKPATCH(5)
+GG_RDHOOKPATCH(6)
+GG_RDHOOKPATCH(7)
+GG_RDHOOKPATCH(8)
+GG_RDHOOKPATCH(9)
+
+void gg_SetPatch(int id, byte page, byte addr, byte value)
+{
+    func_rdhook *fptr;
+    
+    if (id >= GG_MAX_PATCH)
+        return;
+        
+    /* Set parameters for the patch */
+    if (gg_PatchUsed[id] == 0x00)
+    {
+        gg_rdhookPtr[id] = get_page_rdhook(page);    
+    }    
+    
+    gg_PatchedPage[id] = page;
+    gg_PatchedAddr[id] = addr;
+    gg_PatchedValue[id] = value;
+    gg_PatchUsed[id] = 0xFF;
+    
+    /* Set a ReadHook on the page */
+    
+    switch(id)
+    {
+    default:
+    case 0:
+        fptr = gg_RdHookPatch0;
+        break;
+        
+    case 1:
+        fptr = gg_RdHookPatch1;
+        break;
+        
+    case 2:
+        fptr = gg_RdHookPatch2;
+        break;
+        
+    case 3:
+        fptr = gg_RdHookPatch3;
+        break;
+        
+    case 4:
+        fptr = gg_RdHookPatch4;
+        break;
+        
+    case 5:
+        fptr = gg_RdHookPatch5;
+        break;   
+        
+    case 6:
+        fptr = gg_RdHookPatch6;
+        break;
+        
+    case 7:
+        fptr = gg_RdHookPatch7;
+        break;
+        
+    case 8:
+        fptr = gg_RdHookPatch8;
+        break;
+        
+    case 9:
+        fptr = gg_RdHookPatch9;
+        break;        
+    }
+
+    set_page_rd_hook(page, fptr);
+}
+
+
+
+/* Access to the bitmap Buffer */
+extern BITMAP *Buffer;
+BITMAP *gg_Buffer;
+
+void MessageBox(char *title, char *msg)
+{
+
+    int sc_w, sc_h;
+    int box_h, box_t, box_l, box_w;
+    
+    sc_w = screen->w;
+    sc_h = screen->h;
+    
+    gg_Buffer = create_bitmap(sc_w, sc_h);
+    
+    blit(Buffer, gg_Buffer, 0, 0, 0, 0, 512 + 256, 480);
+    
+    box_w = text_length(font, title) + 10;
+    
+    box_w = (box_w>text_length(font, msg))?box_w:text_length(font, msg);
+    
+    box_w += 15 * 2; /*sc_w/2;*/
+    box_h = 15*2 + 10;
+    
+    /* Set the box center */
+    box_t = (sc_h - box_h) / 2;
+    box_l = (sc_w - box_w) / 2;
+
+    rectfill(gg_Buffer, box_l, box_t, box_l + box_w, box_t + box_h, 60);
+    rect(gg_Buffer, box_l + 5, box_t + 5, box_l + box_w - 5, box_t + box_h - 5, 34);
+    
+    /* Display the title */
+    textout_centre_ex(gg_Buffer, font, title, box_w / 2 + box_l, box_t + 2, 34, 60);
+
+    /* Display the message */
+    textout_centre_ex(gg_Buffer, font, msg, box_w / 2 + box_l, 15 + box_t + 2, 34, 60);
+
+    blit(gg_Buffer, screen, 0, 0, 0, 0, 512 + 256, 480);
+
+    sleep(1);
+    
+    release_bitmap(gg_Buffer);
+
+}
+
+unsigned short SelectNumber(char *title, char *msg, byte size)
+{
+
+    int sc_w, sc_h;
+    int box_h, box_t, box_l, box_w;
+    
+    char valueText[10];
+    
+    unsigned short value;    
+    byte digit = 0;
+    
+    sc_w = screen->w;
+    sc_h = screen->h;
+    
+    gg_Buffer = create_bitmap(sc_w, sc_h);
+    
+    blit(Buffer, gg_Buffer, 0, 0, 0, 0, 512 + 256, 480);
+    
+    box_w = text_length(font, title) + 10;
+    
+    box_w = (box_w>text_length(font, msg))?box_w:text_length(font, msg);
+    
+    sprintf(valueText, "0000");
+        
+    box_w = (box_w>text_length(font, valueText))?box_w:text_length(font, msg);
+    
+    box_w += 15 * 2; /*sc_w/2;*/
+    box_h = 15*2 + 30;
+    
+    /* Set the box center */
+    box_t = (sc_h - box_h) / 2;
+    box_l = (sc_w - box_w) / 2;
+    
+
+    value = 0;
+    
+    while(!key[KEY_ENTER])
+    {
+    
+        rectfill(gg_Buffer, box_l, box_t, box_l + box_w, box_t + box_h, 60);
+        rect(gg_Buffer, box_l + 5, box_t + 5, box_l + box_w - 5, box_t + box_h - 5, 34);
+    
+        /* Display the title */
+        textout_centre_ex(gg_Buffer, font, title, box_w / 2 + box_l, box_t + 2, 34, 60);
+
+        /* Display the message */
+        textout_centre_ex(gg_Buffer, font, msg, box_w / 2 + box_l, 15 + box_t + 2, 34, 60);
+
+        if (size == 2)
+            sprintf(valueText, "  %02X", value&0xFF);
+        else
+            sprintf(valueText, "%04X", value);
+
+        textout_centre_ex(gg_Buffer, font, valueText, box_w / 2 + box_l , 15 + box_t + 2 + 10, 34, 60);
+        
+        switch(digit)
+        {
+        default:
+        case 0:
+            textout_centre_ex(gg_Buffer, font, "   ^", box_w / 2 + box_l , 15 + box_t + 2 + 20, 34, 60);
+            break;
+        case 1:
+            textout_centre_ex(gg_Buffer, font, "  ^ ", box_w / 2 + box_l , 15 + box_t + 2 + 20, 34, 60);        
+            break;
+            
+        case 2:
+            textout_centre_ex(gg_Buffer, font, " ^  ", box_w / 2 + box_l , 15 + box_t + 2 + 20, 34, 60);        
+            break;
+            
+        case 3:
+            textout_centre_ex(gg_Buffer, font, "^   ", box_w / 2 + box_l , 15 + box_t + 2 + 20, 34, 60);
+            break;
+        }
+                
+        blit(gg_Buffer, screen, 0, 0, 0, 0, 512 + 256, 480);    
+        
+        if (key[KEY_UP])
+        {
+            usleep(100000);
+            value += ((digit==0)?0x0001:((digit==1)?0x0010:((digit==2)?0x0100:0x1000)));
+            value &= (size==2)?0xFF:0xFFFF;
+        }
+        
+        if (key[KEY_DOWN])
+        {
+            usleep(100000);
+            value -= ((digit==0)?0x0001:((digit==1)?0x0010:((digit==2)?0x0100:0x1000)));
+            value &= (size==2)?0xFF:0xFFFF;
+        }
+
+        if (key[KEY_RIGHT])
+        {
+            usleep(100000);
+            if (digit <= 0)
+                digit = size-1;
+            else            
+                digit --;
+        }
+        
+        if (key[KEY_LEFT])
+        {
+            usleep(100000);
+            if (digit >= size-1)
+                digit = 0;
+            else
+                digit ++;
+        }
+
+    }
+    release_bitmap(gg_Buffer);
+    while(key[KEY_ENTER]);
+    return value;
+}
+
+int DispMenu(int itemc, char *itemv[], char *title)
+{
+    //printf("%s(%d, %p, \"%s\");\n", __func__, itemc, itemv, title);
+    
+    int selection = 0;
+    int i;
+    int sc_w, sc_h;
+    int box_h, box_t, box_l, box_w;
+    
+    sc_w = screen->w;
+    sc_h = screen->h;
+    
+    gg_Buffer = create_bitmap(sc_w, sc_h);
+    
+    blit(Buffer, gg_Buffer, 0, 0, 0, 0, 512 + 256, 480);
+    
+    box_w = text_length(font, title) + 10;
+    
+    for (i = 0; i < itemc; i ++)
+        box_w = (box_w>text_length(font, itemv[i]))?box_w:text_length(font, itemv[i]);
+    
+    box_w += 15 * 2; /*sc_w/2;*/
+    box_h = 15*2 + itemc*10;
+    
+    /* Set the box center */
+    box_t = (sc_h - box_h) / 2;
+    box_l = (sc_w - box_w) / 2;
+    
+    
+    while(!key[KEY_ENTER])
+    {
+        /* Draw the box and highlight the selected item */
+        rectfill(gg_Buffer, box_l, box_t, box_l + box_w, box_t + box_h, 60);
+        rect(gg_Buffer, box_l + 5, box_t + 5, box_l + box_w - 5, box_t + box_h - 5, 34);
+    
+        /* Display the title */
+        textout_centre_ex(gg_Buffer, font, title, box_w / 2 + box_l, box_t + 2, 34, 60);
+        
+        /* Display the highlight item */
+        rectfill(gg_Buffer, box_l+15, 15 + box_t + (selection * 10) , box_l + box_w - 15, 15 + box_t + (selection * 10) + 10, 34);
+        textout_centre_ex(gg_Buffer, font, itemv[selection], box_w / 2 + box_l, 15 + box_t + (selection * 10) + 2, 60, 34);
+        
+        /* Display other items */
+        for (i = 0; i < itemc; i ++)     
+        {
+            if (i != selection)
+                textout_centre_ex(gg_Buffer, font, itemv[i], box_w / 2 + box_l, 15 + box_t + (i * 10) + 2, 34, 60);
+        }
+
+    
+        /* Blit the screen buffer */
+        blit(gg_Buffer, screen, 0, 0, 0, 0, 512 + 256, 480);
+        
+        /* Now get the keyboard state */
+        if (key[KEY_UP])
+        {
+            usleep(100000);
+            if (selection <= 0)
+                selection = itemc - 1;
+            else            
+                selection --;
+        }
+        
+        if (key[KEY_DOWN])
+        {
+            usleep(100000);
+            if (selection >= (itemc - 1))
+                selection = 0;
+            else
+                selection ++;
+        }
+        
+    }
+
+    release_bitmap(gg_Buffer);
+    while(key[KEY_ENTER]);
+    return selection;
+}
+
+byte AskYesNo(char *title)
+{
+    char *YesNo[] = { "No", "Yes" };
+    
+    return DispMenu(2, YesNo, title);
+}
+
+byte gg_CalcChk(unsigned short addr, byte value)
+{
+    int chk = 0x42;
+    chk += (addr  & 0xFF00) >> 8;
+    chk -= (addr  & 0x00FF);
+    chk += (value & 0x00FF);   
+    return chk;
+}
+
+/* 
+   Code is AAAAVVCC where 
+   AAAA = address,
+   VV   = value,
+   CC   = cheksum
+ */
+unsigned long gg_MakeCode(unsigned addr, byte value)
+{
+    unsigned long code = addr << 16;
+    code |= (value << 8);
+    code |= (gg_CalcChk(addr, value) & 0x00FF);
+    
+    return code ^ 0x246FF53A;
+}
+
+byte gg_SelectPatch()
+{
+    char *Items[GG_MAX_PATCH + 1];
+    char *tmp;
+    int i;
+    byte ret;
+    
+    for (i = 0; i < GG_MAX_PATCH; i++)
+    {
+        tmp = (char*) malloc(0x100);
+        printf("Items[%d]: %p\n", i, tmp);        
+        if (gg_PatchUsed[i] == 0x00)
+            sprintf(tmp, "Patch %d: Not used", i);
+        else
+            sprintf(tmp, "Patch %d: Put 0x%02X on address 0x%02X%02X (Code: %08X)", 
+                    i, gg_PatchedValue[i], gg_PatchedPage[i], gg_PatchedAddr[i],
+                    gg_MakeCode((gg_PatchedPage[i]<<8) | gg_PatchedAddr[i], gg_PatchedValue[i]));
+        
+        Items[i] = tmp;
+    }
+               
+    tmp = (char*) malloc(0x100);
+    sprintf(tmp, "Return");
+    Items[GG_MAX_PATCH] = tmp;
+    
+    ret = DispMenu(GG_MAX_PATCH + 1, Items, "Code Breaker - Select a patch");
+    
+    for(i = 0; i < GG_MAX_PATCH; i++)
+        free(Items[i]);
+    
+    if (ret == GG_MAX_PATCH)
+        return 0xFF;
+    
+    return ret;
+}
+
+void gg_PatchManager()
+{
+    printf("DTC!\n");
+}
+
+void gg_InitSearch()
+{
+    unsigned short addr;
+    
+    for(addr = 0x000; addr < 0x800; addr ++)
+    {
+        gg_MainRAM[addr] = ReadMemory((addr&0xFF00)>>8,addr&0x00FF);
+        gg_use_MainRAM[addr] = 0xFF;
+    }
+    
+    gg_ResultNumber = 0x800;
+}
+
+typedef enum gg_SearchForMode_ 
+{
+    GG_SEARCHFOR_LOWER = 0,
+    GG_SEARCHFOR_HIGHER,
+    GG_SEARCHFOR_IDENTIC,
+    GG_SEARCHFOR_DIFFERENT
+       
+} gg_SearchForMode;
+
+void gg_SearchForValue(byte value)
+{
+    unsigned short addr;
+    byte oldValue;
+    byte currentValue;
+    gg_ResultNumber = 0x00;
+    for(addr = 0x000; addr < 0x800; addr ++)
+    {
+        if (gg_use_MainRAM[addr] == 0xFF)
+        {
+            /* "Backup" the old ram */
+            memcpy(gg_OldMainRAM, gg_MainRAM, 0x800);
+            
+            oldValue = gg_MainRAM[addr];
+            currentValue = ReadMemory((addr&0xFF00)>>8,addr&0x00FF);
+            
+            if (currentValue != value)
+            { /* This is not the good one ! */
+                gg_use_MainRAM[addr] = 0x00;
+            }
+            else
+            { /* This can be the good one ! */
+                gg_ResultNumber++;
+                gg_MainRAM[addr] = currentValue;
+            }
+        }
+    }
+}
+
+void gg_SearchFor(gg_SearchForMode mode)
+{
+    unsigned short addr;
+    byte oldValue;
+    byte currentValue;
+    gg_ResultNumber = 0x00;
+    for(addr = 0x000; addr < 0x800; addr ++)
+    {
+        if (gg_use_MainRAM[addr] == 0xFF)
+        {
+            /* "Backup" the old ram */
+            memcpy(gg_OldMainRAM, gg_MainRAM, 0x800);
+            
+            oldValue = gg_MainRAM[addr];
+            currentValue = ReadMemory((addr&0xFF00)>>8,addr&0x00FF);
+            
+            switch(mode)
+            {
+            case GG_SEARCHFOR_LOWER:
+                if (currentValue >= oldValue)
+                { /* This is not the good one ! */
+                    gg_use_MainRAM[addr] = 0x00;
+                }
+                else
+                { /* This can be the good one ! */
+                    gg_ResultNumber++;
+                    gg_MainRAM[addr] = currentValue;
+                }
+                break;
+
+            case GG_SEARCHFOR_HIGHER:
+                if (currentValue <= oldValue)
+                { /* This is not the good one ! */
+                    gg_use_MainRAM[addr] = 0x00;
+                }
+                else
+                { /* This can be the good one ! */
+                    gg_ResultNumber++;
+                    gg_MainRAM[addr] = currentValue;                    
+                }
+                break;
+
+            case GG_SEARCHFOR_IDENTIC:
+                if (currentValue != oldValue)
+                { /* This is not the good one ! */
+                    gg_use_MainRAM[addr] = 0x00;
+                }
+                else
+                { /* This can be the good one ! */
+                    gg_ResultNumber++;
+                    gg_MainRAM[addr] = currentValue;                    
+                }
+                break;
+            case GG_SEARCHFOR_DIFFERENT:
+                if (currentValue == oldValue)
+                { /* This is not the good one ! */
+                    gg_use_MainRAM[addr] = 0x00;
+                }
+                else
+                { /* This can be the good one ! */
+                    gg_ResultNumber++;
+                    gg_MainRAM[addr] = currentValue;                    
+                }
+                break;
+            }
+        }
+    }
+}
+
+byte gg_DisplayResults()
+{
+    char *Items[100];
+    char *tmp;
+    int i, addr = 0x0000;
+    byte ret = 0;
+                
+    unsigned short AddrList[21];
+    if (gg_ResultNumber > 20)
+    {
+        MessageBox("Code Breaker", "Too many results for displaying them!");
+    }
+    else
+    {
+        for (i = 0; i < gg_ResultNumber; i++)
+                {
+                    while(gg_use_MainRAM[addr] != 0xFF)
+                        addr ++;
+                    printf("0x%04X [%d]\n", addr, i);
+                    tmp = (char*) malloc(0x100);
+                    sprintf(tmp,"Patch: %08XAddress 0x%04X - Was: 0x%02X - Actual: 0x%02X",
+                            i,
+                            addr,
+                            gg_OldMainRAM[addr],
+                            gg_MainRAM[addr]);        
+                    Items[i] = tmp;
+                    AddrList[i] = addr;
+                    
+                    addr++;
+                }
+                tmp = (char*) malloc(0x100);
+                sprintf(tmp, "Return");
+                Items[i] = tmp;
+                
+                ret = DispMenu(gg_ResultNumber + 1, Items, "Code Breaker - Search");
+                if (ret < i)
+                {
+                    if (AskYesNo("Code Breaker: Apply this patch?"))
+                    {
+                        /* Now patch it ! */
+                        gg_SetPatch(gg_SelectPatch(), (AddrList[ret]&0xFF00)>>8, (AddrList[ret]&0x00FF),
+                                    SelectNumber("Code Breaker", "Value to apply:", 2) & 0x00FF);
+                        ret = 1;
+                    }
+                }
+                
+                for(i=0 ; i<gg_ResultNumber+1; i++)
+                    free(Items[i]);
+            }
+            
+    return ret;
+}
+
+void gg_Start()
+{
+    char *S00_MenuList[] = { "Search a specific Value", "Search for an Unknown Value", "Enter code",
+                             "Manage Patches", "Exit" };
+    
+    char *S01_MenuList[] = { "Value is identical", "New Value...", "Value is different",
+                             "Value is greater", "Value is lower", "Result", "Restart", "Exit" };
+    
+    char *S02_MenuList[] = { "Value is identical", "Value is different", "Value is greater",
+                             "Value is lower", "Result", "Restart", "Exit" };                            
+    
+    char Buffer[100];
+    int ret;
+    byte value;
+    unsigned short addr;
+    switch(gg_state)
+    {
+    default:
+    case GG_S00_MAIN_STATE:
+        ret = DispMenu(5, S00_MenuList, "Code Breaker - Main");
+        
+        switch (ret)
+        {
+        case 0:
+
+            gg_InitSearch();
+            gg_state = GG_S01_SEARCH_VALUE;
+            value = SelectNumber("Code Breaker", "Select the value:", 2) & 0x00FF;
+            
+            gg_SearchForValue(value);
+            
+            MessageBox("Code Breaker", "Search initialized !");
+            break;
+        case 1:
+            gg_InitSearch();
+            gg_state = GG_S02_SEARCH_BAR;
+            MessageBox("Code Breaker", "Search initialized !");
+            break;
+        case 2: /* Enter code */
+            addr = SelectNumber("Code Breaker", "Select the address", 4);
+            value = SelectNumber("Code Breaker", "Select the value:", 2) & 0x00FF;
+            
+             if (AskYesNo("Code Breaker: Apply this patch?"))
+             {
+                /* Now patch it ! */
+                gg_SetPatch(gg_SelectPatch(),
+                            (addr&0xFF00)>>8, (addr&0x00FF),
+                            value);
+             }
+            
+            break;
+        
+        case 3: /* Patch manager */
+            gg_PatchManager();
+            break;
+        }
+        
+        
+        break;
+        
+    case GG_S01_SEARCH_VALUE:
+S01_MENU:     
+        ret = DispMenu(8, S01_MenuList, "Code Breaker - Search");
+        switch(ret)
+        {
+        case 0:
+            gg_SearchFor(GG_SEARCHFOR_IDENTIC);
+            //goto S02_MENU;
+            break;
+        case 1:
+            value = SelectNumber("Code Breaker", "Select the value:", 2) & 0x00FF;
+            
+            gg_SearchForValue(value);
+            break;
+            
+        case 2:
+            gg_SearchFor(GG_SEARCHFOR_DIFFERENT);
+            //goto S02_MENU;
+            break;
+
+        case 3:
+            gg_SearchFor(GG_SEARCHFOR_HIGHER);
+            //goto S02_MENU;
+            break;
+
+        case 4:
+            gg_SearchFor(GG_SEARCHFOR_LOWER);
+            //goto S02_MENU;
+            break;
+
+        case 5: /* Results */
+            if (gg_DisplayResults() == 1)
+                gg_state = GG_S00_MAIN_STATE;
+            else
+                goto S01_MENU;
+            break;
+            
+        case 6:
+            if (AskYesNo("Code Breaker: Restart?"))
+            {
+                gg_state = GG_S00_MAIN_STATE;
+                gg_Start();
+            }
+            else
+                goto S01_MENU;
+            break;
+            
+        }
+        sprintf(Buffer,"Results found: %d", gg_ResultNumber);
+        MessageBox("Code Breaker", Buffer);
+        
+        break;
+    
+    case GG_S02_SEARCH_BAR:
+S02_MENU:    
+        ret = DispMenu(7, S02_MenuList, "Code Breaker - Search");
+        switch(ret)
+        {
+        case 0:
+            gg_SearchFor(GG_SEARCHFOR_IDENTIC);
+            //goto S02_MENU;
+            break;
+
+        case 1:
+            gg_SearchFor(GG_SEARCHFOR_DIFFERENT);
+            //goto S02_MENU;
+            break;
+
+        case 2:
+            gg_SearchFor(GG_SEARCHFOR_HIGHER);
+            //goto S02_MENU;
+            break;
+
+        case 3:
+            gg_SearchFor(GG_SEARCHFOR_LOWER);
+            //goto S02_MENU;
+            break;
+
+        case 4: /* Results */
+            if (gg_DisplayResults() == 1)
+                gg_state = GG_S00_MAIN_STATE;
+            else
+                goto S02_MENU;
+            break;
+            
+        case 5:
+            if (AskYesNo("Code Breaker: Restart?"))
+            {
+                gg_state = GG_S00_MAIN_STATE;
+                gg_Start();
+            }
+            else
+                goto S02_MENU;
+            break;
+            
+        }
+        sprintf(Buffer,"Results found: %d", gg_ResultNumber);
+        MessageBox("Code Breaker", Buffer);
+        break;
+    }
+}
+
+
+
+int gg_Init()
+{
+    int i;
+    printf("Initializing GG plugin...\n");
+    
+    plugin_install_keypressHandler('g', gg_Start);
+
+    for ( i = 0; i < GG_MAX_PATCH; i++)
+        gg_PatchUsed[i] = 0x00;
+    
+    return 0;
+}
+
+
+int gg_Deinit()
+{
+    return 0;
+}

+ 2 - 0
src/pluginsmanager/plugins/gamegenie.h

@@ -0,0 +1,2 @@
+int gg_Init();
+int gg_Deinit();

+ 10 - 0
src/pluginsmanager/plugins_list.h

@@ -0,0 +1,10 @@
+/* This file could be generated from the plugins directory... */
+
+#include "plugins/gamegenie.h"
+
+Plugin Plugins[] = {
+        { "Game Genie", gg_Init, gg_Deinit },
+ 
+/* EOL tag */
+         { NULL, NULL, NULL }
+                   };

+ 348 - 0
src/ppu/debug/ppu.debug.c

@@ -0,0 +1,348 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <allegro.h>
+
+#define __TINES_PPU_INTERNAL__
+
+#include <ppu/ppu.h>
+#include <ppu/ppu.memory.h>
+#include <ppu/ppu.debug.h>
+
+#include <types.h>
+
+extern BITMAP *Buffer;
+
+extern unsigned short ppu_spritePatternTable;
+
+extern short PPU_Reg_S;
+
+void DebugColor()
+{
+#ifdef TO_MAKE
+  static unsigned short x = 128;
+  static unsigned short y = 128;
+  unsigned char OldDisplayPalette = ppu.DisplayPalette;
+  byte keyb;
+  unsigned int i;
+  unsigned long Color;
+
+  NOBLIT = 1;
+
+  ppu.DisplayPalette = ~0;
+
+  while(!key[KEY_ESC])
+  {
+    frame++;
+    PPUVBlank();
+
+    Color = /*Buffer->line[y][x]*/ _getpixel(Buffer, x, y);
+
+    textprintf(Buffer, font, 5, 340, GetColor(3), "Pos [%d:%d] Color: %d Bg: %d", x, y, Color, BgColor);
+
+    line(Buffer, x-10, y, x+10, y, GetColor(1));
+    line(Buffer, x, y-10, x, y+10, GetColor(1));
+
+
+    /*
+    rect(Buffer, 0, 255, 4 * 20 + 2, 255 + 4 * 20 + 2, 0);
+    rect(Buffer, 90, 255, 90 + 4 * 20 + 2, 255 + 4 * 20 + 2, 0);
+    for (i = 0; i < 16; i++)
+    {
+      rectfill(Buffer, 1 + (i % 4) * 20, 256 + (i / 4) * 20, 1 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, PPU_Rd(0x3F00 + i]);
+      rectfill(Buffer, 91 + (i % 4) * 20, 256 + (i / 4) * 20, 91 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, PPU_Rd(0x3F10 + i]);
+    }*/
+    for( i = 0; i < 16; i++)
+    {
+      if (GetColor(PPU_Rd(0x3F00 + i]) == Color)
+      {
+    line(Buffer, 1+(i%4)*20, 256 + (i / 4) * 20, 1 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, 0xFFFFFFFF); 
+    line(Buffer, 1+(i%4)*20, 256 + (i / 4) * 20 + 20, 1 + (i % 4) * 20 + 20, 256 + (i / 4) * 20, 0xFFFFFFFF); 
+      }
+
+      if (GetColor(PPU_Rd(0x3F10 + i]) == Color)
+      {
+    line(Buffer, 91+(i%4)*20, 256 + (i / 4) * 20, 91 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, 0xFFFFFFFF); 
+    line(Buffer, 91+(i%4)*20, 256 + (i / 4) * 20 + 20, 91 + (i % 4) * 20 + 20, 256 + (i / 4) * 20, 0xFFFFFFFF); 
+      }
+       
+    }
+
+    blit(Buffer, screen, 0, 0, 0, 0, 512 + 256, 480);    
+
+    if (keypressed())
+    {
+      keyb = (readkey() & 0xFF);
+
+      if (keyb == '4')
+      {
+    x--;
+      }
+      if (keyb == '8')
+      {
+    y--;
+      }
+      if (keyb == '6')
+      {
+    x++;
+      }
+      if (keyb == '2')
+      {
+    y++;
+      }
+
+
+    }
+  }
+  ppu.DisplayPalette = OldDisplayPalette;
+  NOBLIT = 0;
+  
+#endif
+}
+
+void DebugSprites()
+{
+#ifdef TO_MAKE
+  byte keyb;
+  static int SelSprite = 0;
+  PPUSprite sprite;
+  NOBLIT = 1;
+  ppu.ControlRegister2.b |= PPU_CR2_SPRTVISIBILITY;
+
+  while(!key[KEY_ESC])
+  {
+    frame++;
+    PPUVBlank();
+    sprite = PPUGetSprite(SelSprite);
+
+    if (ppu.ControlRegister1.b & PPU_CR1_SPRTSIZE)
+    {
+      rect(Buffer, sprite.x-1, sprite.y-1, sprite.x+9, sprite.y+17, 1);
+    }
+    else
+    {
+      rect(Buffer, sprite.x-1, sprite.y-1, sprite.x+9, sprite.y+9, 1);
+    }
+
+    textprintf(Buffer, font, 5, 340, GetColor(3), "Sprite %d [%d:%d]", SelSprite, sprite.x, sprite.y);
+    textprintf(Buffer, font, 5, 349, GetColor(3), "B0: 0x%X  B1: 0x%X  B2: 0x%X  B3: 0x%X",sprite.y,sprite.tileid,sprite.flags.b,sprite.x);
+    textprintf(Buffer, font, 5, 358, GetColor(3), "Tile Index: %d", sprite.tileid);
+    textprintf(Buffer, font, 5, 367, GetColor(3), "Vertical Flip: %d", sprite.flags.s.VFlip);
+    textprintf(Buffer, font, 5, 376, GetColor(3), "Horizontal Flip: %d", sprite.flags.s.HFlip);
+    textprintf(Buffer, font, 5, 385, GetColor(3), "Background Priority: %d", sprite.flags.s.BgPrio);
+    textprintf(Buffer, font, 5, 394, GetColor(3), "Upper Color: %d", sprite.flags.s.UpperColor);
+
+    blit(Buffer, screen, 0, 0, 0, 0, 512 + 256, 480);    
+
+    if (keypressed())
+    {
+      keyb = (readkey() & 0xFF);
+      if (keyb == '+')
+    SelSprite = (SelSprite<63)?SelSprite+1:0;
+      if (keyb == '-')
+    SelSprite = (SelSprite>0)?SelSprite-1:63;
+
+      if (keyb == 'h')
+      {
+    sprite.flags.s.HFlip = ~sprite.flags.s.HFlip;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == 'b')
+      {
+    sprite.flags.s.BgPrio = ~sprite.flags.s.BgPrio;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == 'v')
+      {
+    sprite.flags.s.VFlip = ~sprite.flags.s.VFlip;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+
+      if (keyb == '4')
+      {
+    sprite.x--;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == '8')
+      {
+    sprite.y--;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == '6')
+      {
+    sprite.x++;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == '2')
+      {
+    sprite.y++;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+    }
+  }
+  NOBLIT = 0;
+#endif
+}
+
+#define GetTilePos(addr,x,y) (addr+x+(y*32))
+
+#define GetTileColor(tile,x1,y1)	( ( ppu_readMemory(((tile+y1)>>8)&0xFF, (tile+y1) & 0xFF) & (1<<(7-x1)) ) == 0 ? 0 : 1 ) | \
+									( ( ppu_readMemory(((tile+y1+8)>>8) & 0xFF, (tile+y1+8) &0xFF) & (1<<(7-x1)) ) == 0 ? 0 : 1<<1 )
+
+#define PPU_Rd(addr) ppu_readMemory((addr>>8)&0xFF, addr&0xFF)									
+
+void ppu_dumpOneNameTable(unsigned short nametable, int xd, int yd)
+{
+    byte x,y, x1, y1, Color;
+    unsigned short TileID;
+    
+    for (x = 0; x < 32; x++)
+        for (y = 0; y < 30; y++)
+        {
+            TileID = (PPU_Rd(0x2000 + nametable + x + (y * 32)) << 4) | (PPU_Reg_S);
+
+            for (x1 = 0; x1 < 8; x1++)
+                for (y1 = 0; y1 < 8; y1++)
+                {
+                    Color = GetTileColor(TileID, x1, y1);
+//                    if (ppu.DisplayAttributeTable != 0)
+//                        Color |= (Buffer->line[(8 * y) + 240 + y1][(8 * x) + 256 + x1] & 0x3) << 2;
+                    Color += (nametable>>8);
+                    Color = ppu_readMemory(0x3F, Color);
+                    
+                    
+                    _putpixel(Buffer, (8 * x) + xd + x1, (8 * y) + yd + y1, Color);
+                }
+         }
+}
+
+void ppu_dumpOneAttributeTable(unsigned short nametable, int xd, int yd)
+{
+    int x, y, x1, y1, Color, AttrByte;
+    for (x = 0; x < 0x40; x++)
+    {
+        AttrByte = PPU_Rd(nametable + 0x23C0 + x);
+        x1 = x % 8;
+        y1 = x / 8;
+    
+        Color = AttrByte & 0x3; // Pattern 1;
+//        Color = PPU_Rd(0x3F00 + (Color * 4) + 1);
+        rectfill(Buffer,xd+(x1*32),yd+(y1*32),xd+15+(x1*32),yd+15+(y1*32),Color);
+        
+        textprintf_ex(Buffer, font, 4+xd+(x1*32), 4+yd+(y1*32), ~Color, Color, "%X", Color);
+
+            
+        Color = (AttrByte>>2) & 0x3; // Pattern 2;
+//        Color = PPU_Rd(0x3F00 + (Color * 4) + 1);          
+        rectfill(Buffer,16+xd+(x1*32),yd+(y1*32),16+xd+15+(x1*32),yd+15+(y1*32),Color);
+
+        textprintf_ex(Buffer, font, 4+xd+(x1*32)+16, 4+yd+(y1*32), ~Color, Color, "%X", Color);
+            
+        Color = (AttrByte>>4) & 0x3; // Pattern 3;
+//        Color = PPU_Rd(0x3F00 + (Color * 4) + 1);
+        rectfill(Buffer,xd+(x1*32),16+yd+(y1*32),xd+15+(x1*32),16+yd+15+(y1*32),Color);
+
+        textprintf_ex(Buffer, font, 4+xd+(x1*32), 4+yd+(y1*32)+16, ~Color, Color, "%X", Color);
+            
+        Color = (AttrByte>>6) & 0x3; // Pattern 4;
+//        Color = PPU_Rd(0x3F00 + (Color * 4) + 1);
+        rectfill(Buffer,16+xd+(x1*32),16+yd+(y1*32),16+xd+15+(x1*32),16+yd+15+(y1*32),Color);
+
+        textprintf_ex(Buffer, font, 4+xd+(x1*32)+16, 4+yd+(y1*32)+16, ~Color, Color, "%X", Color);
+        
+        rect(Buffer, xd+(x1*32), yd+(y1*32), xd+(x1*32) + 31 , yd+(y1*32) + 31, 1);
+        line(Buffer, xd+(x1*32)+16, yd+(y1*32), xd+(x1*32) + 16 , yd+(y1*32) + 31, 5);
+        line(Buffer, xd+(x1*32), yd+(y1*32)+16, xd+(x1*32) + 31 , yd+(y1*32) + 16, 5);
+        
+    }
+}
+extern byte *ppu_mem_nameTables;
+extern int ppu_screenMode;
+void ppu_dumpNameTable(int xd, int yd)
+{
+   // ppu_setPagePtr1k(0x20, ppu_mem_nameTables + 0x000);
+   // ppu_setPagePtr1k(0x24, ppu_mem_nameTables + 0x400);
+   // ppu_setPagePtr1k(0x28, ppu_mem_nameTables + 0x800);
+   // ppu_setPagePtr1k(0x2C, ppu_mem_nameTables + 0xC00);
+    
+    ppu_dumpOneNameTable(0x800, xd      , yd);
+    ppu_dumpOneNameTable(0xC00, xd + 256, yd);
+    ppu_dumpOneNameTable(0x000, xd      , yd + 240);
+    ppu_dumpOneNameTable(0x400, xd + 256, yd + 240);
+    
+   // ppu_setScreenMode(ppu_screenMode);
+    
+}
+
+void ppu_dumpAttributeTable(int xd, int yd)
+{
+    ppu_dumpOneAttributeTable(0x800, xd      , yd);
+    ppu_dumpOneAttributeTable(0xC00, xd + 256, yd);
+    ppu_dumpOneAttributeTable(0x000, xd      , yd + 240);
+    ppu_dumpOneAttributeTable(0x400, xd + 256, yd + 240);
+}
+
+void ppu_dumpPattern(int xd, int yd)
+{
+    int x1, y1, x, y, Color, i, TileID;
+/* y:346 */
+        x1 = 0;
+        y1 = 0;
+        for (i = 0; i < 256; i++)
+        {
+            TileID = 0x0000 + (i << 4);
+            for (x = 0; x < 8; x++)
+                for (y = 0; y < 8; y++)
+                {
+                    Color = GetTileColor(TileID, x, y);
+
+                    _putpixel(Buffer, 10 + x1 + x, 347 + y1 + y, /*ppu_readMemory(0x3F,*/ Color/*)*/);
+                }
+
+            x1 += 8;
+            if (x1 >= 128)
+            {
+                x1 = 0;
+                y1 += 8;
+            }
+        }
+        x1 = 0;
+        y1 = 0;
+        for (i = 0; i < 256; i++)
+        {
+            TileID = 0x1000 + (i << 4);
+
+            for (x = 0; x < 8; x++)
+                for (y = 0; y < 8; y++)
+                {
+
+                    Color = GetTileColor(TileID, x, y);
+
+                    _putpixel(Buffer, 10 + 128 + x1 + x, 347 + y1 + y, /*ppu_readMemory(0x3F,*/ 0x10 | Color/*)*/ );
+
+                }
+            x1 += 8;
+            if (x1 >= 128)
+            {
+                x1 = 0;
+                y1 += 8;
+            }
+        }
+}
+									
+void ppu_dumpPalette(int x, int y)
+{
+    int i;
+
+    textout(Buffer, font, "Bg Palette", x , y, 5);
+    textout(Buffer, font, "Sprt Palette", x + 90, y, 5);
+
+    rect(Buffer, x+0, y+20, x+4 * 20 + 2, y + 4 * 20 + 22, 0);
+    rect(Buffer, x+90, y+20, x+90 + 4 * 20 + 2, y + 4 * 20 + 22, 0);
+
+    for (i = 0; i < 16; i++)
+    {
+        rectfill(Buffer, x + 1 + (i % 4) * 20, y + 21 + (i / 4) * 20, x + 1 + (i % 4) * 20 + 20, y + 21 + (i / 4) * 20 + 20, ppu_readMemory(0x3F, i));
+        rectfill(Buffer, x + 91 + (i % 4) * 20, y + 21 +(i / 4) * 20, x + 91 + (i % 4) * 20 + 20, y + 21 +(i / 4) * 20 + 20, ppu_readMemory(0x3F, i+0x10));
+    }
+}

+ 45 - 0
src/ppu/debug/tmp.c.txt

@@ -0,0 +1,45 @@
+   if (ppu.DisplayPalette)
+    {
+        textout(Buffer, font, "Bg Palette", 0, 247, 5);
+        textout(Buffer, font, "Sprt Palette", 90, 247, 5);
+
+        rect(Buffer, 0, 255, 4 * 20 + 2, 255 + 4 * 20 + 2, GetColor(0));
+        rect(Buffer, 90, 255, 90 + 4 * 20 + 2, 255 + 4 * 20 + 2, GetColor(0));
+        for (i = 0; i < 16; i++)
+        {
+            rectfill(Buffer, 1 + (i % 4) * 20, 256 + (i / 4) * 20, 1 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, GetColor(ppu.Memory[0x3F00 + i]));
+            rectfill(Buffer, 91 + (i % 4) * 20, 256 + (i / 4) * 20, 91 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, GetColor(ppu.Memory[0x3F10 + i]));
+        }
+    }
+
+
+    for (i = 0; i < 240; i++)
+    {
+      _putpixel(Buffer, 257 + 0, i, 48);        
+      
+      _putpixel(Buffer, 257 + 1, i, ((ppu.TimedTmpPtr[y]*4))&0xFF);
+      _putpixel(Buffer, 257 + 2, i, ((ppu.TimedTmpPtr[y]*4)>>8)&0xFF);
+      _putpixel(Buffer, 257 + 3, i, ((ppu.TimedTmpPtr[y]*4)>>16)&0xFF);      
+      
+      _putpixel(Buffer, 257 + 4, i, ((ppu.TmpVScroll*4))&0xFF);
+      _putpixel(Buffer, 257 + 5, i, ((ppu.TmpVScroll*4)>>8)&0xFF);
+      _putpixel(Buffer, 257 + 6, i, ((ppu.TmpVScroll*4)>>16)&0xFF);
+      
+      _putpixel(Buffer, 257 + 7, i, ((ppu.TimedHScroll[i]*4)) & 0xFF);
+      _putpixel(Buffer, 257 + 8, i, ((ppu.TimedHScroll[i]*4)>>8) & 0xFF);
+      _putpixel(Buffer, 257 + 9, i, ((ppu.TimedHScroll[i]*4)>>16)& 0xFF);
+
+      _putpixel(Buffer, 257 + 10, i, 48); 
+    }
+
+    if (IRQScanHit != -1)
+    {
+        line(Buffer, 257+12, IRQScanHit, 257+22, IRQScanHit, 10);
+        line(Buffer, 257+12, IRQScanHit, 257+18, IRQScanHit-3, 10);
+        line(Buffer, 257+12, IRQScanHit, 257+18, IRQScanHit+3, 10);
+    }
+
+NoDraw:
+    textprintf(Buffer, font, 5, 340, GetColor(4), "FPS : %d   IPS : %d", FPS, IPS);
+    textprintf(Buffer, font, 5, 3, GetColor(4), "FPS : %d (CPU@~%2.2fMhz : %d%%)", FPS, (float) (((float) IPS) / 1000000.0), (int) ((((float) IPS) / 1770000.0) * 100.0));
+#endif

+ 1061 - 0
src/ppu/oldppu.c

@@ -0,0 +1,1061 @@
+/*
+ *  PPU emulation - The TI-NESulator Project
+ *  oldppu.c
+ *
+ *  Old code for the PPU, still here for unknown purposes
+ *  
+ *  Define and emulate the PPU (Picture Processing Unit) of the real NES
+ * 
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-03-29 12:05:12 +0200 (jeu, 29 mar 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/oldppu.c $
+ *  $Revision: 28 $
+ *
+ */
+#include <allegro.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ppu.h"
+#include "M6502.h"
+
+#define GetTileColor(tile,x1,y1)    ( ( ppu.Memory[tile+y1] & (1<<(7-x1)) ) == 0 ? 0 : 1 ) | \
+                                    ( ( ppu.Memory[tile+y1+8] & (1<<(7-x1)) ) == 0 ? 0 : 1<<1 )
+
+extern PPU ppu;
+extern BITMAP *Buffer;
+extern unsigned short ScanLine;
+unsigned char BgColor;
+
+volatile extern unsigned long IPS, FPS;
+
+int InitPPU(PPU * ppu)
+{
+    if ((ppu->Memory = (unsigned char *) malloc(0x4000)) == NULL)
+        return -1;
+
+    /* Initializing register.. */
+    ppu->In_VBlank = 0;
+    
+    ppu->BaseOneScreen = 0x2000;
+    
+    ppu->ControlRegister1.b = 0;
+    ppu->ControlRegister2.b = 0;
+    ppu->StatusRegister.b = 0;
+    ppu->SPR_RAMAddr = 0;
+    ppu->VRAMAddrReg1.W = 0;
+    ppu->VRAMAddrReg2.W = 0;
+    ppu->VRAMAddrMode = 0;
+
+    ppu->Bg_Pattern_Table = 0x0000;
+    ppu->Name_Table_Addresse = 0x2000;
+    ppu->Sprt_Pattern_Table = 0x0000;
+    ppu->PPU_Inc = 1;
+
+    ppu->MirrorDir = 0;
+    ppu->ScreenType = 1;
+
+
+    ppu->DisplayNameTables = ~0;
+    ppu->DisplayAttributeTable = ~0;
+    ppu->DisplayPalette = ~0;
+    ppu->DisplayVRAM = ~0;
+
+    return 0;
+}
+
+PPUSprite PPUGetSprite(unsigned short i)
+{
+PPUSprite ret;
+
+    ret.y = ppu.SPRRAM[i * 4];
+    ret.tileid = ppu.SPRRAM[i * 4 + 1];
+    ret.flags.BgPrio = (ppu.SPRRAM[i * 4 + 2] & (1 << 5)) == 0 ? 0 : 1;
+    ret.flags.HFlip = (ppu.SPRRAM[i * 4 + 2] & (1 << 6)) == 0 ? 0 : 1;
+    ret.flags.UpperColor = ppu.SPRRAM[i * 4 + 2] & 0x03;
+    ret.flags.VFlip = (ppu.SPRRAM[i * 4 + 2] & (1 << 7)) == 0 ? 0 : 1;
+    ret.x = ppu.SPRRAM[i * 4 + 3];
+    return ret;
+}
+
+
+//(Addr & 0x1FF)    /* Addr In NT */
+// (Addr & 0xC00) >> 2        /* NT number */
+#define GetNT(a)    ( (a&0xC00) >> 10 )
+#define RelAddr(a)    (a & 0x3FF)
+#define PalAddr(a) (a & 0x1F)
+
+unsigned char PPU_Rd(unsigned short Addr)
+{
+    if (Addr > (unsigned short) 0x3FFF)
+    {
+        Addr &= 0x3FFF;
+    }
+    if ((Addr < 0x3F00) && (Addr >= 0x2000))
+    {
+        if (Addr > 0x3000)
+        {
+            Addr -= 0x1000;
+        }
+        if (ppu.ScreenType == 0)
+        {        /* 1 Screen Mode */
+            return ppu.Memory[RelAddr(Addr) + ppu.BaseOneScreen];
+        }
+        else
+            if (ppu.ScreenType)
+            {    /* Miroring mode */
+                if (ppu.MirrorDir)
+                {    /* Horizontal */
+                    if (GetNT(Addr) & 0x2)
+                    {    /* NT2-3 */
+                        return ppu.Memory[0x2000 + RelAddr(Addr)];
+                    }
+                    else
+                    {
+                        return ppu.Memory[0x2400 + RelAddr(Addr)];
+                    }
+                }
+                else
+                {    /* Vertical */
+                    if (GetNT(Addr) & 0x1)
+                    {    /* NT0-2 */
+                        return ppu.Memory[0x2400 + RelAddr(Addr)];
+                    }
+                    else
+                    {
+                        return ppu.Memory[0x2000 + RelAddr(Addr)];
+                    }
+                }
+            }
+            else
+            {    /* Four Screen mode */
+
+            }
+    }
+    else
+        if (Addr >= 0x3F00)
+        {
+            return ppu.Memory[0x3F00 | PalAddr(Addr)];
+        }
+    return ppu.Memory[Addr];
+}
+
+void PPU_Wr(unsigned short Addr, unsigned char Value)
+{
+    if (Addr > (unsigned short) 0x3FFF)
+    {
+        Addr &= 0x3FFF;
+    }
+    if ((Addr < 0x3F00) && (Addr >= 0x2000))
+    {
+        if (Addr > 0x3000)
+        {
+            Addr -= 0x1000;
+        }
+        if (ppu.ScreenType == 0)
+        {        /* 1 Screen Mode */
+            ppu.Memory[RelAddr(Addr) + 0x2000] = Value;
+        }
+        else
+            if (ppu.ScreenType == 1)
+            {    /* Miroring mode */
+                if (ppu.MirrorDir == 0)
+                {    /* Vertical */
+                    if (GetNT(Addr) & 0x1)
+                    {    /* NT0-2 */
+                        ppu.Memory[0x2400 + RelAddr(Addr)] = Value;
+                    }
+                    else
+                    {
+                        ppu.Memory[0x2000 + RelAddr(Addr)] = Value;
+                    }
+                }
+                else
+                {    /* Horizontal */
+                    if (GetNT(Addr) & 0x2)
+                    {    /* NT2-3 */
+                        ppu.Memory[0x2000 + RelAddr(Addr)] = Value;
+                    }
+                    else
+                    {
+                        ppu.Memory[0x2400 + RelAddr(Addr)] = Value;
+                    }
+                }
+            }
+            else
+            {    /* Four Screen mode */
+
+            }
+    }
+    else
+        if (Addr >= 0x3F00)
+        {
+            //printf("%s palette: color %x new value : %d (0x%x)\n", (PalAddr(Addr) < 0x10) ? "Bgnd" : "Sprt", PalAddr(Addr), Value & 0x3F, Addr);
+            ppu.Memory[ /* 0x3F00 | PalAddr(Addr) */ Addr] = Value & 0x3F;
+            if (PalAddr(Addr) == 0x10)
+                ppu.Memory[0x3F00] = Value & 0x3F;
+
+        }
+        else
+        {
+            ppu.Memory[Addr] = Value;
+        }
+}
+
+unsigned short NbOfSprite[249];
+
+void
+ PPUDispSprite(unsigned char bg)
+{
+int x, y, x1, y1, px, py, i;
+char Color;
+PPUSprite sprite;
+
+short SprtAddr;
+//int VFlag, HFlag;
+
+    for (i = 63; i >= 0; i--)
+    {
+        sprite = PPUGetSprite(i);
+        if ((sprite.flags.BgPrio == bg))
+        {        /* On doit afficher ce sprite */
+            y = sprite.y;
+            if (y < 248)
+            {
+                SprtAddr = ((sprite.tileid) << 4) + ppu.Sprt_Pattern_Table;
+                x = sprite.x;
+                for (y1 = 0; y1 < 8; y1++)
+                {
+                    py = y + (sprite.flags.VFlip == 0 ? y1 : (7 - y1));
+                    if ((py > 0) && (py < 249) && ((++NbOfSprite[py]) > 7))
+                    {
+                        ppu.StatusRegister.s.SprtCount = 1;
+                    }
+                    for (x1 = 0; x1 < 8; x1++)
+                    {
+                        Color = GetTileColor(SprtAddr, x1, y1);
+                        if (Color)
+                        {
+                            Color = (Color) | (sprite.flags.UpperColor << 2);
+                            Color = ppu.Memory[0x3F10 + Color];
+                            px = x + (sprite.flags.HFlip == 1 ? (7 - x1) : x1);
+                            if ((i == 0) && (Buffer->line[py][px] != BgColor) && (ppu.HitSpriteAt == 255))
+                            {
+                                //Ligne utilis é pour le d é buguage
+                                   // line(Buffer, 0, py, 256, py, 10);
+                                ppu.HitSpriteAt = py + 1;
+                            }
+                            putpixel(Buffer, px, py, Color);
+                            //Buffer->line[py][px] = Color;
+
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+
+void NewPPUDispSprite()
+{
+int x, y, x1, y1, px, py, i;
+char Color;
+PPUSprite sprite;
+unsigned short bg;
+short SprtAddr;
+
+
+    for (i = 63; i >= 0; i--)
+    {
+        sprite = PPUGetSprite(i);
+        bg = sprite.flags.BgPrio;
+
+        y = sprite.y;
+        if (y < 248)
+        {
+            SprtAddr = ((sprite.tileid) << 4) + ppu.Sprt_Pattern_Table;
+            x = sprite.x;
+
+            for (y1 = 0; y1 < 8; y1++)
+            {
+                py = y + (sprite.flags.VFlip == 0 ? y1 : ((8 - 1) - y1));
+                if ((py > 0) && (py < 249) && ((++NbOfSprite[py]) > 7))
+                {
+                    ppu.StatusRegister.s.SprtCount = 1;
+                }
+                for (x1 = 0; x1 < 8; x1++)
+                {
+                    px = x + (sprite.flags.HFlip == 1 ? (7 - x1) : x1);
+                    Color = GetTileColor(SprtAddr, x1, y1);
+                    if (Color)
+                    {
+                        Color = (Color) | (sprite.flags.UpperColor << 2);
+                        Color = ppu.Memory[0x3F10 + Color];
+                        if ((i == 0) && (Buffer->line[py][px] != BgColor) && (ppu.HitSpriteAt == 255))
+                        {
+                            //Ligne utilisé pour le débuguage
+                            //line(Buffer, 0, py+1, 256, py+1, 10);
+                            ppu.HitSpriteAt = py+1;
+                        }
+                        if ((bg == 0) || ((bg == 1) && (Buffer->line[py][px] == BgColor)))
+                            putpixel(Buffer, px, py, Color);
+                    }
+                }
+            }
+        }
+    }
+}
+
+
+void NewPPUDispSprite_8x16()
+{
+int x, y, x1, y1, px, py, i, loop, tile;
+char Color;
+PPUSprite sprite;
+unsigned short bg;
+short SprtAddr;
+
+unsigned short SprtTable;
+
+    for (i = 63; i >= 0; i--)
+    {
+        sprite = PPUGetSprite(i);
+        bg = sprite.flags.BgPrio;
+        tile = sprite.tileid;
+        y = sprite.y + 1;
+        
+        if (y < 248)
+        {
+            if ( (SprtTable = (tile & 0x1) * 0x1000) == 0x1000)
+                tile -=1;
+            if (sprite.flags.VFlip == 1)
+             {
+                y +=8;
+             }
+            for (loop = 0; loop < 2; loop++)
+            {
+                SprtAddr = ((tile) << 4) + SprtTable;
+                x = sprite.x;
+
+                for (y1 = 0; y1 < 8; y1++)
+                {
+                    py = y + (sprite.flags.VFlip == 0 ? y1 : ((8 - 1) - y1));
+                    if ((py > 0) && (py < 249) && ((++NbOfSprite[py]) > 7))
+                    {
+                        ppu.StatusRegister.s.SprtCount = 1;
+                    }
+                    for (x1 = 0; x1 < 8; x1++)
+                    {
+                        px = x + (sprite.flags.HFlip == 1 ? (7 - x1) : x1);
+                        Color = GetTileColor(SprtAddr, x1, y1);
+                        if (Color)
+                        {
+                            Color = (Color) | (sprite.flags.UpperColor << 2);
+                            Color = ppu.Memory[0x3F10 + Color];
+                            if ((i == 0) && (Buffer->line[py][px] != BgColor) && (ppu.HitSpriteAt == 255))
+                            {
+                                //Ligne utilis é pour le débuguage
+                                //line(Buffer, 0, py, 256, py, 10);
+                                ppu.HitSpriteAt = py+1;
+                            }
+                            if ((bg == 0) || ((bg == 1) && (Buffer->line[py][px] == BgColor)))
+                                putpixel(Buffer, px, py, Color);
+                        }
+                    }
+                }
+                tile += 1;
+                if ( sprite.flags.VFlip == 1)
+                    y -= 8;
+                else
+                    y += 8;
+            }
+
+        }
+    }
+}
+
+
+
+int
+ PPUVBlank()
+{
+int x, y, x1, y1, i;
+
+unsigned long Reg2;
+unsigned short ab_x, ab_y;
+unsigned short Color;
+unsigned short AttrByte;
+unsigned short TileID;
+
+unsigned char XScroll, YScroll;
+
+        ppu.StatusRegister.s.VBlankOccur = 1;
+
+        BgColor = ppu.Memory[0x3F00];//0xC0;
+    clear_to_color(Buffer, BgColor);
+
+/*    if (ppu.ControlRegister2.s.Colour != 0)
+        printf("ppu.ColorEmphasis : %d", ppu.ControlRegister2.s.Colour);*/
+
+
+    for (i = 0; i < 249; i++)
+        NbOfSprite[i] = 0;
+
+    ppu.StatusRegister.s.SprtCount = 0;
+    ppu.HitSpriteAt = 255;
+
+/*
+* A faires les choses qui faut faire durant un lancé de vblank,
+* comme dessiner par ex..
+*/
+
+#define GetTilePos(addr,x,y) (addr+x+(y*32))
+
+    if (ppu.DisplayAttributeTable)
+    {
+/* NT 2000 */
+        for (x = 0; x < 0x40; x++)
+        {
+            AttrByte = /*ppu.Memory[0x23C0 + x];*/PPU_Rd(0x23C0 + x);
+            x1 = x % 8;
+            y1 = x / 8;
+            
+            
+            Color = AttrByte & 0x3; // Pattern 1;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+(x1*32),240+(y1*32),256+15+(x1*32),240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>2) & 0x3; // Pattern 2;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+(x1*32),240+(y1*32),16+256+15+(x1*32),240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>4) & 0x3; // Pattern 3;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+(x1*32),16+240+(y1*32),256+15+(x1*32),16+240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>6) & 0x3; // Pattern 4;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+(x1*32),16+240+(y1*32),16+256+15+(x1*32),16+240+15+(y1*32),Color);
+        }
+
+
+/* NT 2800 */
+        for (x = 0; x < 0x40; x++)
+         {
+            AttrByte = PPU_Rd(0x2BC0 + x);
+            x1 = x % 8;
+            y1 = x / 8;
+            
+            
+            Color = AttrByte & 0x3; // Pattern 1;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+(x1*32),(y1*32),256+15+(x1*32),15+(y1*32),Color);
+            
+            Color = (AttrByte>>2) & 0x3; // Pattern 2;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+(x1*32),(y1*32),16+256+15+(x1*32),15+(y1*32),Color);
+            
+            Color = (AttrByte>>4) & 0x3; // Pattern 3;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+(x1*32),16+(y1*32),256+15+(x1*32),16+15+(y1*32),Color);
+            
+            Color = (AttrByte>>6) & 0x3; // Pattern 4;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+(x1*32),16+(y1*32),16+256+15+(x1*32),16+15+(y1*32),Color);
+         }
+        
+/* NT 2400 */
+        for (x = 0; x < 0x40; x++)
+         {
+            AttrByte = PPU_Rd(0x27C0 + x);
+            x1 = x % 8;
+            y1 = x / 8;
+            
+            
+            Color = AttrByte & 0x3; // Pattern 1;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+256+(x1*32),240+(y1*32),256+256+15+(x1*32),240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>2) & 0x3; // Pattern 2;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+256+(x1*32),240+(y1*32),16+256+256+15+(x1*32),240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>4) & 0x3; // Pattern 3;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+256+(x1*32),240+16+(y1*32),256+256+15+(x1*32),240+16+15+(y1*32),Color);
+            
+            Color = (AttrByte>>6) & 0x3; // Pattern 4;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+256+(x1*32),240+16+(y1*32),16+256+256+15+(x1*32),240+16+15+(y1*32),Color);
+         }
+
+/* NT 2C00 */
+        for (x = 0; x < 0x40; x++)
+        {
+            AttrByte = PPU_Rd(0x2FC0 + x);//PPU_Rd(0x27C0 + x);
+            x1 = x % 8;
+            y1 = x / 8;
+            
+            
+            Color = AttrByte & 0x3; // Pattern 1;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+256+(x1*32),(y1*32),256+256+15+(x1*32),15+(y1*32),Color);
+            
+            Color = (AttrByte>>2) & 0x3; // Pattern 2;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+256+(x1*32),(y1*32),16+256+256+15+(x1*32),15+(y1*32),Color);
+            
+            Color = (AttrByte>>4) & 0x3; // Pattern 3;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+256+(x1*32),16+(y1*32),256+256+15+(x1*32),16+15+(y1*32),Color);
+            
+            Color = (AttrByte>>6) & 0x3; // Pattern 4;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+256+(x1*32),16+(y1*32),16+256+256+15+(x1*32),16+15+(y1*32),Color);
+            }
+
+        if (ppu.DisplayNameTables == 0)
+        {
+            for (x = 0; x < 33; x++)
+            {
+                line(Buffer, 256 + x * 16, 0, 256 + x * 16, 240 + 240, 8);
+                line(Buffer, 256, 0 + x * 16, 256 + 256 + 256, 0 + x * 16, 8);
+            }
+
+            for (x = 0; x < 17; x++)
+            {
+                line(Buffer, 256 + x * 32, 0, 256 + x * 32, 240 + 240, 6);
+                line(Buffer, 256, 0 + x * 32, 256 + 256 + 256, 0 + x * 32, 6);
+            }
+        }
+    }
+
+    if (ppu.DisplayNameTables)
+    {
+/* NT 2000 */
+        for (x = 0; x < 32; x++)
+            for (y = 0; y < 30; y++)
+            {
+                TileID = (PPU_Rd/*ppu.Memory[*/(0x2000 + x + (y * 32))/*]*/ << 4) + ppu.Bg_Pattern_Table;
+
+                for (x1 = 0; x1 < 8; x1++)
+                    for (y1 = 0; y1 < 8; y1++)
+                    {
+                        Color = GetTileColor(TileID, x1, y1);
+                        if (ppu.DisplayAttributeTable != 0)
+                            Color |= (Buffer->line[(8 * y) + 240 + y1][(8 * x) + 256 + x1] & 0x3) << 2;
+
+                        if ((Color != 0) || (ppu.DisplayAttributeTable != 0))
+                        {
+                            Color = ppu.Memory[0x3F00 + Color];
+                            Buffer->line[(8 * y) + 240 + y1][(8 * x) + 256 + x1] = Color;
+                        }
+                    }
+            }
+
+
+/* NT 2800 */
+        for (x = 0; x < 32; x++)
+            for (y = 0; y < 30; y++)
+            {
+                TileID = (PPU_Rd(0x2800 + x + (y * 32)) << 4) + ppu.Bg_Pattern_Table;
+
+                for (x1 = 0; x1 < 8; x1++)
+                    for (y1 = 0; y1 < 8; y1++)
+                    {
+                        Color = GetTileColor(TileID, x1, y1);
+                        if (Color != 0)
+                        {
+                            Color = ppu.Memory[0x3F00 + Color + 4];
+                            Buffer->line[(8 * y) + y1][(8 * x) + 256 + x1] = Color;
+                        }
+                    }
+            }
+
+/* NT 2400 */
+            for (x = 0; x < 32; x++)
+                for (y = 0; y < 30; y++)
+                    {
+                    TileID = (/*PPU_Rd*/ppu.Memory[(0x2400 + x + (y * 32))] << 4) + ppu.Bg_Pattern_Table;
+                    
+                    for (x1 = 0; x1 < 8; x1++)
+                        for (y1 = 0; y1 < 8; y1++)
+                            {
+                            Color = GetTileColor(TileID, x1, y1);
+                            if (ppu.DisplayAttributeTable != 0)
+                                Color |= (Buffer->line[(8 * y) + 240 + y1][(8 * x) + 256 + x1] & 0x3) << 2;
+                            
+                            if ((Color != 0) || (ppu.DisplayAttributeTable != 0))
+                                {
+                                Color = ppu.Memory[0x3F00 + Color];
+                                Buffer->line[(8 * y) + 240+ y1][(8 * x) + 256 + 256 + x1] = Color;
+                                }
+                            }
+                    }
+
+/* NT 2C00 */
+        for (x = 0; x < 32; x++)
+            for (y = 0; y < 30; y++)
+            {
+                TileID = (PPU_Rd(0x2C00 + x + (y * 32)) << 4) + ppu.Bg_Pattern_Table;
+
+                for (x1 = 0; x1 < 8; x1++)
+                    for (y1 = 0; y1 < 8; y1++)
+                    {
+                        Color = GetTileColor(TileID, x1, y1);
+                        if (Color != 0)
+                        {
+                            Color = ppu.Memory[0x3F00 + Color + 12];
+                            Buffer->line[(8 * y) + y1][(8 * x) + 256 + 256 + x1] = Color;
+                        }
+                    }
+            }
+    }
+
+    if (ppu.ControlRegister2.s.BgVisibility == 1)
+    {
+/* Display BG with scrolling informations */
+/* J'ai la solution :D */
+/*
+frame start (line 0) (if background or sprites are enabled):
+v=t
+*/
+        ppu.VRAMAddrReg2.W = ppu.TimedTmpPtr[0] | 0x2000;
+        //printf("Starting addresses : 0x%X\n",ppu.VRAMAddrReg2.W);
+        
+        XScroll = ppu.TimedHScroll[0];
+
+        YScroll = ppu.TmpVScroll;
+
+        for (y = 0; y < 240; y++)
+        {
+/*
+scanline start (if background and sprites are enabled):
+  8421 8421 8421 8421   8421 8421 8421 8421
+v:0000 0100 0001 1111=t:0000 0100 0001 1111
+  1111 1198 7654 3210         
+  5432 10
+*/
+            
+            ppu.VRAMAddrReg2.W = (ppu.VRAMAddrReg2.W & 0xFBE0)
+               | ((ppu.TimedTmpPtr[y]) & 0x041F)
+               | 0x2000;
+
+            
+            TileID = (PPU_Rd(ppu.VRAMAddrReg2.W) << 4)
+               | ppu.Bg_Pattern_Table;
+
+            XScroll = ppu.TimedHScroll[y];
+            //YScroll = ppu.TimedVScroll[y];
+            
+            for (x = 0; x < 256; x++)
+            {
+/* Calculer la couleur du point */
+/* Bits 1 et 0 */
+                Color = GetTileColor(TileID, XScroll, YScroll);
+/* Bits 3 et 2 */
+/*
+XScroll : 0,1,2,3,4
+X = (ppu.VRAMAddrReg2.W & 0x1F)
+
+Y : 5,6,7,8,9
+Y = (ppu.VRAMAddrReg2.W & 0x3E0) >> 5
+*/
+/*ab_y = ((ppu.VRAMAddrReg2.W & 0x3E0) >> 5);
+                ab_x = (ppu.VRAMAddrReg2.W & 0x1F);
+
+                AttrByte = (((ab_y) >> 2) << 3) +
+                   ((ab_x) >> 2);
+
+
+                AttrByte = (PPU_Rd((ppu.VRAMAddrReg2.W & 0x2C00) + 0x3C0 + AttrByte) >>
+                        ((((ab_y & 2) * 2) + (((ppu.VRAMAddrReg2.W & 0x2C00)) & 2)))) & 0x03;*/
+                
+                /*
+                 
+                 00DC BA98 7654 3210
+                 
+                 0000 BA54 3c-2 10b-
+                 0000 0000 0001 1100  : 0x001C >> 2 
+                 0000 0011 1000 0000  : 0x0380 >> 4
+                 
+                   10 --11 11-- ----
+                 
+                 0xC000
+                 & 0x0C3F | 0x23C0
+                 
+                                b                  
+                 val >> ( (Reg2 & 0x2) | ( (Reg2 & 0x0x40)>>4)
+                 */
+                
+                Reg2 = ppu.VRAMAddrReg2.W;
+                AttrByte = ((Reg2 & 0x0380) >> 4) | ((Reg2 & 0x001C) >> 2) | (Reg2 & 0x0C00);
+                AttrByte &= 0x0C3F;
+                AttrByte |= 0x23C0;
+                AttrByte = PPU_Rd(AttrByte);
+                AttrByte = AttrByte >> ( 0x0 | (Reg2 & 0x02) | ( (Reg2 & 0x40) >> 4) );
+                AttrByte &= 0x3;
+                
+                if (Color)
+                {
+                    Color |= (AttrByte << 2);
+                    Color = ppu.Memory[0x3F00 + Color];
+                    Buffer->line[y][x] = Color + 0xC0;
+                }
+                
+                XScroll++;
+                XScroll &= 7;
+                if (XScroll == 0)
+                {    /* On incrémente le compteur de tile */
+                    if ((ppu.VRAMAddrReg2.W & 0x1F) == 0x1F)
+                    {    /* On met a 0 et change
+                         * l'etat du bit 10 */
+                        ppu.VRAMAddrReg2.W &= ~0x1F;
+
+/* A voir si ya pas bcp mieux */
+                        if ((ppu.VRAMAddrReg2.W & 0x400))
+                            ppu.VRAMAddrReg2.W &= ~0x400;
+                        else
+                            ppu.VRAMAddrReg2.W |= 0x400;
+                    }
+                    else
+                    {    /* on incremente juste */
+                        ppu.VRAMAddrReg2.W = (ppu.VRAMAddrReg2.W & ~0x1F) | ((ppu.VRAMAddrReg2.W + 1) & 0x1F);
+                    }
+
+                    TileID = (PPU_Rd(ppu.VRAMAddrReg2.W) << 4)
+                       | ppu.Bg_Pattern_Table;
+                }
+            }
+/*
+you can think of bits 5,6,7,8,9 as the "y scroll"(*8).  this functions
+slightly different from the X.  it wraps to 0 and bit 11 is switched when
+it's incremented from _29_ instead of 31.  there are some odd side effects
+from this.. if you manually set the value above 29 (from either 2005 or
+2006), the wrapping from 29 obviously won't happen, and attrib data will be
+used as name table data.  the "y scroll" still wraps to 0 from 31, but
+without switching bit 11.  this explains why writing 240+ to 'Y' in 2005
+appeared as a negative scroll value.
+*/
+            YScroll++;
+            YScroll &= 7;
+            if (YScroll == 0)
+            {    /* On incrémente le compteur de tile :| */
+                if ((ppu.VRAMAddrReg2.W & 0x3E0) == 0x3A0)
+                {    /* On met a 0 et change l'etat du bit
+                     * 10 */
+                    ppu.VRAMAddrReg2.W &= ~0x3F0;
+
+/* A voir si ya pas bcp mieux */
+                    if ((ppu.VRAMAddrReg2.W & 0x800))
+                        ppu.VRAMAddrReg2.W &= ~0x800;
+                    else
+                        ppu.VRAMAddrReg2.W |= 0x800;
+                }
+                else
+                {    /* on incremente juste */
+                    ppu.VRAMAddrReg2.W = (ppu.VRAMAddrReg2.W & ~0x3F0) | ((((ppu.VRAMAddrReg2.W & 0x3F0) >> 5) + 1) << 5);
+                }
+            }
+        }
+        //while (!key[KEY_ENTER]);
+    }
+/*
+* if (ppu.ControlRegister2.s.SpriteVisibility == 1)
+* PPUDispSprite(0);
+*/
+
+    if (ppu.ControlRegister2.s.SpriteVisibility == 1)
+    {
+        if (ppu.ControlRegister1.s.SpriteSize)
+        {
+            NewPPUDispSprite_8x16();
+        }
+        else
+        {
+            NewPPUDispSprite();
+        }
+    }
+
+/*    for(x=0;x<256;x++)
+        for(y=0;y<240;y++)
+        {
+            if ((i = getpixel(Buffer,x,y)) >= 0xC0)
+                putpixel(Buffer,x,y,ppu.Memory[0x3F00+i-0xC0]);
+        }*/
+    textprintf(Buffer, font, 5, 340, 4, "FPS : %d   IPS : %d", FPS, IPS);
+    //textprintf(Buffer, font, 5, 3, 4, "FPS : %d (CPU@~%2.2fMhz : %d%%)", FPS, (float) (((float) IPS) / 1000000.0), (int) ((((float) IPS) / 1770000.0) * 100.0));
+
+    if (ppu.DisplayPalette)
+    {
+        textout(Buffer, font, "Bg Palette", 0, 247, 5);
+        textout(Buffer, font, "Sprt Palette", 90, 247, 5);
+
+        rect(Buffer, 0, 255, 4 * 20 + 2, 255 + 4 * 20 + 2, 0);
+        rect(Buffer, 90, 255, 90 + 4 * 20 + 2, 255 + 4 * 20 + 2, 0);
+        for (i = 0; i < 16; i++)
+        {
+            rectfill(Buffer, 1 + (i % 4) * 20, 256 + (i / 4) * 20, 1 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, ppu.Memory[0x3F00 + i]);
+            rectfill(Buffer, 91 + (i % 4) * 20, 256 + (i / 4) * 20, 91 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, ppu.Memory[0x3F10 + i]);
+        }
+    }
+    if (ppu.DisplayVRAM)
+    {
+
+/* y:346 */
+        x1 = 0;
+        y1 = 0;
+        for (i = 0; i < 256; i++)
+        {
+            TileID = 0x0000 + (i << 4);
+
+            for (x = 0; x < 8; x++)
+                for (y = 0; y < 8; y++)
+                {
+
+                    Color = GetTileColor(TileID, x, y);
+
+                    putpixel(Buffer, 10 + x1 + x, 347 + y1 + y, /*ppu.Memory[0x3F00 + */Color/*]*/);
+
+                }
+
+
+            x1 += 8;
+            if (x1 >= 128)
+            {
+                x1 = 0;
+                y1 += 8;
+            }
+        }
+        x1 = 0;
+        y1 = 0;
+        for (i = 0; i < 256; i++)
+        {
+            TileID = 0x1000 + (i << 4);
+
+            for (x = 0; x < 8; x++)
+                for (y = 0; y < 8; y++)
+                {
+
+                    Color = GetTileColor(TileID, x, y);
+
+                    putpixel(Buffer, 10 + 128 + x1 + x, 347 + y1 + y, /*ppu.Memory[*/0x3F10 + Color/*]*/);
+
+                }
+            x1 += 8;
+            if (x1 >= 128)
+            {
+                x1 = 0;
+                y1 += 8;
+            }
+        }
+    }
+    blit(Buffer, screen, 0, 0, 0, 0, 512 + 256, 480);
+
+    //stretch_blit(Buffer, screen, 0, 0, 256, 240, 0, 0, 512, 480);
+
+    if (ppu.ControlRegister1.s.VBlank_NMI == 1)
+        return 1;
+
+    return 0;
+}
+
+char
+ ReadPPUReg(char RegID)
+{
+/* RegID is the nb of the reg 0-7 */
+unsigned char ret;
+
+    switch (RegID)
+    {
+    default:
+        ret = (unsigned char) 0x00;    /* Can return every thing you
+                         * want here :D */
+        break;
+    case 1:        /* Control Register 2 */
+        ret = ppu.ControlRegister2.b;
+        break;
+    case 2:
+        ret = ppu.StatusRegister.b;
+
+        ppu.StatusRegister.s.VBlankOccur = 0;
+        //ppu.StatusRegister.s.Sprite0Occur = 0;
+        ppu.VRAMAddrMode = 0;
+        break;
+    case 5:
+        ret = ppu.VRAMAddrReg2.B.l;
+        break;
+    case 6:
+        ret = ppu.VRAMAddrReg2.B.h;
+        break;
+    case 7:        /* PPU in NES is really strange.. Bufferised
+                 * send is nerd.. */
+        if (ppu.VRAMAddrReg2.W < 0x3EFF)
+        {
+            ret = ppu.VRAMBuffer;
+            ppu.VRAMBuffer = PPU_Rd(ppu.VRAMAddrReg2.W);
+            ppu.VRAMAddrReg2.W += ppu.PPU_Inc;
+            ppu.VRAMAddrReg2.W &= 0x3FFF;
+        }
+        else
+         {
+            ret = PPU_Rd(ppu.VRAMAddrReg2.W);
+            //ppu.VRAMAddrReg2.W += ppu.PPU_Inc;
+         }
+        break;
+    }
+    return ret;
+}
+
+void
+ WritePPUReg(char RegID, unsigned char val)
+{
+/* RegID is the nb of the reg 0-7 */
+
+    switch (RegID)
+    {
+        default:/* For not writeable reg */
+        break;
+    case 0:        /* Control Register 1 */
+        ppu.ControlRegister1.b = val;
+        ppu.Bg_Pattern_Table = (ppu.ControlRegister1.s.BgPattern == 1) ? 0x1000 : 0x0000;
+        ppu.Sprt_Pattern_Table = (ppu.ControlRegister1.s.SptPattern == 1) ? 0x1000 : 0x0000;
+        ppu.PPU_Inc = (ppu.ControlRegister1.s.AddrIncrmt == 1) ? 32 : 1;
+
+        ppu.Name_Table_Addresse = 0x2000;
+        switch (ppu.ControlRegister1.s.NameTblAddr)
+        {
+        case 3:
+            ppu.Name_Table_Addresse += 0x400;
+        case 2:
+            ppu.Name_Table_Addresse += 0x400;
+        case 1:
+            ppu.Name_Table_Addresse += 0x400;
+        case 0:
+            break;
+        }
+            ppu.TimedNT[ScanLine] = ppu.ControlRegister1.s.NameTblAddr;
+/*
+2000 write:
+  1111 0011 1111 1111 ( F3FF )
+t:0000 1100 0000 0000 = d:0000 0011
+*/
+        ppu.TmpVRamPtr = ( (ppu.TmpVRamPtr & 0xF3FF)
+                       | ( ((ppu.ControlRegister1.s.NameTblAddr) & 0x03) << 10 )
+                         );
+
+        break;
+    case 1:        /* Control Register 2 */
+        //printf("PPU: new CR2 ; 0x%x\n", val);
+        ppu.ControlRegister2.b = val;
+        break;
+    case 3:        /* SPR-RAM Addresse Register */
+        ppu.SPR_RAMAddr = val;
+        break;
+    case 4:        /* SPR-RAM Input Register */
+        ppu.SPRRAM[ppu.SPR_RAMAddr] = val;
+        break;
+    case 5:        /* VRAM Address register 1 */
+        if (ppu.VRAMAddrMode == 0)
+        {
+/*
+2005 first write:
+t:0000 0000 0001 1111=d:1111 1000
+x=d:00000111
+*/
+            ppu.VRAMAddrMode = 1;
+
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0xFFE0) | ((val & 0xF8) >> 3));
+            ppu.HScroll = val & 0x7;
+
+            //printf("%d -> 2005 w1: 0x%04X (val: 0x%02X)\n", ScanLine, ppu.TmpVRamPtr, val);
+        }
+        else
+        {
+/*
+2005 second write:
+t:0000 0011 1110 0000=d:1111 1000
+t:0111 0000 0000 0000=d:0000 0111
+*/
+            ppu.VRAMAddrMode = 0;
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0xFC1F) | ((val & 0xF8) << 2));
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0x8FFF) | ((val & 0x07) << 12));
+
+            ppu.TmpVScroll = ((ppu.TmpVRamPtr & 0x700) >> 12) & 0x7;
+            if (ppu.TmpVScroll != 0)
+                printf("2002: TmpVScroll == %d \n", ppu.TmpVScroll);
+            
+            //printf("%d -> 2005 w2: 0x%04X (val: 0x%02X)\n", ScanLine, ppu.TmpVRamPtr, val);
+
+        }
+        break;
+    case 6:        /* VRAM Address register 2 */
+        if (ppu.VRAMAddrMode == 0)
+        {
+            ppu.VRAMAddrMode = 1;
+/*
+2006 first write:
+t:0011 1111 0000 0000 = d:0011 1111
+t:1100 0000 0000 0000=0
+*/
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0xC0FF) | ((val&0x3F) << 8)) & 0x3FFF;
+
+            //printf("%d -> 2006 w1: 0x%04X (val: 0x%02X)\n", ScanLine, ppu.TmpVRamPtr, val);
+        }
+        else
+        {
+            ppu.VRAMAddrMode = 0;
+/*
+2006 second write:
+t:0000000011111111=d:11111111
+v=t
+*/
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0xFF00) | (val & 0x00FF));
+            ppu.VRAMAddrReg2.W = ppu.TmpVRamPtr;
+
+            //printf("%d -> 2006 w2: 0x%04X (val: 0x%02X)\n", ScanLine, ppu.TmpVRamPtr, val);
+
+        }
+        break;
+    case 7:        /* VRAM I/O */
+        PPU_Wr(ppu.VRAMAddrReg2.W, val);
+        ppu.VRAMAddrReg2.W += ppu.PPU_Inc;
+        break;
+    }
+}

+ 1311 - 0
src/ppu/ppu.24.c

@@ -0,0 +1,1311 @@
+/*
+ *  PPU emulation - The TI-NESulator Project
+ *  ppu.c
+ *  
+ *  Define and emulate the PPU (Picture Processing Unit) of the real NES
+ * 
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-04-06 17:17:01 +0200 (ven, 06 avr 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/ppu.c $
+ *  $Revision: 32 $
+ *
+ */
+
+#include <allegro.h>
+#include <stdio.h>
+#include <stdlib.h>

+#include "ppu.h"
+#include "memory.h"
+#include "M6502.h"
+
+#define __TINES_PLUGINS__
+#include "plugins.h"
+
+#define GetTileColor(tile,x1,y1)    ( ( ppu.Memory[tile+y1] & (1<<(7-x1)) ) == 0 ? 0 : 1 ) | \
+                                    ( ( ppu.Memory[tile+y1+8] & (1<<(7-x1)) ) == 0 ? 0 : 1<<1 )
+
+#define GetColor(col) (col)
+
+extern PPU ppu;
+extern BITMAP *Buffer;
+extern unsigned short ScanLine;
+unsigned long BgColor;
+
+volatile extern int frame;
+
+volatile extern unsigned long IPS, FPS;
+
+extern unsigned long ColorPalette[ 8 * 63 ];
+
+extern short IRQScanHit;
+
+byte NOBLIT = 0;
+
+int InitPPU(PPU * ppu)
+{
+    int i;
+    if ((ppu->Memory = (unsigned char *) malloc(0x4000)) == NULL)
+        return -1;
+
+    NOBLIT = 0;
+
+    
+
+    /* Initializing register.. */
+    ppu->In_VBlank = 0;
+    
+    ppu->BaseOneScreen = 0x2000;
+    
+    ppu->ControlRegister1.b = 0;
+    ppu->ControlRegister2.b = 0;
+    ppu->StatusRegister.b = 0;
+    ppu->SPR_RAMAddr = 0;
+    ppu->VRAMAddrReg1.W = 0;
+    ppu->VRAMAddrReg2.W = 0;
+    ppu->VRAMAddrMode = 0;
+
+    ppu->Bg_Pattern_Table = 0x0000;
+    ppu->Name_Table_Addresse = 0x2000;
+    ppu->Sprt_Pattern_Table = 0x0000;
+    ppu->PPU_Inc = 1;
+
+    ppu->MirrorDir = 0;
+    ppu->ScreenType = 1;
+
+
+    ppu->ForceBgVisibility = 0;
+    ppu->ForceSpriteVisibility = 0;
+
+    ppu->DisplayNameTables = ~0;
+    ppu->DisplayAttributeTable = ~0;
+    ppu->DisplayPalette = ~0;
+    ppu->DisplayVRAM = ~0;
+
+
+    /* Set PPU registers */
+    set_page_rd_hook(0x20, ReadPPUReg);    
+    set_page_wr_hook(0x20, WritePPUReg);
+    
+    set_page_readable(0x20, true);
+    set_page_writeable(0x20, true);
+    
+    
+    /* Set PPU Ghost Registers */
+    for(i = 0x21; i < 0x40; i++)
+        set_page_ghost(i, true, 0x20);
+
+    plugin_install_keypressHandler('i', DebugSprites);
+    plugin_install_keypressHandler('I', DebugSprites);
+    
+    plugin_install_keypressHandler('u', DebugColor);
+    plugin_install_keypressHandler('U', DebugColor);
+    
+    
+    
+
+    return 0;
+}
+
+PPUSprite PPUGetSprite(unsigned short i)
+{
+PPUSprite ret;
+
+    ret.y = ppu.SPRRAM[i * 4];
+    ret.tileid = ppu.SPRRAM[i * 4 + 1];
+    /*ret.flags.s.BgPrio = (ppu.SPRRAM[i * 4 + 2] & (1 << 5)) == 0 ? 0 : 1;
+    ret.flags.s.HFlip = (ppu.SPRRAM[i * 4 + 2] & (1 << 6)) == 0 ? 0 : 1;
+    ret.flags.s.UpperColor = ppu.SPRRAM[i * 4 + 2] & 0x03;
+    ret.flags.s.VFlip = (ppu.SPRRAM[i * 4 + 2] & (1 << 7)) == 0 ? 0 : 1;*/
+    ret.flags.b = ppu.SPRRAM[i * 4 + 2];
+    ret.x = ppu.SPRRAM[i * 4 + 3];
+    return ret;
+}
+
+void PPUSetSprite(unsigned short i, PPUSprite *sprt)
+{
+    ppu.SPRRAM[i * 4] = sprt->y;
+    ppu.SPRRAM[i * 4 + 1] = sprt->tileid;
+    /*ret.flags.s.BgPrio = (ppu.SPRRAM[i * 4 + 2] & (1 << 5)) == 0 ? 0 : 1;
+    ret.flags.s.HFlip = (ppu.SPRRAM[i * 4 + 2] & (1 << 6)) == 0 ? 0 : 1;
+    ret.flags.s.UpperColor = ppu.SPRRAM[i * 4 + 2] & 0x03;
+    ret.flags.s.VFlip = (ppu.SPRRAM[i * 4 + 2] & (1 << 7)) == 0 ? 0 : 1;*/
+    ppu.SPRRAM[i * 4 + 2] = sprt->flags.b;
+    ppu.SPRRAM[i * 4 + 3] = sprt->x;
+}
+
+
+//(Addr & 0x1FF)    /* Addr In NT */
+// (Addr & 0xC00) >> 2        /* NT number */
+#define GetNT(a)    ( (a&0xC00) >> 10 )
+#define RelAddr(a)    (a & 0x3FF)
+#define PalAddr(a) (a & 0x1F)
+
+unsigned char PPU_Rd(unsigned short Addr)
+{
+    if (Addr > (unsigned short) 0x3FFF)
+    {
+        Addr &= 0x3FFF;
+    }
+    if ((Addr < 0x3F00) && (Addr >= 0x2000))
+    {
+        if (Addr > 0x3000)
+        {
+            Addr -= 0x1000;
+        }
+        if (ppu.ScreenType == 0)
+        {        /* 1 Screen Mode */
+            return ppu.Memory[RelAddr(Addr) + ppu.BaseOneScreen];
+        }
+        else
+            if (ppu.ScreenType)
+            {    /* Miroring mode */
+                if (ppu.MirrorDir)
+                {    /* Horizontal */
+                    if (GetNT(Addr) & 0x2)
+                    {    /* NT2-3 */
+                        return ppu.Memory[0x2000 + RelAddr(Addr)];
+                    }
+                    else
+                    {
+                        return ppu.Memory[0x2400 + RelAddr(Addr)];
+                    }
+                }
+                else
+                {    /* Vertical */
+                    if (GetNT(Addr) & 0x1)
+                    {    /* NT0-2 */
+                        return ppu.Memory[0x2400 + RelAddr(Addr)];
+                    }
+                    else
+                    {
+                        return ppu.Memory[0x2000 + RelAddr(Addr)];
+                    }
+                }
+            }
+            else
+            {    /* Four Screen mode */
+
+            }
+    }
+    else
+        if (Addr >= 0x3F00)
+        {
+            return ppu.Memory[0x3F00 | PalAddr(Addr)];
+        }
+    return ppu.Memory[Addr];
+}
+
+void PPU_Wr(unsigned short Addr, unsigned char Value)
+{
+    if (Addr > (unsigned short) 0x3FFF)
+    {
+        Addr &= 0x3FFF;
+    }
+    if ((Addr < 0x3F00) && (Addr >= 0x2000))
+    {
+        if (Addr > 0x3000)
+        {
+            Addr -= 0x1000;
+        }
+        if (ppu.ScreenType == 0)
+        {        /* 1 Screen Mode */
+            ppu.Memory[RelAddr(Addr) + 0x2000] = Value;
+        }
+        else
+            if (ppu.ScreenType == 1)
+            {    /* Miroring mode */
+                if (ppu.MirrorDir == 0)
+                {    /* Vertical */
+                    if (GetNT(Addr) & 0x1)
+                    {    /* NT0-2 */
+                        ppu.Memory[0x2400 + RelAddr(Addr)] = Value;
+                    }
+                    else
+                    {
+                        ppu.Memory[0x2000 + RelAddr(Addr)] = Value;
+                    }
+                }
+                else
+                {    /* Horizontal */
+                    if (GetNT(Addr) & 0x2)
+                    {    /* NT2-3 */
+                        ppu.Memory[0x2000 + RelAddr(Addr)] = Value;
+                    }
+                    else
+                    {
+                        ppu.Memory[0x2400 + RelAddr(Addr)] = Value;
+                    }
+                }
+            }
+            else
+            {    /* Four Screen mode */
+
+            }
+    }
+    else
+        if (Addr >= 0x3F00)
+        {
+            //printf("%s palette: color %x new value : %d (0x%x)\n", (PalAddr(Addr) < 0x10) ? "Bgnd" : "Sprt", PalAddr(Addr), Value & 0x3F, Addr);
+            ppu.Memory[ /* 0x3F00 | PalAddr(Addr) */ Addr] = Value & 0x3F;
+            if (PalAddr(Addr) == 0x10)
+                ppu.Memory[0x3F00] = Value & 0x3F;
+
+        }
+        else
+        {
+            ppu.Memory[Addr] = Value;
+        }
+}
+
+unsigned short NbOfSprite[255];
+
+
+void NewPPUDispSprite()
+{
+  int x, y, x1, y1, px, py, i;
+  unsigned long Color;
+  PPUSprite sprite;
+  unsigned short bg;
+  short SprtAddr;
+
+  for (i = 63; i >= 0; i--)
+  {
+    sprite = PPUGetSprite(i);
+    bg = sprite.flags.b & PPUSPRITE_FLAGS_BGPRIO;
+    
+    y = sprite.y;
+    if (y < 248)
+    {
+      SprtAddr = ((sprite.tileid) << 4) + ppu.Sprt_Pattern_Table;
+      x = sprite.x;
+      
+      for (y1 = 0; y1 < 8; y1++)
+      {
+    py = y + ((sprite.flags.b & PPUSPRITE_FLAGS_VFLIP) == 0 ? y1 : ((8 - 1) - y1));
+
+    if ((py > 0) && (py < 249) && ((++NbOfSprite[py]) > 7))
+    {
+      ppu.StatusRegister.b |= PPU_FLAG_SR_8SPRT ;
+      //printf("%d Hohoho!\n", py);
+      //          line(Buffer, 0, py+1, 256, py+1, 10);
+      //continue; // Do not display more than 8 sprites on this line :p
+    }
+
+    for (x1 = 0; x1 < 8; x1++)
+    {
+      px = x + ((sprite.flags.b & PPUSPRITE_FLAGS_HFLIP) != 0 ? (7 - x1) : x1);
+      Color = GetTileColor(SprtAddr, x1, y1);
+      if (Color)
+      {
+        Color = (Color) | ((sprite.flags.b & PPUSPRITE_FLAGS_UPPERCOLOR) << 2);
+        Color = ppu.Memory[0x3F10 + Color];
+        if ((i == 0) && (_getpixel8(Buffer,px,py) != BgColor) && (ppu.HitSpriteAt == 255))
+        {
+          //Ligne utilisé pour le débuguage
+          //line(Buffer, 0, py+1, 256, py+1, GetColor(10));
+          ppu.HitSpriteAt = py+1;
+        }
+        if ((bg == 0) || ((bg != 0) && (_getpixel8(Buffer,px,py) == BgColor)))
+          _putpixel8(Buffer, px, py, GetColor(Color));
+      }
+    }
+      }
+    }
+  }
+}
+
+
+void NewPPUDispSprite_8x16()
+{
+  int x, y, x1, y1, px, py, i, loop, tile;
+  unsigned long Color;
+  PPUSprite sprite;
+  unsigned short bg;
+  short SprtAddr;
+  
+  unsigned short SprtTable;
+  
+  for (i = 63; i >= 0; i--)
+  {
+    sprite = PPUGetSprite(i);
+    bg = sprite.flags.b & PPUSPRITE_FLAGS_BGPRIO;
+    tile = sprite.tileid;
+    y = sprite.y + 1;
+
+    if (y < 248)
+    {
+      if ( (SprtTable = (tile & 0x1) * 0x1000) == 0x1000)
+    tile -=1;
+
+      if ((sprite.flags.b & PPUSPRITE_FLAGS_VFLIP) != 0)
+      {
+    y +=8;
+      }
+
+      for (loop = 0; loop < 2; loop++)
+      {
+    SprtAddr = ((tile) << 4) + SprtTable;
+    x = sprite.x;
+    
+    for (y1 = 0; y1 < 8; y1++)
+    {
+      py = y + ((sprite.flags.b & PPUSPRITE_FLAGS_VFLIP) == 0 ? y1 : ((8 - 1) - y1));
+      if ((py > 0) && (py < 249) && ((++NbOfSprite[py]) > 7))
+      {
+        ppu.StatusRegister.b |= PPU_FLAG_SR_8SPRT ;
+        //        puts("Ho!");
+        //continue; // No more sprites on this line :p
+      }
+      for (x1 = 0; x1 < 8; x1++)
+      {
+        px = x + (((sprite.flags.b & PPUSPRITE_FLAGS_HFLIP) != 0) ? (7 - x1) : x1);
+        Color = GetTileColor(SprtAddr, x1, y1);
+        if (Color)
+        {
+          Color = (Color) | ((sprite.flags.b & PPUSPRITE_FLAGS_UPPERCOLOR) << 2);
+          Color = ppu.Memory[0x3F10 + Color];
+          if ((i == 0) && (_getpixel8(Buffer, px, py) != BgColor) && (ppu.HitSpriteAt == 255))
+          {
+            //Ligne utilise pour le debuguage
+            //line(Buffer, 0, py, 256, py, 10);
+            ppu.HitSpriteAt = py+1;
+          }
+          if ((bg == 0) || ((bg != 0) && (_getpixel8(Buffer, px, py) == BgColor)))
+            _putpixel8(Buffer, px, py, GetColor(Color));
+        }
+      }
+    }
+    tile += 1;
+    if ( (sprite.flags.b & PPUSPRITE_FLAGS_VFLIP) != 0)
+      y -= 8;
+    else
+      y += 8;
+      }    
+    }
+  }
+}
+
+
+void DebugColor()
+{
+  static unsigned short x = 128;
+  static unsigned short y = 128;
+  unsigned char OldDisplayPalette = ppu.DisplayPalette;
+  byte keyb;
+  unsigned int i;
+  unsigned long Color;
+
+  NOBLIT = 1;
+
+  ppu.DisplayPalette = ~0;
+
+  while(!key[KEY_ESC])
+  {
+    frame++;
+    PPUVBlank();
+
+    Color = /*Buffer->line[y][x]*/ _getpixel8(Buffer, x, y);
+
+    textprintf(Buffer, font, 5, 340, GetColor(3), "Pos [%d:%d] Color: %d Bg: %d", x, y, Color, BgColor);
+
+    line(Buffer, x-10, y, x+10, y, GetColor(1));
+    line(Buffer, x, y-10, x, y+10, GetColor(1));
+
+
+    /*
+    rect(Buffer, 0, 255, 4 * 20 + 2, 255 + 4 * 20 + 2, 0);
+    rect(Buffer, 90, 255, 90 + 4 * 20 + 2, 255 + 4 * 20 + 2, 0);
+    for (i = 0; i < 16; i++)
+    {
+      rectfill(Buffer, 1 + (i % 4) * 20, 256 + (i / 4) * 20, 1 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, ppu.Memory[0x3F00 + i]);
+      rectfill(Buffer, 91 + (i % 4) * 20, 256 + (i / 4) * 20, 91 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, ppu.Memory[0x3F10 + i]);
+    }*/
+    for( i = 0; i < 16; i++)
+    {
+      if (GetColor(ppu.Memory[0x3F00 + i]) == Color)
+      {
+    line(Buffer, 1+(i%4)*20, 256 + (i / 4) * 20, 1 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, 0xFFFFFFFF); 
+    line(Buffer, 1+(i%4)*20, 256 + (i / 4) * 20 + 20, 1 + (i % 4) * 20 + 20, 256 + (i / 4) * 20, 0xFFFFFFFF); 
+      }
+
+      if (GetColor(ppu.Memory[0x3F10 + i]) == Color)
+      {
+    line(Buffer, 91+(i%4)*20, 256 + (i / 4) * 20, 91 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, 0xFFFFFFFF); 
+    line(Buffer, 91+(i%4)*20, 256 + (i / 4) * 20 + 20, 91 + (i % 4) * 20 + 20, 256 + (i / 4) * 20, 0xFFFFFFFF); 
+      }
+       
+    }
+
+    blit(Buffer, screen, 0, 0, 0, 0, 512 + 256, 480);    
+
+    if (keypressed())
+    {
+      keyb = (readkey() & 0xFF);
+
+      if (keyb == '4')
+      {
+    x--;
+      }
+      if (keyb == '8')
+      {
+    y--;
+      }
+      if (keyb == '6')
+      {
+    x++;
+      }
+      if (keyb == '2')
+      {
+    y++;
+      }
+
+
+    }
+  }
+  ppu.DisplayPalette = OldDisplayPalette;
+  NOBLIT = 0;
+}
+
+void DebugSprites()
+{
+  byte keyb;
+  static int SelSprite = 0;
+  PPUSprite sprite;
+  NOBLIT = 1;
+  ppu.ControlRegister2.b |= PPU_CR2_SPRTVISIBILITY;
+
+  while(!key[KEY_ESC])
+  {
+    frame++;
+    PPUVBlank();
+    sprite = PPUGetSprite(SelSprite);
+
+    if (ppu.ControlRegister1.b & PPU_CR1_SPRTSIZE)
+    {
+      rect(Buffer, sprite.x-1, sprite.y-1, sprite.x+9, sprite.y+17, 1);
+    }
+    else
+    {
+      rect(Buffer, sprite.x-1, sprite.y-1, sprite.x+9, sprite.y+9, 1);
+    }
+
+    textprintf(Buffer, font, 5, 340, GetColor(3), "Sprite %d [%d:%d]", SelSprite, sprite.x, sprite.y);
+    textprintf(Buffer, font, 5, 349, GetColor(3), "B0: 0x%X  B1: 0x%X  B2: 0x%X  B3: 0x%X",sprite.y,sprite.tileid,sprite.flags.b,sprite.x);
+    textprintf(Buffer, font, 5, 358, GetColor(3), "Tile Index: %d", sprite.tileid);
+    textprintf(Buffer, font, 5, 367, GetColor(3), "Vertical Flip: %d", sprite.flags.s.VFlip);
+    textprintf(Buffer, font, 5, 376, GetColor(3), "Horizontal Flip: %d", sprite.flags.s.HFlip);
+    textprintf(Buffer, font, 5, 385, GetColor(3), "Background Priority: %d", sprite.flags.s.BgPrio);
+    textprintf(Buffer, font, 5, 394, GetColor(3), "Upper Color: %d", sprite.flags.s.UpperColor);
+
+    blit(Buffer, screen, 0, 0, 0, 0, 512 + 256, 480);    
+
+    if (keypressed())
+    {
+      keyb = (readkey() & 0xFF);
+      if (keyb == '+')
+    SelSprite = (SelSprite<63)?SelSprite+1:0;
+      if (keyb == '-')
+    SelSprite = (SelSprite>0)?SelSprite-1:63;
+
+      if (keyb == 'h')
+      {
+    sprite.flags.s.HFlip = ~sprite.flags.s.HFlip;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == 'b')
+      {
+    sprite.flags.s.BgPrio = ~sprite.flags.s.BgPrio;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == 'v')
+      {
+    sprite.flags.s.VFlip = ~sprite.flags.s.VFlip;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+
+      if (keyb == '4')
+      {
+    sprite.x--;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == '8')
+      {
+    sprite.y--;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == '6')
+      {
+    sprite.x++;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == '2')
+      {
+    sprite.y++;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+    }
+  }
+  NOBLIT = 0;
+}
+
+#define GetTilePos(addr,x,y) (addr+x+(y*32))
+
+void ppu_displayNameTables()
+{
+int x, y, x1, y1, i;
+
+unsigned long Reg2;
+unsigned short ab_x, ab_y;
+unsigned short Color;
+unsigned short AttrByte;
+unsigned short TileID;  
+
+    if (ppu.DisplayAttributeTable)
+    {
+/* NT 2000 */
+        for (x = 0; x < 0x40; x++)
+        {
+            AttrByte = /*ppu.Memory[0x23C0 + x];*/PPU_Rd(0x23C0 + x);
+            x1 = x % 8;
+            y1 = x / 8;
+            
+            
+            Color = AttrByte & 0x3; // Pattern 1;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+(x1*32),240+(y1*32),256+15+(x1*32),240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>2) & 0x3; // Pattern 2;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+(x1*32),240+(y1*32),16+256+15+(x1*32),240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>4) & 0x3; // Pattern 3;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+(x1*32),16+240+(y1*32),256+15+(x1*32),16+240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>6) & 0x3; // Pattern 4;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+(x1*32),16+240+(y1*32),16+256+15+(x1*32),16+240+15+(y1*32),Color);
+        }
+
+
+/* NT 2800 */
+        for (x = 0; x < 0x40; x++)
+         {
+            AttrByte = PPU_Rd(0x2BC0 + x);
+            x1 = x % 8;
+            y1 = x / 8;
+            
+            
+            Color = AttrByte & 0x3; // Pattern 1;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+(x1*32),(y1*32),256+15+(x1*32),15+(y1*32),Color);
+            
+            Color = (AttrByte>>2) & 0x3; // Pattern 2;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+(x1*32),(y1*32),16+256+15+(x1*32),15+(y1*32),Color);
+            
+            Color = (AttrByte>>4) & 0x3; // Pattern 3;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+(x1*32),16+(y1*32),256+15+(x1*32),16+15+(y1*32),Color);
+            
+            Color = (AttrByte>>6) & 0x3; // Pattern 4;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+(x1*32),16+(y1*32),16+256+15+(x1*32),16+15+(y1*32),Color);
+         }
+        
+/* NT 2400 */
+        for (x = 0; x < 0x40; x++)
+         {
+            AttrByte = PPU_Rd(0x27C0 + x);
+            x1 = x % 8;
+            y1 = x / 8;
+            
+            
+            Color = AttrByte & 0x3; // Pattern 1;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+256+(x1*32),240+(y1*32),256+256+15+(x1*32),240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>2) & 0x3; // Pattern 2;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+256+(x1*32),240+(y1*32),16+256+256+15+(x1*32),240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>4) & 0x3; // Pattern 3;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+256+(x1*32),240+16+(y1*32),256+256+15+(x1*32),240+16+15+(y1*32),Color);
+            
+            Color = (AttrByte>>6) & 0x3; // Pattern 4;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+256+(x1*32),240+16+(y1*32),16+256+256+15+(x1*32),240+16+15+(y1*32),Color);
+         }
+
+/* NT 2C00 */
+        for (x = 0; x < 0x40; x++)
+        {
+            AttrByte = PPU_Rd(0x2FC0 + x);//PPU_Rd(0x27C0 + x);
+            x1 = x % 8;
+            y1 = x / 8;
+            
+            
+            Color = AttrByte & 0x3; // Pattern 1;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+256+(x1*32),(y1*32),256+256+15+(x1*32),15+(y1*32),Color);
+            
+            Color = (AttrByte>>2) & 0x3; // Pattern 2;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+256+(x1*32),(y1*32),16+256+256+15+(x1*32),15+(y1*32),Color);
+            
+            Color = (AttrByte>>4) & 0x3; // Pattern 3;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+256+(x1*32),16+(y1*32),256+256+15+(x1*32),16+15+(y1*32),Color);
+            
+            Color = (AttrByte>>6) & 0x3; // Pattern 4;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+256+(x1*32),16+(y1*32),16+256+256+15+(x1*32),16+15+(y1*32),Color);
+            }
+
+        if (ppu.DisplayNameTables == 0)
+        {
+            for (x = 0; x < 33; x++)
+            {
+                line(Buffer, 256 + x * 16, 0, 256 + x * 16, 240 + 240, 8);
+                line(Buffer, 256, 0 + x * 16, 256 + 256 + 256, 0 + x * 16, 8);
+            }
+
+            for (x = 0; x < 17; x++)
+            {
+                line(Buffer, 256 + x * 32, 0, 256 + x * 32, 240 + 240, 6);
+                line(Buffer, 256, 0 + x * 32, 256 + 256 + 256, 0 + x * 32, 6);
+            }
+        }
+    }
+
+    if (ppu.DisplayNameTables)
+    {
+/* NT 2000 */
+        for (x = 0; x < 32; x++)
+            for (y = 0; y < 30; y++)
+            {
+                TileID = (PPU_Rd/*ppu.Memory[*/(0x2000 + x + (y * 32))/*]*/ << 4) + ppu.Bg_Pattern_Table;
+
+                for (x1 = 0; x1 < 8; x1++)
+                    for (y1 = 0; y1 < 8; y1++)
+                    {
+                        Color = GetTileColor(TileID, x1, y1);
+                        if (ppu.DisplayAttributeTable != 0)
+                            Color |= (Buffer->line[(8 * y) + 240 + y1][(8 * x) + 256 + x1] & 0x3) << 2;
+
+                        if ((Color != 0) || (ppu.DisplayAttributeTable != 0))
+                        {
+                            Color = ppu.Memory[0x3F00 + Color];
+                            Buffer->line[(8 * y) + 240 + y1][(8 * x) + 256 + x1] = Color;
+                        }
+                    }
+            }
+
+
+/* NT 2800 */
+        for (x = 0; x < 32; x++)
+            for (y = 0; y < 30; y++)
+            {
+                TileID = (PPU_Rd(0x2800 + x + (y * 32)) << 4) + ppu.Bg_Pattern_Table;
+
+                for (x1 = 0; x1 < 8; x1++)
+                    for (y1 = 0; y1 < 8; y1++)
+                    {
+                        Color = GetTileColor(TileID, x1, y1);
+                        if (Color != 0)
+                        {
+                            Color = ppu.Memory[0x3F00 + Color + 4];
+                            Buffer->line[(8 * y) + y1][(8 * x) + 256 + x1] = Color;
+                        }
+                    }
+            }
+
+/* NT 2400 */
+            for (x = 0; x < 32; x++)
+                for (y = 0; y < 30; y++)
+                    {
+                    TileID = (PPU_Rd(0x2400 + x + (y * 32)) << 4) + ppu.Bg_Pattern_Table;
+                    
+                    for (x1 = 0; x1 < 8; x1++)
+                        for (y1 = 0; y1 < 8; y1++)
+                            {
+                            Color = GetTileColor(TileID, x1, y1);
+                            if (ppu.DisplayAttributeTable != 0)
+                                Color |= (Buffer->line[(8 * y) + 240 + y1][(8 * x) + 256 + x1] & 0x3) << 2;
+                            
+                            if ((Color != 0) || (ppu.DisplayAttributeTable != 0))
+                                {
+                                Color = ppu.Memory[0x3F00 + Color];
+                                Buffer->line[(8 * y) + 240+ y1][(8 * x) + 256 + 256 + x1] = Color;
+                                }
+                            }
+                    }
+
+/* NT 2C00 */
+        for (x = 0; x < 32; x++)
+            for (y = 0; y < 30; y++)
+            {
+                TileID = (PPU_Rd(0x2C00 + x + (y * 32)) << 4) + ppu.Bg_Pattern_Table;
+
+                for (x1 = 0; x1 < 8; x1++)
+                    for (y1 = 0; y1 < 8; y1++)
+                    {
+                        Color = GetTileColor(TileID, x1, y1);
+                        if (Color != 0)
+                        {
+                            Color = ppu.Memory[0x3F00 + Color + 12];
+                            Buffer->line[(8 * y) + y1][(8 * x) + 256 + 256 + x1] = Color;
+                        }
+                    }
+            }
+    }
+
+}
+
+
+int
+ PPUVBlank()
+{
+int x, y, x1, y1, i;
+
+unsigned long Reg2;
+unsigned short ab_x, ab_y;
+unsigned long Color;
+unsigned short AttrByte;
+unsigned short TileID;
+static short LAST_FPS = 0;
+unsigned char XScroll, YScroll;
+
+    ppu.StatusRegister.b |= PPU_FLAG_SR_VBLANK;
+        
+    BgColor = ppu.Memory[0x3F00];//0xC0;  
+
+//goto NoDraw;
+
+    acquire_bitmap(Buffer);
+        
+    clear_to_color(Buffer, GetColor(BgColor));
+
+    if (ppu.ControlRegister2.s.Colour != 0)
+        printf("ppu.ColorEmphasis : %d\n", ppu.ControlRegister2.s.Colour);
+
+
+    for (i = 0; i < 249; i++)
+        NbOfSprite[i] = 0;
+
+    ppu.StatusRegister.b &= ~(PPU_FLAG_SR_8SPRT);
+    ppu.HitSpriteAt = 255;
+
+/*
+* A faires les choses qui faut faire durant un lancé de vblank,
+* comme dessiner par ex..
+*/
+
+
+    if (((ppu.ControlRegister2.b & PPU_CR2_BGVISIBILITY) != 0) || ppu.ForceBgVisibility)
+    {
+/* Display BG with scrolling informations */
+/* J'ai la solution :D */
+/*
+frame start (line 0) (if background or sprites are enabled):
+v=t
+*/
+        ppu.VRAMAddrReg2.W = ppu.TimedTmpPtr[0] | 0x2000;
+        //printf("Starting addresses : 0x%X\n",ppu.VRAMAddrReg2.W);
+        
+        XScroll = ppu.TimedHScroll[0];
+
+        YScroll = ppu.TmpVScroll;
+
+        for (y = 0; y < 240; y++)
+        {
+/*
+scanline start (if background and sprites are enabled):
+  8421 8421 8421 8421   8421 8421 8421 8421
+v:0000 0100 0001 1111=t:0000 0100 0001 1111
+  1111 1198 7654 3210         
+  5432 10
+*/
+            //if (y == 142)
+            //    printf("______________142 Ptr:0x%04X ____ 0x%04X\n", ppu.TimedTmpPtr[y], ppu.VRAMAddrReg2.W);
+                
+            ppu.VRAMAddrReg2.W = (ppu.VRAMAddrReg2.W & 0xFBE0)
+               | ((ppu.TimedTmpPtr[y]) & 0x041F)
+               | 0x2000;
+
+            //if (y == 142)
+            //    printf("______________142 Ptr:0x%04X ____ 0x%04X\n", ppu.TimedTmpPtr[y], ppu.VRAMAddrReg2.W);
+            
+            TileID = (PPU_Rd(ppu.VRAMAddrReg2.W) << 4)
+               | ppu.Bg_Pattern_Table;
+
+            XScroll = ppu.TimedHScroll[y];
+           
+            for (x = 0; x < 256; x++)
+            {
+/* Calculer la couleur du point */
+/* Bits 1 et 0 */
+                Color = GetTileColor(TileID, XScroll, YScroll);
+/* Bits 3 et 2 */
+/*
+XScroll : 0,1,2,3,4
+X = (ppu.VRAMAddrReg2.W & 0x1F)
+
+Y : 5,6,7,8,9
+Y = (ppu.VRAMAddrReg2.W & 0x3E0) >> 5
+*/
+/*ab_y = ((ppu.VRAMAddrReg2.W & 0x3E0) >> 5);
+                ab_x = (ppu.VRAMAddrReg2.W & 0x1F);
+
+                AttrByte = (((ab_y) >> 2) << 3) +
+                   ((ab_x) >> 2);
+
+
+                AttrByte = (PPU_Rd((ppu.VRAMAddrReg2.W & 0x2C00) + 0x3C0 + AttrByte) >>
+                        ((((ab_y & 2) * 2) + (((ppu.VRAMAddrReg2.W & 0x2C00)) & 2)))) & 0x03;*/
+                
+                /*
+                 
+                 00DC BA98 7654 3210
+                 
+                 0000 BA54 3c-2 10b-
+                 0000 0000 0001 1100  : 0x001C >> 2 
+                 0000 0011 1000 0000  : 0x0380 >> 4
+                 
+                   10 --11 11-- ----
+                 
+                 0xC000
+                 & 0x0C3F | 0x23C0
+                 
+                                b                  
+                 val >> ( (Reg2 & 0x2) | ( (Reg2 & 0x0x40)>>4)
+                 */
+                
+                Reg2 = ppu.VRAMAddrReg2.W;
+                AttrByte = ((Reg2 & 0x0380) >> 4) | ((Reg2 & 0x001C) >> 2) | (Reg2 & 0x0C00);
+                AttrByte &= 0x0C3F;
+                AttrByte |= 0x23C0;
+                AttrByte = PPU_Rd(AttrByte);
+                AttrByte = AttrByte >> ( 0x0 | (Reg2 & 0x02) | ( (Reg2 & 0x40) >> 4) );
+                AttrByte &= 0x3;
+                
+                if (Color)
+                {
+                    Color |= (AttrByte << 2);
+                    Color = ppu.Memory[0x3F00 + Color];
+                    _putpixel8(Buffer, x,y, GetColor(Color));
+                }
+                
+                XScroll++;
+                XScroll &= 7;
+                if (XScroll == 0)
+                {    /* On incrémente le compteur de tile */
+                    if ((ppu.VRAMAddrReg2.W & 0x1F) == 0x1F)
+                    {    /* On met a 0 et change
+                         * l'etat du bit 10 */
+                        ppu.VRAMAddrReg2.W &= ~0x1F;
+
+/* A voir si ya pas bcp mieux */
+                        if ((ppu.VRAMAddrReg2.W & 0x400))
+                            ppu.VRAMAddrReg2.W &= ~0x400;
+                        else
+                            ppu.VRAMAddrReg2.W |= 0x400;
+                    }
+                    else
+                    {    /* on incremente juste */
+                        ppu.VRAMAddrReg2.W = (ppu.VRAMAddrReg2.W & ~0x1F) | ((ppu.VRAMAddrReg2.W + 1) & 0x1F);
+                    }
+
+                    TileID = (PPU_Rd(ppu.VRAMAddrReg2.W) << 4)
+                             | ppu.Bg_Pattern_Table;
+                }
+            }
+/*
+you can think of bits 5,6,7,8,9 as the "y scroll"(*8).  this functions
+slightly different from the X.  it wraps to 0 and bit 11 is switched when
+it's incremented from _29_ instead of 31.  there are some odd side effects
+from this.. if you manually set the value above 29 (from either 2005 or
+2006), the wrapping from 29 obviously won't happen, and attrib data will be
+used as name table data.  the "y scroll" still wraps to 0 from 31, but
+without switching bit 11.  this explains why writing 240+ to 'Y' in 2005
+appeared as a negative scroll value.
+*/
+            YScroll++;
+            YScroll &= 7;
+            if (YScroll == 0)
+            {    /* On incrémente le compteur de tile */
+                if ((ppu.VRAMAddrReg2.W & 0x3E0) == 0x3A0)
+                {    /* On met a 0 et change l'etat du bit
+                     * 10 */
+                    ppu.VRAMAddrReg2.W &= ~0x3F0;
+
+/* A voir si ya pas bcp mieux */
+                    if ((ppu.VRAMAddrReg2.W & 0x800))
+                        ppu.VRAMAddrReg2.W &= ~0x800;
+                    else
+                        ppu.VRAMAddrReg2.W |= 0x800;
+                }
+                else
+                {    /* on incremente juste */
+                    ppu.VRAMAddrReg2.W = (ppu.VRAMAddrReg2.W & ~0x3F0) | ((((ppu.VRAMAddrReg2.W & 0x3F0) >> 5) + 1) << 5);
+                }
+            }
+        }
+        //while (!key[KEY_ENTER]);
+    }
+/*
+* if (ppu.ControlRegister2.s.SpriteVisibility == 1)
+* PPUDispSprite(0);
+*/
+
+    if (((ppu.ControlRegister2.b & PPU_CR2_SPRTVISIBILITY) != 0) || ppu.ForceSpriteVisibility)
+    {
+        if (ppu.ControlRegister1.b & PPU_CR1_SPRTSIZE)
+        {
+          NewPPUDispSprite_8x16();
+        }
+        else
+        {
+          NewPPUDispSprite();
+        }
+    }
+
+    if (ppu.DisplayPalette)
+    {
+        textout(Buffer, font, "Bg Palette", 0, 247, 5);
+        textout(Buffer, font, "Sprt Palette", 90, 247, 5);
+
+        rect(Buffer, 0, 255, 4 * 20 + 2, 255 + 4 * 20 + 2, GetColor(0));
+        rect(Buffer, 90, 255, 90 + 4 * 20 + 2, 255 + 4 * 20 + 2, GetColor(0));
+        for (i = 0; i < 16; i++)
+        {
+            rectfill(Buffer, 1 + (i % 4) * 20, 256 + (i / 4) * 20, 1 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, GetColor(ppu.Memory[0x3F00 + i]));
+            rectfill(Buffer, 91 + (i % 4) * 20, 256 + (i / 4) * 20, 91 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, GetColor(ppu.Memory[0x3F10 + i]));
+        }
+    }
+    if (ppu.DisplayVRAM)
+    {
+
+/* y:346 */
+        x1 = 0;
+        y1 = 0;
+        for (i = 0; i < 256; i++)
+        {
+            TileID = 0x0000 + (i << 4);
+
+            for (x = 0; x < 8; x++)
+                for (y = 0; y < 8; y++)
+                {
+
+                    Color = GetTileColor(TileID, x, y);
+
+                    _putpixel8(Buffer, 10 + x1 + x, 347 + y1 + y, GetColor(ppu.Memory[0x3F00 + Color]));
+
+                }
+
+
+            x1 += 8;
+            if (x1 >= 128)
+            {
+                x1 = 0;
+                y1 += 8;
+            }
+        }
+        x1 = 0;
+        y1 = 0;
+        for (i = 0; i < 256; i++)
+        {
+            TileID = 0x1000 + (i << 4);
+
+            for (x = 0; x < 8; x++)
+                for (y = 0; y < 8; y++)
+                {
+
+                    Color = GetTileColor(TileID, x, y);
+
+                    _putpixel8(Buffer, 10 + 128 + x1 + x, 347 + y1 + y, GetColor(ppu.Memory[0x3F10 + Color]) );
+
+                }
+            x1 += 8;
+            if (x1 >= 128)
+            {
+                x1 = 0;
+                y1 += 8;
+            }
+        }
+    }
+
+    for (i = 0; i < 240; i++)
+    {
+      _putpixel8(Buffer, 257 + 0, i, 0xFFFFFFFF);        
+      
+      _putpixel8(Buffer, 257 + 1, i, ppu.TimedTmpPtr[y]*4 | 0xFF000000);
+      _putpixel8(Buffer, 257 + 2, i, ppu.TimedTmpPtr[y]*4 | 0xFF000000);
+      _putpixel8(Buffer, 257 + 3, i, ppu.TimedTmpPtr[y]*4 | 0xFF000000);
+      
+      _putpixel8(Buffer, 257 + 4, i, ppu.TmpVScroll*4 | 0xFF000000);
+      _putpixel8(Buffer, 257 + 5, i, ppu.TmpVScroll*4 | 0xFF000000);
+      _putpixel8(Buffer, 257 + 6, i, ppu.TmpVScroll*4 | 0xFF000000);
+      
+      _putpixel8(Buffer, 257 + 7, i, ppu.TimedHScroll[i]*4 | 0xFF000000);
+      _putpixel8(Buffer, 257 + 8, i, ppu.TimedHScroll[i]*4 | 0xFF000000);
+      _putpixel8(Buffer, 257 + 9, i, ppu.TimedHScroll[i]*4 | 0xFF000000);
+
+      _putpixel8(Buffer, 257 + 10, i, 0xFFFFFFFF);
+      
+    }
+    
+
+    if (IRQScanHit != -1)
+    {
+        line(Buffer, 257+12, IRQScanHit, 257+22, IRQScanHit, 0xFF00FF);
+        line(Buffer, 257+12, IRQScanHit, 257+18, IRQScanHit-3, 0xFF00FF);
+        line(Buffer, 257+12, IRQScanHit, 257+18, IRQScanHit+3, 0xFF00FF);
+    }
+
+NoDraw:
+
+    textprintf(Buffer, font, 5, 340, GetColor(4), "FPS : %d   IPS : %d", FPS, IPS);
+    textprintf(Buffer, font, 5, 3, GetColor(4), "FPS : %d (CPU@~%2.2fMhz : %d%%)", FPS, (float) (((float) IPS) / 1000000.0), (int) ((((float) IPS) / 1770000.0) * 100.0));
+    
+    release_bitmap(Buffer);
+
+    if (NOBLIT == 0)
+    {
+      blit(Buffer, screen, 0, 0, 0, 0, 512 + 256, 480);
+      //stretch_blit(Buffer, screen, 0, 0, 256, 240, 0, 0, 512, 480);
+    }
+
+
+    if ((ppu.ControlRegister1.b & PPU_CR1_EXECNMI) != 0)
+        return 1;
+
+    return 0;
+}
+
+byte ReadPPUReg(byte RegID)
+{
+/* RegID is the nb of the reg 0-7 */
+    unsigned char ret;
+    RegID &= 0x07;
+    
+    switch (RegID)
+    {
+    default:
+        ret = (unsigned char) 0x00;    /* Can return every thing you
+                         * want here :D */
+        break;
+    case 1:        /* Control Register 2 */
+        ret = ppu.ControlRegister2.b;
+        break;
+    case 2:
+        ret = ppu.StatusRegister.b;
+
+        ppu.StatusRegister.b &= ~(PPU_FLAG_SR_VBLANK);
+        
+        ppu.StatusRegister.b &= ~(PPU_FLAG_SR_SPRT0);
+        ppu.VRAMAddrMode = 0;
+        break;
+    case 5:
+        ret = ppu.VRAMAddrReg2.B.l;
+        break;
+    case 6:
+        ret = ppu.VRAMAddrReg2.B.h;
+        break;
+    case 7:        /* PPU in NES is really strange.. Bufferised
+                 * send is weird.. */
+        if (ppu.VRAMAddrReg2.W < 0x3EFF)
+        {
+            ret = ppu.VRAMBuffer;
+            ppu.VRAMBuffer = PPU_Rd(ppu.VRAMAddrReg2.W);
+            ppu.VRAMAddrReg2.W += ppu.PPU_Inc;
+            ppu.VRAMAddrReg2.W &= 0x3FFF;
+        }
+        else
+        {
+          ret = PPU_Rd(ppu.VRAMAddrReg2.W);
+          ppu.VRAMAddrReg2.W += ppu.PPU_Inc;
+        }
+        break;
+    }
+    return ret;
+}
+
+void WritePPUReg(byte RegID, byte val)
+{
+/* RegID is the nb of the reg 0-7 */
+
+    RegID &= 0x07;
+
+    switch (RegID)
+    {
+        default:/* For not writeable reg */
+        printf("WritePPU error\n");
+        break;
+    case 0:        /* Control Register 1 */
+        ppu.ControlRegister1.b = val;
+        ppu.Bg_Pattern_Table = (ppu.ControlRegister1.s.BgPattern == 1) ? 0x1000 : 0x0000;
+        ppu.Sprt_Pattern_Table = (ppu.ControlRegister1.s.SptPattern == 1) ? 0x1000 : 0x0000;
+        ppu.PPU_Inc = (ppu.ControlRegister1.s.AddrIncrmt == 1) ? 32 : 1;
+
+        ppu.Name_Table_Addresse = 0x2000;
+        switch (ppu.ControlRegister1.s.NameTblAddr)
+        {
+        case 3:
+            ppu.Name_Table_Addresse += 0x400;
+        case 2:
+            ppu.Name_Table_Addresse += 0x400;
+        case 1:
+            ppu.Name_Table_Addresse += 0x400;
+        case 0:
+            break;
+        }
+            ppu.TimedNT[ScanLine] = ppu.ControlRegister1.s.NameTblAddr;
+/*
+2000 write:
+  1111 0011 1111 1111 ( F3FF )
+t:0000 1100 0000 0000 = d:0000 0011
+*/
+        ppu.TmpVRamPtr = ( (ppu.TmpVRamPtr & 0xF3FF)
+                       | ( ((ppu.ControlRegister1.s.NameTblAddr) & 0x03) << 10 )
+                         );
+
+        break;
+    case 1:        /* Control Register 2 */
+        //printf("PPU: new CR2 ; 0x%x\n", val);
+        ppu.ControlRegister2.b = val;
+        break;
+    case 3:        /* SPR-RAM Addresse Register */
+        ppu.SPR_RAMAddr = val;
+        break;
+    case 4:        /* SPR-RAM Input Register */
+        ppu.SPRRAM[ppu.SPR_RAMAddr] = val;
+        break;
+    case 5:        /* VRAM Address register 1 */
+        if (ppu.VRAMAddrMode == 0)
+        {
+/*  
+2005 first write:
+t:0000 0000 0001 1111=d:1111 1000
+x=d:00000111
+*/
+            //printf("2005[1st][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+            ppu.VRAMAddrMode = 1;
+
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0xFFE0) | ((val & 0xF8) >> 3));
+            ppu.HScroll = val & 0x7;
+
+
+            //printf("2005[1st][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+            //printf("%d -> 2005 w1: 0x%04X (val: 0x%02X)\n", ScanLine, ppu.TmpVRamPtr, val);
+        }
+        else
+        {
+/*
+2005 second write:
+
+  1111 1100 0000 0000
+  5432 1098 7654 3210
+  
+  8421 8421 8421 8421
+  -------------------
+t:0000 0011 1110 0000=d:1111 1000
+t:0111 0000 0000 0000=d:0000 0111
+*/
+            //printf("2005[2nd][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+            ppu.VRAMAddrMode = 0;
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0xFC1F) | ((val & 0xF8) << 2));
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0x8FFF) | ((val & 0x07) << 12));
+
+            ppu.TmpVScroll = (val & 0x7);
+            //if (ppu.TmpVScroll != 0)
+                //printf("2002: TmpVScroll == %d \n", ppu.TmpVScroll);
+
+            //printf("2005[2nd][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+                        
+            //printf("%d -> 2005 w2: 0x%04X (val: 0x%02X)\n", ScanLine, ppu.TmpVRamPtr, val);
+
+        }
+        break;
+    case 6:        /* VRAM Address register 2 */
+        if (ppu.VRAMAddrMode == 0)
+        {
+            //printf("2006[1st][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+            ppu.VRAMAddrMode = 1;
+/*
+2006 first write:
+t:0011 1111 0000 0000 = d:0011 1111
+t:1100 0000 0000 0000=0
+*/
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0xC0FF) | ((val&0x3F) << 8)) & 0x3FFF;
+            //printf("2006[1st][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+            
+            //printf("%d -> 2006 w1: 0x%04X (val: 0x%02X)\n", ScanLine, ppu.TmpVRamPtr, val);
+        }
+        else
+        {
+            //printf("2006[2nd][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+            ppu.VRAMAddrMode = 0;
+/*
+2006 second write:
+t:0000000011111111=d:11111111
+v=t
+*/
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0xFF00) | (val & 0x00FF));
+            ppu.VRAMAddrReg2.W = ppu.TmpVRamPtr;
+            //printf("2006[2nd][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+            //printf("%d -> 2006 w2: 0x%04X (val: 0x%02X)\n", ScanLine, ppu.TmpVRamPtr, val);
+
+        }
+        break;
+    case 7:        /* VRAM I/O */
+        PPU_Wr(ppu.VRAMAddrReg2.W, val);
+        ppu.VRAMAddrReg2.W += ppu.PPU_Inc;
+        break;
+    }
+}
+
+void FillSprRamDMA(byte value)
+{
+    int i;
+    for (i = 0x00; i < 0x100; i++)
+    {
+        ppu.SPRRAM[i] = ReadMemory( value, i);
+    }
+}

+ 997 - 0
src/ppu/ppu.c

@@ -0,0 +1,997 @@
+/*
+ *  PPU emulation - The TI-NESulator Project
+ *  ppu.c
+ *  
+ *  Define and emulate the PPU (Picture Processing Unit) of the real NES
+ * 
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-05-31 18:02:16 +0200 (jeu, 31 mai 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/ppu.c $
+ *  $Revision: 58 $
+ *
+ */
+
+#include <allegro.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define __TINES_PPU_INTERNAL__
+#include <ppu/ppu.h>
+#include <ppu/ppu.memory.h>
+#include <ppu/ppu.debug.h>
+
+#include <memory/manager.h>
+
+#define __TINES_PLUGINS__
+#include <plugins/manager.h>
+
+#if ISPAL && !ISNTSC
+//#define VBLANK_TIME 70
+extern int VBLANK_TIME;
+#elif !ISPAL && ISNTSC
+//#define VBLANK_TIME 20
+extern int VBLANK_TIME;
+#else
+#error Cannot use ISPAL with ISNTSC together !
+#endif
+
+#ifdef NO_N_KEY
+#define IF_N_KEY if (!key[KEY_N])
+#else
+#define IF_N_KEY if (key[KEY_N])
+#endif
+
+extern BITMAP *Buffer;
+
+volatile extern int frame;
+volatile extern unsigned long IPS, FPS;
+
+extern unsigned long ColorPalette[ 9 * 63 ];
+extern short IRQScanHit;
+
+BITMAP *VideoBuffer; /* The ppu will only write pixel to this, and then bliting 
+                        this on the screen "surface" */
+
+/* PPU sprite sorted by scanline */
+
+/* Work as follow: 
+
+3322 2222 2222 1111 1111 1100 0000 0000
+1098 7654 3210 9876 5432 1098 7654 3210
+---------------------------------------
+AAAA AAAA TTTT TTTT xxxx XXXX YYYY YYYY
+---------------------------------------
+8421 8421 8421 8421 8421 8421 8421 8421
+
+A = Sprite Attributes
+x = reserved
+T = Tile ID
+X = X relative position
+Y = Y absolute position
+
+x = for future use
+
+ */
+unsigned long PPU_SpriteByScanLine[241][9];        /* There is 240 scanline and 8 sprite per scanline */
+unsigned long PPU_NbSpriteByScanLine[241];         /* There is 240 scanline and 8 sprite per scanline */
+unsigned long PPU_NbSpriteByScanLineOverFlow[241]; /* There is 240 scanline and 8 sprite per scanline */
+
+#define PPU_SCANLINESPRITE_GET_ATTRS(sprt)  (((sprt)&0xFF000000) >> 24)
+#define PPU_SCANLINESPRITE_GET_TILIDX(sprt) (((sprt)&0x00FF0000) >> 16)
+#define PPU_SCANLINESPRITE_GET_RELY(sprt)   (((sprt)&0x00000F00) >>  8)
+#define PPU_SCANLINESPRITE_GET_X(sprt)       ((sprt)&0x000000FF)
+
+#define PPU_SCANLINESPRITE_SET_ATTRS(sprt, v)  sprt = (((sprt)&0x00FFFFFF) | (( (v) & 0xFF) << 24))
+#define PPU_SCANLINESPRITE_SET_TILIDX(sprt, v) sprt = (((sprt)&0xFF00FFFF) | (( (v) & 0xFF) << 16))
+#define PPU_SCANLINESPRITE_SET_RELY(sprt, v)   sprt = (((sprt)&0xFFFFF0FF) | (( (v) & 0x0F) <<  8))
+#define PPU_SCANLINESPRITE_SET_X(sprt, v)      sprt = (((sprt)&0xFFFFFF00) | (  (v) & 0xFF       ))
+
+/* PPU registers */
+
+/* NT: Name Table */
+byte PPU_Reg_NT;
+
+/* AT: Attribute/Color Table */
+byte PPU_Reg_AT;
+
+/* FV: Fine Vertical Scroll latch/counter */
+byte PPU_Reg_FV;
+
+/* HV: Fine Horizontal Scroll latch/counter */
+byte PPU_Reg_FH;
+
+/* VT: Vertical Tile indev latch/counter */
+byte PPU_Reg_VT;
+
+/* HT: Horizontal Tile indev latch/counter */
+byte PPU_Reg_HT;
+
+/* V: Vertical Name Table Selection latch/counter */
+byte PPU_Reg_V;
+
+/* H: Horizontal Name Table Selection latch/counter */
+byte PPU_Reg_H;
+
+/* S: Playfield pattern table selection latch */
+unsigned short PPU_Reg_S;
+
+/* PAR: Picture Address Register */
+byte PPU_Reg_PAR;
+
+/* AR: Tile Attribute (palette select) value latch */
+byte PPU_Reg_AR;
+
+unsigned short PPU_Reg_Counter;
+
+
+/* PPU Memory Areas */
+byte *ppu_mem_nameTables;
+byte *ppu_mem_patternTables;
+byte *ppu_mem_paletteValues;
+
+byte ppu_mem_spritesTable[0x100];
+byte ppu_mem_sptrTablePtr;
+
+
+/* Some other PPU "registers" */
+byte ppu_VramAccessFlipFlop;
+
+byte ppu_inVBlankTime;
+byte ppu_spriteZeroHit;
+byte ppu_scanlineSpriteOverflow;
+
+byte ppu_bgColor;
+
+/* CR #1 variables */
+unsigned short ppu_spritePatternTable;
+byte ppu_spriteSize;
+byte ppu_addrIncrement;
+byte ppu_execNMIonVBlank;
+
+/* CR #2 variables */
+byte ppu_spriteVisibility;
+byte ppu_backgroundVisibility;
+byte ppu_spriteClipping;
+byte ppu_backgroundClipping;
+byte ppu_displayType;
+
+byte ppu_mirrorMode;
+byte ppu_singleScreenMode;
+byte ppu_screenMode;
+
+#define PPU_MEM_PATTERNTABLES_SIZE 0x2000
+#define PPU_MEM_NAMETABLE_SIZE 0x1000
+#define PPU_MEM_PALETTEVALUES_SIZE 0x100 /* in fact its 20 but we must allocate a least one page */
+
+#define PPU_SPRITE_FLAGS_VFLIP       ( 1 << 7 )
+#define PPU_SPRITE_FLAGS_HFLIP       ( 1 << 6 )
+#define PPU_SPRITE_FLAGS_BGPRIO      ( 1 << 5 )
+#define PPU_SPRITE_FLAGS_UPPERCOLOR  ( 0x03 )
+
+#define PPU_FLAG_SR_VBLANK     ( 1 << 7 )
+#define PPU_FLAG_SR_SPRT0      ( 1 << 6 )
+#define PPU_FLAG_SR_8SPRT      ( 1 << 5 ) 
+#define PPU_FLAG_SR_RDWRALLOW  ( 1 << 4 ) 
+
+#define PPU_CR1_SPRTSIZE       ( 1 << 5 )
+#define PPU_CR1_EXECNMI        ( 1 << 7 )
+
+#define PPU_CR2_BGVISIBILITY   ( 1 << 3 )
+#define PPU_CR2_SPRTVISIBILITY ( 1 << 4 )
+
+int ppu_init()
+{
+    int i;
+    
+    if (ppu_initMemory())
+        return -1;
+    
+    /* Set ppu memory parameters */
+    
+    /* First: Allocate each memory zone */
+    ppu_mem_patternTables = (byte*) malloc(PPU_MEM_PATTERNTABLES_SIZE);
+    if (!ppu_mem_patternTables)
+        return -1;
+            
+    ppu_mem_nameTables = (byte*) malloc(PPU_MEM_NAMETABLE_SIZE);
+    if (!ppu_mem_nameTables)
+        return -1;
+        
+    ppu_mem_paletteValues = (byte*) malloc(PPU_MEM_PALETTEVALUES_SIZE);
+    if (!ppu_mem_paletteValues)
+        return -1;
+        
+    printf("ppu_mem_nameTables   :%p\n"
+           "ppu_mem_patternTables:%p\n"
+           "ppu_mem_paletteValues:%p\n",
+           ppu_mem_nameTables,
+           ppu_mem_patternTables,
+           ppu_mem_paletteValues);
+           
+    /* Second: make the ppu memory manager point on the memory zones */
+    ppu_setPagePtr8k(0x00, ppu_mem_patternTables);
+    ppu_setPagePtr4k(0x20, ppu_mem_nameTables);
+    ppu_setPagePtr  (0x3F, ppu_mem_paletteValues);  
+    
+    for ( i = 0x00; i < 0x0F; i++ )
+        ppu_setPageGhost(0x30 + i, true, 0x20 + i);
+    
+    /* Third: set registers to defaults */
+    
+    
+    /* Now test the memory ! */
+
+    /* Fille PPU memory with garbage */
+    for (i = 0x0000; i < 0x2000 ; i++)
+        ppu_mem_patternTables[i]    = rand()%0xFF;
+    for (i = 0x0000; i < 0x1000 ; i++)
+        ppu_mem_nameTables[i] = rand()%0xFF;
+    for (i = 0x0000; i < 0x001F ; i++)
+        ppu_mem_paletteValues[i] = rand()%0xFF;        
+
+    
+    /* Dump PPU memory state */
+    //ppu_memoryDumpState(stdout);
+
+    /* Set some other variables */
+    ppu_VramAccessFlipFlop = 0;
+
+    ppu_addrIncrement      = 1;
+    ppu_spritePatternTable = 0;
+    ppu_spriteSize         = 8;       
+    ppu_execNMIonVBlank    = 0;  
+
+    ppu_spriteVisibility     = 0;
+    ppu_backgroundVisibility = 0;
+    ppu_spriteClipping       = 0;
+    ppu_backgroundClipping   = 0;
+    ppu_displayType          = 0;
+
+    ppu_inVBlankTime = 0;
+    ppu_bgColor = 0;
+
+    /* Set PPU registers on CPU side */
+    set_page_rd_hook(0x20, ppu_readReg);    
+    set_page_wr_hook(0x20, ppu_writeReg);
+    
+    set_page_readable(0x20, true);
+    set_page_writeable(0x20, true);
+    
+    
+    /* Set PPU Ghost Registers */
+    for(i = 0x21; i < 0x40; i++)
+        set_page_ghost(i, true, 0x20);
+
+//    plugin_install_keypressHandler('i', ppu_debugSprites);
+//    plugin_install_keypressHandler('I', ppu_debugSprites);
+    
+//    plugin_install_keypressHandler('u', ppu_debugColor);
+//    plugin_install_keypressHandler('U', ppu_debugColor);
+      
+    /* allocate the PPU Video memory */
+    VideoBuffer = create_bitmap(256, 240);
+    
+    if (VideoBuffer == NULL)
+        return -1;
+      
+    return 0;
+}
+
+void ppu_updateSpriteScanlineTable()
+{
+    int i, line, j, k;
+    volatile int sprite_x, sprite_y, sprite_idx, sprite_attr;
+    
+    int curline;
+    
+    for (line = 0; line < 241; line ++)
+    {
+        PPU_NbSpriteByScanLine[line] = 0;
+        PPU_NbSpriteByScanLineOverFlow[line] = 0;
+        
+        for (i = 0; i < 9; i++)
+            PPU_SpriteByScanLine[line][i] = 0xFFFFFFFF;
+    }
+    
+    for ( i = 0; i < 64; i ++)
+    {
+        /* Fill sprite_zzz variables */
+        sprite_y    = ppu_mem_spritesTable[(i*4) + 0];
+        sprite_idx  = ppu_mem_spritesTable[(i*4) + 1];
+        sprite_attr = ppu_mem_spritesTable[(i*4) + 2] | ((i==0)?0x04:0); /* Add a flag for the sprite #0 */
+        sprite_x    = ppu_mem_spritesTable[(i*4) + 3];
+      
+        /* For each line covered by the sprite */
+        for (line = 0; line < ppu_spriteSize; line ++)
+        {       
+            curline = line + sprite_y;
+            
+            if ((curline < 0) || (curline > 240))
+                continue; /* Don't go beyond, this sprite go beyond the borders */
+                
+            if (PPU_NbSpriteByScanLine[curline] < 7)
+                PPU_NbSpriteByScanLine[curline] ++;
+            else
+            {
+                PPU_NbSpriteByScanLineOverFlow[curline] = 1;
+                //printf("sprite of: %d - %d\n", curline, PPU_NbSpriteByScanLine[curline]);
+                continue; /* We have 8 sprite in this line, don't continue */
+            }
+            
+            if (((sprite_x+8) < 0) && ((sprite_x-8) > 256))
+                continue; /* this sprite isn't either displayable */
+            /* Now test if this sprite can be put in the sprite list */
+            for (j = 0; j <= PPU_NbSpriteByScanLine[curline]; j++)
+            {
+                /* sprite are ordered by their y value, so, the first time that
+                   we have lower y value is where we need to put the sprite */
+                if (sprite_x < PPU_SCANLINESPRITE_GET_X(PPU_SpriteByScanLine[curline][j]))
+                {
+                    /* move the j eme item and next to the right in the list, trashing
+                       if needed the rightest item. */
+                    for (k = 7; k >= j; k--)
+                        PPU_SpriteByScanLine[curline][k] = PPU_SpriteByScanLine[curline][k-1];
+                    
+                    PPU_SpriteByScanLine[curline][j] = 0;
+                    
+                    PPU_SCANLINESPRITE_SET_ATTRS (PPU_SpriteByScanLine[curline][j], sprite_attr);
+                    //printf("new sprite [%02X:%02X:%02X:%02X] at sl:%d : 0x%08X ", 
+                    //sprite_attr, sprite_idx, curline - sprite_x, sprite_y,
+                    //curline, PPU_SpriteByScanLine[curline][j]);
+                    
+                    PPU_SCANLINESPRITE_SET_TILIDX(PPU_SpriteByScanLine[curline][j], sprite_idx);
+                    //printf("- 0x%08X ", PPU_SpriteByScanLine[curline][j]);
+                    
+                    PPU_SCANLINESPRITE_SET_RELY  (PPU_SpriteByScanLine[curline][j], curline - sprite_y);
+                    //printf("- 0x%08X ", PPU_SpriteByScanLine[curline][j]);
+                    
+                    PPU_SCANLINESPRITE_SET_X     (PPU_SpriteByScanLine[curline][j], sprite_x);                    
+                    //printf("- 0x%08X\n", PPU_SpriteByScanLine[curline][j]);
+                    
+                    break; /* Stop the for, we don't need to go further in the line list */
+                }
+            }
+        }
+    }
+}
+
+void ppu_setMirroring(byte direction)
+{
+    if (ppu_screenMode != PPU_SCMODE_NORMAL)
+        return;
+
+    if (ppu_mirrorMode == direction)
+        return; /* Same value, no need to change! */
+
+    switch(direction)
+    {
+    default:
+        direction = PPU_MIRROR_HORIZTAL;
+        ppu_mirrorMode = direction;
+        
+    case PPU_MIRROR_HORIZTAL: /* Horizontal */
+        //printf("Set mirror to Hor\n");
+        ppu_setPagePtr1k(0x20, ppu_mem_nameTables + 0x000);
+        ppu_setPagePtr1k(0x24, ppu_mem_nameTables + 0x000);
+        ppu_setPagePtr1k(0x28, ppu_mem_nameTables + 0x400);
+        ppu_setPagePtr1k(0x2C, ppu_mem_nameTables + 0x400);
+        break;
+    case PPU_MIRROR_VERTICAL: /* Vertical */
+        //printf("Set mirror to Ver\n");
+        ppu_setPagePtr1k(0x20, ppu_mem_nameTables + 0x000);
+        ppu_setPagePtr1k(0x24, ppu_mem_nameTables + 0x400);
+        ppu_setPagePtr1k(0x28, ppu_mem_nameTables + 0x000);
+        ppu_setPagePtr1k(0x2C, ppu_mem_nameTables + 0x400);
+        break;
+    }
+    ppu_mirrorMode = direction;
+}
+
+void ppu_setSingleScreen(byte screen)
+{
+    if (ppu_screenMode != PPU_SCMODE_SINGLE)
+        return;
+    if (ppu_singleScreenMode == screen)
+        return; /* Same value, no need to change! */
+       
+    switch(screen)
+    {
+    default:
+        screen = PPU_SCREEN_000;
+        ppu_singleScreenMode = screen;
+            
+    case PPU_SCREEN_000: /* 0x2000 */
+        //printf("Set screen to 0x000\n");
+        ppu_setPagePtr1k(0x20, ppu_mem_nameTables + 0x000);
+        ppu_setPagePtr1k(0x24, ppu_mem_nameTables + 0x000);
+        ppu_setPagePtr1k(0x28, ppu_mem_nameTables + 0x000);
+        ppu_setPagePtr1k(0x2C, ppu_mem_nameTables + 0x000);
+        break;
+        
+    case PPU_SCREEN_400: /* 0x2400 */
+        //printf("Set screen to 0x400\n");
+        ppu_setPagePtr1k(0x20, ppu_mem_nameTables + 0x400);
+        ppu_setPagePtr1k(0x24, ppu_mem_nameTables + 0x400);
+        ppu_setPagePtr1k(0x28, ppu_mem_nameTables + 0x400);
+        ppu_setPagePtr1k(0x2C, ppu_mem_nameTables + 0x400);
+        break;
+        
+    case PPU_SCREEN_800: /* 0x2800 */
+        //printf("Set screen to 0x800\n");
+        ppu_setPagePtr1k(0x20, ppu_mem_nameTables + 0x800);
+        ppu_setPagePtr1k(0x24, ppu_mem_nameTables + 0x800);
+        ppu_setPagePtr1k(0x28, ppu_mem_nameTables + 0x800);
+        ppu_setPagePtr1k(0x2C, ppu_mem_nameTables + 0x800);
+        break;
+        
+    case PPU_SCREEN_C00: /* 0x2C00 */
+        //printf("Set screen to 0xC00\n");
+        ppu_setPagePtr1k(0x20, ppu_mem_nameTables + 0xC00);
+        ppu_setPagePtr1k(0x24, ppu_mem_nameTables + 0xC00);
+        ppu_setPagePtr1k(0x28, ppu_mem_nameTables + 0xC00);
+        ppu_setPagePtr1k(0x2C, ppu_mem_nameTables + 0xC00);
+        break;
+    }
+    ppu_singleScreenMode = screen;
+}
+
+/* Let set display to
+   Single screen (1 NT with mirroring)
+   Normal screen (2 NT with mirroring)
+   Four   screen (4 NT without mirroring) */
+void ppu_setScreenMode(byte mode)
+{
+    if (ppu_screenMode == mode)
+        return; /* Same value, no need to change! */
+
+    ppu_screenMode = mode;
+
+    switch(mode)
+    {
+    case PPU_SCMODE_SINGLE: /* Single screen (1 NT with mirroring) */
+        //printf("Set Single Screen\n");
+        ppu_setSingleScreen(~ppu_singleScreenMode);
+        break;
+
+    default:
+        mode = PPU_SCMODE_NORMAL;
+        ppu_screenMode = mode;       
+
+    case PPU_SCMODE_NORMAL: /* Normal screen (2 NT with mirroring) */
+        //printf("Set Normal Screen\n");
+        ppu_setMirroring(~ppu_mirrorMode);
+        break;
+
+    case PPU_SCMODE_FOURSC: /* Four   screen (4 NT withou mirroring) */
+        //printf("Set Four   Screen\n");
+        ppu_setPagePtr1k(0x20, ppu_mem_nameTables + 0x000);
+        ppu_setPagePtr1k(0x24, ppu_mem_nameTables + 0x400);
+        ppu_setPagePtr1k(0x28, ppu_mem_nameTables + 0x800);
+        ppu_setPagePtr1k(0x2C, ppu_mem_nameTables + 0xC00);
+        break;
+    }
+}
+
+void ppu_setSprite(unsigned short i, PPU_Sprite *sprt)
+{
+ 
+}
+
+/* update whole counters */
+void ppu_updateCounters()
+{
+/*
++---------------+-----------------------------------------------+
+|               |+===++=++=++=====++=====++===++=++========++==+|
+|PPU registers  || FV||V||H||   VT||   HT|| FH||S||     PAR||AR||
+|PPU counters   |+---++-++-++-----++-----++===++=++========++==+|
+|               |+===++=++=++=====++=====+                      |
++---------------+-----------------------------------------------+
+|2007 access    |  DC  B  A  98765  43210                       |
++===============+===============================================+    
+
+8421 8421 8421 8421
+-------------------
+1111 1100 0000 0000
+5432 1098 7654 3210
+_AAA BCDD DDDE EEEE
+
+*/
+    PPU_Reg_Counter  = (PPU_Reg_FV & 0x07) << 12;
+    PPU_Reg_Counter |= PPU_Reg_V           << 11;
+    PPU_Reg_Counter |= PPU_Reg_H           << 10;
+    PPU_Reg_Counter |= PPU_Reg_VT          << 5;
+    PPU_Reg_Counter |= PPU_Reg_HT;
+    
+    IF_N_KEY printf("Counter update to %04X\n",PPU_Reg_Counter);
+}
+
+int ppu_hblank(int scanline)
+{  
+    int i, j;
+    byte pixelColor = 0x42;
+    byte Color = 0x42;
+    unsigned short addr;
+    byte value;
+    unsigned short tmp_HHT = 0;
+    unsigned short tmp_VVTFV = 0;  
+    
+    /* If no plan activated, we have nothing to do ! */
+    
+    if (scanline == 0)
+    {
+      ppu_bgColor =  ppu_readMemory(0x3F,00);
+      clear_to_color(VideoBuffer, ppu_bgColor);
+      
+      if ((ppu_spriteVisibility != 0) || (ppu_backgroundVisibility != 0))
+          ppu_updateCounters();
+    }
+
+    if (scanline < 240)
+    {
+            
+        /* For each PPU pixel of this scanline */        
+        for (i = 0; i < 256; i ++)
+        {    
+
+            /* determine which from sprite bg, bg and sprite fg is in the front */
+            pixelColor = ppu_readMemory(0x3F,00);
+            
+            /* is there sprite(s) on this line ? */            
+                
+            /*  */
+            /* Didn't display sprite for now, juste the BG */
+            
+            /* Read NameTable */            
+            if (ppu_backgroundVisibility == 1)
+            {
+/*
+xxxx AABB BxxC CCxx
+xxxx AA11 11BB BCCC
+*/
+/*
+s:32 i:235 cnt:089D addr:0BCF cba:0 addr:0000
+
+0000 BBBB CCCC FFFF
+0000 1100 1101 1111
+
+     AABB B  C CC
+     
+     BA98 7654 3210
+     
+xxxx 1111 1100 1111     
+     FFFF CCCC FFFF
+
+*/
+
+                addr  = (PPU_Reg_Counter & 0x0C00);
+                addr  = addr | 0x03C0;
+                addr |= (PPU_Reg_Counter >> 4 ) & 0x0038;
+                addr |= (PPU_Reg_Counter >> 2 ) & 0x0007;            
+            
+                PPU_Reg_AR = ppu_readMemory(0x20 | ((addr>>8) & 0x0F), addr& 0xFF);
+
+                
+                PPU_Reg_AR = PPU_Reg_AR >> (((PPU_Reg_Counter >> 4 ) & 0x04)|((PPU_Reg_Counter ) & 0x02));
+                PPU_Reg_AR = (PPU_Reg_AR<<2) & 0x0C;
+                    
+                PPU_Reg_PAR = ppu_readMemory(0x20 | ((PPU_Reg_Counter>>8) & 0x0F), PPU_Reg_Counter& 0xFF);
+                /* C BA98 7654 3210 */
+                /* 1 8421 8421 8421 */
+            
+                addr  =  PPU_Reg_S;
+                addr |= ((PPU_Reg_PAR & 0xFF) << 4);
+                addr |= ((PPU_Reg_Counter >> 12)  & 0x07);
+                value = ppu_readMemory((addr >> 8) , addr        );
+                Color  = (value & (1 << (7-(i + PPU_Reg_FH) % 8)))?0x01:0;
+            
+                value = ppu_readMemory((addr >> 8) , addr | 0x08 );
+                Color |= (value & (1 << (7-(i + PPU_Reg_FH) % 8)))?0x02:0;
+            
+                if (Color > 0x00)
+                {
+                
+                    Color |= PPU_Reg_AR;
+           	        Color &= 0x0F;
+
+                    pixelColor = ppu_readMemory(0x3F, Color);
+
+                }
+
+                if (((i + PPU_Reg_FH)%8) == 7)
+                {
+                    tmp_HHT = ((PPU_Reg_Counter >> 5) & 0x0020) | 
+                               (PPU_Reg_Counter & 0x001F);
+                    tmp_HHT = (tmp_HHT + 1) & 0x003F;
+                   
+                /* Reassemble with HT & H */
+        		PPU_Reg_Counter = (PPU_Reg_Counter & 0xFBE0) |
+		                          ((tmp_HHT & 0x0020) << 5)  |
+				                  (tmp_HHT & 0x001F);
+            }  
+   
+            }
+            /* draw the pixel */            
+            _putpixel(VideoBuffer, i, scanline, pixelColor);                      
+        }
+        /* Increment only V & VT & FV*/
+        
+/*
+
+8421 8421 8421 8421
+-------------------
+1111 1100 0000 0000
+5432 1098 7654 3210
+_AAA BCDD DDDE EEEE
+ 
+ xxx x xx xxx        : vvtfv = 7BE0
+      x      x xxxx  : hht
+
+        B DDDD DAAA  : vvtfv
+            CE EEEE  : hht
+
+A = FV
+B = V
+C = H
+D = VT
+E = HT
+
+
+*/       
+        if (ppu_backgroundVisibility == 1)
+        {
+
+        tmp_VVTFV = ((PPU_Reg_Counter >> 3 ) & 0x0100) | /* V */
+                    ((PPU_Reg_Counter >> 2 ) & 0x00F8) | /* VT */
+                    ((PPU_Reg_Counter >> 12) & 0x0007);  /* FV */
+        //printf("counter:%04X vvtfv:%04X ", PPU_Reg_Counter, tmp_VVTFV);
+        
+        tmp_VVTFV++;
+        //printf("__ vvtfv:0x%04X == 0x%04X ? ", tmp_VVTFV, 30<<3);
+        if ((tmp_VVTFV&0x0F8) == 0xF0)
+        {
+	        tmp_VVTFV &= ~0x0F8;
+	        tmp_VVTFV ^= 0x100;
+            //printf("YES _");
+        }
+            
+        //printf("vvtfv:%04X ", tmp_VVTFV);
+        PPU_Reg_Counter = ( PPU_Reg_Counter & 0x041F)   |        
+                          ((tmp_VVTFV & 0x0100 ) << 3 ) | /* V */
+                          ((tmp_VVTFV & 0x00F8 ) << 2 ) | /* VT */
+                          ((tmp_VVTFV & 0x0007 ) << 12);  /* FV */
+        
+        //printf("counter:%04X ", PPU_Reg_Counter);
+        /* Update H & HT */
+        PPU_Reg_Counter = (PPU_Reg_Counter & ~0x041F) | 
+                          (PPU_Reg_H << 10)           |
+                           PPU_Reg_HT;
+        }
+        
+        if (PPU_NbSpriteByScanLine[scanline] != 0)
+            {
+                for (j = 0; j < PPU_NbSpriteByScanLine[scanline]; j++)
+                {
+                    static byte i = 0;
+                    pixelColor = (i = (i+1)%4) | ((PPU_SCANLINESPRITE_GET_ATTRS(PPU_SpriteByScanLine[scanline][j]) << 2) & 0x0C);
+                    
+                    pixelColor = ppu_readMemory(0x3F, 0x10 + pixelColor);
+                    line(VideoBuffer,
+                          PPU_SCANLINESPRITE_GET_X(PPU_SpriteByScanLine[scanline][j]),
+                          scanline,
+                          PPU_SCANLINESPRITE_GET_X(PPU_SpriteByScanLine[scanline][j]) + 8,
+                          scanline, pixelColor
+                          );
+                          
+                    if (PPU_SCANLINESPRITE_GET_ATTRS(PPU_SpriteByScanLine[scanline][j]) & 0x04)
+                    {
+                        //printf("Hit!\n");
+                        ppu_spriteZeroHit = 1;
+                    }
+                          
+                }
+            }
+        ppu_scanlineSpriteOverflow = 0;
+        if (PPU_NbSpriteByScanLineOverFlow[scanline] == 1)
+            ppu_scanlineSpriteOverflow = 1;
+        
+    }
+    
+   /* if (scanline == 100)
+        ppu_spriteZeroHit = 1;*/
+    
+    if (scanline == 243)
+    {
+        ppu_inVBlankTime = 1;
+        IF_N_KEY printf("============= enter vblank =================\n");
+        return ppu_execNMIonVBlank;
+    }  
+
+    if (key[KEY_B])
+    {
+        blit(VideoBuffer, Buffer, 0, 0, 0, 0, 256, 240);
+        blit(Buffer, screen, 0, 0, 0, 0, 512 + 256, 480);
+    }
+
+
+    //if (scanline >= (241 + VBLANK_TIME))
+    if (scanline >= 262)
+    {   
+        //textprintf(Buffer, font, 5, 340, 4, "(SL:%d) FPS : %d   IPS : %d", scanline, FPS, IPS);
+        textprintf(screen, font, 260, 3, 4, "FPS : %d (CPU@~%2.2fMhz : %d%%)", FPS, (float) (((float) IPS) / 1000000.0), (int) ((((float) IPS) / 1770000.0) * 100.0));  
+        //printf("(SL:%d) FPS : %d   IPS : %d\n", scanline, FPS, IPS);
+        
+        //ppu_dumpPalette(0, 241);
+        //ppu_dumpPattern(280, 150);
+        //ppu_dumpNameTable(256,0);
+        //ppu_dumpAttributeTable(257, 0);
+                    
+        //blit(VideoBuffer, Buffer, 0, 0, 0, 0, 256, 240);
+        blit(VideoBuffer, screen, 0, 0, 0, 0, 256, 240);
+        //blit(Buffer, screen, 0, 0, 0, 0, 512 + 256, 480);
+        
+        IF_N_KEY printf("_____________ leave vblank _________________\n");
+        ppu_inVBlankTime = 0;
+        ppu_spriteZeroHit = 0;
+        //ppu_updateCounters();       
+    }
+    return 0;
+}
+
+byte PPU_RegValues[8];
+
+byte ppu_readReg(byte id)
+{
+    id &= 0x07;
+    static byte garbage;
+    static byte lastValue;
+    switch(id)
+    { 
+    default:
+        garbage = PPU_RegValues[id];
+        printf("%s: try to read 0x20%02X\n", __func__, id);
+        break;
+    case 0x02: /* PPU Status Register */
+    
+        /* Reset VRam 2005/2006 flipflop */
+        ppu_VramAccessFlipFlop = 0;
+        garbage = 0;
+        
+        garbage |= (ppu_inVBlankTime!=0)          ?PPU_FLAG_SR_VBLANK:0;
+        garbage |= (ppu_spriteZeroHit!=0)         ?PPU_FLAG_SR_SPRT0:0;
+        garbage |= (ppu_scanlineSpriteOverflow!=0)?PPU_FLAG_SR_8SPRT:0;
+        /*garbage ^= PPU_FLAG_SR_RDWRALLOW;*/
+
+        IF_N_KEY printf("%s() = %02X\n", __func__, garbage);
+
+        ppu_inVBlankTime = 0;
+        break;
+        
+    case 0x04: /* SPR-RAM I/O */
+        garbage = ppu_mem_spritesTable[ppu_mem_sptrTablePtr];       
+        break;
+                
+    case 0x07: /* VRAM I/O */           
+        if (PPU_Reg_Counter < 0x3F00)
+        {
+            garbage = lastValue;
+            lastValue = ppu_readMemory((PPU_Reg_Counter>>8) & 0x3F,
+                                       PPU_Reg_Counter & 0xFF);
+        }
+        else
+        {
+            lastValue = ppu_readMemory( 0x2F,
+                                        PPU_Reg_Counter & 0xFF);
+            garbage = ppu_readMemory( 0x3F,
+                                      PPU_Reg_Counter & 0xFF);                                     
+        }
+
+        PPU_Reg_Counter += ppu_addrIncrement;        
+
+        break;        
+    }
+    //printf("ppuread %02X return: %02X\n", id, garbage);
+    return garbage;    
+}
+
+
+void ppu_writeReg(byte id, byte val)
+{
+    id &= 0x07;
+    //printf("ppuwrte %02X val: %02X\n", id, val);
+    PPU_RegValues[id] = val;
+    switch(id)
+    {
+    default:
+        printf("%s: try to write 0x%02X @ 0x20%02X\n", __func__, val, id);
+        break;
+        
+    case 0x00: /* PPU Control Register #1 */
+    
+/*
++===============+===============================================+
+|2000           |      1  0                     4               |
++---------------+-----------------------------------------------+
+|               |+===++=++=++=====++=====++===++=++========++==+|
+|PPU registers  || FV||V||H||   VT||   HT|| FH||S||     PAR||AR||
+|PPU counters   |+---++-++-++-----++-----++===++=++========++==+|
+|               |+===++=++=++=====++=====+                      |
++---------------+-----------------------------------------------+
+*/
+         IF_N_KEY
+         printf("%s(%02X, %02X); /* 2000: "
+	     "NMI:%c SPRTSIZE:%02d BGTA:%04X[0x%04X] SPTA:%04X INC:%02d NTA:%04X */\n"
+	     , __func__, id, val,
+	     (val & 0x80)?'E':'D',
+	     (val & 0x20)?16:8,
+	     (val & 0x10)?0x1000:0x0000, PPU_Reg_S,
+	     (val & 0x08)?0x1000:0x0000,
+	     (val & 0x04)?32:1,
+	     (val & 0x03)<<10|0x2000
+	     );
+
+        /* Set PPU internal registers */
+        PPU_Reg_V = (val & 0x02)?1:0; 
+        PPU_Reg_H = (val & 0x01)?1:0;
+        PPU_Reg_S = (val & 0x10)?0x1000:0x0000;
+        
+        /* Set Other parameters */
+        ppu_addrIncrement      = (val & 0x04)?0x20:0x01;
+        ppu_spritePatternTable = (val & 0x08)?0x1000:0;
+        ppu_spriteSize         = (val & 0x20)?16:8;       
+        ppu_execNMIonVBlank    = (val & 0x80)?1:0;
+        break;
+        
+    case 0x01: /* PPU Control Register #2 */
+
+        ppu_spriteVisibility     = (val & 0x10)?1:0;
+	    ppu_backgroundVisibility = (val & 0x08)?1:0;
+	    ppu_spriteClipping       = (val & 0x04)?1:0;
+	    ppu_backgroundClipping   = (val & 0x02)?1:0;
+	    ppu_displayType          = (val & 0x01)?1:0;
+
+          IF_N_KEY
+          printf("%s(%02X, %02X); /* 2001 : "
+      	          "SprtV:%c BckgV:%c SprtC:%c BckgC:%c DispT:%c"
+                  " */\n", __func__, id, val,
+                  ppu_spriteVisibility?'y':'n',
+                  ppu_backgroundVisibility?'y':'n',
+                  ppu_spriteClipping?'y':'n',
+                  ppu_backgroundClipping?'y':'n',
+                  ppu_displayType?'m':'c'
+                  );    
+        
+        break;
+    
+    case 0x03: /* SPR-RAM Address Register */
+        ppu_mem_sptrTablePtr = val;
+        break;
+
+    case 0x04: /* SPR-RAM I/O */
+        ppu_mem_spritesTable[ppu_mem_sptrTablePtr++] = val;
+        
+        ppu_updateSpriteScanlineTable();
+        
+        break;
+        
+    case 0x05: /* 2005 VRAM Register */
+/*
++===============+===============================================+
+|2005/1         |                   76543  210                  |
+|2005/2         | 210        76543                              |   
++---------------+-----------------------------------------------+
+|               |+===++=++=++=====++=====++===++=++========++==+|
+|PPU registers  || FV||V||H||   VT||   HT|| FH||S||     PAR||AR||
+|PPU counters   |+---++-++-++-----++-----++===++=++========++==+|
+|               |+===++=++=++=====++=====+                      |
++---------------+-----------------------------------------------+
+*/
+        if (ppu_VramAccessFlipFlop == 0)
+        {
+            ppu_VramAccessFlipFlop = ~0;
+            
+            PPU_Reg_FH =  val & 0x07;
+            PPU_Reg_HT = (val & 0xF8) >> 3;
+            IF_N_KEY
+                printf("2005/1[%04X]: fv:%01X v:%01X h:%01X vt:%01X ht:%01X fh:%01X\n",val,PPU_Reg_FV,PPU_Reg_V,PPU_Reg_H,PPU_Reg_VT,PPU_Reg_HT,PPU_Reg_FH);
+        }
+        else
+        {
+            ppu_VramAccessFlipFlop = 0;
+            
+            PPU_Reg_FV =  val & 0x07;
+            PPU_Reg_VT = (val & 0xF8) >> 3;
+            IF_N_KEY
+                printf("2005/2[%04X]: fv:%01X v:%01X h:%01X vt:%01X ht:%01X fh:%01X\n",val,PPU_Reg_FV,PPU_Reg_V,PPU_Reg_H,PPU_Reg_VT,PPU_Reg_HT,PPU_Reg_FH);
+        }
+              
+        break;        
+        
+    case 0x06: /* 2006 VRAM Register */
+/*
++===============+===============================================+
+|2006/1         | -54  3  2  10                                 |
+|2006/2         |              765  43210                       |
++---------------+-----------------------------------------------+
+|               |+===++=++=++=====++=====++===++=++========++==+|
+|PPU registers  || FV||V||H||   VT||   HT|| FH||S||     PAR||AR||
+|PPU counters   |+---++-++-++-----++-----++===++=++========++==+|
+|               |+===++=++=++=====++=====+                      |
++---------------+-----------------------------------------------+
+*/
+        if (ppu_VramAccessFlipFlop == 0)
+        {
+            ppu_VramAccessFlipFlop = ~0;
+
+            PPU_Reg_FV = (val >> 4) & 0x03;            
+            PPU_Reg_V  = (val >> 3) & 0x01;
+            PPU_Reg_H  = (val >> 2) & 0x01;
+            PPU_Reg_VT =  (PPU_Reg_VT & 0x07) | ((val & 0x03) << 3);   
+            IF_N_KEY
+                printf("2006/1[%04X]: fv:%01X v:%01X h:%01X vt:%01X ht:%01X fh:%01X\n",val,PPU_Reg_FV,PPU_Reg_V,PPU_Reg_H,PPU_Reg_VT,PPU_Reg_HT,PPU_Reg_FH);
+        }
+        else
+        {
+            ppu_VramAccessFlipFlop = 0;
+            PPU_Reg_VT = (PPU_Reg_VT & 0x18) | ((val >> 5) & 0x07);
+            PPU_Reg_HT =  val & 0x1F;
+            
+            IF_N_KEY
+                printf("2006/2[%04X]: fv:%01X v:%01X h:%01X vt:%01X ht:%01X fh:%01X\n",val,PPU_Reg_FV,PPU_Reg_V,PPU_Reg_H,PPU_Reg_VT,PPU_Reg_HT,PPU_Reg_FH);
+                
+            ppu_updateCounters();            
+        }       
+        
+        break;
+    
+    case 0x07: /* VRAM I/O */
+/*
++---------------+-----------------------------------------------+
+|               |+===++=++=++=====++=====++===++=++========++==+|
+|PPU registers  || FV||V||H||   VT||   HT|| FH||S||     PAR||AR||
+|PPU counters   |+---++-++-++-----++-----++===++=++========++==+|
+|               |+===++=++=++=====++=====+                      |
++---------------+-----------------------------------------------+
+|2007 access    |  DC  B  A  98765  43210                       |
++===============+===============================================+    
+*/              
+
+        
+
+        //if ( (PPU_Reg_Counter&0xFF00) == 0x3F00)
+        //{
+       //   printf("fv:%01X v:%01X h:%01X vt:%01X ht:%01X fh:%01X\n",PPU_Reg_FV,PPU_Reg_V,PPU_Reg_H,PPU_Reg_VT,PPU_Reg_HT,PPU_Reg_FH);
+       //   printf("will write ppu: counter:%04X pa:%02X%02X v:%02X\n",
+	   //      PPU_Reg_Counter, (PPU_Reg_Counter>>8) & 0x3F, PPU_Reg_Counter & 0xFF, val);
+       // }
+        
+        ppu_writeMemory((PPU_Reg_Counter>>8) & 0x3F, PPU_Reg_Counter & 0xFF, val);       
+
+        IF_N_KEY
+        {
+            ppu_dumpPalette(0, 241);
+            ppu_dumpPattern(280, 150);
+            ppu_dumpNameTable(256,0);
+            blit(Buffer, screen, 0, 0, 0, 0, 512 + 256, 480);
+        }
+        
+        PPU_Reg_Counter += ppu_addrIncrement;        
+
+        break;        
+    }
+}
+
+void ppu_fillSprRamDMA(byte value)
+{
+    short i;
+    byte *ptr = get_page_ptr(value);
+    for (i = 0; i < 0x100; i++)
+    {
+        ppu_mem_spritesTable[(ppu_mem_sptrTablePtr + i)&0xFF] = *(ptr+i);
+    }
+    //memcpy(ppu_mem_spritesTable, ptr, 0xFF);
+    ppu_updateSpriteScanlineTable();
+}

+ 181 - 0
src/ppu/ppu.memory.c

@@ -0,0 +1,181 @@
+/*
+ *  PPU Memory manager - The TI-NESulator Project
+ *  ppu.memory.c - Inspired from the memory manager of the Quick6502 Project.
+ *
+ *  Created by Manoël Trapier on 12/04/07.
+ *  Copyright 2003-2007 986 Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-05-24 15:11:55 +0200 (jeu, 24 mai 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/ppu/ppu.memory.c $
+ *  $Revision: 53 $
+ *
+ */
+
+#ifndef PPU_MEMORY_H
+#define PPU_MEMORY_H
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define __TINES_PPU_INTERNAL__
+
+#include <ppu/ppu.h>
+#include <ppu/ppu.memory.h>
+
+#include <types.h>
+
+/* Simple definition only for readability */
+#define KBYTE * (1024)
+
+/* Internal representation of the PPU memory */ 
+byte *ppu_memoryPages[0x40];
+
+byte ppu_memoryGhostLink[0x40];
+ 
+/* Internal PPU Sprite Ram */
+byte ppu_SpriteRam[0x100];
+ 
+/* 
+ * Memory management functions 
+ *
+ * Yes that true, PPU memory & CPU memory work in a nearly same fashion depite
+ * the fact that we actually didn't have any Read/Write hook and ReadWrite
+ * protection. We even didn't need "attributes" for the page. One of the only
+ * need is the "powerful" ghost system
+ */
+
+int ppu_initMemory()
+{
+    int page; 
+    for(page = 0 ; page < 0x40 ; page++)
+    {
+        ppu_setPagePtr(page,NULL);
+        ppu_memoryGhostLink[page] = 0xFF; /* ( >= 0x40 is not possible) */
+    }
+    return 0;
+}
+
+void ppu_updateGhost(byte page)
+{    
+    byte cur_ghost;
+
+    cur_ghost = ppu_memoryGhostLink[page];
+    if (cur_ghost < 0x40)
+        ppu_memoryPages[cur_ghost] = ppu_memoryPages[page];
+}
+
+void ppu_setPagePtr  (byte page, byte *ptr)
+{
+    ppu_memoryPages[page] = ptr;
+    ppu_updateGhost(page);
+}
+
+void ppu_setPagePtr1k(byte page, byte *ptr)
+{   /* 1k = 4 * 256 */
+    ppu_memoryPages[page + 0] = ptr;
+    ppu_memoryPages[page + 1] = ptr +  0x100;
+    ppu_memoryPages[page + 2] = ptr + (0x100 * 2);
+    ppu_memoryPages[page + 3] = ptr + (0x100 * 3);
+    
+    ppu_updateGhost(page + 0);
+    ppu_updateGhost(page + 1);
+    ppu_updateGhost(page + 2);
+    ppu_updateGhost(page + 3);
+}
+
+void ppu_setPagePtr2k(byte page, byte *ptr)
+{
+    ppu_memoryPages[page + 0] = ptr;
+    ppu_memoryPages[page + 1] = ptr +  0x100;
+    ppu_memoryPages[page + 2] = ptr + (0x100 * 2);
+    ppu_memoryPages[page + 3] = ptr + (0x100 * 3);
+    ppu_memoryPages[page + 4] = ptr + (0x100 * 4);
+    ppu_memoryPages[page + 5] = ptr + (0x100 * 5);
+    ppu_memoryPages[page + 6] = ptr + (0x100 * 6);
+    ppu_memoryPages[page + 7] = ptr + (0x100 * 7);
+    
+    ppu_updateGhost(page + 0);
+    ppu_updateGhost(page + 1);
+    ppu_updateGhost(page + 2);
+    ppu_updateGhost(page + 3);
+    ppu_updateGhost(page + 4);
+    ppu_updateGhost(page + 5);
+    ppu_updateGhost(page + 6);
+    ppu_updateGhost(page + 7);
+}
+
+void ppu_setPagePtr4k(byte page, byte *ptr)
+{
+    ppu_setPagePtr2k(page, ptr);
+    ppu_setPagePtr2k(page+((4 KBYTE / 256) / 2), ptr + 2 KBYTE);    
+}
+
+void ppu_setPagePtr8k(byte page, byte *ptr)
+{
+    ppu_setPagePtr4k(page, ptr);
+    ppu_setPagePtr4k(page+((8 KBYTE / 256) / 2), ptr + 4 KBYTE);        
+}
+
+void ppu_setPageGhost(byte page, bool value, byte ghost)
+{
+    if (value == true)
+    {  
+        ppu_memoryPages[page] = ppu_memoryPages[ghost];        
+        ppu_memoryGhostLink[ghost] = page;
+        printf("set ghost of 0x%02X to 0x%02X (ptr: %p)\n", ghost, page, ppu_memoryGhostLink[ghost]);
+    }
+}
+
+void ppu_memoryDumpState(FILE *fp)
+{
+    int i;
+    for (i = 0x00; i < 0x40; i++)
+    {
+        fprintf(fp,
+                "Page 0x%02X : ptr:%p ghost:0x%02X\n",
+                i,
+                ppu_memoryPages[i],
+                ppu_memoryGhostLink[i]
+                );
+    }
+}
+
+byte ppu_readMemory(byte page, byte addr)
+{
+    byte *ptr;
+    if (page == 0x3F)
+        return ( ppu_memoryPages[0x3F][addr&0x1F] & 0x3F );
+        
+    ptr = ppu_memoryPages[page & 0x3F];
+    return ptr[addr];
+}
+
+void ppu_writeMemory(byte page, byte addr, byte value)
+{
+    byte *ptr;
+    if (page == 0x3F)
+    {
+        /* Here we will cheat with the palette miroring, since we didn't write
+           as often as we read the palette, we will mirror here */
+        //printf("%s palette: color %02X new value : %02d (0x%02X%02X)\n", ((addr&0x10)< 0x10) ? "Bgnd" : "Sprt", addr&0x1F, value & 0x3F, page, addr);
+        if ((addr & 0x10) == 0x00)
+        {
+            ppu_memoryPages[0x3F][addr&0x1F] = value;
+            ppu_memoryPages[0x3F][(addr&0x1F) | 0x10] = value;
+        }
+        else
+        {
+            ppu_memoryPages[0x3F][addr&0x1F] = value;
+            if (( addr & 0x1F ) == 0x10 )
+            ppu_memoryPages[0x3F][(addr&0x1F) & 0xEF] = value;
+        }
+    }
+    else
+    {
+        ptr = ppu_memoryPages[page & 0x3F];
+        ptr[addr] = value;
+    }
+}
+
+#endif

+ 1258 - 0
src/ppu/ppu.new.c

@@ -0,0 +1,1258 @@
+/*
+ *  PPU emulation - The TI-NESulator Project
+ *  ppu.c
+ *  
+ *  Define and emulate the PPU (Picture Processing Unit) of the real NES
+ * 
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-04-04 18:46:30 +0200 (mer, 04 avr 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/ppu.c $
+ *  $Revision: 30 $
+ *
+ */
+
+#include <allegro.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <time.h>
+
+
+#include "ppu.h"
+#include "M6502.h"
+
+#define GetTileColor(tile,x1,y1)    ( ( ppu.Memory[tile+y1] & (1<<(7-x1)) ) == 0 ? 0 : 1 ) | \
+                                    ( ( ppu.Memory[tile+y1+8] & (1<<(7-x1)) ) == 0 ? 0 : 1<<1 )
+
+extern PPU ppu;
+extern BITMAP *Buffer;
+extern unsigned short ScanLine;
+unsigned char BgColor;
+
+volatile extern int frame;
+
+volatile extern unsigned long IPS, FPS;
+
+byte NOBLIT = 0;
+
+int InitPPU(PPU * ppu)
+{
+    if ((ppu->Memory = (unsigned char *) malloc(0x4000)) == NULL)
+        return -1;
+
+    NOBLIT = 0;
+
+    
+
+    /* Initializing register.. */
+    ppu->In_VBlank = 0;
+    
+    ppu->BaseOneScreen = 0x2000;
+    
+    ppu->ControlRegister1.b = 0;
+    ppu->ControlRegister2.b = 0;
+    ppu->StatusRegister.b = 0;
+    ppu->SPR_RAMAddr = 0;
+    ppu->VRAMAddrReg1.W = 0;
+    ppu->VRAMAddrReg2.W = 0;
+    ppu->VRAMAddrMode = 0;
+
+    ppu->Bg_Pattern_Table = 0x0000;
+    ppu->Name_Table_Addresse = 0x2000;
+    ppu->Sprt_Pattern_Table = 0x0000;
+    ppu->PPU_Inc = 1;
+
+    ppu->MirrorDir = 0;
+    ppu->ScreenType = 1;
+
+
+    ppu->ForceBgVisibility = 0;
+    ppu->ForceSpriteVisibility = 0;
+
+    ppu->DisplayNameTables = ~0;
+    ppu->DisplayAttributeTable = ~0;
+    ppu->DisplayPalette = ~0;
+    ppu->DisplayVRAM = ~0;
+
+    return 0;
+}
+
+PPUSprite PPUGetSprite(unsigned short i)
+{
+PPUSprite ret;
+
+    ret.y = ppu.SPRRAM[i * 4];
+    ret.tileid = ppu.SPRRAM[i * 4 + 1];
+    /*ret.flags.s.BgPrio = (ppu.SPRRAM[i * 4 + 2] & (1 << 5)) == 0 ? 0 : 1;
+    ret.flags.s.HFlip = (ppu.SPRRAM[i * 4 + 2] & (1 << 6)) == 0 ? 0 : 1;
+    ret.flags.s.UpperColor = ppu.SPRRAM[i * 4 + 2] & 0x03;
+    ret.flags.s.VFlip = (ppu.SPRRAM[i * 4 + 2] & (1 << 7)) == 0 ? 0 : 1;*/
+    ret.flags.b = ppu.SPRRAM[i * 4 + 2];
+    ret.x = ppu.SPRRAM[i * 4 + 3];
+    return ret;
+}
+
+void PPUSetSprite(unsigned short i, PPUSprite *sprt)
+{
+    ppu.SPRRAM[i * 4] = sprt->y;
+    ppu.SPRRAM[i * 4 + 1] = sprt->tileid;
+    /*ret.flags.s.BgPrio = (ppu.SPRRAM[i * 4 + 2] & (1 << 5)) == 0 ? 0 : 1;
+    ret.flags.s.HFlip = (ppu.SPRRAM[i * 4 + 2] & (1 << 6)) == 0 ? 0 : 1;
+    ret.flags.s.UpperColor = ppu.SPRRAM[i * 4 + 2] & 0x03;
+    ret.flags.s.VFlip = (ppu.SPRRAM[i * 4 + 2] & (1 << 7)) == 0 ? 0 : 1;*/
+    ppu.SPRRAM[i * 4 + 2] = sprt->flags.b;
+    ppu.SPRRAM[i * 4 + 3] = sprt->x;
+}
+
+
+//(Addr & 0x1FF)    /* Addr In NT */
+// (Addr & 0xC00) >> 2        /* NT number */
+#define GetNT(a)    ( (a&0xC00) >> 10 )
+#define RelAddr(a)    (a & 0x3FF)
+#define PalAddr(a) (a & 0x1F)
+
+unsigned char PPU_Rd(unsigned short Addr)
+{
+    if (Addr > (unsigned short) 0x3FFF)
+    {
+        Addr &= 0x3FFF;
+    }
+    if ((Addr < 0x3F00) && (Addr >= 0x2000))
+    {
+        if (Addr > 0x3000)
+        {
+            Addr -= 0x1000;
+        }
+        if (ppu.ScreenType == 0)
+        {        /* 1 Screen Mode */
+            return ppu.Memory[RelAddr(Addr) + ppu.BaseOneScreen];
+        }
+        else
+            if (ppu.ScreenType)
+            {    /* Miroring mode */
+                if (ppu.MirrorDir)
+                {    /* Horizontal */
+                    if (GetNT(Addr) & 0x2)
+                    {    /* NT2-3 */
+                        return ppu.Memory[0x2000 + RelAddr(Addr)];
+                    }
+                    else
+                    {
+                        return ppu.Memory[0x2400 + RelAddr(Addr)];
+                    }
+                }
+                else
+                {    /* Vertical */
+                    if (GetNT(Addr) & 0x1)
+                    {    /* NT0-2 */
+                        return ppu.Memory[0x2400 + RelAddr(Addr)];
+                    }
+                    else
+                    {
+                        return ppu.Memory[0x2000 + RelAddr(Addr)];
+                    }
+                }
+            }
+            else
+            {    /* Four Screen mode */
+
+            }
+    }
+    else
+        if (Addr >= 0x3F00)
+        {
+            return ppu.Memory[0x3F00 | PalAddr(Addr)];
+        }
+    return ppu.Memory[Addr];
+}
+
+void PPU_Wr(unsigned short Addr, unsigned char Value)
+{
+    if (Addr > (unsigned short) 0x3FFF)
+    {
+        Addr &= 0x3FFF;
+    }
+    if ((Addr < 0x3F00) && (Addr >= 0x2000))
+    {
+        if (Addr > 0x3000)
+        {
+            Addr -= 0x1000;
+        }
+        if (ppu.ScreenType == 0)
+        {        /* 1 Screen Mode */
+            ppu.Memory[RelAddr(Addr) + 0x2000] = Value;
+        }
+        else
+            if (ppu.ScreenType == 1)
+            {    /* Miroring mode */
+                if (ppu.MirrorDir == 0)
+                {    /* Vertical */
+                    if (GetNT(Addr) & 0x1)
+                    {    /* NT0-2 */
+                        ppu.Memory[0x2400 + RelAddr(Addr)] = Value;
+                    }
+                    else
+                    {
+                        ppu.Memory[0x2000 + RelAddr(Addr)] = Value;
+                    }
+                }
+                else
+                {    /* Horizontal */
+                    if (GetNT(Addr) & 0x2)
+                    {    /* NT2-3 */
+                        ppu.Memory[0x2000 + RelAddr(Addr)] = Value;
+                    }
+                    else
+                    {
+                        ppu.Memory[0x2400 + RelAddr(Addr)] = Value;
+                    }
+                }
+            }
+            else
+            {    /* Four Screen mode */
+
+            }
+    }
+    else
+        if (Addr >= 0x3F00)
+        {
+            //printf("%s palette: color %x new value : %d (0x%x)\n", (PalAddr(Addr) < 0x10) ? "Bgnd" : "Sprt", PalAddr(Addr), Value & 0x3F, Addr);
+            ppu.Memory[ /* 0x3F00 | PalAddr(Addr) */ Addr] = Value & 0x3F;
+            if (PalAddr(Addr) == 0x10)
+                ppu.Memory[0x3F00] = Value & 0x3F;
+
+        }
+        else
+        {
+            ppu.Memory[Addr] = Value;
+        }
+}
+
+unsigned short NbOfSprite[255];
+
+
+void NewPPUDispSprite()
+{
+  int x, y, x1, y1, px, py, i;
+  char Color;
+  PPUSprite sprite;
+  unsigned short bg;
+  short SprtAddr;
+
+  for (i = 63; i >= 0; i--)
+  {
+    sprite = PPUGetSprite(i);
+    bg = sprite.flags.b & PPUSPRITE_FLAGS_BGPRIO;
+    
+    y = sprite.y;
+    if (y < 248)
+    {
+      SprtAddr = ((sprite.tileid) << 4) + ppu.Sprt_Pattern_Table;
+      x = sprite.x;
+      
+      for (y1 = 0; y1 < 8; y1++)
+      {
+    py = y + ((sprite.flags.b & PPUSPRITE_FLAGS_VFLIP) == 0 ? y1 : ((8 - 1) - y1));
+
+    if ((py > 0) && (py < 249) && ((++NbOfSprite[py]) > 7))
+    {
+      ppu.StatusRegister.b |= PPU_FLAG_SR_8SPRT ;
+      //printf("%d Hohoho!\n", py);
+      //          line(Buffer, 0, py+1, 256, py+1, 10);
+      //continue; // Do not display more than 8 sprites on this line :p
+    }
+
+    for (x1 = 0; x1 < 8; x1++)
+    {
+      px = x + ((sprite.flags.b & PPUSPRITE_FLAGS_HFLIP) != 0 ? (7 - x1) : x1);
+      Color = GetTileColor(SprtAddr, x1, y1);
+      if (Color)
+      {
+        Color = (Color) | ((sprite.flags.b & PPUSPRITE_FLAGS_UPPERCOLOR) << 2);
+        Color = ppu.Memory[0x3F10 + Color];
+        if ((i == 0) && (Buffer->line[py][px] != BgColor) && (ppu.HitSpriteAt == 255))
+        {
+          //Ligne utilisé pour le débuguage
+          //line(Buffer, 0, py+1, 256, py+1, 10);
+          ppu.HitSpriteAt = py+1;
+        }
+        if ((bg == 0) || ((bg != 0) && (Buffer->line[py][px] == BgColor)))
+          putpixel(Buffer, px, py, Color);
+      }
+    }
+      }
+    }
+  }
+}
+
+
+void NewPPUDispSprite_8x16()
+{
+  int x, y, x1, y1, px, py, i, loop, tile;
+  char Color;
+  PPUSprite sprite;
+  unsigned short bg;
+  short SprtAddr;
+  
+  unsigned short SprtTable;
+  
+  for (i = 63; i >= 0; i--)
+  {
+    sprite = PPUGetSprite(i);
+    bg = sprite.flags.b & PPUSPRITE_FLAGS_BGPRIO;
+    tile = sprite.tileid;
+    y = sprite.y + 1;
+
+    if (y < 248)
+    {
+      if ( (SprtTable = (tile & 0x1) * 0x1000) == 0x1000)
+    tile -=1;
+
+      if ((sprite.flags.b & PPUSPRITE_FLAGS_VFLIP) != 0)
+      {
+    y +=8;
+      }
+
+      for (loop = 0; loop < 2; loop++)
+      {
+    SprtAddr = ((tile) << 4) + SprtTable;
+    x = sprite.x;
+    
+    for (y1 = 0; y1 < 8; y1++)
+    {
+      py = y + ((sprite.flags.b & PPUSPRITE_FLAGS_VFLIP) == 0 ? y1 : ((8 - 1) - y1));
+      if ((py > 0) && (py < 249) && ((++NbOfSprite[py]) > 7))
+      {
+        ppu.StatusRegister.b |= PPU_FLAG_SR_8SPRT ;
+        //        puts("Ho!");
+        //continue; // No more sprites on this line :p
+      }
+      for (x1 = 0; x1 < 8; x1++)
+      {
+        px = x + (((sprite.flags.b & PPUSPRITE_FLAGS_HFLIP) != 0) ? (7 - x1) : x1);
+        Color = GetTileColor(SprtAddr, x1, y1);
+        if (Color)
+        {
+          Color = (Color) | ((sprite.flags.b & PPUSPRITE_FLAGS_UPPERCOLOR) << 2);
+          Color = ppu.Memory[0x3F10 + Color];
+          if ((i == 0) && (Buffer->line[py][px] != BgColor) && (ppu.HitSpriteAt == 255))
+          {
+        //Ligne utilise pour le debuguage
+        //line(Buffer, 0, py, 256, py, 10);
+        ppu.HitSpriteAt = py+1;
+          }
+          if ((bg == 0) || ((bg != 0) && (Buffer->line[py][px] == BgColor)))
+        putpixel(Buffer, px, py, Color);
+        }
+      }
+    }
+    tile += 1;
+    if ( (sprite.flags.b & PPUSPRITE_FLAGS_VFLIP) != 0)
+      y -= 8;
+    else
+      y += 8;
+      }    
+    }
+  }
+}
+
+
+void DebugColor()
+{
+  static unsigned short x = 128;
+  static unsigned short y = 128;
+  unsigned char OldDisplayPalette = ppu.DisplayPalette;
+  byte keyb;
+  unsigned int i;
+  unsigned short Color;
+
+  NOBLIT = 1;
+
+  ppu.DisplayPalette = ~0;
+
+  while(!key[KEY_ESC])
+  {
+    frame++;
+    PPUVBlank();
+
+    Color = Buffer->line[y][x];
+
+    textprintf(Buffer, font, 5, 340, 3, "Pos [%d:%d] Color: %d Bg: %d", x, y, Color, BgColor);
+
+    line(Buffer, x-10, y, x+10, y, 1);
+    line(Buffer, x, y-10, x, y+10, 1);
+
+
+    /*
+    rect(Buffer, 0, 255, 4 * 20 + 2, 255 + 4 * 20 + 2, 0);
+    rect(Buffer, 90, 255, 90 + 4 * 20 + 2, 255 + 4 * 20 + 2, 0);
+    for (i = 0; i < 16; i++)
+    {
+      rectfill(Buffer, 1 + (i % 4) * 20, 256 + (i / 4) * 20, 1 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, ppu.Memory[0x3F00 + i]);
+      rectfill(Buffer, 91 + (i % 4) * 20, 256 + (i / 4) * 20, 91 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, ppu.Memory[0x3F10 + i]);
+    }*/
+    for( i = 0; i < 16; i++)
+    {
+      if (ppu.Memory[0x3F00 + i] == Color)
+      {
+    line(Buffer, 1+(i%4)*20, 256 + (i / 4) * 20, 1 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, ~Color); 
+    line(Buffer, 1+(i%4)*20, 256 + (i / 4) * 20 + 20, 1 + (i % 4) * 20 + 20, 256 + (i / 4) * 20, ~Color); 
+      }
+
+      if (ppu.Memory[0x3F10 + i] == Color)
+      {
+    line(Buffer, 91+(i%4)*20, 256 + (i / 4) * 20, 91 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, ~Color); 
+    line(Buffer, 91+(i%4)*20, 256 + (i / 4) * 20 + 20, 91 + (i % 4) * 20 + 20, 256 + (i / 4) * 20, ~Color); 
+      }
+       
+    }
+
+    blit(Buffer, screen, 0, 0, 0, 0, 512 + 256, 480);    
+
+    if (keypressed())
+    {
+      keyb = (readkey() & 0xFF);
+
+      if (keyb == '4')
+      {
+    x--;
+      }
+      if (keyb == '8')
+      {
+    y--;
+      }
+      if (keyb == '6')
+      {
+    x++;
+      }
+      if (keyb == '2')
+      {
+    y++;
+      }
+
+
+    }
+  }
+  ppu.DisplayPalette = OldDisplayPalette;
+  NOBLIT = 0;
+}
+
+void DebugSprites()
+{
+  byte keyb;
+  static int SelSprite = 0;
+  PPUSprite sprite;
+  NOBLIT = 1;
+  ppu.ControlRegister2.b |= PPU_CR2_SPRTVISIBILITY;
+
+  while(!key[KEY_ESC])
+  {
+    frame++;
+    PPUVBlank();
+    sprite = PPUGetSprite(SelSprite);
+
+    if (ppu.ControlRegister1.b & PPU_CR1_SPRTSIZE)
+    {
+      rect(Buffer, sprite.x-1, sprite.y-1, sprite.x+9, sprite.y+17, 1);
+    }
+    else
+    {
+      rect(Buffer, sprite.x-1, sprite.y-1, sprite.x+9, sprite.y+9, 1);
+    }
+
+    textprintf(Buffer, font, 5, 340, 3, "Sprite %d [%d:%d]", SelSprite, sprite.x, sprite.y);
+    textprintf(Buffer, font, 5, 349, 3, "B0: 0x%X  B1: 0x%X  B2: 0x%X  B3: 0x%X",sprite.y,sprite.tileid,sprite.flags.b,sprite.x);
+    textprintf(Buffer, font, 5, 358, 3, "Tile Index: %d", sprite.tileid);
+    textprintf(Buffer, font, 5, 367, 3, "Vertical Flip: %d", sprite.flags.s.VFlip);
+    textprintf(Buffer, font, 5, 376, 3, "Horizontal Flip: %d", sprite.flags.s.HFlip);
+    textprintf(Buffer, font, 5, 385, 3, "Background Priority: %d", sprite.flags.s.BgPrio);
+    textprintf(Buffer, font, 5, 394, 3, "Upper Color: %d", sprite.flags.s.UpperColor);
+
+    blit(Buffer, screen, 0, 0, 0, 0, 512 + 256, 480);    
+
+    if (keypressed())
+    {
+      keyb = (readkey() & 0xFF);
+      if (keyb == '+')
+    SelSprite = (SelSprite<63)?SelSprite+1:0;
+      if (keyb == '-')
+    SelSprite = (SelSprite>0)?SelSprite-1:63;
+
+      if (keyb == 'h')
+      {
+    sprite.flags.s.HFlip = ~sprite.flags.s.HFlip;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == 'b')
+      {
+    sprite.flags.s.BgPrio = ~sprite.flags.s.BgPrio;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == 'v')
+      {
+    sprite.flags.s.VFlip = ~sprite.flags.s.VFlip;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+
+      if (keyb == '4')
+      {
+    sprite.x--;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == '8')
+      {
+    sprite.y--;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == '6')
+      {
+    sprite.x++;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == '2')
+      {
+    sprite.y++;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+    }
+  }
+  NOBLIT = 0;
+}
+
+
+int
+ PPUVBlank()
+{
+int x, y, x1, y1, i;
+
+unsigned long Reg2;
+unsigned short ab_x, ab_y;
+unsigned short Color;
+unsigned short AttrByte;
+unsigned short TileID;
+static short WaitTime = 7000;
+static short LAST_FPS = 0;
+unsigned char XScroll, YScroll;
+struct timeval timeStart, timeEnd;
+
+    ppu.StatusRegister.b |= PPU_FLAG_SR_VBLANK;
+        
+    BgColor = ppu.Memory[0x3F00];//0xC0;
+    
+    gettimeofday(&timeStart, NULL);
+
+//goto NoDraw;
+
+    acquire_bitmap(Buffer);
+        
+    clear_to_color(Buffer, BgColor);
+
+/*    if (ppu.ControlRegister2.s.Colour != 0)
+        printf("ppu.ColorEmphasis : %d", ppu.ControlRegister2.s.Colour);*/
+
+
+    for (i = 0; i < 249; i++)
+        NbOfSprite[i] = 0;
+
+    ppu.StatusRegister.b &= ~(PPU_FLAG_SR_8SPRT);
+    ppu.HitSpriteAt = 255;
+
+/*
+* A faires les choses qui faut faire durant un lancé de vblank,
+* comme dessiner par ex..
+*/
+
+#define GetTilePos(addr,x,y) (addr+x+(y*32))
+
+    if (ppu.DisplayAttributeTable)
+    {
+/* NT 2000 */
+        for (x = 0; x < 0x40; x++)
+        {
+            AttrByte = /*ppu.Memory[0x23C0 + x];*/PPU_Rd(0x23C0 + x);
+            x1 = x % 8;
+            y1 = x / 8;
+            
+            
+            Color = AttrByte & 0x3; // Pattern 1;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+(x1*32),240+(y1*32),256+15+(x1*32),240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>2) & 0x3; // Pattern 2;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+(x1*32),240+(y1*32),16+256+15+(x1*32),240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>4) & 0x3; // Pattern 3;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+(x1*32),16+240+(y1*32),256+15+(x1*32),16+240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>6) & 0x3; // Pattern 4;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+(x1*32),16+240+(y1*32),16+256+15+(x1*32),16+240+15+(y1*32),Color);
+        }
+
+
+/* NT 2800 */
+        for (x = 0; x < 0x40; x++)
+         {
+            AttrByte = PPU_Rd(0x2BC0 + x);
+            x1 = x % 8;
+            y1 = x / 8;
+            
+            
+            Color = AttrByte & 0x3; // Pattern 1;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+(x1*32),(y1*32),256+15+(x1*32),15+(y1*32),Color);
+            
+            Color = (AttrByte>>2) & 0x3; // Pattern 2;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+(x1*32),(y1*32),16+256+15+(x1*32),15+(y1*32),Color);
+            
+            Color = (AttrByte>>4) & 0x3; // Pattern 3;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+(x1*32),16+(y1*32),256+15+(x1*32),16+15+(y1*32),Color);
+            
+            Color = (AttrByte>>6) & 0x3; // Pattern 4;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+(x1*32),16+(y1*32),16+256+15+(x1*32),16+15+(y1*32),Color);
+         }
+        
+/* NT 2400 */
+        for (x = 0; x < 0x40; x++)
+         {
+            AttrByte = PPU_Rd(0x27C0 + x);
+            x1 = x % 8;
+            y1 = x / 8;
+            
+            
+            Color = AttrByte & 0x3; // Pattern 1;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+256+(x1*32),240+(y1*32),256+256+15+(x1*32),240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>2) & 0x3; // Pattern 2;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+256+(x1*32),240+(y1*32),16+256+256+15+(x1*32),240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>4) & 0x3; // Pattern 3;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+256+(x1*32),240+16+(y1*32),256+256+15+(x1*32),240+16+15+(y1*32),Color);
+            
+            Color = (AttrByte>>6) & 0x3; // Pattern 4;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+256+(x1*32),240+16+(y1*32),16+256+256+15+(x1*32),240+16+15+(y1*32),Color);
+         }
+
+/* NT 2C00 */
+        for (x = 0; x < 0x40; x++)
+        {
+            AttrByte = PPU_Rd(0x2FC0 + x);//PPU_Rd(0x27C0 + x);
+            x1 = x % 8;
+            y1 = x / 8;
+            
+            
+            Color = AttrByte & 0x3; // Pattern 1;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+256+(x1*32),(y1*32),256+256+15+(x1*32),15+(y1*32),Color);
+            
+            Color = (AttrByte>>2) & 0x3; // Pattern 2;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+256+(x1*32),(y1*32),16+256+256+15+(x1*32),15+(y1*32),Color);
+            
+            Color = (AttrByte>>4) & 0x3; // Pattern 3;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+256+(x1*32),16+(y1*32),256+256+15+(x1*32),16+15+(y1*32),Color);
+            
+            Color = (AttrByte>>6) & 0x3; // Pattern 4;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+256+(x1*32),16+(y1*32),16+256+256+15+(x1*32),16+15+(y1*32),Color);
+            }
+
+        if (ppu.DisplayNameTables == 0)
+        {
+            for (x = 0; x < 33; x++)
+            {
+                line(Buffer, 256 + x * 16, 0, 256 + x * 16, 240 + 240, 8);
+                line(Buffer, 256, 0 + x * 16, 256 + 256 + 256, 0 + x * 16, 8);
+            }
+
+            for (x = 0; x < 17; x++)
+            {
+                line(Buffer, 256 + x * 32, 0, 256 + x * 32, 240 + 240, 6);
+                line(Buffer, 256, 0 + x * 32, 256 + 256 + 256, 0 + x * 32, 6);
+            }
+        }
+    }
+
+    if (ppu.DisplayNameTables)
+    {
+/* NT 2000 */
+        for (x = 0; x < 32; x++)
+            for (y = 0; y < 30; y++)
+            {
+                TileID = (PPU_Rd/*ppu.Memory[*/(0x2000 + x + (y * 32))/*]*/ << 4) + ppu.Bg_Pattern_Table;
+
+                for (x1 = 0; x1 < 8; x1++)
+                    for (y1 = 0; y1 < 8; y1++)
+                    {
+                        Color = GetTileColor(TileID, x1, y1);
+                        if (ppu.DisplayAttributeTable != 0)
+                            Color |= (Buffer->line[(8 * y) + 240 + y1][(8 * x) + 256 + x1] & 0x3) << 2;
+
+                        if ((Color != 0) || (ppu.DisplayAttributeTable != 0))
+                        {
+                            Color = ppu.Memory[0x3F00 + Color];
+                            Buffer->line[(8 * y) + 240 + y1][(8 * x) + 256 + x1] = Color;
+                        }
+                    }
+            }
+
+
+/* NT 2800 */
+        for (x = 0; x < 32; x++)
+            for (y = 0; y < 30; y++)
+            {
+                TileID = (PPU_Rd(0x2800 + x + (y * 32)) << 4) + ppu.Bg_Pattern_Table;
+
+                for (x1 = 0; x1 < 8; x1++)
+                    for (y1 = 0; y1 < 8; y1++)
+                    {
+                        Color = GetTileColor(TileID, x1, y1);
+                        if (Color != 0)
+                        {
+                            Color = ppu.Memory[0x3F00 + Color + 4];
+                            Buffer->line[(8 * y) + y1][(8 * x) + 256 + x1] = Color;
+                        }
+                    }
+            }
+
+/* NT 2400 */
+            for (x = 0; x < 32; x++)
+                for (y = 0; y < 30; y++)
+                    {
+                    TileID = (PPU_Rd(0x2400 + x + (y * 32)) << 4) + ppu.Bg_Pattern_Table;
+                    
+                    for (x1 = 0; x1 < 8; x1++)
+                        for (y1 = 0; y1 < 8; y1++)
+                            {
+                            Color = GetTileColor(TileID, x1, y1);
+                            if (ppu.DisplayAttributeTable != 0)
+                                Color |= (Buffer->line[(8 * y) + 240 + y1][(8 * x) + 256 + x1] & 0x3) << 2;
+                            
+                            if ((Color != 0) || (ppu.DisplayAttributeTable != 0))
+                                {
+                                Color = ppu.Memory[0x3F00 + Color];
+                                Buffer->line[(8 * y) + 240+ y1][(8 * x) + 256 + 256 + x1] = Color;
+                                }
+                            }
+                    }
+
+/* NT 2C00 */
+        for (x = 0; x < 32; x++)
+            for (y = 0; y < 30; y++)
+            {
+                TileID = (PPU_Rd(0x2C00 + x + (y * 32)) << 4) + ppu.Bg_Pattern_Table;
+
+                for (x1 = 0; x1 < 8; x1++)
+                    for (y1 = 0; y1 < 8; y1++)
+                    {
+                        Color = GetTileColor(TileID, x1, y1);
+                        if (Color != 0)
+                        {
+                            Color = ppu.Memory[0x3F00 + Color + 12];
+                            Buffer->line[(8 * y) + y1][(8 * x) + 256 + 256 + x1] = Color;
+                        }
+                    }
+            }
+    }
+
+    if (((ppu.ControlRegister2.b & PPU_CR2_BGVISIBILITY) != 0) || ppu.ForceBgVisibility)
+    {
+/* Display BG with scrolling informations */
+/* J'ai la solution :D */
+/*
+frame start (line 0) (if background or sprites are enabled):
+v=t
+*/
+        ppu.VRAMAddrReg2.W = ppu.TimedTmpPtr[0] | 0x2000;
+        //printf("Starting addresses : 0x%X\n",ppu.VRAMAddrReg2.W);
+        
+        XScroll = ppu.TimedHScroll[0];
+
+        YScroll = /*ppu.TimedVScroll[0]*/ ppu.TmpVScroll;
+
+        for (y = 0; y < 240; y++)
+        {
+/*
+scanline start (if background and sprites are enabled):
+  8421 8421 8421 8421   8421 8421 8421 8421
+v:0000 0100 0001 1111=t:0000 0100 0001 1111
+  1111 1198 7654 3210         
+  5432 10
+*/
+            
+            ppu.VRAMAddrReg2.W = (ppu.VRAMAddrReg2.W & 0xFBE0)
+               | ((ppu.TimedTmpPtr[y]) & 0x041F)
+               | 0x2000;
+
+            
+            TileID = (PPU_Rd(ppu.VRAMAddrReg2.W) << 4)
+               | ppu.Bg_Pattern_Table;
+
+            XScroll = ppu.TimedHScroll[y];
+            
+            /*YScroll += ppu.TimedVScroll[y];*/
+/*            printf("Y:%d -_- ", YScroll);
+            if (ppu.TimedVScroll[y] != 0)
+            {
+                YScroll = ppu.TimedVScroll[y];
+                printf("Y:%d", YScroll);                
+            }
+           printf("\n");*/
+            for (x = 0; x < 256; x++)
+            {
+/* Calculer la couleur du point */
+/* Bits 1 et 0 */
+                Color = GetTileColor(TileID, XScroll, YScroll);
+/* Bits 3 et 2 */
+/*
+XScroll : 0,1,2,3,4
+X = (ppu.VRAMAddrReg2.W & 0x1F)
+
+Y : 5,6,7,8,9
+Y = (ppu.VRAMAddrReg2.W & 0x3E0) >> 5
+*/
+/*ab_y = ((ppu.VRAMAddrReg2.W & 0x3E0) >> 5);
+                ab_x = (ppu.VRAMAddrReg2.W & 0x1F);
+
+                AttrByte = (((ab_y) >> 2) << 3) +
+                   ((ab_x) >> 2);
+
+
+                AttrByte = (PPU_Rd((ppu.VRAMAddrReg2.W & 0x2C00) + 0x3C0 + AttrByte) >>
+                        ((((ab_y & 2) * 2) + (((ppu.VRAMAddrReg2.W & 0x2C00)) & 2)))) & 0x03;*/
+                
+                /*
+                 
+                 00DC BA98 7654 3210
+                 
+                 0000 BA54 3c-2 10b-
+                 0000 0000 0001 1100  : 0x001C >> 2 
+                 0000 0011 1000 0000  : 0x0380 >> 4
+                 
+                   10 --11 11-- ----
+                 
+                 0xC000
+                 & 0x0C3F | 0x23C0
+                 
+                                b                  
+                 val >> ( (Reg2 & 0x2) | ( (Reg2 & 0x0x40)>>4)
+                 */
+                
+                Reg2 = ppu.VRAMAddrReg2.W;
+                AttrByte = ((Reg2 & 0x0380) >> 4) | ((Reg2 & 0x001C) >> 2) | (Reg2 & 0x0C00);
+                AttrByte &= 0x0C3F;
+                AttrByte |= 0x23C0;
+                AttrByte = PPU_Rd(AttrByte);
+                AttrByte = AttrByte >> ( 0x0 | (Reg2 & 0x02) | ( (Reg2 & 0x40) >> 4) );
+                AttrByte &= 0x3;
+                
+                if (Color)
+                {
+                    Color |= (AttrByte << 2);
+                    Color = ppu.Memory[0x3F00 + Color];
+                    Buffer->line[y][x] = Color + 0xC0;
+                }
+                
+                XScroll++;
+                XScroll &= 7;
+                if (XScroll == 0)
+                {    /* On incrémente le compteur de tile */
+                    if ((ppu.VRAMAddrReg2.W & 0x1F) == 0x1F)
+                    {    /* On met a 0 et change
+                         * l'etat du bit 10 */
+                        ppu.VRAMAddrReg2.W &= ~0x1F;
+
+/* A voir si ya pas bcp mieux */
+                        if ((ppu.VRAMAddrReg2.W & 0x400))
+                            ppu.VRAMAddrReg2.W &= ~0x400;
+                        else
+                            ppu.VRAMAddrReg2.W |= 0x400;
+                    }
+                    else
+                    {    /* on incremente juste */
+                        ppu.VRAMAddrReg2.W = (ppu.VRAMAddrReg2.W & ~0x1F) | ((ppu.VRAMAddrReg2.W + 1) & 0x1F);
+                    }
+
+                    TileID = (PPU_Rd(ppu.VRAMAddrReg2.W) << 4)
+                       | ppu.Bg_Pattern_Table;
+                }
+            }
+/*
+you can think of bits 5,6,7,8,9 as the "y scroll"(*8).  this functions
+slightly different from the X.  it wraps to 0 and bit 11 is switched when
+it's incremented from _29_ instead of 31.  there are some odd side effects
+from this.. if you manually set the value above 29 (from either 2005 or
+2006), the wrapping from 29 obviously won't happen, and attrib data will be
+used as name table data.  the "y scroll" still wraps to 0 from 31, but
+without switching bit 11.  this explains why writing 240+ to 'Y' in 2005
+appeared as a negative scroll value.
+*/
+            YScroll++;
+            YScroll &= 7;
+            if (YScroll == 0)
+            {    /* On incrémente le compteur de tile :| */
+                if ((ppu.VRAMAddrReg2.W & 0x3E0) == 0x3A0)
+                {    /* On met a 0 et change l'etat du bit
+                     * 10 */
+                    ppu.VRAMAddrReg2.W &= ~0x3F0;
+
+/* A voir si ya pas bcp mieux */
+                    if ((ppu.VRAMAddrReg2.W & 0x800))
+                        ppu.VRAMAddrReg2.W &= ~0x800;
+                    else
+                        ppu.VRAMAddrReg2.W |= 0x800;
+                }
+                else
+                {    /* on incremente juste */
+                    ppu.VRAMAddrReg2.W = (ppu.VRAMAddrReg2.W & ~0x3F0) | ((((ppu.VRAMAddrReg2.W & 0x3F0) >> 5) + 1) << 5);
+                }
+            }
+        }
+        //while (!key[KEY_ENTER]);
+    }
+/*
+* if (ppu.ControlRegister2.s.SpriteVisibility == 1)
+* PPUDispSprite(0);
+*/
+
+    if (((ppu.ControlRegister2.b & PPU_CR2_SPRTVISIBILITY) != 0) || ppu.ForceSpriteVisibility)
+    {
+               if (ppu.ControlRegister1.b & PPU_CR1_SPRTSIZE)
+        {
+          NewPPUDispSprite_8x16();
+        }
+        else
+        {
+          NewPPUDispSprite();
+        }
+    }
+
+/*    for(x=0;x<256;x++)
+        for(y=0;y<240;y++)
+        {
+            if ((i = getpixel(Buffer,x,y)) >= 0xC0)
+                putpixel(Buffer,x,y,ppu.Memory[0x3F00+i-0xC0]);
+        }*/
+
+    if (ppu.DisplayPalette)
+    {
+        textout(Buffer, font, "Bg Palette", 0, 247, 5);
+        textout(Buffer, font, "Sprt Palette", 90, 247, 5);
+
+        rect(Buffer, 0, 255, 4 * 20 + 2, 255 + 4 * 20 + 2, 0);
+        rect(Buffer, 90, 255, 90 + 4 * 20 + 2, 255 + 4 * 20 + 2, 0);
+        for (i = 0; i < 16; i++)
+        {
+            rectfill(Buffer, 1 + (i % 4) * 20, 256 + (i / 4) * 20, 1 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, ppu.Memory[0x3F00 + i]);
+            rectfill(Buffer, 91 + (i % 4) * 20, 256 + (i / 4) * 20, 91 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, ppu.Memory[0x3F10 + i]);
+        }
+    }
+    if (ppu.DisplayVRAM)
+    {
+
+/* y:346 */
+        x1 = 0;
+        y1 = 0;
+        for (i = 0; i < 256; i++)
+        {
+            TileID = 0x0000 + (i << 4);
+
+            for (x = 0; x < 8; x++)
+                for (y = 0; y < 8; y++)
+                {
+
+                    Color = GetTileColor(TileID, x, y);
+
+                    putpixel(Buffer, 10 + x1 + x, 347 + y1 + y, ppu.Memory[0x3F00 + Color]);
+
+                }
+
+
+            x1 += 8;
+            if (x1 >= 128)
+            {
+                x1 = 0;
+                y1 += 8;
+            }
+        }
+        x1 = 0;
+        y1 = 0;
+        for (i = 0; i < 256; i++)
+        {
+            TileID = 0x1000 + (i << 4);
+
+            for (x = 0; x < 8; x++)
+                for (y = 0; y < 8; y++)
+                {
+
+                    Color = GetTileColor(TileID, x, y);
+
+                    putpixel(Buffer, 10 + 128 + x1 + x, 347 + y1 + y, ppu.Memory[0x3F10 + Color]);
+
+                }
+            x1 += 8;
+            if (x1 >= 128)
+            {
+                x1 = 0;
+                y1 += 8;
+            }
+        }
+    }
+
+    for (i = 0; i < 240; i++)
+    {
+      putpixel(Buffer, 0, i, ppu.TimedTmpPtr[i] & 0xFF);
+      putpixel(Buffer, 1, i, ppu.TimedTmpPtr[i] & 0xFF);
+      putpixel(Buffer, 2, i, ppu.TimedTmpPtr[i] & 0xFF);
+    }
+
+NoDraw:
+
+    textprintf(Buffer, font, 5, 340, 4, "FPS : %d   IPS : %d", FPS, IPS);
+    textprintf(Buffer, font, 5, 3, 4, "FPS : %d (CPU@~%2.2fMhz : %d%%)", FPS, (float) (((float) IPS) / 1000000.0), (int) ((((float) IPS) / 1770000.0) * 100.0));
+    
+    release_bitmap(Buffer);
+
+    if (NOBLIT == 0)
+    {
+      unsigned long TimeStart, TimeEnd;
+      blit(Buffer, screen, 0, 0, 0, 0, 512 + 256, 480);
+      //stretch_blit(Buffer, screen, 0, 0, 256, 240, 0, 0, 512, 480);
+      gettimeofday(&timeEnd, NULL);
+      
+      TimeStart = 1000000 * timeStart.tv_sec + timeStart.tv_usec;
+      TimeEnd = 1000000 * timeEnd.tv_sec + timeEnd.tv_usec;
+      
+      //printf("Start: %d\nEnd: %d\nResult: %d\n",TimeStart, TimeEnd, 16666 - (TimeEnd - TimeStart));
+      WaitTime = 14000 - (TimeEnd - TimeStart);
+      if (!key[KEY_PGUP])
+          usleep(WaitTime<0?0:WaitTime);
+    }
+
+
+    if ((ppu.ControlRegister1.b & PPU_CR1_EXECNMI) != 0)
+        return 1;
+
+    return 0;
+}
+
+byte ReadPPUReg(byte RegID)
+{
+/* RegID is the nb of the reg 0-7 */
+    unsigned char ret;
+    RegID &= 0x07;
+    
+    switch (RegID)
+    {
+    default:
+        ret = (unsigned char) 0x00;    /* Can return every thing you
+                         * want here :D */
+        break;
+    case 1:        /* Control Register 2 */
+        ret = ppu.ControlRegister2.b;
+        break;
+    case 2:
+        ret = ppu.StatusRegister.b;
+
+        ppu.StatusRegister.b &= ~(PPU_FLAG_SR_VBLANK);
+        
+        ppu.StatusRegister.b &= ~(PPU_FLAG_SR_SPRT0);
+        ppu.VRAMAddrMode = 0;
+        break;
+    case 5:
+        ret = ppu.VRAMAddrReg2.B.l;
+        break;
+    case 6:
+        ret = ppu.VRAMAddrReg2.B.h;
+        break;
+    case 7:        /* PPU in NES is really strange.. Bufferised
+                 * send is weird.. */
+        if (ppu.VRAMAddrReg2.W < 0x3EFF)
+        {
+            ret = ppu.VRAMBuffer;
+            ppu.VRAMBuffer = PPU_Rd(ppu.VRAMAddrReg2.W);
+            ppu.VRAMAddrReg2.W += ppu.PPU_Inc;
+            ppu.VRAMAddrReg2.W &= 0x3FFF;
+        }
+        else
+        {
+          ret = PPU_Rd(ppu.VRAMAddrReg2.W);
+          ppu.VRAMAddrReg2.W += ppu.PPU_Inc;
+        }
+        break;
+    }
+    return ret;
+}
+
+void WritePPUReg(byte RegID, byte val)
+{
+/* RegID is the nb of the reg 0-7 */
+
+    RegID &= 0x07;
+
+    switch (RegID)
+    {
+        default:/* For not writeable reg */
+        printf("WritePPU error\n");
+        break;
+    case 0:        /* Control Register 1 */
+        ppu.ControlRegister1.b = val;
+        ppu.Bg_Pattern_Table = (ppu.ControlRegister1.s.BgPattern == 1) ? 0x1000 : 0x0000;
+        ppu.Sprt_Pattern_Table = (ppu.ControlRegister1.s.SptPattern == 1) ? 0x1000 : 0x0000;
+        ppu.PPU_Inc = (ppu.ControlRegister1.s.AddrIncrmt == 1) ? 32 : 1;
+
+        ppu.Name_Table_Addresse = 0x2000;
+        switch (ppu.ControlRegister1.s.NameTblAddr)
+        {
+        case 3:
+            ppu.Name_Table_Addresse += 0x400;
+        case 2:
+            ppu.Name_Table_Addresse += 0x400;
+        case 1:
+            ppu.Name_Table_Addresse += 0x400;
+        case 0:
+            break;
+        }
+            ppu.TimedNT[ScanLine] = ppu.ControlRegister1.s.NameTblAddr;
+/*
+2000 write:
+  1111 0011 1111 1111 ( F3FF )
+t:0000 1100 0000 0000 = d:0000 0011
+*/
+        ppu.TmpVRamPtr = ( (ppu.TmpVRamPtr & 0xF3FF)
+                       | ( ((ppu.ControlRegister1.s.NameTblAddr) & 0x03) << 10 )
+                         );
+
+        break;
+    case 1:        /* Control Register 2 */
+        //printf("PPU: new CR2 ; 0x%x\n", val);
+        ppu.ControlRegister2.b = val;
+        break;
+    case 3:        /* SPR-RAM Addresse Register */
+        ppu.SPR_RAMAddr = val;
+        break;
+    case 4:        /* SPR-RAM Input Register */
+        ppu.SPRRAM[ppu.SPR_RAMAddr] = val;
+        break;
+    case 5:        /* VRAM Address register 1 */
+        if (ppu.VRAMAddrMode == 0)
+        {
+/*
+2005 first write:
+t:0000 0000 0001 1111=d:1111 1000
+x=d:00000111
+*/
+            ppu.VRAMAddrMode = 1;
+
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0xFFE0) | ((val & 0xF8) >> 3));
+            ppu.HScroll = val & 0x7;
+
+            //printf("%d -> 2005 w1: 0x%04X (val: 0x%02X)\n", ScanLine, ppu.TmpVRamPtr, val);
+        }
+        else
+        {
+/*
+2005 second write:
+t:0000 0011 1110 0000=d:1111 1000
+t:0111 0000 0000 0000=d:0000 0111
+*/
+            ppu.VRAMAddrMode = 0;
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0xFC1F) | ((val & 0xF8) << 2));
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0x8FFF) | ((val & 0x07) << 12));
+
+            ppu.TmpVScroll = ((ppu.TmpVRamPtr & 0x700) >> 12) & 0x7;
+            if (ppu.TmpVScroll != 0)
+                printf("2002: TmpVScroll == %d \n", ppu.TmpVScroll);
+            
+            //printf("%d -> 2005 w2: 0x%04X (val: 0x%02X)\n", ScanLine, ppu.TmpVRamPtr, val);
+
+        }
+        break;
+    case 6:        /* VRAM Address register 2 */
+        if (ppu.VRAMAddrMode == 0)
+        {
+            ppu.VRAMAddrMode = 1;
+/*
+2006 first write:
+t:0011 1111 0000 0000 = d:0011 1111
+t:1100 0000 0000 0000=0
+*/
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0xC0FF) | ((val&0x3F) << 8)) & 0x3FFF;
+
+            //printf("%d -> 2006 w1: 0x%04X (val: 0x%02X)\n", ScanLine, ppu.TmpVRamPtr, val);
+        }
+        else
+        {
+            ppu.VRAMAddrMode = 0;
+/*
+2006 second write:
+t:0000000011111111=d:11111111
+v=t
+*/
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0xFF00) | (val & 0x00FF));
+            ppu.VRAMAddrReg2.W = ppu.TmpVRamPtr;
+
+            //printf("%d -> 2006 w2: 0x%04X (val: 0x%02X)\n", ScanLine, ppu.TmpVRamPtr, val);
+
+        }
+        break;
+    case 7:        /* VRAM I/O */
+        PPU_Wr(ppu.VRAMAddrReg2.W, val);
+        ppu.VRAMAddrReg2.W += ppu.PPU_Inc;
+        break;
+    }
+}
+
+void FillSprRamDMA(byte value)
+{
+    int i;
+    for (i = 0x00; i < 0x100; i++)
+    {
+        ppu.SPRRAM[i] = ReadMemory( value, i);
+    }
+}

+ 1328 - 0
src/ppu/ppu.new2.c

@@ -0,0 +1,1328 @@
+/*
+ *  PPU emulation - The TI-NESulator Project
+ *  ppu.c
+ *  
+ *  Define and emulate the PPU (Picture Processing Unit) of the real NES
+ * 
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-04-06 17:17:01 +0200 (ven, 06 avr 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/ppu.c $
+ *  $Revision: 32 $
+ *
+ */
+
+#include <allegro.h>
+#include <stdio.h>
+#include <stdlib.h>

+#include "ppu.h"
+#include "memory.h"
+#include "M6502.h"
+
+#define __TINES_PLUGINS__
+#include "plugins.h"
+
+#define GetTileColor(tile,x1,y1)    ( ( ppu.Memory[tile+y1] & (1<<(7-x1)) ) == 0 ? 0 : 1 ) | \
+                                    ( ( ppu.Memory[tile+y1+8] & (1<<(7-x1)) ) == 0 ? 0 : 1<<1 )
+
+#define GetColor(col) (col&0xFF)
+
+extern PPU ppu;
+extern BITMAP *Buffer;
+extern unsigned short ScanLine;
+unsigned long BgColor;
+
+volatile extern int frame;
+
+volatile extern unsigned long IPS, FPS;
+
+extern unsigned long ColorPalette[ 8 * 63 ];
+
+extern short IRQScanHit;
+
+byte NOBLIT = 0;
+
+int InitPPU(PPU * ppu)
+{
+    int i;
+    if ((ppu->Memory = (unsigned char *) malloc(0x4000)) == NULL)
+        return -1;
+
+    NOBLIT = 0;
+
+    
+
+    /* Initializing register.. */
+    ppu->In_VBlank = 0;
+    
+    ppu->BaseOneScreen = 0x2000;
+    
+    ppu->ControlRegister1.b = 0;
+    ppu->ControlRegister2.b = 0;
+    ppu->StatusRegister.b = 0;
+    ppu->SPR_RAMAddr = 0;
+    ppu->VRAMAddrReg1.W = 0;
+    ppu->VRAMAddrReg2.W = 0;
+    ppu->VRAMAddrMode = 0;
+
+    ppu->Bg_Pattern_Table = 0x0000;
+    ppu->Name_Table_Addresse = 0x2000;
+    ppu->Sprt_Pattern_Table = 0x0000;
+    ppu->PPU_Inc = 1;
+
+    ppu->MirrorDir = 0;
+    ppu->ScreenType = 1;
+
+
+    ppu->ForceBgVisibility = 0;
+    ppu->ForceSpriteVisibility = 0;
+
+    ppu->DisplayNameTables = ~0;
+    ppu->DisplayAttributeTable = ~0;
+    ppu->DisplayPalette = ~0;
+    ppu->DisplayVRAM = ~0;
+
+
+    /* Set PPU registers */
+    set_page_rd_hook(0x20, ReadPPUReg);    
+    set_page_wr_hook(0x20, WritePPUReg);
+    
+    set_page_readable(0x20, true);
+    set_page_writeable(0x20, true);
+    
+    
+    /* Set PPU Ghost Registers */
+    for(i = 0x21; i < 0x40; i++)
+        set_page_ghost(i, true, 0x20);
+
+    plugin_install_keypressHandler('i', DebugSprites);
+    plugin_install_keypressHandler('I', DebugSprites);
+    
+    plugin_install_keypressHandler('u', DebugColor);
+    plugin_install_keypressHandler('U', DebugColor);
+    
+    
+    
+
+    return 0;
+}
+
+PPUSprite PPUGetSprite(unsigned short i)
+{
+PPUSprite ret;
+
+    ret.y = ppu.SPRRAM[i * 4];
+    ret.tileid = ppu.SPRRAM[i * 4 + 1];
+    /*ret.flags.s.BgPrio = (ppu.SPRRAM[i * 4 + 2] & (1 << 5)) == 0 ? 0 : 1;
+    ret.flags.s.HFlip = (ppu.SPRRAM[i * 4 + 2] & (1 << 6)) == 0 ? 0 : 1;
+    ret.flags.s.UpperColor = ppu.SPRRAM[i * 4 + 2] & 0x03;
+    ret.flags.s.VFlip = (ppu.SPRRAM[i * 4 + 2] & (1 << 7)) == 0 ? 0 : 1;*/
+    ret.flags.b = ppu.SPRRAM[i * 4 + 2];
+    ret.x = ppu.SPRRAM[i * 4 + 3];
+    return ret;
+}
+
+void PPUSetSprite(unsigned short i, PPUSprite *sprt)
+{
+    ppu.SPRRAM[i * 4] = sprt->y;
+    ppu.SPRRAM[i * 4 + 1] = sprt->tileid;
+    /*ret.flags.s.BgPrio = (ppu.SPRRAM[i * 4 + 2] & (1 << 5)) == 0 ? 0 : 1;
+    ret.flags.s.HFlip = (ppu.SPRRAM[i * 4 + 2] & (1 << 6)) == 0 ? 0 : 1;
+    ret.flags.s.UpperColor = ppu.SPRRAM[i * 4 + 2] & 0x03;
+    ret.flags.s.VFlip = (ppu.SPRRAM[i * 4 + 2] & (1 << 7)) == 0 ? 0 : 1;*/
+    ppu.SPRRAM[i * 4 + 2] = sprt->flags.b;
+    ppu.SPRRAM[i * 4 + 3] = sprt->x;
+}
+
+
+//(Addr & 0x1FF)    /* Addr In NT */
+// (Addr & 0xC00) >> 2        /* NT number */
+#define GetNT(a)    ( (a&0xC00) >> 10 )
+#define RelAddr(a)    (a & 0x3FF)
+#define PalAddr(a) (a & 0x1F)
+
+unsigned char PPU_Rd(unsigned short Addr)
+{
+    if (Addr > (unsigned short) 0x3FFF)
+    {
+        Addr &= 0x3FFF;
+    }
+    if ((Addr < 0x3F00) && (Addr >= 0x2000))
+    {
+        if (Addr > 0x3000)
+        {
+            Addr -= 0x1000;
+        }
+        if (ppu.ScreenType == 0)
+        {        /* 1 Screen Mode */
+            return ppu.Memory[RelAddr(Addr) + ppu.BaseOneScreen];
+        }
+        else
+            if (ppu.ScreenType)
+            {    /* Miroring mode */
+                if (ppu.MirrorDir)
+                {    /* Horizontal */
+                    if (GetNT(Addr) & 0x2)
+                    {    /* NT2-3 */
+                        return ppu.Memory[0x2000 + RelAddr(Addr)];
+                    }
+                    else
+                    {
+                        return ppu.Memory[0x2400 + RelAddr(Addr)];
+                    }
+                }
+                else
+                {    /* Vertical */
+                    if (GetNT(Addr) & 0x1)
+                    {    /* NT0-2 */
+                        return ppu.Memory[0x2400 + RelAddr(Addr)];
+                    }
+                    else
+                    {
+                        return ppu.Memory[0x2000 + RelAddr(Addr)];
+                    }
+                }
+            }
+            else
+            {    /* Four Screen mode */
+
+            }
+    }
+    else
+        if (Addr >= 0x3F00)
+        {
+            return ppu.Memory[0x3F00 | PalAddr(Addr)];
+        }
+    return ppu.Memory[Addr];
+}
+
+void PPU_Wr(unsigned short Addr, unsigned char Value)
+{
+    if (Addr > (unsigned short) 0x3FFF)
+    {
+        Addr &= 0x3FFF;
+    }
+    if ((Addr < 0x3F00) && (Addr >= 0x2000))
+    {
+        if (Addr > 0x3000)
+        {
+            Addr -= 0x1000;
+        }
+        if (ppu.ScreenType == 0)
+        {        /* 1 Screen Mode */
+            ppu.Memory[RelAddr(Addr) + 0x2000] = Value;
+        }
+        else
+            if (ppu.ScreenType == 1)
+            {    /* Miroring mode */
+                if (ppu.MirrorDir == 0)
+                {    /* Vertical */
+                    if (GetNT(Addr) & 0x1)
+                    {    /* NT0-2 */
+                        ppu.Memory[0x2400 + RelAddr(Addr)] = Value;
+                    }
+                    else
+                    {
+                        ppu.Memory[0x2000 + RelAddr(Addr)] = Value;
+                    }
+                }
+                else
+                {    /* Horizontal */
+                    if (GetNT(Addr) & 0x2)
+                    {    /* NT2-3 */
+                        ppu.Memory[0x2000 + RelAddr(Addr)] = Value;
+                    }
+                    else
+                    {
+                        ppu.Memory[0x2400 + RelAddr(Addr)] = Value;
+                    }
+                }
+            }
+            else
+            {    /* Four Screen mode */
+
+            }
+    }
+    else
+        if (Addr >= 0x3F00)
+        {
+            //printf("%s palette: color %x new value : %d (0x%x)\n", (PalAddr(Addr) < 0x10) ? "Bgnd" : "Sprt", PalAddr(Addr), Value & 0x3F, Addr);
+            ppu.Memory[ /* 0x3F00 | PalAddr(Addr) */ Addr] = Value & 0x3F;
+            if (PalAddr(Addr) == 0x10)
+                ppu.Memory[0x3F00] = Value & 0x3F;
+
+        }
+        else
+        {
+            ppu.Memory[Addr] = Value;
+        }
+}
+
+unsigned short NbOfSprite[255];
+
+
+void NewPPUDispSprite()
+{
+  int x, y, x1, y1, px, py, i;
+  unsigned long Color;
+  PPUSprite sprite;
+  unsigned short bg;
+  short SprtAddr;
+
+  for (i = 63; i >= 0; i--)
+  {
+    sprite = PPUGetSprite(i);
+    bg = sprite.flags.b & PPUSPRITE_FLAGS_BGPRIO;
+    
+    y = sprite.y;
+    if (y < 248)
+    {
+      SprtAddr = ((sprite.tileid) << 4) + ppu.Sprt_Pattern_Table;
+      x = sprite.x;
+      
+      for (y1 = 0; y1 < 8; y1++)
+      {
+    py = y + ((sprite.flags.b & PPUSPRITE_FLAGS_VFLIP) == 0 ? y1 : ((8 - 1) - y1));
+
+    if ((py > 0) && (py < 249) && ((++NbOfSprite[py]) > 7))
+    {
+      ppu.StatusRegister.b |= PPU_FLAG_SR_8SPRT ;
+      //printf("%d Hohoho!\n", py);
+      //          line(Buffer, 0, py+1, 256, py+1, 10);
+      //continue; // Do not display more than 8 sprites on this line :p
+    }
+
+    for (x1 = 0; x1 < 8; x1++)
+    {
+      px = x + ((sprite.flags.b & PPUSPRITE_FLAGS_HFLIP) != 0 ? (7 - x1) : x1);
+      Color = GetTileColor(SprtAddr, x1, y1);
+      if (Color)
+      {
+        Color = (Color) | ((sprite.flags.b & PPUSPRITE_FLAGS_UPPERCOLOR) << 2);
+        Color = ppu.Memory[0x3F10 + Color];
+        if ((i == 0) && (_getpixel(Buffer,px,py) != BgColor) && (ppu.HitSpriteAt == 255))
+        {
+          //Ligne utilisé pour le débuguage
+          //line(Buffer, 0, py+1, 256, py+1, GetColor(10));
+          ppu.HitSpriteAt = py+1;
+        }
+        if ((bg == 0) || ((bg != 0) && (_getpixel(Buffer,px,py) == BgColor)))
+          _putpixel(Buffer, px, py, GetColor(Color));
+      }
+    }
+      }
+    }
+  }
+}
+
+
+void NewPPUDispSprite_8x16()
+{
+  int x, y, x1, y1, px, py, i, loop, tile;
+  unsigned long Color;
+  PPUSprite sprite;
+  unsigned short bg;
+  short SprtAddr;
+  
+  unsigned short SprtTable;
+  
+  for (i = 63; i >= 0; i--)
+  {
+    sprite = PPUGetSprite(i);
+    bg = sprite.flags.b & PPUSPRITE_FLAGS_BGPRIO;
+    tile = sprite.tileid;
+    y = sprite.y + 1;
+
+    if (y < 248)
+    {
+      if ( (SprtTable = (tile & 0x1) * 0x1000) == 0x1000)
+    tile -=1;
+
+      if ((sprite.flags.b & PPUSPRITE_FLAGS_VFLIP) != 0)
+      {
+    y +=8;
+      }
+
+      for (loop = 0; loop < 2; loop++)
+      {
+    SprtAddr = ((tile) << 4) + SprtTable;
+    x = sprite.x;
+    
+    for (y1 = 0; y1 < 8; y1++)
+    {
+      py = y + ((sprite.flags.b & PPUSPRITE_FLAGS_VFLIP) == 0 ? y1 : ((8 - 1) - y1));
+      if ((py > 0) && (py < 249) && ((++NbOfSprite[py]) > 7))
+      {
+        ppu.StatusRegister.b |= PPU_FLAG_SR_8SPRT ;
+        //        puts("Ho!");
+        //continue; // No more sprites on this line :p
+      }
+      for (x1 = 0; x1 < 8; x1++)
+      {
+        px = x + (((sprite.flags.b & PPUSPRITE_FLAGS_HFLIP) != 0) ? (7 - x1) : x1);
+        Color = GetTileColor(SprtAddr, x1, y1);
+        if (Color)
+        {
+          Color = (Color) | ((sprite.flags.b & PPUSPRITE_FLAGS_UPPERCOLOR) << 2);
+          Color = ppu.Memory[0x3F10 + Color];
+          if ((i == 0) && (_getpixel(Buffer, px, py) != BgColor) && (ppu.HitSpriteAt == 255))
+          {
+            //Ligne utilise pour le debuguage
+            //line(Buffer, 0, py, 256, py, 10);
+            ppu.HitSpriteAt = py+1;
+          }
+          if ((bg == 0) || ((bg != 0) && (_getpixel(Buffer, px, py) == BgColor)))
+            _putpixel(Buffer, px, py, GetColor(Color));
+        }
+      }
+    }
+    tile += 1;
+    if ( (sprite.flags.b & PPUSPRITE_FLAGS_VFLIP) != 0)
+      y -= 8;
+    else
+      y += 8;
+      }    
+    }
+  }
+}
+
+
+void DebugColor()
+{
+  static unsigned short x = 128;
+  static unsigned short y = 128;
+  unsigned char OldDisplayPalette = ppu.DisplayPalette;
+  byte keyb;
+  unsigned int i;
+  unsigned long Color;
+
+  NOBLIT = 1;
+
+  ppu.DisplayPalette = ~0;
+
+  while(!key[KEY_ESC])
+  {
+    frame++;
+    PPUVBlank();
+
+    Color = /*Buffer->line[y][x]*/ _getpixel(Buffer, x, y);
+
+    textprintf(Buffer, font, 5, 340, GetColor(3), "Pos [%d:%d] Color: %d Bg: %d", x, y, Color, BgColor);
+
+    line(Buffer, x-10, y, x+10, y, GetColor(1));
+    line(Buffer, x, y-10, x, y+10, GetColor(1));
+
+
+    /*
+    rect(Buffer, 0, 255, 4 * 20 + 2, 255 + 4 * 20 + 2, 0);
+    rect(Buffer, 90, 255, 90 + 4 * 20 + 2, 255 + 4 * 20 + 2, 0);
+    for (i = 0; i < 16; i++)
+    {
+      rectfill(Buffer, 1 + (i % 4) * 20, 256 + (i / 4) * 20, 1 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, ppu.Memory[0x3F00 + i]);
+      rectfill(Buffer, 91 + (i % 4) * 20, 256 + (i / 4) * 20, 91 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, ppu.Memory[0x3F10 + i]);
+    }*/
+    for( i = 0; i < 16; i++)
+    {
+      if (GetColor(ppu.Memory[0x3F00 + i]) == Color)
+      {
+    line(Buffer, 1+(i%4)*20, 256 + (i / 4) * 20, 1 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, 0xFFFFFFFF); 
+    line(Buffer, 1+(i%4)*20, 256 + (i / 4) * 20 + 20, 1 + (i % 4) * 20 + 20, 256 + (i / 4) * 20, 0xFFFFFFFF); 
+      }
+
+      if (GetColor(ppu.Memory[0x3F10 + i]) == Color)
+      {
+    line(Buffer, 91+(i%4)*20, 256 + (i / 4) * 20, 91 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, 0xFFFFFFFF); 
+    line(Buffer, 91+(i%4)*20, 256 + (i / 4) * 20 + 20, 91 + (i % 4) * 20 + 20, 256 + (i / 4) * 20, 0xFFFFFFFF); 
+      }
+       
+    }
+
+    blit(Buffer, screen, 0, 0, 0, 0, 512 + 256, 480);    
+
+    if (keypressed())
+    {
+      keyb = (readkey() & 0xFF);
+
+      if (keyb == '4')
+      {
+    x--;
+      }
+      if (keyb == '8')
+      {
+    y--;
+      }
+      if (keyb == '6')
+      {
+    x++;
+      }
+      if (keyb == '2')
+      {
+    y++;
+      }
+
+
+    }
+  }
+  ppu.DisplayPalette = OldDisplayPalette;
+  NOBLIT = 0;
+}
+
+void DebugSprites()
+{
+  byte keyb;
+  static int SelSprite = 0;
+  PPUSprite sprite;
+  NOBLIT = 1;
+  ppu.ControlRegister2.b |= PPU_CR2_SPRTVISIBILITY;
+
+  while(!key[KEY_ESC])
+  {
+    frame++;
+    PPUVBlank();
+    sprite = PPUGetSprite(SelSprite);
+
+    if (ppu.ControlRegister1.b & PPU_CR1_SPRTSIZE)
+    {
+      rect(Buffer, sprite.x-1, sprite.y-1, sprite.x+9, sprite.y+17, 1);
+    }
+    else
+    {
+      rect(Buffer, sprite.x-1, sprite.y-1, sprite.x+9, sprite.y+9, 1);
+    }
+
+    textprintf(Buffer, font, 5, 340, GetColor(3), "Sprite %d [%d:%d]", SelSprite, sprite.x, sprite.y);
+    textprintf(Buffer, font, 5, 349, GetColor(3), "B0: 0x%X  B1: 0x%X  B2: 0x%X  B3: 0x%X",sprite.y,sprite.tileid,sprite.flags.b,sprite.x);
+    textprintf(Buffer, font, 5, 358, GetColor(3), "Tile Index: %d", sprite.tileid);
+    textprintf(Buffer, font, 5, 367, GetColor(3), "Vertical Flip: %d", sprite.flags.s.VFlip);
+    textprintf(Buffer, font, 5, 376, GetColor(3), "Horizontal Flip: %d", sprite.flags.s.HFlip);
+    textprintf(Buffer, font, 5, 385, GetColor(3), "Background Priority: %d", sprite.flags.s.BgPrio);
+    textprintf(Buffer, font, 5, 394, GetColor(3), "Upper Color: %d", sprite.flags.s.UpperColor);
+
+    blit(Buffer, screen, 0, 0, 0, 0, 512 + 256, 480);    
+
+    if (keypressed())
+    {
+      keyb = (readkey() & 0xFF);
+      if (keyb == '+')
+    SelSprite = (SelSprite<63)?SelSprite+1:0;
+      if (keyb == '-')
+    SelSprite = (SelSprite>0)?SelSprite-1:63;
+
+      if (keyb == 'h')
+      {
+    sprite.flags.s.HFlip = ~sprite.flags.s.HFlip;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == 'b')
+      {
+    sprite.flags.s.BgPrio = ~sprite.flags.s.BgPrio;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == 'v')
+      {
+    sprite.flags.s.VFlip = ~sprite.flags.s.VFlip;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+
+      if (keyb == '4')
+      {
+    sprite.x--;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == '8')
+      {
+    sprite.y--;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == '6')
+      {
+    sprite.x++;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == '2')
+      {
+    sprite.y++;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+    }
+  }
+  NOBLIT = 0;
+}
+
+#define GetTilePos(addr,x,y) (addr+x+(y*32))
+
+void ppu_displayNameTables()
+{
+int x, y, x1, y1, i;
+
+unsigned long Reg2;
+unsigned short ab_x, ab_y;
+unsigned short Color;
+unsigned short AttrByte;
+unsigned short TileID;  
+
+    if (ppu.DisplayAttributeTable)
+    {
+/* NT 2000 */
+        for (x = 0; x < 0x40; x++)
+        {
+            AttrByte = /*ppu.Memory[0x23C0 + x];*/PPU_Rd(0x23C0 + x);
+            x1 = x % 8;
+            y1 = x / 8;
+            
+            
+            Color = AttrByte & 0x3; // Pattern 1;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+(x1*32),240+(y1*32),256+15+(x1*32),240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>2) & 0x3; // Pattern 2;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+(x1*32),240+(y1*32),16+256+15+(x1*32),240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>4) & 0x3; // Pattern 3;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+(x1*32),16+240+(y1*32),256+15+(x1*32),16+240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>6) & 0x3; // Pattern 4;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+(x1*32),16+240+(y1*32),16+256+15+(x1*32),16+240+15+(y1*32),Color);
+        }
+
+
+/* NT 2800 */
+        for (x = 0; x < 0x40; x++)
+         {
+            AttrByte = PPU_Rd(0x2BC0 + x);
+            x1 = x % 8;
+            y1 = x / 8;
+            
+            
+            Color = AttrByte & 0x3; // Pattern 1;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+(x1*32),(y1*32),256+15+(x1*32),15+(y1*32),Color);
+            
+            Color = (AttrByte>>2) & 0x3; // Pattern 2;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+(x1*32),(y1*32),16+256+15+(x1*32),15+(y1*32),Color);
+            
+            Color = (AttrByte>>4) & 0x3; // Pattern 3;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+(x1*32),16+(y1*32),256+15+(x1*32),16+15+(y1*32),Color);
+            
+            Color = (AttrByte>>6) & 0x3; // Pattern 4;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+(x1*32),16+(y1*32),16+256+15+(x1*32),16+15+(y1*32),Color);
+         }
+        
+/* NT 2400 */
+        for (x = 0; x < 0x40; x++)
+         {
+            AttrByte = PPU_Rd(0x27C0 + x);
+            x1 = x % 8;
+            y1 = x / 8;
+            
+            
+            Color = AttrByte & 0x3; // Pattern 1;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+256+(x1*32),240+(y1*32),256+256+15+(x1*32),240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>2) & 0x3; // Pattern 2;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+256+(x1*32),240+(y1*32),16+256+256+15+(x1*32),240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>4) & 0x3; // Pattern 3;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+256+(x1*32),240+16+(y1*32),256+256+15+(x1*32),240+16+15+(y1*32),Color);
+            
+            Color = (AttrByte>>6) & 0x3; // Pattern 4;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+256+(x1*32),240+16+(y1*32),16+256+256+15+(x1*32),240+16+15+(y1*32),Color);
+         }
+
+/* NT 2C00 */
+        for (x = 0; x < 0x40; x++)
+        {
+            AttrByte = PPU_Rd(0x2FC0 + x);//PPU_Rd(0x27C0 + x);
+            x1 = x % 8;
+            y1 = x / 8;
+            
+            
+            Color = AttrByte & 0x3; // Pattern 1;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+256+(x1*32),(y1*32),256+256+15+(x1*32),15+(y1*32),Color);
+            
+            Color = (AttrByte>>2) & 0x3; // Pattern 2;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+256+(x1*32),(y1*32),16+256+256+15+(x1*32),15+(y1*32),Color);
+            
+            Color = (AttrByte>>4) & 0x3; // Pattern 3;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+256+(x1*32),16+(y1*32),256+256+15+(x1*32),16+15+(y1*32),Color);
+            
+            Color = (AttrByte>>6) & 0x3; // Pattern 4;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+256+(x1*32),16+(y1*32),16+256+256+15+(x1*32),16+15+(y1*32),Color);
+            }
+
+        if (ppu.DisplayNameTables == 0)
+        {
+            for (x = 0; x < 33; x++)
+            {
+                line(Buffer, 256 + x * 16, 0, 256 + x * 16, 240 + 240, 8);
+                line(Buffer, 256, 0 + x * 16, 256 + 256 + 256, 0 + x * 16, 8);
+            }
+
+            for (x = 0; x < 17; x++)
+            {
+                line(Buffer, 256 + x * 32, 0, 256 + x * 32, 240 + 240, 6);
+                line(Buffer, 256, 0 + x * 32, 256 + 256 + 256, 0 + x * 32, 6);
+            }
+        }
+    }
+
+    if (ppu.DisplayNameTables)
+    {
+/* NT 2000 */
+        for (x = 0; x < 32; x++)
+            for (y = 0; y < 30; y++)
+            {
+                TileID = (PPU_Rd/*ppu.Memory[*/(0x2000 + x + (y * 32))/*]*/ << 4) + ppu.Bg_Pattern_Table;
+
+                for (x1 = 0; x1 < 8; x1++)
+                    for (y1 = 0; y1 < 8; y1++)
+                    {
+                        Color = GetTileColor(TileID, x1, y1);
+                        if (ppu.DisplayAttributeTable != 0)
+                            Color |= (Buffer->line[(8 * y) + 240 + y1][(8 * x) + 256 + x1] & 0x3) << 2;
+
+                        if ((Color != 0) || (ppu.DisplayAttributeTable != 0))
+                        {
+                            Color = ppu.Memory[0x3F00 + Color];
+                            Buffer->line[(8 * y) + 240 + y1][(8 * x) + 256 + x1] = Color;
+                        }
+                    }
+            }
+
+
+/* NT 2800 */
+        for (x = 0; x < 32; x++)
+            for (y = 0; y < 30; y++)
+            {
+                TileID = (PPU_Rd(0x2800 + x + (y * 32)) << 4) + ppu.Bg_Pattern_Table;
+
+                for (x1 = 0; x1 < 8; x1++)
+                    for (y1 = 0; y1 < 8; y1++)
+                    {
+                        Color = GetTileColor(TileID, x1, y1);
+                        if (Color != 0)
+                        {
+                            Color = ppu.Memory[0x3F00 + Color + 4];
+                            Buffer->line[(8 * y) + y1][(8 * x) + 256 + x1] = Color;
+                        }
+                    }
+            }
+
+/* NT 2400 */
+            for (x = 0; x < 32; x++)
+                for (y = 0; y < 30; y++)
+                    {
+                    TileID = (PPU_Rd(0x2400 + x + (y * 32)) << 4) + ppu.Bg_Pattern_Table;
+                    
+                    for (x1 = 0; x1 < 8; x1++)
+                        for (y1 = 0; y1 < 8; y1++)
+                            {
+                            Color = GetTileColor(TileID, x1, y1);
+                            if (ppu.DisplayAttributeTable != 0)
+                                Color |= (Buffer->line[(8 * y) + 240 + y1][(8 * x) + 256 + x1] & 0x3) << 2;
+                            
+                            if ((Color != 0) || (ppu.DisplayAttributeTable != 0))
+                                {
+                                Color = ppu.Memory[0x3F00 + Color];
+                                Buffer->line[(8 * y) + 240+ y1][(8 * x) + 256 + 256 + x1] = Color;
+                                }
+                            }
+                    }
+
+/* NT 2C00 */
+        for (x = 0; x < 32; x++)
+            for (y = 0; y < 30; y++)
+            {
+                TileID = (PPU_Rd(0x2C00 + x + (y * 32)) << 4) + ppu.Bg_Pattern_Table;
+
+                for (x1 = 0; x1 < 8; x1++)
+                    for (y1 = 0; y1 < 8; y1++)
+                    {
+                        Color = GetTileColor(TileID, x1, y1);
+                        if (Color != 0)
+                        {
+                            Color = ppu.Memory[0x3F00 + Color + 12];
+                            Buffer->line[(8 * y) + y1][(8 * x) + 256 + 256 + x1] = Color;
+                        }
+                    }
+            }
+    }
+
+}
+
+
+int
+ PPUVBlank()
+{
+int x, y, x1, y1, i;
+
+unsigned long Reg2;
+unsigned short ab_x, ab_y;
+unsigned long Color;
+unsigned short AttrByte;
+unsigned short TileID;
+static short LAST_FPS = 0;
+unsigned char XScroll, YScroll;
+
+    ppu.StatusRegister.b |= PPU_FLAG_SR_VBLANK;
+        
+    BgColor = ppu.Memory[0x3F00];//0xC0;  
+
+//goto NoDraw;
+
+    acquire_bitmap(Buffer);
+        
+    clear_to_color(Buffer, GetColor(BgColor));
+
+
+    ppu_displayNameTables();
+
+    if (ppu.ControlRegister2.s.Colour != 0)
+        printf("ppu.ColorEmphasis : %d\n", ppu.ControlRegister2.s.Colour);
+
+
+    for (i = 0; i < 249; i++)
+        NbOfSprite[i] = 0;
+
+    ppu.StatusRegister.b &= ~(PPU_FLAG_SR_8SPRT);
+    ppu.HitSpriteAt = 255;
+
+/*
+* A faires les choses qui faut faire durant un lancé de vblank,
+* comme dessiner par ex..
+*/
+
+
+    if (((ppu.ControlRegister2.b & PPU_CR2_BGVISIBILITY) != 0) || ppu.ForceBgVisibility)
+    {
+/* Display BG with scrolling informations */
+/* J'ai la solution :D */
+/*
+frame start (line 0) (if background or sprites are enabled):
+v=t
+*/
+        ppu.VRAMAddrReg2.W = ppu.TimedTmpPtr[0]|0x2000;
+        //printf("Starting addresses : 0x%X\n",ppu.VRAMAddrReg2.W);        
+
+        YScroll = ppu.TmpVScroll;
+
+        for (y = 0; y < 240; y++)
+        {
+/*
+scanline start (if background and sprites are enabled):
+  8421 8421 8421 8421   8421 8421 8421 8421
+v:0000 0100 0001 1111=t:0000 0100 0001 1111
+  1111 1198 7654 3210         
+  5432 10
+*/               
+            if ((y == IRQScanHit)||(y == IRQScanHit + 1))
+                printf("%s: IRQ Hit : bf Reg2: 0x%04X\n", __func__, ppu.VRAMAddrReg2.W);
+
+#define PPU_SSCAN_MASK 0x041F
+
+            ppu.VRAMAddrReg2.W = (ppu.VRAMAddrReg2.W & (~PPU_SSCAN_MASK))
+                               | (ppu.TimedTmpPtr[y] &   PPU_SSCAN_MASK )
+                               | 0x2000;
+
+
+            TileID = (PPU_Rd(ppu.VRAMAddrReg2.W) << 4)
+                   | ppu.Bg_Pattern_Table;
+
+            XScroll = ppu.TimedHScroll[y];
+
+            if (y == IRQScanHit)
+              printf("%s: IRQ Hit    : Reg2: 0x%04X TmpPtr:0x%04X \n", __func__, ppu.VRAMAddrReg2.W, ppu.TimedTmpPtr[y]);
+
+            if (y == IRQScanHit + 1)
+              printf("%s: IRQ Hit + 1: Reg2: 0x%04X TmpPtr:0x%04X \n", __func__, ppu.VRAMAddrReg2.W, ppu.TimedTmpPtr[y]);
+
+           
+            for (x = 0; x < 256; x++)
+            {
+/* Calculer la couleur du point */
+/* Bits 1 et 0 */
+                Color = GetTileColor(TileID, XScroll, YScroll);
+/* Bits 3 et 2 */
+/*
+XScroll : 0,1,2,3,4
+X = (ppu.VRAMAddrReg2.W & 0x1F)
+
+Y : 5,6,7,8,9
+Y = (ppu.VRAMAddrReg2.W & 0x3E0) >> 5
+*/
+/*ab_y = ((ppu.VRAMAddrReg2.W & 0x3E0) >> 5);
+                ab_x = (ppu.VRAMAddrReg2.W & 0x1F);
+
+                AttrByte = (((ab_y) >> 2) << 3) +
+                   ((ab_x) >> 2);
+
+
+                AttrByte = (PPU_Rd((ppu.VRAMAddrReg2.W & 0x2C00) + 0x3C0 + AttrByte) >>
+                        ((((ab_y & 2) * 2) + (((ppu.VRAMAddrReg2.W & 0x2C00)) & 2)))) & 0x03;*/
+                
+                /*
+                 
+                 00DC BA98 7654 3210
+                 
+                 0000 BA54 3c-2 10b-
+                 0000 0000 0001 1100  : 0x001C >> 2 
+                 0000 0011 1000 0000  : 0x0380 >> 4
+                 
+                   10 --11 11-- ----
+                 
+                 0xC000
+                 & 0x0C3F | 0x23C0
+                 
+                                b                  
+                 val >> ( (Reg2 & 0x2) | ( (Reg2 & 0x0x40)>>4)
+                 */
+                
+                Reg2 = ppu.VRAMAddrReg2.W;
+                AttrByte = ((Reg2 & 0x0380) >> 4) | ((Reg2 & 0x001C) >> 2) | (Reg2 & 0x0C00);
+                AttrByte &= 0x0C3F;
+                AttrByte |= 0x23C0;
+                AttrByte = PPU_Rd(AttrByte);
+                AttrByte = AttrByte >> ( 0x0 | (Reg2 & 0x02) | ( (Reg2 & 0x40) >> 4) );
+                AttrByte &= 0x3;
+                
+                if (Color)
+                {
+                    Color |= (AttrByte << 2);
+                    Color = ppu.Memory[0x3F00 + Color];
+                    _putpixel(Buffer, x,y, GetColor(Color));
+                }
+                
+                XScroll++;
+                XScroll &= 7;
+                if (XScroll == 0)
+                {    /* On incrémente le compteur de tile */
+                    if ((ppu.VRAMAddrReg2.W & 0x1F) == 0x1F)
+                    {    /* On met a 0 et change
+                         * l'etat du bit 10 */
+                        ppu.VRAMAddrReg2.W &= ~0x1F;
+
+/* A voir si ya pas bcp mieux */
+/*                        if ((ppu.VRAMAddrReg2.W & 0x400))
+                            ppu.VRAMAddrReg2.W &= ~0x400;
+                        else                        
+                            ppu.VRAMAddrReg2.W |= 0x400;*/
+                        ppu.VRAMAddrReg2.W ^= 0x400;
+                    }
+                    else
+                    {    /* on incremente juste */
+                        ppu.VRAMAddrReg2.W = (ppu.VRAMAddrReg2.W & ~0x1F) | ((ppu.VRAMAddrReg2.W + 1) & 0x1F);
+                    }
+
+                    TileID = (PPU_Rd(ppu.VRAMAddrReg2.W) << 4)
+                             | ppu.Bg_Pattern_Table;
+                }
+            }
+/*
+you can think of bits 5,6,7,8,9 as the "y scroll"(*8).  this functions
+slightly different from the X.  it wraps to 0 and bit 11 is switched when
+it's incremented from _29_ instead of 31.  there are some odd side effects
+from this.. if you manually set the value above 29 (from either 2005 or
+2006), the wrapping from 29 obviously won't happen, and attrib data will be
+used as name table data.  the "y scroll" still wraps to 0 from 31, but
+without switching bit 11.  this explains why writing 240+ to 'Y' in 2005
+appeared as a negative scroll value.
+
+  8421 8421 8421 8421
+v:0000 0011 1110 0000
+  1111 1198 7654 3210         
+  5432 10
+
+*/
+            YScroll++;
+            YScroll &= 7;
+            if (YScroll == 0)
+            {    /* On incrémente le compteur de tile */
+                if ((ppu.VRAMAddrReg2.W & 0x3E0) == (29<<5))
+                {    /* On met a 0 et change l'etat du bit 11 */
+                    ppu.VRAMAddrReg2.W &= ~0x3E0;
+
+/* A voir si ya pas bcp mieux */
+/*                    if ((ppu.VRAMAddrReg2.W & 0x800))
+                        ppu.VRAMAddrReg2.W &= ~0x800;
+                    else                    
+                        ppu.VRAMAddrReg2.W |= 0x800;*/
+                        
+                    ppu.VRAMAddrReg2.W ^= 0x800;
+                }
+                else
+                {    /* on incremente juste */
+                    ppu.VRAMAddrReg2.W = (ppu.VRAMAddrReg2.W & (~0x3F0)) 
+                                       | ((((ppu.VRAMAddrReg2.W & 0x3F0) >> 5) + 1) << 5);
+                }
+            }
+        }
+        //while (!key[KEY_ENTER]);
+    }
+/*
+* if (ppu.ControlRegister2.s.SpriteVisibility == 1)
+* PPUDispSprite(0);
+*/
+
+    if (((ppu.ControlRegister2.b & PPU_CR2_SPRTVISIBILITY) != 0) || ppu.ForceSpriteVisibility)
+    {
+        if (ppu.ControlRegister1.b & PPU_CR1_SPRTSIZE)
+        {
+          NewPPUDispSprite_8x16();
+        }
+        else
+        {
+          NewPPUDispSprite();
+        }
+    }
+
+    if (ppu.DisplayPalette)
+    {
+        textout(Buffer, font, "Bg Palette", 0, 247, 5);
+        textout(Buffer, font, "Sprt Palette", 90, 247, 5);
+
+        rect(Buffer, 0, 255, 4 * 20 + 2, 255 + 4 * 20 + 2, GetColor(0));
+        rect(Buffer, 90, 255, 90 + 4 * 20 + 2, 255 + 4 * 20 + 2, GetColor(0));
+        for (i = 0; i < 16; i++)
+        {
+            rectfill(Buffer, 1 + (i % 4) * 20, 256 + (i / 4) * 20, 1 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, GetColor(ppu.Memory[0x3F00 + i]));
+            rectfill(Buffer, 91 + (i % 4) * 20, 256 + (i / 4) * 20, 91 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, GetColor(ppu.Memory[0x3F10 + i]));
+        }
+    }
+    if (ppu.DisplayVRAM)
+    {
+
+/* y:346 */
+        x1 = 0;
+        y1 = 0;
+        for (i = 0; i < 256; i++)
+        {
+            TileID = 0x0000 + (i << 4);
+
+            for (x = 0; x < 8; x++)
+                for (y = 0; y < 8; y++)
+                {
+
+                    Color = GetTileColor(TileID, x, y);
+
+                    _putpixel(Buffer, 10 + x1 + x, 347 + y1 + y, GetColor(ppu.Memory[0x3F00 + Color]));
+
+                }
+
+
+            x1 += 8;
+            if (x1 >= 128)
+            {
+                x1 = 0;
+                y1 += 8;
+            }
+        }
+        x1 = 0;
+        y1 = 0;
+        for (i = 0; i < 256; i++)
+        {
+            TileID = 0x1000 + (i << 4);
+
+            for (x = 0; x < 8; x++)
+                for (y = 0; y < 8; y++)
+                {
+
+                    Color = GetTileColor(TileID, x, y);
+
+                    _putpixel(Buffer, 10 + 128 + x1 + x, 347 + y1 + y, GetColor(ppu.Memory[0x3F10 + Color]) );
+
+                }
+            x1 += 8;
+            if (x1 >= 128)
+            {
+                x1 = 0;
+                y1 += 8;
+            }
+        }
+    }
+
+    for (i = 0; i < 240; i++)
+    {
+      _putpixel(Buffer, 257 + 0, i, 48);        
+      
+      _putpixel(Buffer, 257 + 1, i, ((ppu.TimedTmpPtr[y]*4))&0xFF);
+      _putpixel(Buffer, 257 + 2, i, ((ppu.TimedTmpPtr[y]*4)>>8)&0xFF);
+      _putpixel(Buffer, 257 + 3, i, ((ppu.TimedTmpPtr[y]*4)>>16)&0xFF);      
+      
+      _putpixel(Buffer, 257 + 4, i, ((ppu.TmpVScroll*4))&0xFF);
+      _putpixel(Buffer, 257 + 5, i, ((ppu.TmpVScroll*4)>>8)&0xFF);
+      _putpixel(Buffer, 257 + 6, i, ((ppu.TmpVScroll*4)>>16)&0xFF);
+      
+      _putpixel(Buffer, 257 + 7, i, ((ppu.TimedHScroll[i]*4)) & 0xFF);
+      _putpixel(Buffer, 257 + 8, i, ((ppu.TimedHScroll[i]*4)>>8) & 0xFF);
+      _putpixel(Buffer, 257 + 9, i, ((ppu.TimedHScroll[i]*4)>>16)& 0xFF);
+
+      _putpixel(Buffer, 257 + 10, i, 48);
+      
+    }
+    
+
+    if (IRQScanHit != -1)
+    {
+        line(Buffer, 257+12, IRQScanHit, 257+22, IRQScanHit, 10);
+        line(Buffer, 257+12, IRQScanHit, 257+18, IRQScanHit-3, 10);
+        line(Buffer, 257+12, IRQScanHit, 257+18, IRQScanHit+3, 10);
+    }
+
+NoDraw:
+
+    textprintf(Buffer, font, 5, 340, GetColor(4), "FPS : %d   IPS : %d", FPS, IPS);
+    textprintf(Buffer, font, 5, 3, GetColor(4), "FPS : %d (CPU@~%2.2fMhz : %d%%)", FPS, (float) (((float) IPS) / 1000000.0), (int) ((((float) IPS) / 1770000.0) * 100.0));
+    
+    release_bitmap(Buffer);
+
+    if (NOBLIT == 0)
+    {
+      blit(Buffer, screen, 0, 0, 0, 0, 512 + 256, 480);
+      //stretch_blit(Buffer, screen, 0, 0, 256, 240, 0, 0, 512, 480);
+    }
+
+
+    if ((ppu.ControlRegister1.b & PPU_CR1_EXECNMI) != 0)
+        return 1;
+
+    return 0;
+}
+
+byte ReadPPUReg(byte RegID)
+{
+/* RegID is the nb of the reg 0-7 */
+    unsigned char ret;
+    RegID &= 0x07;
+    
+    switch (RegID)
+    {
+    default:
+        ret = (unsigned char) 0x00;    /* Can return every thing you
+                         * want here :D */
+        break;
+    case 1:        /* Control Register 2 */
+        ret = ppu.ControlRegister2.b;
+        break;
+    case 2:
+        ret = ppu.StatusRegister.b;
+
+        ppu.StatusRegister.b &= ~(PPU_FLAG_SR_VBLANK);
+        
+        ppu.StatusRegister.b &= ~(PPU_FLAG_SR_SPRT0);
+        ppu.VRAMAddrMode = 0;
+        break;
+    case 5:
+        ret = ppu.VRAMAddrReg2.B.l;
+        break;
+    case 6:
+        ret = ppu.VRAMAddrReg2.B.h;
+        break;
+    case 7:        /* PPU in NES is really strange.. Bufferised
+                 * send is weird.. */
+        if (ppu.VRAMAddrReg2.W < 0x3EFF)
+        {
+            ret = ppu.VRAMBuffer;
+            ppu.VRAMBuffer = PPU_Rd(ppu.VRAMAddrReg2.W);
+            ppu.VRAMAddrReg2.W += ppu.PPU_Inc;
+            ppu.VRAMAddrReg2.W &= 0x3FFF;
+        }
+        else
+        {
+          ret = PPU_Rd(ppu.VRAMAddrReg2.W);
+          ppu.VRAMAddrReg2.W += ppu.PPU_Inc;
+        }
+        break;
+    }
+    return ret;
+}
+
+void WritePPUReg(byte RegID, byte val)
+{
+/* RegID is the nb of the reg 0-7 */
+
+    RegID &= 0x07;
+
+    switch (RegID)
+    {
+        default:/* For not writeable reg */
+        printf("WritePPU error\n");
+        break;
+    case 0:        /* Control Register 1 */
+        ppu.ControlRegister1.b = val;
+        ppu.Bg_Pattern_Table = (ppu.ControlRegister1.s.BgPattern == 1) ? 0x1000 : 0x0000;
+        ppu.Sprt_Pattern_Table = (ppu.ControlRegister1.s.SptPattern == 1) ? 0x1000 : 0x0000;
+        ppu.PPU_Inc = (ppu.ControlRegister1.s.AddrIncrmt == 1) ? 32 : 1;
+
+        ppu.Name_Table_Addresse = 0x2000;
+        switch (ppu.ControlRegister1.s.NameTblAddr)
+        {
+        case 3:
+            ppu.Name_Table_Addresse += 0x400;
+        case 2:
+            ppu.Name_Table_Addresse += 0x400;
+        case 1:
+            ppu.Name_Table_Addresse += 0x400;
+        case 0:
+            break;
+        }
+        ppu.TimedNT[ScanLine] = ppu.ControlRegister1.s.NameTblAddr;
+/*
+2000 write:
+  1111 0011 1111 1111 ( F3FF )
+t:0000 1100 0000 0000 = d:0000 0011
+*/
+        ppu.TmpVRamPtr = ( (ppu.TmpVRamPtr & 0xF3FF)
+                       | ( (ppu.ControlRegister1.s.NameTblAddr & 0x03) << 10 )
+                         );              
+        
+        break;
+    case 1:        /* Control Register 2 */
+        //printf("PPU: new CR2 ; 0x%x\n", val);
+        ppu.ControlRegister2.b = val;
+        break;
+    case 3:        /* SPR-RAM Addresse Register */
+        ppu.SPR_RAMAddr = val;
+        break;
+    case 4:        /* SPR-RAM Input Register */
+        ppu.SPRRAM[ppu.SPR_RAMAddr] = val;
+        break;
+    case 5:        /* VRAM Address register 1 */
+        if (ppu.VRAMAddrMode == 0)
+        {
+/*  
+2005 first write:
+t:0000 0000 0001 1111=d:1111 1000
+x=d:00000111
+*/
+            //printf("2005[1st][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+            ppu.VRAMAddrMode = 1;
+
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0xFFE0) | ((val & 0xF8) >> 3));
+            ppu.HScroll = val & 0x7;
+
+
+            //printf("2005[1st][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+            //printf("%d -> 2005 w1: 0x%04X (val: 0x%02X)\n", ScanLine, ppu.TmpVRamPtr, val);
+        }
+        else
+        {
+/*
+2005 second write:
+
+  1111 1100 0000 0000
+  5432 1098 7654 3210
+  
+  8421 8421 8421 8421
+  -------------------
+t:0000 0011 1110 0000=d:1111 1000
+t:0111 0000 0000 0000=d:0000 0111
+*/
+            //printf("2005[2nd][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+            ppu.VRAMAddrMode = 0;
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & ~(0x03E0)) | ((val & 0xF8) << 2));
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr &   0x8FFF ) | ((val & 0x07) << 12));
+
+            ppu.TmpVScroll = (val & 0x7);
+            //if (ppu.TmpVScroll != 0)
+                //printf("2002: TmpVScroll == %d \n", ppu.TmpVScroll);
+
+            //printf("2005[2nd][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+                        
+            //printf("%d -> 2005 w2: 0x%04X (val: 0x%02X)\n", ScanLine, ppu.TmpVRamPtr, val);
+
+        }
+        break;
+    case 6:        /* VRAM Address register 2 */
+        if (ppu.VRAMAddrMode == 0)
+        {
+            //printf("2006[1st][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+            ppu.VRAMAddrMode = 1;
+/*
+2006 first write:
+t:0011 1111 0000 0000 = d:0011 1111
+t:1100 0000 0000 0000=0
+*/
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0x00FF) | ((val&0x3F) << 8));
+            //printf("2006[1st][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+            
+            //printf("%d -> 2006 w1: 0x%04X (val: 0x%02X)\n", ScanLine, ppu.TmpVRamPtr, val);
+        }
+        else
+        {
+            //printf("2006[2nd][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+            ppu.VRAMAddrMode = 0;
+/*
+2006 second write:
+t:0000000011111111=d:11111111
+v=t
+*/
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0xFF00) | (val & 0x00FF));
+            ppu.VRAMAddrReg2.W = ppu.TmpVRamPtr;
+            //printf("2006[2nd][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+            //printf("%d -> 2006 w2: 0x%04X (val: 0x%02X)\n", ScanLine, ppu.TmpVRamPtr, val);
+
+        }
+        break;
+    case 7:        /* VRAM I/O */
+        PPU_Wr(ppu.VRAMAddrReg2.W, val);
+        ppu.VRAMAddrReg2.W += ppu.PPU_Inc;
+        break;
+    }
+}
+
+void FillSprRamDMA(byte value)
+{
+    int i;
+    for (i = 0x00; i < 0x100; i++)
+    {
+        ppu.SPRRAM[i] = ReadMemory( value, i);
+    }
+}

+ 1279 - 0
src/ppu/ppu.old.c

@@ -0,0 +1,1279 @@
+/*
+ *  PPU emulation - The TI-NESulator Project
+ *  ppu.c
+ *  
+ *  Define and emulate the PPU (Picture Processing Unit) of the real NES
+ * 
+ *  Created by Manoel TRAPIER.
+ *  Copyright (c) 2003-2007 986Corp. All rights reserved.
+ *
+ *  $LastChangedDate: 2007-04-06 17:17:01 +0200 (ven, 06 avr 2007) $
+ *  $Author: mtrapier $
+ *  $HeadURL: file:///media/HD6G/SVNROOT/trunk/TI-NESulator/src/ppu.c $
+ *  $Revision: 32 $
+ *
+ */
+
+#include <allegro.h>
+#include <stdio.h>
+#include <stdlib.h>

+#include "ppu.h"
+#include "memory.h"
+#include "M6502.h"
+
+#define GetTileColor(tile,x1,y1)    ( ( ppu.Memory[tile+y1] & (1<<(7-x1)) ) == 0 ? 0 : 1 ) | \
+                                    ( ( ppu.Memory[tile+y1+8] & (1<<(7-x1)) ) == 0 ? 0 : 1<<1 )
+
+extern PPU ppu;
+extern BITMAP *Buffer;
+extern unsigned short ScanLine;
+unsigned char BgColor;
+
+volatile extern int frame;
+
+volatile extern unsigned long IPS, FPS;
+
+byte NOBLIT = 0;
+
+int InitPPU(PPU * ppu)
+{
+    int i;
+    if ((ppu->Memory = (unsigned char *) malloc(0x4000)) == NULL)
+        return -1;
+
+    NOBLIT = 0;
+
+    
+
+    /* Initializing register.. */
+    ppu->In_VBlank = 0;
+    
+    ppu->BaseOneScreen = 0x2000;
+    
+    ppu->ControlRegister1.b = 0;
+    ppu->ControlRegister2.b = 0;
+    ppu->StatusRegister.b = 0;
+    ppu->SPR_RAMAddr = 0;
+    ppu->VRAMAddrReg1.W = 0;
+    ppu->VRAMAddrReg2.W = 0;
+    ppu->VRAMAddrMode = 0;
+
+    ppu->Bg_Pattern_Table = 0x0000;
+    ppu->Name_Table_Addresse = 0x2000;
+    ppu->Sprt_Pattern_Table = 0x0000;
+    ppu->PPU_Inc = 1;
+
+    ppu->MirrorDir = 0;
+    ppu->ScreenType = 1;
+
+
+    ppu->ForceBgVisibility = 0;
+    ppu->ForceSpriteVisibility = 0;
+
+    ppu->DisplayNameTables = ~0;
+    ppu->DisplayAttributeTable = ~0;
+    ppu->DisplayPalette = ~0;
+    ppu->DisplayVRAM = ~0;
+
+
+    /* Set PPU registers */
+    set_page_rd_hook(0x20, ReadPPUReg);    
+    set_page_wr_hook(0x20, WritePPUReg);
+    
+    set_page_readable(0x20, true);
+    set_page_writeable(0x20, true);
+    
+    
+    /* Set PPU Ghost Registers */
+    for(i = 0x21; i < 0x40; i++)
+        set_page_ghost(i, true, 0x20);
+
+    return 0;
+}
+
+PPUSprite PPUGetSprite(unsigned short i)
+{
+PPUSprite ret;
+
+    ret.y = ppu.SPRRAM[i * 4];
+    ret.tileid = ppu.SPRRAM[i * 4 + 1];
+    /*ret.flags.s.BgPrio = (ppu.SPRRAM[i * 4 + 2] & (1 << 5)) == 0 ? 0 : 1;
+    ret.flags.s.HFlip = (ppu.SPRRAM[i * 4 + 2] & (1 << 6)) == 0 ? 0 : 1;
+    ret.flags.s.UpperColor = ppu.SPRRAM[i * 4 + 2] & 0x03;
+    ret.flags.s.VFlip = (ppu.SPRRAM[i * 4 + 2] & (1 << 7)) == 0 ? 0 : 1;*/
+    ret.flags.b = ppu.SPRRAM[i * 4 + 2];
+    ret.x = ppu.SPRRAM[i * 4 + 3];
+    return ret;
+}
+
+void PPUSetSprite(unsigned short i, PPUSprite *sprt)
+{
+    ppu.SPRRAM[i * 4] = sprt->y;
+    ppu.SPRRAM[i * 4 + 1] = sprt->tileid;
+    /*ret.flags.s.BgPrio = (ppu.SPRRAM[i * 4 + 2] & (1 << 5)) == 0 ? 0 : 1;
+    ret.flags.s.HFlip = (ppu.SPRRAM[i * 4 + 2] & (1 << 6)) == 0 ? 0 : 1;
+    ret.flags.s.UpperColor = ppu.SPRRAM[i * 4 + 2] & 0x03;
+    ret.flags.s.VFlip = (ppu.SPRRAM[i * 4 + 2] & (1 << 7)) == 0 ? 0 : 1;*/
+    ppu.SPRRAM[i * 4 + 2] = sprt->flags.b;
+    ppu.SPRRAM[i * 4 + 3] = sprt->x;
+}
+
+
+//(Addr & 0x1FF)    /* Addr In NT */
+// (Addr & 0xC00) >> 2        /* NT number */
+#define GetNT(a)    ( (a&0xC00) >> 10 )
+#define RelAddr(a)    (a & 0x3FF)
+#define PalAddr(a) (a & 0x1F)
+
+unsigned char PPU_Rd(unsigned short Addr)
+{
+    if (Addr > (unsigned short) 0x3FFF)
+    {
+        Addr &= 0x3FFF;
+    }
+    if ((Addr < 0x3F00) && (Addr >= 0x2000))
+    {
+        if (Addr > 0x3000)
+        {
+            Addr -= 0x1000;
+        }
+        if (ppu.ScreenType == 0)
+        {        /* 1 Screen Mode */
+            return ppu.Memory[RelAddr(Addr) + ppu.BaseOneScreen];
+        }
+        else
+            if (ppu.ScreenType)
+            {    /* Miroring mode */
+                if (ppu.MirrorDir)
+                {    /* Horizontal */
+                    if (GetNT(Addr) & 0x2)
+                    {    /* NT2-3 */
+                        return ppu.Memory[0x2000 + RelAddr(Addr)];
+                    }
+                    else
+                    {
+                        return ppu.Memory[0x2400 + RelAddr(Addr)];
+                    }
+                }
+                else
+                {    /* Vertical */
+                    if (GetNT(Addr) & 0x1)
+                    {    /* NT0-2 */
+                        return ppu.Memory[0x2400 + RelAddr(Addr)];
+                    }
+                    else
+                    {
+                        return ppu.Memory[0x2000 + RelAddr(Addr)];
+                    }
+                }
+            }
+            else
+            {    /* Four Screen mode */
+
+            }
+    }
+    else
+        if (Addr >= 0x3F00)
+        {
+            return ppu.Memory[0x3F00 | PalAddr(Addr)];
+        }
+    return ppu.Memory[Addr];
+}
+
+void PPU_Wr(unsigned short Addr, unsigned char Value)
+{
+    if (Addr > (unsigned short) 0x3FFF)
+    {
+        Addr &= 0x3FFF;
+    }
+    if ((Addr < 0x3F00) && (Addr >= 0x2000))
+    {
+        if (Addr > 0x3000)
+        {
+            Addr -= 0x1000;
+        }
+        if (ppu.ScreenType == 0)
+        {        /* 1 Screen Mode */
+            ppu.Memory[RelAddr(Addr) + 0x2000] = Value;
+        }
+        else
+            if (ppu.ScreenType == 1)
+            {    /* Miroring mode */
+                if (ppu.MirrorDir == 0)
+                {    /* Vertical */
+                    if (GetNT(Addr) & 0x1)
+                    {    /* NT0-2 */
+                        ppu.Memory[0x2400 + RelAddr(Addr)] = Value;
+                    }
+                    else
+                    {
+                        ppu.Memory[0x2000 + RelAddr(Addr)] = Value;
+                    }
+                }
+                else
+                {    /* Horizontal */
+                    if (GetNT(Addr) & 0x2)
+                    {    /* NT2-3 */
+                        ppu.Memory[0x2000 + RelAddr(Addr)] = Value;
+                    }
+                    else
+                    {
+                        ppu.Memory[0x2400 + RelAddr(Addr)] = Value;
+                    }
+                }
+            }
+            else
+            {    /* Four Screen mode */
+
+            }
+    }
+    else
+        if (Addr >= 0x3F00)
+        {
+            //printf("%s palette: color %x new value : %d (0x%x)\n", (PalAddr(Addr) < 0x10) ? "Bgnd" : "Sprt", PalAddr(Addr), Value & 0x3F, Addr);
+            ppu.Memory[ /* 0x3F00 | PalAddr(Addr) */ Addr] = Value & 0x3F;
+            if (PalAddr(Addr) == 0x10)
+                ppu.Memory[0x3F00] = Value & 0x3F;
+
+        }
+        else
+        {
+            ppu.Memory[Addr] = Value;
+        }
+}
+
+unsigned short NbOfSprite[255];
+
+
+void NewPPUDispSprite()
+{
+  int x, y, x1, y1, px, py, i;
+  char Color;
+  PPUSprite sprite;
+  unsigned short bg;
+  short SprtAddr;
+
+  for (i = 63; i >= 0; i--)
+  {
+    sprite = PPUGetSprite(i);
+    bg = sprite.flags.b & PPUSPRITE_FLAGS_BGPRIO;
+    
+    y = sprite.y;
+    if (y < 248)
+    {
+      SprtAddr = ((sprite.tileid) << 4) + ppu.Sprt_Pattern_Table;
+      x = sprite.x;
+      
+      for (y1 = 0; y1 < 8; y1++)
+      {
+    py = y + ((sprite.flags.b & PPUSPRITE_FLAGS_VFLIP) == 0 ? y1 : ((8 - 1) - y1));
+
+    if ((py > 0) && (py < 249) && ((++NbOfSprite[py]) > 7))
+    {
+      ppu.StatusRegister.b |= PPU_FLAG_SR_8SPRT ;
+      //printf("%d Hohoho!\n", py);
+      //          line(Buffer, 0, py+1, 256, py+1, 10);
+      //continue; // Do not display more than 8 sprites on this line :p
+    }
+
+    for (x1 = 0; x1 < 8; x1++)
+    {
+      px = x + ((sprite.flags.b & PPUSPRITE_FLAGS_HFLIP) != 0 ? (7 - x1) : x1);
+      Color = GetTileColor(SprtAddr, x1, y1);
+      if (Color)
+      {
+        Color = (Color) | ((sprite.flags.b & PPUSPRITE_FLAGS_UPPERCOLOR) << 2);
+        Color = ppu.Memory[0x3F10 + Color];
+        if ((i == 0) && (Buffer->line[py][px] != BgColor) && (ppu.HitSpriteAt == 255))
+        {
+          //Ligne utilisé pour le débuguage
+          line(Buffer, 0, py+1, 256, py+1, 10);
+          ppu.HitSpriteAt = py+1;
+        }
+        if ((bg == 0) || ((bg != 0) && (Buffer->line[py][px] == BgColor)))
+          putpixel(Buffer, px, py, Color);
+      }
+    }
+      }
+    }
+  }
+}
+
+
+void NewPPUDispSprite_8x16()
+{
+  int x, y, x1, y1, px, py, i, loop, tile;
+  char Color;
+  PPUSprite sprite;
+  unsigned short bg;
+  short SprtAddr;
+  
+  unsigned short SprtTable;
+  
+  for (i = 63; i >= 0; i--)
+  {
+    sprite = PPUGetSprite(i);
+    bg = sprite.flags.b & PPUSPRITE_FLAGS_BGPRIO;
+    tile = sprite.tileid;
+    y = sprite.y + 1;
+
+    if (y < 248)
+    {
+      if ( (SprtTable = (tile & 0x1) * 0x1000) == 0x1000)
+    tile -=1;
+
+      if ((sprite.flags.b & PPUSPRITE_FLAGS_VFLIP) != 0)
+      {
+    y +=8;
+      }
+
+      for (loop = 0; loop < 2; loop++)
+      {
+    SprtAddr = ((tile) << 4) + SprtTable;
+    x = sprite.x;
+    
+    for (y1 = 0; y1 < 8; y1++)
+    {
+      py = y + ((sprite.flags.b & PPUSPRITE_FLAGS_VFLIP) == 0 ? y1 : ((8 - 1) - y1));
+      if ((py > 0) && (py < 249) && ((++NbOfSprite[py]) > 7))
+      {
+        ppu.StatusRegister.b |= PPU_FLAG_SR_8SPRT ;
+        //        puts("Ho!");
+        //continue; // No more sprites on this line :p
+      }
+      for (x1 = 0; x1 < 8; x1++)
+      {
+        px = x + (((sprite.flags.b & PPUSPRITE_FLAGS_HFLIP) != 0) ? (7 - x1) : x1);
+        Color = GetTileColor(SprtAddr, x1, y1);
+        if (Color)
+        {
+          Color = (Color) | ((sprite.flags.b & PPUSPRITE_FLAGS_UPPERCOLOR) << 2);
+          Color = ppu.Memory[0x3F10 + Color];
+          if ((i == 0) && (Buffer->line[py][px] != BgColor) && (ppu.HitSpriteAt == 255))
+          {
+        //Ligne utilise pour le debuguage
+        line(Buffer, 0, py, 256, py, 10);
+        ppu.HitSpriteAt = py+1;
+          }
+          if ((bg == 0) || ((bg != 0) && (Buffer->line[py][px] == BgColor)))
+        putpixel(Buffer, px, py, Color);
+        }
+      }
+    }
+    tile += 1;
+    if ( (sprite.flags.b & PPUSPRITE_FLAGS_VFLIP) != 0)
+      y -= 8;
+    else
+      y += 8;
+      }    
+    }
+  }
+}
+
+
+void DebugColor()
+{
+  static unsigned short x = 128;
+  static unsigned short y = 128;
+  unsigned char OldDisplayPalette = ppu.DisplayPalette;
+  byte keyb;
+  unsigned int i;
+  unsigned short Color;
+
+  NOBLIT = 1;
+
+  ppu.DisplayPalette = ~0;
+
+  while(!key[KEY_ESC])
+  {
+    frame++;
+    PPUVBlank();
+
+    Color = Buffer->line[y][x];
+
+    textprintf(Buffer, font, 5, 340, 3, "Pos [%d:%d] Color: %d Bg: %d", x, y, Color, BgColor);
+
+    line(Buffer, x-10, y, x+10, y, 1);
+    line(Buffer, x, y-10, x, y+10, 1);
+
+
+    /*
+    rect(Buffer, 0, 255, 4 * 20 + 2, 255 + 4 * 20 + 2, 0);
+    rect(Buffer, 90, 255, 90 + 4 * 20 + 2, 255 + 4 * 20 + 2, 0);
+    for (i = 0; i < 16; i++)
+    {
+      rectfill(Buffer, 1 + (i % 4) * 20, 256 + (i / 4) * 20, 1 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, ppu.Memory[0x3F00 + i]);
+      rectfill(Buffer, 91 + (i % 4) * 20, 256 + (i / 4) * 20, 91 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, ppu.Memory[0x3F10 + i]);
+    }*/
+    for( i = 0; i < 16; i++)
+    {
+      if (ppu.Memory[0x3F00 + i] == Color)
+      {
+    line(Buffer, 1+(i%4)*20, 256 + (i / 4) * 20, 1 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, ~Color); 
+    line(Buffer, 1+(i%4)*20, 256 + (i / 4) * 20 + 20, 1 + (i % 4) * 20 + 20, 256 + (i / 4) * 20, ~Color); 
+      }
+
+      if (ppu.Memory[0x3F10 + i] == Color)
+      {
+    line(Buffer, 91+(i%4)*20, 256 + (i / 4) * 20, 91 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, ~Color); 
+    line(Buffer, 91+(i%4)*20, 256 + (i / 4) * 20 + 20, 91 + (i % 4) * 20 + 20, 256 + (i / 4) * 20, ~Color); 
+      }
+       
+    }
+
+    blit(Buffer, screen, 0, 0, 0, 0, 512 + 256, 480);    
+
+    if (keypressed())
+    {
+      keyb = (readkey() & 0xFF);
+
+      if (keyb == '4')
+      {
+    x--;
+      }
+      if (keyb == '8')
+      {
+    y--;
+      }
+      if (keyb == '6')
+      {
+    x++;
+      }
+      if (keyb == '2')
+      {
+    y++;
+      }
+
+
+    }
+  }
+  ppu.DisplayPalette = OldDisplayPalette;
+  NOBLIT = 0;
+}
+
+void DebugSprites()
+{
+  byte keyb;
+  static int SelSprite = 0;
+  PPUSprite sprite;
+  NOBLIT = 1;
+  ppu.ControlRegister2.b |= PPU_CR2_SPRTVISIBILITY;
+
+  while(!key[KEY_ESC])
+  {
+    frame++;
+    PPUVBlank();
+    sprite = PPUGetSprite(SelSprite);
+
+    if (ppu.ControlRegister1.b & PPU_CR1_SPRTSIZE)
+    {
+      rect(Buffer, sprite.x-1, sprite.y-1, sprite.x+9, sprite.y+17, 1);
+    }
+    else
+    {
+      rect(Buffer, sprite.x-1, sprite.y-1, sprite.x+9, sprite.y+9, 1);
+    }
+
+    textprintf(Buffer, font, 5, 340, 3, "Sprite %d [%d:%d]", SelSprite, sprite.x, sprite.y);
+    textprintf(Buffer, font, 5, 349, 3, "B0: 0x%X  B1: 0x%X  B2: 0x%X  B3: 0x%X",sprite.y,sprite.tileid,sprite.flags.b,sprite.x);
+    textprintf(Buffer, font, 5, 358, 3, "Tile Index: %d", sprite.tileid);
+    textprintf(Buffer, font, 5, 367, 3, "Vertical Flip: %d", sprite.flags.s.VFlip);
+    textprintf(Buffer, font, 5, 376, 3, "Horizontal Flip: %d", sprite.flags.s.HFlip);
+    textprintf(Buffer, font, 5, 385, 3, "Background Priority: %d", sprite.flags.s.BgPrio);
+    textprintf(Buffer, font, 5, 394, 3, "Upper Color: %d", sprite.flags.s.UpperColor);
+
+    blit(Buffer, screen, 0, 0, 0, 0, 512 + 256, 480);    
+
+    if (keypressed())
+    {
+      keyb = (readkey() & 0xFF);
+      if (keyb == '+')
+    SelSprite = (SelSprite<63)?SelSprite+1:0;
+      if (keyb == '-')
+    SelSprite = (SelSprite>0)?SelSprite-1:63;
+
+      if (keyb == 'h')
+      {
+    sprite.flags.s.HFlip = ~sprite.flags.s.HFlip;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == 'b')
+      {
+    sprite.flags.s.BgPrio = ~sprite.flags.s.BgPrio;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == 'v')
+      {
+    sprite.flags.s.VFlip = ~sprite.flags.s.VFlip;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+
+      if (keyb == '4')
+      {
+    sprite.x--;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == '8')
+      {
+    sprite.y--;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == '6')
+      {
+    sprite.x++;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+      if (keyb == '2')
+      {
+    sprite.y++;
+    PPUSetSprite(SelSprite, &sprite);
+      }
+    }
+  }
+  NOBLIT = 0;
+}
+
+#define GetTilePos(addr,x,y) (addr+x+(y*32))
+
+void ppu_displayNameTables()
+{
+int x, y, x1, y1, i;
+
+unsigned long Reg2;
+unsigned short ab_x, ab_y;
+unsigned short Color;
+unsigned short AttrByte;
+unsigned short TileID;  
+
+    if (ppu.DisplayAttributeTable)
+    {
+/* NT 2000 */
+        for (x = 0; x < 0x40; x++)
+        {
+            AttrByte = /*ppu.Memory[0x23C0 + x];*/PPU_Rd(0x23C0 + x);
+            x1 = x % 8;
+            y1 = x / 8;
+            
+            
+            Color = AttrByte & 0x3; // Pattern 1;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+(x1*32),240+(y1*32),256+15+(x1*32),240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>2) & 0x3; // Pattern 2;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+(x1*32),240+(y1*32),16+256+15+(x1*32),240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>4) & 0x3; // Pattern 3;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+(x1*32),16+240+(y1*32),256+15+(x1*32),16+240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>6) & 0x3; // Pattern 4;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+(x1*32),16+240+(y1*32),16+256+15+(x1*32),16+240+15+(y1*32),Color);
+        }
+
+
+/* NT 2800 */
+        for (x = 0; x < 0x40; x++)
+         {
+            AttrByte = PPU_Rd(0x2BC0 + x);
+            x1 = x % 8;
+            y1 = x / 8;
+            
+            
+            Color = AttrByte & 0x3; // Pattern 1;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+(x1*32),(y1*32),256+15+(x1*32),15+(y1*32),Color);
+            
+            Color = (AttrByte>>2) & 0x3; // Pattern 2;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+(x1*32),(y1*32),16+256+15+(x1*32),15+(y1*32),Color);
+            
+            Color = (AttrByte>>4) & 0x3; // Pattern 3;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+(x1*32),16+(y1*32),256+15+(x1*32),16+15+(y1*32),Color);
+            
+            Color = (AttrByte>>6) & 0x3; // Pattern 4;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+(x1*32),16+(y1*32),16+256+15+(x1*32),16+15+(y1*32),Color);
+         }
+        
+/* NT 2400 */
+        for (x = 0; x < 0x40; x++)
+         {
+            AttrByte = PPU_Rd(0x27C0 + x);
+            x1 = x % 8;
+            y1 = x / 8;
+            
+            
+            Color = AttrByte & 0x3; // Pattern 1;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+256+(x1*32),240+(y1*32),256+256+15+(x1*32),240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>2) & 0x3; // Pattern 2;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+256+(x1*32),240+(y1*32),16+256+256+15+(x1*32),240+15+(y1*32),Color);
+            
+            Color = (AttrByte>>4) & 0x3; // Pattern 3;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+256+(x1*32),240+16+(y1*32),256+256+15+(x1*32),240+16+15+(y1*32),Color);
+            
+            Color = (AttrByte>>6) & 0x3; // Pattern 4;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+256+(x1*32),240+16+(y1*32),16+256+256+15+(x1*32),240+16+15+(y1*32),Color);
+         }
+
+/* NT 2C00 */
+        for (x = 0; x < 0x40; x++)
+        {
+            AttrByte = PPU_Rd(0x2FC0 + x);//PPU_Rd(0x27C0 + x);
+            x1 = x % 8;
+            y1 = x / 8;
+            
+            
+            Color = AttrByte & 0x3; // Pattern 1;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+256+(x1*32),(y1*32),256+256+15+(x1*32),15+(y1*32),Color);
+            
+            Color = (AttrByte>>2) & 0x3; // Pattern 2;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+256+(x1*32),(y1*32),16+256+256+15+(x1*32),15+(y1*32),Color);
+            
+            Color = (AttrByte>>4) & 0x3; // Pattern 3;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,256+256+(x1*32),16+(y1*32),256+256+15+(x1*32),16+15+(y1*32),Color);
+            
+            Color = (AttrByte>>6) & 0x3; // Pattern 4;
+            if (ppu.DisplayNameTables == 0)
+                Color = ppu.Memory[0x3F00 + (Color * 4) + 1];
+            
+            rectfill(Buffer,16+256+256+(x1*32),16+(y1*32),16+256+256+15+(x1*32),16+15+(y1*32),Color);
+            }
+
+        if (ppu.DisplayNameTables == 0)
+        {
+            for (x = 0; x < 33; x++)
+            {
+                line(Buffer, 256 + x * 16, 0, 256 + x * 16, 240 + 240, 8);
+                line(Buffer, 256, 0 + x * 16, 256 + 256 + 256, 0 + x * 16, 8);
+            }
+
+            for (x = 0; x < 17; x++)
+            {
+                line(Buffer, 256 + x * 32, 0, 256 + x * 32, 240 + 240, 6);
+                line(Buffer, 256, 0 + x * 32, 256 + 256 + 256, 0 + x * 32, 6);
+            }
+        }
+    }
+
+    if (ppu.DisplayNameTables)
+    {
+/* NT 2000 */
+        for (x = 0; x < 32; x++)
+            for (y = 0; y < 30; y++)
+            {
+                TileID = (PPU_Rd/*ppu.Memory[*/(0x2000 + x + (y * 32))/*]*/ << 4) + ppu.Bg_Pattern_Table;
+
+                for (x1 = 0; x1 < 8; x1++)
+                    for (y1 = 0; y1 < 8; y1++)
+                    {
+                        Color = GetTileColor(TileID, x1, y1);
+                        if (ppu.DisplayAttributeTable != 0)
+                            Color |= (Buffer->line[(8 * y) + 240 + y1][(8 * x) + 256 + x1] & 0x3) << 2;
+
+                        if ((Color != 0) || (ppu.DisplayAttributeTable != 0))
+                        {
+                            Color = ppu.Memory[0x3F00 + Color];
+                            Buffer->line[(8 * y) + 240 + y1][(8 * x) + 256 + x1] = Color;
+                        }
+                    }
+            }
+
+
+/* NT 2800 */
+        for (x = 0; x < 32; x++)
+            for (y = 0; y < 30; y++)
+            {
+                TileID = (PPU_Rd(0x2800 + x + (y * 32)) << 4) + ppu.Bg_Pattern_Table;
+
+                for (x1 = 0; x1 < 8; x1++)
+                    for (y1 = 0; y1 < 8; y1++)
+                    {
+                        Color = GetTileColor(TileID, x1, y1);
+                        if (Color != 0)
+                        {
+                            Color = ppu.Memory[0x3F00 + Color + 4];
+                            Buffer->line[(8 * y) + y1][(8 * x) + 256 + x1] = Color;
+                        }
+                    }
+            }
+
+/* NT 2400 */
+            for (x = 0; x < 32; x++)
+                for (y = 0; y < 30; y++)
+                    {
+                    TileID = (PPU_Rd(0x2400 + x + (y * 32)) << 4) + ppu.Bg_Pattern_Table;
+                    
+                    for (x1 = 0; x1 < 8; x1++)
+                        for (y1 = 0; y1 < 8; y1++)
+                            {
+                            Color = GetTileColor(TileID, x1, y1);
+                            if (ppu.DisplayAttributeTable != 0)
+                                Color |= (Buffer->line[(8 * y) + 240 + y1][(8 * x) + 256 + x1] & 0x3) << 2;
+                            
+                            if ((Color != 0) || (ppu.DisplayAttributeTable != 0))
+                                {
+                                Color = ppu.Memory[0x3F00 + Color];
+                                Buffer->line[(8 * y) + 240+ y1][(8 * x) + 256 + 256 + x1] = Color;
+                                }
+                            }
+                    }
+
+/* NT 2C00 */
+        for (x = 0; x < 32; x++)
+            for (y = 0; y < 30; y++)
+            {
+                TileID = (PPU_Rd(0x2C00 + x + (y * 32)) << 4) + ppu.Bg_Pattern_Table;
+
+                for (x1 = 0; x1 < 8; x1++)
+                    for (y1 = 0; y1 < 8; y1++)
+                    {
+                        Color = GetTileColor(TileID, x1, y1);
+                        if (Color != 0)
+                        {
+                            Color = ppu.Memory[0x3F00 + Color + 12];
+                            Buffer->line[(8 * y) + y1][(8 * x) + 256 + 256 + x1] = Color;
+                        }
+                    }
+            }
+    }
+
+}
+
+
+int
+ PPUVBlank()
+{
+int x, y, x1, y1, i;
+
+unsigned long Reg2;
+unsigned short ab_x, ab_y;
+unsigned short Color;
+unsigned short AttrByte;
+unsigned short TileID;
+static short LAST_FPS = 0;
+unsigned char XScroll, YScroll;
+
+    ppu.StatusRegister.b |= PPU_FLAG_SR_VBLANK;
+        
+    BgColor = ppu.Memory[0x3F00];//0xC0;  
+
+//goto NoDraw;
+
+    acquire_bitmap(Buffer);
+        
+    clear_to_color(Buffer, BgColor);
+
+/*    if (ppu.ControlRegister2.s.Colour != 0)
+        printf("ppu.ColorEmphasis : %d", ppu.ControlRegister2.s.Colour);*/
+
+
+    for (i = 0; i < 249; i++)
+        NbOfSprite[i] = 0;
+
+    ppu.StatusRegister.b &= ~(PPU_FLAG_SR_8SPRT);
+    ppu.HitSpriteAt = 255;
+
+/*
+* A faires les choses qui faut faire durant un lancé de vblank,
+* comme dessiner par ex..
+*/
+
+
+    if (((ppu.ControlRegister2.b & PPU_CR2_BGVISIBILITY) != 0) || ppu.ForceBgVisibility)
+    {
+/* Display BG with scrolling informations */
+/* J'ai la solution :D */
+/*
+frame start (line 0) (if background or sprites are enabled):
+v=t
+*/
+        ppu.VRAMAddrReg2.W = ppu.TimedTmpPtr[0] | 0x2000;
+        //printf("Starting addresses : 0x%X\n",ppu.VRAMAddrReg2.W);
+        
+        XScroll = ppu.TimedHScroll[0];
+
+        YScroll = ppu.TmpVScroll;
+
+        for (y = 0; y < 240; y++)
+        {
+/*
+scanline start (if background and sprites are enabled):
+  8421 8421 8421 8421   8421 8421 8421 8421
+v:0000 0100 0001 1111=t:0000 0100 0001 1111
+  1111 1198 7654 3210         
+  5432 10
+*/
+            //if (y == 142)
+            //    printf("______________142 Ptr:0x%04X ____ 0x%04X\n", ppu.TimedTmpPtr[y], ppu.VRAMAddrReg2.W);
+                
+            ppu.VRAMAddrReg2.W = (ppu.VRAMAddrReg2.W & 0xFBE0)
+               | ((ppu.TimedTmpPtr[y]) & 0x041F)
+               | 0x2000;
+
+            //if (y == 142)
+            //    printf("______________142 Ptr:0x%04X ____ 0x%04X\n", ppu.TimedTmpPtr[y], ppu.VRAMAddrReg2.W);
+            
+            TileID = (PPU_Rd(ppu.VRAMAddrReg2.W) << 4)
+               | ppu.Bg_Pattern_Table;
+
+            XScroll = ppu.TimedHScroll[y];
+           
+            for (x = 0; x < 256; x++)
+            {
+/* Calculer la couleur du point */
+/* Bits 1 et 0 */
+                Color = GetTileColor(TileID, XScroll, YScroll);
+/* Bits 3 et 2 */
+/*
+XScroll : 0,1,2,3,4
+X = (ppu.VRAMAddrReg2.W & 0x1F)
+
+Y : 5,6,7,8,9
+Y = (ppu.VRAMAddrReg2.W & 0x3E0) >> 5
+*/
+/*ab_y = ((ppu.VRAMAddrReg2.W & 0x3E0) >> 5);
+                ab_x = (ppu.VRAMAddrReg2.W & 0x1F);
+
+                AttrByte = (((ab_y) >> 2) << 3) +
+                   ((ab_x) >> 2);
+
+
+                AttrByte = (PPU_Rd((ppu.VRAMAddrReg2.W & 0x2C00) + 0x3C0 + AttrByte) >>
+                        ((((ab_y & 2) * 2) + (((ppu.VRAMAddrReg2.W & 0x2C00)) & 2)))) & 0x03;*/
+                
+                /*
+                 
+                 00DC BA98 7654 3210
+                 
+                 0000 BA54 3c-2 10b-
+                 0000 0000 0001 1100  : 0x001C >> 2 
+                 0000 0011 1000 0000  : 0x0380 >> 4
+                 
+                   10 --11 11-- ----
+                 
+                 0xC000
+                 & 0x0C3F | 0x23C0
+                 
+                                b                  
+                 val >> ( (Reg2 & 0x2) | ( (Reg2 & 0x0x40)>>4)
+                 */
+                
+                Reg2 = ppu.VRAMAddrReg2.W;
+                AttrByte = ((Reg2 & 0x0380) >> 4) | ((Reg2 & 0x001C) >> 2) | (Reg2 & 0x0C00);
+                AttrByte &= 0x0C3F;
+                AttrByte |= 0x23C0;
+                AttrByte = PPU_Rd(AttrByte);
+                AttrByte = AttrByte >> ( 0x0 | (Reg2 & 0x02) | ( (Reg2 & 0x40) >> 4) );
+                AttrByte &= 0x3;
+                
+                if (Color)
+                {
+                    Color |= (AttrByte << 2);
+                    Color = ppu.Memory[0x3F00 + Color];
+                    Buffer->line[y][x] = Color + 0xC0;
+                }
+                
+                XScroll++;
+                XScroll &= 7;
+                if (XScroll == 0)
+                {    /* On incrémente le compteur de tile */
+                    if ((ppu.VRAMAddrReg2.W & 0x1F) == 0x1F)
+                    {    /* On met a 0 et change
+                         * l'etat du bit 10 */
+                        ppu.VRAMAddrReg2.W &= ~0x1F;
+
+/* A voir si ya pas bcp mieux */
+                        if ((ppu.VRAMAddrReg2.W & 0x400))
+                            ppu.VRAMAddrReg2.W &= ~0x400;
+                        else
+                            ppu.VRAMAddrReg2.W |= 0x400;
+                    }
+                    else
+                    {    /* on incremente juste */
+                        ppu.VRAMAddrReg2.W = (ppu.VRAMAddrReg2.W & ~0x1F) | ((ppu.VRAMAddrReg2.W + 1) & 0x1F);
+                    }
+
+                    TileID = (PPU_Rd(ppu.VRAMAddrReg2.W) << 4)
+                             | ppu.Bg_Pattern_Table;
+                }
+            }
+/*
+you can think of bits 5,6,7,8,9 as the "y scroll"(*8).  this functions
+slightly different from the X.  it wraps to 0 and bit 11 is switched when
+it's incremented from _29_ instead of 31.  there are some odd side effects
+from this.. if you manually set the value above 29 (from either 2005 or
+2006), the wrapping from 29 obviously won't happen, and attrib data will be
+used as name table data.  the "y scroll" still wraps to 0 from 31, but
+without switching bit 11.  this explains why writing 240+ to 'Y' in 2005
+appeared as a negative scroll value.
+*/
+            YScroll++;
+            YScroll &= 7;
+            if (YScroll == 0)
+            {    /* On incrémente le compteur de tile */
+                if ((ppu.VRAMAddrReg2.W & 0x3E0) == 0x3A0)
+                {    /* On met a 0 et change l'etat du bit
+                     * 10 */
+                    ppu.VRAMAddrReg2.W &= ~0x3F0;
+
+/* A voir si ya pas bcp mieux */
+                    if ((ppu.VRAMAddrReg2.W & 0x800))
+                        ppu.VRAMAddrReg2.W &= ~0x800;
+                    else
+                        ppu.VRAMAddrReg2.W |= 0x800;
+                }
+                else
+                {    /* on incremente juste */
+                    ppu.VRAMAddrReg2.W = (ppu.VRAMAddrReg2.W & ~0x3F0) | ((((ppu.VRAMAddrReg2.W & 0x3F0) >> 5) + 1) << 5);
+                }
+            }
+        }
+        //while (!key[KEY_ENTER]);
+    }
+/*
+* if (ppu.ControlRegister2.s.SpriteVisibility == 1)
+* PPUDispSprite(0);
+*/
+
+    if (((ppu.ControlRegister2.b & PPU_CR2_SPRTVISIBILITY) != 0) || ppu.ForceSpriteVisibility)
+    {
+               if (ppu.ControlRegister1.b & PPU_CR1_SPRTSIZE)
+        {
+          NewPPUDispSprite_8x16();
+        }
+        else
+        {
+          NewPPUDispSprite();
+        }
+    }
+
+/*    for(x=0;x<256;x++)
+        for(y=0;y<240;y++)
+        {
+            if ((i = getpixel(Buffer,x,y)) >= 0xC0)
+                putpixel(Buffer,x,y,ppu.Memory[0x3F00+i-0xC0]);
+        }*/
+
+    if (ppu.DisplayPalette)
+    {
+        textout(Buffer, font, "Bg Palette", 0, 247, 5);
+        textout(Buffer, font, "Sprt Palette", 90, 247, 5);
+
+        rect(Buffer, 0, 255, 4 * 20 + 2, 255 + 4 * 20 + 2, 0);
+        rect(Buffer, 90, 255, 90 + 4 * 20 + 2, 255 + 4 * 20 + 2, 0);
+        for (i = 0; i < 16; i++)
+        {
+            rectfill(Buffer, 1 + (i % 4) * 20, 256 + (i / 4) * 20, 1 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, ppu.Memory[0x3F00 + i]);
+            rectfill(Buffer, 91 + (i % 4) * 20, 256 + (i / 4) * 20, 91 + (i % 4) * 20 + 20, 256 + (i / 4) * 20 + 20, ppu.Memory[0x3F10 + i]);
+        }
+    }
+    if (ppu.DisplayVRAM)
+    {
+
+/* y:346 */
+        x1 = 0;
+        y1 = 0;
+        for (i = 0; i < 256; i++)
+        {
+            TileID = 0x0000 + (i << 4);
+
+            for (x = 0; x < 8; x++)
+                for (y = 0; y < 8; y++)
+                {
+
+                    Color = GetTileColor(TileID, x, y);
+
+                    putpixel(Buffer, 10 + x1 + x, 347 + y1 + y, ppu.Memory[0x3F00 + Color]);
+
+                }
+
+
+            x1 += 8;
+            if (x1 >= 128)
+            {
+                x1 = 0;
+                y1 += 8;
+            }
+        }
+        x1 = 0;
+        y1 = 0;
+        for (i = 0; i < 256; i++)
+        {
+            TileID = 0x1000 + (i << 4);
+
+            for (x = 0; x < 8; x++)
+                for (y = 0; y < 8; y++)
+                {
+
+                    Color = GetTileColor(TileID, x, y);
+
+                    putpixel(Buffer, 10 + 128 + x1 + x, 347 + y1 + y, ppu.Memory[0x3F10 + Color]);
+
+                }
+            x1 += 8;
+            if (x1 >= 128)
+            {
+                x1 = 0;
+                y1 += 8;
+            }
+        }
+    }
+
+    for (i = 0; i < 240; i++)
+    {
+      putpixel(Buffer, 0, i, ppu.TimedVScroll[i] & 0xFF);
+      putpixel(Buffer, 1, i, ppu.TimedVScroll[i] & 0xFF);
+      putpixel(Buffer, 2, i, ppu.TimedVScroll[i] & 0xFF);
+    }
+
+NoDraw:
+
+    textprintf(Buffer, font, 5, 340, 4, "FPS : %d   IPS : %d", FPS, IPS);
+    textprintf(Buffer, font, 5, 3, 4, "FPS : %d (CPU@~%2.2fMhz : %d%%)", FPS, (float) (((float) IPS) / 1000000.0), (int) ((((float) IPS) / 1770000.0) * 100.0));
+    
+    release_bitmap(Buffer);
+
+    if (NOBLIT == 0)
+    {
+      blit(Buffer, screen, 0, 0, 0, 0, 512 + 256, 480);
+      //stretch_blit(Buffer, screen, 0, 0, 256, 240, 0, 0, 512, 480);
+    }
+
+
+    if ((ppu.ControlRegister1.b & PPU_CR1_EXECNMI) != 0)
+        return 1;
+
+    return 0;
+}
+
+byte ReadPPUReg(byte RegID)
+{
+/* RegID is the nb of the reg 0-7 */
+    unsigned char ret;
+    RegID &= 0x07;
+    
+    switch (RegID)
+    {
+    default:
+        ret = (unsigned char) 0x00;    /* Can return every thing you
+                         * want here :D */
+        break;
+    case 1:        /* Control Register 2 */
+        ret = ppu.ControlRegister2.b;
+        break;
+    case 2:
+        ret = ppu.StatusRegister.b;
+
+        ppu.StatusRegister.b &= ~(PPU_FLAG_SR_VBLANK);
+        
+        ppu.StatusRegister.b &= ~(PPU_FLAG_SR_SPRT0);
+        ppu.VRAMAddrMode = 0;
+        break;
+    case 5:
+        ret = ppu.VRAMAddrReg2.B.l;
+        break;
+    case 6:
+        ret = ppu.VRAMAddrReg2.B.h;
+        break;
+    case 7:        /* PPU in NES is really strange.. Bufferised
+                 * send is weird.. */
+        if (ppu.VRAMAddrReg2.W < 0x3EFF)
+        {
+            ret = ppu.VRAMBuffer;
+            ppu.VRAMBuffer = PPU_Rd(ppu.VRAMAddrReg2.W);
+            ppu.VRAMAddrReg2.W += ppu.PPU_Inc;
+            ppu.VRAMAddrReg2.W &= 0x3FFF;
+        }
+        else
+        {
+          ret = PPU_Rd(ppu.VRAMAddrReg2.W);
+          ppu.VRAMAddrReg2.W += ppu.PPU_Inc;
+        }
+        break;
+    }
+    return ret;
+}
+
+void WritePPUReg(byte RegID, byte val)
+{
+/* RegID is the nb of the reg 0-7 */
+
+    RegID &= 0x07;
+
+    switch (RegID)
+    {
+        default:/* For not writeable reg */
+        printf("WritePPU error\n");
+        break;
+    case 0:        /* Control Register 1 */
+        ppu.ControlRegister1.b = val;
+        ppu.Bg_Pattern_Table = (ppu.ControlRegister1.s.BgPattern == 1) ? 0x1000 : 0x0000;
+        ppu.Sprt_Pattern_Table = (ppu.ControlRegister1.s.SptPattern == 1) ? 0x1000 : 0x0000;
+        ppu.PPU_Inc = (ppu.ControlRegister1.s.AddrIncrmt == 1) ? 32 : 1;
+
+        ppu.Name_Table_Addresse = 0x2000;
+        switch (ppu.ControlRegister1.s.NameTblAddr)
+        {
+        case 3:
+            ppu.Name_Table_Addresse += 0x400;
+        case 2:
+            ppu.Name_Table_Addresse += 0x400;
+        case 1:
+            ppu.Name_Table_Addresse += 0x400;
+        case 0:
+            break;
+        }
+            ppu.TimedNT[ScanLine] = ppu.ControlRegister1.s.NameTblAddr;
+/*
+2000 write:
+  1111 0011 1111 1111 ( F3FF )
+t:0000 1100 0000 0000 = d:0000 0011
+*/
+        ppu.TmpVRamPtr = ( (ppu.TmpVRamPtr & 0xF3FF)
+                       | ( ((ppu.ControlRegister1.s.NameTblAddr) & 0x03) << 10 )
+                         );
+
+        break;
+    case 1:        /* Control Register 2 */
+        //printf("PPU: new CR2 ; 0x%x\n", val);
+        ppu.ControlRegister2.b = val;
+        break;
+    case 3:        /* SPR-RAM Addresse Register */
+        ppu.SPR_RAMAddr = val;
+        break;
+    case 4:        /* SPR-RAM Input Register */
+        ppu.SPRRAM[ppu.SPR_RAMAddr] = val;
+        break;
+    case 5:        /* VRAM Address register 1 */
+        if (ppu.VRAMAddrMode == 0)
+        {
+/*  
+2005 first write:
+t:0000 0000 0001 1111=d:1111 1000
+x=d:00000111
+*/
+            //printf("2005[1st][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+            ppu.VRAMAddrMode = 1;
+
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0xFFE0) | ((val & 0xF8) >> 3));
+            ppu.HScroll = val & 0x7;
+
+
+            //printf("2005[1st][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+            //printf("%d -> 2005 w1: 0x%04X (val: 0x%02X)\n", ScanLine, ppu.TmpVRamPtr, val);
+        }
+        else
+        {
+/*
+2005 second write:
+
+  1111 1100 0000 0000
+  5432 1098 7654 3210
+  
+  8421 8421 8421 8421
+  -------------------
+t:0000 0011 1110 0000=d:1111 1000
+t:0111 0000 0000 0000=d:0000 0111
+*/
+            //printf("2005[2nd][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+            ppu.VRAMAddrMode = 0;
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0xFC1F) | ((val & 0xF8) << 2));
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0x8FFF) | ((val & 0x07) << 12));
+
+            ppu.TmpVScroll = (val & 0x7);
+            //if (ppu.TmpVScroll != 0)
+                //printf("2002: TmpVScroll == %d \n", ppu.TmpVScroll);
+
+            //printf("2005[2nd][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+                        
+            //printf("%d -> 2005 w2: 0x%04X (val: 0x%02X)\n", ScanLine, ppu.TmpVRamPtr, val);
+
+        }
+        break;
+    case 6:        /* VRAM Address register 2 */
+        if (ppu.VRAMAddrMode == 0)
+        {
+            //printf("2006[1st][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+            ppu.VRAMAddrMode = 1;
+/*
+2006 first write:
+t:0011 1111 0000 0000 = d:0011 1111
+t:1100 0000 0000 0000=0
+*/
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0xC0FF) | ((val&0x3F) << 8)) & 0x3FFF;
+            //printf("2006[1st][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+            
+            //printf("%d -> 2006 w1: 0x%04X (val: 0x%02X)\n", ScanLine, ppu.TmpVRamPtr, val);
+        }
+        else
+        {
+            //printf("2006[2nd][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+            ppu.VRAMAddrMode = 0;
+/*
+2006 second write:
+t:0000000011111111=d:11111111
+v=t
+*/
+            ppu.TmpVRamPtr = ((ppu.TmpVRamPtr & 0xFF00) | (val & 0x00FF));
+            ppu.VRAMAddrReg2.W = ppu.TmpVRamPtr;
+            //printf("2006[2nd][%d]: 0x%02X [0x%04X]\n", ScanLine, val, ppu.TmpVRamPtr);
+            //printf("%d -> 2006 w2: 0x%04X (val: 0x%02X)\n", ScanLine, ppu.TmpVRamPtr, val);
+
+        }
+        break;
+    case 7:        /* VRAM I/O */
+        PPU_Wr(ppu.VRAMAddrReg2.W, val);
+        ppu.VRAMAddrReg2.W += ppu.PPU_Inc;
+        break;
+    }
+}
+
+void FillSprRamDMA(byte value)
+{
+    int i;
+    for (i = 0x00; i < 0x100; i++)
+    {
+        ppu.SPRRAM[i] = ReadMemory( value, i);
+    }
+}

+ 75 - 0
src/utils/bin2h/bin2h.c

@@ -0,0 +1,75 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+
+int main(int argc, char *argv[])
+{
+	int i;
+	char *infile;
+	FILE *fpin = stdin;
+	FILE *fpout = stdout;
+	short c;
+	infile = "stdin";
+	if (argc > 1)
+	{
+	
+		for(i = 1; argv[i] && argv[i][0] == '-'; i++)
+		{
+			if (i < argc)
+			{
+				switch(argv[i][1])
+				{
+				case 'i':
+					fpin = fopen(argv[i+1], "rb");
+					infile = argv[i+1];
+					if (fpin == NULL)
+					{
+						fprintf(stderr,"Error: cannot open in file '%s'\n", argv[i+1]);
+						exit(-1);
+					}
+					i++;
+					break;
+					
+				case 'o':
+				
+					fpout = fopen(argv[i+1], "wb");
+					if (fpout == NULL)
+					{
+						fprintf(stderr,"Error: cannot open out file '%s'\n", argv[i+1]);
+						exit(-1);
+					}
+					i++;
+					break;
+					
+				default:
+					fprintf(stderr,"Error: unknown argument: %s\n", argv[i]);
+					exit(-1);
+				}
+			}
+		}
+	}
+
+	fprintf(fpout, "/* Generated data file from file '%s' */\n\n\n", infile);
+	
+	fprintf(fpout, "unsigned char data[] = {\n");
+	i = 0;
+	while((c = fgetc(fpin)) >= 0)
+	{
+		if (i == 0)
+			fprintf(fpout, "\t\t0x%02X", (unsigned char)c);
+		else
+			fprintf(fpout, ", 0x%02X", (unsigned char)c);
+			
+		i++;
+		if (i > 10)
+		{
+			fprintf(fpout,", \\\n");
+			i = 0;
+		}
+	
+	
+	}
+	fprintf(fpout, "\n\t\t};\n");
+
+	return 0;
+}

+ 94 - 0
src/utils/sendromdata.py

@@ -0,0 +1,94 @@
+"""
+Simple TI-NESulator game database importer
+
+This python application will generate a XML file on the output with all information needed to import in the TI-NESulator
+game database.
+"""
+
+import sys, md5, sha, urllib, urlparse
+
+
+def get_page(url, post_data=None, headers=()):
+    """
+    Helper method that gets the given URL, handling headers
+    """
+    opener = urllib.URLopener()
+    for k, v in headers:
+        opener.addheader(k, v)
+    try:
+        f = opener.open(url, post_data)
+    except IOError, e:
+        if e[1] == 302:
+            return '<html></html>'
+        else:
+            raise
+    return f.read()
+
+if __name__ == '__main__':
+    #print "<gamelist>"
+    for filename in sys.argv[1:]:
+        f = open(filename)
+    
+        
+        try:
+        
+            fs = f.read()
+            if fs[0:4] == "NES%c" % 0x1A:
+                Flags = ord(fs[6]) & 0x0F;
+                DiskDude = 0
+                if fs[7:16] == "DiskDude!":
+                    DiskDude = 1
+                    
+                mapperID = ord(fs[6]) >> 4
+                if DiskDude == 0:
+                    mapperID = mapperID | (ord(fs[7]) & 0xF0)
+                    
+                prgsize = ord(fs[4]) * 16 * 1024
+                chrsize = ord(fs[5]) *  8 * 1024
+                mirror = 0
+                if Flags & 0x01:
+                    mirror = 1
+                    
+                sram = 0
+                if Flags & 0x02:
+                    sram = 1
+                    
+                Trainer = 0               
+                if Flags & 0x04:
+                    Trainer = 1
+                
+                print " <game>"
+                print "  <name>%s</name>" % filename
+                print "  <sha>%s</sha>" % sha.new(fs).hexdigest()
+                print "  <md5>%s</md5>" % md5.new(fs).hexdigest()
+                print "  <mapperID>%d</mapperID>" % mapperID
+                print "  <prgsize>%d</prgsize>" % prgsize
+                print "  <chrsize>%d</chrsize>" % chrsize
+                print "  <miror>%d</miror>" % mirror
+                print "  <sram>%d</sram>" % sram
+                print "  <trainer>%d</trainer>" % Trainer
+                print "  <diskdude>%d</diskdude>" % DiskDude
+                print " </game>"
+                 
+                 
+                #will fill the DB :
+                url = "http://127.0.0.1/~mtrapier/nesstat/add.php"
+                
+                html = get_page(url, urllib.urlencode({
+                    'n': filename,
+                    'md5': md5.new(fs).hexdigest(),
+                    'sha1': sha.new(fs).hexdigest(),
+                    'm': mapperID,
+                    'prg': prgsize,
+                    'chr': chrsize,
+                    'mir': mirror,
+                    'sram': sram,
+                    't': Trainer,
+                    'd': DiskDude,
+                }))
+                
+                print html
+        finally:
+            f.close()
+            
+    #print "</gamelist>"

+ 56 - 0
unix/Makefile

@@ -0,0 +1,56 @@
+CC = gcc
+
+#For Debug
+#CFLAGS = -g `allegro-config --cflags debug` -I../src -Wall -pg -DISPAL -DNO_DECIMAL -DUNIX -DFAST_RDOP -DLSB_FIRST -DUSE_SOUND
+
+#For Retail
+CFLAGS = -I$(SRC)/include -I$(SRC) -g `allegro-config --cflags release`l -DISNTSC -DNO_DECIMAL -DUNIX -DFAST_RDOP -DLSB_FIRST -DDEBUG -DUSE_SOUND
+# -fomit-frame-pointer -funroll-loops 
+#-DNO_N_KEY
+#-DUSE_SOUND
+#-DDETECT_BUS_CONFLICT
+#-DISNTSC or -DISPAL
+#-DDEBUG 
+
+LDFLAGS = `allegro-config --libs debug` -lpthread
+#-pg #-lefence
+#debug` -lpthread -lefence -pg
+#release` -lpthread
+#-pg
+
+SRC=../src
+PLUGINS_DIR=pluginsmanager/plugins/
+MAPPERS_DIR=mappersmanager/mappers/
+
+PLUGINS=$(shell ls $(SRC)/$(PLUGINS_DIR)/*.c)
+MAPPERS=$(shell ls $(SRC)/$(MAPPERS_DIR)/*.c)
+
+all: tines
+
+tines: tines.a corecpu.a apu.a ppu.a plugins.a mappers.a memory.a
+	$(CC)  -o $@ $(LDFLAGS) $^
+
+apu.a: $(SRC)/apu/Sound.c $(SRC)/apu/SndAlleg.c
+	$(CC) $(CFLAGS) -c $^ -o $@
+	
+corecpu.a: $(SRC)/corecpu/Debug.c $(SRC)/corecpu/M6502.c
+	$(CC) $(CFLAGS) -Wno-pointer-sign -c $^ -o $@
+	
+tines.a: $(SRC)/main.c $(SRC)/paddle.c $(SRC)/NESCarts.c
+	$(CC) $(CFLAGS) -c $^ -o $@
+	
+memory.a: $(SRC)/memorymanager/memory.c
+	$(CC) $(CFLAGS) -c $^ -o $@
+	
+ppu.a: $(SRC)/ppu/ppu.c $(SRC)/ppu/ppu.memory.c $(SRC)/ppu/debug/ppu.debug.c
+	$(CC) $(CFLAGS) -c $^ -o $@
+	
+mappers.a: $(SRC)/mappersmanager/manager.c $(SRC)/mappersmanager/utils.c $(MAPPERS)
+	$(CC) $(CFLAGS) -c $^ -o $@
+	
+plugins.a: $(SRC)/pluginsmanager/manager.c $(PLUGINS)
+	$(CC) $(CFLAGS) -c $^ -o $@
+
+.PHONY: clean
+clean:
+	rm -Rf *.o *~ core