TI-NESulator SVN reimport

This commit is contained in:
godzil 2008-02-20 15:40:31 +00:00
commit 3f4b4990ea
67 changed files with 19026 additions and 0 deletions

105
doc/Changements.txt Executable file
View File

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

127
doc/Changes.txt Executable file
View File

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

233
doc/LisezMoi.txt Executable file
View File

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

7
doc/TODO.txt Normal file
View File

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

112
src/NESCarts.c Executable file
View File

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

418
src/apu/SndAlleg.c Executable file
View File

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

554
src/apu/SndUnixT.c Executable file
View File

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

497
src/apu/Sound.c Normal file
View File

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

837
src/corecpu/Codes.h Executable file
View File

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

480
src/corecpu/Debug.c Executable file
View File

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

317
src/corecpu/M6502.c Executable file
View File

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

148
src/corecpu/M6502.h Executable file
View File

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

71
src/corecpu/Tables.h Executable file
View File

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

1038
src/include/MIDIFreq.h Normal file

File diff suppressed because it is too large Load Diff

39
src/include/NESCarts.h Executable file
View File

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

232
src/include/Sound.h Normal file
View File

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

60
src/include/mappers/manager.h Executable file
View File

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

68
src/include/memory/manager.h Executable file
View File

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

36
src/include/paddle.h Executable file
View File

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

261
src/include/palette.h Normal file
View File

@ -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 }
};

View File

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

View File

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

68
src/include/ppu/ppu.h Executable file
View File

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

View File

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

28
src/include/types.h Executable file
View File

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

1232
src/main.c Executable file

File diff suppressed because it is too large Load Diff

View File

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

361
src/mappersmanager/mappers/mmc1.c Executable file
View File

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

View File

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

View File

@ -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");
}

View File

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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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 }
};

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

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

355
src/mappersmanager/unused/mmc1.h Executable file
View File

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

317
src/mappersmanager/unused/mmc3.h Executable file
View File

@ -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;
}

View File

@ -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;
}

View File

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

118
src/mappersmanager/utils.c Executable file
View File

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

275
src/memorymanager/memory.c Executable file
View File

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

31
src/os/unix/loadfile.c Normal file
View File

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

121
src/paddle.c Executable file
View File

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

View File

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

View File

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

View File

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

View File

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

348
src/ppu/debug/ppu.debug.c Normal file
View File

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

45
src/ppu/debug/tmp.c.txt Normal file
View File

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

1061
src/ppu/oldppu.c Executable file

File diff suppressed because it is too large Load Diff

1311
src/ppu/ppu.24.c Executable file

File diff suppressed because it is too large Load Diff

997
src/ppu/ppu.c Executable file
View File

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

181
src/ppu/ppu.memory.c Normal file
View File

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

1258
src/ppu/ppu.new.c Executable file

File diff suppressed because it is too large Load Diff

1328
src/ppu/ppu.new2.c Executable file

File diff suppressed because it is too large Load Diff

1279
src/ppu/ppu.old.c Executable file

File diff suppressed because it is too large Load Diff

75
src/utils/bin2h/bin2h.c Executable file
View File

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

94
src/utils/sendromdata.py Normal file
View File

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

56
unix/Makefile Executable file
View File

@ -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