From 3f4b4990ea43b8c71e87d7d9dc65e9ddb48b0201 Mon Sep 17 00:00:00 2001 From: godzil Date: Wed, 20 Feb 2008 15:40:31 +0000 Subject: [PATCH] TI-NESulator SVN reimport --- doc/Changements.txt | 105 ++ doc/Changes.txt | 127 ++ doc/LisezMoi.txt | 233 +++ doc/TODO.txt | 7 + src/NESCarts.c | 112 ++ src/apu/SndAlleg.c | 418 ++++++ src/apu/SndUnixT.c | 554 +++++++ src/apu/Sound.c | 497 ++++++ src/corecpu/Codes.h | 837 +++++++++++ src/corecpu/Debug.c | 480 ++++++ src/corecpu/M6502.c | 317 ++++ src/corecpu/M6502.h | 148 ++ src/corecpu/Tables.h | 71 + src/include/MIDIFreq.h | 1038 +++++++++++++ src/include/NESCarts.h | 39 + src/include/Sound.h | 232 +++ src/include/mappers/manager.h | 60 + src/include/memory/manager.h | 68 + src/include/paddle.h | 36 + src/include/palette.h | 261 ++++ src/include/plugins/manager.h | 46 + src/include/ppu/ppu.debug.h | 24 + src/include/ppu/ppu.h | 68 + src/include/ppu/ppu.memory.h | 32 + src/include/types.h | 28 + src/main.c | 1232 +++++++++++++++ src/mappersmanager/manager.c | 62 + src/mappersmanager/mappers/mmc1.c | 361 +++++ src/mappersmanager/mappers/mmc1.h | 16 + src/mappersmanager/mappers/norom.c | 48 + src/mappersmanager/mappers/norom.h | 16 + src/mappersmanager/mappers/temp/aorom.c | 63 + src/mappersmanager/mappers/temp/cnrom.c | 47 + .../mappers/temp/genericmapper.c | 35 + src/mappersmanager/mappers/temp/iremh3001.c | 125 ++ src/mappersmanager/mappers/temp/mmc3.c | 317 ++++ src/mappersmanager/mappers/temp/mmc4.c | 137 ++ src/mappersmanager/mappers/temp/unrom.c | 51 + src/mappersmanager/mappers_list.h | 20 + src/mappersmanager/unused/aorom.h | 63 + src/mappersmanager/unused/cnrom.h | 47 + src/mappersmanager/unused/genericmapper.h | 35 + src/mappersmanager/unused/iremh3001.h | 125 ++ src/mappersmanager/unused/mmc1.h | 355 +++++ src/mappersmanager/unused/mmc3.h | 317 ++++ src/mappersmanager/unused/mmc4.h | 137 ++ src/mappersmanager/unused/unrom.h | 51 + src/mappersmanager/utils.c | 118 ++ src/memorymanager/memory.c | 275 ++++ src/os/unix/loadfile.c | 31 + src/paddle.c | 121 ++ src/pluginsmanager/manager.c | 136 ++ src/pluginsmanager/plugins/gamegenie.c | 802 ++++++++++ src/pluginsmanager/plugins/gamegenie.h | 2 + src/pluginsmanager/plugins_list.h | 10 + src/ppu/debug/ppu.debug.c | 348 +++++ src/ppu/debug/tmp.c.txt | 45 + src/ppu/oldppu.c | 1061 +++++++++++++ src/ppu/ppu.24.c | 1311 ++++++++++++++++ src/ppu/ppu.c | 997 +++++++++++++ src/ppu/ppu.memory.c | 181 +++ src/ppu/ppu.new.c | 1258 ++++++++++++++++ src/ppu/ppu.new2.c | 1328 +++++++++++++++++ src/ppu/ppu.old.c | 1279 ++++++++++++++++ src/utils/bin2h/bin2h.c | 75 + src/utils/sendromdata.py | 94 ++ unix/Makefile | 56 + 67 files changed, 19026 insertions(+) create mode 100755 doc/Changements.txt create mode 100755 doc/Changes.txt create mode 100755 doc/LisezMoi.txt create mode 100644 doc/TODO.txt create mode 100755 src/NESCarts.c create mode 100755 src/apu/SndAlleg.c create mode 100755 src/apu/SndUnixT.c create mode 100644 src/apu/Sound.c create mode 100755 src/corecpu/Codes.h create mode 100755 src/corecpu/Debug.c create mode 100755 src/corecpu/M6502.c create mode 100755 src/corecpu/M6502.h create mode 100755 src/corecpu/Tables.h create mode 100644 src/include/MIDIFreq.h create mode 100755 src/include/NESCarts.h create mode 100644 src/include/Sound.h create mode 100755 src/include/mappers/manager.h create mode 100755 src/include/memory/manager.h create mode 100755 src/include/paddle.h create mode 100644 src/include/palette.h create mode 100644 src/include/plugins/manager.h create mode 100644 src/include/ppu/ppu.debug.h create mode 100755 src/include/ppu/ppu.h create mode 100644 src/include/ppu/ppu.memory.h create mode 100755 src/include/types.h create mode 100755 src/main.c create mode 100644 src/mappersmanager/manager.c create mode 100755 src/mappersmanager/mappers/mmc1.c create mode 100644 src/mappersmanager/mappers/mmc1.h create mode 100644 src/mappersmanager/mappers/norom.c create mode 100644 src/mappersmanager/mappers/norom.h create mode 100755 src/mappersmanager/mappers/temp/aorom.c create mode 100755 src/mappersmanager/mappers/temp/cnrom.c create mode 100755 src/mappersmanager/mappers/temp/genericmapper.c create mode 100755 src/mappersmanager/mappers/temp/iremh3001.c create mode 100755 src/mappersmanager/mappers/temp/mmc3.c create mode 100644 src/mappersmanager/mappers/temp/mmc4.c create mode 100755 src/mappersmanager/mappers/temp/unrom.c create mode 100644 src/mappersmanager/mappers_list.h create mode 100755 src/mappersmanager/unused/aorom.h create mode 100755 src/mappersmanager/unused/cnrom.h create mode 100755 src/mappersmanager/unused/genericmapper.h create mode 100755 src/mappersmanager/unused/iremh3001.h create mode 100755 src/mappersmanager/unused/mmc1.h create mode 100755 src/mappersmanager/unused/mmc3.h create mode 100644 src/mappersmanager/unused/mmc4.h create mode 100755 src/mappersmanager/unused/unrom.h create mode 100755 src/mappersmanager/utils.c create mode 100755 src/memorymanager/memory.c create mode 100644 src/os/unix/loadfile.c create mode 100755 src/paddle.c create mode 100644 src/pluginsmanager/manager.c create mode 100644 src/pluginsmanager/plugins/gamegenie.c create mode 100644 src/pluginsmanager/plugins/gamegenie.h create mode 100644 src/pluginsmanager/plugins_list.h create mode 100644 src/ppu/debug/ppu.debug.c create mode 100644 src/ppu/debug/tmp.c.txt create mode 100755 src/ppu/oldppu.c create mode 100755 src/ppu/ppu.24.c create mode 100755 src/ppu/ppu.c create mode 100644 src/ppu/ppu.memory.c create mode 100755 src/ppu/ppu.new.c create mode 100755 src/ppu/ppu.new2.c create mode 100755 src/ppu/ppu.old.c create mode 100755 src/utils/bin2h/bin2h.c create mode 100644 src/utils/sendromdata.py create mode 100755 unix/Makefile diff --git a/doc/Changements.txt b/doc/Changements.txt new file mode 100755 index 0000000..f5fdb6e --- /dev/null +++ b/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 $ +-------------------------------------------------------------------------------- diff --git a/doc/Changes.txt b/doc/Changes.txt new file mode 100755 index 0000000..3393ee6 --- /dev/null +++ b/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 $ +-------------------------------------------------------------------------------- diff --git a/doc/LisezMoi.txt b/doc/LisezMoi.txt new file mode 100755 index 0000000..1e5b7af --- /dev/null +++ b/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 + +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 $ +-------------------------------------------------------------------------------- diff --git a/doc/TODO.txt b/doc/TODO.txt new file mode 100644 index 0000000..cc8fada --- /dev/null +++ b/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?) + [ ] + \ No newline at end of file diff --git a/src/NESCarts.c b/src/NESCarts.c new file mode 100755 index 0000000..6151647 --- /dev/null +++ b/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 +#include + +/* 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; +} diff --git a/src/apu/SndAlleg.c b/src/apu/SndAlleg.c new file mode 100755 index 0000000..5753d96 --- /dev/null +++ b/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 + +#include +#include +//#include +//#include +#include +//#include + +#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<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>=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;I0? (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>15)+1; + N = ((K-(L2&0x7FFF))>>15)+1; + } + /* Add waveform to the buffer */ + for(I=0;I>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=SoundRate/3) break; + K=0x10000*CH[J].Freq/SoundRate; + L1=CH[J].Count; + V<<=7; + for(I=0;I=SoundRate/3) break; + K=0x10000*CH[J].Freq/SoundRate; + L1=CH[J].Count; + V<<=7; + for(I=0;I=SoundRate/3) break; + K=0x10000*CH[J].Freq/SoundRate; + L1=CH[J].Count; + V<<=7; + for(I=0;I=SoundRate/3) break; + K=0x10000*CH[J].Freq/SoundRate; + L1=CH[J].Count; + V<<=7; + for(I=0;I>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 */ +} diff --git a/src/apu/SndUnixT.c b/src/apu/SndUnixT.c new file mode 100755 index 0000000..b5faeea --- /dev/null +++ b/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 +#include +#include +#include +#include +#include + +#ifdef SUN_AUDIO + +#include +#include +#include + +#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 +#endif + +#ifdef __NetBSD__ +#include +#endif + +#ifdef __linux__ +#include +#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<>16)>=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;I0? (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>15)+1; + N = ((K-(L2&0x7FFF))>>15)+1; + } + /* Add waveform to the buffer */ + for(I=0;I>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=SoundRate/3) break; + K=0x10000*CH[J].Freq/SoundRate; + L1=CH[J].Count; + V<<=7; + for(I=0;I=SoundRate/3) break; + K=0x10000*CH[J].Freq/SoundRate; + L1=CH[J].Count; + V<<=7; + for(I=0;I=SoundRate/3) break; + K=0x10000*CH[J].Freq/SoundRate; + L1=CH[J].Count; + V<<=7; + for(I=0;I=SoundRate/3) break; + K=0x10000*CH[J].Freq/SoundRate; + L1=CH[J].Count; + V<<=7; + for(I=0;I>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_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 */ diff --git a/src/apu/Sound.c b/src/apu/Sound.c new file mode 100644 index 0000000..b38e0cd --- /dev/null +++ b/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 +#include + +#ifdef UNIX +#include +#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>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>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=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((FreqMIDI_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); +} diff --git a/src/corecpu/Codes.h b/src/corecpu/Codes.h new file mode 100755 index 0000000..861d6c6 --- /dev/null +++ b/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 */ + diff --git a/src/corecpu/Debug.c b/src/corecpu/Debug.c new file mode 100755 index 0000000..ecede48 --- /dev/null +++ b/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 +#include +#include +#include + +#include + +#include + +#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; ", 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(" : Break at the next instruction"); + puts("= : Break at addr"); + puts("+ : Break at PC + offset"); + puts("t : Set PC to addr"); + puts("c : Continue without break"); + puts("j : Continue from addr"); + puts("m : Memory dump at addr"); + puts("d : 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 : Dump PPU memory at addr"); + puts("a : Dump all memory to memory.log"); + puts("s : Dump sprite table to sprite.log"); + puts("n : Dump name table to nt.log"); + puts("z : Show lastest opcode executed"); + puts("i : SpriteTable Dump"); + puts("g : Get sprite 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 */ diff --git a/src/corecpu/M6502.c b/src/corecpu/M6502.c new file mode 100755 index 0000000..989606b --- /dev/null +++ b/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 + +/** 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); +} diff --git a/src/corecpu/M6502.h b/src/corecpu/M6502.h new file mode 100755 index 0000000..8f2d9fb --- /dev/null +++ b/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 */ diff --git a/src/corecpu/Tables.h b/src/corecpu/Tables.h new file mode 100755 index 0000000..e977670 --- /dev/null +++ b/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, +}; + diff --git a/src/include/MIDIFreq.h b/src/include/MIDIFreq.h new file mode 100644 index 0000000..399eb6b --- /dev/null +++ b/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 } diff --git a/src/include/NESCarts.h b/src/include/NESCarts.h new file mode 100755 index 0000000..4f8e345 --- /dev/null +++ b/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 diff --git a/src/include/Sound.h b/src/include/Sound.h new file mode 100644 index 0000000..5103012 --- /dev/null +++ b/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 + +/** 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 */ diff --git a/src/include/mappers/manager.h b/src/include/mappers/manager.h new file mode 100755 index 0000000..bf5475a --- /dev/null +++ b/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 +#include +#include + +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 diff --git a/src/include/memory/manager.h b/src/include/memory/manager.h new file mode 100755 index 0000000..ad02902 --- /dev/null +++ b/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 diff --git a/src/include/paddle.h b/src/include/paddle.h new file mode 100755 index 0000000..f736ee9 --- /dev/null +++ b/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 diff --git a/src/include/palette.h b/src/include/palette.h new file mode 100644 index 0000000..4365f09 --- /dev/null +++ b/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 } + }; diff --git a/src/include/plugins/manager.h b/src/include/plugins/manager.h new file mode 100644 index 0000000..2241eec --- /dev/null +++ b/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 + +/* 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 */ diff --git a/src/include/ppu/ppu.debug.h b/src/include/ppu/ppu.debug.h new file mode 100644 index 0000000..3f27604 --- /dev/null +++ b/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 diff --git a/src/include/ppu/ppu.h b/src/include/ppu/ppu.h new file mode 100755 index 0000000..c345d92 --- /dev/null +++ b/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 + +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 diff --git a/src/include/ppu/ppu.memory.h b/src/include/ppu/ppu.memory.h new file mode 100644 index 0000000..0999585 --- /dev/null +++ b/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 diff --git a/src/include/types.h b/src/include/types.h new file mode 100755 index 0000000..a66e44e --- /dev/null +++ b/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 diff --git a/src/main.c b/src/main.c new file mode 100755 index 0000000..49ae111 --- /dev/null +++ b/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 +#include +#include +#include + +#include +#include +#include + +#ifndef WIN32 + +//#include +#include + +#else + +#define USE_CONSOLE +#include + +#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 +#include +#include +#include + +#include + +#include + +#include + +#ifdef USE_SOUND +#include +#endif + +#include + +#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;IFlags & 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; +} diff --git a/src/mappersmanager/manager.c b/src/mappersmanager/manager.c new file mode 100644 index 0000000..92996a3 --- /dev/null +++ b/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 +#include + +#include + +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; +} \ No newline at end of file diff --git a/src/mappersmanager/mappers/mmc1.c b/src/mappersmanager/mappers/mmc1.c new file mode 100755 index 0000000..cd963ed --- /dev/null +++ b/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 +#include + +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); diff --git a/src/mappersmanager/mappers/mmc1.h b/src/mappersmanager/mappers/mmc1.h new file mode 100644 index 0000000..1cd69f8 --- /dev/null +++ b/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 + +int mmc1_InitMapper (NesCart *cart); +int mmc1_MapperIRQ (int cycledone); +void mmc1_MapperDump (); +void mmc1_MapperWriteHook(register byte Addr, register byte Value); \ No newline at end of file diff --git a/src/mappersmanager/mappers/norom.c b/src/mappersmanager/mappers/norom.c new file mode 100644 index 0000000..93b2ef0 --- /dev/null +++ b/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"); +} \ No newline at end of file diff --git a/src/mappersmanager/mappers/norom.h b/src/mappersmanager/mappers/norom.h new file mode 100644 index 0000000..6c095c4 --- /dev/null +++ b/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 + +int norom_InitMapper (NesCart *cart); +int norom_MapperIRQ (int cycledone); +void norom_MapperDump (); +void norom_MapperWriteHook(register byte Addr, register byte Value); \ No newline at end of file diff --git a/src/mappersmanager/mappers/temp/aorom.c b/src/mappersmanager/mappers/temp/aorom.c new file mode 100755 index 0000000..3d80dff --- /dev/null +++ b/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); +} diff --git a/src/mappersmanager/mappers/temp/cnrom.c b/src/mappersmanager/mappers/temp/cnrom.c new file mode 100755 index 0000000..72a40b7 --- /dev/null +++ b/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); +} diff --git a/src/mappersmanager/mappers/temp/genericmapper.c b/src/mappersmanager/mappers/temp/genericmapper.c new file mode 100755 index 0000000..b746580 --- /dev/null +++ b/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; +} diff --git a/src/mappersmanager/mappers/temp/iremh3001.c b/src/mappersmanager/mappers/temp/iremh3001.c new file mode 100755 index 0000000..517089b --- /dev/null +++ b/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; +} diff --git a/src/mappersmanager/mappers/temp/mmc3.c b/src/mappersmanager/mappers/temp/mmc3.c new file mode 100755 index 0000000..ee07302 --- /dev/null +++ b/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; +} diff --git a/src/mappersmanager/mappers/temp/mmc4.c b/src/mappersmanager/mappers/temp/mmc4.c new file mode 100644 index 0000000..54a885c --- /dev/null +++ b/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; + +} + diff --git a/src/mappersmanager/mappers/temp/unrom.c b/src/mappersmanager/mappers/temp/unrom.c new file mode 100755 index 0000000..be1cae0 --- /dev/null +++ b/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); +} diff --git a/src/mappersmanager/mappers_list.h b/src/mappersmanager/mappers_list.h new file mode 100644 index 0000000..e859049 --- /dev/null +++ b/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 } +}; diff --git a/src/mappersmanager/unused/aorom.h b/src/mappersmanager/unused/aorom.h new file mode 100755 index 0000000..6153c5a --- /dev/null +++ b/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); +} diff --git a/src/mappersmanager/unused/cnrom.h b/src/mappersmanager/unused/cnrom.h new file mode 100755 index 0000000..72a40b7 --- /dev/null +++ b/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); +} diff --git a/src/mappersmanager/unused/genericmapper.h b/src/mappersmanager/unused/genericmapper.h new file mode 100755 index 0000000..b746580 --- /dev/null +++ b/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; +} diff --git a/src/mappersmanager/unused/iremh3001.h b/src/mappersmanager/unused/iremh3001.h new file mode 100755 index 0000000..517089b --- /dev/null +++ b/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; +} diff --git a/src/mappersmanager/unused/mmc1.h b/src/mappersmanager/unused/mmc1.h new file mode 100755 index 0000000..250f3ff --- /dev/null +++ b/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); diff --git a/src/mappersmanager/unused/mmc3.h b/src/mappersmanager/unused/mmc3.h new file mode 100755 index 0000000..ee07302 --- /dev/null +++ b/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; +} diff --git a/src/mappersmanager/unused/mmc4.h b/src/mappersmanager/unused/mmc4.h new file mode 100644 index 0000000..54a885c --- /dev/null +++ b/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; + +} + diff --git a/src/mappersmanager/unused/unrom.h b/src/mappersmanager/unused/unrom.h new file mode 100755 index 0000000..be1cae0 --- /dev/null +++ b/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); +} diff --git a/src/mappersmanager/utils.c b/src/mappersmanager/utils.c new file mode 100755 index 0000000..f7b2104 --- /dev/null +++ b/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 +#include + +#include +#include +#include +#include + +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); + } +} diff --git a/src/memorymanager/memory.c b/src/memorymanager/memory.c new file mode 100755 index 0000000..cc93381 --- /dev/null +++ b/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 +#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; + } +} diff --git a/src/os/unix/loadfile.c b/src/os/unix/loadfile.c new file mode 100644 index 0000000..8dce40f --- /dev/null +++ b/src/os/unix/loadfile.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + + +/* 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; +} diff --git a/src/paddle.c b/src/paddle.c new file mode 100755 index 0000000..a4aa001 --- /dev/null +++ b/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 +#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; + +} diff --git a/src/pluginsmanager/manager.c b/src/pluginsmanager/manager.c new file mode 100644 index 0000000..d3e44e6 --- /dev/null +++ b/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 +#include + +#include + +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; +} diff --git a/src/pluginsmanager/plugins/gamegenie.c b/src/pluginsmanager/plugins/gamegenie.c new file mode 100644 index 0000000..a299c66 --- /dev/null +++ b/src/pluginsmanager/plugins/gamegenie.c @@ -0,0 +1,802 @@ +#include +#include +#include + +#define __TINES_PLUGINS__ +#include + +#include +#include +#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>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; +} diff --git a/src/pluginsmanager/plugins/gamegenie.h b/src/pluginsmanager/plugins/gamegenie.h new file mode 100644 index 0000000..515b25f --- /dev/null +++ b/src/pluginsmanager/plugins/gamegenie.h @@ -0,0 +1,2 @@ +int gg_Init(); +int gg_Deinit(); diff --git a/src/pluginsmanager/plugins_list.h b/src/pluginsmanager/plugins_list.h new file mode 100644 index 0000000..912552e --- /dev/null +++ b/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 } + }; diff --git a/src/ppu/debug/ppu.debug.c b/src/ppu/debug/ppu.debug.c new file mode 100644 index 0000000..c9568d8 --- /dev/null +++ b/src/ppu/debug/ppu.debug.c @@ -0,0 +1,348 @@ +#include +#include + +#include + +#define __TINES_PPU_INTERNAL__ + +#include +#include +#include + +#include + +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)); + } +} diff --git a/src/ppu/debug/tmp.c.txt b/src/ppu/debug/tmp.c.txt new file mode 100644 index 0000000..a818523 --- /dev/null +++ b/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 diff --git a/src/ppu/oldppu.c b/src/ppu/oldppu.c new file mode 100755 index 0000000..482b3d6 --- /dev/null +++ b/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 +#include +#include + +#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; + } +} diff --git a/src/ppu/ppu.24.c b/src/ppu/ppu.24.c new file mode 100755 index 0000000..beeddcb --- /dev/null +++ b/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 +#include +#include +#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); + } +} diff --git a/src/ppu/ppu.c b/src/ppu/ppu.c new file mode 100755 index 0000000..31b9399 --- /dev/null +++ b/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 +#include +#include + +#define __TINES_PPU_INTERNAL__ +#include +#include +#include + +#include + +#define __TINES_PLUGINS__ +#include + +#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(); +} diff --git a/src/ppu/ppu.memory.c b/src/ppu/ppu.memory.c new file mode 100644 index 0000000..463c822 --- /dev/null +++ b/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 +#include + +#define __TINES_PPU_INTERNAL__ + +#include +#include + +#include + +/* 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 diff --git a/src/ppu/ppu.new.c b/src/ppu/ppu.new.c new file mode 100755 index 0000000..8416e35 --- /dev/null +++ b/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 +#include +#include +#include +#include + + +#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); + } +} diff --git a/src/ppu/ppu.new2.c b/src/ppu/ppu.new2.c new file mode 100755 index 0000000..dcfc6f2 --- /dev/null +++ b/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 +#include +#include +#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); + } +} diff --git a/src/ppu/ppu.old.c b/src/ppu/ppu.old.c new file mode 100755 index 0000000..2dc9bbf --- /dev/null +++ b/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 +#include +#include +#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); + } +} diff --git a/src/utils/bin2h/bin2h.c b/src/utils/bin2h/bin2h.c new file mode 100755 index 0000000..2084ba7 --- /dev/null +++ b/src/utils/bin2h/bin2h.c @@ -0,0 +1,75 @@ +#include +#include + + +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; +} diff --git a/src/utils/sendromdata.py b/src/utils/sendromdata.py new file mode 100644 index 0000000..d5f8909 --- /dev/null +++ b/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 '' + else: + raise + return f.read() + +if __name__ == '__main__': + #print "" + 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 " " + print " %s" % filename + print " %s" % sha.new(fs).hexdigest() + print " %s" % md5.new(fs).hexdigest() + print " %d" % mapperID + print " %d" % prgsize + print " %d" % chrsize + print " %d" % mirror + print " %d" % sram + print " %d" % Trainer + print " %d" % DiskDude + print " " + + + #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 "" diff --git a/unix/Makefile b/unix/Makefile new file mode 100755 index 0000000..2609268 --- /dev/null +++ b/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 \ No newline at end of file