init commit

master
xerox 2 years ago
commit daa4f9a04f

@ -0,0 +1,198 @@
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
(c) Copyright 1996 - 2002 Gary Henderson (gary.henderson@ntlworld.com),
Jerremy Koot (jkoot@snes9x.com)
(c) Copyright 2002 - 2004 Matthew Kendora
(c) Copyright 2002 - 2005 Peter Bortas (peter@bortas.org)
(c) Copyright 2004 - 2005 Joel Yliluoma (http://iki.fi/bisqwit/)
(c) Copyright 2001 - 2006 John Weidman (jweidman@slip.net)
(c) Copyright 2002 - 2006 funkyass (funkyass@spam.shaw.ca),
Kris Bleakley (codeviolation@hotmail.com)
(c) Copyright 2002 - 2010 Brad Jorsch (anomie@users.sourceforge.net),
Nach (n-a-c-h@users.sourceforge.net),
(c) Copyright 2002 - 2011 zones (kasumitokoduck@yahoo.com)
(c) Copyright 2006 - 2007 nitsuja
(c) Copyright 2009 - 2019 BearOso,
OV2
(c) Copyright 2017 qwertymodo
(c) Copyright 2011 - 2017 Hans-Kristian Arntzen,
Daniel De Matteis
(Under no circumstances will commercial rights be given)
BS-X C emulator code
(c) Copyright 2005 - 2006 Dreamer Nom,
zones
C4 x86 assembler and some C emulation code
(c) Copyright 2000 - 2003 _Demo_ (_demo_@zsnes.com),
Nach,
zsKnight (zsknight@zsnes.com)
C4 C++ code
(c) Copyright 2003 - 2006 Brad Jorsch,
Nach
DSP-1 emulator code
(c) Copyright 1998 - 2006 _Demo_,
Andreas Naive (andreasnaive@gmail.com),
Gary Henderson,
Ivar (ivar@snes9x.com),
John Weidman,
Kris Bleakley,
Matthew Kendora,
Nach,
neviksti (neviksti@hotmail.com)
DSP-2 emulator code
(c) Copyright 2003 John Weidman,
Kris Bleakley,
Lord Nightmare (lord_nightmare@users.sourceforge.net),
Matthew Kendora,
neviksti
DSP-3 emulator code
(c) Copyright 2003 - 2006 John Weidman,
Kris Bleakley,
Lancer,
z80 gaiden
DSP-4 emulator code
(c) Copyright 2004 - 2006 Dreamer Nom,
John Weidman,
Kris Bleakley,
Nach,
z80 gaiden
OBC1 emulator code
(c) Copyright 2001 - 2004 zsKnight,
pagefault (pagefault@zsnes.com),
Kris Bleakley
Ported from x86 assembler to C by sanmaiwashi
SPC7110 and RTC C++ emulator code used in 1.39-1.51
(c) Copyright 2002 Matthew Kendora with research by
zsKnight,
John Weidman,
Dark Force
SPC7110 and RTC C++ emulator code used in 1.52+
(c) Copyright 2009 byuu,
neviksti
S-DD1 C emulator code
(c) Copyright 2003 Brad Jorsch with research by
Andreas Naive,
John Weidman
S-RTC C emulator code
(c) Copyright 2001 - 2006 byuu,
John Weidman
ST010 C++ emulator code
(c) Copyright 2003 Feather,
John Weidman,
Kris Bleakley,
Matthew Kendora
Super FX x86 assembler emulator code
(c) Copyright 1998 - 2003 _Demo_,
pagefault,
zsKnight
Super FX C emulator code
(c) Copyright 1997 - 1999 Ivar,
Gary Henderson,
John Weidman
Sound emulator code used in 1.5-1.51
(c) Copyright 1998 - 2003 Brad Martin
(c) Copyright 1998 - 2006 Charles Bilyue'
Sound emulator code used in 1.52+
(c) Copyright 2004 - 2007 Shay Green (gblargg@gmail.com)
S-SMP emulator code used in 1.54+
(c) Copyright 2016 byuu
SH assembler code partly based on x86 assembler code
(c) Copyright 2002 - 2004 Marcus Comstedt (marcus@mc.pp.se)
2xSaI filter
(c) Copyright 1999 - 2001 Derek Liauw Kie Fa
HQ2x, HQ3x, HQ4x filters
(c) Copyright 2003 Maxim Stepin (maxim@hiend3d.com)
NTSC filter
(c) Copyright 2006 - 2007 Shay Green
GTK+ GUI code
(c) Copyright 2004 - 2019 BearOso
Win32 GUI code
(c) Copyright 2003 - 2006 blip,
funkyass,
Matthew Kendora,
Nach,
nitsuja
(c) Copyright 2009 - 2019 OV2
Mac OS GUI code
(c) Copyright 1998 - 2001 John Stiles
(c) Copyright 2001 - 2011 zones
Libretro port
(c) Copyright 2011 - 2017 Hans-Kristian Arntzen,
Daniel De Matteis
(Under no circumstances will commercial rights be given)
Specific ports contains the works of other authors. See headers in
individual files.
Snes9x homepage: http://www.snes9x.com/
Snes9x source code: https://github.com/snes9xgit/snes9x/
Permission to use, copy, modify and/or distribute Snes9x in both binary
and source form, for non-commercial purposes, is hereby granted without
fee, providing that this license information and copyright notice appear
with all copies and any derived work.
This software is provided 'as-is', without any express or implied
warranty. In no event shall the authors be held liable for any damages
arising from the use of this software or it's derivatives.
Snes9x is freeware for PERSONAL USE only. Commercial users should
seek permission of the copyright holders first. Commercial use includes,
but is not limited to, charging money for Snes9x or software derived from
Snes9x, including Snes9x or derivatives in commercial game bundles, and/or
using Snes9x as a promotion for your commercial product.
The copyright holders request that bug fixes and improvements to the code
should be forwarded to them so everyone can benefit from the modifications
in future versions.
Super NES and Super Nintendo Entertainment System are trademarks of
Nintendo Co., Limited and its subsidiary companies.
-------------------------------------------------------------------------------
Some parts included in Snes9x are usually available under a different license,
their authors have granted exception for their use in Snes9x (and/or they are
licensed as LGPL):
- JMA: GPL/LGPL, see jma/license.txt
- snes_ntsc: LGPL, see filter/snes_ntsc-license.txt
- xBRZ: GPLv3, see filter/xbrz-license.txt

@ -0,0 +1,2 @@
# FBSnes9x - Unix Frame Buffer Port

505
snes9x/.gitignore vendored

@ -0,0 +1,505 @@
# Files that can arise when using snes9x
win32/snes9x.conf
win32/Valid.Ext
win32/stdout.txt
win32/stderr.txt
win32/Roms
win32/Saves
win32/Cheats
win32/Screenshots
win32/Movies
win32/SPCs
win32/BIOS
*.smc
*.sfc
*.fig
*.srm
*.00[0123456789]
*.oops
*.ips
*.ups
*.bps
*.avi
*.shader
*.cg
*.cgp
*.smv
*.cht
*.rtc
# Included libraries in OS X that should not be ignored.
!macosx/libz_u.a
!macosx/libHIDUtilities_u.a
# Included libraries in windows that should not be ignored.
!win32/ddraw/ddraw_x86.lib
!win32/ddraw/ddraw_x64.lib
# Created by https://www.gitignore.io/api/c,c++,xcode,visualstudio
# Edit at https://www.gitignore.io/?templates=c,c++,xcode,visualstudio
### C ###
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
### C++ ###
# Prerequisites
# Compiled Object files
*.slo
# Precompiled Headers
# Compiled Dynamic libraries
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
# Executables
### Xcode ###
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## User settings
xcuserdata/
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
*.xcscmblueprint
*.xccheckout
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
build/
DerivedData/
*.moved-aside
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
## Xcode Patch
*.xcodeproj/*
!*.xcodeproj/project.pbxproj
!*.xcodeproj/xcshareddata/
!*.xcworkspace/contents.xcworkspacedata
/*.gcno
### Xcode Patch ###
**/xcshareddata/WorkspaceSettings.xcsettings
### VisualStudio ###
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.meta
*.iobj
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# End of https://www.gitignore.io/api/c,c++,xcode,visualstudio
# vim
*.swp
# OS X temporary files
.DS_Store
*.lock
profile

@ -0,0 +1,105 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _65C816_H_
#define _65C816_H_
#define Carry 1
#define Zero 2
#define IRQ 4
#define Decimal 8
#define IndexFlag 16
#define MemoryFlag 32
#define Overflow 64
#define Negative 128
#define Emulation 256
#define SetCarry() (ICPU._Carry = 1)
#define ClearCarry() (ICPU._Carry = 0)
#define SetZero() (ICPU._Zero = 0)
#define ClearZero() (ICPU._Zero = 1)
#define SetIRQ() (Registers.PL |= IRQ)
#define ClearIRQ() (Registers.PL &= ~IRQ)
#define SetDecimal() (Registers.PL |= Decimal)
#define ClearDecimal() (Registers.PL &= ~Decimal)
#define SetIndex() (Registers.PL |= IndexFlag)
#define ClearIndex() (Registers.PL &= ~IndexFlag)
#define SetMemory() (Registers.PL |= MemoryFlag)
#define ClearMemory() (Registers.PL &= ~MemoryFlag)
#define SetOverflow() (ICPU._Overflow = 1)
#define ClearOverflow() (ICPU._Overflow = 0)
#define SetNegative() (ICPU._Negative = 0x80)
#define ClearNegative() (ICPU._Negative = 0)
#define CheckCarry() (ICPU._Carry)
#define CheckZero() (ICPU._Zero == 0)
#define CheckIRQ() (Registers.PL & IRQ)
#define CheckDecimal() (Registers.PL & Decimal)
#define CheckIndex() (Registers.PL & IndexFlag)
#define CheckMemory() (Registers.PL & MemoryFlag)
#define CheckOverflow() (ICPU._Overflow)
#define CheckNegative() (ICPU._Negative & 0x80)
#define CheckEmulation() (Registers.P.W & Emulation)
#define SetFlags(f) (Registers.P.W |= (f))
#define ClearFlags(f) (Registers.P.W &= ~(f))
#define CheckFlag(f) (Registers.PL & (f))
typedef union
{
#ifdef LSB_FIRST
struct { uint8 l, h; } B;
#else
struct { uint8 h, l; } B;
#endif
uint16 W;
} pair;
typedef union
{
#ifdef LSB_FIRST
struct { uint8 xPCl, xPCh, xPB, z; } B;
struct { uint16 xPC, d; } W;
#else
struct { uint8 z, xPB, xPCh, xPCl; } B;
struct { uint16 d, xPC; } W;
#endif
uint32 xPBPC;
} PC_t;
struct SRegisters
{
uint8 DB;
pair P;
pair A;
pair D;
pair S;
pair X;
pair Y;
PC_t PC;
};
#define AL A.B.l
#define AH A.B.h
#define XL X.B.l
#define XH X.B.h
#define YL Y.B.l
#define YH Y.B.h
#define SL S.B.l
#define SH S.B.h
#define DL D.B.l
#define DH D.B.h
#define PL P.B.l
#define PH P.B.h
#define PBPC PC.xPBPC
#define PCw PC.W.xPC
#define PCh PC.B.xPCh
#define PCl PC.B.xPCl
#define PB PC.B.xPB
extern struct SRegisters Registers;
#endif

@ -0,0 +1,61 @@
# Snes9x
*Snes9x - Portable Super Nintendo Entertainment System (TM) emulator*
This is the official source code repository for the Snes9x project.
Please check the [Wiki](https://github.com/snes9xgit/snes9x/wiki) for additional information.
## Nightly builds
Download nightly builds from continuous integration:
### snes9x
| OS | status |
|---------------|--------------------------------------------------|
| Windows | [![Status][s9x-win-all]][appveyor] |
| Linux (GTK) | [![Status][snes9x_linux-gtk-amd64]][cirrus-ci] |
| Linux (X11) | [![Status][snes9x_linux-x11-amd64]][cirrus-ci] |
| FreeBSD (X11) | [![Status][snes9x_freebsd-x11-amd64]][cirrus-ci] |
| macOS | [![Status][snes9x_macOS-amd64]][cirrus-ci] |
[appveyor]: https://ci.appveyor.com/project/snes9x/snes9x
[cirrus-ci]: http://cirrus-ci.com/github/snes9xgit/snes9x
[s9x-win-all]: https://ci.appveyor.com/api/projects/status/github/snes9xgit/snes9x?branch=master&svg=true
[snes9x_linux-gtk-amd64]: https://api.cirrus-ci.com/github/snes9xgit/snes9x.svg?task=snes9x_linux-gtk-amd64
[snes9x_linux-x11-amd64]: https://api.cirrus-ci.com/github/snes9xgit/snes9x.svg?task=snes9x_linux-x11-amd64
[snes9x_freebsd-x11-amd64]: https://api.cirrus-ci.com/github/snes9xgit/snes9x.svg?task=snes9x_freebsd-x11-amd64
[snes9x_macOS-amd64]: https://api.cirrus-ci.com/github/snes9xgit/snes9x.svg?task=snes9x_macOS-amd64
### libretro core
| OS | status |
|---------------------|---------------------------------------------------------|
| Linux/amd64 | [![Status][libretro_linux-amd64]][cirrus-ci] |
| Linux/i386 | [![Status][libretro_linux-i386]][cirrus-ci] |
| Linux/armhf | [![Status][libretro_linux-armhf]][cirrus-ci] |
| Linux/armv7-neon-hf | [![Status][libretro_linux-armv7-neon-hf]][cirrus-ci] |
| Linux/arm64 | [![Status][libretro_linux-arm64]][cirrus-ci] |
| Android/arm | [![Status][libretro_android-arm]][cirrus-ci] |
| Android/arm64 | [![Status][libretro_android-arm64]][cirrus-ci] |
| Emscripten | [![Status][libretro_emscripten]][cirrus-ci] |
| macOS/amd64 | [![Status][libretro_macOS-amd64]][cirrus-ci] |
| Nintendo Wii | [![Status][libretro_nintendo-wii]][cirrus-ci] |
| Nintendo Switch | [![Status][libretro_nintendo-switch-libnx]][cirrus-ci] |
| Nintendo GameCube | [![Status][libretro_nintendo-ngc]][cirrus-ci] |
| PSP | [![Status][libretro_playstation-psp]][cirrus-ci] |
[libretro_linux-amd64]: https://api.cirrus-ci.com/github/snes9xgit/snes9x.svg?task=libretro_linux-amd64
[libretro_linux-i386]: https://api.cirrus-ci.com/github/snes9xgit/snes9x.svg?task=libretro_linux-i386
[libretro_linux-armhf]: https://api.cirrus-ci.com/github/snes9xgit/snes9x.svg?task=libretro_linux-armhf
[libretro_linux-armv7-neon-hf]: https://api.cirrus-ci.com/github/snes9xgit/snes9x.svg?task=libretro_linux-armv7-neon-hf
[libretro_linux-arm64]: https://api.cirrus-ci.com/github/snes9xgit/snes9x.svg?task=libretro_linux-arm64
[libretro_android-arm]: https://api.cirrus-ci.com/github/snes9xgit/snes9x.svg?task=libretro_android-arm
[libretro_android-arm64]: https://api.cirrus-ci.com/github/snes9xgit/snes9x.svg?task=libretro_android-arm64
[libretro_emscripten]: https://api.cirrus-ci.com/github/snes9xgit/snes9x.svg?task=libretro_emscripten
[libretro_macOS-amd64]: https://api.cirrus-ci.com/github/snes9xgit/snes9x.svg?task=libretro_macOS-amd64
[libretro_nintendo-wii]: https://api.cirrus-ci.com/github/snes9xgit/snes9x.svg?task=libretro_nintendo-wii
[libretro_nintendo-switch-libnx]: https://api.cirrus-ci.com/github/snes9xgit/snes9x.svg?task=libretro_nintendo-switch-libnx
[libretro_nintendo-ngc]: https://api.cirrus-ci.com/github/snes9xgit/snes9x.svg?task=libretro_nintendo-ngc
[libretro_playstation-psp]: https://api.cirrus-ci.com/github/snes9xgit/snes9x.svg?task=libretro_playstation-psp

@ -0,0 +1,536 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include <cmath>
#include "../snes9x.h"
#include "apu.h"
#include "../msu1.h"
#include "../snapshot.h"
#include "../display.h"
#include "resampler.h"
#include "bapu/snes/snes.hpp"
static const int APU_DEFAULT_INPUT_RATE = 31950; // ~59.94Hz
static const int APU_SAMPLE_BLOCK = 48;
static const int APU_NUMERATOR_NTSC = 15664;
static const int APU_DENOMINATOR_NTSC = 328125;
static const int APU_NUMERATOR_PAL = 34176;
static const int APU_DENOMINATOR_PAL = 709379;
// Max number of samples we'll ever generate before call to port API and
// moving the samples to the resampler.
// This is 535 sample frames, which corresponds to 1 video frame + some leeway
// for use with SoundSync, multiplied by 2, for left and right samples.
static const int MINIMUM_BUFFER_SIZE = 550 * 2;
namespace SNES {
#include "bapu/dsp/blargg_endian.h"
CPU cpu;
} // namespace SNES
namespace spc {
static apu_callback callback = NULL;
static void *callback_data = NULL;
static bool8 sound_in_sync = TRUE;
static bool8 sound_enabled = FALSE;
static Resampler *resampler = NULL;
static int32 reference_time;
static uint32 remainder;
static const int timing_hack_numerator = 256;
static int timing_hack_denominator = 256;
/* Set these to NTSC for now. Will change to PAL in S9xAPUTimingSetSpeedup
if necessary on game load. */
static uint32 ratio_numerator = APU_NUMERATOR_NTSC;
static uint32 ratio_denominator = APU_DENOMINATOR_NTSC;
static double dynamic_rate_multiplier = 1.0;
} // namespace spc
namespace msu {
// Always 16-bit, Stereo; 1.5x dsp buffer to never overflow
static Resampler *resampler = NULL;
static int16 *resample_buffer = NULL;
static int resample_buffer_size = 0;
} // namespace msu
static void UpdatePlaybackRate(void);
static void SPCSnapshotCallback(void);
static inline int S9xAPUGetClock(int32);
static inline int S9xAPUGetClockRemainder(int32);
bool8 S9xMixSamples(uint8 *dest, int sample_count)
{
int16 *out = (int16 *)dest;
if (Settings.Mute)
{
memset(out, 0, sample_count << 1);
S9xClearSamples();
}
else
{
if (spc::resampler->avail() >= sample_count)
{
spc::resampler->read((short *)out, sample_count);
if (Settings.MSU1)
{
if (msu::resampler->avail() >= sample_count)
{
if (msu::resample_buffer_size < sample_count)
{
if (msu::resample_buffer)
delete[] msu::resample_buffer;
msu::resample_buffer = new int16[sample_count];
msu::resample_buffer_size = sample_count;
}
msu::resampler->read(msu::resample_buffer,
sample_count);
for (int i = 0; i < sample_count; ++i)
{
int32 mixed = (int32)out[i] + msu::resample_buffer[i];
out[i] = ((int16)mixed != mixed) ? (mixed >> 31) ^ 0x7fff : mixed;
}
}
else // should never occur
assert(0);
}
}
else
{
memset(out, 0, sample_count << 1);
return false;
}
}
if (spc::resampler->space_empty() >= 535 * 2 || !Settings.SoundSync ||
Settings.TurboMode || Settings.Mute)
spc::sound_in_sync = true;
else
spc::sound_in_sync = false;
return true;
}
int S9xGetSampleCount(void)
{
return spc::resampler->avail();
}
void S9xLandSamples(void)
{
if (spc::callback != NULL)
spc::callback(spc::callback_data);
if (spc::resampler->space_empty() >= 535 * 2 || !Settings.SoundSync ||
Settings.TurboMode || Settings.Mute)
spc::sound_in_sync = true;
else
spc::sound_in_sync = false;
}
void S9xClearSamples(void)
{
spc::resampler->clear();
if (Settings.MSU1)
msu::resampler->clear();
}
bool8 S9xSyncSound(void)
{
if (!Settings.SoundSync || spc::sound_in_sync)
return (TRUE);
S9xLandSamples();
return (spc::sound_in_sync);
}
void S9xSetSamplesAvailableCallback(apu_callback callback, void *data)
{
spc::callback = callback;
spc::callback_data = data;
}
void S9xUpdateDynamicRate(int avail, int buffer_size)
{
spc::dynamic_rate_multiplier = 1.0 + (Settings.DynamicRateLimit * (buffer_size - 2 * avail)) /
(double)(1000 * buffer_size);
UpdatePlaybackRate();
}
static void UpdatePlaybackRate(void)
{
if (Settings.SoundInputRate == 0)
Settings.SoundInputRate = APU_DEFAULT_INPUT_RATE;
double time_ratio = (double)Settings.SoundInputRate * spc::timing_hack_numerator / (Settings.SoundPlaybackRate * spc::timing_hack_denominator);
if (Settings.DynamicRateControl)
{
time_ratio *= spc::dynamic_rate_multiplier;
}
spc::resampler->time_ratio(time_ratio);
if (Settings.MSU1)
{
time_ratio = (44100.0 / Settings.SoundPlaybackRate) * (Settings.SoundInputRate / 32040.0);
msu::resampler->time_ratio(time_ratio);
}
}
bool8 S9xInitSound(int buffer_ms)
{
// The resampler and spc unit use samples (16-bit short) as arguments.
int buffer_size_samples = MINIMUM_BUFFER_SIZE;
int requested_buffer_size_samples = Settings.SoundPlaybackRate * buffer_ms * 2 / 1000;
if (requested_buffer_size_samples > buffer_size_samples)
buffer_size_samples = requested_buffer_size_samples;
if (!spc::resampler)
{
spc::resampler = new Resampler(buffer_size_samples);
if (!spc::resampler)
return (FALSE);
}
else
spc::resampler->resize(buffer_size_samples);
if (!msu::resampler)
{
msu::resampler = new Resampler(buffer_size_samples * 3 / 2);
if (!msu::resampler)
return (FALSE);
}
else
msu::resampler->resize(buffer_size_samples * 3 / 2);
SNES::dsp.spc_dsp.set_output(spc::resampler);
S9xMSU1SetOutput(msu::resampler);
UpdatePlaybackRate();
spc::sound_enabled = S9xOpenSoundDevice();
return (spc::sound_enabled);
}
void S9xSetSoundControl(uint8 voice_switch)
{
SNES::dsp.spc_dsp.set_stereo_switch(voice_switch << 8 | voice_switch);
}
void S9xSetSoundMute(bool8 mute)
{
Settings.Mute = mute;
if (!spc::sound_enabled)
Settings.Mute = TRUE;
}
void S9xDumpSPCSnapshot(void)
{
SNES::dsp.spc_dsp.dump_spc_snapshot();
}
static void SPCSnapshotCallback(void)
{
S9xSPCDump(S9xGetFilenameInc((".spc"), SPC_DIR));
printf("Dumped key-on triggered spc snapshot.\n");
}
bool8 S9xInitAPU(void)
{
spc::resampler = NULL;
msu::resampler = NULL;
return (TRUE);
}
void S9xDeinitAPU(void)
{
if (spc::resampler)
{
delete spc::resampler;
spc::resampler = NULL;
}
if (msu::resampler)
{
delete msu::resampler;
msu::resampler = NULL;
}
S9xMSU1DeInit();
}
static inline int S9xAPUGetClock(int32 cpucycles)
{
return (spc::ratio_numerator * (cpucycles - spc::reference_time) + spc::remainder) /
spc::ratio_denominator;
}
static inline int S9xAPUGetClockRemainder(int32 cpucycles)
{
return (spc::ratio_numerator * (cpucycles - spc::reference_time) + spc::remainder) %
spc::ratio_denominator;
}
uint8 S9xAPUReadPort(int port)
{
S9xAPUExecute();
return ((uint8)SNES::smp.port_read(port & 3));
}
void S9xAPUWritePort(int port, uint8 byte)
{
S9xAPUExecute();
SNES::cpu.port_write(port & 3, byte);
}
void S9xAPUSetReferenceTime(int32 cpucycles)
{
spc::reference_time = cpucycles;
}
void S9xAPUExecute(void)
{
SNES::smp.clock -= S9xAPUGetClock(CPU.Cycles);
SNES::smp.enter();
spc::remainder = S9xAPUGetClockRemainder(CPU.Cycles);
S9xAPUSetReferenceTime(CPU.Cycles);
}
void S9xAPUEndScanline(void)
{
S9xAPUExecute();
SNES::dsp.synchronize();
if (spc::resampler->space_filled() >= APU_SAMPLE_BLOCK || !spc::sound_in_sync)
S9xLandSamples();
}
void S9xAPUTimingSetSpeedup(int ticks)
{
if (ticks != 0)
printf("APU speedup hack: %d\n", ticks);
spc::timing_hack_denominator = 256 - ticks;
spc::ratio_numerator = Settings.PAL ? APU_NUMERATOR_PAL : APU_NUMERATOR_NTSC;
spc::ratio_denominator = Settings.PAL ? APU_DENOMINATOR_PAL : APU_DENOMINATOR_NTSC;
spc::ratio_denominator = spc::ratio_denominator * spc::timing_hack_denominator / spc::timing_hack_numerator;
UpdatePlaybackRate();
}
void S9xResetAPU(void)
{
spc::reference_time = 0;
spc::remainder = 0;
SNES::cpu.reset();
SNES::smp.power();
SNES::dsp.power();
SNES::dsp.spc_dsp.set_spc_snapshot_callback(SPCSnapshotCallback);
S9xClearSamples();
}
void S9xSoftResetAPU(void)
{
spc::reference_time = 0;
spc::remainder = 0;
SNES::cpu.reset();
SNES::smp.reset();
SNES::dsp.reset();
S9xClearSamples();
}
void S9xAPUSaveState(uint8 *block)
{
uint8 *ptr = block;
SNES::smp.save_state(&ptr);
SNES::dsp.save_state(&ptr);
SNES::set_le32(ptr, spc::reference_time);
ptr += sizeof(int32);
SNES::set_le32(ptr, spc::remainder);
ptr += sizeof(int32);
SNES::set_le32(ptr, SNES::dsp.clock);
ptr += sizeof(int32);
memcpy(ptr, SNES::cpu.registers, 4);
ptr += sizeof(int32);
memset(ptr, 0, SPC_SAVE_STATE_BLOCK_SIZE - (ptr - block));
}
void S9xAPULoadState(uint8 *block)
{
uint8 *ptr = block;
SNES::smp.load_state(&ptr);
SNES::dsp.load_state(&ptr);
spc::reference_time = SNES::get_le32(ptr);
ptr += sizeof(int32);
spc::remainder = SNES::get_le32(ptr);
ptr += sizeof(int32);
SNES::dsp.clock = SNES::get_le32(ptr);
ptr += sizeof(int32);
memcpy(SNES::cpu.registers, ptr, 4);
}
static void to_var_from_buf(uint8 **buf, void *var, size_t size)
{
memcpy(var, *buf, size);
*buf += size;
}
#undef IF_0_THEN_256
#define IF_0_THEN_256(n) ((uint8)((n)-1) + 1)
void S9xAPULoadBlarggState(uint8 *oldblock)
{
uint8 *ptr = oldblock;
SNES::SPC_State_Copier copier(&ptr, to_var_from_buf);
copier.copy(SNES::smp.apuram, 0x10000); // RAM
uint8 regs_in[0x10];
uint8 regs[0x10];
uint16 pc, spc_time, dsp_time;
uint8 a, x, y, psw, sp;
copier.copy(regs, 0x10); // REGS
copier.copy(regs_in, 0x10); // REGS_IN
// CPU Regs
pc = copier.copy_int(0, sizeof(uint16));
a = copier.copy_int(0, sizeof(uint8));
x = copier.copy_int(0, sizeof(uint8));
y = copier.copy_int(0, sizeof(uint8));
psw = copier.copy_int(0, sizeof(uint8));
sp = copier.copy_int(0, sizeof(uint8));
copier.extra();
// times
spc_time = copier.copy_int(0, sizeof(uint16));
dsp_time = copier.copy_int(0, sizeof(uint16));
int cur_time = S9xAPUGetClock(CPU.Cycles);
// spc_time is absolute, dsp_time is relative
// smp.clock is relative, dsp.clock relative but counting upwards
SNES::smp.clock = spc_time - cur_time;
SNES::dsp.clock = -1 * dsp_time;
// DSP
SNES::dsp.load_state(&ptr);
// Timers
uint16 next_time[3];
uint8 divider[3], counter[3];
for (int i = 0; i < 3; i++)
{
next_time[i] = copier.copy_int(0, sizeof(uint16));
divider[i] = copier.copy_int(0, sizeof(uint8));
counter[i] = copier.copy_int(0, sizeof(uint8));
copier.extra();
}
// construct timers out of available parts from blargg smp
SNES::smp.timer0.enable = regs[1] >> 0 & 1; // regs[1] = CONTROL
SNES::smp.timer0.target = IF_0_THEN_256(regs[10]); // regs[10+i] = TiTARGET
// blargg counts time, get ticks through timer frequency
// (assume tempo = 256)
SNES::smp.timer0.stage1_ticks = 128 - (next_time[0] - cur_time) / 128;
SNES::smp.timer0.stage2_ticks = divider[0];
SNES::smp.timer0.stage3_ticks = counter[0];
SNES::smp.timer1.enable = regs[1] >> 1 & 1;
SNES::smp.timer1.target = IF_0_THEN_256(regs[11]);
SNES::smp.timer1.stage1_ticks = 128 - (next_time[1] - cur_time) / 128;
SNES::smp.timer1.stage2_ticks = divider[0];
SNES::smp.timer1.stage3_ticks = counter[0];
SNES::smp.timer2.enable = regs[1] >> 2 & 1;
SNES::smp.timer2.target = IF_0_THEN_256(regs[12]);
SNES::smp.timer2.stage1_ticks = 16 - (next_time[2] - cur_time) / 16;
SNES::smp.timer2.stage2_ticks = divider[0];
SNES::smp.timer2.stage3_ticks = counter[0];
copier.extra();
SNES::smp.opcode_number = 0;
SNES::smp.opcode_cycle = 0;
SNES::smp.regs.pc = pc;
SNES::smp.regs.sp = sp;
SNES::smp.regs.B.a = a;
SNES::smp.regs.x = x;
SNES::smp.regs.B.y = y;
// blargg's psw has same layout as byuu's flags
SNES::smp.regs.p = psw;
// blargg doesn't explicitly store iplrom_enable
SNES::smp.status.iplrom_enable = regs[1] & 0x80;
SNES::smp.status.dsp_addr = regs[2];
SNES::smp.status.ram00f8 = regs_in[8];
SNES::smp.status.ram00f9 = regs_in[9];
// default to 0 - we are on an opcode boundary, shouldn't matter
SNES::smp.rd = SNES::smp.wr = SNES::smp.dp = SNES::smp.sp = SNES::smp.ya = SNES::smp.bit = 0;
spc::reference_time = SNES::get_le32(ptr);
ptr += sizeof(int32);
spc::remainder = SNES::get_le32(ptr);
// blargg stores CPUIx in regs_in
memcpy(SNES::cpu.registers, regs_in + 4, 4);
}
bool8 S9xSPCDump(const char *filename)
{
FILE *fs;
uint8 buf[SPC_FILE_SIZE];
size_t ignore;
fs = fopen(filename, "wb");
if (!fs)
return (FALSE);
S9xSetSoundMute(TRUE);
SNES::smp.save_spc(buf);
ignore = fwrite(buf, SPC_FILE_SIZE, 1, fs);
if (ignore == 0)
{
fprintf(stderr, "Couldn't write file %s.\n", filename);
}
fclose(fs);
S9xSetSoundMute(FALSE);
return (TRUE);
}

@ -0,0 +1,52 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _APU_H_
#define _APU_H_
#include "../snes9x.h"
typedef void (*apu_callback) (void *);
#define SPC_SAVE_STATE_BLOCK_SIZE (1024 * 65)
#define SPC_FILE_SIZE (66048)
bool8 S9xInitAPU (void);
void S9xDeinitAPU (void);
void S9xResetAPU (void);
void S9xSoftResetAPU (void);
uint8 S9xAPUReadPort (int);
void S9xAPUWritePort (int, uint8);
void S9xAPUExecute (void);
void S9xAPUEndScanline (void);
void S9xAPUSetReferenceTime (int32);
void S9xAPUTimingSetSpeedup (int);
void S9xAPULoadState (uint8 *);
void S9xAPULoadBlarggState(uint8 *oldblock);
void S9xAPUSaveState (uint8 *);
void S9xDumpSPCSnapshot (void);
bool8 S9xSPCDump (const char *);
bool8 S9xInitSound (int);
bool8 S9xOpenSoundDevice (void);
bool8 S9xSyncSound (void);
int S9xGetSampleCount (void);
void S9xSetSoundControl (uint8);
void S9xSetSoundMute (bool8);
void S9xLandSamples (void);
void S9xClearSamples (void);
bool8 S9xMixSamples (uint8 *, int);
void S9xSetSamplesAvailableCallback (apu_callback, void *);
void S9xUpdateDynamicRate (int, int);
#define DSP_INTERPOLATION_NONE 0
#define DSP_INTERPOLATION_LINEAR 1
#define DSP_INTERPOLATION_GAUSSIAN 2
#define DSP_INTERPOLATION_CUBIC 3
#define DSP_INTERPOLATION_SINC 4
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,324 @@
// Highly accurate SNES SPC-700 DSP emulator
// snes_spc 0.9.0
#ifndef SPC_DSP_H
#define SPC_DSP_H
#include "blargg_common.h"
extern "C" { typedef void (*dsp_copy_func_t)( unsigned char** io, void* state, size_t ); }
class SPC_DSP {
public:
typedef BOOST::uint8_t uint8_t;
// Setup
// Initializes DSP and has it use the 64K RAM provided
void init( void* ram_64k );
// Sets destination for output samples. If out is NULL or out_size is 0,
// doesn't generate any.
typedef short sample_t;
void set_output( sample_t* out, int out_size );
void set_output( Resampler* resampler );
// Number of samples written to output since it was last set, always
// a multiple of 2. Undefined if more samples were generated than
// output buffer could hold.
int sample_count() const;
// Emulation
// Resets DSP to power-on state
void reset();
// Emulates pressing reset switch on SNES
void soft_reset();
// Reads/writes DSP registers. For accuracy, you must first call run()
// to catch the DSP up to present.
int read ( int addr ) const;
void write( int addr, int data );
// Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks
// a pair of samples is be generated.
void run( int clock_count );
// Sound control
// Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events).
// Reduces emulation accuracy.
enum { voice_count = 8 };
void mute_voices( int mask );
// State
// Resets DSP and uses supplied values to initialize registers
enum { register_count = 128 };
void load( uint8_t const regs [register_count] );
// Saves/loads exact emulator state
enum { state_size = 640 }; // maximum space needed when saving
typedef dsp_copy_func_t copy_func_t;
void copy_state( unsigned char** io, copy_func_t );
// Returns non-zero if new key-on events occurred since last call
bool check_kon();
// Snes9x Accessor
int stereo_switch;
int take_spc_snapshot;
void (*spc_snapshot_callback) (void);
void set_spc_snapshot_callback( void (*callback) (void) );
void dump_spc_snapshot( void );
void set_stereo_switch( int );
uint8_t reg_value( int, int );
int envx_value( int );
// DSP register addresses
// Global registers
enum {
r_mvoll = 0x0C, r_mvolr = 0x1C,
r_evoll = 0x2C, r_evolr = 0x3C,
r_kon = 0x4C, r_koff = 0x5C,
r_flg = 0x6C, r_endx = 0x7C,
r_efb = 0x0D, r_pmon = 0x2D,
r_non = 0x3D, r_eon = 0x4D,
r_dir = 0x5D, r_esa = 0x6D,
r_edl = 0x7D,
r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F
};
// Voice registers
enum {
v_voll = 0x00, v_volr = 0x01,
v_pitchl = 0x02, v_pitchh = 0x03,
v_srcn = 0x04, v_adsr0 = 0x05,
v_adsr1 = 0x06, v_gain = 0x07,
v_envx = 0x08, v_outx = 0x09
};
public:
enum { extra_size = 16 };
sample_t* extra() { return m.extra; }
sample_t const* out_pos() const { return m.out; }
void disable_surround( bool ) { } // not supported
public:
BLARGG_DISABLE_NOTHROW
typedef BOOST::int8_t int8_t;
typedef BOOST::int16_t int16_t;
enum { echo_hist_size = 8 };
enum env_mode_t { env_release, env_attack, env_decay, env_sustain };
enum { brr_buf_size = 12 };
struct voice_t
{
int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling)
int buf_pos; // place in buffer where next samples will be decoded
int interp_pos; // relative fractional position in sample (0x1000 = 1.0)
int brr_addr; // address of current BRR block
int brr_offset; // current decoding offset in BRR block
uint8_t* regs; // pointer to voice's DSP registers
int vbit; // bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc.
int kon_delay; // KON delay/current setup phase
env_mode_t env_mode;
int env; // current envelope level
int hidden_env; // used by GAIN mode 7, very obscure quirk
uint8_t t_envx_out;
int voice_number;
};
private:
enum { brr_block_size = 9 };
Resampler *resampler;
struct state_t
{
uint8_t regs [register_count];
// Echo history keeps most recent 8 samples (twice the size to simplify wrap handling)
int echo_hist [echo_hist_size * 2] [2];
int (*echo_hist_pos) [2]; // &echo_hist [0 to 7]
int every_other_sample; // toggles every sample
int kon; // KON value when last checked
int noise;
int counter;
int echo_offset; // offset from ESA in echo buffer
int echo_length; // number of bytes that echo_offset will stop at
int phase; // next clock cycle to run (0-31)
bool kon_check; // set when a new KON occurs
// Hidden registers also written to when main register is written to
int new_kon;
uint8_t endx_buf;
uint8_t envx_buf;
uint8_t outx_buf;
// Temporary state between clocks
// read once per sample
int t_pmon;
int t_non;
int t_eon;
int t_dir;
int t_koff;
// read a few clocks ahead then used
int t_brr_next_addr;
int t_adsr0;
int t_brr_header;
int t_brr_byte;
int t_srcn;
int t_esa;
int t_echo_enabled;
// internal state that is recalculated every sample
int t_dir_addr;
int t_pitch;
int t_output;
int t_looped;
int t_echo_ptr;
// left/right sums
int t_main_out [2];
int t_echo_out [2];
int t_echo_in [2];
voice_t voices [voice_count];
// non-emulation state
uint8_t* ram; // 64K shared RAM between DSP and SMP
int mute_mask;
sample_t* out;
sample_t* out_end;
sample_t* out_begin;
sample_t extra [extra_size];
uint8_t separate_echo_buffer [0x10000];
};
state_t m;
void init_counter();
void run_counters();
unsigned read_counter( int rate );
int interpolate( voice_t const* v );
void run_envelope( voice_t* const v );
void decode_brr( voice_t* v );
void misc_27();
void misc_28();
void misc_29();
void misc_30();
void voice_output( voice_t const* v, int ch );
void voice_V1( voice_t* const );
void voice_V2( voice_t* const );
void voice_V3( voice_t* const );
void voice_V3a( voice_t* const );
void voice_V3b( voice_t* const );
void voice_V3c( voice_t* const );
void voice_V4( voice_t* const );
void voice_V5( voice_t* const );
void voice_V6( voice_t* const );
void voice_V7( voice_t* const );
void voice_V8( voice_t* const );
void voice_V9( voice_t* const );
void voice_V7_V4_V1( voice_t* const );
void voice_V8_V5_V2( voice_t* const );
void voice_V9_V6_V3( voice_t* const );
void echo_read( int ch );
int echo_output( int ch );
void echo_write( int ch );
void echo_22();
void echo_23();
void echo_24();
void echo_25();
void echo_26();
void echo_27();
void echo_28();
void echo_29();
void echo_30();
void soft_reset_common();
};
#include <assert.h>
inline int SPC_DSP::sample_count() const { return m.out - m.out_begin; }
inline int SPC_DSP::read( int addr ) const
{
assert( (unsigned) addr < register_count );
return m.regs [addr];
}
inline void SPC_DSP::write( int addr, int data )
{
assert( (unsigned) addr < register_count );
m.regs [addr] = (uint8_t) data;
switch ( addr & 0x0F )
{
case v_envx:
m.envx_buf = (uint8_t) data;
break;
case v_outx:
m.outx_buf = (uint8_t) data;
break;
case 0x0C:
if ( addr == r_kon )
m.new_kon = (uint8_t) data;
if ( addr == r_endx ) // always cleared, regardless of data written
{
m.endx_buf = 0;
m.regs [r_endx] = 0;
}
break;
}
}
inline void SPC_DSP::mute_voices( int mask ) { m.mute_mask = mask; }
inline bool SPC_DSP::check_kon()
{
bool old = m.kon_check;
m.kon_check = 0;
return old;
}
#if !SPC_NO_COPY_STATE_FUNCS
class SPC_State_Copier {
SPC_DSP::copy_func_t func;
unsigned char** buf;
public:
SPC_State_Copier( unsigned char** p, SPC_DSP::copy_func_t f ) { func = f; buf = p; }
void copy( void* state, size_t size );
int copy_int( int state, int size );
void skip( int count );
void extra();
};
#define SPC_COPY( type, state )\
{\
state = (BOOST::type) copier.copy_int( state, sizeof (BOOST::type) );\
assert( (BOOST::type) state == state );\
}
#endif
#endif

@ -0,0 +1,186 @@
// Sets up common environment for Shay Green's libraries.
// To change configuration options, modify blargg_config.h, not this file.
// snes_spc 0.9.0
#ifndef BLARGG_COMMON_H
#define BLARGG_COMMON_H
#include <stddef.h>
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
#undef BLARGG_COMMON_H
// allow blargg_config.h to #include blargg_common.h
#include "blargg_config.h"
#ifndef BLARGG_COMMON_H
#define BLARGG_COMMON_H
// BLARGG_RESTRICT: equivalent to restrict, where supported
#if defined (__GNUC__) || _MSC_VER >= 1100
#define BLARGG_RESTRICT __restrict
#else
#define BLARGG_RESTRICT
#endif
// STATIC_CAST(T,expr): Used in place of static_cast<T> (expr)
#ifndef STATIC_CAST
#define STATIC_CAST(T,expr) ((T) (expr))
#endif
// blargg_err_t (0 on success, otherwise error string)
#ifndef blargg_err_t
typedef const char* blargg_err_t;
#endif
// blargg_vector - very lightweight vector of POD types (no constructor/destructor)
template<class T>
class blargg_vector {
T* begin_;
size_t size_;
public:
blargg_vector() : begin_( 0 ), size_( 0 ) { }
~blargg_vector() { free( begin_ ); }
size_t size() const { return size_; }
T* begin() const { return begin_; }
T* end() const { return begin_ + size_; }
blargg_err_t resize( size_t n )
{
// TODO: blargg_common.cpp to hold this as an outline function, ugh
void* p = realloc( begin_, n * sizeof (T) );
if ( p )
begin_ = (T*) p;
else if ( n > size_ ) // realloc failure only a problem if expanding
return "Out of memory";
size_ = n;
return 0;
}
void clear() { void* p = begin_; begin_ = 0; size_ = 0; free( p ); }
T& operator [] ( size_t n ) const
{
assert( n <= size_ ); // <= to allow past-the-end value
return begin_ [n];
}
};
#ifndef BLARGG_DISABLE_NOTHROW
// throw spec mandatory in ISO C++ if operator new can return NULL
#if __cplusplus >= 199711 || defined (__GNUC__)
#define BLARGG_THROWS( spec ) throw spec
#else
#define BLARGG_THROWS( spec )
#endif
#define BLARGG_DISABLE_NOTHROW \
void* operator new ( size_t s ) BLARGG_THROWS(()) { return malloc( s ); }\
void operator delete ( void* p ) { free( p ); }
#define BLARGG_NEW new
#else
#include <new>
#define BLARGG_NEW new (std::nothrow)
#endif
// BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant)
#define BLARGG_4CHAR( a, b, c, d ) \
((a&0xFF)*0x1000000L + (b&0xFF)*0x10000L + (c&0xFF)*0x100L + (d&0xFF))
// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0.
#ifndef BOOST_STATIC_ASSERT
#ifdef _MSC_VER
// MSVC6 (_MSC_VER < 1300) fails for use of __LINE__ when /Zl is specified
#define BOOST_STATIC_ASSERT( expr ) \
void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] )
#else
// Some other compilers fail when declaring same function multiple times in class,
// so differentiate them by line
#define BOOST_STATIC_ASSERT( expr ) \
void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] )
#endif
#endif
// BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1,
// compiler is assumed to support bool. If undefined, availability is determined.
#ifndef BLARGG_COMPILER_HAS_BOOL
#if defined (__MWERKS__)
#if !__option(bool)
#define BLARGG_COMPILER_HAS_BOOL 0
#endif
#elif defined (_MSC_VER)
#if _MSC_VER < 1100
#define BLARGG_COMPILER_HAS_BOOL 0
#endif
#elif defined (__GNUC__)
// supports bool
#elif __cplusplus < 199711
#define BLARGG_COMPILER_HAS_BOOL 0
#endif
#endif
#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL
// If you get errors here, modify your blargg_config.h file
typedef int bool;
const bool true = 1;
const bool false = 0;
#endif
// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough
#if INT_MAX < 0x7FFFFFFF || LONG_MAX == 0x7FFFFFFF
typedef long blargg_long;
#else
typedef int blargg_long;
#endif
#if UINT_MAX < 0xFFFFFFFF || ULONG_MAX == 0xFFFFFFFF
typedef unsigned long blargg_ulong;
#else
typedef unsigned blargg_ulong;
#endif
// BOOST::int8_t etc.
// HAVE_STDINT_H: If defined, use <stdint.h> for int8_t etc.
#if defined (HAVE_STDINT_H)
#include <stdint.h>
#define BOOST
// HAVE_INTTYPES_H: If defined, use <stdint.h> for int8_t etc.
#elif defined (HAVE_INTTYPES_H)
#include <inttypes.h>
#define BOOST
#else
struct BOOST
{
#if UCHAR_MAX == 0xFF && SCHAR_MAX == 0x7F
typedef signed char int8_t;
typedef unsigned char uint8_t;
#else
// No suitable 8-bit type available
typedef struct see_blargg_common_h int8_t;
typedef struct see_blargg_common_h uint8_t;
#endif
#if USHRT_MAX == 0xFFFF
typedef short int16_t;
typedef unsigned short uint16_t;
#else
// No suitable 16-bit type available
typedef struct see_blargg_common_h int16_t;
typedef struct see_blargg_common_h uint16_t;
#endif
#if ULONG_MAX == 0xFFFFFFFF
typedef long int32_t;
typedef unsigned long uint32_t;
#elif UINT_MAX == 0xFFFFFFFF
typedef int int32_t;
typedef unsigned int uint32_t;
#else
// No suitable 32-bit type available
typedef struct see_blargg_common_h int32_t;
typedef struct see_blargg_common_h uint32_t;
#endif
};
#endif
#endif
#endif

@ -0,0 +1,28 @@
// snes_spc 0.9.0 user configuration file. Don't replace when updating library.
// snes_spc 0.9.0
#ifndef BLARGG_CONFIG_H
#define BLARGG_CONFIG_H
// Uncomment to disable debugging checks
#if !defined(DEBUGGER) && !defined(_DEBUG)
#define NDEBUG 1
#endif
// Uncomment to enable platform-specific (and possibly non-portable) optimizations
#if !defined(__CELLOS_LV2__)
#define BLARGG_NONPORTABLE 1
#endif
// Uncomment if automatic byte-order determination doesn't work
//#define BLARGG_BIG_ENDIAN 1
// Uncomment if you get errors in the bool section of blargg_common.h
//#define BLARGG_COMPILER_HAS_BOOL 1
// Use standard config.h if present
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#endif

@ -0,0 +1,185 @@
// CPU Byte Order Utilities
// snes_spc 0.9.0
#ifndef BLARGG_ENDIAN
#define BLARGG_ENDIAN
#include "blargg_common.h"
// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16)
#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \
defined (__x86_64__) || defined (__ia64__) || defined (__i386__)
#define BLARGG_CPU_X86 1
#define BLARGG_CPU_CISC 1
#endif
#if defined (__powerpc__) || defined (__ppc__) || defined (__POWERPC__) || defined (__powerc)
#define BLARGG_CPU_POWERPC 1
#define BLARGG_CPU_RISC 1
#endif
// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only
// one may be #defined to 1. Only needed if something actually depends on byte order.
#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN)
#ifdef __GLIBC__
// GCC handles this for us
#include <endian.h>
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define BLARGG_LITTLE_ENDIAN 1
#elif __BYTE_ORDER == __BIG_ENDIAN
#define BLARGG_BIG_ENDIAN 1
#endif
#else
#if defined (LSB_FIRST) || defined (__LITTLE_ENDIAN__) || BLARGG_CPU_X86 || \
(defined (LITTLE_ENDIAN) && LITTLE_ENDIAN+0 != 1234)
#define BLARGG_LITTLE_ENDIAN 1
#endif
#if defined (MSB_FIRST) || defined (__BIG_ENDIAN__) || defined (WORDS_BIGENDIAN) || \
defined (__sparc__) || BLARGG_CPU_POWERPC || \
(defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321)
#define BLARGG_BIG_ENDIAN 1
#elif !defined (__mips__)
// No endian specified; assume little-endian, since it's most common
#define BLARGG_LITTLE_ENDIAN 1
#endif
#endif
#endif
#if BLARGG_LITTLE_ENDIAN && BLARGG_BIG_ENDIAN
#undef BLARGG_LITTLE_ENDIAN
#undef BLARGG_BIG_ENDIAN
#endif
inline void blargg_verify_byte_order()
{
#ifndef NDEBUG
#if BLARGG_BIG_ENDIAN
volatile int i = 1;
assert( *(volatile char*) &i == 0 );
#elif BLARGG_LITTLE_ENDIAN
volatile int i = 1;
assert( *(volatile char*) &i != 0 );
#endif
#endif
}
inline unsigned get_le16( void const* p )
{
return (unsigned) ((unsigned char const*) p) [1] << 8 |
(unsigned) ((unsigned char const*) p) [0];
}
inline unsigned get_be16( void const* p )
{
return (unsigned) ((unsigned char const*) p) [0] << 8 |
(unsigned) ((unsigned char const*) p) [1];
}
inline blargg_ulong get_le32( void const* p )
{
return (blargg_ulong) ((unsigned char const*) p) [3] << 24 |
(blargg_ulong) ((unsigned char const*) p) [2] << 16 |
(blargg_ulong) ((unsigned char const*) p) [1] << 8 |
(blargg_ulong) ((unsigned char const*) p) [0];
}
inline blargg_ulong get_be32( void const* p )
{
return (blargg_ulong) ((unsigned char const*) p) [0] << 24 |
(blargg_ulong) ((unsigned char const*) p) [1] << 16 |
(blargg_ulong) ((unsigned char const*) p) [2] << 8 |
(blargg_ulong) ((unsigned char const*) p) [3];
}
inline void set_le16( void* p, unsigned n )
{
((unsigned char*) p) [1] = (unsigned char) (n >> 8);
((unsigned char*) p) [0] = (unsigned char) n;
}
inline void set_be16( void* p, unsigned n )
{
((unsigned char*) p) [0] = (unsigned char) (n >> 8);
((unsigned char*) p) [1] = (unsigned char) n;
}
inline void set_le32( void* p, blargg_ulong n )
{
((unsigned char*) p) [0] = (unsigned char) n;
((unsigned char*) p) [1] = (unsigned char) (n >> 8);
((unsigned char*) p) [2] = (unsigned char) (n >> 16);
((unsigned char*) p) [3] = (unsigned char) (n >> 24);
}
inline void set_be32( void* p, blargg_ulong n )
{
((unsigned char*) p) [3] = (unsigned char) n;
((unsigned char*) p) [2] = (unsigned char) (n >> 8);
((unsigned char*) p) [1] = (unsigned char) (n >> 16);
((unsigned char*) p) [0] = (unsigned char) (n >> 24);
}
#if BLARGG_NONPORTABLE
// Optimized implementation if byte order is known
#if BLARGG_LITTLE_ENDIAN
#define GET_LE16( addr ) (*(BOOST::uint16_t*) (addr))
#define GET_LE32( addr ) (*(BOOST::uint32_t*) (addr))
#define SET_LE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data))
#define SET_LE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data))
#elif BLARGG_BIG_ENDIAN
#define GET_BE16( addr ) (*(BOOST::uint16_t*) (addr))
#define GET_BE32( addr ) (*(BOOST::uint32_t*) (addr))
#define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data))
#define SET_BE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data))
#if BLARGG_CPU_POWERPC
// PowerPC has special byte-reversed instructions
#if defined (__MWERKS__)
#define GET_LE16( addr ) (__lhbrx( addr, 0 ))
#define GET_LE32( addr ) (__lwbrx( addr, 0 ))
#define SET_LE16( addr, in ) (__sthbrx( in, addr, 0 ))
#define SET_LE32( addr, in ) (__stwbrx( in, addr, 0 ))
#elif defined (__GNUC__)
#define GET_LE16( addr ) ({unsigned ppc_lhbrx_; asm( "lhbrx %0,0,%1" : "=r" (ppc_lhbrx_) : "r" (addr), "0" (ppc_lhbrx_) ); ppc_lhbrx_;})
#define GET_LE32( addr ) ({unsigned ppc_lwbrx_; asm( "lwbrx %0,0,%1" : "=r" (ppc_lwbrx_) : "r" (addr), "0" (ppc_lwbrx_) ); ppc_lwbrx_;})
#define SET_LE16( addr, in ) ({asm( "sthbrx %0,0,%1" : : "r" (in), "r" (addr) );})
#define SET_LE32( addr, in ) ({asm( "stwbrx %0,0,%1" : : "r" (in), "r" (addr) );})
#endif
#endif
#endif
#endif
#ifndef GET_LE16
#define GET_LE16( addr ) get_le16( addr )
#define SET_LE16( addr, data ) set_le16( addr, data )
#endif
#ifndef GET_LE32
#define GET_LE32( addr ) get_le32( addr )
#define SET_LE32( addr, data ) set_le32( addr, data )
#endif
#ifndef GET_BE16
#define GET_BE16( addr ) get_be16( addr )
#define SET_BE16( addr, data ) set_be16( addr, data )
#endif
#ifndef GET_BE32
#define GET_BE32( addr ) get_be32( addr )
#define SET_BE32( addr, data ) set_be32( addr, data )
#endif
// auto-selecting versions
inline void set_le( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); }
inline void set_le( BOOST::uint32_t* p, blargg_ulong n ) { SET_LE32( p, n ); }
inline void set_be( BOOST::uint16_t* p, unsigned n ) { SET_BE16( p, n ); }
inline void set_be( BOOST::uint32_t* p, blargg_ulong n ) { SET_BE32( p, n ); }
inline unsigned get_le( BOOST::uint16_t* p ) { return GET_LE16( p ); }
inline blargg_ulong get_le( BOOST::uint32_t* p ) { return GET_LE32( p ); }
inline unsigned get_be( BOOST::uint16_t* p ) { return GET_BE16( p ); }
inline blargg_ulong get_be( BOOST::uint32_t* p ) { return GET_BE32( p ); }
#endif

@ -0,0 +1,100 @@
/* Included at the beginning of library source files, after all other #include lines.
Sets up helpful macros and services used in my source code. They don't need
module an annoying module prefix on their names since they are defined after
all other #include lines. */
// snes_spc 0.9.0
#ifndef BLARGG_SOURCE_H
#define BLARGG_SOURCE_H
// If debugging is enabled, abort program if expr is false. Meant for checking
// internal state and consistency. A failed assertion indicates a bug in the module.
// void assert( bool expr );
#include <assert.h>
// If debugging is enabled and expr is false, abort program. Meant for checking
// caller-supplied parameters and operations that are outside the control of the
// module. A failed requirement indicates a bug outside the module.
// void require( bool expr );
#undef require
#define require( expr ) assert( expr )
// Like printf() except output goes to debug log file. Might be defined to do
// nothing (not even evaluate its arguments).
// void dprintf( const char* format, ... );
static inline void blargg_dprintf_( const char*, ... ) { }
#undef dprintf
#define dprintf (1) ? (void) 0 : blargg_dprintf_
// If enabled, evaluate expr and if false, make debug log entry with source file
// and line. Meant for finding situations that should be examined further, but that
// don't indicate a problem. In all cases, execution continues normally.
#undef check
#define check( expr ) ((void) 0)
// If expr yields error string, return it from current function, otherwise continue.
#undef RETURN_ERR
#define RETURN_ERR( expr ) do { \
blargg_err_t blargg_return_err_ = (expr); \
if ( blargg_return_err_ ) return blargg_return_err_; \
} while ( 0 )
// If ptr is 0, return out of memory error string.
#undef CHECK_ALLOC
#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 )
// Avoid any macros which evaluate their arguments multiple times
#undef min
#undef max
#define DEF_MIN_MAX( type ) \
static inline type min( type x, type y ) { if ( x < y ) return x; return y; }\
static inline type max( type x, type y ) { if ( y < x ) return x; return y; }
DEF_MIN_MAX( int )
DEF_MIN_MAX( unsigned )
DEF_MIN_MAX( long )
DEF_MIN_MAX( unsigned long )
DEF_MIN_MAX( float )
DEF_MIN_MAX( double )
#undef DEF_MIN_MAX
/*
// using const references generates crappy code, and I am currenly only using these
// for built-in types, so they take arguments by value
// TODO: remove
inline int min( int x, int y )
template<class T>
inline T min( T x, T y )
{
if ( x < y )
return x;
return y;
}
template<class T>
inline T max( T x, T y )
{
if ( x < y )
return y;
return x;
}
*/
// TODO: good idea? bad idea?
#undef byte
#define byte byte_
typedef unsigned char byte;
// deprecated
#define BLARGG_CHECK_ALLOC CHECK_ALLOC
#define BLARGG_RETURN_ERR RETURN_ERR
// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf and check
#ifdef BLARGG_SOURCE_BEGIN
#include BLARGG_SOURCE_BEGIN
#endif
#endif

@ -0,0 +1,50 @@
#include "../snes/snes.hpp"
#define DSP_CPP
namespace SNES {
DSP dsp;
#include "SPC_DSP.cpp"
void DSP::power()
{
spc_dsp.init(smp.apuram);
spc_dsp.reset();
clock = 0;
}
void DSP::reset()
{
spc_dsp.soft_reset();
clock = 0;
}
static void from_dsp_to_state (uint8 **buf, void *var, size_t size)
{
memcpy(*buf, var, size);
*buf += size;
}
static void to_dsp_from_state (uint8 **buf, void *var, size_t size)
{
memcpy(var, *buf, size);
*buf += size;
}
void DSP::save_state (uint8 **ptr)
{
spc_dsp.copy_state(ptr, from_dsp_to_state);
}
void DSP::load_state (uint8 **ptr)
{
spc_dsp.copy_state(ptr, to_dsp_from_state);
}
DSP::DSP()
{
clock = 0;
}
}

@ -0,0 +1,34 @@
#include "SPC_DSP.h"
#include <stdio.h>
class DSP : public Processor {
public:
inline uint8 read(uint8 addr) {
synchronize ();
return spc_dsp.read(addr);
}
inline void synchronize (void) {
if (clock) {
spc_dsp.run (clock);
clock = 0;
}
}
inline void write(uint8 addr, uint8 data) {
synchronize ();
spc_dsp.write(addr, data);
}
void save_state(uint8 **);
void load_state(uint8 **);
void power();
void reset();
DSP();
SPC_DSP spc_dsp;
};
extern DSP dsp;

@ -0,0 +1,122 @@
uint8 SMP::op_adc(uint8 x, uint8 y) {
int r = x + y + regs.p.c;
regs.p.n = r & 0x80;
regs.p.v = ~(x ^ y) & (x ^ r) & 0x80;
regs.p.h = (x ^ y ^ r) & 0x10;
regs.p.z = (uint8)r == 0;
regs.p.c = r > 0xff;
return r;
}
uint16 SMP::op_addw(uint16 x, uint16 y) {
uint16 r;
regs.p.c = 0;
r = op_adc(x, y);
r |= op_adc(x >> 8, y >> 8) << 8;
regs.p.z = r == 0;
return r;
}
uint8 SMP::op_and(uint8 x, uint8 y) {
x &= y;
regs.p.n = x & 0x80;
regs.p.z = x == 0;
return x;
}
uint8 SMP::op_cmp(uint8 x, uint8 y) {
int r = x - y;
regs.p.n = r & 0x80;
regs.p.z = (uint8)r == 0;
regs.p.c = r >= 0;
return x;
}
uint16 SMP::op_cmpw(uint16 x, uint16 y) {
int r = x - y;
regs.p.n = r & 0x8000;
regs.p.z = (uint16)r == 0;
regs.p.c = r >= 0;
return x;
}
uint8 SMP::op_eor(uint8 x, uint8 y) {
x ^= y;
regs.p.n = x & 0x80;
regs.p.z = x == 0;
return x;
}
uint8 SMP::op_or(uint8 x, uint8 y) {
x |= y;
regs.p.n = x & 0x80;
regs.p.z = x == 0;
return x;
}
uint8 SMP::op_sbc(uint8 x, uint8 y) {
int r = x - y - !regs.p.c;
regs.p.n = r & 0x80;
regs.p.v = (x ^ y) & (x ^ r) & 0x80;
regs.p.h = !((x ^ y ^ r) & 0x10);
regs.p.z = (uint8)r == 0;
regs.p.c = r >= 0;
return r;
}
uint16 SMP::op_subw(uint16 x, uint16 y) {
uint16 r;
regs.p.c = 1;
r = op_sbc(x, y);
r |= op_sbc(x >> 8, y >> 8) << 8;
regs.p.z = r == 0;
return r;
}
uint8 SMP::op_inc(uint8 x) {
x++;
regs.p.n = x & 0x80;
regs.p.z = x == 0;
return x;
}
uint8 SMP::op_dec(uint8 x) {
x--;
regs.p.n = x & 0x80;
regs.p.z = x == 0;
return x;
}
uint8 SMP::op_asl(uint8 x) {
regs.p.c = x & 0x80;
x <<= 1;
regs.p.n = x & 0x80;
regs.p.z = x == 0;
return x;
}
uint8 SMP::op_lsr(uint8 x) {
regs.p.c = x & 0x01;
x >>= 1;
regs.p.n = x & 0x80;
regs.p.z = x == 0;
return x;
}
uint8 SMP::op_rol(uint8 x) {
unsigned carry = (unsigned)regs.p.c;
regs.p.c = x & 0x80;
x = (x << 1) | carry;
regs.p.n = x & 0x80;
regs.p.z = x == 0;
return x;
}
uint8 SMP::op_ror(uint8 x) {
unsigned carry = (unsigned)regs.p.c << 7;
regs.p.c = x & 0x01;
x = carry | (x >> 1);
regs.p.n = x & 0x80;
regs.p.z = x == 0;
return x;
}

@ -0,0 +1,78 @@
void SMP::tick() {
timer0.tick();
timer1.tick();
timer2.tick();
clock++;
dsp.clock++;
}
void SMP::tick(unsigned clocks) {
timer0.tick(clocks);
timer1.tick(clocks);
timer2.tick(clocks);
clock += clocks;
dsp.clock += clocks;
}
void SMP::op_io() {
tick();
}
void SMP::op_io(unsigned clocks) {
tick(clocks);
}
uint8 SMP::op_read(uint16 addr) {
tick();
if((addr & 0xfff0) == 0x00f0) return mmio_read(addr);
if(addr >= 0xffc0 && status.iplrom_enable) return iplrom[addr & 0x3f];
return apuram[addr];
}
void SMP::op_write(uint16 addr, uint8 data) {
tick();
if((addr & 0xfff0) == 0x00f0) mmio_write(addr, data);
apuram[addr] = data; //all writes go to RAM, even MMIO writes
}
uint8 SMP::op_readstack()
{
tick();
return apuram[0x0100 | ++regs.sp];
}
void SMP::op_writestack(uint8 data)
{
tick();
apuram[0x0100 | regs.sp--] = data;
}
void SMP::op_step() {
#define op_readpc() op_read(regs.pc++)
#define op_readdp(addr) op_read((regs.p.p << 8) + ((addr) & 0xff))
#define op_writedp(addr, data) op_write((regs.p.p << 8) + ((addr) & 0xff), data)
#define op_readaddr(addr) op_read(addr)
#define op_writeaddr(addr, data) op_write(addr, data)
if(opcode_cycle == 0)
{
#ifdef DEBUGGER
if (Settings.TraceSMP)
{
disassemble_opcode(tmp, regs.pc);
S9xTraceMessage (tmp);
}
#endif
opcode_number = op_readpc();
}
switch(opcode_number) {
#include "core/oppseudo_misc.cpp"
#include "core/oppseudo_mov.cpp"
#include "core/oppseudo_pc.cpp"
#include "core/oppseudo_read.cpp"
#include "core/oppseudo_rmw.cpp"
}
}

@ -0,0 +1,311 @@
case 0x00: {
op_io();
break;
}
case 0xef: {
op_io(2);
regs.pc--;
break;
}
case 0xff: {
op_io(2);
regs.pc--;
break;
}
case 0x9f: {
op_io(4);
regs.B.a = (regs.B.a >> 4) | (regs.B.a << 4);
regs.p.n = !!(regs.B.a & 0x80);
regs.p.z = (regs.B.a == 0);
break;
}
case 0xdf: {
op_io(2);
if(regs.p.c || (regs.B.a) > 0x99) {
regs.B.a += 0x60;
regs.p.c = 1;
}
if(regs.p.h || (regs.B.a & 15) > 0x09) {
regs.B.a += 0x06;
}
regs.p.n = !!(regs.B.a & 0x80);
regs.p.z = (regs.B.a == 0);
break;
}
case 0xbe: {
op_io(2);
if(!regs.p.c || (regs.B.a) > 0x99) {
regs.B.a -= 0x60;
regs.p.c = 0;
}
if(!regs.p.h || (regs.B.a & 15) > 0x09) {
regs.B.a -= 0x06;
}
regs.p.n = !!(regs.B.a & 0x80);
regs.p.z = (regs.B.a == 0);
break;
}
case 0x60: {
op_io();
regs.p.c = 0;
break;
}
case 0x20: {
op_io();
regs.p.p = 0;
break;
}
case 0x80: {
op_io();
regs.p.c = 1;
break;
}
case 0x40: {
op_io();
regs.p.p = 1;
break;
}
case 0xe0: {
op_io();
regs.p.v = 0;
regs.p.h = 0;
break;
}
case 0xed: {
op_io(2);
regs.p.c = !regs.p.c;
break;
}
case 0xa0: {
op_io(2);
regs.p.i = 1;
break;
}
case 0xc0: {
op_io(2);
regs.p.i = 0;
break;
}
case 0x02: {
dp = op_readpc();
rd = op_readdp(dp);
rd |= 0x01;
op_writedp(dp, rd);
break;
}
case 0x12: {
dp = op_readpc();
rd = op_readdp(dp);
rd &= ~0x01;
op_writedp(dp, rd);
break;
}
case 0x22: {
dp = op_readpc();
rd = op_readdp(dp);
rd |= 0x02;
op_writedp(dp, rd);
break;
}
case 0x32: {
dp = op_readpc();
rd = op_readdp(dp);
rd &= ~0x02;
op_writedp(dp, rd);
break;
}
case 0x42: {
dp = op_readpc();
rd = op_readdp(dp);
rd |= 0x04;
op_writedp(dp, rd);
break;
}
case 0x52: {
dp = op_readpc();
rd = op_readdp(dp);
rd &= ~0x04;
op_writedp(dp, rd);
break;
}
case 0x62: {
dp = op_readpc();
rd = op_readdp(dp);
rd |= 0x08;
op_writedp(dp, rd);
break;
}
case 0x72: {
dp = op_readpc();
rd = op_readdp(dp);
rd &= ~0x08;
op_writedp(dp, rd);
break;
}
case 0x82: {
dp = op_readpc();
rd = op_readdp(dp);
rd |= 0x10;
op_writedp(dp, rd);
break;
}
case 0x92: {
dp = op_readpc();
rd = op_readdp(dp);
rd &= ~0x10;
op_writedp(dp, rd);
break;
}
case 0xa2: {
dp = op_readpc();
rd = op_readdp(dp);
rd |= 0x20;
op_writedp(dp, rd);
break;
}
case 0xb2: {
dp = op_readpc();
rd = op_readdp(dp);
rd &= ~0x20;
op_writedp(dp, rd);
break;
}
case 0xc2: {
dp = op_readpc();
rd = op_readdp(dp);
rd |= 0x40;
op_writedp(dp, rd);
break;
}
case 0xd2: {
dp = op_readpc();
rd = op_readdp(dp);
rd &= ~0x40;
op_writedp(dp, rd);
break;
}
case 0xe2: {
dp = op_readpc();
rd = op_readdp(dp);
rd |= 0x80;
op_writedp(dp, rd);
break;
}
case 0xf2: {
dp = op_readpc();
rd = op_readdp(dp);
rd &= ~0x80;
op_writedp(dp, rd);
break;
}
case 0x2d: {
op_io(2);
op_writestack(regs.B.a);
break;
}
case 0x4d: {
op_io(2);
op_writestack(regs.x);
break;
}
case 0x6d: {
op_io(2);
op_writestack(regs.B.y);
break;
}
case 0x0d: {
op_io(2);
op_writestack(regs.p);
break;
}
case 0xae: {
op_io(2);
regs.B.a = op_readstack();
break;
}
case 0xce: {
op_io(2);
regs.x = op_readstack();
break;
}
case 0xee: {
op_io(2);
regs.B.y = op_readstack();
break;
}
case 0x8e: {
op_io(2);
regs.p = op_readstack();
break;
}
case 0xcf: {
op_io(8);
ya = regs.B.y * regs.B.a;
regs.B.a = ya;
regs.B.y = ya >> 8;
//result is set based on y (high-byte) only
regs.p.n = !!(regs.B.y & 0x80);
regs.p.z = (regs.B.y == 0);
break;
}
case 0x9e: {
op_io(11);
ya = regs.ya;
//overflow set if quotient >= 256
regs.p.v = !!(regs.B.y >= regs.x);
regs.p.h = !!((regs.B.y & 15) >= (regs.x & 15));
if(regs.B.y < (regs.x << 1)) {
//if quotient is <= 511 (will fit into 9-bit result)
regs.B.a = ya / regs.x;
regs.B.y = ya % regs.x;
} else {
//otherwise, the quotient won't fit into regs.p.v + regs.B.a
//this emulates the odd behavior of the S-SMP in this case
regs.B.a = 255 - (ya - (regs.x << 9)) / (256 - regs.x);
regs.B.y = regs.x + (ya - (regs.x << 9)) % (256 - regs.x);
}
//result is set based on a (quotient) only
regs.p.n = !!(regs.B.a & 0x80);
regs.p.z = (regs.B.a == 0);
break;
}

@ -0,0 +1,706 @@
case 0x7d: {
op_io();
regs.B.a = regs.x;
regs.p.n = !!(regs.B.a & 0x80);
regs.p.z = (regs.B.a == 0);
break;
}
case 0xdd: {
op_io();
regs.B.a = regs.B.y;
regs.p.n = !!(regs.B.a & 0x80);
regs.p.z = (regs.B.a == 0);
break;
}
case 0x5d: {
op_io();
regs.x = regs.B.a;
regs.p.n = !!(regs.x & 0x80);
regs.p.z = (regs.x == 0);
break;
}
case 0xfd: {
op_io();
regs.B.y = regs.B.a;
regs.p.n = !!(regs.B.y & 0x80);
regs.p.z = (regs.B.y == 0);
break;
}
case 0x9d: {
op_io();
regs.x = regs.sp;
regs.p.n = !!(regs.x & 0x80);
regs.p.z = (regs.x == 0);
break;
}
case 0xbd: {
op_io();
regs.sp = regs.x;
break;
}
case 0xe8: {
regs.B.a = op_readpc();
regs.p.n = !!(regs.B.a & 0x80);
regs.p.z = (regs.B.a == 0);
break;
}
case 0xcd: {
regs.x = op_readpc();
regs.p.n = !!(regs.x & 0x80);
regs.p.z = (regs.x == 0);
break;
}
case 0x8d: {
regs.B.y = op_readpc();
regs.p.n = !!(regs.B.y & 0x80);
regs.p.z = (regs.B.y == 0);
break;
}
case 0xe6: {
switch(++opcode_cycle) {
case 1:
op_io();
break;
case 2:
regs.B.a = op_readdp(regs.x);
regs.p.n = !!(regs.B.a & 0x80);
regs.p.z = (regs.B.a == 0);
opcode_cycle = 0;
break;
}
break;
}
case 0xbf: {
switch(++opcode_cycle) {
case 1:
op_io();
break;
case 2:
regs.B.a = op_readdp(regs.x++);
op_io();
regs.p.n = !!(regs.B.a & 0x80);
regs.p.z = (regs.B.a == 0);
opcode_cycle = 0;
break;
}
break;
}
case 0xe4: {
switch(++opcode_cycle) {
case 1:
sp = op_readpc();
break;
case 2:
regs.B.a = op_readdp(sp);
regs.p.n = !!(regs.B.a & 0x80);
regs.p.z = (regs.B.a == 0);
opcode_cycle = 0;
break;
}
break;
}
case 0xf8: {
switch(++opcode_cycle) {
case 1:
sp = op_readpc();
break;
case 2:
regs.x = op_readdp(sp);
regs.p.n = !!(regs.x & 0x80);
regs.p.z = (regs.x == 0);
opcode_cycle = 0;
break;
}
break;
}
case 0xeb: {
switch(++opcode_cycle) {
case 1:
sp = op_readpc();
break;
case 2:
regs.B.y = op_readdp(sp);
regs.p.n = !!(regs.B.y & 0x80);
regs.p.z = (regs.B.y == 0);
opcode_cycle = 0;
break;
}
break;
}
case 0xf4: {
switch(++opcode_cycle) {
case 1:
sp = op_readpc();
op_io();
break;
case 2:
regs.B.a = op_readdp(sp + regs.x);
regs.p.n = !!(regs.B.a & 0x80);
regs.p.z = (regs.B.a == 0);
opcode_cycle = 0;
break;
}
break;
}
case 0xf9: {
switch(++opcode_cycle) {
case 1:
sp = op_readpc();
op_io();
break;
case 2:
regs.x = op_readdp(sp + regs.B.y);
regs.p.n = !!(regs.x & 0x80);
regs.p.z = (regs.x == 0);
opcode_cycle = 0;
break;
}
break;
}
case 0xfb: {
switch(++opcode_cycle) {
case 1:
sp = op_readpc();
op_io();
break;
case 2:
regs.B.y = op_readdp(sp + regs.x);
regs.p.n = !!(regs.B.y & 0x80);
regs.p.z = (regs.B.y == 0);
opcode_cycle = 0;
break;
}
break;
}
case 0xe5: {
switch(++opcode_cycle) {
case 1:
sp = op_readpc();
break;
case 2:
sp |= op_readpc() << 8;
break;
case 3:
regs.B.a = op_readaddr(sp);
regs.p.n = !!(regs.B.a & 0x80);
regs.p.z = (regs.B.a == 0);
opcode_cycle = 0;
break;
}
break;
}
case 0xe9: {
switch(++opcode_cycle) {
case 1:
sp = op_readpc();
sp |= op_readpc() << 8;
break;
case 2:
regs.x = op_readaddr(sp);
regs.p.n = !!(regs.x & 0x80);
regs.p.z = (regs.x == 0);
opcode_cycle = 0;
break;
}
break;
}
case 0xec: {
switch(++opcode_cycle) {
case 1:
sp = op_readpc();
sp |= op_readpc() << 8;
break;
case 2:
regs.B.y = op_readaddr(sp);
regs.p.n = !!(regs.B.y & 0x80);
regs.p.z = (regs.B.y == 0);
opcode_cycle = 0;
break;
}
break;
}
case 0xf5: {
switch(++opcode_cycle) {
case 1:
sp = op_readpc();
sp |= op_readpc() << 8;
op_io();
break;
case 2:
regs.B.a = op_readaddr(sp + regs.x);
regs.p.n = !!(regs.B.a & 0x80);
regs.p.z = (regs.B.a == 0);
opcode_cycle = 0;
break;
}
break;
}
case 0xf6: {
switch(++opcode_cycle) {
case 1:
sp = op_readpc();
sp |= op_readpc() << 8;
op_io();
break;
case 2:
regs.B.a = op_readaddr(sp + regs.B.y);
regs.p.n = !!(regs.B.a & 0x80);
regs.p.z = (regs.B.a == 0);
opcode_cycle = 0;
break;
}
break;
}
case 0xe7: {
switch(++opcode_cycle) {
case 1:
dp = op_readpc() + regs.x;
op_io();
break;
case 2:
sp = op_readdp(dp);
break;
case 3:
sp |= op_readdp(dp + 1) << 8;
break;
case 4:
regs.B.a = op_readaddr(sp);
regs.p.n = !!(regs.B.a & 0x80);
regs.p.z = (regs.B.a == 0);
opcode_cycle = 0;
break;
}
break;
}
case 0xf7: {
switch(++opcode_cycle) {
case 1:
dp = op_readpc();
op_io();
break;
case 2:
sp = op_readdp(dp);
break;
case 3:
sp |= op_readdp(dp + 1) << 8;
break;
case 4:
regs.B.a = op_readaddr(sp + regs.B.y);
regs.p.n = !!(regs.B.a & 0x80);
regs.p.z = (regs.B.a == 0);
opcode_cycle = 0;
break;
}
break;
}
case 0xfa: {
switch(++opcode_cycle) {
case 1:
sp = op_readpc();
break;
case 2:
rd = op_readdp(sp);
break;
case 3:
dp = op_readpc();
break;
case 4:
op_writedp(dp, rd);
opcode_cycle = 0;
break;
}
break;
}
case 0x8f: {
switch(++opcode_cycle) {
case 1:
rd = op_readpc();
dp = op_readpc();
break;
case 2:
op_readdp(dp);
break;
case 3:
op_writedp(dp, rd);
opcode_cycle = 0;
break;
}
break;
}
case 0xc6: {
switch(++opcode_cycle) {
case 1:
op_io();
break;
case 2:
op_readdp(regs.x);
break;
case 3:
op_writedp(regs.x, regs.B.a);
opcode_cycle = 0;
break;
}
break;
}
case 0xaf: {
switch(++opcode_cycle) {
case 1:
op_io(2);
break;
case 2:
op_writedp(regs.x++, regs.B.a);
opcode_cycle = 0;
break;
}
break;
}
case 0xc4: {
switch(++opcode_cycle) {
case 1:
dp = op_readpc();
break;
case 2:
op_readdp(dp);
break;
case 3:
op_writedp(dp, regs.B.a);
opcode_cycle = 0;
break;
}
break;
}
case 0xd8: {
switch(++opcode_cycle) {
case 1:
dp = op_readpc();
break;
case 2:
op_readdp(dp);
break;
case 3:
op_writedp(dp, regs.x);
opcode_cycle = 0;
break;
}
break;
}
case 0xcb: {
switch(++opcode_cycle) {
case 1:
dp = op_readpc();
break;
case 2:
op_readdp(dp);
break;
case 3:
op_writedp(dp, regs.B.y);
opcode_cycle = 0;
break;
}
break;
}
case 0xd4: {
switch(++opcode_cycle) {
case 1:
dp = op_readpc();
op_io();
dp += regs.x;
break;
case 2:
op_readdp(dp);
break;
case 3:
op_writedp(dp, regs.B.a);
opcode_cycle = 0;
break;
}
break;
}
case 0xd9: {
switch(++opcode_cycle) {
case 1:
dp = op_readpc();
op_io();
dp += regs.B.y;
break;
case 2:
op_readdp(dp);
break;
case 3:
op_writedp(dp, regs.x);
opcode_cycle = 0;
break;
}
break;
}
case 0xdb: {
switch(++opcode_cycle) {
case 1:
dp = op_readpc();
op_io();
dp += regs.x;
break;
case 2:
op_readdp(dp);
break;
case 3:
op_writedp(dp, regs.B.y);
opcode_cycle = 0;
break;
}
break;
}
case 0xc5: {
switch(++opcode_cycle) {
case 1:
dp = op_readpc();
break;
case 2:
dp |= op_readpc() << 8;
break;
case 3:
op_readaddr(dp);
break;
case 4:
op_writeaddr(dp, regs.B.a);
opcode_cycle = 0;
break;
}
break;
}
case 0xc9: {
switch(++opcode_cycle) {
case 1:
dp = op_readpc();
break;
case 2:
dp |= op_readpc() << 8;
break;
case 3:
op_readaddr(dp);
break;
case 4:
op_writeaddr(dp, regs.x);
opcode_cycle = 0;
break;
}
break;
}
case 0xcc: {
switch(++opcode_cycle) {
case 1:
dp = op_readpc();
break;
case 2:
dp |= op_readpc() << 8;
break;
case 3:
op_readaddr(dp);
break;
case 4:
op_writeaddr(dp, regs.B.y);
opcode_cycle = 0;
break;
}
break;
}
case 0xd5: {
switch(++opcode_cycle) {
case 1:
dp = op_readpc();
dp |= op_readpc() << 8;
op_io();
dp += regs.x;
break;
case 2:
op_readaddr(dp);
break;
case 3:
op_writeaddr(dp, regs.B.a);
opcode_cycle = 0;
break;
}
break;
}
case 0xd6: {
switch(++opcode_cycle) {
case 1:
dp = op_readpc();
dp |= op_readpc() << 8;
op_io();
dp += regs.B.y;
break;
case 2:
op_readaddr(dp);
break;
case 3:
op_writeaddr(dp, regs.B.a);
opcode_cycle = 0;
break;
}
break;
}
case 0xc7: {
switch(++opcode_cycle) {
case 1:
sp = op_readpc();
op_io();
sp += regs.x;
break;
case 2:
dp = op_readdp(sp);
break;
case 3:
dp |= op_readdp(sp + 1) << 8;
break;
case 4:
op_readaddr(dp);
break;
case 5:
op_writeaddr(dp, regs.B.a);
opcode_cycle = 0;
break;
}
break;
}
case 0xd7: {
switch(++opcode_cycle) {
case 1:
sp = op_readpc();
break;
case 2:
dp = op_readdp(sp);
break;
case 3:
dp |= op_readdp(sp + 1) << 8;
op_io();
dp += regs.B.y;
break;
case 4:
op_readaddr(dp);
break;
case 5:
op_writeaddr(dp, regs.B.a);
opcode_cycle = 0;
break;
}
break;
}
case 0xba: {
switch(++opcode_cycle) {
case 1:
sp = op_readpc();
break;
case 2:
regs.B.a = op_readdp(sp);
op_io();
break;
case 3:
regs.B.y = op_readdp(sp + 1);
regs.p.n = !!(regs.ya & 0x8000);
regs.p.z = (regs.ya == 0);
opcode_cycle = 0;
break;
}
break;
}
case 0xda: {
switch(++opcode_cycle) {
case 1:
dp = op_readpc();
break;
case 2:
op_readdp(dp);
break;
case 3:
op_writedp(dp, regs.B.a);
break;
case 4:
op_writedp(dp + 1, regs.B.y);
opcode_cycle = 0;
break;
}
break;
}
case 0xaa: {
switch(++opcode_cycle) {
case 1:
sp = op_readpc();
sp |= op_readpc() << 8;
break;
case 2:
bit = sp >> 13;
sp &= 0x1fff;
rd = op_readaddr(sp);
regs.p.c = !!(rd & (1 << bit));
opcode_cycle = 0;
break;
}
break;
}
case 0xca: {
switch(++opcode_cycle) {
case 1:
dp = op_readpc();
dp |= op_readpc() << 8;
break;
case 2:
bit = dp >> 13;
dp &= 0x1fff;
rd = op_readaddr(dp);
if(regs.p.c)rd |= (1 << bit);
else rd &= ~(1 << bit);
op_io();
break;
case 3:
op_writeaddr(dp, rd);
opcode_cycle = 0;
break;
}
break;
}

@ -0,0 +1,536 @@
case 0x2f: {
rd = op_readpc();
if(0){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0xf0: {
rd = op_readpc();
if(!regs.p.z){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0xd0: {
rd = op_readpc();
if(regs.p.z){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0xb0: {
rd = op_readpc();
if(!regs.p.c){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0x90: {
rd = op_readpc();
if(regs.p.c){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0x70: {
rd = op_readpc();
if(!regs.p.v){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0x50: {
rd = op_readpc();
if(regs.p.v){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0x30: {
rd = op_readpc();
if(!regs.p.n){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0x10: {
rd = op_readpc();
if(regs.p.n){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0x03: {
dp = op_readpc();
sp = op_readdp(dp);
rd = op_readpc();
op_io();
if((sp & 0x01) != 0x01){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0x13: {
dp = op_readpc();
sp = op_readdp(dp);
rd = op_readpc();
op_io();
if((sp & 0x01) == 0x01){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0x23: {
dp = op_readpc();
sp = op_readdp(dp);
rd = op_readpc();
op_io();
if((sp & 0x02) != 0x02){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0x33: {
dp = op_readpc();
sp = op_readdp(dp);
rd = op_readpc();
op_io();
if((sp & 0x02) == 0x02){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0x43: {
dp = op_readpc();
sp = op_readdp(dp);
rd = op_readpc();
op_io();
if((sp & 0x04) != 0x04){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0x53: {
dp = op_readpc();
sp = op_readdp(dp);
rd = op_readpc();
op_io();
if((sp & 0x04) == 0x04){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0x63: {
dp = op_readpc();
sp = op_readdp(dp);
rd = op_readpc();
op_io();
if((sp & 0x08) != 0x08){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0x73: {
dp = op_readpc();
sp = op_readdp(dp);
rd = op_readpc();
op_io();
if((sp & 0x08) == 0x08){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0x83: {
dp = op_readpc();
sp = op_readdp(dp);
rd = op_readpc();
op_io();
if((sp & 0x10) != 0x10){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0x93: {
dp = op_readpc();
sp = op_readdp(dp);
rd = op_readpc();
op_io();
if((sp & 0x10) == 0x10){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0xa3: {
dp = op_readpc();
sp = op_readdp(dp);
rd = op_readpc();
op_io();
if((sp & 0x20) != 0x20){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0xb3: {
dp = op_readpc();
sp = op_readdp(dp);
rd = op_readpc();
op_io();
if((sp & 0x20) == 0x20){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0xc3: {
dp = op_readpc();
sp = op_readdp(dp);
rd = op_readpc();
op_io();
if((sp & 0x40) != 0x40){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0xd3: {
dp = op_readpc();
sp = op_readdp(dp);
rd = op_readpc();
op_io();
if((sp & 0x40) == 0x40){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0xe3: {
dp = op_readpc();
sp = op_readdp(dp);
rd = op_readpc();
op_io();
if((sp & 0x80) != 0x80){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0xf3: {
dp = op_readpc();
sp = op_readdp(dp);
rd = op_readpc();
op_io();
if((sp & 0x80) == 0x80){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0x2e: {
dp = op_readpc();
sp = op_readdp(dp);
rd = op_readpc();
op_io();
if(regs.B.a == sp){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0xde: {
dp = op_readpc();
op_io();
sp = op_readdp(dp + regs.x);
rd = op_readpc();
op_io();
if(regs.B.a == sp){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0x6e: {
dp = op_readpc();
wr = op_readdp(dp);
op_writedp(dp, --wr);
rd = op_readpc();
if(wr == 0x00){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0xfe: {
rd = op_readpc();
op_io();
regs.B.y--;
op_io();
if(regs.B.y == 0x00){ break; }
op_io(2);
regs.pc += (int8)rd;
break;
}
case 0x5f: {
rd = op_readpc();
rd |= op_readpc() << 8;
regs.pc = rd;
break;
}
case 0x1f: {
dp = op_readpc();
dp |= op_readpc() << 8;
op_io();
dp += regs.x;
rd = op_readaddr(dp);
rd |= op_readaddr(dp + 1) << 8;
regs.pc = rd;
break;
}
case 0x3f: {
rd = op_readpc();
rd |= op_readpc() << 8;
op_io(3);
op_writestack(regs.pc >> 8);
op_writestack(regs.pc);
regs.pc = rd;
break;
}
case 0x4f: {
rd = op_readpc();
op_io(2);
op_writestack(regs.pc >> 8);
op_writestack(regs.pc);
regs.pc = 0xff00 | rd;
break;
}
case 0x01: {
dp = 0xffde - (0 << 1);
rd = op_readaddr(dp);
rd |= op_readaddr(dp + 1) << 8;
op_io(3);
op_writestack(regs.pc >> 8);
op_writestack(regs.pc);
regs.pc = rd;
break;
}
case 0x11: {
dp = 0xffde - (1 << 1);
rd = op_readaddr(dp);
rd |= op_readaddr(dp + 1) << 8;
op_io(3);
op_writestack(regs.pc >> 8);
op_writestack(regs.pc);
regs.pc = rd;
break;
}
case 0x21: {
dp = 0xffde - (2 << 1);
rd = op_readaddr(dp);
rd |= op_readaddr(dp + 1) << 8;
op_io(3);
op_writestack(regs.pc >> 8);
op_writestack(regs.pc);
regs.pc = rd;
break;
}
case 0x31: {
dp = 0xffde - (3 << 1);
rd = op_readaddr(dp);
rd |= op_readaddr(dp + 1) << 8;
op_io(3);
op_writestack(regs.pc >> 8);
op_writestack(regs.pc);
regs.pc = rd;
break;
}
case 0x41: {
dp = 0xffde - (4 << 1);
rd = op_readaddr(dp);
rd |= op_readaddr(dp + 1) << 8;
op_io(3);
op_writestack(regs.pc >> 8);
op_writestack(regs.pc);
regs.pc = rd;
break;
}
case 0x51: {
dp = 0xffde - (5 << 1);
rd = op_readaddr(dp);
rd |= op_readaddr(dp + 1) << 8;
op_io(3);
op_writestack(regs.pc >> 8);
op_writestack(regs.pc);
regs.pc = rd;
break;
}
case 0x61: {
dp = 0xffde - (6 << 1);
rd = op_readaddr(dp);
rd |= op_readaddr(dp + 1) << 8;
op_io(3);
op_writestack(regs.pc >> 8);
op_writestack(regs.pc);
regs.pc = rd;
break;
}
case 0x71: {
dp = 0xffde - (7 << 1);
rd = op_readaddr(dp);
rd |= op_readaddr(dp + 1) << 8;
op_io(3);
op_writestack(regs.pc >> 8);
op_writestack(regs.pc);
regs.pc = rd;
break;
}
case 0x81: {
dp = 0xffde - (8 << 1);
rd = op_readaddr(dp);
rd |= op_readaddr(dp + 1) << 8;
op_io(3);
op_writestack(regs.pc >> 8);
op_writestack(regs.pc);
regs.pc = rd;
break;
}
case 0x91: {
dp = 0xffde - (9 << 1);
rd = op_readaddr(dp);
rd |= op_readaddr(dp + 1) << 8;
op_io(3);
op_writestack(regs.pc >> 8);
op_writestack(regs.pc);
regs.pc = rd;
break;
}
case 0xa1: {
dp = 0xffde - (10 << 1);
rd = op_readaddr(dp);
rd |= op_readaddr(dp + 1) << 8;
op_io(3);
op_writestack(regs.pc >> 8);
op_writestack(regs.pc);
regs.pc = rd;
break;
}
case 0xb1: {
dp = 0xffde - (11 << 1);
rd = op_readaddr(dp);
rd |= op_readaddr(dp + 1) << 8;
op_io(3);
op_writestack(regs.pc >> 8);
op_writestack(regs.pc);
regs.pc = rd;
break;
}
case 0xc1: {
dp = 0xffde - (12 << 1);
rd = op_readaddr(dp);
rd |= op_readaddr(dp + 1) << 8;
op_io(3);
op_writestack(regs.pc >> 8);
op_writestack(regs.pc);
regs.pc = rd;
break;
}
case 0xd1: {
dp = 0xffde - (13 << 1);
rd = op_readaddr(dp);
rd |= op_readaddr(dp + 1) << 8;
op_io(3);
op_writestack(regs.pc >> 8);
op_writestack(regs.pc);
regs.pc = rd;
break;
}
case 0xe1: {
dp = 0xffde - (14 << 1);
rd = op_readaddr(dp);
rd |= op_readaddr(dp + 1) << 8;
op_io(3);
op_writestack(regs.pc >> 8);
op_writestack(regs.pc);
regs.pc = rd;
break;
}
case 0xf1: {
dp = 0xffde - (15 << 1);
rd = op_readaddr(dp);
rd |= op_readaddr(dp + 1) << 8;
op_io(3);
op_writestack(regs.pc >> 8);
op_writestack(regs.pc);
regs.pc = rd;
break;
}
case 0x0f: {
rd = op_readaddr(0xffde);
rd |= op_readaddr(0xffdf) << 8;
op_io(2);
op_writestack(regs.pc >> 8);
op_writestack(regs.pc);
op_writestack(regs.p);
regs.pc = rd;
regs.p.b = 1;
regs.p.i = 0;
break;
}
case 0x6f: {
rd = op_readstack();
rd |= op_readstack() << 8;
op_io(2);
regs.pc = rd;
break;
}
case 0x7f: {
regs.p = op_readstack();
rd = op_readstack();
rd |= op_readstack() << 8;
op_io(2);
regs.pc = rd;
break;
}

@ -0,0 +1,751 @@
case 0x88: {
rd = op_readpc();
regs.B.a = op_adc(regs.B.a, rd);
break;
}
case 0x28: {
rd = op_readpc();
regs.B.a = op_and(regs.B.a, rd);
break;
}
case 0x68: {
rd = op_readpc();
regs.B.a = op_cmp(regs.B.a, rd);
break;
}
case 0xc8: {
rd = op_readpc();
regs.x = op_cmp(regs.x, rd);
break;
}
case 0xad: {
rd = op_readpc();
regs.B.y = op_cmp(regs.B.y, rd);
break;
}
case 0x48: {
rd = op_readpc();
regs.B.a = op_eor(regs.B.a, rd);
break;
}
case 0x08: {
rd = op_readpc();
regs.B.a = op_or(regs.B.a, rd);
break;
}
case 0xa8: {
rd = op_readpc();
regs.B.a = op_sbc(regs.B.a, rd);
break;
}
case 0x86: {
op_io();
rd = op_readdp(regs.x);
regs.B.a = op_adc(regs.B.a, rd);
break;
}
case 0x26: {
op_io();
rd = op_readdp(regs.x);
regs.B.a = op_and(regs.B.a, rd);
break;
}
case 0x66: {
op_io();
rd = op_readdp(regs.x);
regs.B.a = op_cmp(regs.B.a, rd);
break;
}
case 0x46: {
op_io();
rd = op_readdp(regs.x);
regs.B.a = op_eor(regs.B.a, rd);
break;
}
case 0x06: {
op_io();
rd = op_readdp(regs.x);
regs.B.a = op_or(regs.B.a, rd);
break;
}
case 0xa6: {
op_io();
rd = op_readdp(regs.x);
regs.B.a = op_sbc(regs.B.a, rd);
break;
}
case 0x84: {
dp = op_readpc();
rd = op_readdp(dp);
regs.B.a = op_adc(regs.B.a, rd);
break;
}
case 0x24: {
dp = op_readpc();
rd = op_readdp(dp);
regs.B.a = op_and(regs.B.a, rd);
break;
}
case 0x64: {
dp = op_readpc();
rd = op_readdp(dp);
regs.B.a = op_cmp(regs.B.a, rd);
break;
}
case 0x3e: {
dp = op_readpc();
rd = op_readdp(dp);
regs.x = op_cmp(regs.x, rd);
break;
}
case 0x7e: {
switch(++opcode_cycle) {
case 1:
dp = op_readpc();
break;
case 2:
rd = op_readdp(dp);
regs.B.y = op_cmp(regs.B.y, rd);
opcode_cycle = 0;
break;
}
break;
}
case 0x44: {
dp = op_readpc();
rd = op_readdp(dp);
regs.B.a = op_eor(regs.B.a, rd);
break;
}
case 0x04: {
dp = op_readpc();
rd = op_readdp(dp);
regs.B.a = op_or(regs.B.a, rd);
break;
}
case 0xa4: {
dp = op_readpc();
rd = op_readdp(dp);
regs.B.a = op_sbc(regs.B.a, rd);
break;
}
case 0x94: {
dp = op_readpc();
op_io();
rd = op_readdp(dp + regs.x);
regs.B.a = op_adc(regs.B.a, rd);
break;
}
case 0x34: {
dp = op_readpc();
op_io();
rd = op_readdp(dp + regs.x);
regs.B.a = op_and(regs.B.a, rd);
break;
}
case 0x74: {
dp = op_readpc();
op_io();
rd = op_readdp(dp + regs.x);
regs.B.a = op_cmp(regs.B.a, rd);
break;
}
case 0x54: {
dp = op_readpc();
op_io();
rd = op_readdp(dp + regs.x);
regs.B.a = op_eor(regs.B.a, rd);
break;
}
case 0x14: {
dp = op_readpc();
op_io();
rd = op_readdp(dp + regs.x);
regs.B.a = op_or(regs.B.a, rd);
break;
}
case 0xb4: {
dp = op_readpc();
op_io();
rd = op_readdp(dp + regs.x);
regs.B.a = op_sbc(regs.B.a, rd);
break;
}
case 0x85: {
dp = op_readpc();
dp |= op_readpc() << 8;
rd = op_readaddr(dp);
regs.B.a = op_adc(regs.B.a, rd);
break;
}
case 0x25: {
dp = op_readpc();
dp |= op_readpc() << 8;
rd = op_readaddr(dp);
regs.B.a = op_and(regs.B.a, rd);
break;
}
case 0x65: {
dp = op_readpc();
dp |= op_readpc() << 8;
rd = op_readaddr(dp);
regs.B.a = op_cmp(regs.B.a, rd);
break;
}
case 0x1e: {
dp = op_readpc();
dp |= op_readpc() << 8;
rd = op_readaddr(dp);
regs.x = op_cmp(regs.x, rd);
break;
}
case 0x5e: {
dp = op_readpc();
dp |= op_readpc() << 8;
rd = op_readaddr(dp);
regs.B.y = op_cmp(regs.B.y, rd);
break;
}
case 0x45: {
dp = op_readpc();
dp |= op_readpc() << 8;
rd = op_readaddr(dp);
regs.B.a = op_eor(regs.B.a, rd);
break;
}
case 0x05: {
dp = op_readpc();
dp |= op_readpc() << 8;
rd = op_readaddr(dp);
regs.B.a = op_or(regs.B.a, rd);
break;
}
case 0xa5: {
dp = op_readpc();
dp |= op_readpc() << 8;
rd = op_readaddr(dp);
regs.B.a = op_sbc(regs.B.a, rd);
break;
}
case 0x95: {
dp = op_readpc();
dp |= op_readpc() << 8;
op_io();
rd = op_readaddr(dp + regs.x);
regs.B.a = op_adc(regs.B.a, rd);
break;
}
case 0x96: {
dp = op_readpc();
dp |= op_readpc() << 8;
op_io();
rd = op_readaddr(dp + regs.B.y);
regs.B.a = op_adc(regs.B.a, rd);
break;
}
case 0x35: {
dp = op_readpc();
dp |= op_readpc() << 8;
op_io();
rd = op_readaddr(dp + regs.x);
regs.B.a = op_and(regs.B.a, rd);
break;
}
case 0x36: {
dp = op_readpc();
dp |= op_readpc() << 8;
op_io();
rd = op_readaddr(dp + regs.B.y);
regs.B.a = op_and(regs.B.a, rd);
break;
}
case 0x75: {
dp = op_readpc();
dp |= op_readpc() << 8;
op_io();
rd = op_readaddr(dp + regs.x);
regs.B.a = op_cmp(regs.B.a, rd);
break;
}
case 0x76: {
dp = op_readpc();
dp |= op_readpc() << 8;
op_io();
rd = op_readaddr(dp + regs.B.y);
regs.B.a = op_cmp(regs.B.a, rd);
break;
}
case 0x55: {
dp = op_readpc();
dp |= op_readpc() << 8;
op_io();
rd = op_readaddr(dp + regs.x);
regs.B.a = op_eor(regs.B.a, rd);
break;
}
case 0x56: {
dp = op_readpc();
dp |= op_readpc() << 8;
op_io();
rd = op_readaddr(dp + regs.B.y);
regs.B.a = op_eor(regs.B.a, rd);
break;
}
case 0x15: {
dp = op_readpc();
dp |= op_readpc() << 8;
op_io();
rd = op_readaddr(dp + regs.x);
regs.B.a = op_or(regs.B.a, rd);
break;
}
case 0x16: {
dp = op_readpc();
dp |= op_readpc() << 8;
op_io();
rd = op_readaddr(dp + regs.B.y);
regs.B.a = op_or(regs.B.a, rd);
break;
}
case 0xb5: {
dp = op_readpc();
dp |= op_readpc() << 8;
op_io();
rd = op_readaddr(dp + regs.x);
regs.B.a = op_sbc(regs.B.a, rd);
break;
}
case 0xb6: {
dp = op_readpc();
dp |= op_readpc() << 8;
op_io();
rd = op_readaddr(dp + regs.B.y);
regs.B.a = op_sbc(regs.B.a, rd);
break;
}
case 0x87: {
dp = op_readpc() + regs.x;
op_io();
sp = op_readdp(dp);
sp |= op_readdp(dp + 1) << 8;
rd = op_readaddr(sp);
regs.B.a = op_adc(regs.B.a, rd);
break;
}
case 0x27: {
dp = op_readpc() + regs.x;
op_io();
sp = op_readdp(dp);
sp |= op_readdp(dp + 1) << 8;
rd = op_readaddr(sp);
regs.B.a = op_and(regs.B.a, rd);
break;
}
case 0x67: {
dp = op_readpc() + regs.x;
op_io();
sp = op_readdp(dp);
sp |= op_readdp(dp + 1) << 8;
rd = op_readaddr(sp);
regs.B.a = op_cmp(regs.B.a, rd);
break;
}
case 0x47: {
dp = op_readpc() + regs.x;
op_io();
sp = op_readdp(dp);
sp |= op_readdp(dp + 1) << 8;
rd = op_readaddr(sp);
regs.B.a = op_eor(regs.B.a, rd);
break;
}
case 0x07: {
dp = op_readpc() + regs.x;
op_io();
sp = op_readdp(dp);
sp |= op_readdp(dp + 1) << 8;
rd = op_readaddr(sp);
regs.B.a = op_or(regs.B.a, rd);
break;
}
case 0xa7: {
dp = op_readpc() + regs.x;
op_io();
sp = op_readdp(dp);
sp |= op_readdp(dp + 1) << 8;
rd = op_readaddr(sp);
regs.B.a = op_sbc(regs.B.a, rd);
break;
}
case 0x97: {
dp = op_readpc();
op_io();
sp = op_readdp(dp);
sp |= op_readdp(dp + 1) << 8;
rd = op_readaddr(sp + regs.B.y);
regs.B.a = op_adc(regs.B.a, rd);
break;
}
case 0x37: {
dp = op_readpc();
op_io();
sp = op_readdp(dp);
sp |= op_readdp(dp + 1) << 8;
rd = op_readaddr(sp + regs.B.y);
regs.B.a = op_and(regs.B.a, rd);
break;
}
case 0x77: {
dp = op_readpc();
op_io();
sp = op_readdp(dp);
sp |= op_readdp(dp + 1) << 8;
rd = op_readaddr(sp + regs.B.y);
regs.B.a = op_cmp(regs.B.a, rd);
break;
}
case 0x57: {
dp = op_readpc();
op_io();
sp = op_readdp(dp);
sp |= op_readdp(dp + 1) << 8;
rd = op_readaddr(sp + regs.B.y);
regs.B.a = op_eor(regs.B.a, rd);
break;
}
case 0x17: {
dp = op_readpc();
op_io();
sp = op_readdp(dp);
sp |= op_readdp(dp + 1) << 8;
rd = op_readaddr(sp + regs.B.y);
regs.B.a = op_or(regs.B.a, rd);
break;
}
case 0xb7: {
dp = op_readpc();
op_io();
sp = op_readdp(dp);
sp |= op_readdp(dp + 1) << 8;
rd = op_readaddr(sp + regs.B.y);
regs.B.a = op_sbc(regs.B.a, rd);
break;
}
case 0x99: {
op_io();
rd = op_readdp(regs.B.y);
wr = op_readdp(regs.x);
wr = op_adc(wr, rd);
(1) ? op_writedp(regs.x, wr) : op_io();
break;
}
case 0x39: {
op_io();
rd = op_readdp(regs.B.y);
wr = op_readdp(regs.x);
wr = op_and(wr, rd);
(1) ? op_writedp(regs.x, wr) : op_io();
break;
}
case 0x79: {
op_io();
rd = op_readdp(regs.B.y);
wr = op_readdp(regs.x);
wr = op_cmp(wr, rd);
(0) ? op_writedp(regs.x, wr) : op_io();
break;
}
case 0x59: {
op_io();
rd = op_readdp(regs.B.y);
wr = op_readdp(regs.x);
wr = op_eor(wr, rd);
(1) ? op_writedp(regs.x, wr) : op_io();
break;
}
case 0x19: {
op_io();
rd = op_readdp(regs.B.y);
wr = op_readdp(regs.x);
wr = op_or(wr, rd);
(1) ? op_writedp(regs.x, wr) : op_io();
break;
}
case 0xb9: {
op_io();
rd = op_readdp(regs.B.y);
wr = op_readdp(regs.x);
wr = op_sbc(wr, rd);
(1) ? op_writedp(regs.x, wr) : op_io();
break;
}
case 0x89: {
sp = op_readpc();
rd = op_readdp(sp);
dp = op_readpc();
wr = op_readdp(dp);
wr = op_adc(wr, rd);
(1) ? op_writedp(dp, wr) : op_io();
break;
}
case 0x29: {
sp = op_readpc();
rd = op_readdp(sp);
dp = op_readpc();
wr = op_readdp(dp);
wr = op_and(wr, rd);
(1) ? op_writedp(dp, wr) : op_io();
break;
}
case 0x69: {
sp = op_readpc();
rd = op_readdp(sp);
dp = op_readpc();
wr = op_readdp(dp);
wr = op_cmp(wr, rd);
(0) ? op_writedp(dp, wr) : op_io();
break;
}
case 0x49: {
sp = op_readpc();
rd = op_readdp(sp);
dp = op_readpc();
wr = op_readdp(dp);
wr = op_eor(wr, rd);
(1) ? op_writedp(dp, wr) : op_io();
break;
}
case 0x09: {
sp = op_readpc();
rd = op_readdp(sp);
dp = op_readpc();
wr = op_readdp(dp);
wr = op_or(wr, rd);
(1) ? op_writedp(dp, wr) : op_io();
break;
}
case 0xa9: {
sp = op_readpc();
rd = op_readdp(sp);
dp = op_readpc();
wr = op_readdp(dp);
wr = op_sbc(wr, rd);
(1) ? op_writedp(dp, wr) : op_io();
break;
}
case 0x98: {
rd = op_readpc();
dp = op_readpc();
wr = op_readdp(dp);
wr = op_adc(wr, rd);
(1) ? op_writedp(dp, wr) : op_io();
break;
}
case 0x38: {
rd = op_readpc();
dp = op_readpc();
wr = op_readdp(dp);
wr = op_and(wr, rd);
(1) ? op_writedp(dp, wr) : op_io();
break;
}
case 0x78: {
rd = op_readpc();
dp = op_readpc();
wr = op_readdp(dp);
wr = op_cmp(wr, rd);
(0) ? op_writedp(dp, wr) : op_io();
break;
}
case 0x58: {
rd = op_readpc();
dp = op_readpc();
wr = op_readdp(dp);
wr = op_eor(wr, rd);
(1) ? op_writedp(dp, wr) : op_io();
break;
}
case 0x18: {
rd = op_readpc();
dp = op_readpc();
wr = op_readdp(dp);
wr = op_or(wr, rd);
(1) ? op_writedp(dp, wr) : op_io();
break;
}
case 0xb8: {
rd = op_readpc();
dp = op_readpc();
wr = op_readdp(dp);
wr = op_sbc(wr, rd);
(1) ? op_writedp(dp, wr) : op_io();
break;
}
case 0x7a: {
dp = op_readpc();
rd = op_readdp(dp);
op_io();
rd |= op_readdp(dp + 1) << 8;
regs.ya = op_addw(regs.ya, rd);
break;
}
case 0x9a: {
dp = op_readpc();
rd = op_readdp(dp);
op_io();
rd |= op_readdp(dp + 1) << 8;
regs.ya = op_subw(regs.ya, rd);
break;
}
case 0x5a: {
dp = op_readpc();
rd = op_readdp(dp);
rd |= op_readdp(dp + 1) << 8;
op_cmpw(regs.ya, rd);
break;
}
case 0x4a: {
dp = op_readpc();
dp |= op_readpc() << 8;
bit = dp >> 13;
dp &= 0x1fff;
rd = op_readaddr(dp);
regs.p.c = regs.p.c & !!(rd & (1 << bit));
break;
}
case 0x6a: {
dp = op_readpc();
dp |= op_readpc() << 8;
bit = dp >> 13;
dp &= 0x1fff;
rd = op_readaddr(dp);
regs.p.c = regs.p.c & !(rd & (1 << bit));
break;
}
case 0x8a: {
dp = op_readpc();
dp |= op_readpc() << 8;
bit = dp >> 13;
dp &= 0x1fff;
rd = op_readaddr(dp);
op_io();
regs.p.c = regs.p.c ^ !!(rd & (1 << bit));
break;
}
case 0xea: {
dp = op_readpc();
dp |= op_readpc() << 8;
bit = dp >> 13;
dp &= 0x1fff;
rd = op_readaddr(dp);
rd ^= (1 << bit);
op_writeaddr(dp, rd);
break;
}
case 0x0a: {
dp = op_readpc();
dp |= op_readpc() << 8;
bit = dp >> 13;
dp &= 0x1fff;
rd = op_readaddr(dp);
op_io();
regs.p.c = regs.p.c | !!(rd & (1 << bit));
break;
}
case 0x2a: {
dp = op_readpc();
dp |= op_readpc() << 8;
bit = dp >> 13;
dp &= 0x1fff;
rd = op_readaddr(dp);
op_io();
regs.p.c = regs.p.c | !(rd & (1 << bit));
break;
}

@ -0,0 +1,262 @@
case 0xbc: {
op_io();
regs.B.a = op_inc(regs.B.a);
break;
}
case 0x3d: {
op_io();
regs.x = op_inc(regs.x);
break;
}
case 0xfc: {
op_io();
regs.B.y = op_inc(regs.B.y);
break;
}
case 0x9c: {
op_io();
regs.B.a = op_dec(regs.B.a);
break;
}
case 0x1d: {
op_io();
regs.x = op_dec(regs.x);
break;
}
case 0xdc: {
op_io();
regs.B.y = op_dec(regs.B.y);
break;
}
case 0x1c: {
op_io();
regs.B.a = op_asl(regs.B.a);
break;
}
case 0x5c: {
op_io();
regs.B.a = op_lsr(regs.B.a);
break;
}
case 0x3c: {
op_io();
regs.B.a = op_rol(regs.B.a);
break;
}
case 0x7c: {
op_io();
regs.B.a = op_ror(regs.B.a);
break;
}
case 0xab: {
dp = op_readpc();
rd = op_readdp(dp);
rd = op_inc(rd);
op_writedp(dp, rd);
break;
}
case 0x8b: {
dp = op_readpc();
rd = op_readdp(dp);
rd = op_dec(rd);
op_writedp(dp, rd);
break;
}
case 0x0b: {
dp = op_readpc();
rd = op_readdp(dp);
rd = op_asl(rd);
op_writedp(dp, rd);
break;
}
case 0x4b: {
dp = op_readpc();
rd = op_readdp(dp);
rd = op_lsr(rd);
op_writedp(dp, rd);
break;
}
case 0x2b: {
dp = op_readpc();
rd = op_readdp(dp);
rd = op_rol(rd);
op_writedp(dp, rd);
break;
}
case 0x6b: {
dp = op_readpc();
rd = op_readdp(dp);
rd = op_ror(rd);
op_writedp(dp, rd);
break;
}
case 0xbb: {
dp = op_readpc();
op_io();
rd = op_readdp(dp + regs.x);
rd = op_inc(rd);
op_writedp(dp + regs.x, rd);
break;
}
case 0x9b: {
dp = op_readpc();
op_io();
rd = op_readdp(dp + regs.x);
rd = op_dec(rd);
op_writedp(dp + regs.x, rd);
break;
}
case 0x1b: {
dp = op_readpc();
op_io();
rd = op_readdp(dp + regs.x);
rd = op_asl(rd);
op_writedp(dp + regs.x, rd);
break;
}
case 0x5b: {
dp = op_readpc();
op_io();
rd = op_readdp(dp + regs.x);
rd = op_lsr(rd);
op_writedp(dp + regs.x, rd);
break;
}
case 0x3b: {
dp = op_readpc();
op_io();
rd = op_readdp(dp + regs.x);
rd = op_rol(rd);
op_writedp(dp + regs.x, rd);
break;
}
case 0x7b: {
dp = op_readpc();
op_io();
rd = op_readdp(dp + regs.x);
rd = op_ror(rd);
op_writedp(dp + regs.x, rd);
break;
}
case 0xac: {
dp = op_readpc();
dp |= op_readpc() << 8;
rd = op_readaddr(dp);
rd = op_inc(rd);
op_writeaddr(dp, rd);
break;
}
case 0x8c: {
dp = op_readpc();
dp |= op_readpc() << 8;
rd = op_readaddr(dp);
rd = op_dec(rd);
op_writeaddr(dp, rd);
break;
}
case 0x0c: {
dp = op_readpc();
dp |= op_readpc() << 8;
rd = op_readaddr(dp);
rd = op_asl(rd);
op_writeaddr(dp, rd);
break;
}
case 0x4c: {
dp = op_readpc();
dp |= op_readpc() << 8;
rd = op_readaddr(dp);
rd = op_lsr(rd);
op_writeaddr(dp, rd);
break;
}
case 0x2c: {
dp = op_readpc();
dp |= op_readpc() << 8;
rd = op_readaddr(dp);
rd = op_rol(rd);
op_writeaddr(dp, rd);
break;
}
case 0x6c: {
dp = op_readpc();
dp |= op_readpc() << 8;
rd = op_readaddr(dp);
rd = op_ror(rd);
op_writeaddr(dp, rd);
break;
}
case 0x0e: {
dp = op_readpc();
dp |= op_readpc() << 8;
rd = op_readaddr(dp);
regs.p.n = !!((regs.B.a - rd) & 0x80);
regs.p.z = ((regs.B.a - rd) == 0);
op_readaddr(dp);
op_writeaddr(dp, rd | regs.B.a);
break;
}
case 0x4e: {
dp = op_readpc();
dp |= op_readpc() << 8;
rd = op_readaddr(dp);
regs.p.n = !!((regs.B.a - rd) & 0x80);
regs.p.z = ((regs.B.a - rd) == 0);
op_readaddr(dp);
op_writeaddr(dp, rd &~ regs.B.a);
break;
}
case 0x3a: {
dp = op_readpc();
rd = op_readdp(dp);
rd++;
op_writedp(dp++, rd);
rd += op_readdp(dp) << 8;
op_writedp(dp, rd >> 8);
regs.p.n = !!(rd & 0x8000);
regs.p.z = (rd == 0);
break;
}
case 0x1a: {
dp = op_readpc();
rd = op_readdp(dp);
rd--;
op_writedp(dp++, rd);
rd += op_readdp(dp) << 8;
op_writedp(dp, rd >> 8);
regs.p.n = !!(rd & 0x8000);
regs.p.z = (rd == 0);
break;
}

@ -0,0 +1,75 @@
#ifdef SMP_CPP
void SMPDebugger::op_step() {
bool break_event = false;
usage[regs.pc] |= UsageExec;
opcode_pc = regs.pc;
opcode_edge = true;
debugger.breakpoint_test(Debugger::Breakpoint::Source::APURAM, Debugger::Breakpoint::Mode::Exec, regs.pc, 0x00);
if(step_event && step_event() == true) {
debugger.break_event = Debugger::BreakEvent::SMPStep;
scheduler.exit(Scheduler::ExitReason::DebuggerEvent);
}
opcode_edge = false;
SMP::op_step();
synchronize_cpu();
}
uint8 SMPDebugger::op_read(uint16 addr) {
uint8 data = SMP::op_read(addr);
usage[addr] |= UsageRead;
debugger.breakpoint_test(Debugger::Breakpoint::Source::APURAM, Debugger::Breakpoint::Mode::Read, addr, data);
return data;
}
void SMPDebugger::op_write(uint16 addr, uint8 data) {
SMP::op_write(addr, data);
usage[addr] |= UsageWrite;
usage[addr] &= ~UsageExec;
debugger.breakpoint_test(Debugger::Breakpoint::Source::APURAM, Debugger::Breakpoint::Mode::Write, addr, data);
}
SMPDebugger::SMPDebugger() {
usage = new uint8[1 << 16]();
opcode_pc = 0xffc0;
opcode_edge = false;
}
SMPDebugger::~SMPDebugger() {
delete[] usage;
}
bool SMPDebugger::property(unsigned id, string &name, string &value) {
unsigned n = 0;
#define item(name_, value_) \
if(id == n++) { \
name = name_; \
value = value_; \
return true; \
}
//$00f0
item("$00f0", "");
item("Clock Speed", (unsigned)status.clock_speed);
item("Timers Enable", status.timers_enable);
item("RAM Disable", status.ram_disable);
item("RAM Writable", status.ram_writable);
item("Timers Disable", status.timers_disable);
//$00f1
item("$00f1", "");
item("IPLROM Enable", status.iplrom_enable);
//$00f2
item("$00f2", "");
item("DSP Address", string("0x", hex<2>(status.dsp_addr)));
#undef item
return false;
}
#endif

@ -0,0 +1,27 @@
class SMPDebugger : public SMP, public ChipDebugger {
public:
bool property(unsigned id, string &name, string &value);
function<bool ()> step_event;
enum Usage {
UsageRead = 0x80,
UsageWrite = 0x40,
UsageExec = 0x20,
};
uint8 *usage;
uint16 opcode_pc;
bool opcode_edge;
void op_step();
uint8 op_read(uint16 addr);
void op_write(uint16 addr, uint8 data);
SMPDebugger();
~SMPDebugger();
//disassembler
void disassemble_opcode(char *output, uint16 addr);
inline uint8 disassemble_read(uint16 addr);
inline uint16 relb(int8 offset, int op_len);
};

@ -0,0 +1,309 @@
uint8 SMP::disassemble_read(uint16 addr) {
if(addr >= 0xffc0) return smp.iplrom[addr & 0x3f];
return smp.apuram[addr];
}
uint16 SMP::relb(int8 offset, int op_len) {
uint16 pc = regs.pc + op_len;
return pc + offset;
}
void SMP::disassemble_opcode(char *output, uint16 addr) {
char *s, t[512];
uint8 op, op0, op1;
uint16 opw, opdp0, opdp1;
s = output;
sprintf(s, "..%.4x ", addr);
op = disassemble_read(addr + 0);
op0 = disassemble_read(addr + 1);
op1 = disassemble_read(addr + 2);
opw = (op0) | (op1 << 8);
opdp0 = ((unsigned)regs.p.p << 8) + op0;
opdp1 = ((unsigned)regs.p.p << 8) + op1;
strcpy(t, " ");
switch(op) {
case 0x00: sprintf(t, "nop"); break;
case 0x01: sprintf(t, "tcall 0"); break;
case 0x02: sprintf(t, "set0 $%.3x", opdp0); break;
case 0x03: sprintf(t, "bbs0 $%.3x,$%.4x", opdp0, relb(op1, 3)); break;
case 0x04: sprintf(t, "or a,$%.3x", opdp0); break;
case 0x05: sprintf(t, "or a,$%.4x", opw); break;
case 0x06: sprintf(t, "or a,(x)"); break;
case 0x07: sprintf(t, "or a,($%.3x+x)", opdp0); break;
case 0x08: sprintf(t, "or a,#$%.2x", op0); break;
case 0x09: sprintf(t, "or $%.3x,$%.3x", opdp1, opdp0); break;
case 0x0a: sprintf(t, "or1 c,$%.4x:%d", opw & 0x1fff, opw >> 13); break;
case 0x0b: sprintf(t, "asl $%.3x", opdp0); break;
case 0x0c: sprintf(t, "asl $%.4x", opw); break;
case 0x0d: sprintf(t, "push p"); break;
case 0x0e: sprintf(t, "tset $%.4x,a", opw); break;
case 0x0f: sprintf(t, "brk"); break;
case 0x10: sprintf(t, "bpl $%.4x", relb(op0, 2)); break;
case 0x11: sprintf(t, "tcall 1"); break;
case 0x12: sprintf(t, "clr0 $%.3x", opdp0); break;
case 0x13: sprintf(t, "bbc0 $%.3x,$%.4x", opdp0, relb(op1, 3)); break;
case 0x14: sprintf(t, "or a,$%.3x+x", opdp0); break;
case 0x15: sprintf(t, "or a,$%.4x+x", opw); break;
case 0x16: sprintf(t, "or a,$%.4x+y", opw); break;
case 0x17: sprintf(t, "or a,($%.3x)+y", opdp0); break;
case 0x18: sprintf(t, "or $%.3x,#$%.2x", opdp1, op0); break;
case 0x19: sprintf(t, "or (x),(y)"); break;
case 0x1a: sprintf(t, "decw $%.3x", opdp0); break;
case 0x1b: sprintf(t, "asl $%.3x+x", opdp0); break;
case 0x1c: sprintf(t, "asl a"); break;
case 0x1d: sprintf(t, "dec x"); break;
case 0x1e: sprintf(t, "cmp x,$%.4x", opw); break;
case 0x1f: sprintf(t, "jmp ($%.4x+x)", opw); break;
case 0x20: sprintf(t, "clrp"); break;
case 0x21: sprintf(t, "tcall 2"); break;
case 0x22: sprintf(t, "set1 $%.3x", opdp0); break;
case 0x23: sprintf(t, "bbs1 $%.3x,$%.4x", opdp0, relb(op1, 3)); break;
case 0x24: sprintf(t, "and a,$%.3x", opdp0); break;
case 0x25: sprintf(t, "and a,$%.4x", opw); break;
case 0x26: sprintf(t, "and a,(x)"); break;
case 0x27: sprintf(t, "and a,($%.3x+x)", opdp0); break;
case 0x28: sprintf(t, "and a,#$%.2x", op0); break;
case 0x29: sprintf(t, "and $%.3x,$%.3x", opdp1, opdp0); break;
case 0x2a: sprintf(t, "or1 c,!$%.4x:%d", opw & 0x1fff, opw >> 13); break;
case 0x2b: sprintf(t, "rol $%.3x", opdp0); break;
case 0x2c: sprintf(t, "rol $%.4x", opw); break;
case 0x2d: sprintf(t, "push a"); break;
case 0x2e: sprintf(t, "cbne $%.3x,$%.4x", opdp0, relb(op1, 3)); break;
case 0x2f: sprintf(t, "bra $%.4x", relb(op0, 2)); break;
case 0x30: sprintf(t, "bmi $%.4x", relb(op0, 2)); break;
case 0x31: sprintf(t, "tcall 3"); break;
case 0x32: sprintf(t, "clr1 $%.3x", opdp0); break;
case 0x33: sprintf(t, "bbc1 $%.3x,$%.4x", opdp0, relb(op1, 3)); break;
case 0x34: sprintf(t, "and a,$%.3x+x", opdp0); break;
case 0x35: sprintf(t, "and a,$%.4x+x", opw); break;
case 0x36: sprintf(t, "and a,$%.4x+y", opw); break;
case 0x37: sprintf(t, "and a,($%.3x)+y", opdp0); break;
case 0x38: sprintf(t, "and $%.3x,#$%.2x", opdp1, op0); break;
case 0x39: sprintf(t, "and (x),(y)"); break;
case 0x3a: sprintf(t, "incw $%.3x", opdp0); break;
case 0x3b: sprintf(t, "rol $%.3x+x", opdp0); break;
case 0x3c: sprintf(t, "rol a"); break;
case 0x3d: sprintf(t, "inc x"); break;
case 0x3e: sprintf(t, "cmp x,$%.3x", opdp0); break;
case 0x3f: sprintf(t, "call $%.4x", opw); break;
case 0x40: sprintf(t, "setp"); break;
case 0x41: sprintf(t, "tcall 4"); break;
case 0x42: sprintf(t, "set2 $%.3x", opdp0); break;
case 0x43: sprintf(t, "bbs2 $%.3x,$%.4x", opdp0, relb(op1, 3)); break;
case 0x44: sprintf(t, "eor a,$%.3x", opdp0); break;
case 0x45: sprintf(t, "eor a,$%.4x", opw); break;
case 0x46: sprintf(t, "eor a,(x)"); break;
case 0x47: sprintf(t, "eor a,($%.3x+x)", opdp0); break;
case 0x48: sprintf(t, "eor a,#$%.2x", op0); break;
case 0x49: sprintf(t, "eor $%.3x,$%.3x", opdp1, opdp0); break;
case 0x4a: sprintf(t, "and1 c,$%.4x:%d", opw & 0x1fff, opw >> 13); break;
case 0x4b: sprintf(t, "lsr $%.3x", opdp0); break;
case 0x4c: sprintf(t, "lsr $%.4x", opw); break;
case 0x4d: sprintf(t, "push x"); break;
case 0x4e: sprintf(t, "tclr $%.4x,a", opw); break;
case 0x4f: sprintf(t, "pcall $ff%.2x", op0); break;
case 0x50: sprintf(t, "bvc $%.4x", relb(op0, 2)); break;
case 0x51: sprintf(t, "tcall 5"); break;
case 0x52: sprintf(t, "clr2 $%.3x", opdp0); break;
case 0x53: sprintf(t, "bbc2 $%.3x,$%.4x", opdp0, relb(op1, 3)); break;
case 0x54: sprintf(t, "eor a,$%.3x+x", opdp0); break;
case 0x55: sprintf(t, "eor a,$%.4x+x", opw); break;
case 0x56: sprintf(t, "eor a,$%.4x+y", opw); break;
case 0x57: sprintf(t, "eor a,($%.3x)+y", opdp0); break;
case 0x58: sprintf(t, "eor $%.3x,#$%.2x", opdp1, op0); break;
case 0x59: sprintf(t, "eor (x),(y)"); break;
case 0x5a: sprintf(t, "cmpw ya,$%.3x", opdp0); break;
case 0x5b: sprintf(t, "lsr $%.3x+x", opdp0); break;
case 0x5c: sprintf(t, "lsr a"); break;
case 0x5d: sprintf(t, "mov x,a"); break;
case 0x5e: sprintf(t, "cmp y,$%.4x", opw); break;
case 0x5f: sprintf(t, "jmp $%.4x", opw); break;
case 0x60: sprintf(t, "clrc"); break;
case 0x61: sprintf(t, "tcall 6"); break;
case 0x62: sprintf(t, "set3 $%.3x", opdp0); break;
case 0x63: sprintf(t, "bbs3 $%.3x,$%.4x", opdp0, relb(op1, 3)); break;
case 0x64: sprintf(t, "cmp a,$%.3x", opdp0); break;
case 0x65: sprintf(t, "cmp a,$%.4x", opw); break;
case 0x66: sprintf(t, "cmp a,(x)"); break;
case 0x67: sprintf(t, "cmp a,($%.3x+x)", opdp0); break;
case 0x68: sprintf(t, "cmp a,#$%.2x", op0); break;
case 0x69: sprintf(t, "cmp $%.3x,$%.3x", opdp1, opdp0); break;
case 0x6a: sprintf(t, "and1 c,!$%.4x:%d", opw & 0x1fff, opw >> 13); break;
case 0x6b: sprintf(t, "ror $%.3x", opdp0); break;
case 0x6c: sprintf(t, "ror $%.4x", opw); break;
case 0x6d: sprintf(t, "push y"); break;
case 0x6e: sprintf(t, "dbnz $%.3x,$%.4x", opdp0, relb(op1, 3)); break;
case 0x6f: sprintf(t, "ret"); break;
case 0x70: sprintf(t, "bvs $%.4x", relb(op0, 2)); break;
case 0x71: sprintf(t, "tcall 7"); break;
case 0x72: sprintf(t, "clr3 $%.3x", opdp0); break;
case 0x73: sprintf(t, "bbc3 $%.3x,$%.4x", opdp0, relb(op1, 3)); break;
case 0x74: sprintf(t, "cmp a,$%.3x+x", opdp0); break;
case 0x75: sprintf(t, "cmp a,$%.4x+x", opw); break;
case 0x76: sprintf(t, "cmp a,$%.4x+y", opw); break;
case 0x77: sprintf(t, "cmp a,($%.3x)+y", opdp0); break;
case 0x78: sprintf(t, "cmp $%.3x,#$%.2x", opdp1, op0); break;
case 0x79: sprintf(t, "cmp (x),(y)"); break;
case 0x7a: sprintf(t, "addw ya,$%.3x", opdp0); break;
case 0x7b: sprintf(t, "ror $%.3x+x", opdp0); break;
case 0x7c: sprintf(t, "ror a"); break;
case 0x7d: sprintf(t, "mov a,x"); break;
case 0x7e: sprintf(t, "cmp y,$%.3x", opdp0); break;
case 0x7f: sprintf(t, "reti"); break;
case 0x80: sprintf(t, "setc"); break;
case 0x81: sprintf(t, "tcall 8"); break;
case 0x82: sprintf(t, "set4 $%.3x", opdp0); break;
case 0x83: sprintf(t, "bbs4 $%.3x,$%.4x", opdp0, relb(op1, 3)); break;
case 0x84: sprintf(t, "adc a,$%.3x", opdp0); break;
case 0x85: sprintf(t, "adc a,$%.4x", opw); break;
case 0x86: sprintf(t, "adc a,(x)"); break;
case 0x87: sprintf(t, "adc a,($%.3x+x)", opdp0); break;
case 0x88: sprintf(t, "adc a,#$%.2x", op0); break;
case 0x89: sprintf(t, "adc $%.3x,$%.3x", opdp1, opdp0); break;
case 0x8a: sprintf(t, "eor1 c,$%.4x:%d", opw & 0x1fff, opw >> 13); break;
case 0x8b: sprintf(t, "dec $%.3x", opdp0); break;
case 0x8c: sprintf(t, "dec $%.4x", opw); break;
case 0x8d: sprintf(t, "mov y,#$%.2x", op0); break;
case 0x8e: sprintf(t, "pop p"); break;
case 0x8f: sprintf(t, "mov $%.3x,#$%.2x", opdp1, op0); break;
case 0x90: sprintf(t, "bcc $%.4x", relb(op0, 2)); break;
case 0x91: sprintf(t, "tcall 9"); break;
case 0x92: sprintf(t, "clr4 $%.3x", opdp0); break;
case 0x93: sprintf(t, "bbc4 $%.3x,$%.4x", opdp0, relb(op1, 3)); break;
case 0x94: sprintf(t, "adc a,$%.3x+x", opdp0); break;
case 0x95: sprintf(t, "adc a,$%.4x+x", opw); break;
case 0x96: sprintf(t, "adc a,$%.4x+y", opw); break;
case 0x97: sprintf(t, "adc a,($%.3x)+y", opdp0); break;
case 0x98: sprintf(t, "adc $%.3x,#$%.2x", opdp1, op0); break;
case 0x99: sprintf(t, "adc (x),(y)"); break;
case 0x9a: sprintf(t, "subw ya,$%.3x", opdp0); break;
case 0x9b: sprintf(t, "dec $%.3x+x", opdp0); break;
case 0x9c: sprintf(t, "dec a"); break;
case 0x9d: sprintf(t, "mov x,sp"); break;
case 0x9e: sprintf(t, "div ya,x"); break;
case 0x9f: sprintf(t, "xcn a"); break;
case 0xa0: sprintf(t, "ei"); break;
case 0xa1: sprintf(t, "tcall 10"); break;
case 0xa2: sprintf(t, "set5 $%.3x", opdp0); break;
case 0xa3: sprintf(t, "bbs5 $%.3x,$%.4x", opdp0, relb(op1, 3)); break;
case 0xa4: sprintf(t, "sbc a,$%.3x", opdp0); break;
case 0xa5: sprintf(t, "sbc a,$%.4x", opw); break;
case 0xa6: sprintf(t, "sbc a,(x)"); break;
case 0xa7: sprintf(t, "sbc a,($%.3x+x)", opdp0); break;
case 0xa8: sprintf(t, "sbc a,#$%.2x", op0); break;
case 0xa9: sprintf(t, "sbc $%.3x,$%.3x", opdp1, opdp0); break;
case 0xaa: sprintf(t, "mov1 c,$%.4x:%d", opw & 0x1fff, opw >> 13); break;
case 0xab: sprintf(t, "inc $%.3x", opdp0); break;
case 0xac: sprintf(t, "inc $%.4x", opw); break;
case 0xad: sprintf(t, "cmp y,#$%.2x", op0); break;
case 0xae: sprintf(t, "pop a"); break;
case 0xaf: sprintf(t, "mov (x)+,a"); break;
case 0xb0: sprintf(t, "bcs $%.4x", relb(op0, 2)); break;
case 0xb1: sprintf(t, "tcall 11"); break;
case 0xb2: sprintf(t, "clr5 $%.3x", opdp0); break;
case 0xb3: sprintf(t, "bbc5 $%.3x,$%.4x", opdp0, relb(op1, 3)); break;
case 0xb4: sprintf(t, "sbc a,$%.3x+x", opdp0); break;
case 0xb5: sprintf(t, "sbc a,$%.4x+x", opw); break;
case 0xb6: sprintf(t, "sbc a,$%.4x+y", opw); break;
case 0xb7: sprintf(t, "sbc a,($%.3x)+y", opdp0); break;
case 0xb8: sprintf(t, "sbc $%.3x,#$%.2x", opdp1, op0); break;
case 0xb9: sprintf(t, "sbc (x),(y)"); break;
case 0xba: sprintf(t, "movw ya,$%.3x", opdp0); break;
case 0xbb: sprintf(t, "inc $%.3x+x", opdp0); break;
case 0xbc: sprintf(t, "inc a"); break;
case 0xbd: sprintf(t, "mov sp,x"); break;
case 0xbe: sprintf(t, "das a"); break;
case 0xbf: sprintf(t, "mov a,(x)+"); break;
case 0xc0: sprintf(t, "di"); break;
case 0xc1: sprintf(t, "tcall 12"); break;
case 0xc2: sprintf(t, "set6 $%.3x", opdp0); break;
case 0xc3: sprintf(t, "bbs6 $%.3x,$%.4x", opdp0, relb(op1, 3)); break;
case 0xc4: sprintf(t, "mov $%.3x,a", opdp0); break;
case 0xc5: sprintf(t, "mov $%.4x,a", opw); break;
case 0xc6: sprintf(t, "mov (x),a"); break;
case 0xc7: sprintf(t, "mov ($%.3x+x),a", opdp0); break;
case 0xc8: sprintf(t, "cmp x,#$%.2x", op0); break;
case 0xc9: sprintf(t, "mov $%.4x,x", opw); break;
case 0xca: sprintf(t, "mov1 $%.4x:%d,c", opw & 0x1fff, opw >> 13); break;
case 0xcb: sprintf(t, "mov $%.3x,y", opdp0); break;
case 0xcc: sprintf(t, "mov $%.4x,y", opw); break;
case 0xcd: sprintf(t, "mov x,#$%.2x", op0); break;
case 0xce: sprintf(t, "pop x"); break;
case 0xcf: sprintf(t, "mul ya"); break;
case 0xd0: sprintf(t, "bne $%.4x", relb(op0, 2)); break;
case 0xd1: sprintf(t, "tcall 13"); break;
case 0xd2: sprintf(t, "clr6 $%.3x", opdp0); break;
case 0xd3: sprintf(t, "bbc6 $%.3x,$%.4x", opdp0, relb(op1, 3)); break;
case 0xd4: sprintf(t, "mov $%.3x+x,a", opdp0); break;
case 0xd5: sprintf(t, "mov $%.4x+x,a", opw); break;
case 0xd6: sprintf(t, "mov $%.4x+y,a", opw); break;
case 0xd7: sprintf(t, "mov ($%.3x)+y,a", opdp0); break;
case 0xd8: sprintf(t, "mov $%.3x,x", opdp0); break;
case 0xd9: sprintf(t, "mov $%.3x+y,x", opdp0); break;
case 0xda: sprintf(t, "movw $%.3x,ya", opdp0); break;
case 0xdb: sprintf(t, "mov $%.3x+x,y", opdp0); break;
case 0xdc: sprintf(t, "dec y"); break;
case 0xdd: sprintf(t, "mov a,y"); break;
case 0xde: sprintf(t, "cbne $%.3x+x,$%.4x", opdp0, relb(op1, 3)); break;
case 0xdf: sprintf(t, "daa a"); break;
case 0xe0: sprintf(t, "clrv"); break;
case 0xe1: sprintf(t, "tcall 14"); break;
case 0xe2: sprintf(t, "set7 $%.3x", opdp0); break;
case 0xe3: sprintf(t, "bbs7 $%.3x,$%.4x", opdp0, relb(op1, 3)); break;
case 0xe4: sprintf(t, "mov a,$%.3x", opdp0); break;
case 0xe5: sprintf(t, "mov a,$%.4x", opw); break;
case 0xe6: sprintf(t, "mov a,(x)"); break;
case 0xe7: sprintf(t, "mov a,($%.3x+x)", opdp0); break;
case 0xe8: sprintf(t, "mov a,#$%.2x", op0); break;
case 0xe9: sprintf(t, "mov x,$%.4x", opw); break;
case 0xea: sprintf(t, "not1 c,$%.4x:%d", opw & 0x1fff, opw >> 13); break;
case 0xeb: sprintf(t, "mov y,$%.3x", opdp0); break;
case 0xec: sprintf(t, "mov y,$%.4x", opw); break;
case 0xed: sprintf(t, "notc"); break;
case 0xee: sprintf(t, "pop y"); break;
case 0xef: sprintf(t, "sleep"); break;
case 0xf0: sprintf(t, "beq $%.4x", relb(op0, 2)); break;
case 0xf1: sprintf(t, "tcall 15"); break;
case 0xf2: sprintf(t, "clr7 $%.3x", opdp0); break;
case 0xf3: sprintf(t, "bbc7 $%.3x,$%.4x", opdp0, relb(op1, 3)); break;
case 0xf4: sprintf(t, "mov a,$%.3x+x", opdp0); break;
case 0xf5: sprintf(t, "mov a,$%.4x+x", opw); break;
case 0xf6: sprintf(t, "mov a,$%.4x+y", opw); break;
case 0xf7: sprintf(t, "mov a,($%.3x)+y", opdp0); break;
case 0xf8: sprintf(t, "mov x,$%.3x", opdp0); break;
case 0xf9: sprintf(t, "mov x,$%.3x+y", opdp0); break;
case 0xfa: sprintf(t, "mov $%.3x,$%.3x", opdp1, opdp0); break;
case 0xfb: sprintf(t, "mov y,$%.3x+x", opdp0); break;
case 0xfc: sprintf(t, "inc y"); break;
case 0xfd: sprintf(t, "mov y,a"); break;
case 0xfe: sprintf(t, "dbnz y,$%.4x", relb(op0, 2)); break;
case 0xff: sprintf(t, "stop"); break;
}
t[strlen(t)] = ' ';
strcat(s, t);
sprintf(t, "A:%.2x X:%.2x Y:%.2x SP:01%.2x YA:%.4x ",
regs.B.a, regs.x, regs.B.y, regs.sp, (uint16)regs.ya);
strcat(s, t);
sprintf(t, "%c%c%c%c%c%c%c%c",
regs.p.n ? 'N' : 'n',
regs.p.v ? 'V' : 'v',
regs.p.p ? 'P' : 'p',
regs.p.b ? 'B' : 'b',
regs.p.h ? 'H' : 'h',
regs.p.i ? 'I' : 'i',
regs.p.z ? 'Z' : 'z',
regs.p.c ? 'C' : 'c');
strcat(s, t);
sprintf(t, " %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x",
apuram[0xf4], apuram[0xf5], apuram[0xf6], apuram[0xf7],
cpu.port_read(0), cpu.port_read(1), cpu.port_read(2), cpu.port_read(3));
strcat(s, t);
}

@ -0,0 +1,44 @@
#ifdef SMP_CPP
//this is the IPLROM for the S-SMP coprocessor.
//the S-SMP does not allow writing to the IPLROM.
//all writes are instead mapped to the extended
//RAM region, accessible when $f1.d7 is clear.
const uint8 SMP::iplrom[64] = {
/*ffc0*/ 0xcd, 0xef, //mov x,#$ef
/*ffc2*/ 0xbd, //mov sp,x
/*ffc3*/ 0xe8, 0x00, //mov a,#$00
/*ffc5*/ 0xc6, //mov (x),a
/*ffc6*/ 0x1d, //dec x
/*ffc7*/ 0xd0, 0xfc, //bne $ffc5
/*ffc9*/ 0x8f, 0xaa, 0xf4, //mov $f4,#$aa
/*ffcc*/ 0x8f, 0xbb, 0xf5, //mov $f5,#$bb
/*ffcf*/ 0x78, 0xcc, 0xf4, //cmp $f4,#$cc
/*ffd2*/ 0xd0, 0xfb, //bne $ffcf
/*ffd4*/ 0x2f, 0x19, //bra $ffef
/*ffd6*/ 0xeb, 0xf4, //mov y,$f4
/*ffd8*/ 0xd0, 0xfc, //bne $ffd6
/*ffda*/ 0x7e, 0xf4, //cmp y,$f4
/*ffdc*/ 0xd0, 0x0b, //bne $ffe9
/*ffde*/ 0xe4, 0xf5, //mov a,$f5
/*ffe0*/ 0xcb, 0xf4, //mov $f4,y
/*ffe2*/ 0xd7, 0x00, //mov ($00)+y,a
/*ffe4*/ 0xfc, //inc y
/*ffe5*/ 0xd0, 0xf3, //bne $ffda
/*ffe7*/ 0xab, 0x01, //inc $01
/*ffe9*/ 0x10, 0xef, //bpl $ffda
/*ffeb*/ 0x7e, 0xf4, //cmp y,$f4
/*ffed*/ 0x10, 0xeb, //bpl $ffda
/*ffef*/ 0xba, 0xf6, //movw ya,$f6
/*fff1*/ 0xda, 0x00, //movw $00,ya
/*fff3*/ 0xba, 0xf4, //movw ya,$f4
/*fff5*/ 0xc4, 0xf4, //mov $f4,a
/*fff7*/ 0xdd, //mov a,y
/*fff8*/ 0x5d, //mov x,a
/*fff9*/ 0xd0, 0xdb, //bne $ffd6
/*fffb*/ 0x1f, 0x00, 0x00, //jmp ($0000+x)
/*fffe*/ 0xc0, 0xff //reset vector location ($ffc0)
};
#endif

@ -0,0 +1,126 @@
unsigned SMP::port_read(unsigned addr) {
return apuram[0xf4 + (addr & 3)];
}
void SMP::port_write(unsigned addr, unsigned data) {
apuram[0xf4 + (addr & 3)] = data;
}
unsigned SMP::mmio_read(unsigned addr) {
switch(addr) {
case 0xf2:
return status.dsp_addr;
case 0xf3:
return dsp.read(status.dsp_addr & 0x7f);
case 0xf4:
case 0xf5:
case 0xf6:
case 0xf7:
return cpu.port_read(addr);
case 0xf8:
return status.ram00f8;
case 0xf9:
return status.ram00f9;
case 0xfd: {
unsigned result = timer0.stage3_ticks & 15;
timer0.stage3_ticks = 0;
return result;
}
case 0xfe: {
unsigned result = timer1.stage3_ticks & 15;
timer1.stage3_ticks = 0;
return result;
}
case 0xff: {
unsigned result = timer2.stage3_ticks & 15;
timer2.stage3_ticks = 0;
return result;
}
}
return 0x00;
}
void SMP::mmio_write(unsigned addr, unsigned data) {
switch(addr) {
case 0xf1:
status.iplrom_enable = data & 0x80;
if(data & 0x30) {
if(data & 0x20) {
cpu.port_write(3, 0x00);
cpu.port_write(2, 0x00);
}
if(data & 0x10) {
cpu.port_write(1, 0x00);
cpu.port_write(0, 0x00);
}
}
if(timer2.enable == false && (data & 0x04)) {
timer2.stage2_ticks = 0;
timer2.stage3_ticks = 0;
}
timer2.enable = data & 0x04;
if(timer1.enable == false && (data & 0x02)) {
timer1.stage2_ticks = 0;
timer1.stage3_ticks = 0;
}
timer1.enable = data & 0x02;
if(timer0.enable == false && (data & 0x01)) {
timer0.stage2_ticks = 0;
timer0.stage3_ticks = 0;
}
timer0.enable = data & 0x01;
break;
case 0xf2:
status.dsp_addr = data;
break;
case 0xf3:
if(status.dsp_addr & 0x80) break;
dsp.write(status.dsp_addr, data);
break;
case 0xf4:
case 0xf5:
case 0xf6:
case 0xf7:
port_write(addr, data);
break;
case 0xf8:
status.ram00f8 = data;
break;
case 0xf9:
status.ram00f9 = data;
break;
case 0xfa:
timer0.target = data;
break;
case 0xfb:
timer1.target = data;
break;
case 0xfc:
timer2.target = data;
break;
}
}

@ -0,0 +1,76 @@
#ifdef DEBUGGER
#include "../../../snes9x.h"
#include "../../../debug.h"
char tmp[1024];
#endif
#include "../snes/snes.hpp"
#define SMP_CPP
namespace SNES {
#ifdef DEBUGGER
#include "debugger/disassembler.cpp"
#endif
SMP smp;
#include "algorithms.cpp"
#include "core.cpp"
#include "iplrom.cpp"
#include "memory.cpp"
#include "timing.cpp"
void SMP::enter() {
while(clock < 0) op_step();
}
void SMP::power() {
Processor::clock = 0;
timer0.target = 0;
timer1.target = 0;
timer2.target = 0;
reset();
}
void SMP::reset() {
for(unsigned n = 0x0000; n <= 0xffff; n++) apuram[n] = 0x00;
opcode_number = 0;
opcode_cycle = 0;
regs.pc = 0xffc0;
regs.sp = 0xef;
regs.B.a = 0x00;
regs.x = 0x00;
regs.B.y = 0x00;
regs.p = 0x02;
//$00f1
status.iplrom_enable = true;
//$00f2
status.dsp_addr = 0x00;
//$00f8,$00f9
status.ram00f8 = 0x00;
status.ram00f9 = 0x00;
//timers
timer0.enable = timer1.enable = timer2.enable = false;
timer0.stage1_ticks = timer1.stage1_ticks = timer2.stage1_ticks = 0;
timer0.stage2_ticks = timer1.stage2_ticks = timer2.stage2_ticks = 0;
timer0.stage3_ticks = timer1.stage3_ticks = timer2.stage3_ticks = 0;
}
SMP::SMP() {
apuram = new uint8[64 * 1024];
}
SMP::~SMP() {
delete[] apuram;
}
}

@ -0,0 +1,126 @@
class SMP : public Processor {
public:
static const uint8 iplrom[64];
uint8 *apuram;
unsigned port_read(unsigned port);
void port_write(unsigned port, unsigned data);
unsigned mmio_read(unsigned addr);
void mmio_write(unsigned addr, unsigned data);
void enter();
void power();
void reset();
void load_state(uint8 **);
void save_state(uint8 **);
void save_spc (uint8 *);
SMP();
~SMP();
//private:
struct Flags {
bool n, v, p, b, h, i, z, c;
alwaysinline operator unsigned() const {
return (n << 7) | (v << 6) | (p << 5) | (b << 4)
| (h << 3) | (i << 2) | (z << 1) | (c << 0);
};
alwaysinline unsigned operator=(unsigned data) {
n = data & 0x80; v = data & 0x40; p = data & 0x20; b = data & 0x10;
h = data & 0x08; i = data & 0x04; z = data & 0x02; c = data & 0x01;
return data;
}
alwaysinline unsigned operator|=(unsigned data) { return operator=(operator unsigned() | data); }
alwaysinline unsigned operator^=(unsigned data) { return operator=(operator unsigned() ^ data); }
alwaysinline unsigned operator&=(unsigned data) { return operator=(operator unsigned() & data); }
};
unsigned opcode_number;
unsigned opcode_cycle;
uint16 rd, wr, dp, sp, ya, bit;
struct Regs {
uint16 pc;
uint8 sp;
union {
uint16 ya;
#ifndef __BIG_ENDIAN__
struct { uint8 a, y; } B;
#else
struct { uint8 y, a; } B;
#endif
};
uint8 x;
Flags p;
} regs;
struct Status {
//$00f1
bool iplrom_enable;
//$00f2
unsigned dsp_addr;
//$00f8,$00f9
unsigned ram00f8;
unsigned ram00f9;
} status;
template<unsigned frequency>
struct Timer {
bool enable;
uint8 target;
uint8 stage1_ticks;
uint8 stage2_ticks;
uint8 stage3_ticks;
inline void tick();
inline void tick(unsigned clocks);
};
Timer<128> timer0;
Timer<128> timer1;
Timer< 16> timer2;
inline void tick();
inline void tick(unsigned clocks);
alwaysinline void op_io();
alwaysinline void op_io(unsigned clocks);
debugvirtual alwaysinline uint8 op_read(uint16 addr);
debugvirtual alwaysinline void op_write(uint16 addr, uint8 data);
debugvirtual alwaysinline void op_step();
alwaysinline void op_writestack(uint8 data);
alwaysinline uint8 op_readstack();
static const unsigned cycle_count_table[256];
uint64 cycle_table_cpu[256];
unsigned cycle_table_dsp[256];
uint64 cycle_step_cpu;
inline uint8 op_adc (uint8 x, uint8 y);
inline uint16 op_addw(uint16 x, uint16 y);
inline uint8 op_and (uint8 x, uint8 y);
inline uint8 op_cmp (uint8 x, uint8 y);
inline uint16 op_cmpw(uint16 x, uint16 y);
inline uint8 op_eor (uint8 x, uint8 y);
inline uint8 op_inc (uint8 x);
inline uint8 op_dec (uint8 x);
inline uint8 op_or (uint8 x, uint8 y);
inline uint8 op_sbc (uint8 x, uint8 y);
inline uint16 op_subw(uint16 x, uint16 y);
inline uint8 op_asl (uint8 x);
inline uint8 op_lsr (uint8 x);
inline uint8 op_rol (uint8 x);
inline uint8 op_ror (uint8 x);
#ifdef DEBUGGER
void disassemble_opcode(char *output, uint16 addr);
inline uint8 disassemble_read(uint16 addr);
inline uint16 relb(int8 offset, int op_len);
#endif
};
extern SMP smp;

@ -0,0 +1,197 @@
#include "../snes/snes.hpp"
#include <stdio.h>
typedef struct spc_file {
uint8 header[33];
uint8 idtag[3];
uint8 version_minor;
uint8 pc_low;
uint8 pc_high;
uint8 a;
uint8 x;
uint8 y;
uint8 psw;
uint8 sp;
uint8 unused_a[2];
uint8 id666[210];
uint8 apuram[65536];
uint8 dsp_registers[128];
uint8 unused_b[64];
uint8 iplrom[64];
} spc_file;
namespace SNES {
#include "../dsp/blargg_endian.h"
void SMP::save_spc (uint8 *block) {
spc_file out;
const char *header = "SNES-SPC700 Sound File Data v0.30";
memcpy (out.header, header, 33);
out.idtag[0] = out.idtag[1] = 26;
out.idtag[2] = 27;
out.version_minor = 30;
out.pc_low = regs.pc & 0xff;
out.pc_high = (regs.pc >> 8) & 0xff;
out.a = regs.B.a;
out.x = regs.x;
out.y = regs.B.y;
out.psw = (uint8) ((unsigned) regs.p);
out.sp = regs.sp;
out.unused_a[0] = out.unused_a[1] = 0;
memset (out.id666, 0, 210);
memcpy (out.apuram, apuram, 65536);
for (int i = 0xf2; i <= 0xf9; i++)
{
out.apuram[i] = mmio_read (i);
}
for (int i = 0xfd; i <= 0xff; i++)
{
out.apuram[i] = mmio_read (i);
}
for (int i = 0; i < 128; i++)
{
out.dsp_registers[i] = dsp.read (i);
}
memset (out.unused_b, 0, 64);
memcpy (out.iplrom, iplrom, 64);
memcpy (block, &out, 66048);
}
void SMP::save_state(uint8 **block) {
uint8 *ptr = *block;
memcpy(ptr, apuram, 64 * 1024);
ptr += 64 * 1024;
#undef INT32
#define INT32(i) set_le32(ptr, (i)); ptr += sizeof(int32)
INT32(clock);
INT32(opcode_number);
INT32(opcode_cycle);
INT32(regs.pc);
INT32(regs.sp);
INT32(regs.B.a);
INT32(regs.x);
INT32(regs.B.y);
INT32(regs.p.n);
INT32(regs.p.v);
INT32(regs.p.p);
INT32(regs.p.b);
INT32(regs.p.h);
INT32(regs.p.i);
INT32(regs.p.z);
INT32(regs.p.c);
INT32(status.iplrom_enable);
INT32(status.dsp_addr);
INT32(status.ram00f8);
INT32(status.ram00f9);
INT32(timer0.enable);
INT32(timer0.target);
INT32(timer0.stage1_ticks);
INT32(timer0.stage2_ticks);
INT32(timer0.stage3_ticks);
INT32(timer1.enable);
INT32(timer1.target);
INT32(timer1.stage1_ticks);
INT32(timer1.stage2_ticks);
INT32(timer1.stage3_ticks);
INT32(timer2.enable);
INT32(timer2.target);
INT32(timer2.stage1_ticks);
INT32(timer2.stage2_ticks);
INT32(timer2.stage3_ticks);
INT32(rd);
INT32(wr);
INT32(dp);
INT32(sp);
INT32(ya);
INT32(bit);
*block = ptr;
}
void SMP::load_state(uint8 **block) {
uint8 *ptr = *block;
memcpy(apuram, ptr, 64 * 1024);
ptr += 64 * 1024;
#undef INT32
#define INT32(i) i = get_le32(ptr); ptr += sizeof(int32)
INT32(clock);
INT32(opcode_number);
INT32(opcode_cycle);
INT32(regs.pc);
INT32(regs.sp);
INT32(regs.B.a);
INT32(regs.x);
INT32(regs.B.y);
INT32(regs.p.n);
INT32(regs.p.v);
INT32(regs.p.p);
INT32(regs.p.b);
INT32(regs.p.h);
INT32(regs.p.i);
INT32(regs.p.z);
INT32(regs.p.c);
INT32(status.iplrom_enable);
INT32(status.dsp_addr);
INT32(status.ram00f8);
INT32(status.ram00f9);
INT32(timer0.enable);
INT32(timer0.target);
INT32(timer0.stage1_ticks);
INT32(timer0.stage2_ticks);
INT32(timer0.stage3_ticks);
INT32(timer1.enable);
INT32(timer1.target);
INT32(timer1.stage1_ticks);
INT32(timer1.stage2_ticks);
INT32(timer1.stage3_ticks);
INT32(timer2.enable);
INT32(timer2.target);
INT32(timer2.stage1_ticks);
INT32(timer2.stage2_ticks);
INT32(timer2.stage3_ticks);
INT32(rd);
INT32(wr);
INT32(dp);
INT32(sp);
INT32(ya);
INT32(bit);
*block = ptr;
}
} // namespace SNES

@ -0,0 +1,26 @@
template<unsigned cycle_frequency>
void SMP::Timer<cycle_frequency>::tick() {
if(++stage1_ticks < cycle_frequency) return;
stage1_ticks = 0;
if(enable == false) return;
if(++stage2_ticks != target) return;
stage2_ticks = 0;
stage3_ticks = (stage3_ticks + 1) & 15;
}
template<unsigned cycle_frequency>
void SMP::Timer<cycle_frequency>::tick(unsigned clocks) {
stage1_ticks += clocks;
if(stage1_ticks < cycle_frequency) return;
stage1_ticks -= cycle_frequency;
if(enable == false) return;
if(++stage2_ticks != target) return;
stage2_ticks = 0;
stage3_ticks = (stage3_ticks + 1) & 15;
}

@ -0,0 +1,47 @@
#ifndef __SNES_HPP
#define __SNES_HPP
#include "../../../snes9x.h"
#include "../../resampler.h"
#include "../../../msu1.h"
#define debugvirtual
namespace SNES
{
struct Processor
{
unsigned frequency;
int32 clock;
};
#include "../smp/smp.hpp"
#include "../dsp/sdsp.hpp"
class CPU
{
public:
uint8 registers[4];
inline void reset ()
{
registers[0] = registers[1] = registers[2] = registers[3] = 0;
}
alwaysinline void port_write (uint8 port, uint8 data)
{
registers[port & 3] = data;
}
alwaysinline uint8 port_read (uint8 port)
{
return registers[port & 3];
}
};
extern CPU cpu;
} // namespace SNES
#endif

@ -0,0 +1,229 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef __NEW_RESAMPLER_H
#define __NEW_RESAMPLER_H
#include <cstring>
#include <cassert>
#if __cplusplus >= 201103L
#include <cstdint>
#else
#include <stdint.h>
#endif
#include <cmath>
class Resampler
{
public:
int size;
int buffer_size;
int start;
int16_t *buffer;
float r_step;
float r_frac;
int r_left[4], r_right[4];
static inline int16_t short_clamp(int n)
{
return (int16_t)(((int16_t)n != n) ? (n >> 31) ^ 0x7fff : n);
}
static inline int min(int a, int b)
{
return ((a) < (b) ? (a) : (b));
}
static inline float hermite(float mu1, float a, float b, float c, float d)
{
float mu2, mu3, m0, m1, a0, a1, a2, a3;
mu2 = mu1 * mu1;
mu3 = mu2 * mu1;
m0 = (c - a) * 0.5;
m1 = (d - b) * 0.5;
a0 = +2 * mu3 - 3 * mu2 + 1;
a1 = mu3 - 2 * mu2 + mu1;
a2 = mu3 - mu2;
a3 = -2 * mu3 + 3 * mu2;
return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
}
Resampler()
{
this->buffer_size = 0;
buffer = NULL;
r_step = 1.0;
}
Resampler(int num_samples)
{
this->buffer_size = num_samples;
buffer = new int16_t[this->buffer_size];
r_step = 1.0;
clear();
}
~Resampler()
{
delete[] buffer;
buffer = NULL;
}
inline void time_ratio(double ratio)
{
r_step = ratio;
}
inline void clear(void)
{
if (!buffer)
return;
start = 0;
size = 0;
memset(buffer, 0, buffer_size * 2);
r_frac = 0.0;
r_left[0] = r_left[1] = r_left[2] = r_left[3] = 0;
r_right[0] = r_right[1] = r_right[2] = r_right[3] = 0;
}
inline bool pull(int16_t *dst, int num_samples)
{
if (space_filled() < num_samples)
return false;
memcpy(dst, buffer + start, min(num_samples, buffer_size - start) * 2);
if (num_samples > (buffer_size - start))
memcpy(dst + (buffer_size - start), buffer, (num_samples - (buffer_size - start)) * 2);
start = (start + num_samples) % buffer_size;
size -= num_samples;
return true;
}
inline void push_sample(int16_t l, int16_t r)
{
if (space_empty() >= 2)
{
int end = start + size;
if (end >= buffer_size)
end -= buffer_size;
buffer[end] = l;
buffer[end + 1] = r;
size += 2;
}
}
inline bool push(int16_t *src, int num_samples)
{
if (space_empty() < num_samples)
return false;
int end = start + size;
if (end > buffer_size)
end -= buffer_size;
int first_write_size = min(num_samples, buffer_size - end);
memcpy(buffer + end, src, first_write_size * 2);
if (num_samples > first_write_size)
memcpy(buffer, src + first_write_size, (num_samples - first_write_size) * 2);
size += num_samples;
return true;
}
void read(int16_t *data, int num_samples)
{
//If we are outputting the exact same ratio as the input, pull directly from the input buffer
if (r_step == 1.0)
{
pull(data, num_samples);
return;
}
assert((num_samples & 1) == 0); // resampler always processes both stereo samples
int o_position = 0;
while (o_position < num_samples && size > 0)
{
int s_left = buffer[start];
int s_right = buffer[start + 1];
int hermite_val[2];
while (r_frac <= 1.0 && o_position < num_samples)
{
hermite_val[0] = (int)hermite(r_frac, (float)r_left[0], (float)r_left[1], (float)r_left[2], (float)r_left[3]);
hermite_val[1] = (int)hermite(r_frac, (float)r_right[0], (float)r_right[1], (float)r_right[2], (float)r_right[3]);
data[o_position] = short_clamp(hermite_val[0]);
data[o_position + 1] = short_clamp(hermite_val[1]);
o_position += 2;
r_frac += r_step;
}
if (r_frac > 1.0)
{
r_left[0] = r_left[1];
r_left[1] = r_left[2];
r_left[2] = r_left[3];
r_left[3] = s_left;
r_right[0] = r_right[1];
r_right[1] = r_right[2];
r_right[2] = r_right[3];
r_right[3] = s_right;
r_frac -= 1.0;
start += 2;
if (start >= buffer_size)
start -= buffer_size;
size -= 2;
}
}
}
inline int space_empty(void) const
{
return buffer_size - size;
}
inline int space_filled(void) const
{
return size;
}
inline int avail(void)
{
//If we are outputting the exact same ratio as the input, find out directly from the input buffer
if (r_step == 1.0)
return size;
return (int)trunc(((size >> 1) - r_frac) / r_step) * 2;
}
void resize(int num_samples)
{
if (buffer)
delete[] buffer;
buffer_size = num_samples;
buffer = new int16_t[buffer_size];
clear();
}
};
#endif /* __NEW_RESAMPLER_H */

@ -0,0 +1,291 @@
#include <vector>
#include <iostream>
#include <fstream>
#include <stack>
#include <stdio.h>
#include "port.h"
#include "bml.h"
bml_node::bml_node()
{
type = CHILD;
depth = -1;
}
static inline int islf(char c)
{
return (c == '\r' || c == '\n');
}
static inline int isblank(char c)
{
return (c == ' ' || c == '\t');
}
static inline int isblankorlf(char c)
{
return (islf(c) || isblank(c));
}
static inline int isalnum(char c)
{
return ((c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9'));
}
static inline int bml_valid(char c)
{
return (isalnum(c) || c == '-');
}
static std::string trim(std::string str)
{
int start;
int end;
for (start = 0; str[start] && start != (int)str.length() && isblank(str[start]); start++) {}
if (start >= (int)str.length())
return std::string("");
for (end = str.length() - 1; isblankorlf(str[end]); end--) {}
return str.substr(start, end - start + 1);
}
static std::string trimcomments(std::string str)
{
int end = str.length();
size_t comment = str.find("//");
if (comment != std::string::npos)
end = comment;
for (int i = end - 1; i >= 0; i--)
{
if (!isblankorlf(str[i]))
{
end = i + 1;
break;
}
}
return str.substr(0, end);
}
static inline int bml_read_depth(std::string &data)
{
size_t depth;
for (depth = 0; isblank(data[depth]) && depth < data.length(); depth++) {}
return depth == data.length() ? -1 : depth;
}
static void bml_parse_depth(bml_node &node, std::string &line)
{
unsigned int depth = bml_read_depth(line);
line.erase(0, depth);
node.depth = depth;
}
static void bml_parse_name(bml_node &node, std::string &line)
{
int len;
for (len = 0; bml_valid(line[len]); len++) {};
node.name = trim(line.substr(0, len));
line.erase(0, len);
}
static void bml_parse_data(bml_node &node, std::string &line)
{
int len;
if (line[0] == '=' && line[1] == '\"')
{
len = 2;
while (line[len] && line[len] != '\"' && !islf(line[len]))
len++;
if (line[len] != '\"')
return;
node.data = line.substr(2, len - 2);
line.erase(0, len + 1);
}
else if (line[0] == '=')
{
len = 1;
while (line[len] && !islf(line[len]) && line[len] != '"' && line[len] != ' ')
len++;
if (line[len] == '\"')
return;
node.data = line.substr(1, len - 1);
line.erase(0, len);
}
else if (line[0] == ':')
{
len = 1;
while (line[len] && !islf(line[len]))
len++;
node.data = trim(line.substr(1, len - 1));
line.erase(0, len);
}
return;
}
static std::string bml_read_line(std::ifstream &fd)
{
std::string line;
while (fd)
{
std::getline(fd, line);
line = trimcomments(line);
if (!line.empty())
{
return line;
}
}
return std::string("");
}
static void bml_parse_attr(bml_node &node, std::string &line)
{
int len;
while (line.length() > 0)
{
if (!isblank(line[0]))
return;
while (isblank(line[0]))
line.erase(0, 1);
bml_node n;
len = 0;
while (bml_valid(line[len]))
len++;
if (len == 0)
return;
n.name = trim(line.substr(0, len));
line.erase(0, len);
bml_parse_data(n, line);
n.depth = node.depth + 1;
n.type = bml_node::ATTRIBUTE;
node.child.push_back(n);
}
}
static int contains_space(const char *str)
{
for (int i = 0; str[i]; i++)
{
if (isblank(str[i]))
return 1;
}
return 0;
}
static void bml_print_node(bml_node &node, int depth)
{
int i;
for (i = 0; i < depth * 2; i++)
{
printf(" ");
}
if (!node.name.empty())
printf("%s", node.name.c_str());
if (!node.data.empty())
{
if (contains_space(node.data.c_str()))
printf("=\"%s\"", node.data.c_str());
else
printf(": %s", node.data.c_str());
}
for (i = 0; i < (int)node.child.size() && node.child[i].type == bml_node::ATTRIBUTE; i++)
{
if (!node.child[i].name.empty())
{
printf(" %s", node.child[i].name.c_str());
if (!node.child[i].data.empty())
{
if (contains_space(node.child[i].data.c_str()))
printf("=\"%s\"", node.child[i].data.c_str());
else
printf("=%s", node.child[i].data.c_str());
}
}
}
if (depth >= 0)
printf("\n");
for (; i < (int)node.child.size(); i++)
{
bml_print_node(node.child[i], depth + 1);
}
if (depth == 0)
printf("\n");
}
void bml_node::print()
{
bml_print_node(*this, -1);
}
void bml_node::parse(std::ifstream &fd)
{
std::stack<bml_node *> nodestack;
nodestack.push(this);
while (fd)
{
bml_node newnode;
std::string line = bml_read_line(fd);
if (line.empty())
return;
int line_depth = bml_read_depth(line);
while (line_depth <= nodestack.top()->depth && nodestack.size() > 1)
nodestack.pop();
bml_parse_depth(newnode, line);
bml_parse_name(newnode, line);
bml_parse_data(newnode, line);
bml_parse_attr(newnode, line);
nodestack.top()->child.push_back(newnode);
nodestack.push(&nodestack.top()->child.back());
}
return;
}
bml_node *bml_node::find_subnode(std::string name)
{
unsigned int i;
for (i = 0; i < child.size(); i++)
{
if (name.compare(child[i].name) == 0)
return &child[i];
}
return NULL;
}
bool bml_node::parse_file(std::string filename)
{
std::ifstream file(filename.c_str(), std::ios_base::binary);
if (!file)
return false;
parse(file);
return true;
}

@ -0,0 +1,27 @@
#ifndef __BML_H
#define __BML_H
#include <vector>
#include <string>
#include <fstream>
struct bml_node
{
enum node_type {
CHILD,
ATTRIBUTE
};
bml_node();
bool parse_file(std::string filename);
void parse(std::ifstream &fd);
bml_node *find_subnode(std::string name);
void print();
std::string name;
std::string data;
int depth;
std::vector<bml_node> child;
node_type type;
};
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,58 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _BSX_H_
#define _BSX_H_
#include <fstream>
struct SBSX
{
bool8 dirty; // Changed register values
bool8 dirty2; // Changed register values
bool8 bootup; // Start in bios mapping
bool8 flash_enable; // Flash state
bool8 write_enable; // ROM write protection
bool8 read_enable; // Allow card vendor reading
uint32 flash_command; // Flash command
uint32 old_write; // Previous flash write address
uint32 new_write; // Current flash write address
uint8 out_index;
uint8 output[32];
uint8 PPU[32];
uint8 MMC[16];
uint8 prevMMC[16];
uint8 test2192[32];
bool flash_csr;
bool flash_gsr;
bool flash_bsr;
bool flash_cmd_done;
std::ifstream sat_stream1;
std::ifstream sat_stream2;
bool sat_pf_latch1_enable, sat_dt_latch1_enable;
bool sat_pf_latch2_enable, sat_dt_latch2_enable;
bool sat_stream1_loaded, sat_stream2_loaded;
bool sat_stream1_first, sat_stream2_first;
uint8 sat_stream1_count, sat_stream2_count;
uint16 sat_stream1_queue, sat_stream2_queue;
};
extern struct SBSX BSX;
uint8 S9xGetBSX (uint32);
void S9xSetBSX (uint8, uint32);
uint8 S9xGetBSXPPU (uint16);
void S9xSetBSXPPU (uint8, uint16);
uint8 * S9xGetBasePointerBSX (uint32);
void S9xInitBSX (void);
void S9xResetBSX (void);
void S9xBSXPostLoadState (void);
#endif

@ -0,0 +1,158 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include <math.h>
#include "snes9x.h"
#include "memmap.h"
#define C4_PI 3.14159265
int16 C4WFXVal;
int16 C4WFYVal;
int16 C4WFZVal;
int16 C4WFX2Val;
int16 C4WFY2Val;
int16 C4WFDist;
int16 C4WFScale;
int16 C41FXVal;
int16 C41FYVal;
int16 C41FAngleRes;
int16 C41FDist;
int16 C41FDistVal;
static double tanval;
static double c4x, c4y, c4z;
static double c4x2, c4y2, c4z2;
void C4TransfWireFrame (void)
{
c4x = (double) C4WFXVal;
c4y = (double) C4WFYVal;
c4z = (double) C4WFZVal - 0x95;
// Rotate X
tanval = -(double) C4WFX2Val * C4_PI * 2 / 128;
c4y2 = c4y * cos(tanval) - c4z * sin(tanval);
c4z2 = c4y * sin(tanval) + c4z * cos(tanval);
// Rotate Y
tanval = -(double) C4WFY2Val * C4_PI * 2 / 128;
c4x2 = c4x * cos(tanval) + c4z2 * sin(tanval);
c4z = c4x * -sin(tanval) + c4z2 * cos(tanval);
// Rotate Z
tanval = -(double) C4WFDist * C4_PI * 2 / 128;
c4x = c4x2 * cos(tanval) - c4y2 * sin(tanval);
c4y = c4x2 * sin(tanval) + c4y2 * cos(tanval);
// Scale
C4WFXVal = (int16) (c4x * (double) C4WFScale / (0x90 * (c4z + 0x95)) * 0x95);
C4WFYVal = (int16) (c4y * (double) C4WFScale / (0x90 * (c4z + 0x95)) * 0x95);
}
void C4TransfWireFrame2 (void)
{
c4x = (double) C4WFXVal;
c4y = (double) C4WFYVal;
c4z = (double) C4WFZVal;
// Rotate X
tanval = -(double) C4WFX2Val * C4_PI * 2 / 128;
c4y2 = c4y * cos(tanval) - c4z * sin(tanval);
c4z2 = c4y * sin(tanval) + c4z * cos(tanval);
// Rotate Y
tanval = -(double) C4WFY2Val * C4_PI * 2 / 128;
c4x2 = c4x * cos(tanval) + c4z2 * sin(tanval);
c4z = c4x * -sin(tanval) + c4z2 * cos(tanval);
// Rotate Z
tanval = -(double) C4WFDist * C4_PI * 2 / 128;
c4x = c4x2 * cos(tanval) - c4y2 * sin(tanval);
c4y = c4x2 * sin(tanval) + c4y2 * cos(tanval);
// Scale
C4WFXVal = (int16) (c4x * (double) C4WFScale / 0x100);
C4WFYVal = (int16) (c4y * (double) C4WFScale / 0x100);
}
void C4CalcWireFrame (void)
{
C4WFXVal = C4WFX2Val - C4WFXVal;
C4WFYVal = C4WFY2Val - C4WFYVal;
if (abs(C4WFXVal) > abs(C4WFYVal))
{
C4WFDist = abs(C4WFXVal) + 1;
C4WFYVal = (int16) (256 * (double) C4WFYVal / abs(C4WFXVal));
if (C4WFXVal < 0)
C4WFXVal = -256;
else
C4WFXVal = 256;
}
else
{
if (C4WFYVal != 0)
{
C4WFDist = abs(C4WFYVal) + 1;
C4WFXVal = (int16) (256 * (double) C4WFXVal / abs(C4WFYVal));
if (C4WFYVal < 0)
C4WFYVal = -256;
else
C4WFYVal = 256;
}
else
C4WFDist = 0;
}
}
void C4Op1F (void)
{
if (C41FXVal == 0)
{
if (C41FYVal > 0)
C41FAngleRes = 0x80;
else
C41FAngleRes = 0x180;
}
else
{
tanval = (double) C41FYVal / C41FXVal;
C41FAngleRes = (int16) (atan(tanval) / (C4_PI * 2) * 512);
if (C41FXVal< 0)
C41FAngleRes += 0x100;
C41FAngleRes &= 0x1FF;
}
}
void C4Op15 (void)
{
tanval = sqrt((double) C41FYVal * C41FYVal + (double) C41FXVal * C41FXVal);
C41FDist = (int16) tanval;
}
void C4Op0D (void)
{
tanval = sqrt((double) C41FYVal * C41FYVal + (double) C41FXVal * C41FXVal);
tanval = C41FDistVal / tanval;
C41FYVal = (int16) (C41FYVal * tanval * 0.99);
C41FXVal = (int16) (C41FXVal * tanval * 0.98);
}
uint8 * S9xGetBasePointerC4 (uint16 Address)
{
if (Address >= 0x7f40 && Address <= 0x7f5e)
return (NULL);
return (Memory.C4RAM - 0x6000);
}
uint8 * S9xGetMemPointerC4 (uint16 Address)
{
if (Address >= 0x7f40 && Address <= 0x7f5e)
return (NULL);
return (Memory.C4RAM - 0x6000 + (Address & 0xffff));
}

@ -0,0 +1,40 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _C4_H_
#define _C4_H_
extern int16 C4WFXVal;
extern int16 C4WFYVal;
extern int16 C4WFZVal;
extern int16 C4WFX2Val;
extern int16 C4WFY2Val;
extern int16 C4WFDist;
extern int16 C4WFScale;
extern int16 C41FXVal;
extern int16 C41FYVal;
extern int16 C41FAngleRes;
extern int16 C41FDist;
extern int16 C41FDistVal;
void C4TransfWireFrame (void);
void C4TransfWireFrame2 (void);
void C4CalcWireFrame (void);
void C4Op0D (void);
void C4Op15 (void);
void C4Op1F (void);
void S9xInitC4 (void);
void S9xSetC4 (uint8, uint16);
uint8 S9xGetC4 (uint16);
uint8 * S9xGetBasePointerC4 (uint16);
uint8 * S9xGetMemPointerC4 (uint16);
static inline uint8 * C4GetMemPointer (uint32 Address)
{
return (Memory.ROM + ((Address & 0xff0000) >> 1) + (Address & 0x7fff));
}
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,410 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include <ctype.h>
#include "snes9x.h"
#include "memmap.h"
#include "cheats.h"
#define WRAM_BITS ALL_BITS
#define SRAM_BITS ALL_BITS + (0x20000 >> 5)
#define IRAM_BITS ALL_BITS + (0x30000 >> 5)
#define BIT_CLEAR(a, v) (a)[(v) >> 5] &= ~(1 << ((v) & 31))
#define TEST_BIT(a, v) ((a)[(v) >> 5] & (1 << ((v) & 31)))
#define _S9XCHTC(c, a, b) \
((c) == S9X_LESS_THAN ? (a) < (b) : \
(c) == S9X_GREATER_THAN ? (a) > (b) : \
(c) == S9X_LESS_THAN_OR_EQUAL ? (a) <= (b) : \
(c) == S9X_GREATER_THAN_OR_EQUAL ? (a) >= (b) : \
(c) == S9X_EQUAL ? (a) == (b) : \
(a) != (b))
#define _S9XCHTD(s, m, o) \
((s) == S9X_8_BITS ? ((uint8) (*((m) + (o)))) : \
(s) == S9X_16_BITS ? ((uint16) (*((m) + (o)) + (*((m) + (o) + 1) << 8))) : \
(s) == S9X_24_BITS ? ((uint32) (*((m) + (o)) + (*((m) + (o) + 1) << 8) + (*((m) + (o) + 2) << 16))) : \
((uint32) (*((m) + (o)) + (*((m) + (o) + 1) << 8) + (*((m) + (o) + 2) << 16) + (*((m) + (o) + 3) << 24))))
#define _S9XCHTDS(s, m, o) \
((s) == S9X_8_BITS ? ((int8) (*((m) + (o)))) : \
(s) == S9X_16_BITS ? ((int16) (*((m) + (o)) + (*((m) + (o) + 1) << 8))) : \
(s) == S9X_24_BITS ? (((int32) ((*((m) + (o)) + (*((m) + (o) + 1) << 8) + (*((m) + (o) + 2) << 16)) << 8)) >> 8): \
((int32) (*((m) + (o)) + (*((m) + (o) + 1) << 8) + (*((m) + (o) + 2) << 16) + (*((m) + (o) + 3) << 24))))
static bool8 S9xAllHex (const char *, int);
static bool8 S9xAllHex (const char *code, int len)
{
for (int i = 0; i < len; i++)
if ((code[i] < '0' || code[i] > '9') && (code[i] < 'a' || code[i] > 'f') && (code[i] < 'A' || code[i] > 'F'))
return (FALSE);
return (TRUE);
}
const char * S9xProActionReplayToRaw (const char *code, uint32 &address, uint8 &byte)
{
uint32 data = 0;
if (strlen(code) != 8 || !S9xAllHex(code, 8) || sscanf(code, "%x", &data) != 1)
return ("Invalid Pro Action Replay code - should be 8 hex digits in length.");
address = data >> 8;
byte = (uint8) data;
return (NULL);
}
const char * S9xGoldFingerToRaw (const char *code, uint32 &address, bool8 &sram, uint8 &num_bytes, uint8 bytes[3])
{
char tmp[15];
int i;
if (strlen(code) != 14)
return ("Invalid Gold Finger code - should be 14 hex digits in length.");
strncpy(tmp, code, 5);
tmp[5] = 0;
if (sscanf(tmp, "%x", &address) != 1)
return ("Invalid Gold Finger code.");
// Correct GoldFinger Address
address = (address & 0x7FFF) | ((address & 0x7F8000) << 1) | 0x8000;
for (i = 0; i < 3; i++)
{
unsigned int byte;
strncpy(tmp, code + 5 + i * 2, 2);
tmp[2] = 0;
if (sscanf(tmp, "%x", &byte) != 1)
break;
bytes[i] = (uint8) byte;
}
num_bytes = i;
sram = code[13] == '1';
return (NULL);
}
const char * S9xGameGenieToRaw (const char *code, uint32 &address, uint8 &byte)
{
char new_code[12];
if (strlen(code) != 9 || *(code + 4) != '-' || !S9xAllHex(code, 4) || !S9xAllHex(code + 5, 4))
return ("Invalid Game Genie(tm) code - should be 'xxxx-xxxx'.");
strcpy(new_code, "0x");
strncpy(new_code + 2, code, 4);
strcpy(new_code + 6, code + 5);
static const char *real_hex = "0123456789ABCDEF";
static const char *genie_hex = "DF4709156BC8A23E";
for (int i = 2; i < 10; i++)
{
if (islower(new_code[i]))
new_code[i] = toupper(new_code[i]);
int j;
for (j = 0; j < 16; j++)
{
if (new_code[i] == genie_hex[j])
{
new_code[i] = real_hex[j];
break;
}
}
if (j == 16)
return ("Invalid hex-character in Game Genie(tm) code.");
}
uint32 data = 0;
sscanf(new_code, "%x", &data);
byte = (uint8) (data >> 24);
address = data & 0xffffff;
address = ((address & 0x003c00) << 10) +
((address & 0x00003c) << 14) +
((address & 0xf00000) >> 8) +
((address & 0x000003) << 10) +
((address & 0x00c000) >> 6) +
((address & 0x0f0000) >> 12) +
((address & 0x0003c0) >> 6);
return (NULL);
}
void S9xStartCheatSearch (SCheatData *d)
{
memmove(d->CWRAM, d->RAM, 0x20000);
memmove(d->CSRAM, d->SRAM, 0x80000);
memmove(d->CIRAM, &d->FillRAM[0x3000], 0x2000);
memset((char *) d->ALL_BITS, 0xff, 0x32000 >> 3);
}
void S9xSearchForChange (SCheatData *d, S9xCheatComparisonType cmp, S9xCheatDataSize size, bool8 is_signed, bool8 update)
{
int l, i;
switch (size)
{
case S9X_8_BITS: l = 0; break;
case S9X_16_BITS: l = 1; break;
case S9X_24_BITS: l = 2; break;
default:
case S9X_32_BITS: l = 3; break;
}
if (is_signed)
{
for (i = 0; i < 0x20000 - l; i++)
{
if (TEST_BIT(d->WRAM_BITS, i) && _S9XCHTC(cmp, _S9XCHTDS(size, d->RAM, i), _S9XCHTDS(size, d->CWRAM, i)))
{
if (update)
d->CWRAM[i] = d->RAM[i];
}
else
BIT_CLEAR(d->WRAM_BITS, i);
}
for (i = 0; i < 0x10000 - l; i++)
{
if (TEST_BIT(d->SRAM_BITS, i) && _S9XCHTC(cmp, _S9XCHTDS(size, d->SRAM, i), _S9XCHTDS(size, d->CSRAM, i)))
{
if (update)
d->CSRAM[i] = d->SRAM[i];
}
else
BIT_CLEAR(d->SRAM_BITS, i);
}
for (i = 0; i < 0x2000 - l; i++)
{
if (TEST_BIT(d->IRAM_BITS, i) && _S9XCHTC(cmp, _S9XCHTDS(size, d->FillRAM + 0x3000, i), _S9XCHTDS(size, d->CIRAM, i)))
{
if (update)
d->CIRAM[i] = d->FillRAM[i + 0x3000];
}
else
BIT_CLEAR(d->IRAM_BITS, i);
}
}
else
{
for (i = 0; i < 0x20000 - l; i++)
{
if (TEST_BIT(d->WRAM_BITS, i) && _S9XCHTC(cmp, _S9XCHTD(size, d->RAM, i), _S9XCHTD(size, d->CWRAM, i)))
{
if (update)
d->CWRAM[i] = d->RAM[i];
}
else
BIT_CLEAR(d->WRAM_BITS, i);
}
for (i = 0; i < 0x10000 - l; i++)
{
if (TEST_BIT(d->SRAM_BITS, i) && _S9XCHTC(cmp, _S9XCHTD(size, d->SRAM, i), _S9XCHTD(size, d->CSRAM, i)))
{
if (update)
d->CSRAM[i] = d->SRAM[i];
}
else
BIT_CLEAR(d->SRAM_BITS, i);
}
for (i = 0; i < 0x2000 - l; i++)
{
if (TEST_BIT(d->IRAM_BITS, i) && _S9XCHTC(cmp, _S9XCHTD(size, d->FillRAM + 0x3000, i), _S9XCHTD(size, d->CIRAM, i)))
{
if (update)
d->CIRAM[i] = d->FillRAM[i + 0x3000];
}
else
BIT_CLEAR(d->IRAM_BITS, i);
}
}
for (i = 0x20000 - l; i < 0x20000; i++)
BIT_CLEAR(d->WRAM_BITS, i);
for (i = 0x10000 - l; i < 0x10000; i++)
BIT_CLEAR(d->SRAM_BITS, i);
}
void S9xSearchForValue (SCheatData *d, S9xCheatComparisonType cmp, S9xCheatDataSize size, uint32 value, bool8 is_signed, bool8 update)
{
int l, i;
switch (size)
{
case S9X_8_BITS: l = 0; break;
case S9X_16_BITS: l = 1; break;
case S9X_24_BITS: l = 2; break;
default:
case S9X_32_BITS: l = 3; break;
}
if (is_signed)
{
for (i = 0; i < 0x20000 - l; i++)
{
if (TEST_BIT(d->WRAM_BITS, i) && _S9XCHTC(cmp, _S9XCHTDS(size, d->RAM, i), (int32) value))
{
if (update)
d->CWRAM[i] = d->RAM[i];
}
else
BIT_CLEAR(d->WRAM_BITS, i);
}
for (i = 0; i < 0x10000 - l; i++)
{
if (TEST_BIT(d->SRAM_BITS, i) && _S9XCHTC(cmp, _S9XCHTDS(size, d->SRAM, i), (int32) value))
{
if (update)
d->CSRAM[i] = d->SRAM[i];
}
else
BIT_CLEAR(d->SRAM_BITS, i);
}
for (i = 0; i < 0x2000 - l; i++)
{
if (TEST_BIT(d->IRAM_BITS, i) && _S9XCHTC(cmp, _S9XCHTDS(size, d->FillRAM + 0x3000, i), (int32) value))
{
if (update)
d->CIRAM[i] = d->FillRAM[i + 0x3000];
}
else
BIT_CLEAR(d->IRAM_BITS, i);
}
}
else
{
for (i = 0; i < 0x20000 - l; i++)
{
if (TEST_BIT(d->WRAM_BITS, i) && _S9XCHTC(cmp, _S9XCHTD(size, d->RAM, i), value))
{
if (update)
d->CWRAM[i] = d->RAM[i];
}
else
BIT_CLEAR(d->WRAM_BITS, i);
}
for (i = 0; i < 0x10000 - l; i++)
{
if (TEST_BIT(d->SRAM_BITS, i) && _S9XCHTC(cmp, _S9XCHTD(size, d->SRAM, i), value))
{
if (update)
d->CSRAM[i] = d->SRAM[i];
}
else
BIT_CLEAR(d->SRAM_BITS, i);
}
for (i = 0; i < 0x2000 - l; i++)
{
if (TEST_BIT(d->IRAM_BITS, i) && _S9XCHTC(cmp, _S9XCHTD(size, d->FillRAM + 0x3000, i), value))
{
if (update)
d->CIRAM[i] = d->FillRAM[i + 0x3000];
}
else
BIT_CLEAR(d->IRAM_BITS, i);
}
}
for (i = 0x20000 - l; i < 0x20000; i++)
BIT_CLEAR(d->WRAM_BITS, i);
for (i = 0x10000 - l; i < 0x10000; i++)
BIT_CLEAR(d->SRAM_BITS, i);
}
void S9xSearchForAddress (SCheatData *d, S9xCheatComparisonType cmp, S9xCheatDataSize size, uint32 value, bool8 update)
{
int l, i;
switch (size)
{
case S9X_8_BITS: l = 0; break;
case S9X_16_BITS: l = 1; break;
case S9X_24_BITS: l = 2; break;
default:
case S9X_32_BITS: l = 3; break;
}
for (i = 0; i < 0x20000 - l; i++)
{
if (TEST_BIT(d->WRAM_BITS, i) && _S9XCHTC(cmp, i, (int32) value))
{
if (update)
d->CWRAM[i] = d->RAM[i];
}
else
BIT_CLEAR(d->WRAM_BITS, i);
}
for (i = 0; i < 0x10000 - l; i++)
{
if (TEST_BIT(d->SRAM_BITS, i) && _S9XCHTC(cmp, i + 0x20000, (int32) value))
{
if (update)
d->CSRAM[i] = d->SRAM[i];
}
else
BIT_CLEAR(d->SRAM_BITS, i);
}
for (i = 0; i < 0x2000 - l; i++)
{
if (TEST_BIT(d->IRAM_BITS, i) && _S9XCHTC(cmp, i + 0x30000, (int32) value))
{
if (update)
d->CIRAM[i] = d->FillRAM[i + 0x3000];
}
else
BIT_CLEAR(d->IRAM_BITS, i);
}
for (i = 0x20000 - l; i < 0x20000; i++)
BIT_CLEAR(d->WRAM_BITS, i);
for (i = 0x10000 - l; i < 0x10000; i++)
BIT_CLEAR(d->SRAM_BITS, i);
}
void S9xOutputCheatSearchResults (SCheatData *d)
{
int i;
for (i = 0; i < 0x20000; i++)
{
if (TEST_BIT(d->WRAM_BITS, i))
printf("WRAM: %05x: %02x\n", i, d->RAM[i]);
}
for (i = 0; i < 0x10000; i++)
{
if (TEST_BIT(d->SRAM_BITS, i))
printf("SRAM: %04x: %02x\n", i, d->SRAM[i]);
}
for (i = 0; i < 0x2000; i++)
{
if (TEST_BIT(d->IRAM_BITS, i))
printf("IRAM: %05x: %02x\n", i, d->FillRAM[i + 0x3000]);
}
}

@ -0,0 +1,103 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _CHEATS_H_
#define _CHEATS_H_
#include "port.h"
#include <vector>
struct SCheat
{
uint32 address;
uint8 byte;
uint8 saved_byte;
bool8 conditional;
bool8 cond_true;
uint8 cond_byte;
bool8 enabled;
};
struct SCheatGroup
{
char *name;
bool8 enabled;
std::vector<struct SCheat> c;
};
struct SCheatData
{
std::vector<struct SCheatGroup> g;
bool8 enabled;
uint8 CWRAM[0x20000];
uint8 CSRAM[0x80000];
uint8 CIRAM[0x2000];
uint8 *RAM;
uint8 *FillRAM;
uint8 *SRAM;
uint32 ALL_BITS[0x32000 >> 5];
uint8 CWatchRAM[0x32000];
};
struct Watch
{
bool on;
int size;
int format;
uint32 address;
char buf[12];
char desc[32];
};
typedef enum
{
S9X_LESS_THAN,
S9X_GREATER_THAN,
S9X_LESS_THAN_OR_EQUAL,
S9X_GREATER_THAN_OR_EQUAL,
S9X_EQUAL,
S9X_NOT_EQUAL
} S9xCheatComparisonType;
typedef enum
{
S9X_8_BITS,
S9X_16_BITS,
S9X_24_BITS,
S9X_32_BITS
} S9xCheatDataSize;
extern SCheatData Cheat;
extern Watch watches[16];
int S9xAddCheatGroup (const char *name, const char *cheat);
int S9xModifyCheatGroup (uint32 index, const char *name, const char *cheat);
void S9xEnableCheatGroup (uint32 index);
void S9xDisableCheatGroup (uint32 index);
void S9xDeleteCheats (void);
char *S9xCheatGroupToText (uint32 index);
void S9xDeleteCheatGroup (uint32 index);
bool8 S9xLoadCheatFile (const char *filename);
bool8 S9xSaveCheatFile (const char *filename);
void S9xUpdateCheatsInMemory (void);
int S9xImportCheatsFromDatabase(const char *filename);
void S9xCheatsDisable (void);
void S9xCheatsEnable (void);
char *S9xCheatValidate (const char *cheat);
void S9xInitCheatData (void);
void S9xInitWatchedAddress (void);
void S9xStartCheatSearch (SCheatData *);
void S9xSearchForChange (SCheatData *, S9xCheatComparisonType, S9xCheatDataSize, bool8, bool8);
void S9xSearchForValue (SCheatData *, S9xCheatComparisonType, S9xCheatDataSize, uint32, bool8, bool8);
void S9xSearchForAddress (SCheatData *, S9xCheatComparisonType, S9xCheatDataSize, uint32, bool8);
void S9xOutputCheatSearchResults (SCheatData *);
const char * S9xGameGenieToRaw (const char *, uint32 &, uint8 &);
const char * S9xProActionReplayToRaw (const char *, uint32 &, uint8 &);
const char * S9xGoldFingerToRaw (const char *, uint32 &, bool8 &, uint8 &, uint8 bytes[3]);
#endif

@ -0,0 +1,767 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include <ctype.h>
#include "snes9x.h"
#include "memmap.h"
#include "cheats.h"
#include "bml.h"
static inline char *trim (char *string)
{
int start;
int end;
for (start = 0; string[start] && isspace (string[start]); start++) {}
for (end = start; string[end] && !isspace (string[end]); end++) {}
string[end] = '\0';
return &string[start];
}
static inline uint8 S9xGetByteFree (uint32 Address)
{
int block = (Address & 0xffffff) >> MEMMAP_SHIFT;
uint8 *GetAddress = Memory.Map[block];
uint8 byte;
if (GetAddress >= (uint8 *) CMemory::MAP_LAST)
{
byte = *(GetAddress + (Address & 0xffff));
return (byte);
}
switch ((pint) GetAddress)
{
case CMemory::MAP_CPU:
byte = S9xGetCPU(Address & 0xffff);
return (byte);
case CMemory::MAP_PPU:
if (CPU.InDMAorHDMA && (Address & 0xff00) == 0x2100)
return (OpenBus);
byte = S9xGetPPU(Address & 0xffff);
return (byte);
case CMemory::MAP_LOROM_SRAM:
case CMemory::MAP_SA1RAM:
// Address & 0x7fff : offset into bank
// Address & 0xff0000 : bank
// bank >> 1 | offset : SRAM address, unbound
// unbound & SRAMMask : SRAM offset
byte = *(Memory.SRAM + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Memory.SRAMMask));
return (byte);
case CMemory::MAP_LOROM_SRAM_B:
byte = *(Multi.sramB + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Multi.sramMaskB));
return (byte);
case CMemory::MAP_HIROM_SRAM:
case CMemory::MAP_RONLY_SRAM:
byte = *(Memory.SRAM + (((Address & 0x7fff) - 0x6000 + ((Address & 0x1f0000) >> 3)) & Memory.SRAMMask));
return (byte);
case CMemory::MAP_BWRAM:
byte = *(Memory.BWRAM + ((Address & 0x7fff) - 0x6000));
return (byte);
case CMemory::MAP_DSP:
byte = S9xGetDSP(Address & 0xffff);
return (byte);
case CMemory::MAP_SPC7110_ROM:
byte = S9xGetSPC7110Byte(Address);
return (byte);
case CMemory::MAP_SPC7110_DRAM:
byte = S9xGetSPC7110(0x4800);
return (byte);
case CMemory::MAP_C4:
byte = S9xGetC4(Address & 0xffff);
return (byte);
case CMemory::MAP_OBC_RAM:
byte = S9xGetOBC1(Address & 0xffff);
return (byte);
case CMemory::MAP_SETA_DSP:
byte = S9xGetSetaDSP(Address);
return (byte);
case CMemory::MAP_SETA_RISC:
byte = S9xGetST018(Address);
return (byte);
case CMemory::MAP_BSX:
byte = S9xGetBSX(Address);
return (byte);
case CMemory::MAP_NONE:
default:
byte = OpenBus;
return (byte);
}
}
static inline void S9xSetByteFree (uint8 Byte, uint32 Address)
{
int block = (Address & 0xffffff) >> MEMMAP_SHIFT;
uint8 *SetAddress = Memory.Map[block];
if (SetAddress >= (uint8 *) CMemory::MAP_LAST)
{
*(SetAddress + (Address & 0xffff)) = Byte;
return;
}
switch ((pint) SetAddress)
{
case CMemory::MAP_CPU:
S9xSetCPU(Byte, Address & 0xffff);
return;
case CMemory::MAP_PPU:
if (CPU.InDMAorHDMA && (Address & 0xff00) == 0x2100)
return;
S9xSetPPU(Byte, Address & 0xffff);
return;
case CMemory::MAP_LOROM_SRAM:
if (Memory.SRAMMask)
{
*(Memory.SRAM + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Memory.SRAMMask)) = Byte;
CPU.SRAMModified = TRUE;
}
return;
case CMemory::MAP_LOROM_SRAM_B:
if (Multi.sramMaskB)
{
*(Multi.sramB + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Multi.sramMaskB)) = Byte;
CPU.SRAMModified = TRUE;
}
return;
case CMemory::MAP_HIROM_SRAM:
if (Memory.SRAMMask)
{
*(Memory.SRAM + (((Address & 0x7fff) - 0x6000 + ((Address & 0x1f0000) >> 3)) & Memory.SRAMMask)) = Byte;
CPU.SRAMModified = TRUE;
}
return;
case CMemory::MAP_BWRAM:
*(Memory.BWRAM + ((Address & 0x7fff) - 0x6000)) = Byte;
CPU.SRAMModified = TRUE;
return;
case CMemory::MAP_SA1RAM:
*(Memory.SRAM + (Address & 0xffff)) = Byte;
return;
case CMemory::MAP_DSP:
S9xSetDSP(Byte, Address & 0xffff);
return;
case CMemory::MAP_C4:
S9xSetC4(Byte, Address & 0xffff);
return;
case CMemory::MAP_OBC_RAM:
S9xSetOBC1(Byte, Address & 0xffff);
return;
case CMemory::MAP_SETA_DSP:
S9xSetSetaDSP(Byte, Address);
return;
case CMemory::MAP_SETA_RISC:
S9xSetST018(Byte, Address);
return;
case CMemory::MAP_BSX:
S9xSetBSX(Byte, Address);
return;
case CMemory::MAP_NONE:
default:
return;
}
}
void S9xInitWatchedAddress (void)
{
for (unsigned int i = 0; i < sizeof(watches) / sizeof(watches[0]); i++)
watches[i].on = false;
}
void S9xInitCheatData (void)
{
Cheat.RAM = Memory.RAM;
Cheat.SRAM = Memory.SRAM;
Cheat.FillRAM = Memory.FillRAM;
}
void S9xUpdateCheatInMemory (SCheat *c)
{
uint8 byte;
if (!c->enabled)
return;
byte = S9xGetByteFree (c->address);
if (byte != c->byte)
{
/* The game wrote a different byte to the address, update saved_byte */
c->saved_byte = byte;
if (c->conditional)
{
if (c->saved_byte != c->cond_byte && c->cond_true)
{
/* Condition is now false, let the byte stand */
c->cond_true = false;
}
else if (c->saved_byte == c->cond_byte && !c->cond_true)
{
c->cond_true = true;
S9xSetByteFree (c->byte, c->address);
}
}
else
S9xSetByteFree (c->byte, c->address);
}
else if (c->conditional)
{
if (byte == c->cond_byte)
{
c->cond_true = true;
c->saved_byte = byte;
S9xSetByteFree (c->byte, c->address);
}
}
}
void S9xDisableCheat (SCheat *c)
{
if (!c->enabled)
return;
if (!Cheat.enabled)
{
c->enabled = false;
return;
}
/* Make sure we restore the up-to-date written byte */
S9xUpdateCheatInMemory (c);
c->enabled = false;
if (c->conditional && !c->cond_true)
return;
S9xSetByteFree (c->saved_byte, c->address);
c->cond_true = false;
}
void S9xDeleteCheatGroup (uint32 g)
{
unsigned int i;
if (g >= Cheat.g.size ())
return;
for (i = 0; i < Cheat.g[g].c.size (); i++)
{
S9xDisableCheat (&Cheat.g[g].c[i]);
}
delete[] Cheat.g[g].name;
Cheat.g.erase (Cheat.g.begin () + g);
}
void S9xDeleteCheats (void)
{
unsigned int i;
for (i = 0; i < Cheat.g.size (); i++)
{
S9xDisableCheatGroup (i);
delete[] Cheat.g[i].name;
}
Cheat.g.clear ();
}
void S9xEnableCheat (SCheat *c)
{
uint8 byte;
if (c->enabled)
return;
c->enabled = true;
if (!Cheat.enabled)
return;
byte = S9xGetByteFree(c->address);
if (c->conditional)
{
if (byte != c->cond_byte)
return;
c->cond_true = true;
}
c->saved_byte = byte;
S9xSetByteFree (c->byte, c->address);
}
void S9xEnableCheatGroup (uint32 num)
{
unsigned int i;
for (i = 0; i < Cheat.g[num].c.size (); i++)
{
S9xEnableCheat (&Cheat.g[num].c[i]);
}
Cheat.g[num].enabled = true;
}
void S9xDisableCheatGroup (uint32 num)
{
unsigned int i;
for (i = 0; i < Cheat.g[num].c.size (); i++)
{
S9xDisableCheat (&Cheat.g[num].c[i]);
}
Cheat.g[num].enabled = false;
}
SCheat S9xTextToCheat (char *text)
{
SCheat c;
unsigned int byte = 0;
unsigned int cond_byte = 0;
c.enabled = false;
c.conditional = false;
if (!S9xGameGenieToRaw (text, c.address, c.byte))
{
byte = c.byte;
}
else if (!S9xProActionReplayToRaw (text, c.address, c.byte))
{
byte = c.byte;
}
else if (sscanf (text, "%x = %x ? %x", &c.address, &cond_byte, &byte) == 3)
{
c.conditional = true;
}
else if (sscanf (text, "%x = %x", &c.address, &byte) == 2)
{
}
else if (sscanf (text, "%x / %x / %x", &c.address, &cond_byte, &byte) == 3)
{
c.conditional = true;
}
else if (sscanf (text, "%x / %x", &c.address, &byte) == 2)
{
}
else
{
c.address = 0;
byte = 0;
}
c.byte = byte;
c.cond_byte = cond_byte;
return c;
}
SCheatGroup S9xCreateCheatGroup (const char *name, const char *cheat)
{
SCheatGroup g;
char *code_string = strdup (cheat);
char *code_ptr = code_string;
int len;
g.name = strdup (name);
g.enabled = false;
for (len = strcspn (code_ptr, "+"); len; len = strcspn (code_ptr, "+"))
{
char *code = code_ptr;
code_ptr += len + (code_ptr[len] == '\0' ? 0 : 1);
code[len] = '\0';
code = trim (code);
SCheat c = S9xTextToCheat (code);
if (c.address)
g.c.push_back (c);
}
free(code_string);
return g;
}
int S9xAddCheatGroup (const char *name, const char *cheat)
{
SCheatGroup g = S9xCreateCheatGroup (name, cheat);
if (g.c.size () == 0)
return -1;
Cheat.g.push_back (g);
return Cheat.g.size () - 1;
}
int S9xModifyCheatGroup (uint32 num, const char *name, const char *cheat)
{
if (num >= Cheat.g.size())
return -1;
S9xDisableCheatGroup (num);
delete[] Cheat.g[num].name;
Cheat.g[num] = S9xCreateCheatGroup (name, cheat);
return num;
}
char *S9xCheatToText (SCheat *c)
{
int size = 10; /* 6 address, 1 =, 2 byte, 1 NUL */
char *text;
if (c->conditional)
size += 3; /* additional 2 byte, 1 ? */
text = new char[size];
if (c->conditional)
snprintf (text, size, "%06x=%02x?%02x", c->address, c->cond_byte, c->byte);
else
snprintf (text, size, "%06x=%02x", c->address, c->byte);
return text;
}
char *S9xCheatGroupToText (SCheatGroup *g)
{
std::string text = "";
unsigned int i;
if (g->c.size () == 0)
return NULL;
for (i = 0; i < g->c.size (); i++)
{
char *tmp = S9xCheatToText (&g->c[i]);
if (i != 0)
text += " + ";
text += tmp;
delete[] tmp;
}
return strdup (text.c_str ());
}
char *S9xCheatValidate (const char *code_string)
{
SCheatGroup g = S9xCreateCheatGroup ("temp", code_string);
delete[] g.name;
if (g.c.size() > 0)
{
return S9xCheatGroupToText (&g);
}
return NULL;
}
char *S9xCheatGroupToText (uint32 num)
{
if (num >= Cheat.g.size ())
return NULL;
return S9xCheatGroupToText (&Cheat.g[num]);
}
void S9xUpdateCheatsInMemory (void)
{
unsigned int i;
unsigned int j;
if (!Cheat.enabled)
return;
for (i = 0; i < Cheat.g.size (); i++)
{
for (j = 0; j < Cheat.g[i].c.size (); j++)
{
S9xUpdateCheatInMemory (&Cheat.g[i].c[j]);
}
}
}
static int S9xCheatIsDuplicate (const char *name, const char *code)
{
unsigned int i;
for (i = 0; i < Cheat.g.size(); i++)
{
if (!strcmp (name, Cheat.g[i].name))
{
char *code_string = S9xCheatGroupToText (i);
char *validated = S9xCheatValidate (code);
if (validated && !strcmp (code_string, validated))
{
free (code_string);
free (validated);
return TRUE;
}
free (code_string);
free (validated);
}
}
return FALSE;
}
static void S9xLoadCheatsFromBMLNode (bml_node *n)
{
unsigned int i;
for (i = 0; i < n->child.size (); i++)
{
if (!strcasecmp (n->child[i].name.c_str(), "cheat"))
{
const char *desc = NULL;
const char *code = NULL;
bool8 enabled = false;
bml_node *c = &n->child[i];
bml_node *tmp = NULL;
tmp = c->find_subnode("name");
if (!tmp)
desc = (char *) "";
else
desc = tmp->data.c_str();
tmp = c->find_subnode("code");
if (tmp)
code = tmp->data.c_str();
if (c->find_subnode("enable"))
enabled = true;
if (code && !S9xCheatIsDuplicate (desc, code))
{
int index = S9xAddCheatGroup (desc, code);
if (enabled)
S9xEnableCheatGroup (index);
}
}
}
return;
}
static bool8 S9xLoadCheatFileClassic (const char *filename)
{
FILE *fs;
uint8 data[28];
fs = fopen(filename, "rb");
if (!fs)
return (FALSE);
while (fread ((void *) data, 1, 28, fs) == 28)
{
SCheat c;
char name[21];
char cheat[10];
c.enabled = (data[0] & 4) == 0;
c.byte = data[1];
c.address = data[2] | (data[3] << 8) | (data[4] << 16);
memcpy (name, &data[8], 20);
name[20] = 0;
snprintf (cheat, 10, "%x=%x", c.address, c.byte);
S9xAddCheatGroup (name, cheat);
if (c.enabled)
S9xEnableCheatGroup (Cheat.g.size () - 1);
}
fclose(fs);
return (TRUE);
}
bool8 S9xLoadCheatFile (const char *filename)
{
bml_node bml;
if (!bml.parse_file(filename))
{
return S9xLoadCheatFileClassic (filename);
}
bml_node *n = bml.find_subnode("cheat");
if (n)
{
S9xLoadCheatsFromBMLNode (&bml);
}
if (!n)
{
return S9xLoadCheatFileClassic (filename);
}
return (TRUE);
}
bool8 S9xSaveCheatFile (const char *filename)
{
unsigned int i;
FILE *file = NULL;
if (Cheat.g.size () == 0)
{
remove (filename);
return TRUE;
}
file = fopen (filename, "w");
if (!file)
return FALSE;
for (i = 0; i < Cheat.g.size (); i++)
{
char *txt = S9xCheatGroupToText (i);
fprintf (file,
"cheat\n"
" name: %s\n"
" code: %s\n"
"%s\n",
Cheat.g[i].name ? Cheat.g[i].name : "",
txt,
Cheat.g[i].enabled ? " enable\n" : ""
);
delete[] txt;
}
fclose (file);
return TRUE;
}
void S9xCheatsDisable (void)
{
unsigned int i;
if (!Cheat.enabled)
return;
for (i = 0; i < Cheat.g.size (); i++)
{
if (Cheat.g[i].enabled)
{
S9xDisableCheatGroup (i);
Cheat.g[i].enabled = TRUE;
}
}
Cheat.enabled = FALSE;
}
void S9xCheatsEnable (void)
{
unsigned int i;
if (Cheat.enabled)
return;
Cheat.enabled = TRUE;
for (i = 0; i < Cheat.g.size (); i++)
{
if (Cheat.g[i].enabled)
{
Cheat.g[i].enabled = FALSE;
S9xEnableCheatGroup (i);
}
}
}
int S9xImportCheatsFromDatabase (const char *filename)
{
char sha256_txt[65];
char hextable[] = "0123456789abcdef";
unsigned int i;
bml_node bml;
if (!bml.parse_file(filename))
return -1; // No file
for (i = 0; i < 32; i++)
{
sha256_txt[i * 2] = hextable[Memory.ROMSHA256[i] >> 4];
sha256_txt[i * 2 + 1] = hextable[Memory.ROMSHA256[i] & 0xf];
}
sha256_txt[64] = '\0';
for (i = 0; i < bml.child.size (); i++)
{
if (!strcasecmp (bml.child[i].name.c_str(), "cartridge"))
{
bml_node *n;
if ((n = bml.child[i].find_subnode ("sha256")))
{
if (!strcasecmp (n->data.c_str(), sha256_txt))
{
S9xLoadCheatsFromBMLNode (&bml.child[i]);
return 0;
}
}
}
}
return -2; /* No codes */
}

@ -0,0 +1,234 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "snes9x.h"
#include "memmap.h"
static uint8 region_map[6][6] =
{
{ 0, 0x01, 0x03, 0x07, 0x0f, 0x1f },
{ 0, 0, 0x02, 0x06, 0x0e, 0x1e },
{ 0, 0, 0, 0x04, 0x0c, 0x1c },
{ 0, 0, 0, 0, 0x08, 0x18 },
{ 0, 0, 0, 0, 0, 0x10 }
};
static inline uint8 CalcWindowMask (int, uint8, uint8);
static inline void StoreWindowRegions (uint8, struct ClipData *, int, int16 *, uint8 *, bool8, bool8 s = FALSE);
static inline uint8 CalcWindowMask (int i, uint8 W1, uint8 W2)
{
if (!PPU.ClipWindow1Enable[i])
{
if (!PPU.ClipWindow2Enable[i])
return (0);
else
{
if (!PPU.ClipWindow2Inside[i])
return (~W2);
return (W2);
}
}
else
{
if (!PPU.ClipWindow2Enable[i])
{
if (!PPU.ClipWindow1Inside[i])
return (~W1);
return (W1);
}
else
{
if (!PPU.ClipWindow1Inside[i])
W1 = ~W1;
if (!PPU.ClipWindow2Inside[i])
W2 = ~W2;
switch (PPU.ClipWindowOverlapLogic[i])
{
case 0: // OR
return (W1 | W2);
case 1: // AND
return (W1 & W2);
case 2: // XOR
return (W1 ^ W2);
case 3: // XNOR
return (~(W1 ^ W2));
}
}
}
// Never get here
return (0);
}
static inline void StoreWindowRegions (uint8 Mask, struct ClipData *Clip, int n_regions, int16 *windows, uint8 *drawing_modes, bool8 sub, bool8 StoreMode0)
{
int ct = 0;
for (int j = 0; j < n_regions; j++)
{
int DrawMode = drawing_modes[j];
if (sub)
DrawMode |= 1;
if (Mask & (1 << j))
DrawMode = 0;
if (!StoreMode0 && !DrawMode)
continue;
if (ct > 0 && Clip->Right[ct - 1] == windows[j] && Clip->DrawMode[ct - 1] == DrawMode)
Clip->Right[ct - 1] = windows[j + 1]; // This region borders with and has the same drawing mode as the previous region: merge them.
else
{
// Add a new region to the BG
Clip->Left[ct] = windows[j];
Clip->Right[ct] = windows[j + 1];
Clip->DrawMode[ct] = DrawMode;
ct++;
}
}
Clip->Count = ct;
}
void S9xComputeClipWindows (void)
{
int16 windows[6] = { 0, 256, 256, 256, 256, 256 };
uint8 drawing_modes[5] = { 0, 0, 0, 0, 0 };
int n_regions = 1;
int i, j;
// Calculate window regions. We have at most 5 regions, because we have 6 control points
// (screen edges, window 1 left & right, and window 2 left & right).
if (PPU.Window1Left <= PPU.Window1Right)
{
if (PPU.Window1Left > 0)
{
windows[2] = 256;
windows[1] = PPU.Window1Left;
n_regions = 2;
}
if (PPU.Window1Right < 255)
{
windows[n_regions + 1] = 256;
windows[n_regions] = PPU.Window1Right + 1;
n_regions++;
}
}
if (PPU.Window2Left <= PPU.Window2Right)
{
for (i = 0; i <= n_regions; i++)
{
if (PPU.Window2Left == windows[i])
break;
if (PPU.Window2Left < windows[i])
{
for (j = n_regions; j >= i; j--)
windows[j + 1] = windows[j];
windows[i] = PPU.Window2Left;
n_regions++;
break;
}
}
for (; i <= n_regions; i++)
{
if (PPU.Window2Right + 1 == windows[i])
break;
if (PPU.Window2Right + 1 < windows[i])
{
for (j = n_regions; j >= i; j--)
windows[j + 1] = windows[j];
windows[i] = PPU.Window2Right + 1;
n_regions++;
break;
}
}
}
// Get a bitmap of which regions correspond to each window.
uint8 W1, W2;
if (PPU.Window1Left <= PPU.Window1Right)
{
for (i = 0; windows[i] != PPU.Window1Left; i++) ;
for (j = i; windows[j] != PPU.Window1Right + 1; j++) ;
W1 = region_map[i][j];
}
else
W1 = 0;
if (PPU.Window2Left <= PPU.Window2Right)
{
for (i = 0; windows[i] != PPU.Window2Left; i++) ;
for (j = i; windows[j] != PPU.Window2Right + 1; j++) ;
W2 = region_map[i][j];
}
else
W2 = 0;
// Color Window affects the drawing mode for each region.
// Modes are: 3=Draw as normal, 2=clip color (math only), 1=no math (draw only), 0=nothing.
uint8 CW_color = 0, CW_math = 0;
uint8 CW = CalcWindowMask(5, W1, W2);
switch (Memory.FillRAM[0x2130] & 0xc0)
{
case 0x00: CW_color = 0; break;
case 0x40: CW_color = ~CW; break;
case 0x80: CW_color = CW; break;
case 0xc0: CW_color = 0xff; break;
}
switch (Memory.FillRAM[0x2130] & 0x30)
{
case 0x00: CW_math = 0; break;
case 0x10: CW_math = ~CW; break;
case 0x20: CW_math = CW; break;
case 0x30: CW_math = 0xff; break;
}
for (i = 0; i < n_regions; i++)
{
if (!(CW_color & (1 << i)))
drawing_modes[i] |= 1;
if (!(CW_math & (1 << i)))
drawing_modes[i] |= 2;
}
// Store backdrop clip window (draw everywhere color window allows)
StoreWindowRegions(0, &IPPU.Clip[0][5], n_regions, windows, drawing_modes, FALSE, TRUE);
StoreWindowRegions(0, &IPPU.Clip[1][5], n_regions, windows, drawing_modes, TRUE, TRUE);
// Store per-BG and OBJ clip windows
for (j = 0; j < 5; j++)
{
uint8 W = Settings.DisableGraphicWindows ? 0 : CalcWindowMask(j, W1, W2);
for (int sub = 0; sub < 2; sub++)
{
if (Memory.FillRAM[sub + 0x212e] & (1 << j))
StoreWindowRegions(W, &IPPU.Clip[sub][j], n_regions, windows, drawing_modes, sub);
else
StoreWindowRegions(0, &IPPU.Clip[sub][j], n_regions, windows, drawing_modes, sub);
}
}
}

@ -0,0 +1,67 @@
#include <cstring>
#include "port.h"
void _splitpath(const char *path, char *drive, char *dir, char *fname, char *ext)
{
char *slash = strrchr((char *)path, SLASH_CHAR);
char *dot = strrchr((char *)path, '.');
*drive = '\0';
if (dot && slash && dot < slash)
{
dot = 0;
}
if (!slash)
{
*dir = '\0';
strcpy(fname, path);
if (dot)
{
fname[dot - path] = '\0';
strcpy(ext, dot + 1);
}
else
{
*ext = '\0';
}
}
else
{
strcpy(dir, path);
dir[slash - path] = '\0';
strcpy(fname, slash + 1);
if (dot)
{
fname[(dot - slash) - 1] = '\0';
strcpy(ext, dot + 1);
}
else
{
*ext = '\0';
}
}
}
void _makepath(char *path, const char *drive, const char *dir, const char *fname, const char *ext)
{
if (dir && *dir)
{
strcpy(path, dir);
strcat(path, "/");
}
else
*path = '\0';
strcat(path, fname);
if (ext && *ext)
{
if (*ext != '.')
strcat(path, ".");
strcat(path, ext);
}
}

@ -0,0 +1,486 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <string>
#include "conffile.h"
#ifdef __WIN32__
#define snprintf _snprintf // needs ANSI compliant name
#endif
#define SORT_SECTIONS_BY_SIZE // output
using namespace std;
bool ConfigFile::defaultAutoAdd = false;
bool ConfigFile::niceAlignment = false;
bool ConfigFile::showComments = true;
bool ConfigFile::alphaSort = true;
bool ConfigFile::timeSort = false;
static ConfigFile* curConfigFile = NULL; // for section_then_key_less
ConfigFile::ConfigFile(void) {
Clear();
}
void ConfigFile::Clear(void){
data.clear();
sectionSizes.ClearSections();
linectr = 0;
}
bool ConfigFile::LoadFile(const char *filename){
FSTREAM s;
bool ret=false;
const char *n, *n2;
if((s=OPEN_FSTREAM(filename, "r"))){
n=filename;
n2=strrchr(n, '/'); if(n2!=NULL) n=n2+1;
n2=strrchr(n, '\\'); if(n2!=NULL) n=n2+1;
fStream fS(s);
LoadFile(&fS, n);
CLOSE_FSTREAM(s);
ret = true;
} else {
fprintf(stderr, "Couldn't open conffile ");
perror(filename);
}
return ret;
}
void ConfigFile::LoadFile(Stream *r, const char *name){
curConfigFile = this;
string l, key, val;
string section;
string comment;
int i, line, line2;
bool eof;
line=line2=0;
section.clear();
do {
line=line2++;
l=r->getline(eof);
ConfigEntry::trim(l);
if(l.size()==0) continue;
if(l[0]=='#' || l[0]==';'){
// comment
continue;
}
if(l[0]=='['){
if(*l.rbegin()!=']'){
if(name) fprintf(stderr, "%s:", name);
fprintf(stderr, "[%d]: Ignoring invalid section header\n", line);
continue;
}
section.assign(l, 1, l.size()-2);
continue;
}
while(*l.rbegin()=='\\'){
l.erase(l.size()-1);
line2++;
val=r->getline(eof);
if(eof){
fprintf(stderr, "Unexpected EOF reading config file");
if(name) fprintf(stderr, " '%s'", name);
fprintf(stderr, "\n");
return;
}
ConfigEntry::trim(val);
l+=val;
}
i=l.find('=');
if(i<0){
if(name) fprintf(stderr, "%s:", name);
fprintf(stderr, "[%d]: Ignoring invalid entry\n", line);
continue;
}
key=l.substr(0,i); ConfigEntry::trim(key);
val=l.substr(i+1); comment = ConfigEntry::trimCommented(val);
if(val.size() > 0 && val[0]=='"' && *val.rbegin()=='"') val=val.substr(1, val.size()-2);
ConfigEntry e(line, section, key, val);
e.comment = comment;
if(data.erase(e))
sectionSizes.DecreaseSectionSize(e.section);
data.insert(e);
sectionSizes.IncreaseSectionSize(e.section);
} while(!eof);
curConfigFile = NULL;
}
bool ConfigFile::SaveTo(const char *filename){
string section;
FILE *fp;
if((fp=fopen(filename, "w"))==NULL){
fprintf(stderr, "Couldn't write conffile ");
perror(filename);
return false;
}
curConfigFile = this;
section.clear();
set<ConfigEntry, ConfigEntry::line_less> tmp;
fprintf(fp, "# Config file output by snes9x\n");
time_t t=time(NULL);
fprintf(fp, "# %s", ctime(&t));
#ifdef SORT_SECTIONS_BY_SIZE
std::set<ConfigEntry, ConfigEntry::section_then_key_less> data2;
for(set<ConfigEntry, ConfigEntry::key_less>::iterator k=data.begin(); k!=data.end(); k++){
ConfigEntry e (k->line, k->section, k->key, k->val); e.comment = k->comment;
data2.insert(e);
}
#else
#define data2 data
#define section_then_key_less key_less
#endif
for(set<ConfigEntry, ConfigEntry::section_then_key_less>::iterator j=data2.begin(); ; j++){
if(j==data2.end() || j->section!=section){
if(!tmp.empty()){
fprintf(fp, "\n[%s]\n", section.c_str());
unsigned int maxKeyLen=0, maxValLen=0; int maxLeftDiv=0; int maxRightDiv=-1;
if(niceAlignment){
for(set<ConfigEntry, ConfigEntry::line_less>::iterator i=tmp.begin(); i!=tmp.end(); i++){
int len3 = i->key.find_last_of(':');
maxRightDiv = MAX(maxRightDiv, len3);
len3 = i->key.length() - len3;
maxLeftDiv = MAX(maxLeftDiv, len3);
maxKeyLen = MAX(maxKeyLen, i->key.length()+3);
if(showComments){
string o=i->val; ConfigEntry::trim(o);
unsigned int len = o.length();
for(signed int j=len-1;j>=0;j--) if(o.at(j)=='#') len++;
if(o!=i->val) len+=2;
maxValLen = MAX(maxValLen, len);
}
}
if(maxValLen>48) maxValLen=48; // limit spacing
}
for(set<ConfigEntry, ConfigEntry::line_less>::iterator i=tmp.begin(); i!=tmp.end(); i++){
string o=i->val; ConfigEntry::trim(o);
if(o!=i->val) o="\""+i->val+"\"";
int off=0, len3=0;
for(;;){
int k=o.find('#',off);
if(k>=0){
o.insert(k,1,'#'); // re-double any comment characters
off=k+2;
if(off<(int)o.length()) continue;
}
break;
}
if(niceAlignment){
len3=i->key.find_last_of(':');
int len3sub=0;
if(len3 < maxRightDiv){
for(int j=len3;j<maxRightDiv;j++) fputc(' ',fp);
len3sub=maxRightDiv-len3;
len3 = maxRightDiv;
}
len3+=maxLeftDiv-i->key.length();
for(unsigned int j=i->key.length()+len3+3;j<maxKeyLen;j++) fputc(' ',fp);
fprintf(fp, "%s", i->key.c_str());
for(int j=0;j<len3-len3sub;j++) fputc(' ',fp);
fprintf(fp, " = %s", o.c_str());
} else
fprintf(fp, "%s = %s", i->key.c_str(), o.c_str());
if(showComments && !i->comment.empty()){
if(niceAlignment) for(unsigned int j=o.length();j<maxValLen;j++) fputc(' ',fp);
fprintf(fp, " # %s", i->comment.c_str());
}
fprintf(fp, "\n");
}
}
if(j==data2.end()) break;
section=j->section;
tmp.clear();
}
tmp.insert(*j);
}
curConfigFile = NULL;
#undef data2
#undef section_then_key_less
if(ferror(fp))
{
printf ("Error writing config file %s\n", filename);
}
fclose(fp);
return true;
}
/***********************************************/
string ConfigFile::Get(const char *key){
set<ConfigEntry, ConfigEntry::key_less>::iterator i;
i=data.find(ConfigEntry(key));
i->used=true;
return i->val;
}
bool ConfigFile::Has(const char *key){
return data.find(ConfigEntry(key))!=data.end();
}
// exists and isn't completely empty (any side-effects are intentional)
bool ConfigFile::Exists(const char *key){
const char* val = GetString(key, NULL);
return val && *val;
}
string ConfigFile::GetString(const char *key, string def){
if(!Exists(key))
return def;
return Get(key);
}
char *ConfigFile::GetString(const char *key, char *out, uint32 outlen){
if(!Exists(key)) return NULL;
memset(out, 0, outlen);
string o=Get(key);
if(outlen>0){
outlen--;
if(o.size()<outlen) outlen=o.size();
memcpy(out, o.data(), outlen);
}
return out;
}
const char *ConfigFile::GetString(const char *key, const char *def){
set<ConfigEntry, ConfigEntry::key_less>::iterator i;
i=data.find(ConfigEntry(key));
if(i==data.end())
{
if(defaultAutoAdd) SetString(key,""); //SetString(key, def?def:"");
return def;
}
i->used=true;
// This should be OK, until this key gets removed
const std::string &iVal = i->val;
return iVal.c_str();
}
char *ConfigFile::GetStringDup(const char *key, const char *def){
const char *c=GetString(key, def);
if(c==NULL) return NULL;
return strdup(c);
}
bool ConfigFile::SetString(const char *key, string val, const char *comment){
set<ConfigEntry, ConfigEntry::key_less>::iterator i;
bool ret=false;
bool found;
ConfigEntry e(key, val);
if(comment && *comment) e.comment = comment;
e.used=true;
i=data.find(e);
found=(i==data.end());
if(!found){
e.line=i->line;
data.erase(e);
sectionSizes.DecreaseSectionSize(e.section);
ret=true;
}
if((found && (!alphaSort || timeSort)) || (!alphaSort && timeSort))
e.line = linectr++;
data.insert(e);
sectionSizes.IncreaseSectionSize(e.section);
return ret;
}
int32 ConfigFile::GetInt(const char *key, int32 def, bool *bad){
if(bad) *bad=false;
if(!Exists(key))
return def;
char *c;
int32 i;
string o=Get(key);
i=strtol(o.c_str(), &c, 10);
if(c!=NULL && *c!='\0'){
i=def;
if(bad) *bad=true;
}
return i;
}
bool ConfigFile::SetInt(const char *key, int32 val, const char *comment){
char buf[20];
snprintf(buf, sizeof(buf), "%d", (int)val);
return SetString(key, buf, comment);
}
uint32 ConfigFile::GetUInt(const char *key, uint32 def, int base, bool *bad){
if(bad) *bad=false;
if(!Exists(key))
return def;
if(base!=8 && base!=10 && base!=16) base=0;
char *c;
uint32 i;
string o=Get(key);
i=strtol(o.c_str(), &c, base);
if(c!=NULL && *c!='\0'){
i=def;
if(bad) *bad=true;
}
return i;
}
bool ConfigFile::SetUInt(const char *key, uint32 val, int base, const char *comment){
char buf[20];
switch(base){
case 10:
default:
snprintf(buf, sizeof(buf), "%u", (unsigned int)val);
break;
case 8:
snprintf(buf, sizeof(buf), "%#o", (unsigned int)val);
break;
case 16:
snprintf(buf, sizeof(buf), "%#x", (unsigned int)val);
break;
}
return SetString(key, buf, comment);
}
bool ConfigFile::GetBool(const char *key, bool def, bool *bad){
if(bad) *bad=false;
if(!Exists(key))
return def;
string o=Get(key);
const char *c=o.c_str();
if(!strcasecmp(c, "true") || !strcasecmp(c, "1") || !strcasecmp(c, "yes") || !strcasecmp(c, "on")) return true;
if(!strcasecmp(c, "false") || !strcasecmp(c, "0") || !strcasecmp(c, "no") || !strcasecmp(c, "off")) return false;
if(bad) *bad=true;
return def;
}
bool ConfigFile::SetBool(const char *key, bool val, const char *true_val, const char *false_val, const char *comment){
return SetString(key, val?true_val:false_val, comment);
}
const char* ConfigFile::GetComment(const char *key)
{
set<ConfigEntry, ConfigEntry::key_less>::iterator i;
i=data.find(ConfigEntry(key));
if(i==data.end())
return NULL;
// This should be OK, until this key gets removed
const std::string &iCom = i->comment;
return iCom.c_str();
}
bool ConfigFile::DeleteKey(const char *key){
ConfigEntry e = ConfigEntry(key);
if(data.erase(e)) {
sectionSizes.DecreaseSectionSize(e.section);
return true;
}
return false;
}
/***********************************************/
bool ConfigFile::DeleteSection(const char *section){
set<ConfigEntry, ConfigEntry::key_less>::iterator s, e;
for(s=data.begin(); s!=data.end() && s->section!=section; s++) ;
if(s==data.end()) return false;
for(e=s; e!=data.end() && e->section==section; e++) ;
data.erase(s, e);
sectionSizes.DeleteSection(section);
return true;
}
ConfigFile::secvec_t ConfigFile::GetSection(const char *section){
secvec_t v;
set<ConfigEntry, ConfigEntry::key_less>::iterator i;
v.clear();
for(i=data.begin(); i!=data.end(); i++){
if(i->section!=section) continue;
v.push_back(std::pair<string,string>(i->key, i->val));
}
return v;
}
int ConfigFile::GetSectionSize(const std::string section){
return sectionSizes.GetSectionSize(section);
}
// Clears all key-value pairs that didn't receive a "Get" or "Exists" command
void ConfigFile::ClearUnused()
{
set<ConfigEntry, ConfigEntry::key_less>::iterator i;
again:
for(i=data.begin(); i!=data.end(); i++){
if(!i->used){
data.erase(i);
goto again;
}
}
}
void ConfigFile::ClearLines()
{
set<ConfigEntry, ConfigEntry::key_less>::iterator i;
for(i=data.begin(); i!=data.end(); i++){
*(const_cast<int*>(&i->line)) = -1;
}
}
bool ConfigFile::ConfigEntry::section_then_key_less::operator()(const ConfigEntry &a, const ConfigEntry &b) const{
if(curConfigFile && a.section!=b.section){
const int sva = curConfigFile->GetSectionSize(a.section);
const int svb = curConfigFile->GetSectionSize(b.section);
if(sva<svb) return true;
if(sva>svb) return false;
return a.section<b.section;
}
return a.key<b.key;
}
void ConfigFile::SetDefaultAutoAdd(bool autoAdd)
{
defaultAutoAdd = autoAdd;
}
void ConfigFile::SetNiceAlignment(bool align)
{
niceAlignment = align;
}
void ConfigFile::SetShowComments(bool show)
{
showComments = show;
}
void ConfigFile::SetAlphaSort(bool sort)
{
alphaSort = sort;
}
void ConfigFile::SetTimeSort(bool sort)
{
timeSort = sort;
}

@ -0,0 +1,282 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _CONFIG_H_
#define _CONFIG_H_
#include <set>
#include <map>
#include <vector>
#include <string>
#ifdef UNZIP_SUPPORT
# ifdef SYSTEM_ZIP
# include <minizip/unzip.h>
# else
# include "unzip/unzip.h"
# endif
#endif
#include "snes9x.h"
#ifndef MAX
# define MAX(a,b) ((a) > (b)? (a) : (b))
# define MIN(a,b) ((a) < (b)? (a) : (b))
#endif
class ConfigFile {
public:
ConfigFile(void);
void Clear(void);
// return false on failure
bool LoadFile(const char *filename);
void LoadFile(Stream *r, const char *name=NULL);
// return false if key does not exist or is empty
bool Exists(const char *key);
// return the value / default
std::string GetString(const char *key, std::string def);
char *GetString(const char *key, char *out, uint32 outlen); // return NULL if it doesn't exist, out not affected
const char *GetString(const char *key, const char *def=NULL); // NOTE: returned pointer becomes invalid when key is deleted/modified, or the ConfigFile is Clear()ed or deleted.
char *GetStringDup(const char *key, const char *def=NULL); // Much like "strdup(GetString(key, def))"
int32 GetInt(const char *key, int32 def=-1, bool *bad=NULL);
uint32 GetUInt(const char *key, uint32 def=0, int base=0, bool *bad=NULL); // base = 0, 8, 10, or 16
bool GetBool(const char *key, bool def=false, bool *bad=NULL);
const char* GetComment(const char *key); // NOTE: returned pointer becomes invalid when key is deleted/modified, or the ConfigFile is Clear()ed or deleted.
// return true if the key existed prior to setting
bool SetString(const char *key, std::string val, const char *comment="");
bool SetInt(const char *key, int32 val, const char *comment="");
bool SetUInt(const char *key, uint32 val, int base=10, const char *comment=""); // base = 8, 10, or 16
bool SetBool(const char *key, bool val, const char *true_val="TRUE", const char *false_val="FALSE", const char *comment="");
bool DeleteKey(const char *key);
// Operation on entire sections
bool DeleteSection(const char *section);
typedef std::vector<std::pair<std::string,std::string> > secvec_t;
secvec_t GetSection(const char *section);
int GetSectionSize(const std::string section);
// Clears all key-value pairs that didn't receive a Set command, or a Get command with autoAdd on
void ClearUnused(void);
// Clears all stored line numbers
void ClearLines(void);
bool SaveTo(const char *filename);
static void SetDefaultAutoAdd(bool autoAdd);
static void SetNiceAlignment(bool align);
static void SetShowComments(bool show);
static void SetAlphaSort(bool sort);
static void SetTimeSort(bool sort);
private:
std::string Get(const char *key);
bool Has(const char *key);
class ConfigEntry {
protected:
int line;
std::string section;
std::string key;
std::string val;
std::string comment;
mutable bool used;
struct section_then_key_less {
bool operator()(const ConfigEntry &a, const ConfigEntry &b) const;
};
struct key_less {
bool operator()(const ConfigEntry &a, const ConfigEntry &b) const{
if(a.section!=b.section) return a.section<b.section;
return a.key<b.key;
}
};
struct line_less {
bool operator()(const ConfigEntry &a, const ConfigEntry &b) const{
if(a.line==b.line) return (b.val.empty() && !a.val.empty()) || a.key<b.key;
if(b.line<0) return true;
if(a.line<0) return false;
return a.line<b.line;
}
};
static void trim(std::string &s){
int i;
i=s.find_first_not_of(" \f\n\r\t\v");
if(i==-1){
s.clear();
return;
}
if(i>0) s.erase(0, i); // erase leading whitespace
i=s.find_last_not_of(" \f\n\r\t\v");
if(i!=-1) s.erase(i+1); // erase trailing whitespace
return;
}
// trims comments and leading/trailing whitespace from s, and returns any trimmed comments
// make sure not to call this more than once on the same string
static std::string trimCommented(std::string &s){
std::string cmt;
int i;
i=s.find_first_not_of(" \f\n\r\t\v");
if(i==-1){
s.clear();
return cmt;
}
if(i>0) s.erase(0, i); // erase leading whitespace
int off=0;
for(;;){
i=s.find('#',off); // find trailing comment
if(i>=0)
{
if((int)s.length()>i+1 && s.at(i+1) == '#') {
s.erase(i,1); // ignore ## and change to #
off = i+1;
continue;
} else {
int j=s.find_first_not_of(" \f\n\r\t\v",i+1);
if(j!=-1) cmt = s.substr(j); // store
s.erase(i); // erase trailing comment
}
}
break;
}
i=s.find_last_not_of(" \f\n\r\t\v");
if(i!=-1) s.erase(i+1); // erase trailing whitespace
return cmt;
}
public:
ConfigEntry(int l, const std::string &s, const std::string &k, const std::string &v) :
line(l), section(s), key(k), val(v) {
trim(section);
trim(key);
used=false;
}
void parse_key(const std::string &k){
int i=k.find("::");
if(i==-1){
section="Uncategorized"; key=k;
} else {
section=k.substr(0,i); key=k.substr(i+2);
}
trim(section);
trim(key);
used=false;
}
ConfigEntry(const std::string k){
parse_key(k);
}
ConfigEntry(const std::string k, const std::string &v) : line(-1), val(v) {
parse_key(k);
}
friend class ConfigFile;
friend struct key_less;
friend struct line_less;
};
class SectionSizes {
protected:
std::map<std::string,uint32> sections;
public:
uint32 GetSectionSize(const std::string section) {
uint32 count=0;
uint32 seclen;
std::map<std::string,uint32>::iterator it;
for(it=sections.begin(); it!=sections.end(); it++) {
seclen = MIN(section.size(),it->first.size());
if(it->first==section || !section.compare(0,seclen,it->first,0,seclen)) count+=it->second;
}
return count;
}
void IncreaseSectionSize(const std::string section) {
std::map<std::string,uint32>::iterator it=sections.find(section);
if(it!=sections.end())
it->second++;
else
sections.insert(std::pair<std::string,uint32>(section,1));
}
void DecreaseSectionSize(const std::string section) {
std::map<std::string,uint32>::iterator it=sections.find(section);
if(it!=sections.end())
it->second--;
}
void ClearSections() {
sections.clear();
}
void DeleteSection(const std::string section) {
sections.erase(section);
}
};
std::set<ConfigEntry, ConfigEntry::key_less> data;
SectionSizes sectionSizes;
int linectr;
static bool defaultAutoAdd;
static bool niceAlignment;
static bool showComments;
static bool alphaSort;
static bool timeSort;
};
/* Config file format:
*
* Comments are any lines whose first non-whitespace character is ';' or '#'.
* Note that comments can also follow a value, on the same line.
* To intentionally have a '#' character in the value, use ##
*
* All parameters fall into sections. To name a section, the first
* non-whitespace character on the line will be '[', and the last will be ']'.
*
* Parameters are simple key=value pairs. Whitespace around the '=', and at the
* beginning or end of the line is ignored. Key names may not contain '=' nor
* begin with '[', however values can. If the last character of the value is
* '\', the next line (sans leading/trailing whitespace) is considered part of
* the value as well. Programmatically, the key "K" in section "S" is referred
* to as "S::K", much like C++ namespaces. For example:
* [Section1]
* # this is a comment
* foo = bar \
* baz\
* quux \
* ## this is not a comment! # this IS a comment
* means the value of "Section1::foo" is "bar bazquux # this is not a comment!"
*
* Parameters may be of several types:
* String - Bare characters. If the first and last characters are both '"',
* they are removed (so just double them if you really want quotes
* there)
* Int - A decimal number from -2147483648 to 2147483647
* UInt - A number in decimal, hex, or octal from 0 to 4294967295 (or
* 0xffffffff, or 037777777777)
* Bool - true/false, 0/1, on/off, yes/no
*
* Of course, the actual accepted values for a parameter may be further
* restricted ;)
*/
/* You must write this for your port */
void S9xParsePortConfig(ConfigFile &, int pass);
/* This may or may not be useful to you */
const char *S9xParseDisplayConfig(ConfigFile &, int pass);
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,291 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _CONTROLS_H_
#define _CONTROLS_H_
#define S9xNoMapping 0
#define S9xButtonJoypad 1
#define S9xButtonMouse 2
#define S9xButtonSuperscope 3
#define S9xButtonJustifier 4
#define S9xButtonCommand 5
#define S9xButtonMulti 6
#define S9xButtonMacsRifle 7
#define S9xAxisJoypad 8
#define S9xPointer 9
#define S9xButtonPseudopointer 254
#define S9xAxisPseudopointer 253
#define S9xAxisPseudobuttons 252
// These are automatically kicked out to the S9xHandlePortCommand function.
// If your port wants to define port-specific commands or whatever, use these values for the s9xcommand_t type field.
#define S9xButtonPort 251
#define S9xAxisPort 250
#define S9xPointerPort 249
#define S9xBadMapping 255
#define InvalidControlID ((uint32) -1)
// S9xButtonPseudopointer and S9xAxisPseudopointer will report pointer motion using IDs PseudoPointerBase through PseudoPointerBase+7.
// S9xAxisPseudopointer command types. S9xAxisPseudobuttons will report buttons with IDs PseudoButtonBase to PseudoButtonBase+255.
#define PseudoPointerBase (InvalidControlID - 8)
#define PseudoButtonBase (PseudoPointerBase - 256)
typedef struct
{
uint8 type;
uint8 multi_press:2;
uint8 button_norpt:1;
union
{
union
{
struct
{
uint8 idx:3; // Pad number 0-7
uint8 toggle:1; // If set, toggle turbo/sticky for the button
uint8 turbo:1; // If set, be a 'turbo' button
uint8 sticky:1; // If set, toggle button state (on/turbo or off) when pressed and do nothing on release
uint16 buttons; // Which buttons to actuate. Use SNES_*_MASK constants from snes9x.h
} joypad;
struct
{
uint8 idx:1; // Mouse number 0-1
uint8 left:1; // buttons
uint8 right:1;
} mouse;
struct
{
uint8 fire:1;
uint8 cursor:1;
uint8 turbo:1;
uint8 pause:1;
uint8 aim_offscreen:1; // Pretend we're pointing the gun offscreen (ignore the pointer)
} scope;
struct
{
uint8 idx:3; // Pseudo-pointer number 0-7
uint8 speed_type:2; // 0=variable, 1=slow, 2=med, 3=fast
int8 UD:2; // -1=up, 1=down, 0=no vertical motion
int8 LR:2; // -1=left, 1=right, 0=no horizontal motion
} pointer;
struct
{
uint8 idx:1; // Justifier number 0-1
uint8 trigger:1; // buttons
uint8 start:1;
uint8 aim_offscreen:1; // Pretend we're pointing the gun offscreen (ignore the pointer)
} justifier;
struct
{
uint8 trigger:1;
} macsrifle;
int32 multi_idx;
uint16 command;
} button;
union
{
struct
{
uint8 idx:3; // Pad number 0-7
uint8 invert:1; // 1 = positive is Left/Up/Y/X/L
uint8 axis:3; // 0=Left/Right, 1=Up/Down, 2=Y/A, 3=X/B, 4=L/R
uint8 threshold; // (threshold+1)/256% deflection is a button press
} joypad;
struct
{
uint8 idx:3; // Pseudo-pointer number 0-7
uint8 speed_type:2; // 0=variable, 1=slow, 2=med, 3=fast
uint8 invert:1; // 1 = invert axis, so positive is up/left
uint8 HV:1; // 0=horizontal, 1=vertical
} pointer;
struct
{
uint8 threshold; // (threshold+1)/256% deflection is a button press
uint8 negbutton; // Button ID for negative deflection
uint8 posbutton; // Button ID for positive deflection
} button;
} axis;
struct // Which SNES-pointers to control with this pointer
{
uint16 aim_mouse0:1;
uint16 aim_mouse1:1;
uint16 aim_scope:1;
uint16 aim_justifier0:1;
uint16 aim_justifier1:1;
uint16 aim_macsrifle:1;
} pointer;
uint8 port[4];
};
} s9xcommand_t;
// Starting out...
void S9xUnmapAllControls (void);
// Setting which controllers are plugged in.
enum controllers
{
CTL_NONE, // all ids ignored
CTL_JOYPAD, // use id1 to specify 0-7
CTL_MOUSE, // use id1 to specify 0-1
CTL_SUPERSCOPE,
CTL_JUSTIFIER, // use id1: 0=one justifier, 1=two justifiers
CTL_MP5, // use id1-id4 to specify pad 0-7 (or -1)
CTL_MACSRIFLE
};
void S9xSetController (int port, enum controllers controller, int8 id1, int8 id2, int8 id3, int8 id4); // port=0-1
void S9xGetController (int port, enum controllers *controller, int8 *id1, int8 *id2, int8 *id3, int8 *id4);
void S9xReportControllers (void);
// Call this when you're done with S9xSetController, or if you change any of the controller Settings.*Master flags.
// Returns true if something was disabled.
bool S9xVerifyControllers (void);
// Functions for translation s9xcommand_t's into strings, and vice versa.
// free() the returned string after you're done with it.
char * S9xGetCommandName (s9xcommand_t command);
s9xcommand_t S9xGetCommandT (const char *name);
// Returns an array of strings naming all the snes9x commands.
// Note that this is only the strings for S9xButtonCommand!
// The idea is that this would be used for a pull-down list in a config GUI. DO NOT free() the returned value.
const char ** S9xGetAllSnes9xCommands (void);
// Generic mapping functions
s9xcommand_t S9xGetMapping (uint32 id);
void S9xUnmapID (uint32 id);
// Button mapping functions.
// If a button is mapped with poll=TRUE, then S9xPollButton will be called whenever snes9x feels a need for that mapping.
// Otherwise, snes9x will assume you will call S9xReportButton() whenever the button state changes.
// S9xMapButton() will fail and return FALSE if mapping.type isn't an S9xButton* type.
bool S9xMapButton (uint32 id, s9xcommand_t mapping, bool poll);
void S9xReportButton (uint32 id, bool pressed);
// Pointer mapping functions.
// If a pointer is mapped with poll=TRUE, then S9xPollPointer will be called whenever snes9x feels a need for that mapping.
// Otherwise, snes9x will assume you will call S9xReportPointer() whenever the pointer position changes.
// S9xMapPointer() will fail and return FALSE if mapping.type isn't an S9xPointer* type.
// Note that position [0,0] is considered the upper-left corner of the 'screen',
// and either [255,223] or [255,239] is the lower-right.
// Note that the SNES mouse doesn't aim at a particular point,
// so the SNES's idea of where the mouse pointer is will probably differ from your OS's idea.
bool S9xMapPointer (uint32 id, s9xcommand_t mapping, bool poll);
void S9xReportPointer (uint32 id, int16 x, int16 y);
// Axis mapping functions.
// If an axis is mapped with poll=TRUE, then S9xPollAxis will be called whenever snes9x feels a need for that mapping.
// Otherwise, snes9x will assume you will call S9xReportAxis() whenever the axis deflection changes.
// S9xMapAxis() will fail and return FALSE if mapping.type isn't an S9xAxis* type.
// Note that value is linear -32767 through 32767 with 0 being no deflection.
// If your axis reports differently you should transform the value before passing it to S9xReportAxis().
bool S9xMapAxis (uint32 id, s9xcommand_t mapping, bool poll);
void S9xReportAxis (uint32 id, int16 value);
// Do whatever the s9xcommand_t says to do.
// If cmd.type is a button type, data1 should be TRUE (non-0) or FALSE (0) to indicate whether the 'button' is pressed or released.
// If cmd.type is an axis, data1 holds the deflection value.
// If cmd.type is a pointer, data1 and data2 are the positions of the pointer.
void S9xApplyCommand (s9xcommand_t cmd, int16 data1, int16 data2);
//////////
// These functions are called by snes9x into your port, so each port should implement them.
// If something was mapped with poll=TRUE, these functions will be called when snes9x needs the button/axis/pointer state.
// Fill in the reference options as appropriate.
bool S9xPollButton (uint32 id, bool *pressed);
bool S9xPollPointer (uint32 id, int16 *x, int16 *y);
bool S9xPollAxis (uint32 id, int16 *value);
// These are called when snes9x tries to apply a command with a S9x*Port type.
// data1 and data2 are filled in like S9xApplyCommand.
void S9xHandlePortCommand (s9xcommand_t cmd, int16 data1, int16 data2);
// Called before already-read SNES joypad data is being used by the game if your port defines SNES_JOY_READ_CALLBACKS.
#ifdef SNES_JOY_READ_CALLBACKS
void S9xOnSNESPadRead (void);
#endif
// These are for your use.
s9xcommand_t S9xGetPortCommandT (const char *name);
char * S9xGetPortCommandName (s9xcommand_t command);
void S9xSetupDefaultKeymap (void);
bool8 S9xMapInput (const char *name, s9xcommand_t *cmd);
//////////
// These functions are called from snes9x into this subsystem. No need to use them from a port.
// Use when resetting snes9x.
void S9xControlsReset (void);
void S9xControlsSoftReset (void);
// Use when writing to $4016.
void S9xSetJoypadLatch (bool latch);
// Use when reading $4016/7 (JOYSER0 and JOYSER1).
uint8 S9xReadJOYSERn (int n);
// End-Of-Frame processing. Sets gun latch variables and tries to draw crosshairs
void S9xControlEOF (void);
// Functions and a structure for snapshot.
struct SControlSnapshot
{
uint8 ver;
uint8 port1_read_idx[2];
uint8 dummy1[4]; // for future expansion
uint8 port2_read_idx[2];
uint8 dummy2[4];
uint8 mouse_speed[2];
uint8 justifier_select;
uint8 dummy3[8];
bool8 pad_read, pad_read_last;
uint8 internal[60]; // yes, we need to save this!
uint8 internal_macs[5];
};
void S9xControlPreSaveState (struct SControlSnapshot *s);
void S9xControlPostLoadState (struct SControlSnapshot *s);
#endif

@ -0,0 +1,172 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "snes9x.h"
#include "memmap.h"
#include "dma.h"
#include "apu/apu.h"
#include "fxemu.h"
#include "sdd1.h"
#include "srtc.h"
#include "snapshot.h"
#include "cheats.h"
#include "logger.h"
#ifdef DEBUGGER
#include "debug.h"
#endif
static void S9xResetCPU (void);
static void S9xSoftResetCPU (void);
static void S9xResetCPU (void)
{
S9xSoftResetCPU();
Registers.SL = 0xff;
Registers.P.W = 0;
Registers.A.W = 0;
Registers.X.W = 0;
Registers.Y.W = 0;
SetFlags(MemoryFlag | IndexFlag | IRQ | Emulation);
ClearFlags(Decimal);
}
static void S9xSoftResetCPU (void)
{
CPU.Cycles = 182; // Or 188. This is the cycle count just after the jump to the Reset Vector.
CPU.PrevCycles = CPU.Cycles;
CPU.V_Counter = 0;
CPU.Flags = CPU.Flags & (DEBUG_MODE_FLAG | TRACE_FLAG);
CPU.PCBase = NULL;
CPU.NMIPending = FALSE;
CPU.IRQLine = FALSE;
CPU.IRQTransition = FALSE;
CPU.IRQExternal = FALSE;
CPU.MemSpeed = SLOW_ONE_CYCLE;
CPU.MemSpeedx2 = SLOW_ONE_CYCLE * 2;
CPU.FastROMSpeed = SLOW_ONE_CYCLE;
CPU.InDMA = FALSE;
CPU.InHDMA = FALSE;
CPU.InDMAorHDMA = FALSE;
CPU.InWRAMDMAorHDMA = FALSE;
CPU.HDMARanInDMA = 0;
CPU.CurrentDMAorHDMAChannel = -1;
CPU.WhichEvent = HC_RENDER_EVENT;
CPU.NextEvent = Timings.RenderPos;
CPU.WaitingForInterrupt = FALSE;
CPU.AutoSaveTimer = 0;
CPU.SRAMModified = FALSE;
Registers.PBPC = 0;
Registers.PB = 0;
Registers.PCw = S9xGetWord(0xfffc);
OpenBus = Registers.PCh;
Registers.D.W = 0;
Registers.DB = 0;
Registers.SH = 1;
Registers.SL -= 3;
Registers.XH = 0;
Registers.YH = 0;
ICPU.ShiftedPB = 0;
ICPU.ShiftedDB = 0;
SetFlags(MemoryFlag | IndexFlag | IRQ | Emulation);
ClearFlags(Decimal);
Timings.InterlaceField = FALSE;
Timings.H_Max = Timings.H_Max_Master;
Timings.V_Max = Timings.V_Max_Master;
Timings.NMITriggerPos = 0xffff;
Timings.NextIRQTimer = 0x0fffffff;
Timings.IRQFlagChanging = IRQ_NONE;
if (Model->_5A22 == 2)
Timings.WRAMRefreshPos = SNES_WRAM_REFRESH_HC_v2;
else
Timings.WRAMRefreshPos = SNES_WRAM_REFRESH_HC_v1;
S9xSetPCBase(Registers.PBPC);
ICPU.S9xOpcodes = S9xOpcodesE1;
ICPU.S9xOpLengths = S9xOpLengthsM1X1;
S9xUnpackStatus();
}
void S9xReset (void)
{
S9xResetSaveTimer(FALSE);
S9xResetLogger();
memset(Memory.RAM, 0x55, 0x20000);
memset(Memory.VRAM, 0x00, 0x10000);
memset(Memory.FillRAM, 0, 0x8000);
S9xResetBSX();
S9xResetCPU();
S9xResetPPU();
S9xResetDMA();
S9xResetAPU();
S9xResetMSU();
if (Settings.DSP)
S9xResetDSP();
if (Settings.SuperFX)
S9xResetSuperFX();
if (Settings.SA1)
S9xSA1Init();
if (Settings.SDD1)
S9xResetSDD1();
if (Settings.SPC7110)
S9xResetSPC7110();
if (Settings.C4)
S9xInitC4();
if (Settings.OBC1)
S9xResetOBC1();
if (Settings.SRTC)
S9xResetSRTC();
if (Settings.MSU1)
S9xMSU1Init();
S9xInitCheatData();
}
void S9xSoftReset (void)
{
S9xResetSaveTimer(FALSE);
memset(Memory.FillRAM, 0, 0x8000);
if (Settings.BS)
S9xResetBSX();
S9xSoftResetCPU();
S9xSoftResetPPU();
S9xResetDMA();
S9xSoftResetAPU();
S9xResetMSU();
if (Settings.DSP)
S9xResetDSP();
if (Settings.SuperFX)
S9xResetSuperFX();
if (Settings.SA1)
S9xSA1Init();
if (Settings.SDD1)
S9xResetSDD1();
if (Settings.SPC7110)
S9xResetSPC7110();
if (Settings.C4)
S9xInitC4();
if (Settings.OBC1)
S9xResetOBC1();
if (Settings.SRTC)
S9xResetSRTC();
if (Settings.MSU1)
S9xMSU1Init();
S9xInitCheatData();
}

@ -0,0 +1,521 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _CPUADDR_H_
#define _CPUADDR_H_
typedef enum
{
NONE = 0,
READ = 1,
WRITE = 2,
MODIFY = 3,
JUMP = 5,
JSR = 8
} AccessMode;
static inline uint8 Immediate8Slow (AccessMode a)
{
uint8 val = S9xGetByte(Registers.PBPC);
if (a & READ)
OpenBus = val;
Registers.PCw++;
return (val);
}
static inline uint8 Immediate8 (AccessMode a)
{
uint8 val = CPU.PCBase[Registers.PCw];
if (a & READ)
OpenBus = val;
AddCycles(CPU.MemSpeed);
Registers.PCw++;
return (val);
}
static inline uint16 Immediate16Slow (AccessMode a)
{
uint16 val = S9xGetWord(Registers.PBPC, WRAP_BANK);
if (a & READ)
OpenBus = (uint8) (val >> 8);
Registers.PCw += 2;
return (val);
}
static inline uint16 Immediate16 (AccessMode a)
{
uint16 val = READ_WORD(CPU.PCBase + Registers.PCw);
if (a & READ)
OpenBus = (uint8) (val >> 8);
AddCycles(CPU.MemSpeedx2);
Registers.PCw += 2;
return (val);
}
static inline uint32 RelativeSlow (AccessMode a) // branch $xx
{
int8 offset = Immediate8Slow(a);
return ((int16) Registers.PCw + offset) & 0xffff;
}
static inline uint32 Relative (AccessMode a) // branch $xx
{
int8 offset = Immediate8(a);
return ((int16) Registers.PCw + offset) & 0xffff;
}
static inline uint32 RelativeLongSlow (AccessMode a) // BRL $xxxx
{
int16 offset = Immediate16Slow(a);
return ((int32) Registers.PCw + offset) & 0xffff;
}
static inline uint32 RelativeLong (AccessMode a) // BRL $xxxx
{
int16 offset = Immediate16(a);
return ((int32) Registers.PCw + offset) & 0xffff;
}
static inline uint32 AbsoluteIndexedIndirectSlow (AccessMode a) // (a,X)
{
uint16 addr;
if (a & JSR)
{
// JSR (a,X) pushes the old address in the middle of loading the new.
// OpenBus needs to be set to account for this.
addr = Immediate8Slow(READ);
if (a == JSR)
OpenBus = Registers.PCl;
addr |= Immediate8Slow(READ) << 8;
}
else
addr = Immediate16Slow(READ);
AddCycles(ONE_CYCLE);
addr += Registers.X.W;
// Address load wraps within the bank
uint16 addr2 = S9xGetWord(ICPU.ShiftedPB | addr, WRAP_BANK);
OpenBus = addr2 >> 8;
return (addr2);
}
static inline uint32 AbsoluteIndexedIndirect (AccessMode a) // (a,X)
{
uint16 addr = Immediate16Slow(READ);
AddCycles(ONE_CYCLE);
addr += Registers.X.W;
// Address load wraps within the bank
uint16 addr2 = S9xGetWord(ICPU.ShiftedPB | addr, WRAP_BANK);
OpenBus = addr2 >> 8;
return (addr2);
}
static inline uint32 AbsoluteIndirectLongSlow (AccessMode a) // [a]
{
uint16 addr = Immediate16Slow(READ);
// No info on wrapping, but it doesn't matter anyway due to mirroring
uint32 addr2 = S9xGetWord(addr);
OpenBus = addr2 >> 8;
addr2 |= (OpenBus = S9xGetByte(addr + 2)) << 16;
return (addr2);
}
static inline uint32 AbsoluteIndirectLong (AccessMode a) // [a]
{
uint16 addr = Immediate16(READ);
// No info on wrapping, but it doesn't matter anyway due to mirroring
uint32 addr2 = S9xGetWord(addr);
OpenBus = addr2 >> 8;
addr2 |= (OpenBus = S9xGetByte(addr + 2)) << 16;
return (addr2);
}
static inline uint32 AbsoluteIndirectSlow (AccessMode a) // (a)
{
// No info on wrapping, but it doesn't matter anyway due to mirroring
uint16 addr2 = S9xGetWord(Immediate16Slow(READ));
OpenBus = addr2 >> 8;
return (addr2);
}
static inline uint32 AbsoluteIndirect (AccessMode a) // (a)
{
// No info on wrapping, but it doesn't matter anyway due to mirroring
uint16 addr2 = S9xGetWord(Immediate16(READ));
OpenBus = addr2 >> 8;
return (addr2);
}
static inline uint32 AbsoluteSlow (AccessMode a) // a
{
return (ICPU.ShiftedDB | Immediate16Slow(a));
}
static inline uint32 Absolute (AccessMode a) // a
{
return (ICPU.ShiftedDB | Immediate16(a));
}
static inline uint32 AbsoluteLongSlow (AccessMode a) // l
{
uint32 addr = Immediate16Slow(READ);
// JSR l pushes the old bank in the middle of loading the new.
// OpenBus needs to be set to account for this.
if (a == JSR)
OpenBus = Registers.PB;
addr |= Immediate8Slow(a) << 16;
return (addr);
}
static inline uint32 AbsoluteLong (AccessMode a) // l
{
uint32 addr = READ_3WORD(CPU.PCBase + Registers.PCw);
AddCycles(CPU.MemSpeedx2 + CPU.MemSpeed);
if (a & READ)
OpenBus = addr >> 16;
Registers.PCw += 3;
return (addr);
}
static inline uint32 DirectSlow (AccessMode a) // d
{
uint16 addr = Immediate8Slow(a) + Registers.D.W;
if (Registers.DL != 0)
AddCycles(ONE_CYCLE);
return (addr);
}
static inline uint32 Direct (AccessMode a) // d
{
uint16 addr = Immediate8(a) + Registers.D.W;
if (Registers.DL != 0)
AddCycles(ONE_CYCLE);
return (addr);
}
static inline uint32 DirectIndirectSlow (AccessMode a) // (d)
{
uint32 addr = S9xGetWord(DirectSlow(READ), (!CheckEmulation() || Registers.DL) ? WRAP_BANK : WRAP_PAGE);
if (a & READ)
OpenBus = (uint8) (addr >> 8);
addr |= ICPU.ShiftedDB;
return (addr);
}
static inline uint32 DirectIndirectE0 (AccessMode a) // (d)
{
uint32 addr = S9xGetWord(Direct(READ));
if (a & READ)
OpenBus = (uint8) (addr >> 8);
addr |= ICPU.ShiftedDB;
return (addr);
}
static inline uint32 DirectIndirectE1 (AccessMode a) // (d)
{
uint32 addr = S9xGetWord(DirectSlow(READ), Registers.DL ? WRAP_BANK : WRAP_PAGE);
if (a & READ)
OpenBus = (uint8) (addr >> 8);
addr |= ICPU.ShiftedDB;
return (addr);
}
static inline uint32 DirectIndirectIndexedSlow (AccessMode a) // (d),Y
{
uint32 addr = DirectIndirectSlow(a);
if (a & WRITE || !CheckIndex() || (addr & 0xff) + Registers.YL >= 0x100)
AddCycles(ONE_CYCLE);
return (addr + Registers.Y.W);
}
static inline uint32 DirectIndirectIndexedE0X0 (AccessMode a) // (d),Y
{
uint32 addr = DirectIndirectE0(a);
AddCycles(ONE_CYCLE);
return (addr + Registers.Y.W);
}
static inline uint32 DirectIndirectIndexedE0X1 (AccessMode a) // (d),Y
{
uint32 addr = DirectIndirectE0(a);
if (a & WRITE || (addr & 0xff) + Registers.YL >= 0x100)
AddCycles(ONE_CYCLE);
return (addr + Registers.Y.W);
}
static inline uint32 DirectIndirectIndexedE1 (AccessMode a) // (d),Y
{
uint32 addr = DirectIndirectE1(a);
if (a & WRITE || (addr & 0xff) + Registers.YL >= 0x100)
AddCycles(ONE_CYCLE);
return (addr + Registers.Y.W);
}
static inline uint32 DirectIndirectLongSlow (AccessMode a) // [d]
{
uint16 addr = DirectSlow(READ);
uint32 addr2 = S9xGetWord(addr);
OpenBus = addr2 >> 8;
addr2 |= (OpenBus = S9xGetByte(addr + 2)) << 16;
return (addr2);
}
static inline uint32 DirectIndirectLong (AccessMode a) // [d]
{
uint16 addr = Direct(READ);
uint32 addr2 = S9xGetWord(addr);
OpenBus = addr2 >> 8;
addr2 |= (OpenBus = S9xGetByte(addr + 2)) << 16;
return (addr2);
}
static inline uint32 DirectIndirectIndexedLongSlow (AccessMode a) // [d],Y
{
return (DirectIndirectLongSlow(a) + Registers.Y.W);
}
static inline uint32 DirectIndirectIndexedLong (AccessMode a) // [d],Y
{
return (DirectIndirectLong(a) + Registers.Y.W);
}
static inline uint32 DirectIndexedXSlow (AccessMode a) // d,X
{
pair addr;
addr.W = DirectSlow(a);
if (!CheckEmulation() || Registers.DL)
addr.W += Registers.X.W;
else
addr.B.l += Registers.XL;
AddCycles(ONE_CYCLE);
return (addr.W);
}
static inline uint32 DirectIndexedXE0 (AccessMode a) // d,X
{
uint16 addr = Direct(a) + Registers.X.W;
AddCycles(ONE_CYCLE);
return (addr);
}
static inline uint32 DirectIndexedXE1 (AccessMode a) // d,X
{
if (Registers.DL)
return (DirectIndexedXE0(a));
else
{
pair addr;
addr.W = Direct(a);
addr.B.l += Registers.XL;
AddCycles(ONE_CYCLE);
return (addr.W);
}
}
static inline uint32 DirectIndexedYSlow (AccessMode a) // d,Y
{
pair addr;
addr.W = DirectSlow(a);
if (!CheckEmulation() || Registers.DL)
addr.W += Registers.Y.W;
else
addr.B.l += Registers.YL;
AddCycles(ONE_CYCLE);
return (addr.W);
}
static inline uint32 DirectIndexedYE0 (AccessMode a) // d,Y
{
uint16 addr = Direct(a) + Registers.Y.W;
AddCycles(ONE_CYCLE);
return (addr);
}
static inline uint32 DirectIndexedYE1 (AccessMode a) // d,Y
{
if (Registers.DL)
return (DirectIndexedYE0(a));
else
{
pair addr;
addr.W = Direct(a);
addr.B.l += Registers.YL;
AddCycles(ONE_CYCLE);
return (addr.W);
}
}
static inline uint32 DirectIndexedIndirectSlow (AccessMode a) // (d,X)
{
uint32 addr = S9xGetWord(DirectIndexedXSlow(READ), (!CheckEmulation() || Registers.DL) ? WRAP_BANK : WRAP_PAGE);
if (a & READ)
OpenBus = (uint8) (addr >> 8);
return (ICPU.ShiftedDB | addr);
}
static inline uint32 DirectIndexedIndirectE0 (AccessMode a) // (d,X)
{
uint32 addr = S9xGetWord(DirectIndexedXE0(READ));
if (a & READ)
OpenBus = (uint8) (addr >> 8);
return (ICPU.ShiftedDB | addr);
}
static inline uint32 DirectIndexedIndirectE1 (AccessMode a) // (d,X)
{
uint32 addr = S9xGetWord(DirectIndexedXE1(READ), Registers.DL ? WRAP_BANK : WRAP_PAGE);
if (a & READ)
OpenBus = (uint8) (addr >> 8);
return (ICPU.ShiftedDB | addr);
}
static inline uint32 AbsoluteIndexedXSlow (AccessMode a) // a,X
{
uint32 addr = AbsoluteSlow(a);
if (a & WRITE || !CheckIndex() || (addr & 0xff) + Registers.XL >= 0x100)
AddCycles(ONE_CYCLE);
return (addr + Registers.X.W);
}
static inline uint32 AbsoluteIndexedXX0 (AccessMode a) // a,X
{
uint32 addr = Absolute(a);
AddCycles(ONE_CYCLE);
return (addr + Registers.X.W);
}
static inline uint32 AbsoluteIndexedXX1 (AccessMode a) // a,X
{
uint32 addr = Absolute(a);
if (a & WRITE || (addr & 0xff) + Registers.XL >= 0x100)
AddCycles(ONE_CYCLE);
return (addr + Registers.X.W);
}
static inline uint32 AbsoluteIndexedYSlow (AccessMode a) // a,Y
{
uint32 addr = AbsoluteSlow(a);
if (a & WRITE || !CheckIndex() || (addr & 0xff) + Registers.YL >= 0x100)
AddCycles(ONE_CYCLE);
return (addr + Registers.Y.W);
}
static inline uint32 AbsoluteIndexedYX0 (AccessMode a) // a,Y
{
uint32 addr = Absolute(a);
AddCycles(ONE_CYCLE);
return (addr + Registers.Y.W);
}
static inline uint32 AbsoluteIndexedYX1 (AccessMode a) // a,Y
{
uint32 addr = Absolute(a);
if (a & WRITE || (addr & 0xff) + Registers.YL >= 0x100)
AddCycles(ONE_CYCLE);
return (addr + Registers.Y.W);
}
static inline uint32 AbsoluteLongIndexedXSlow (AccessMode a) // l,X
{
return (AbsoluteLongSlow(a) + Registers.X.W);
}
static inline uint32 AbsoluteLongIndexedX (AccessMode a) // l,X
{
return (AbsoluteLong(a) + Registers.X.W);
}
static inline uint32 StackRelativeSlow (AccessMode a) // d,S
{
uint16 addr = Immediate8Slow(a) + Registers.S.W;
AddCycles(ONE_CYCLE);
return (addr);
}
static inline uint32 StackRelative (AccessMode a) // d,S
{
uint16 addr = Immediate8(a) + Registers.S.W;
AddCycles(ONE_CYCLE);
return (addr);
}
static inline uint32 StackRelativeIndirectIndexedSlow (AccessMode a) // (d,S),Y
{
uint32 addr = S9xGetWord(StackRelativeSlow(READ));
if (a & READ)
OpenBus = (uint8) (addr >> 8);
addr = (addr + Registers.Y.W + ICPU.ShiftedDB) & 0xffffff;
AddCycles(ONE_CYCLE);
return (addr);
}
static inline uint32 StackRelativeIndirectIndexed (AccessMode a) // (d,S),Y
{
uint32 addr = S9xGetWord(StackRelative(READ));
if (a & READ)
OpenBus = (uint8) (addr >> 8);
addr = (addr + Registers.Y.W + ICPU.ShiftedDB) & 0xffffff;
AddCycles(ONE_CYCLE);
return (addr);
}
#endif

@ -0,0 +1,425 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "snes9x.h"
#include "memmap.h"
#include "cpuops.h"
#include "dma.h"
#include "apu/apu.h"
#include "fxemu.h"
#include "snapshot.h"
#include "movie.h"
#ifdef DEBUGGER
#include "debug.h"
#include "missing.h"
#endif
static inline void S9xReschedule (void);
void S9xMainLoop (void)
{
#define CHECK_FOR_IRQ_CHANGE() \
if (Timings.IRQFlagChanging) \
{ \
if (Timings.IRQFlagChanging & IRQ_TRIGGER_NMI) \
{ \
CPU.NMIPending = TRUE; \
Timings.NMITriggerPos = CPU.Cycles + 6; \
} \
if (Timings.IRQFlagChanging & IRQ_CLEAR_FLAG) \
ClearIRQ(); \
else if (Timings.IRQFlagChanging & IRQ_SET_FLAG) \
SetIRQ(); \
Timings.IRQFlagChanging = IRQ_NONE; \
}
if (CPU.Flags & SCAN_KEYS_FLAG)
{
CPU.Flags &= ~SCAN_KEYS_FLAG;
S9xMovieUpdate();
}
for (;;)
{
if (CPU.NMIPending)
{
#ifdef DEBUGGER
if (Settings.TraceHCEvent)
S9xTraceFormattedMessage ("Comparing %d to %d\n", Timings.NMITriggerPos, CPU.Cycles);
#endif
if (Timings.NMITriggerPos <= CPU.Cycles)
{
CPU.NMIPending = FALSE;
Timings.NMITriggerPos = 0xffff;
if (CPU.WaitingForInterrupt)
{
CPU.WaitingForInterrupt = FALSE;
Registers.PCw++;
CPU.Cycles += TWO_CYCLES + ONE_DOT_CYCLE / 2;
while (CPU.Cycles >= CPU.NextEvent)
S9xDoHEventProcessing();
}
CHECK_FOR_IRQ_CHANGE();
S9xOpcode_NMI();
}
}
if (CPU.Cycles >= Timings.NextIRQTimer)
{
#ifdef DEBUGGER
S9xTraceMessage ("Timer triggered\n");
#endif
S9xUpdateIRQPositions(false);
CPU.IRQLine = TRUE;
}
if (CPU.IRQLine || CPU.IRQExternal)
{
if (CPU.WaitingForInterrupt)
{
CPU.WaitingForInterrupt = FALSE;
Registers.PCw++;
CPU.Cycles += TWO_CYCLES + ONE_DOT_CYCLE / 2;
while (CPU.Cycles >= CPU.NextEvent)
S9xDoHEventProcessing();
}
if (!CheckFlag(IRQ))
{
/* The flag pushed onto the stack is the new value */
CHECK_FOR_IRQ_CHANGE();
S9xOpcode_IRQ();
}
}
/* Change IRQ flag for instructions that set it only on last cycle */
CHECK_FOR_IRQ_CHANGE();
#ifdef DEBUGGER
if ((CPU.Flags & BREAK_FLAG) && !(CPU.Flags & SINGLE_STEP_FLAG))
{
for (int Break = 0; Break != 6; Break++)
{
if (S9xBreakpoint[Break].Enabled &&
S9xBreakpoint[Break].Bank == Registers.PB &&
S9xBreakpoint[Break].Address == Registers.PCw)
{
if (S9xBreakpoint[Break].Enabled == 2)
S9xBreakpoint[Break].Enabled = TRUE;
else
CPU.Flags |= DEBUG_MODE_FLAG;
}
}
}
if (CPU.Flags & DEBUG_MODE_FLAG)
break;
if (CPU.Flags & TRACE_FLAG)
S9xTrace();
if (CPU.Flags & SINGLE_STEP_FLAG)
{
CPU.Flags &= ~SINGLE_STEP_FLAG;
CPU.Flags |= DEBUG_MODE_FLAG;
}
#endif
if (CPU.Flags & SCAN_KEYS_FLAG)
{
#ifdef DEBUGGER
if (!(CPU.Flags & FRAME_ADVANCE_FLAG))
#endif
{
S9xSyncSpeed();
}
break;
}
uint8 Op;
struct SOpcodes *Opcodes;
if (CPU.PCBase)
{
Op = CPU.PCBase[Registers.PCw];
CPU.Cycles += CPU.MemSpeed;
Opcodes = ICPU.S9xOpcodes;
}
else
{
Op = S9xGetByte(Registers.PBPC);
OpenBus = Op;
Opcodes = S9xOpcodesSlow;
}
if ((Registers.PCw & MEMMAP_MASK) + ICPU.S9xOpLengths[Op] >= MEMMAP_BLOCK_SIZE)
{
uint8 *oldPCBase = CPU.PCBase;
CPU.PCBase = S9xGetBasePointer(ICPU.ShiftedPB + ((uint16) (Registers.PCw + 4)));
if (oldPCBase != CPU.PCBase || (Registers.PCw & ~MEMMAP_MASK) == (0xffff & ~MEMMAP_MASK))
Opcodes = S9xOpcodesSlow;
}
Registers.PCw++;
(*Opcodes[Op].S9xOpcode)();
if (Settings.SA1)
S9xSA1MainLoop();
}
S9xPackStatus();
}
static inline void S9xReschedule (void)
{
switch (CPU.WhichEvent)
{
case HC_HBLANK_START_EVENT:
CPU.WhichEvent = HC_HDMA_START_EVENT;
CPU.NextEvent = Timings.HDMAStart;
break;
case HC_HDMA_START_EVENT:
CPU.WhichEvent = HC_HCOUNTER_MAX_EVENT;
CPU.NextEvent = Timings.H_Max;
break;
case HC_HCOUNTER_MAX_EVENT:
CPU.WhichEvent = HC_HDMA_INIT_EVENT;
CPU.NextEvent = Timings.HDMAInit;
break;
case HC_HDMA_INIT_EVENT:
CPU.WhichEvent = HC_RENDER_EVENT;
CPU.NextEvent = Timings.RenderPos;
break;
case HC_RENDER_EVENT:
CPU.WhichEvent = HC_WRAM_REFRESH_EVENT;
CPU.NextEvent = Timings.WRAMRefreshPos;
break;
case HC_WRAM_REFRESH_EVENT:
CPU.WhichEvent = HC_HBLANK_START_EVENT;
CPU.NextEvent = Timings.HBlankStart;
break;
}
}
void S9xDoHEventProcessing (void)
{
#ifdef DEBUGGER
static char eventname[7][32] =
{
"",
"HC_HBLANK_START_EVENT",
"HC_HDMA_START_EVENT ",
"HC_HCOUNTER_MAX_EVENT",
"HC_HDMA_INIT_EVENT ",
"HC_RENDER_EVENT ",
"HC_WRAM_REFRESH_EVENT"
};
#endif
#ifdef DEBUGGER
if (Settings.TraceHCEvent)
S9xTraceFormattedMessage("--- HC event processing (%s) expected HC:%04d executed HC:%04d VC:%04d",
eventname[CPU.WhichEvent], CPU.NextEvent, CPU.Cycles, CPU.V_Counter);
#endif
switch (CPU.WhichEvent)
{
case HC_HBLANK_START_EVENT:
S9xReschedule();
break;
case HC_HDMA_START_EVENT:
S9xReschedule();
if (PPU.HDMA && CPU.V_Counter <= PPU.ScreenHeight)
{
#ifdef DEBUGGER
S9xTraceFormattedMessage("*** HDMA Transfer HC:%04d, Channel:%02x", CPU.Cycles, PPU.HDMA);
#endif
PPU.HDMA = S9xDoHDMA(PPU.HDMA);
}
break;
case HC_HCOUNTER_MAX_EVENT:
if (Settings.SuperFX)
{
if (!SuperFX.oneLineDone)
S9xSuperFXExec();
SuperFX.oneLineDone = FALSE;
}
S9xAPUEndScanline();
CPU.Cycles -= Timings.H_Max;
if (Timings.NMITriggerPos != 0xffff)
Timings.NMITriggerPos -= Timings.H_Max;
if (Timings.NextIRQTimer != 0x0fffffff)
Timings.NextIRQTimer -= Timings.H_Max;
S9xAPUSetReferenceTime(CPU.Cycles);
if (Settings.SA1)
SA1.Cycles -= Timings.H_Max * 3;
CPU.V_Counter++;
if (CPU.V_Counter >= Timings.V_Max) // V ranges from 0 to Timings.V_Max - 1
{
CPU.V_Counter = 0;
Timings.InterlaceField ^= 1;
// From byuu:
// [NTSC]
// interlace mode has 525 scanlines: 263 on the even frame, and 262 on the odd.
// non-interlace mode has 524 scanlines: 262 scanlines on both even and odd frames.
// [PAL] <PAL info is unverified on hardware>
// interlace mode has 625 scanlines: 313 on the even frame, and 312 on the odd.
// non-interlace mode has 624 scanlines: 312 scanlines on both even and odd frames.
if (IPPU.Interlace && !Timings.InterlaceField)
Timings.V_Max = Timings.V_Max_Master + 1; // 263 (NTSC), 313?(PAL)
else
Timings.V_Max = Timings.V_Max_Master; // 262 (NTSC), 312?(PAL)
Memory.FillRAM[0x213F] ^= 0x80;
PPU.RangeTimeOver = 0;
// FIXME: reading $4210 will wait 2 cycles, then perform reading, then wait 4 more cycles.
Memory.FillRAM[0x4210] = Model->_5A22;
ICPU.Frame++;
PPU.HVBeamCounterLatched = 0;
}
// From byuu:
// In non-interlace mode, there are 341 dots per scanline, and 262 scanlines per frame.
// On odd frames, scanline 240 is one dot short.
// In interlace mode, there are always 341 dots per scanline. Even frames have 263 scanlines,
// and odd frames have 262 scanlines.
// Interlace mode scanline 240 on odd frames is not missing a dot.
if (CPU.V_Counter == 240 && !IPPU.Interlace && Timings.InterlaceField) // V=240
Timings.H_Max = Timings.H_Max_Master - ONE_DOT_CYCLE; // HC=1360
else
Timings.H_Max = Timings.H_Max_Master; // HC=1364
if (Model->_5A22 == 2)
{
if (CPU.V_Counter != 240 || IPPU.Interlace || !Timings.InterlaceField) // V=240
{
if (Timings.WRAMRefreshPos == SNES_WRAM_REFRESH_HC_v2 - ONE_DOT_CYCLE) // HC=534
Timings.WRAMRefreshPos = SNES_WRAM_REFRESH_HC_v2; // HC=538
else
Timings.WRAMRefreshPos = SNES_WRAM_REFRESH_HC_v2 - ONE_DOT_CYCLE; // HC=534
}
}
else
Timings.WRAMRefreshPos = SNES_WRAM_REFRESH_HC_v1;
if (CPU.V_Counter == PPU.ScreenHeight + FIRST_VISIBLE_LINE) // VBlank starts from V=225(240).
{
S9xEndScreenRefresh();
CPU.Flags |= SCAN_KEYS_FLAG;
PPU.HDMA = 0;
// Bits 7 and 6 of $4212 are computed when read in S9xGetPPU.
#ifdef DEBUGGER
missing.dma_this_frame = 0;
#endif
IPPU.MaxBrightness = PPU.Brightness;
PPU.ForcedBlanking = (Memory.FillRAM[0x2100] >> 7) & 1;
if (!PPU.ForcedBlanking)
{
PPU.OAMAddr = PPU.SavedOAMAddr;
uint8 tmp = 0;
if (PPU.OAMPriorityRotation)
tmp = (PPU.OAMAddr & 0xFE) >> 1;
if ((PPU.OAMFlip & 1) || PPU.FirstSprite != tmp)
{
PPU.FirstSprite = tmp;
IPPU.OBJChanged = TRUE;
}
PPU.OAMFlip = 0;
}
// FIXME: writing to $4210 will wait 6 cycles.
Memory.FillRAM[0x4210] = 0x80 | Model->_5A22;
if (Memory.FillRAM[0x4200] & 0x80)
{
#ifdef DEBUGGER
if (Settings.TraceHCEvent)
S9xTraceFormattedMessage ("NMI Scheduled for next scanline.");
#endif
// FIXME: triggered at HC=6, checked just before the final CPU cycle,
// then, when to call S9xOpcode_NMI()?
CPU.NMIPending = TRUE;
Timings.NMITriggerPos = 6 + 6;
}
}
if (CPU.V_Counter == PPU.ScreenHeight + 3) // FIXME: not true
{
if (Memory.FillRAM[0x4200] & 1)
S9xDoAutoJoypad();
}
if (CPU.V_Counter == FIRST_VISIBLE_LINE) // V=1
S9xStartScreenRefresh();
S9xReschedule();
break;
case HC_HDMA_INIT_EVENT:
S9xReschedule();
if (CPU.V_Counter == 0)
{
#ifdef DEBUGGER
S9xTraceFormattedMessage("*** HDMA Init HC:%04d, Channel:%02x", CPU.Cycles, PPU.HDMA);
#endif
S9xStartHDMA();
}
break;
case HC_RENDER_EVENT:
if (CPU.V_Counter >= FIRST_VISIBLE_LINE && CPU.V_Counter <= PPU.ScreenHeight)
RenderLine((uint8) (CPU.V_Counter - FIRST_VISIBLE_LINE));
S9xReschedule();
break;
case HC_WRAM_REFRESH_EVENT:
#ifdef DEBUGGER
S9xTraceFormattedMessage("*** WRAM Refresh HC:%04d", CPU.Cycles);
#endif
CPU.Cycles += SNES_WRAM_REFRESH_CYCLES;
S9xReschedule();
break;
}
#ifdef DEBUGGER
if (Settings.TraceHCEvent)
S9xTraceFormattedMessage("--- HC event rescheduled (%s) expected HC:%04d current HC:%04d",
eventname[CPU.WhichEvent], CPU.NextEvent, CPU.Cycles);
#endif
}

@ -0,0 +1,102 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _CPUEXEC_H_
#define _CPUEXEC_H_
#include "ppu.h"
#ifdef DEBUGGER
#include "debug.h"
#endif
struct SOpcodes
{
void (*S9xOpcode) (void);
};
struct SICPU
{
struct SOpcodes *S9xOpcodes;
uint8 *S9xOpLengths;
uint8 _Carry;
uint8 _Zero;
uint8 _Negative;
uint8 _Overflow;
uint32 ShiftedPB;
uint32 ShiftedDB;
uint32 Frame;
uint32 FrameAdvanceCount;
};
extern struct SICPU ICPU;
extern struct SOpcodes S9xOpcodesE1[256];
extern struct SOpcodes S9xOpcodesM1X1[256];
extern struct SOpcodes S9xOpcodesM1X0[256];
extern struct SOpcodes S9xOpcodesM0X1[256];
extern struct SOpcodes S9xOpcodesM0X0[256];
extern struct SOpcodes S9xOpcodesSlow[256];
extern uint8 S9xOpLengthsM1X1[256];
extern uint8 S9xOpLengthsM1X0[256];
extern uint8 S9xOpLengthsM0X1[256];
extern uint8 S9xOpLengthsM0X0[256];
void S9xMainLoop (void);
void S9xReset (void);
void S9xSoftReset (void);
void S9xDoHEventProcessing (void);
static inline void S9xUnpackStatus (void)
{
ICPU._Zero = (Registers.PL & Zero) == 0;
ICPU._Negative = (Registers.PL & Negative);
ICPU._Carry = (Registers.PL & Carry);
ICPU._Overflow = (Registers.PL & Overflow) >> 6;
}
static inline void S9xPackStatus (void)
{
Registers.PL &= ~(Zero | Negative | Carry | Overflow);
Registers.PL |= ICPU._Carry | ((ICPU._Zero == 0) << 1) | (ICPU._Negative & 0x80) | (ICPU._Overflow << 6);
}
static inline void S9xFixCycles (void)
{
if (CheckEmulation())
{
ICPU.S9xOpcodes = S9xOpcodesE1;
ICPU.S9xOpLengths = S9xOpLengthsM1X1;
}
else
if (CheckMemory())
{
if (CheckIndex())
{
ICPU.S9xOpcodes = S9xOpcodesM1X1;
ICPU.S9xOpLengths = S9xOpLengthsM1X1;
}
else
{
ICPU.S9xOpcodes = S9xOpcodesM1X0;
ICPU.S9xOpLengths = S9xOpLengthsM1X0;
}
}
else
{
if (CheckIndex())
{
ICPU.S9xOpcodes = S9xOpcodesM0X1;
ICPU.S9xOpLengths = S9xOpLengthsM0X1;
}
else
{
ICPU.S9xOpcodes = S9xOpcodesM0X0;
ICPU.S9xOpLengths = S9xOpLengthsM0X0;
}
}
}
#endif

@ -0,0 +1,674 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _CPUMACRO_H_
#define _CPUMACRO_H_
#define rOP8(OP, ADDR, WRAP, FUNC) \
static void Op##OP (void) \
{ \
uint8 val = OpenBus = S9xGetByte(ADDR(READ)); \
FUNC(val); \
}
#define rOP16(OP, ADDR, WRAP, FUNC) \
static void Op##OP (void) \
{ \
uint16 val = S9xGetWord(ADDR(READ), WRAP); \
OpenBus = (uint8) (val >> 8); \
FUNC(val); \
}
#define rOPC(OP, COND, ADDR, WRAP, FUNC) \
static void Op##OP (void) \
{ \
if (Check##COND()) \
{ \
uint8 val = OpenBus = S9xGetByte(ADDR(READ)); \
FUNC(val); \
} \
else \
{ \
uint16 val = S9xGetWord(ADDR(READ), WRAP); \
OpenBus = (uint8) (val >> 8); \
FUNC(val); \
} \
}
#define rOPM(OP, ADDR, WRAP, FUNC) \
rOPC(OP, Memory, ADDR, WRAP, FUNC)
#define rOPX(OP, ADDR, WRAP, FUNC) \
rOPC(OP, Index, ADDR, WRAP, FUNC)
#define wOP8(OP, ADDR, WRAP, FUNC) \
static void Op##OP (void) \
{ \
FUNC##8(ADDR(WRITE)); \
}
#define wOP16(OP, ADDR, WRAP, FUNC) \
static void Op##OP (void) \
{ \
FUNC##16(ADDR(WRITE), WRAP); \
}
#define wOPC(OP, COND, ADDR, WRAP, FUNC) \
static void Op##OP (void) \
{ \
if (Check##COND()) \
FUNC##8(ADDR(WRITE)); \
else \
FUNC##16(ADDR(WRITE), WRAP); \
}
#define wOPM(OP, ADDR, WRAP, FUNC) \
wOPC(OP, Memory, ADDR, WRAP, FUNC)
#define wOPX(OP, ADDR, WRAP, FUNC) \
wOPC(OP, Index, ADDR, WRAP, FUNC)
#define mOP8(OP, ADDR, WRAP, FUNC) \
static void Op##OP (void) \
{ \
FUNC##8(ADDR(MODIFY)); \
}
#define mOP16(OP, ADDR, WRAP, FUNC) \
static void Op##OP (void) \
{ \
FUNC##16(ADDR(MODIFY), WRAP); \
}
#define mOPC(OP, COND, ADDR, WRAP, FUNC) \
static void Op##OP (void) \
{ \
if (Check##COND()) \
FUNC##8(ADDR(MODIFY)); \
else \
FUNC##16(ADDR(MODIFY), WRAP); \
}
#define mOPM(OP, ADDR, WRAP, FUNC) \
mOPC(OP, Memory, ADDR, WRAP, FUNC)
#define bOP(OP, REL, COND, CHK, E) \
static void Op##OP (void) \
{ \
pair newPC; \
newPC.W = REL(JUMP); \
if (COND) \
{ \
AddCycles(ONE_CYCLE); \
if (E && Registers.PCh != newPC.B.h) \
AddCycles(ONE_CYCLE); \
if ((Registers.PCw & ~MEMMAP_MASK) != (newPC.W & ~MEMMAP_MASK)) \
S9xSetPCBase(ICPU.ShiftedPB + newPC.W); \
else \
Registers.PCw = newPC.W; \
} \
}
static inline void SetZN (uint16 Work16)
{
ICPU._Zero = Work16 != 0;
ICPU._Negative = (uint8) (Work16 >> 8);
}
static inline void SetZN (uint8 Work8)
{
ICPU._Zero = Work8;
ICPU._Negative = Work8;
}
static inline void ADC (uint16 Work16)
{
if (CheckDecimal())
{
uint32 result;
uint32 carry = CheckCarry();
result = (Registers.A.W & 0x000F) + (Work16 & 0x000F) + carry;
if (result > 0x0009)
result += 0x0006;
carry = (result > 0x000F);
result = (Registers.A.W & 0x00F0) + (Work16 & 0x00F0) + (result & 0x000F) + carry * 0x10;
if (result > 0x009F)
result += 0x0060;
carry = (result > 0x00FF);
result = (Registers.A.W & 0x0F00) + (Work16 & 0x0F00) + (result & 0x00FF) + carry * 0x100;
if (result > 0x09FF)
result += 0x0600;
carry = (result > 0x0FFF);
result = (Registers.A.W & 0xF000) + (Work16 & 0xF000) + (result & 0x0FFF) + carry * 0x1000;
if ((Registers.A.W & 0x8000) == (Work16 & 0x8000) && (Registers.A.W & 0x8000) != (result & 0x8000))
SetOverflow();
else
ClearOverflow();
if (result > 0x9FFF)
result += 0x6000;
if (result > 0xFFFF)
SetCarry();
else
ClearCarry();
Registers.A.W = result & 0xFFFF;
SetZN(Registers.A.W);
}
else
{
uint32 Ans32 = Registers.A.W + Work16 + CheckCarry();
ICPU._Carry = Ans32 >= 0x10000;
if (~(Registers.A.W ^ Work16) & (Work16 ^ (uint16) Ans32) & 0x8000)
SetOverflow();
else
ClearOverflow();
Registers.A.W = (uint16) Ans32;
SetZN(Registers.A.W);
}
}
static inline void ADC (uint8 Work8)
{
if (CheckDecimal())
{
uint32 result;
uint32 carry = CheckCarry();
result = (Registers.AL & 0x0F) + (Work8 & 0x0F) + carry;
if ( result > 0x09 )
result += 0x06;
carry = (result > 0x0F);
result = (Registers.AL & 0xF0) + (Work8 & 0xF0) + (result & 0x0F) + (carry * 0x10);
if ((Registers.AL & 0x80) == (Work8 & 0x80) && (Registers.AL & 0x80) != (result & 0x80))
SetOverflow();
else
ClearOverflow();
if (result > 0x9F)
result += 0x60;
if (result > 0xFF)
SetCarry();
else
ClearCarry();
Registers.AL = result & 0xFF;
SetZN(Registers.AL);
}
else
{
uint16 Ans16 = Registers.AL + Work8 + CheckCarry();
ICPU._Carry = Ans16 >= 0x100;
if (~(Registers.AL ^ Work8) & (Work8 ^ (uint8) Ans16) & 0x80)
SetOverflow();
else
ClearOverflow();
Registers.AL = (uint8) Ans16;
SetZN(Registers.AL);
}
}
static inline void AND (uint16 Work16)
{
Registers.A.W &= Work16;
SetZN(Registers.A.W);
}
static inline void AND (uint8 Work8)
{
Registers.AL &= Work8;
SetZN(Registers.AL);
}
static inline void ASL16 (uint32 OpAddress, s9xwrap_t w)
{
uint16 Work16 = S9xGetWord(OpAddress, w);
ICPU._Carry = (Work16 & 0x8000) != 0;
Work16 <<= 1;
AddCycles(ONE_CYCLE);
S9xSetWord(Work16, OpAddress, w, WRITE_10);
OpenBus = Work16 & 0xff;
SetZN(Work16);
}
static inline void ASL8 (uint32 OpAddress)
{
uint8 Work8 = S9xGetByte(OpAddress);
ICPU._Carry = (Work8 & 0x80) != 0;
Work8 <<= 1;
AddCycles(ONE_CYCLE);
S9xSetByte(Work8, OpAddress);
OpenBus = Work8;
SetZN(Work8);
}
static inline void BIT (uint16 Work16)
{
ICPU._Overflow = (Work16 & 0x4000) != 0;
ICPU._Negative = (uint8) (Work16 >> 8);
ICPU._Zero = (Work16 & Registers.A.W) != 0;
}
static inline void BIT (uint8 Work8)
{
ICPU._Overflow = (Work8 & 0x40) != 0;
ICPU._Negative = Work8;
ICPU._Zero = Work8 & Registers.AL;
}
static inline void CMP (uint16 val)
{
int32 Int32 = (int32) Registers.A.W - (int32) val;
ICPU._Carry = Int32 >= 0;
SetZN((uint16) Int32);
}
static inline void CMP (uint8 val)
{
int16 Int16 = (int16) Registers.AL - (int16) val;
ICPU._Carry = Int16 >= 0;
SetZN((uint8) Int16);
}
static inline void CPX (uint16 val)
{
int32 Int32 = (int32) Registers.X.W - (int32) val;
ICPU._Carry = Int32 >= 0;
SetZN((uint16) Int32);
}
static inline void CPX (uint8 val)
{
int16 Int16 = (int16) Registers.XL - (int16) val;
ICPU._Carry = Int16 >= 0;
SetZN((uint8) Int16);
}
static inline void CPY (uint16 val)
{
int32 Int32 = (int32) Registers.Y.W - (int32) val;
ICPU._Carry = Int32 >= 0;
SetZN((uint16) Int32);
}
static inline void CPY (uint8 val)
{
int16 Int16 = (int16) Registers.YL - (int16) val;
ICPU._Carry = Int16 >= 0;
SetZN((uint8) Int16);
}
static inline void DEC16 (uint32 OpAddress, s9xwrap_t w)
{
uint16 Work16 = S9xGetWord(OpAddress, w) - 1;
AddCycles(ONE_CYCLE);
S9xSetWord(Work16, OpAddress, w, WRITE_10);
OpenBus = Work16 & 0xff;
SetZN(Work16);
}
static inline void DEC8 (uint32 OpAddress)
{
uint8 Work8 = S9xGetByte(OpAddress) - 1;
AddCycles(ONE_CYCLE);
S9xSetByte(Work8, OpAddress);
OpenBus = Work8;
SetZN(Work8);
}
static inline void EOR (uint16 val)
{
Registers.A.W ^= val;
SetZN(Registers.A.W);
}
static inline void EOR (uint8 val)
{
Registers.AL ^= val;
SetZN(Registers.AL);
}
static inline void INC16 (uint32 OpAddress, s9xwrap_t w)
{
uint16 Work16 = S9xGetWord(OpAddress, w) + 1;
AddCycles(ONE_CYCLE);
S9xSetWord(Work16, OpAddress, w, WRITE_10);
OpenBus = Work16 & 0xff;
SetZN(Work16);
}
static inline void INC8 (uint32 OpAddress)
{
uint8 Work8 = S9xGetByte(OpAddress) + 1;
AddCycles(ONE_CYCLE);
S9xSetByte(Work8, OpAddress);
OpenBus = Work8;
SetZN(Work8);
}
static inline void LDA (uint16 val)
{
Registers.A.W = val;
SetZN(Registers.A.W);
}
static inline void LDA (uint8 val)
{
Registers.AL = val;
SetZN(Registers.AL);
}
static inline void LDX (uint16 val)
{
Registers.X.W = val;
SetZN(Registers.X.W);
}
static inline void LDX (uint8 val)
{
Registers.XL = val;
SetZN(Registers.XL);
}
static inline void LDY (uint16 val)
{
Registers.Y.W = val;
SetZN(Registers.Y.W);
}
static inline void LDY (uint8 val)
{
Registers.YL = val;
SetZN(Registers.YL);
}
static inline void LSR16 (uint32 OpAddress, s9xwrap_t w)
{
uint16 Work16 = S9xGetWord(OpAddress, w);
ICPU._Carry = Work16 & 1;
Work16 >>= 1;
AddCycles(ONE_CYCLE);
S9xSetWord(Work16, OpAddress, w, WRITE_10);
OpenBus = Work16 & 0xff;
SetZN(Work16);
}
static inline void LSR8 (uint32 OpAddress)
{
uint8 Work8 = S9xGetByte(OpAddress);
ICPU._Carry = Work8 & 1;
Work8 >>= 1;
AddCycles(ONE_CYCLE);
S9xSetByte(Work8, OpAddress);
OpenBus = Work8;
SetZN(Work8);
}
static inline void ORA (uint16 val)
{
Registers.A.W |= val;
SetZN(Registers.A.W);
}
static inline void ORA (uint8 val)
{
Registers.AL |= val;
SetZN(Registers.AL);
}
static inline void ROL16 (uint32 OpAddress, s9xwrap_t w)
{
uint32 Work32 = (((uint32) S9xGetWord(OpAddress, w)) << 1) | CheckCarry();
ICPU._Carry = Work32 >= 0x10000;
AddCycles(ONE_CYCLE);
S9xSetWord((uint16) Work32, OpAddress, w, WRITE_10);
OpenBus = Work32 & 0xff;
SetZN((uint16) Work32);
}
static inline void ROL8 (uint32 OpAddress)
{
uint16 Work16 = (((uint16) S9xGetByte(OpAddress)) << 1) | CheckCarry();
ICPU._Carry = Work16 >= 0x100;
AddCycles(ONE_CYCLE);
S9xSetByte((uint8) Work16, OpAddress);
OpenBus = Work16 & 0xff;
SetZN((uint8) Work16);
}
static inline void ROR16 (uint32 OpAddress, s9xwrap_t w)
{
uint32 Work32 = ((uint32) S9xGetWord(OpAddress, w)) | (((uint32) CheckCarry()) << 16);
ICPU._Carry = Work32 & 1;
Work32 >>= 1;
AddCycles(ONE_CYCLE);
S9xSetWord((uint16) Work32, OpAddress, w, WRITE_10);
OpenBus = Work32 & 0xff;
SetZN((uint16) Work32);
}
static inline void ROR8 (uint32 OpAddress)
{
uint16 Work16 = ((uint16) S9xGetByte(OpAddress)) | (((uint16) CheckCarry()) << 8);
ICPU._Carry = Work16 & 1;
Work16 >>= 1;
AddCycles(ONE_CYCLE);
S9xSetByte((uint8) Work16, OpAddress);
OpenBus = Work16 & 0xff;
SetZN((uint8) Work16);
}
static inline void SBC (uint16 Work16)
{
if (CheckDecimal())
{
int result;
int carry = CheckCarry();
Work16 ^= 0xFFFF;
result = (Registers.A.W & 0x000F) + (Work16 & 0x000F) + carry;
if (result < 0x0010)
result -= 0x0006;
carry = (result > 0x000F);
result = (Registers.A.W & 0x00F0) + (Work16 & 0x00F0) + (result & 0x000F) + carry * 0x10;
if (result < 0x0100)
result -= 0x0060;
carry = (result > 0x00FF);
result = (Registers.A.W & 0x0F00) + (Work16 & 0x0F00) + (result & 0x00FF) + carry * 0x100;
if (result < 0x1000)
result -= 0x0600;
carry = (result > 0x0FFF);
result = (Registers.A.W & 0xF000) + (Work16 & 0xF000) + (result & 0x0FFF) + carry * 0x1000;
if (((Registers.A.W ^ Work16) & 0x8000) == 0 && ((Registers.A.W ^ result) & 0x8000))
SetOverflow();
else
ClearOverflow();
if (result < 0x10000)
result -= 0x6000;
if (result > 0xFFFF)
SetCarry();
else
ClearCarry();
Registers.A.W = result & 0xFFFF;
SetZN(Registers.A.W);
}
else
{
int32 Int32 = (int32) Registers.A.W - (int32) Work16 + (int32) CheckCarry() - 1;
ICPU._Carry = Int32 >= 0;
if ((Registers.A.W ^ Work16) & (Registers.A.W ^ (uint16) Int32) & 0x8000)
SetOverflow();
else
ClearOverflow();
Registers.A.W = (uint16) Int32;
SetZN(Registers.A.W);
}
}
static inline void SBC (uint8 Work8)
{
if (CheckDecimal())
{
int result;
int carry = CheckCarry();
Work8 ^= 0xFF;
result = (Registers.AL & 0x0F) + (Work8 & 0x0F) + carry;
if (result < 0x10)
result -= 0x06;
carry = (result > 0x0F);
result = (Registers.AL & 0xF0) + (Work8 & 0xF0) + (result & 0x0F) + carry * 0x10;
if ((Registers.AL & 0x80) == (Work8 & 0x80) && (Registers.AL & 0x80) != (result & 0x80))
SetOverflow();
else
ClearOverflow();
if (result < 0x100 )
result -= 0x60;
if (result > 0xFF)
SetCarry();
else
ClearCarry();
Registers.AL = result & 0xFF;
SetZN(Registers.AL);
}
else
{
int16 Int16 = (int16) Registers.AL - (int16) Work8 + (int16) CheckCarry() - 1;
ICPU._Carry = Int16 >= 0;
if ((Registers.AL ^ Work8) & (Registers.AL ^ (uint8) Int16) & 0x80)
SetOverflow();
else
ClearOverflow();
Registers.AL = (uint8) Int16;
SetZN(Registers.AL);
}
}
static inline void STA16 (uint32 OpAddress, enum s9xwrap_t w)
{
S9xSetWord(Registers.A.W, OpAddress, w);
OpenBus = Registers.AH;
}
static inline void STA8 (uint32 OpAddress)
{
S9xSetByte(Registers.AL, OpAddress);
OpenBus = Registers.AL;
}
static inline void STX16 (uint32 OpAddress, enum s9xwrap_t w)
{
S9xSetWord(Registers.X.W, OpAddress, w);
OpenBus = Registers.XH;
}
static inline void STX8 (uint32 OpAddress)
{
S9xSetByte(Registers.XL, OpAddress);
OpenBus = Registers.XL;
}
static inline void STY16 (uint32 OpAddress, enum s9xwrap_t w)
{
S9xSetWord(Registers.Y.W, OpAddress, w);
OpenBus = Registers.YH;
}
static inline void STY8 (uint32 OpAddress)
{
S9xSetByte(Registers.YL, OpAddress);
OpenBus = Registers.YL;
}
static inline void STZ16 (uint32 OpAddress, enum s9xwrap_t w)
{
S9xSetWord(0, OpAddress, w);
OpenBus = 0;
}
static inline void STZ8 (uint32 OpAddress)
{
S9xSetByte(0, OpAddress);
OpenBus = 0;
}
static inline void TSB16 (uint32 OpAddress, enum s9xwrap_t w)
{
uint16 Work16 = S9xGetWord(OpAddress, w);
ICPU._Zero = (Work16 & Registers.A.W) != 0;
Work16 |= Registers.A.W;
AddCycles(ONE_CYCLE);
S9xSetWord(Work16, OpAddress, w, WRITE_10);
OpenBus = Work16 & 0xff;
}
static inline void TSB8 (uint32 OpAddress)
{
uint8 Work8 = S9xGetByte(OpAddress);
ICPU._Zero = Work8 & Registers.AL;
Work8 |= Registers.AL;
AddCycles(ONE_CYCLE);
S9xSetByte(Work8, OpAddress);
OpenBus = Work8;
}
static inline void TRB16 (uint32 OpAddress, enum s9xwrap_t w)
{
uint16 Work16 = S9xGetWord(OpAddress, w);
ICPU._Zero = (Work16 & Registers.A.W) != 0;
Work16 &= ~Registers.A.W;
AddCycles(ONE_CYCLE);
S9xSetWord(Work16, OpAddress, w, WRITE_10);
OpenBus = Work16 & 0xff;
}
static inline void TRB8 (uint32 OpAddress)
{
uint8 Work8 = S9xGetByte(OpAddress);
ICPU._Zero = Work8 & Registers.AL;
Work8 &= ~Registers.AL;
AddCycles(ONE_CYCLE);
S9xSetByte(Work8, OpAddress);
OpenBus = Work8;
}
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,18 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _CPUOPS_H_
#define _CPUOPS_H_
void S9xOpcode_NMI (void);
void S9xOpcode_IRQ (void);
#ifndef SA1_OPCODES
#define CHECK_FOR_IRQ() {} // if (CPU.IRQLine) S9xOpcode_IRQ(); }
#else
#define CHECK_FOR_IRQ() {}
#endif
#endif

@ -0,0 +1,511 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifdef HAVE_LIBPNG
#include <png.h>
#endif
#include "port.h"
#include "crosshairs.h"
static const char *crosshairs[32] =
{
"` " // Crosshair 0 (no image)
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" ",
"` " // Crosshair 1 (the classic small dot)
" "
" "
" "
" "
" "
" "
" #. "
" "
" "
" "
" "
" "
" "
" ",
"` " // Crosshair 2 (a standard cross)
" "
" "
" "
" .#. "
" .#. "
" ...#... "
" ####### "
" ...#... "
" .#. "
" .#. "
" "
" "
" "
" ",
"` .#. " // Crosshair 3 (a standard cross)
" .#. "
" .#. "
" .#. "
" .#. "
" .#. "
".......#......."
"###############"
".......#......."
" .#. "
" .#. "
" .#. "
" .#. "
" .#. "
" .#. ",
"` " // Crosshair 4 (an X)
" "
" "
" . . "
" .#. .#. "
" .#. .#. "
" .#.#. "
" .#. "
" .#.#. "
" .#. .#. "
" .#. .#. "
" . . "
" "
" "
" ",
"`. . " // Crosshair 5 (an X)
".#. .#."
" .#. .#. "
" .#. .#. "
" .#. .#. "
" .#. .#. "
" .#.#. "
" .#. "
" .#.#. "
" .#. .#. "
" .#. .#. "
" .#. .#. "
" .#. .#. "
".#. .#."
" . . ",
"` " // Crosshair 6 (a combo)
" "
" "
" "
" # . # "
" # . # "
" #.# "
" ...#... "
" #.# "
" # . # "
" # . # "
" "
" "
" "
" ",
"` . " // Crosshair 7 (a combo)
" # . # "
" # . # "
" # . # "
" # . # "
" # . # "
" #.# "
".......#......."
" #.# "
" # . # "
" # . # "
" # . # "
" # . # "
" # . # "
" . ",
"` # " // Crosshair 8 (a diamond cross)
" #.# "
" # . # "
" # . # "
" # . # "
" # . # "
" # . # "
"#......#......#"
" # . # "
" # . # "
" # . # "
" # . # "
" # . # "
" #.# "
" # ",
"` ### " // Crosshair 9 (a circle cross)
" ## . ## "
" # . # "
" # . # "
" # . # "
" # . # "
"# . #"
"#......#......#"
"# . #"
" # . # "
" # . # "
" # . # "
" # . # "
" ## . ## "
" ### ",
"` .#. " // Crosshair 10 (a square cross)
" .#. "
" .#. "
" ....#.... "
" .#######. "
" .# #. "
"....# #...."
"##### #####"
"....# #...."
" .# #. "
" .#######. "
" ....#.... "
" .#. "
" .#. "
" .#. ",
"` .#. " // Crosshair 11 (an interrupted cross)
" .#. "
" .#. "
" .#. "
" .#. "
" "
"..... ....."
"##### #####"
"..... ....."
" "
" .#. "
" .#. "
" .#. "
" .#. "
" .#. ",
"`. . " // Crosshair 12 (an interrupted X)
".#. .#."
" .#. .#. "
" .#. .#. "
" .#. .#. "
" "
" "
" "
" "
" "
" .#. .#. "
" .#. .#. "
" .#. .#. "
".#. .#."
" . . ",
"` . " // Crosshair 13 (an interrupted combo)
" # . # "
" # . # "
" # . # "
" # . # "
" "
" "
"..... ....."
" "
" "
" # . # "
" # . # "
" # . # "
" # . # "
" . ",
"`#### #### " // Crosshair 14
"#.... ....#"
"#. .#"
"#. .#"
"#. .#"
" # "
" # "
" ##### "
" # "
" # "
"#. .#"
"#. .#"
"#. .#"
"#.... ....#"
" #### #### ",
"` .# #. " // Crosshair 15
" .# #. "
" .# #. "
"....# #...."
"##### #####"
" "
" "
" "
" "
" "
"##### #####"
"....# #...."
" .# #. "
" .# #. "
" .# #. ",
"` # " // Crosshair 16
" # "
" # "
" ....#.... "
" . # . "
" . # . "
" . # . "
"###############"
" . # . "
" . # . "
" . # . "
" ....#.... "
" # "
" # "
" # ",
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
bool S9xLoadCrosshairFile (int idx, const char *filename)
{
if (idx < 1 || idx > 31)
return (false);
char *s = (char *) calloc(15 * 15 + 1, sizeof(char));
if (s == NULL)
{
fprintf(stderr, "S9xLoadCrosshairFile: malloc error while reading ");
perror(filename);
return (false);
}
FILE *fp = fopen(filename, "rb");
if (fp == NULL)
{
fprintf(stderr, "S9xLoadCrosshairFile: Couldn't open ");
perror(filename);
free(s);
return (false);
}
size_t l = fread(s, 1, 8, fp);
if (l != 8)
{
fprintf(stderr, "S9xLoadCrosshairFile: File is too short!\n");
free(s);
fclose(fp);
return (false);
}
#ifdef HAVE_LIBPNG
png_structp png_ptr;
png_infop info_ptr;
if (!png_sig_cmp((png_byte *) s, 0, 8))
{
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr)
{
free(s);
fclose(fp);
return (false);
}
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
{
png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
free(s);
fclose(fp);
return (false);
}
png_init_io(png_ptr, fp);
png_set_sig_bytes(png_ptr, 8);
png_read_info(png_ptr, info_ptr);
png_uint_32 width, height;
int bit_depth, color_type;
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
if (color_type != PNG_COLOR_TYPE_PALETTE)
{
fprintf(stderr, "S9xLoadCrosshairFile: Input PNG is not a palettized image!\n");
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
free(s);
fclose(fp);
return (false);
}
if (bit_depth == 16)
png_set_strip_16(png_ptr);
if (width != 15 || height != 15)
{
fprintf(stderr, "S9xLoadCrosshairFile: Expecting a 15x15 PNG\n");
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
free(s);
fclose(fp);
return (false);
}
png_color *pngpal;
png_byte *trans;
int num_palette = 0, num_trans = 0;
int transcol = -1, fgcol = -1, bgcol = -1;
png_get_PLTE(png_ptr, info_ptr, &pngpal, &num_palette);
png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
if (num_palette != 3 || num_trans != 1)
{
fprintf(stderr, "S9xLoadCrosshairFile: Expecting a 3-color PNG with 1 trasnparent color\n");
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
free(s);
fclose(fp);
return (false);
}
for (int i = 0; i < 3; i++)
{
if (trans[0] == i)
transcol = i;
else
if (pngpal[i].red == 0 && pngpal[i].green == 0 && pngpal[i].blue == 0)
bgcol = i;
else
if (pngpal[i].red == 255 && pngpal[i].green == 255 && pngpal[i].blue == 255)
fgcol = i;
}
if (transcol < 0 || fgcol < 0 || bgcol < 0)
{
fprintf(stderr, "S9xLoadCrosshairFile: PNG must have 3 colors: white (fg), black (bg), and transparent.\n");
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
free(s);
fclose(fp);
return (false);
}
png_set_packing(png_ptr);
png_read_update_info(png_ptr, info_ptr);
png_byte *row_pointer = new png_byte[png_get_rowbytes(png_ptr, info_ptr)];
for (int r = 0; r < 15 * 15; r += 15)
{
png_read_row(png_ptr, row_pointer, NULL);
for (int i = 0; i < 15; i++)
{
if (row_pointer[i] == transcol)
s[r + i] = ' ';
else
if (row_pointer[i] == fgcol)
s[r + i] = '#';
else
if (row_pointer[i] == bgcol)
s[r + i] = '.';
else
{
fprintf(stderr, "S9xLoadCrosshairFile: WTF? This was supposed to be a 3-color PNG!\n");
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
free(s);
fclose(fp);
return (false);
}
}
}
s[15 * 15] = 0;
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
}
else
#endif
{
l = fread(s + 8, 1, 15 - 8, fp);
if (l != 15 - 8)
{
fprintf(stderr, "S9xLoadCrosshairFile: File is too short!\n");
free(s);
fclose(fp);
return (false);
}
if (getc(fp) != '\n')
{
fprintf(stderr, "S9xLoadCrosshairFile: Invalid file format! (note: PNG support is not available)\n");
free(s);
fclose(fp);
return (false);
}
for (int r = 1; r < 15; r++)
{
l = fread(s + r * 15, 1, 15, fp);
if (l != 15)
{
fprintf(stderr, "S9xLoadCrosshairFile: File is too short! (note: PNG support is not available)\n");
free(s);
fclose(fp);
return (false);
}
if (getc(fp) != '\n')
{
fprintf(stderr, "S9xLoadCrosshairFile: Invalid file format! (note: PNG support is not available)\n");
free(s);
fclose(fp);
return (false);
}
}
for (int i = 0; i < 15 * 15; i++)
{
if (s[i] != ' ' && s[i] != '#' && s[i] != '.')
{
fprintf(stderr, "S9xLoadCrosshairFile: Invalid file format! (note: PNG support is not available)\n");
free(s);
fclose(fp);
return (false);
}
}
}
fclose(fp);
if (crosshairs[idx] != NULL && crosshairs[idx][0] != '`')
free((void *) crosshairs[idx]);
crosshairs[idx] = s;
return (true);
}
const char * S9xGetCrosshair (int idx)
{
if (idx < 0 || idx > 31)
return (NULL);
return (crosshairs[idx]);
}

@ -0,0 +1,64 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _CROSSHAIRS_H_
#define _CROSSHAIRS_H_
// Read in the specified crosshair file, replacing whatever data might be in that slot.
// Available slots are 1-31.
// The input file must be a PNG or a text file.
// PNG: 15x15 pixels, palettized, with 3 colors (white, black, and transparent).
// text: 15 lines of 16 characters (counting the \n), consisting of ' ', '#', or '.'.
bool S9xLoadCrosshairFile (int idx, const char *filename);
// Return the specified crosshair. Woo-hoo.
// char * to a 225-byte string, with '#' marking foreground, '.' marking background,
// and anything else transparent.
const char * S9xGetCrosshair (int idx);
// In controls.cpp. Sets the crosshair for the specified device. Defaults are:
// cross fgcolor bgcolor
// Mouse 1: 1 White Black
// Mouse 2: 1 Purple White
// Superscope: 2 White Black
// Justifier 1: 4 Blue Black
// Justifier 2: 4 MagicPink Black
// Macs Rifle: 2 White Black
//
// Available colors are: Trans, Black, 25Grey, 50Grey, 75Grey, White, Red, Orange,
// Yellow, Green, Cyan, Sky, Blue, Violet, MagicPink, and Purple.
// You may also prefix a 't' (e.g. tBlue) for a 50%-transparent version.
// Use idx = -1 or fg/bg = NULL to keep the current setting.
enum crosscontrols
{
X_MOUSE1,
X_MOUSE2,
X_SUPERSCOPE,
X_JUSTIFIER1,
X_JUSTIFIER2,
X_MACSRIFLE
};
void S9xSetControllerCrosshair (enum crosscontrols ctl, int8 idx, const char *fg, const char *bg);
void S9xGetControllerCrosshair (enum crosscontrols ctl, int8 *idx, const char **fg, const char **bg);
// In gfx.cpp, much like S9xDisplayChar() except it takes the parameters
// listed and looks up GFX.Screen.
// The 'crosshair' arg is a 15x15 image, with '#' meaning fgcolor,
// '.' meaning bgcolor, and anything else meaning transparent.
// Color values should be (RGB):
// 0 = transparent 4 = 23 23 23 8 = 31 31 0 12 = 0 0 31
// 1 = 0 0 0 5 = 31 31 31 9 = 0 31 0 13 = 23 0 31
// 2 = 8 8 8 6 = 31 0 0 10 = 0 31 31 14 = 31 0 31
// 3 = 16 16 16 7 = 31 16 0 11 = 0 23 31 15 = 31 0 16
// 16-31 are 50% transparent versions of 0-15.
void S9xDrawCrosshair (const char *crosshair, uint8 fgcolor, uint8 bgcolor, int16 x, int16 y);
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,41 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifdef DEBUGGER
#ifndef _DEBUG_H_
#define _DEBUG_H_
#include <string>
struct SBreakPoint
{
bool8 Enabled;
uint8 Bank;
uint16 Address;
};
#define ENSURE_TRACE_OPEN(fp, file, mode) \
if (!fp) \
{ \
std::string fn = S9xGetDirectory(LOG_DIR); \
fn += SLASH_STR file; \
fp = fopen(fn.c_str(), mode); \
}
extern struct SBreakPoint S9xBreakpoint[6];
void S9xDoDebug (void);
void S9xTrace (void);
void S9xSA1Trace (void);
void S9xTraceMessage (const char *);
void S9xTraceFormattedMessage (const char *, ...);
void S9xPrintHVPosition (char *);
void S9xDebugProcessCommand(char *);
#endif
#endif

@ -0,0 +1,66 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _DISPLAY_H_
#define _DISPLAY_H_
#include "snes9x.h"
enum s9x_getdirtype
{
DEFAULT_DIR = 0,
HOME_DIR,
ROMFILENAME_DIR,
ROM_DIR,
SRAM_DIR,
SNAPSHOT_DIR,
SCREENSHOT_DIR,
SPC_DIR,
CHEAT_DIR,
PATCH_DIR,
BIOS_DIR,
LOG_DIR,
SAT_DIR,
LAST_DIR
};
void S9xUsage (void);
char * S9xParseArgs (char **, int);
void S9xParseArgsForCheats (char **, int);
void S9xLoadConfigFiles (char **, int);
void S9xSetInfoString (const char *);
// Routines the port has to implement even if it doesn't use them
void S9xPutImage (int, int);
void S9xInitDisplay (int, char **);
void S9xDeinitDisplay (void);
void S9xTextMode (void);
void S9xGraphicsMode (void);
void S9xToggleSoundChannel (int);
bool8 S9xOpenSnapshotFile (const char *, bool8, STREAM *);
void S9xCloseSnapshotFile (STREAM);
const char * S9xStringInput (const char *);
const char * S9xGetDirectory (enum s9x_getdirtype);
const char * S9xGetFilename (const char *, enum s9x_getdirtype);
const char * S9xGetFilenameInc (const char *, enum s9x_getdirtype);
const char * S9xBasename (const char *);
// Routines the port has to implement if it uses command-line
void S9xExtraUsage (void);
void S9xParseArg (char **, int &, int);
// Routines the port may implement as needed
void S9xExtraDisplayUsage (void);
void S9xParseDisplayArg (char **, int &, int);
void S9xSetTitle (const char *);
void S9xInitInputDevices (void);
void S9xProcessEvents (bool8);
const char * S9xSelectFilename (const char *, const char *, const char *, const char *);
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,40 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _DMA_H_
#define _DMA_H_
struct SDMA
{
bool8 ReverseTransfer;
bool8 HDMAIndirectAddressing;
bool8 UnusedBit43x0;
bool8 AAddressFixed;
bool8 AAddressDecrement;
uint8 TransferMode;
uint8 BAddress;
uint16 AAddress;
uint8 ABank;
uint16 DMACount_Or_HDMAIndirectAddress;
uint8 IndirectBank;
uint16 Address;
uint8 Repeat;
uint8 LineCount;
uint8 UnknownByte;
uint8 DoTransfer;
};
#define TransferBytes DMACount_Or_HDMAIndirectAddress
#define IndirectAddress DMACount_Or_HDMAIndirectAddress
extern struct SDMA DMA[8];
bool8 S9xDoDMA (uint8);
void S9xStartHDMA (void);
uint8 S9xDoHDMA (uint8);
void S9xResetDMA (void);
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,68 @@
Control input names are completely defined by the individual ports. This
document is intended to collect the rules for all ports.
The various meta-characters in the rules are:
# - A number. The range is determined by the context
## - A two-digit number (i.e. with leading zeros)
[...] - Something optional
(...) - For grouping with |
| - "or", choose one of the options.
<...> - A named field
{...} - A list of possible values. Multiple values may be used, but they
must be in the order listed and joined with +-signs.
"" - 'ditto', used to indicate the same list as the above line.
================================================================================
Unix
================================================================================
Input names:
Jxx:Axis# Axis # on joystick xx. Axis0 may be
Up/Down, and Axis1 Left/Right.
Jxx:B# Button # on joystick xx.
Jxx:{M1,M2,M3,M4,M5,M6,M7,M8}+B# Used with the 'JSx Meta#' port
Jxx:{M1,M2,M3,M4,M5,M6,M7,M8}+Axis# command.
Jxx:X+B# Used to 'define' this key for all
Jxx:X+Axis# combinations of JS Meta.
Port-specific Commands:
JSx Meta# Used to specify modifier keys (i.e. Shift, Control) to
affect the specified joystick. For example, you could
map J00:B20 to "JS0 Meta1", then map J00:B0 to "Joypad1
A" and J00:M1+B0 to "Joypad1 Turbo A". '#' may range
from 1-8.
Jsx ToggleMeta# Like the above, but toggles the meta-state each time
the button is pressed.
================================================================================
Unix/X11
================================================================================
Keyboard Input:
Note that only one keyboard (K00) is currently supported. If you know how
to support multiple keyboards (and can test it!), feel free to fix x11.cpp
and delete this note.
Keyboard modifiers are S=Shift, C=Control, A=Alt, M=Meta. Combine them in
order, i.e. all 4 would be "SCAM".
Kxx:<keyname> Key names are as recognized by XStringToKeysym.
Kxx:<mods>+<keyname> Note however that keys are mapped by keycode,
so for example on a standard qwerty keyboard
"K00:colon" and "K00:semicolon" are identical.
Pointer Input:
Note that only one mouse (M00) is currently supported. If you know how to
support multiple pointing devices (and can test it!), feel free to fix
x11.cpp and delete this note.
Mxx:Pointer Map the mouse pointer. If someone has a mouse
Mxx:Pointer# device with multiple pointers, fix x11.cpp to
report that and you can use the second syntax.
Mxx:B# Mouse buttons.

@ -0,0 +1,97 @@
This lists the available commands, excluding the ones you get back from
S9xGetAllSnes9xCommands(). The various meta-characters are:
# - A number. The range is determined by the context
## - A two-digit number (i.e. with leading zeros)
[...] - Something optional
(...) - For grouping with |
| - "or", choose one of the options.
<...> - A named field
{...} - A list of possible values. Multiple values may be used, but they
must be in the order listed and joined with +-signs.
"" - 'ditto', used to indicate the same list as the above line.
Speeds are: Var, Slow, Med, and Fast. 'Var' starts slow and speeds up as the
button is held.
Axes are: Left/Right, Right/Left, Up/Down, Down/Up, Y/A, A/Y, X/B, B/X, L/R,
and R/L. Negative is listed first (i.e. "Y/A" means negative deflection is
towards Y, while "A/Y" means negative deflection is towards A).
AxisToPointer, ButtonToPointer, and AxisToButtons allow for translating
between different input types. There are 8 'pointers' with IDs
PseudoPointerBase+0 to PseudoPointerBase+7, and 256 'buttons' with IDs
PseudoButtonBase+0 to PseudoButtonBase+255. So for example,
"AxisToButtons 0/255 T=50%" would take the axis data, and do
S9xReportButton(PseudoButtonBase+0,1) when said axis goes past 50% in the
negative direction and S9xReportButton(PseudoButtonBase+255,1) when it goes
over 50% deflection in the positive direction. Similarly, it will do
S9xReportButton(...,0) when the deflection drops under 50% in either
direction. "ButtonToPointer 1u Slow" would move the pointer with ID
PseudoPointerBase+0 up one pixel per frame as long as the button is pressed
(reporting this change at the end of each frame).
---------------
Button Commands
---------------
Joypad# {Up, Down, Left, Right, A, B, X, Y, L, R, Start, Select}
Joypad# Turbo ""
Joypad# Sticky ""
Joypad# StickyTurbo ""
Joypad# ToggleTurbo ""
Joypad# ToggleSticky ""
Joypad# ToggleStickyTurbo ""
Mouse# (L|R|LR)
Superscope AimOffscreen
Superscope {Fire, Cursor, ToggleTurbo, Pause}
Superscope AimOffscreen ""
Justifier# AimOffscreen
Justifier# {Trigger, Start}
Justifier# AimOffscreen ""
ButtonToPointer #[u|d][l|r] <speed> ; NOTE: "# <speed>" is invalid
-------------
Axis Commands
-------------
Joypad# Axis <axis> T=#% ; T = 0.1 to 100 by tenths
AxisToButtons #/# T=#% ; neg then pos, range 0-255, T as above
AxisToPointer #(h|v) [-]<speed> ; NOTE: '-' inverts the axis
----------------
Pointer Commands
----------------
Pointer {Mouse1, Mouse2, Superscope, Justifier1, Justifier2}
------
Multis
------
Multis are a type of button command. The basic format of a multi is "{...}",
where the '...' consists of 1 or more valid non-multi button command
strings. The braces are literal, not metacharacters. Subcommands separated
by commas are executed one after the next. Semicolons skip one frame before
continuing subcommand execution. Semicolons may be repeated. When the multi
button is pressed, each subcommand is 'pressed', and when the multi button
is released each subcommand is 'released'.
There are also press-only multis, defined as "+{...}". These act just like
regular multis, with two differences: the multi is only run when you press
the button (release is ignored), and each subcommand must be prefixed with
'+' or '-' to indicate whether the the subcommand should be pressed or
released.
For example: {Joypad1 A,Joypad2 A;Joypad3 A;;;;;QuickSave000}
This presses (or releases) A on pads 1 and 2, then waits one frame, then
presses A on pad 3, then waits 5 frames, then saves to snapshot 0 (on press
only).
You may access the multi number in the returned s9xcommand_t structure as
cmd.button.multi_idx. This may be used to assign the same multi to multiple
buttons:
MULTI#<num> ; NOTE: that's a literal octothorpe

@ -0,0 +1,369 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en-US">
<head>
<meta http-equiv="content-type" content="text/html;charset=iso-8859-1">
<meta http-equiv="Content-Style-Type" content="text/css">
<meta name="description" content="How to Port Snes9x to a New Platform">
<style type="text/css">
<!-- ul { list-style-type:none } h2 { margin-top:3em } h3 { margin-top:2em } -->
</style>
<title>Porting Snes9x</title>
</head>
<body>
<h1 style="text-align:center">How to Port Snes9x to a New Platform</h1>
<div style="text-align:right">
Version: 1.60<br>
</div>
<h2>Introduction</h2>
<p>
This is brief description of the steps to port Snes9x to the new platform. It describes what code you have to write and what functions exist that you can make use of. It also gives some insights as to how Snes9x actually works, although that will be subject of another document yet to be written.
</p>
<h2>System Requirements</h2>
<p>
A C++ compiler. For the most part Snes9x really isn't written in C++, it just uses the C++ compiler as a &ldquo;better C&rdquo; compiler to get inline functions and so on. GCC is good for compiling Snes9x (<a href="http://gcc.gnu.org/">http://gcc.gnu.org/</a>).
</p>
<p>
A fast CPU. SNES emulation is very compute intensive; two, or sometimes three CPUs to emulate, an 8-channel 16-bit stereo sound digital signal processor with real-time sample decompression, filter and echo effects, two custom graphics processor chips that can produce transparency, scaling, rotation and window effects in 32768 colors, and finally hardware DMA all take their toll on the host CPU.
</p>
<p>
Enough RAM. Snes9x uses 8MB to load SNES ROM images and several MB for emulating sound, graphics, custom chips, and so on.
</p>
<p>
A 16-bit color (two bytes per pixel) or deeper display, at least 512*478 pixels in resolution. Pixel format conversion may be required before you place the rendered SNES screen on to the display.
</p>
<p>
Snes9x outputs 16-bit stereo digital sound data. Ports may convert it from there to 8-bit or mono. Some ports can use interrupts or callbacks from the sound system to know when more sound data is required, most other ports have to periodically poll the host sound system to see if more data is required; if it is then the sound mixing code is called to fill the sound buffer with SNES sound data, which then can be passed on to the host sound system. Sound data is generated as an array of bytes (<code>uint8</code>) for 8-bit sound or shorts (<code>int16</code>) for 16-bit data. Stereo sound data generates twice as many samples, with each channel's samples interleaved, first left's then right's.
</p>
<p>
For the user to be able to control and play SNES games, some form of input device is required, a joypad or keyboard, for example. The real SNES can have 2 eight-button digital joypads connected to it or 5 joypads when an optional multi-player adaptor is connected, although most games only require a single joypad. Access to all eight buttons and the direction pad, of course, are usually required by most games. Snes9x does emulate the multi-player adaptor hardware, if you were wondering, but its still up to you to provide the emulation of the individual joypads.
</p>
<p>
The real SNES also has a SNES mouse, Super Scope and Justifier (light-gun) available as optional extras. Snes9x can emulate all of these using some form of pointing device, usually the host system's mouse.
</p>
<p>
Some SNES game cartridges contains a small amount of extra RAM and a battery, so ROMs could save a player's progress through a game for games that takes many hours to play from start to finish. Snes9x simulates this S-RAM by saving the contents of the area of memory occupied by the S-RAM into a file then automatically restoring it again the next time the user plays the same game. If the hardware you're porting to doesn't have a storage media available then you could be in trouble.
</p>
<p>
Snes9x also implements freeze-game files which can record the state of the SNES hardware and RAM at a particular point in time and can restore it to that exact state at a later date - the result is that users can save a game at any point, not just at save-game or password points provided by the original game coders. Each freeze file is over 400k in size. To help save disk space, Snes9x can be compiled with zlib (<a href="http://www.zlib.net/">http://www.zlib.net/</a>), which is used to GZIP compress the freeze files, reducing the size to typically below 100k. zlib is also used to load GZIP or ZIP compressed ROM images. Additionally, Snes9x supports JMA archives compressed with NSRT (<a href="http://nsrt.edgeemu.com/">http://nsrt.edgeemu.com/</a>).
</p>
<h2>Compile-Time Options</h2>
<h3><code>DEBUGGER</code></h3>
<p>
Enables extra code to assist you in debugging SNES ROMs. The debugger has only ever been a quick-hack and user-interface to debugger facilities is virtually non-existent. Most of the debugger information is output via stdout and enabling the debugger slows the whole emulator down slightly. However, the debugger options available are very powerful; you could use it to help get your port working. You probably still want to ship the finished version with the debugger disabled, it will only confuse non-technical users.
</p>
<h3><code>RIGHTSHIFT_IS_SAR</code></h3>
<p>
Define this if your compiler uses shift right arithmetic for signed values. For example, GCC and Visual C++ use shift right arithmetic.
</p>
<h3><code>ZLIB / UNZIP_SUPPORT / JMA_SUPPORT</code></h3>
<p>
Define these if you want to support GZIP/ZIP/JMA compressed ROM images and GZIP compressed freeze-game files.
</p>
<h3><code>USE_OPENGL</code></h3>
<p>
Define this and set <code>Settings.OpenGLEnable</code> to <code>true</code>, then you'll get the rendered SNES image as one OpenGL texture.
</p>
<h3>Typical Options Common for Most Platforms</h3>
<p><code>
ZLIB<br>
UNZIP_SUPPORT<br>
JMA_SUPPORT<br>
RIGHTSHIFT_IS_SAR<br>
</code></p>
<h2>Editing port.h</h2>
<p>
You may need to edit <code>port.h</code> to fit Snes9x to your system.
</p>
<p>
If the byte ordering of your system is least significant byte first, make sure <code>LSB_FIRST</code> is defined, otherwise make sure it's not defined.
</p>
<p>
You'll need to make sure what pixel format your system uses for 16-bit colors (<code>RGB565</code>, <code>RGB555</code>, <code>BGR565</code> or <code>BGR555</code>), and if it's not <code>RGB565</code>, define <code>PIXEL_FORMAT</code> to it so that Snes9x will use it to render the SNES screen. For example, Windows uses <code>RGB565</code>, Mac OS X uses <code>RGB555</code>. If your system supports more than one pixel format, you can define <code>GFX_MULTI_FORMAT</code> and change Snes9x's pixel format dynamically by calling <code>S9xSetRenderPixelFormat</code> function. If your system is 24 or 32-bit only, then don't define anything; instead write a conversion routine that will take a complete rendered 16-bit SNES screen in <code>RGB565</code> format and convert to the format required to be displayed on your system.
</p>
<p>
<code>port.h</code> also typedefs some types; <code>uint8</code> for an unsigned 8-bit quantity, <code>uint16</code> for an unsigned 16-bit quantity, <code>uint32</code> for a 32-bit unsigned quantity and <code>bool8</code> for a <code>true</code>/<code>false</code> type. Signed versions are also typedef'ed.
</p>
<h2>Controllers Management</h2>
<p>
Read <code>controls.h</code>, <code>crosshair.h</code>, <code>controls.txt</code> and <code>control-inputs.txt</code> for details. This section is the minimal explanation to get the SNES controls workable.
</p>
<p>
The real SNES allows several different types of devices to be plugged into the game controller ports. The devices Snes9x emulates are a joypad, multi-player adaptor known as the Multi Player 5 or Multi Tap (allowing a further 4 joypads to be plugged in), a 2-button mouse, a light gun known as the Super Scope, and a light gun known as the Justifier.
</p>
<p>
In your initialization code, call <code>S9xUnmapAllControl</code> function.
</p>
<p>
Map any IDs to each SNES controller's buttons and pointers. (ID 249-255 are reserved).
</p>
<p>
Typically, use <code>S9xMapPointer</code> function for the pointer of the SNES mouse, Super Scope and Justifier, <code>S9xMapButton</code> function for other buttons. Set <code>poll</code> to <code>false</code> for the joypad buttons, <code>true</code> for the other buttons and pointers.
</p>
<p>
<code>S9xMapButton(k1P_A_Button, s9xcommand_t cmd = S9xGetCommandT(&quot;Joypad1 A&quot;), false);</code>
</p>
<p>
In your main emulation loop, before <code>S9xMainLoop</code> function is called, check your system's keyboard/joypad, and call <code>S9xReportButton</code> function to report the states of the SNES joypad buttons to Snes9x. Checking and reporting input immediately before <code>S9xMainLoop</code> is preferred to minimize input latency.
</p>
<p>
<code>void MyMainLoop (void)<br>
{<br>
&nbsp;&nbsp;&nbsp;&nbsp;MyReportButttons();<br>
&nbsp;&nbsp;&nbsp;&nbsp;S9xMainLoop();<br>
}</code>
</p>
<p>
<code>void MyReportButtons (void)<br>
{<br>
&nbsp;&nbsp;&nbsp;&nbsp;S9xReportButton(k1P_A_Button, (key_is_pressed ? true : false));<br>
}</code>
</p>
<p>
Prepare your <code>S9xPollButton</code> and <code>S9xPollPointer</code> function to reply Snes9x's request for other buttons/cursors states.
</p>
<p>
Call <code>S9xSetController</code> function. It connects each input device to each SNES input port.<br>
Here's typical controller settings that is used by the real SNES games:
</p>
<p>Joypad<br>
<code>S9xSetController(0, CTL_JOYPAD, 0, 0, 0, 0);<br>
S9xSetController(1, CTL_JOYPAD, 1, 0, 0, 0);</code>
</p>
<p>Mouse (port 1)<br>
<code>S9xSetController(0, CTL_MOUSE, 0, 0, 0, 0);<br>
S9xSetController(1, CTL_JOYPAD, 1, 0, 0, 0);</code>
</p>
<p>Mouse (port 2)<br>
<code>S9xSetController(0, CTL_JOYPAD, 0, 0, 0, 0);<br>
S9xSetController(1, CTL_MOUSE, 1, 0, 0, 0);</code>
</p>
<p>Super Scope<br>
<code>S9xSetController(0, CTL_JOYPAD, 0, 0, 0, 0);<br>
S9xSetController(1, CTL_SUPERSCOPE, 0, 0, 0, 0);</code>
</p>
<p>Multi Player 5<br>
<code>S9xSetController(0, CTL_JOYPAD, 0, 0, 0, 0);<br>
S9xSetController(1, CTL_MP5, 1, 2, 3, 4);</code>
</p>
<p>Justifier<br>
<code>S9xSetController(0, CTL_JOYPAD, 0, 0, 0, 0);<br>
S9xSetController(1, CTL_JUSTIFIER, 0, 0, 0, 0);</code>
</p>
<p>Justifier (2 players)<br>
<code>S9xSetController(0, CTL_JOYPAD, 0, 0, 0, 0);<br>
S9xSetController(1, CTL_JUSTIFIER, 1, 0, 0, 0);</code>
</p>
<h2>Existing Interface Functions</h2>
<h3><code>bool8 Memory.Init (void)</code></h3>
<p>
Allocates and initializes several major lumps of memory, for example the SNES ROM and RAM arrays, tile cache arrays, etc. Returns <code>false</code> if memory allocation fails.
</p>
<h3><code>void Memory.Deinit (void)</code></h3>
<p>
Deallocates the memory allocations made by <code>Memory.Init</code> function.
</p>
<h3><code>bool8 S9xGraphicsInit (void)</code></h3>
<p>
Allocates and initializes several lookup tables used to speed up SNES graphics rendering. Call after you have initialized the <code>GFX.Screen</code> and <code>GFX.Pitch</code> values. Returns <code>false</code> if memory allocation fails.
</p>
<h3><code>void S9xGraphicsDeinit (void)</code></h3>
<p>
Deallocates the memory allocations made by <code>S9xGraphicsInit</code> function.
</p>
<h3><code>bool8 S9xInitAPU (void)</code></h3>
<p>
Allocates and initializes several arrays used by the sound CPU and sound generation code. Returns <code>false</code> if memory allocation fails.
</p>
<h3><code>void S9xDeinitAPU (void)</code></h3>
<p>
Deallocates the allocations made by <code>S9xInitAPU</code> function.
</p>
<h3><code>bool8 S9xInitSound (int buffer_ms)</code></h3>
<p>
Allocates memory for mixing and queueing SNES sound data, does more sound code initialization and opens the host system's sound device by calling <code>S9xOpenSoundDevice</code>, a function you must provide. Before calling this function you must set up <code>Settings.SoundSync</code>, <code>Settings.SoundPlaybackRate</code>, and <code>Settings.SoundInputRate</code>. (see section below)<br>
<code>buffer_ms</code>, given in milliseconds, is the memory buffer size for queueing sound data. Leave this at 0 to use the suggested default.<br>
</p>
<h3><code>void S9xReset (void)</code></h3>
<p>
Resets the SNES emulated hardware back to the state it was in at &ldquo;switch-on&rdquo; except the S-RAM area is preserved (&ldquo;hardware reset&rdquo;). The effect is it resets the current game back to the start. This function is automatically called by <code>Memory.LoadROM</code> function.
</p>
<h3><code>void S9xSoftReset (void)</code></h3>
<p>
Similar to <code>S9xReset</code> function, but &ldquo;software reset&rdquo; as you press the SNES reset button.
</p>
<h3><code>bool8 Memory.LoadROM (const char *filepath)</code></h3>
<p>
Attempts to load the specified ROM image filename into the emulated ROM area. There are many different SNES ROM image formats and the code attempts to auto-detect as many different types as it can and in a vast majority of the cases gets it right.<br>
There are several ROM image options in the <code>Settings</code>structure; allow the user to set them before calling <code>Memory.LoadROM</code> function, or make sure they are all reset to default values before each call to <code>Memory.LoadROM</code> function. See <code>Settings.ForceXXX</code> in <code>snes9x.h</code>.
</p>
<h3><code>bool8 Memory.LoadMultiCart (const char *cartApath, const char *cartBpath)</code></h3>
<p>
Attempts to load multiple ROM images into the emulated ROM area, for the multiple cartridge systems such as Sufami Turbo, Same Game, etc.
</p>
<h3><code>bool8 Memory.LoadSRAM (const char *filepath)</code></h3>
<p>
Call this function to load the associated S-RAM save file (if any). The filename should be based on the ROM image name to allow easy linkage. The current ports change the directory and the filename extension of the ROM filename to derive the S-RAM filename.
</p>
<h3><code>bool8 Memory.SaveSRAM (const char *filepath)</code></h3>
<p>
Call this function to save the emulated S-RAM area into a file so it can be restored again the next time the user wants to play the game. Remember to call this when just before the emulator exits or when the user has been playing a game and is about to load another one.
</p>
<h3><code>void S9xMainLoop (void)</code></h3>
<p>
The emulator main loop. Call this from your own main loop that calls this function (if a ROM image is loaded and the game is not paused), processes any pending host system events, then goes back around the loop again until the emulator exits. <code>S9xMainLoop</code> function normally returns control to your main loop once every emulated frame, when it reaches the start of scan-line zero. However it may take a few frames when a huge memory transfer is being emulated. The function can return more often if the <code>DEBUGGER</code> compile-time flag is defined and the CPU has hit a break point, or the <code>DEBUG_MODE_FLAG</code> bit is set in <code>CPU.Flags</code> or instruction single-stepping is enabled.
</p>
<h3><code>void S9xMixSamples (uint8 *buffer, int sample_count)</code></h3>
<p>
Call this function from your host sound system handling code to fill <code>buffer</code> with ready-mixed SNES sound data. The buffer will be filled with an array of <code>sample_count</code> <code>int16</code>. Samples are in pairs, first a left channel sample followed by the right channel sample.<br>
If there are fewer queued samples than you request by <code>sample_count</code>, the function fills <code>buffer</code> with silent sound data and returns <code>false</code>.
</p>
<h3><code>int S9xGetSampleCount (void)</code></h3>
<p>
Returns the number of sound samples available in the buffer for your configured playback settings.
</p>
<h3><code>void S9xSetSamplesAvailableCallback (void (*) samples_available (void *), void *data)</code></h3>
<p>
Call this function to set up a callback that is run when sound samples are made available. <code>samples_available</code> is a function you provide that returns <code>void</code> and takes a pointer as an argument. <code>data</code> is a pointer that you may wish to pass to your callback or can be <code>NULL</code>. Inside this function is the only time that it is safe to access Snes9x's sound data. Ideally, all samples reported available will be removed from Snes9x's buffer when this function is called and moved to either the sound driver output or a separate buffer that can be locked in a thread-safe fashion. If you are using a callback-oriented sound API, it is recommended to set up a separate buffer that can accumulate at least enough samples to fulfill more than one host sound driver callback request.<br>
If you wish to disable a callback you have set up or need to temporarily shut down your sound system, you may pass <code>NULL</code> for both arguments.
</p>
<h3><code>bool8 S9xSyncSound (void)</code></h3>
<p>
Call this function to synchronize the sound buffers to the game state. This will call the <code>samples_available</code> function to try and move sound data out of Snes9x. If Snes9x is unable to make enough space to accumulate the sound data for a whole video frame, this function will return <code>false</code>. In this case, you may wish to wait until your host sound system uses the available samples, and <code>S9xSyncSound</code> returns <code>true</code> before continuing to execute <code>S9xMainLoop</code>.
</p>
<h3><code>bool8 S9xSetSoundMute (bool8 mute)</code></h3>
<p>
Call with a <code>true</code> parameter to cause the <code>S9xMixSamples</code> to fill the output buffer with silence, instead of filling the output buffer with sound data. Useful if your sound system is interrupt or callback driven and the game has been paused either directly or indirectly because the user is interacting with the emulator's user interface in some way.
</p>
<h3><code>bool8 S9xFreezeGame (const char *filepath)</code></h3>
<p>
Call this function to record the current SNES hardware state into a file, the file can be loaded back using <code>S9xUnfreezeGame</code> function at a later date effectively restoring the current game to exact same spot. Call this function while you're processing any pending system events when <code>S9xMainLoop</code> function has returned control to you in your main loop.
</p>
<h3><code>bool8 S9xUnfreezeGame (const char *filepath)</code></h3>
<p>
Restore the SNES hardware back to the exactly the state it was in when <code>S9xFreezeGame</code> function was used to generate the file specified. You have to arrange the correct ROM is already loaded using <code>Memory.LoadROM</code> function, an easy way to arrange this is to base freeze-game filenames on the ROM image name. The UNIX/Linux ports load freeze-game files when the user presses a function key, with the names romfilename.000 for F1, romfilename.001 for F2, etc. Games are frozen in the first place when the user presses Shift-function key. You could choose some other scheme.
</p>
<h3><code>void S9xDumpSPCSnapshot (void)</code></h3>
<p>
Call this funtion to make a so-called SPC file, a snapshot of SNES sound state. Actual dump occurs at the first key-on event after this function is called.
</p>
<h3><code>void S9xSetInfoString (const char *string)</code></h3>
<p>
Call this function if you want to show a message onto the SNES screen.
</p>
<h3>Other Available Functions</h3>
<p>
See <code>movie.h</code> and <code>movie.cpp</code> to support the Snes9x movie feature.<br>
See <code>cheats.h</code>, <code>cheats.cpp</code> and <code>cheats2.cpp</code> to support the cheat feature.
</p>
<h2>Interface Functions You Need to Implement</h2>
<h3><code>bool8 S9xOpenSnapshotFile (const char *filepath, bool8 read_only, STREAM *file)</code></h3>
<p>
This function opens a freeze-game file. <code>STREAM</code> is defined as a <code>gzFile</code> if <code>ZLIB</code> is defined else it's defined as <code>FILE *</code>. The <code>read_only</code> parameter is set to <code>true</code> when reading a freeze-game file and <code>false</code> when writing a freeze-game file. Open the file <code>filepath</code> and return its pointer <code>file</code>.
</p>
<h3><code>void S9xCloseSnapshotFile (STREAM file)</code></h3>
<p>
This function closes the freeze-game file opened by <code>S9xOpenSnapshotFile</code> function.
</p>
<h3><code>void S9xExit (void)</code></h3>
<p>
Called when some fatal error situation arises or when the &ldquo;q&rdquo; debugger command is used.
</p>
<h3><code>bool8 S9xInitUpdate (void)</code></h3>
<p>
Called just before Snes9x begins to render an SNES screen. Use this function if you should prepare before drawing, otherwise let it empty.
</p>
<h3><code>bool8 S9xDeinitUpdate (int width, int height)</code></h3>
<p>
Called once a complete SNES screen has been rendered into the <code>GFX.Screen</code> memory buffer, now is your chance to copy the SNES rendered screen to the host computer's screen memory. The problem is that you have to cope with different sized SNES rendered screens: 256*224, 256*239, 512*224, 512*239, 512*448 and 512*478.
</p>
<h3><code>void S9xMessage (int type, int number, const char *message)</code></h3>
<p>
When Snes9x wants to display an error, information or warning message, it calls this function. Check in <code>messages.h</code> for the types and individual message numbers that Snes9x currently passes as parameters.<br>
The idea is display the message string so the user can see it, but you choose not to display anything at all, or change the message based on the message number or message type.<br>
Eventually all debug output will also go via this function, trace information already does.
</p>
<h3><code>bool8 S9xOpenSoundDevice (void)</code></h3>
<p>
<code>S9xInitSound</code> function calls this function to actually open the host sound device.
</p>
<h3><code>const char *S9xGetFilename (const char *extension, enum s9x_getdirtype dirtype)</code></h3>
<p>
When Snes9x needs to know the name of the cheat/IPS file and so on, this function is called. Check <code>extension</code> and <code>dirtype</code>, and return the appropriate filename. The current ports return the ROM file path with the given extension.
</p>
<h3><code>const char *S9xGetFilenameInc (const char *extension, enum s9x_getdirtype dirtype)</code></h3>
<p>
Almost the same as <code>S9xGetFilename</code> function, but used for saving SPC files etc. So you have to take care not to delete the previously saved file, by increasing the number of the filename; romname.000.spc, romname.001.spc, ...
</p>
<h3><code>const char *S9xGetDirectory (enum s9x_getdirtype dirtype)</code></h3>
<p>
Called when Snes9x wants to know the directory <code>dirtype</code>.
</p>
<h3><code>const char *S9xBasename (const char *path)</code></h3>
<p>
Called when Snes9x wants to know the name of the ROM image. Typically, extract the filename from <code>path</code> and return it.
</p>
<h3><code>void S9xAutoSaveSRAM (void)</code></h3>
<p>
If <code>Settings.AutoSaveDelay</code> is not zero, Snes9x calls this function when the contents of the S-RAM has been changed. Typically, call <code>Memory.SaveSRAM</code> function from this function.
</p>
<h3><code>void S9xToggleSoundChannel (int c)</code></h3>
<p>
If your port can match Snes9x's built-in <code>SoundChannelXXX</code> command (see <code>controls.cpp</code>), you may choose to use this function. Otherwise return <code>NULL</code>. Basically, turn on/off the sound channel <code>c</code> (0-7), and turn on all channels if <code>c</code> is 8.
</p>
<h3><code>void S9xSyncSpeed (void)</code></h3>
<p>
Called at the end of <code>S9xMainLoop</code> function, when emulating one frame has been done. You should adjust the frame rate in this function.
</p>
<h2>Global Variables</h2>
<h3><code>uint16 *GFX.Screen</code></h3>
<p>
A <code>uint16</code> array pointer to (at least) 2*512*478 bytes buffer where Snes9x puts the rendered SNES screen. However, if your port will not support hires mode (<code>Settings.SupportHiRes = false</code>), then a 2*256*239 bytes buffer is allowed. You should allocate the space by yourself. As well you can change the <code>GFX.Screen</code> value after <code>S9xDeinitUpdate</code> function is called so that double-buffering will be easy.
</p>
<h3><code>uint32 GFX.Pitch</code></h3>
<p>
Bytes per line (not pixels per line) of the <code>GFX.Screen</code> buffer. Typically set it to 1024. When the SNES screen is 256 pixels width and <code>Settings.OpenGLEnable</code> is <code>false</code>, last half 512 bytes per line are unused. When <code>Settings.OpenGLEnable</code> is <code>true</code>, <code>GFX.Pitch</code> is ignored.
</p>
<h3>Settings structure</h3>
<p>
There are various switches in the <code>Settings</code> structure. See <code>snes9x.h</code> for details. At least the settings below are required for good emulation.
</p>
<p><code>
memset(&amp;Settings, 0, sizeof(Settings));<br>
Settings.MouseMaster = true;<br>
Settings.SuperScopeMaster = true;<br>
Settings.JustifierMaster = true;<br>
Settings.MultiPlayer5Master = true;<br>
Settings.FrameTimePAL = 20000;<br>
Settings.FrameTimeNTSC = 16667;<br>
Settings.SoundPlaybackRate = 32040;<br>
Settings.SoundInputRate = 31947;<br>
Settings.SupportHiRes = true;<br>
Settings.Transparency = true;<br>
Settings.AutoDisplayMessages = true;<br>
Settings.InitialInfoStringTimeout = 120;<br>
Settings.HDMATimingHack = 100;<br>
Settings.BlockInvalidVRAMAccessMaster = true;
</code></p>
<h3><code>Settings.SoundInputRate</code></h3>
<p>
Adjusts the sound rate through resampling. For every <code>Settings.SoundInputRate</code> samples generated by the SNES, <code>Settings.SoundPlaybackRate</code> samples will be produced.
</p>
<p>
On a NTSC SNES, the video refresh rate is usually 60.09881Hz, while the audio output is 32040Hz. This means for every video frame displayed, 532.4565 sound frames are produced. A modern computer display or television will usually provide one of two video rates, 60.00Hz exactly or 59.94Hz to support broadcast television. The sound output, however, is usually fixed at exact values like 32000Hz, 44100Hz, or 48000Hz. This means that the closest video-to-sound ratio we can achieve, with 60.00Hz and 32000Hz, is 1.0/533.3333. This discrepancy means if we wait for vertical sync and update once per refresh, we will experience a sound deficit of 0.87 samples every frame, eventually causing a sound underrun and producing audio problems. <br>
</p>
<p>
Settings.SoundInputRate can be set to a value such that the ratio with the video rate matches the SNES output. A 60.00Hz display, for example, should be set to 60.00 / 60.09881 * 32040, which is about 31987. The Snes9x resampler will then stretch the sound samples to fit this ratio. It may be beneficial to provide an option for users to configure <code>Settings.SoundInputRate</code> to suit their own systems. Setting <code>Settings.SoundInputRate</code> to a value that matches the actual output rate (i.e. 31915hz for 59.94Hz) or lower will allow the users to eliminate crackling. A range of 31000hz to 33000hz should be inclusive enough for all displays. Use of this setting paired with the <code>S9xSyncSound</code> function can eliminate sound discontinuity. However, this concept is difficult for most users to understand. If possible, read the display's refresh rate and automatically calculate the appropriate input rate by default. In practice, few displays exceed a target of 60.00Hz, so a good default would be no greater than 31950Hz, but probably lower.
</p>
<h3><code>Settings.DynamicRateControl</code> and <code>S9xUpdateDynamicRate(int numerator, int denominator)</code></h3>
<p>
To eliminate buffer overflows and underflows, dynamic rate control can be implemented and used. While this can accommodate for Settings.SoundInputRate not being set correctly, it works better the closer that value is to ideal.
</p>
<p>
To implement, set <code>Settings.DynamicRateControl</code> to <code>true</code>. At the beginning of your <code>samples_available</code> callback, check the hardware output buffer's fill level. Report the amount of free space in the buffer as a fraction of total buffer size to <code>S9xUpdateDynamicRate</code>, and Snes9x will try and keep the buffer close to 50% full. To tune this, <code>Settings.DynamicRateLimit</code> can be changed. A larger value will increase the range of frequencies it can use, but will also cause more noticeable pitch changes.
</p>
<div style="text-align:right; margin-top:3em">
Original document (c) Copyright 1998 Gary Henderson;
Updated most recently by: 2019/2/26 BearOso
</div>
</body>
</html>

@ -0,0 +1,135 @@
These are all the known ports of Snes9x to other consoles/handhelds/etc as of
2011/08/28. They are all supported and welcomed on the official Snes9x site.
**If you know of anyone who is currently working on a port of Snes9x, or if you
have some interest in making a port, please have them go to the Snes9x forums
(http://www.snes9x.com/phpbb2/) and have them register an account there. After
that, speak to Ryan and/or Jerremy so you can be let into the devs area and the
git so you can have access to the most current code, collaborate with the other
developers, make the port officialized, etc.**
Ports and how to get them running are as follows:
*PSP Version of Snes9x*
Name: Snes9X Euphoria
Latest version: R5 Beta
Homepage/forum: http://code.google.com/p/snes9x-euphoria/
Maintainer: Open due to Zack discontinuing the port; he has made the source
code available. If you are interested in taking over the project or starting a
new port from scratch, let Ryan and/or Jerremy know ASAP.
HOW TO GET IT RUNNING:
*DISCLAIMER* You will have to do some Googling, including but not limited to:
* Downgrading/upgrading your firmware
* Checking if your PSP-2000 series can use Pandora's Battery
* Checking if your PSP-3000 series can use DaveeFTW's and/or some1's Downgrader
* Creating Pandora's Battery (as needed)
* Finding the hacks, HENs, CFWs, etc and how to use/install them
1. Make sure your PSP is hackable in some way. This means:
* All PSP-1000 series and certain PSP-2000 series can use Pandora Battery; this
would be considered fully hackable.
* PSP-2000 series that can't use Pandora Battery, just about all PSP-3000
series, and PSP Gos are hackable via other means (DaveeFTW's and/or some1's
Downgrader, etc); this would be considered partially hackable.
2. Make sure your PSP has custom firmware or a HEN that's useable (you'll have
to upgrade/downgrade the firmware as necessary). (Hint: I personally prefer
5.50 GEN-D3 if your PSP can use Pandora Battery; latest version of 6.20 or
6.35 PRO CFW if your PSP can't use Pandora Battery (if you can't downgrade past
6.35, then you should be able to use 6.39 and/or 6.60 PRO).)
3. When thats done, be sure to put the Snes9X Euphoria folder in /PSP/GAME on
your PSPs memory stick (PSP-1000/2000/3000 series) or internal memory
(PSP Go). Be sure to copy the ROMs into the roms folder, saves (*.srm, etc)
into the saves folder, and cheats into the cheats folder.
Note: as of right now, there is no one to maintain the PSP port; I am leaving
these instructions up just in case someone wants to take it over, and you can
use these tips to get other homebrews going.
*Wii/Gamecube version of Snes9x*
Name: Snes9X GX
Latest Version: 4.2.8
Homepage/forum: http://code.google.com/p/snes9x-gx
Maintainer: Tantric
HOW TO GET IT RUNNING:
*DISCLAIMER* You will have to do some Googling, including but not limited to:
* Finding and installing the latest versions of: Homebrew Channel, Snes9X GX
Channel (optional), and/or IOS58 installer (also optional; this is to make the
Snes9X GX channel work)
* Finding a modchip for your GameCube and installing it
Wii: You will need the latest Homebrew Channel installed on your Wii. After
that, copy and paste the apps folder onto the root of your SD card; same goes
for the snes9xgx folder. After that, copy over any ROMs you have to the
\snes9xgx\roms folder, save files (*.srm, etc) to the \snes9xgx\saves folder,
and cheats to the \snes9xgx\cheats folder.
In addition, there appears to be a channel for Snes9X GX; you will need the
Homebrew Channel installed and you MUST be on System Menu 4.3 so you can be on
IOS58 (or use the IOS58 installer). After that, you should be able to run the
installer from the Homebrew Channel, and you'll be good to go!
Gamecube: You might need a modchip.
*Android and iOS (Apple iPhone/iPod Touch) version of Snes9x*
Name: Snes9X EX
Latest Version: 1.4.2 (iOS); 1.4.7.1 (Android)
Homepage/forum: http://www.explusalpha.com/home/snes9x-ex
Maintainer: Rakashazi
HOW TO GET IT RUNNING:
*DISCLAIMER* You will have to do some Googling, including but not limited to:
* iOS (iPhone/iPod Touch) ONLY!!!: Jailbreaking your firmware
Android: Due to the Android Marketplace unfairly taking down Snes9X EX, you
will have to visit Rakashazi's website and download the apk either using your
PC (you'll have to connect the Android to your computer, mount the SD card,
then copy the apk to it) or your Android, then run package installer
(or easy installer is fine too) on your Android to install the app.
iOS: Youll have to jailbreak your firmware and install the Cydia app
installer. Then youll have to install the BigBoss repository within Cydia and
search for Snes9X EX; you may also want to search for the sshd and all needed
stuff for that, as its the only way you can put the ROMs, saves, etc onto your
iPhone/iPod Touch. After that you should be able to download and run from there
:)
Hint: a more detailed description of copying your ROMs/saves/etc over to the
iOS device can be found here:
http://snes9x.com/phpbb2/viewtopic.php?t=5107
*PS3 version of Snes9x*
Name: Snes9X PS3
Latest Version: 4.4.9
Homepage/forum: https://code.google.com/p/snes9x-ps3/ (although for some
reason, you may have to Google for the latest version)
Maintainer: Squarepusher
HOW TO GET IT RUNNING:
*DISCLAIMER* You will have to do some Googling, including but not limited to:
* Finding a HEN/Jailbreaker/CFW/etc onto your PS3 and installing it
* (if necessary) Downgrading/Upgrading your PS3's firmware
Youll have to install a HEN/Jailbreaker/CFW/etc on your PS3 (you might have to
upgrade or downgrade your PS3s firmware as needed). After that, it should be
as simple as copy the emulator, ROMs, saves, etc over to the PS3 and it should
work :)
*X-Box Version of Snes9x*
Name: Snes9xbox
Latest Version: V2 (V3 should be out soon)
Homepage/forum: http://forums.xbox-scene.com/index.php?showforum=96
HOW TO GET IT RUNNING:
*DISCLAIMER* You will need to do some Googling, including but not limited to:
* Finding a softmod (hack) or a modchip and installing/using it
* Finding a replacement dashboard such as XBMC
Really, it's not hard. Read the readme, and use a softmod or modchip and a
replacement dashboard. After that's installed, it should be as simple as
copying over the emulator, ROMs, saves, etc to the X-Box and it should work.
Updated most recently by: 2011/11/2 adventure_of_link

@ -0,0 +1,84 @@
***** Important notice ********************************************************
This document describes the snapshot file format for Snes9x 1.52 and later,
not compatible with 1.51.
*******************************************************************************
Snes9x snapshot file format: (may be gzip-compressed)
Begins with fixed length signature, consisting of a string, ':', a 4-digit
decimal version, and a '\n'.
#!s9xsnp:0006 <-- '\n' after the 6
Then we have various blocks. The block format is: 3-character block name,
':', 6-digit length, ':', then the data. Blocks are written in a defined
order. Structs are written packed with their members in a defined order, in
big-endian order where applicable.
NAM:000019:Chrono Trigger.zip
Currently defined blocks (in order) are:
Essential parts:
NAM - ROM filename, from Memory.ROMFilename. 0-terminated string.
CPU - struct SCPUState, CPU internal state variables.
REG - struct SRegisters, emulated CPU registers.
PPU - struct SPPU, PPU internal variables. Note that IPPU is never saved.
DMA - struct SDMA, DMA/HDMA state variables.
VRA - Memory.VRAM, 0x10000 bytes.
RAM - Memory.RAM, 0x20000 bytes (WRAM).
SRA - Memory.SRAM, 0x20000 bytes.
FIL - Memory.FillRAM, 0x8000 bytes (register backing store).
SND - All of sound emulated registers and state valiables.
CTL - struct SControlSnapshot, controller emulation.
TIM - struct STimings, variables about timings between emulated events.
Optional parts:
SFX - struct FxRegs_s, Super FX.
SA1 - struct SSA1, SA1 internal state variables.
SAR - struct SSA1Registers, SA1 emulated registers.
DP1 - struct SDSP1, DSP-1.
DP2 - struct SDSP2, DSP-2.
DP4 - struct SDSP4, DSP-4.
CX4 - Memory.C4RAM, 0x2000 bytes.
ST0 - struct SST010, ST-010.
OBC - struct SOBC1, OBC1 internal state variables.
OBM - Memory.OBC1RAM, 0x2000 byts.
S71 - struct SSPC7110Snapshot, SPC7110.
SRT - struct SSRTCSnapshot, S-RTC internal state variables.
CLK - struct SRTCData, S-RTC emulated registers.
BSX - struct SBSX, BS-X.
SHO - rendered SNES screen.
MOV - struct SnapshotMovieInfo.
MID - Some block of data the movie subsystem.
==================
Without changing the snapshot version number:
---------------------------------------------
Blocks may be safely added at the END of the file, as anything after the last
block is ignored. Blocks may not be moved or removed.
Blocks may not decrease in size. Say you decrease from 10 bytes to 5. Then
later you increase back to 8. The only way you could safely do this is if
bytes 5-7 still mean the same thing they meant when the block was 10 bytes
long.
Blocks may increase in size as you wish, as long as you can handle old
savestates with the old shorter size.
Struct members may not change in interpretation. New struct members may be
added (at the END!) only if you can cope with them being binary-0 in older
savestates. Struct members may not be removed or changed in size/type.
With changing the snapshot version number:
------------------------------------------
Blocks may be added, moved, or removed at will.
Blocks may decrease in size.
Struct members may be added, moved, or deleted, and their
interpretations/types may be changed. Use the 'debuted_in' and 'deleted_in'
fields to indicate when the new member debuted or the old member went away.

@ -0,0 +1,58 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "snes9x.h"
#include "memmap.h"
#ifdef DEBUGGER
#include "missing.h"
#endif
uint8 (*GetDSP) (uint16) = NULL;
void (*SetDSP) (uint8, uint16) = NULL;
void S9xResetDSP (void)
{
memset(&DSP1, 0, sizeof(DSP1));
DSP1.waiting4command = TRUE;
DSP1.first_parameter = TRUE;
memset(&DSP2, 0, sizeof(DSP2));
DSP2.waiting4command = TRUE;
memset(&DSP3, 0, sizeof(DSP3));
DSP3_Reset();
memset(&DSP4, 0, sizeof(DSP4));
DSP4.waiting4command = TRUE;
}
uint8 S9xGetDSP (uint16 address)
{
#ifdef DEBUGGER
if (Settings.TraceDSP)
{
sprintf(String, "DSP read: 0x%04X", address);
S9xMessage(S9X_TRACE, S9X_TRACE_DSP1, String);
}
#endif
return ((*GetDSP)(address));
}
void S9xSetDSP (uint8 byte, uint16 address)
{
#ifdef DEBUGGER
missing.unknowndsp_write = address;
if (Settings.TraceDSP)
{
sprintf(String, "DSP write: 0x%04X=0x%02X", address, byte);
S9xMessage(S9X_TRACE, S9X_TRACE_DSP1, String);
}
#endif
(*SetDSP)(byte, address);
}

@ -0,0 +1,447 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _DSP1_H_
#define _DSP1_H_
enum
{
M_DSP1_LOROM_S,
M_DSP1_LOROM_L,
M_DSP1_HIROM,
M_DSP2_LOROM,
M_DSP3_LOROM,
M_DSP4_LOROM
};
struct SDSP0
{
uint32 maptype;
uint32 boundary;
};
struct SDSP1
{
bool8 waiting4command;
bool8 first_parameter;
uint8 command;
uint32 in_count;
uint32 in_index;
uint32 out_count;
uint32 out_index;
uint8 parameters[512];
uint8 output[512];
int16 CentreX;
int16 CentreY;
int16 VOffset;
int16 VPlane_C;
int16 VPlane_E;
// Azimuth and Zenith angles
int16 SinAas;
int16 CosAas;
int16 SinAzs;
int16 CosAzs;
// Clipped Zenith angle
int16 SinAZS;
int16 CosAZS;
int16 SecAZS_C1;
int16 SecAZS_E1;
int16 SecAZS_C2;
int16 SecAZS_E2;
int16 Nx;
int16 Ny;
int16 Nz;
int16 Gx;
int16 Gy;
int16 Gz;
int16 C_Les;
int16 E_Les;
int16 G_Les;
int16 matrixA[3][3];
int16 matrixB[3][3];
int16 matrixC[3][3];
int16 Op00Multiplicand;
int16 Op00Multiplier;
int16 Op00Result;
int16 Op20Multiplicand;
int16 Op20Multiplier;
int16 Op20Result;
int16 Op10Coefficient;
int16 Op10Exponent;
int16 Op10CoefficientR;
int16 Op10ExponentR;
int16 Op04Angle;
int16 Op04Radius;
int16 Op04Sin;
int16 Op04Cos;
int16 Op0CA;
int16 Op0CX1;
int16 Op0CY1;
int16 Op0CX2;
int16 Op0CY2;
int16 Op02FX;
int16 Op02FY;
int16 Op02FZ;
int16 Op02LFE;
int16 Op02LES;
int16 Op02AAS;
int16 Op02AZS;
int16 Op02VOF;
int16 Op02VVA;
int16 Op02CX;
int16 Op02CY;
int16 Op0AVS;
int16 Op0AA;
int16 Op0AB;
int16 Op0AC;
int16 Op0AD;
int16 Op06X;
int16 Op06Y;
int16 Op06Z;
int16 Op06H;
int16 Op06V;
int16 Op06M;
int16 Op01m;
int16 Op01Zr;
int16 Op01Xr;
int16 Op01Yr;
int16 Op11m;
int16 Op11Zr;
int16 Op11Xr;
int16 Op11Yr;
int16 Op21m;
int16 Op21Zr;
int16 Op21Xr;
int16 Op21Yr;
int16 Op0DX;
int16 Op0DY;
int16 Op0DZ;
int16 Op0DF;
int16 Op0DL;
int16 Op0DU;
int16 Op1DX;
int16 Op1DY;
int16 Op1DZ;
int16 Op1DF;
int16 Op1DL;
int16 Op1DU;
int16 Op2DX;
int16 Op2DY;
int16 Op2DZ;
int16 Op2DF;
int16 Op2DL;
int16 Op2DU;
int16 Op03F;
int16 Op03L;
int16 Op03U;
int16 Op03X;
int16 Op03Y;
int16 Op03Z;
int16 Op13F;
int16 Op13L;
int16 Op13U;
int16 Op13X;
int16 Op13Y;
int16 Op13Z;
int16 Op23F;
int16 Op23L;
int16 Op23U;
int16 Op23X;
int16 Op23Y;
int16 Op23Z;
int16 Op14Zr;
int16 Op14Xr;
int16 Op14Yr;
int16 Op14U;
int16 Op14F;
int16 Op14L;
int16 Op14Zrr;
int16 Op14Xrr;
int16 Op14Yrr;
int16 Op0EH;
int16 Op0EV;
int16 Op0EX;
int16 Op0EY;
int16 Op0BX;
int16 Op0BY;
int16 Op0BZ;
int16 Op0BS;
int16 Op1BX;
int16 Op1BY;
int16 Op1BZ;
int16 Op1BS;
int16 Op2BX;
int16 Op2BY;
int16 Op2BZ;
int16 Op2BS;
int16 Op28X;
int16 Op28Y;
int16 Op28Z;
int16 Op28R;
int16 Op1CX;
int16 Op1CY;
int16 Op1CZ;
int16 Op1CXBR;
int16 Op1CYBR;
int16 Op1CZBR;
int16 Op1CXAR;
int16 Op1CYAR;
int16 Op1CZAR;
int16 Op1CX1;
int16 Op1CY1;
int16 Op1CZ1;
int16 Op1CX2;
int16 Op1CY2;
int16 Op1CZ2;
uint16 Op0FRamsize;
uint16 Op0FPass;
int16 Op2FUnknown;
int16 Op2FSize;
int16 Op08X;
int16 Op08Y;
int16 Op08Z;
int16 Op08Ll;
int16 Op08Lh;
int16 Op18X;
int16 Op18Y;
int16 Op18Z;
int16 Op18R;
int16 Op18D;
int16 Op38X;
int16 Op38Y;
int16 Op38Z;
int16 Op38R;
int16 Op38D;
};
struct SDSP2
{
bool8 waiting4command;
uint8 command;
uint32 in_count;
uint32 in_index;
uint32 out_count;
uint32 out_index;
uint8 parameters[512];
uint8 output[512];
bool8 Op05HasLen;
int32 Op05Len;
uint8 Op05Transparent;
bool8 Op06HasLen;
int32 Op06Len;
uint16 Op09Word1;
uint16 Op09Word2;
bool8 Op0DHasLen;
int32 Op0DOutLen;
int32 Op0DInLen;
};
struct SDSP3
{
uint16 DR;
uint16 SR;
uint16 MemoryIndex;
int16 WinLo;
int16 WinHi;
int16 AddLo;
int16 AddHi;
uint16 Codewords;
uint16 Outwords;
uint16 Symbol;
uint16 BitCount;
uint16 Index;
uint16 Codes[512];
uint16 BitsLeft;
uint16 ReqBits;
uint16 ReqData;
uint16 BitCommand;
uint8 BaseLength;
uint16 BaseCodes;
uint16 BaseCode;
uint8 CodeLengths[8];
uint16 CodeOffsets[8];
uint16 LZCode;
uint8 LZLength;
uint16 X;
uint16 Y;
uint8 Bitmap[8];
uint8 Bitplane[8];
uint16 BMIndex;
uint16 BPIndex;
uint16 Count;
int16 op3e_x;
int16 op3e_y;
int16 op1e_terrain[0x2000];
int16 op1e_cost[0x2000];
int16 op1e_weight[0x2000];
int16 op1e_cell;
int16 op1e_turn;
int16 op1e_search;
int16 op1e_x;
int16 op1e_y;
int16 op1e_min_radius;
int16 op1e_max_radius;
int16 op1e_max_search_radius;
int16 op1e_max_path_radius;
int16 op1e_lcv_radius;
int16 op1e_lcv_steps;
int16 op1e_lcv_turns;
};
struct SDSP4
{
bool8 waiting4command;
bool8 half_command;
uint16 command;
uint32 in_count;
uint32 in_index;
uint32 out_count;
uint32 out_index;
uint8 parameters[512];
uint8 output[512];
uint8 byte;
uint16 address;
// op control
int8 Logic; // controls op flow
// projection format
int16 lcv; // loop-control variable
int16 distance; // z-position into virtual world
int16 raster; // current raster line
int16 segments; // number of raster lines drawn
// 1.15.16 or 1.15.0 [sign, integer, fraction]
int32 world_x; // line of x-projection in world
int32 world_y; // line of y-projection in world
int32 world_dx; // projection line x-delta
int32 world_dy; // projection line y-delta
int16 world_ddx; // x-delta increment
int16 world_ddy; // y-delta increment
int32 world_xenv; // world x-shaping factor
int16 world_yofs; // world y-vertical scroll
int16 view_x1; // current viewer-x
int16 view_y1; // current viewer-y
int16 view_x2; // future viewer-x
int16 view_y2; // future viewer-y
int16 view_dx; // view x-delta factor
int16 view_dy; // view y-delta factor
int16 view_xofs1; // current viewer x-vertical scroll
int16 view_yofs1; // current viewer y-vertical scroll
int16 view_xofs2; // future viewer x-vertical scroll
int16 view_yofs2; // future viewer y-vertical scroll
int16 view_yofsenv; // y-scroll shaping factor
int16 view_turnoff_x; // road turnoff data
int16 view_turnoff_dx; // road turnoff delta factor
// drawing area
int16 viewport_cx; // x-center of viewport window
int16 viewport_cy; // y-center of render window
int16 viewport_left; // x-left of viewport
int16 viewport_right; // x-right of viewport
int16 viewport_top; // y-top of viewport
int16 viewport_bottom; // y-bottom of viewport
// sprite structure
int16 sprite_x; // projected x-pos of sprite
int16 sprite_y; // projected y-pos of sprite
int16 sprite_attr; // obj attributes
bool8 sprite_size; // sprite size: 8x8 or 16x16
int16 sprite_clipy; // visible line to clip pixels off
int16 sprite_count;
// generic projection variables designed for two solid polygons + two polygon sides
int16 poly_clipLf[2][2]; // left clip boundary
int16 poly_clipRt[2][2]; // right clip boundary
int16 poly_ptr[2][2]; // HDMA structure pointers
int16 poly_raster[2][2]; // current raster line below horizon
int16 poly_top[2][2]; // top clip boundary
int16 poly_bottom[2][2]; // bottom clip boundary
int16 poly_cx[2][2]; // center for left/right points
int16 poly_start[2]; // current projection points
int16 poly_plane[2]; // previous z-plane distance
// OAM
int16 OAM_attr[16]; // OAM (size, MSB) data
int16 OAM_index; // index into OAM table
int16 OAM_bits; // offset into OAM table
int16 OAM_RowMax; // maximum number of tiles per 8 aligned pixels (row)
int16 OAM_Row[32]; // current number of tiles per row
};
extern struct SDSP0 DSP0;
extern struct SDSP1 DSP1;
extern struct SDSP2 DSP2;
extern struct SDSP3 DSP3;
extern struct SDSP4 DSP4;
uint8 S9xGetDSP (uint16);
void S9xSetDSP (uint8, uint16);
void S9xResetDSP (void);
uint8 DSP1GetByte (uint16);
void DSP1SetByte (uint8, uint16);
uint8 DSP2GetByte (uint16);
void DSP2SetByte (uint8, uint16);
uint8 DSP3GetByte (uint16);
void DSP3SetByte (uint8, uint16);
uint8 DSP4GetByte (uint16);
void DSP4SetByte (uint8, uint16);
void DSP3_Reset (void);
extern uint8 (*GetDSP) (uint16);
extern void (*SetDSP) (uint8, uint16);
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,361 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "snes9x.h"
#include "memmap.h"
static void DSP2_Op01 (void);
static void DSP2_Op03 (void);
static void DSP2_Op05 (void);
static void DSP2_Op06 (void);
static void DSP2_Op09 (void);
static void DSP2_Op0D (void);
// convert bitmap to bitplane tile
static void DSP2_Op01 (void)
{
// Op01 size is always 32 bytes input and output
// The hardware does strange things if you vary the size
uint8 c0, c1, c2, c3;
uint8 *p1 = DSP2.parameters;
uint8 *p2a = DSP2.output;
uint8 *p2b = DSP2.output + 16; // halfway
// Process 8 blocks of 4 bytes each
for (int j = 0; j < 8; j++)
{
c0 = *p1++;
c1 = *p1++;
c2 = *p1++;
c3 = *p1++;
*p2a++ = (c0 & 0x10) << 3 |
(c0 & 0x01) << 6 |
(c1 & 0x10) << 1 |
(c1 & 0x01) << 4 |
(c2 & 0x10) >> 1 |
(c2 & 0x01) << 2 |
(c3 & 0x10) >> 3 |
(c3 & 0x01);
*p2a++ = (c0 & 0x20) << 2 |
(c0 & 0x02) << 5 |
(c1 & 0x20) |
(c1 & 0x02) << 3 |
(c2 & 0x20) >> 2 |
(c2 & 0x02) << 1 |
(c3 & 0x20) >> 4 |
(c3 & 0x02) >> 1;
*p2b++ = (c0 & 0x40) << 1 |
(c0 & 0x04) << 4 |
(c1 & 0x40) >> 1 |
(c1 & 0x04) << 2 |
(c2 & 0x40) >> 3 |
(c2 & 0x04) |
(c3 & 0x40) >> 5 |
(c3 & 0x04) >> 2;
*p2b++ = (c0 & 0x80) |
(c0 & 0x08) << 3 |
(c1 & 0x80) >> 2 |
(c1 & 0x08) << 1 |
(c2 & 0x80) >> 4 |
(c2 & 0x08) >> 1 |
(c3 & 0x80) >> 6 |
(c3 & 0x08) >> 3;
}
}
// set transparent color
static void DSP2_Op03 (void)
{
DSP2.Op05Transparent = DSP2.parameters[0];
}
// replace bitmap using transparent color
static void DSP2_Op05 (void)
{
// Overlay bitmap with transparency.
// Input:
//
// Bitmap 1: i[0] <=> i[size-1]
// Bitmap 2: i[size] <=> i[2*size-1]
//
// Output:
//
// Bitmap 3: o[0] <=> o[size-1]
//
// Processing:
//
// Process all 4-bit pixels (nibbles) in the bitmap
//
// if ( BM2_pixel == transparent_color )
// pixelout = BM1_pixel
// else
// pixelout = BM2_pixel
// The max size bitmap is limited to 255 because the size parameter is a byte
// I think size=0 is an error. The behavior of the chip on size=0 is to
// return the last value written to DR if you read DR on Op05 with
// size = 0. I don't think it's worth implementing this quirk unless it's
// proven necessary.
uint8 color;
uint8 c1, c2;
uint8 *p1 = DSP2.parameters;
uint8 *p2 = DSP2.parameters + DSP2.Op05Len;
uint8 *p3 = DSP2.output;
color = DSP2.Op05Transparent & 0x0f;
for (int32 n = 0; n < DSP2.Op05Len; n++)
{
c1 = *p1++;
c2 = *p2++;
*p3++ = (((c2 >> 4) == color) ? c1 & 0xf0: c2 & 0xf0) | (((c2 & 0x0f) == color) ? c1 & 0x0f: c2 & 0x0f);
}
}
// reverse bitmap
static void DSP2_Op06 (void)
{
// Input:
// size
// bitmap
for (int32 i = 0, j = DSP2.Op06Len - 1; i < DSP2.Op06Len; i++, j--)
DSP2.output[j] = (DSP2.parameters[i] << 4) | (DSP2.parameters[i] >> 4);
}
// multiply
static void DSP2_Op09 (void)
{
DSP2.Op09Word1 = DSP2.parameters[0] | (DSP2.parameters[1] << 8);
DSP2.Op09Word2 = DSP2.parameters[2] | (DSP2.parameters[3] << 8);
uint32 temp = DSP2.Op09Word1 * DSP2.Op09Word2;
DSP2.output[0] = temp & 0xFF;
DSP2.output[1] = (temp >> 8) & 0xFF;
DSP2.output[2] = (temp >> 16) & 0xFF;
DSP2.output[3] = (temp >> 24) & 0xFF;
}
// scale bitmap
static void DSP2_Op0D (void)
{
// Bit accurate hardware algorithm - uses fixed point math
// This should match the DSP2 Op0D output exactly
// I wouldn't recommend using this unless you're doing hardware debug.
// In some situations it has small visual artifacts that
// are not readily apparent on a TV screen but show up clearly
// on a monitor. Use Overload's scaling instead.
// This is for hardware verification testing.
//
// One note: the HW can do odd byte scaling but since we divide
// by two to get the count of bytes this won't work well for
// odd byte scaling (in any of the current algorithm implementations).
// So far I haven't seen Dungeon Master use it.
// If it does we can adjust the parameters and code to work with it
uint32 multiplier; // Any size int >= 32-bits
uint32 pixloc; // match size of multiplier
uint8 pixelarray[512];
if (DSP2.Op0DInLen <= DSP2.Op0DOutLen)
multiplier = 0x10000; // In our self defined fixed point 0x10000 == 1
else
multiplier = (DSP2.Op0DInLen << 17) / ((DSP2.Op0DOutLen << 1) + 1);
pixloc = 0;
for (int32 i = 0; i < DSP2.Op0DOutLen * 2; i++)
{
int32 j = pixloc >> 16;
if (j & 1)
pixelarray[i] = DSP2.parameters[j >> 1] & 0x0f;
else
pixelarray[i] = (DSP2.parameters[j >> 1] & 0xf0) >> 4;
pixloc += multiplier;
}
for (int32 i = 0; i < DSP2.Op0DOutLen; i++)
DSP2.output[i] = (pixelarray[i << 1] << 4) | pixelarray[(i << 1) + 1];
}
/*
static void DSP2_Op0D (void)
{
// Overload's algorithm - use this unless doing hardware testing
// One note: the HW can do odd byte scaling but since we divide
// by two to get the count of bytes this won't work well for
// odd byte scaling (in any of the current algorithm implementations).
// So far I haven't seen Dungeon Master use it.
// If it does we can adjust the parameters and code to work with it
int32 pixel_offset;
uint8 pixelarray[512];
for (int32 i = 0; i < DSP2.Op0DOutLen * 2; i++)
{
pixel_offset = (i * DSP2.Op0DInLen) / DSP2.Op0DOutLen;
if ((pixel_offset & 1) == 0)
pixelarray[i] = DSP2.parameters[pixel_offset >> 1] >> 4;
else
pixelarray[i] = DSP2.parameters[pixel_offset >> 1] & 0x0f;
}
for (int32 i = 0; i < DSP2.Op0DOutLen; i++)
DSP2.output[i] = (pixelarray[i << 1] << 4) | pixelarray[(i << 1) + 1];
}
*/
void DSP2SetByte (uint8 byte, uint16 address)
{
if ((address & 0xf000) == 0x6000 || (address >= 0x8000 && address < 0xc000))
{
if (DSP2.waiting4command)
{
DSP2.command = byte;
DSP2.in_index = 0;
DSP2.waiting4command = FALSE;
switch (byte)
{
case 0x01: DSP2.in_count = 32; break;
case 0x03: DSP2.in_count = 1; break;
case 0x05: DSP2.in_count = 1; break;
case 0x06: DSP2.in_count = 1; break;
case 0x09: DSP2.in_count = 4; break;
case 0x0D: DSP2.in_count = 2; break;
default:
#ifdef DEBUGGER
//printf("Op%02X\n", byte);
#endif
case 0x0f: DSP2.in_count = 0; break;
}
}
else
{
DSP2.parameters[DSP2.in_index] = byte;
DSP2.in_index++;
}
if (DSP2.in_count == DSP2.in_index)
{
DSP2.waiting4command = TRUE;
DSP2.out_index = 0;
switch (DSP2.command)
{
case 0x01:
DSP2.out_count = 32;
DSP2_Op01();
break;
case 0x03:
DSP2_Op03();
break;
case 0x05:
if (DSP2.Op05HasLen)
{
DSP2.Op05HasLen = FALSE;
DSP2.out_count = DSP2.Op05Len;
DSP2_Op05();
}
else
{
DSP2.Op05Len = DSP2.parameters[0];
DSP2.in_index = 0;
DSP2.in_count = 2 * DSP2.Op05Len;
DSP2.Op05HasLen = TRUE;
if (byte)
DSP2.waiting4command = FALSE;
}
break;
case 0x06:
if (DSP2.Op06HasLen)
{
DSP2.Op06HasLen = FALSE;
DSP2.out_count = DSP2.Op06Len;
DSP2_Op06();
}
else
{
DSP2.Op06Len = DSP2.parameters[0];
DSP2.in_index = 0;
DSP2.in_count = DSP2.Op06Len;
DSP2.Op06HasLen = TRUE;
if (byte)
DSP2.waiting4command = FALSE;
}
break;
case 0x09:
DSP2.out_count = 4;
DSP2_Op09();
break;
case 0x0D:
if (DSP2.Op0DHasLen)
{
DSP2.Op0DHasLen = FALSE;
DSP2.out_count = DSP2.Op0DOutLen;
DSP2_Op0D();
}
else
{
DSP2.Op0DInLen = DSP2.parameters[0];
DSP2.Op0DOutLen = DSP2.parameters[1];
DSP2.in_index = 0;
DSP2.in_count = (DSP2.Op0DInLen + 1) >> 1;
DSP2.Op0DHasLen = TRUE;
if (byte)
DSP2.waiting4command = FALSE;
}
break;
case 0x0f:
default:
break;
}
}
}
}
uint8 DSP2GetByte (uint16 address)
{
uint8 t;
if ((address & 0xf000) == 0x6000 || (address >= 0x8000 && address < 0xc000))
{
if (DSP2.out_count)
{
t = (uint8) DSP2.output[DSP2.out_index];
DSP2.out_index++;
if (DSP2.out_count == DSP2.out_index)
DSP2.out_count = 0;
}
else
t = 0xff;
}
else
t = 0x80;
return (t);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,468 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "snes9x.h"
#include "2xsai.h"
#define ALL_COLOR_MASK (FIRST_COLOR_MASK | SECOND_COLOR_MASK | THIRD_COLOR_MASK)
#define colorMask (((~RGB_LOW_BITS_MASK & ALL_COLOR_MASK) << 16) | (~RGB_LOW_BITS_MASK & ALL_COLOR_MASK))
#define qcolorMask (((~TWO_LOW_BITS_MASK & ALL_COLOR_MASK) << 16) | (~TWO_LOW_BITS_MASK & ALL_COLOR_MASK))
#define lowPixelMask ((RGB_LOW_BITS_MASK << 16) | RGB_LOW_BITS_MASK)
#define qlowpixelMask ((TWO_LOW_BITS_MASK << 16) | TWO_LOW_BITS_MASK)
static inline int GetResult (uint32, uint32, uint32, uint32);
static inline int GetResult1 (uint32, uint32, uint32, uint32, uint32);
static inline int GetResult2 (uint32, uint32, uint32, uint32, uint32);
static inline uint32 INTERPOLATE (uint32, uint32);
static inline uint32 Q_INTERPOLATE (uint32, uint32, uint32, uint32);
static inline int GetResult (uint32 A, uint32 B, uint32 C, uint32 D)
{
int x = 0, y = 0, r = 0;
if (A == C) x += 1; else if (B == C) y += 1;
if (A == D) x += 1; else if (B == D) y += 1;
if (x <= 1) r += 1;
if (y <= 1) r -= 1;
return (r);
}
static inline int GetResult1 (uint32 A, uint32 B, uint32 C, uint32 D, uint32 E)
{
int x = 0, y = 0, r = 0;
if (A == C) x += 1; else if (B == C) y += 1;
if (A == D) x += 1; else if (B == D) y += 1;
if (x <= 1) r += 1;
if (y <= 1) r -= 1;
return (r);
}
static inline int GetResult2 (uint32 A, uint32 B, uint32 C, uint32 D, uint32 E)
{
int x = 0, y = 0, r = 0;
if (A == C) x += 1; else if (B == C) y += 1;
if (A == D) x += 1; else if (B == D) y += 1;
if (x <= 1) r -= 1;
if (y <= 1) r += 1;
return (r);
}
static inline uint32 INTERPOLATE (uint32 A, uint32 B)
{
return (((A & colorMask) >> 1) + ((B & colorMask) >> 1) + (A & B & lowPixelMask));
}
static inline uint32 Q_INTERPOLATE (uint32 A, uint32 B, uint32 C, uint32 D)
{
uint32 x = ((A & qcolorMask) >> 2) + ((B & qcolorMask) >> 2) + ((C & qcolorMask) >> 2) + ((D & qcolorMask) >> 2);
uint32 y = (A & qlowpixelMask) + (B & qlowpixelMask) + (C & qlowpixelMask) + (D & qlowpixelMask);
y = (y >> 2) & qlowpixelMask;
return (x + y);
}
bool8 S9xBlit2xSaIFilterInit (void)
{
return (TRUE);
}
void S9xBlit2xSaIFilterDeinit (void)
{
return;
}
void SuperEagle (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
uint16 *bP;
uint32 *dP;
uint32 nextline = srcRowBytes >> 1;
for (; height; height--)
{
bP = (uint16 *) srcPtr;
dP = (uint32 *) dstPtr;
for (int i = 0; i < width; i++)
{
uint32 color1, color2, color3, color4, color5, color6;
uint32 colorA1, colorA2, colorB1, colorB2, colorS1, colorS2;
uint32 product1a, product1b, product2a, product2b;
colorB1 = *(bP - nextline );
colorB2 = *(bP - nextline + 1);
color4 = *(bP - 1);
color5 = *(bP );
color6 = *(bP + 1);
colorS2 = *(bP + 2);
color1 = *(bP + nextline - 1);
color2 = *(bP + nextline );
color3 = *(bP + nextline + 1);
colorS1 = *(bP + nextline + 2);
colorA1 = *(bP + nextline + nextline );
colorA2 = *(bP + nextline + nextline + 1);
if (color2 == color6 && color5 != color3)
{
product1b = product2a = color2;
if ((color1 == color2 && color6 == colorS2) || (color2 == colorA1 && color6 == colorB2))
{
product1a = INTERPOLATE(color2, color5);
product1a = INTERPOLATE(color2, product1a);
product2b = INTERPOLATE(color2, color3);
product2b = INTERPOLATE(color2, product2b);
}
else
{
product1a = INTERPOLATE(color5, color6);
product2b = INTERPOLATE(color2, color3);
}
}
else
if (color5 == color3 && color2 != color6)
{
product2b = product1a = color5;
if ((colorB1 == color5 && color3 == colorA2) || (color4 == color5 && color3 == colorS1))
{
product1b = INTERPOLATE(color5, color6);
product1b = INTERPOLATE(color5, product1b);
product2a = INTERPOLATE(color5, color2);
product2a = INTERPOLATE(color5, product2a);
}
else
{
product1b = INTERPOLATE(color5, color6);
product2a = INTERPOLATE(color2, color3);
}
}
else
if (color5 == color3 && color2 == color6 && color5 != color6)
{
int r = 0;
r += GetResult(color6, color5, color1, colorA1);
r += GetResult(color6, color5, color4, colorB1);
r += GetResult(color6, color5, colorA2, colorS1);
r += GetResult(color6, color5, colorB2, colorS2);
if (r > 0)
{
product1b = product2a = color2;
product1a = product2b = INTERPOLATE(color5, color6);
}
else
if (r < 0)
{
product2b = product1a = color5;
product1b = product2a = INTERPOLATE(color5, color6);
}
else
{
product2b = product1a = color5;
product1b = product2a = color2;
}
}
else
{
if ((color2 == color5) || (color3 == color6))
{
product1a = color5;
product2a = color2;
product1b = color6;
product2b = color3;
}
else
{
product1b = product1a = INTERPOLATE(color5, color6);
product1a = INTERPOLATE(color5, product1a);
product1b = INTERPOLATE(color6, product1b);
product2a = product2b = INTERPOLATE(color2, color3);
product2a = INTERPOLATE(color2, product2a);
product2b = INTERPOLATE(color3, product2b);
}
}
#ifdef MSB_FIRST
product1a = (product1a << 16) | product1b;
product2a = (product2a << 16) | product2b;
#else
product1a = product1a | (product1b << 16);
product2a = product2a | (product2b << 16);
#endif
*(dP) = product1a;
*(dP + (dstRowBytes >> 2)) = product2a;
bP++;
dP++;
}
dstPtr += dstRowBytes << 1;
srcPtr += srcRowBytes;
}
}
void _2xSaI (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
uint16 *bP;
uint32 *dP;
uint32 nextline = srcRowBytes >> 1;
for (; height; height--)
{
bP = (uint16 *) srcPtr;
dP = (uint32 *) dstPtr;
for (int i = 0; i < width; i++)
{
uint32 colorA, colorB, colorC, colorD, colorE, colorF, colorG, colorH, colorI, colorJ, colorK, colorL, colorM, colorN, colorO, colorP;
uint32 product, product1, product2;
colorI = *(bP - nextline - 1);
colorE = *(bP - nextline );
colorF = *(bP - nextline + 1);
colorJ = *(bP - nextline + 2);
colorG = *(bP - 1);
colorA = *(bP );
colorB = *(bP + 1);
colorK = *(bP + 2);
colorH = *(bP + nextline - 1);
colorC = *(bP + nextline );
colorD = *(bP + nextline + 1);
colorL = *(bP + nextline + 2);
colorM = *(bP + nextline + nextline - 1);
colorN = *(bP + nextline + nextline );
colorO = *(bP + nextline + nextline + 1);
colorP = *(bP + nextline + nextline + 2);
if ((colorA == colorD) && (colorB != colorC))
{
if (((colorA == colorE) && (colorB == colorL)) || ((colorA == colorC) && (colorA == colorF) && (colorB != colorE) && (colorB == colorJ)))
product = colorA;
else
product = INTERPOLATE(colorA, colorB);
if (((colorA == colorG) && (colorC == colorO)) || ((colorA == colorB) && (colorA == colorH) && (colorG != colorC) && (colorC == colorM)))
product1 = colorA;
else
product1 = INTERPOLATE(colorA, colorC);
product2 = colorA;
}
else
if ((colorB == colorC) && (colorA != colorD))
{
if (((colorB == colorF) && (colorA == colorH)) || ((colorB == colorE) && (colorB == colorD) && (colorA != colorF) && (colorA == colorI)))
product = colorB;
else
product = INTERPOLATE(colorA, colorB);
if (((colorC == colorH) && (colorA == colorF)) || ((colorC == colorG) && (colorC == colorD) && (colorA != colorH) && (colorA == colorI)))
product1 = colorC;
else
product1 = INTERPOLATE(colorA, colorC);
product2 = colorB;
}
else
if ((colorA == colorD) && (colorB == colorC))
{
if (colorA == colorB)
{
product = colorA;
product1 = colorA;
product2 = colorA;
}
else
{
int r = 0;
product1 = INTERPOLATE(colorA, colorC);
product = INTERPOLATE(colorA, colorB);
r += GetResult1(colorA, colorB, colorG, colorE, colorI);
r += GetResult2(colorB, colorA, colorK, colorF, colorJ);
r += GetResult2(colorB, colorA, colorH, colorN, colorM);
r += GetResult1(colorA, colorB, colorL, colorO, colorP);
if (r > 0)
product2 = colorA;
else
if (r < 0)
product2 = colorB;
else
product2 = Q_INTERPOLATE(colorA, colorB, colorC, colorD);
}
}
else
{
product2 = Q_INTERPOLATE(colorA, colorB, colorC, colorD);
if ((colorA == colorC) && (colorA == colorF) && (colorB != colorE) && (colorB == colorJ))
product = colorA;
else
if ((colorB == colorE) && (colorB == colorD) && (colorA != colorF) && (colorA == colorI))
product = colorB;
else
product = INTERPOLATE(colorA, colorB);
if ((colorA == colorB) && (colorA == colorH) && (colorG != colorC) && (colorC == colorM))
product1 = colorA;
else
if ((colorC == colorG) && (colorC == colorD) && (colorA != colorH) && (colorA == colorI))
product1 = colorC;
else
product1 = INTERPOLATE(colorA, colorC);
}
#ifdef MSB_FIRST
product = (colorA << 16) | product;
product1 = (product1 << 16) | product2;
#else
product = colorA | (product << 16);
product1 = product1 | (product2 << 16);
#endif
*(dP) = product;
*(dP + (dstRowBytes >> 2)) = product1;
bP++;
dP++;
}
dstPtr += dstRowBytes << 1;
srcPtr += srcRowBytes;
}
}
void Super2xSaI (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
uint16 *bP;
uint32 *dP;
uint32 nextline = srcRowBytes >> 1;
for (; height; height--)
{
bP = (uint16 *) srcPtr;
dP = (uint32 *) dstPtr;
for (int i = 0; i < width; i++)
{
uint32 color1, color2, color3, color4, color5, color6;
uint32 colorA0, colorA1, colorA2, colorA3, colorB0, colorB1, colorB2, colorB3, colorS1, colorS2;
uint32 product1a, product1b, product2a, product2b;
colorB0 = *(bP - nextline - 1);
colorB1 = *(bP - nextline );
colorB2 = *(bP - nextline + 1);
colorB3 = *(bP - nextline + 2);
color4 = *(bP - 1);
color5 = *(bP );
color6 = *(bP + 1);
colorS2 = *(bP + 2);
color1 = *(bP + nextline - 1);
color2 = *(bP + nextline );
color3 = *(bP + nextline + 1);
colorS1 = *(bP + nextline + 2);
colorA0 = *(bP + nextline + nextline - 1);
colorA1 = *(bP + nextline + nextline );
colorA2 = *(bP + nextline + nextline + 1);
colorA3 = *(bP + nextline + nextline + 2);
if (color2 == color6 && color5 != color3)
product2b = product1b = color2;
else
if (color5 == color3 && color2 != color6)
product2b = product1b = color5;
else
if (color5 == color3 && color2 == color6 && color5 != color6)
{
int r = 0;
r += GetResult(color6, color5, color1, colorA1);
r += GetResult(color6, color5, color4, colorB1);
r += GetResult(color6, color5, colorA2, colorS1);
r += GetResult(color6, color5, colorB2, colorS2);
if (r > 0)
product2b = product1b = color6;
else
if (r < 0)
product2b = product1b = color5;
else
product2b = product1b = INTERPOLATE(color5, color6);
}
else
{
if (color6 == color3 && color3 == colorA1 && color2 != colorA2 && color3 != colorA0)
product2b = Q_INTERPOLATE(color3, color3, color3, color2);
else
if (color5 == color2 && color2 == colorA2 && colorA1 != color3 && color2 != colorA3)
product2b = Q_INTERPOLATE(color2, color2, color2, color3);
else
product2b = INTERPOLATE(color2, color3);
if (color6 == color3 && color6 == colorB1 && color5 != colorB2 && color6 != colorB0)
product1b = Q_INTERPOLATE(color6, color6, color6, color5);
else
if (color5 == color2 && color5 == colorB2 && colorB1 != color6 && color5 != colorB3)
product1b = Q_INTERPOLATE(color6, color5, color5, color5);
else
product1b = INTERPOLATE (color5, color6);
}
if (color5 == color3 && color2 != color6 && color4 == color5 && color5 != colorA2)
product2a = INTERPOLATE(color2, color5);
else
if (color5 == color1 && color6 == color5 && color4 != color2 && color5 != colorA0)
product2a = INTERPOLATE(color2, color5);
else
product2a = color2;
if (color2 == color6 && color5 != color3 && color1 == color2 && color2 != colorB2)
product1a = INTERPOLATE(color2, color5);
else
if (color4 == color2 && color3 == color2 && color1 != color5 && color2 != colorB0)
product1a = INTERPOLATE(color2, color5);
else
product1a = color5;
#ifdef MSB_FIRST
product1a = (product1a << 16) | product1b;
product2a = (product2a << 16) | product2b;
#else
product1a = product1a | (product1b << 16);
product2a = product2a | (product2b << 16);
#endif
*(dP) = product1a;
*(dP +(dstRowBytes >> 2)) = product2a;
bP++;
dP++;
}
dstPtr += dstRowBytes << 1;
srcPtr += srcRowBytes;
}
}

@ -0,0 +1,16 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _2xsai_h_
#define _2xsai_h_
bool8 S9xBlit2xSaIFilterInit (void);
void S9xBlit2xSaIFilterDeinit (void);
void SuperEagle (uint8 *, int, uint8 *, int, int, int);
void _2xSaI (uint8 *, int, uint8 *, int, int, int);
void Super2xSaI (uint8 *, int, uint8 *, int, int, int);
#endif

@ -0,0 +1,529 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "snes9x.h"
#include "blit.h"
#define ALL_COLOR_MASK (FIRST_COLOR_MASK | SECOND_COLOR_MASK | THIRD_COLOR_MASK)
#define lowPixelMask (RGB_LOW_BITS_MASK)
#define qlowPixelMask ((RGB_HI_BITS_MASK >> 3) | TWO_LOW_BITS_MASK)
#define highBitsMask (ALL_COLOR_MASK & RGB_REMOVE_LOW_BITS_MASK)
#define colorMask (((~RGB_HI_BITS_MASK & ALL_COLOR_MASK) << 16) | (~RGB_HI_BITS_MASK & ALL_COLOR_MASK))
static snes_ntsc_t *ntsc = NULL;
static uint8 *XDelta = NULL;
bool8 S9xBlitFilterInit (void)
{
XDelta = new uint8[SNES_WIDTH * SNES_HEIGHT_EXTENDED * 4];
if (!XDelta)
return (FALSE);
S9xBlitClearDelta();
return (TRUE);
}
void S9xBlitFilterDeinit (void)
{
if (XDelta)
{
delete[] XDelta;
XDelta = NULL;
}
}
void S9xBlitClearDelta (void)
{
uint32 *d = (uint32 *) XDelta;
for (int y = 0; y < SNES_HEIGHT_EXTENDED; y++)
for (int x = 0; x < SNES_WIDTH; x++)
*d++ = 0x80008000;
}
bool8 S9xBlitNTSCFilterInit (void)
{
ntsc = (snes_ntsc_t *) malloc(sizeof(snes_ntsc_t));
if (!ntsc)
return (FALSE);
snes_ntsc_init(ntsc, &snes_ntsc_composite);
return (TRUE);
}
void S9xBlitNTSCFilterDeinit (void)
{
if (ntsc)
{
free(ntsc);
ntsc = NULL;
}
}
void S9xBlitNTSCFilterSet (const snes_ntsc_setup_t *setup)
{
snes_ntsc_init(ntsc, setup);
}
void S9xBlitPixSimple1x1 (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
width <<= 1;
for (; height; height--)
{
memcpy(dstPtr, srcPtr, width);
srcPtr += srcRowBytes;
dstPtr += dstRowBytes;
}
}
void S9xBlitPixSimple1x2 (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
width <<= 1;
for (; height; height--)
{
memcpy(dstPtr, srcPtr, width);
dstPtr += dstRowBytes;
memcpy(dstPtr, srcPtr, width);
srcPtr += srcRowBytes;
dstPtr += dstRowBytes;
}
}
void S9xBlitPixSimple2x1 (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
for (; height; height--)
{
uint16 *dP = (uint16 *) dstPtr, *bP = (uint16 *) srcPtr;
for (int i = 0; i < (width >> 1); i++)
{
*dP++ = *bP;
*dP++ = *bP++;
*dP++ = *bP;
*dP++ = *bP++;
}
srcPtr += srcRowBytes;
dstPtr += dstRowBytes;
}
}
void S9xBlitPixSimple2x2 (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
uint8 *dstPtr2 = dstPtr + dstRowBytes, *deltaPtr = XDelta;
dstRowBytes <<= 1;
for (; height; height--)
{
uint32 *dP1 = (uint32 *) dstPtr, *dP2 = (uint32 *) dstPtr2, *bP = (uint32 *) srcPtr, *xP = (uint32 *) deltaPtr;
uint32 currentPixel, lastPixel, currentPixA, currentPixB, colorA, colorB;
for (int i = 0; i < (width >> 1); i++)
{
currentPixel = *bP;
lastPixel = *xP;
if (currentPixel != lastPixel)
{
#ifdef MSB_FIRST
colorA = (currentPixel >> 16) & 0xFFFF;
colorB = (currentPixel ) & 0xFFFF;
#else
colorA = (currentPixel ) & 0xFFFF;
colorB = (currentPixel >> 16) & 0xFFFF;
#endif
currentPixA = (colorA << 16) | colorA;
currentPixB = (colorB << 16) | colorB;
dP1[0] = currentPixA;
dP1[1] = currentPixB;
dP2[0] = currentPixA;
dP2[1] = currentPixB;
*xP = *bP;
}
bP++;
xP++;
dP1 += 2;
dP2 += 2;
}
srcPtr += srcRowBytes;
deltaPtr += srcRowBytes;
dstPtr += dstRowBytes;
dstPtr2 += dstRowBytes;
}
}
void S9xBlitPixBlend1x1 (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
for (; height; height--)
{
uint16 *dP = (uint16 *) dstPtr, *bP = (uint16 *) srcPtr;
uint16 prev, curr;
prev = *bP;
for (int i = 0; i < (width >> 1); i++)
{
curr = *bP++;
*dP++ = (prev & curr) + (((prev ^ curr) & highBitsMask) >> 1);
prev = curr;
curr = *bP++;
*dP++ = (prev & curr) + (((prev ^ curr) & highBitsMask) >> 1);
prev = curr;
}
srcPtr += srcRowBytes;
dstPtr += dstRowBytes;
}
}
void S9xBlitPixBlend2x1 (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
for (; height; height--)
{
uint16 *dP = (uint16 *) dstPtr, *bP = (uint16 *) srcPtr;
uint16 prev, curr;
prev = *bP;
for (int i = 0; i < (width >> 1); i++)
{
curr = *bP++;
*dP++ = (prev & curr) + (((prev ^ curr) & highBitsMask) >> 1);
*dP++ = curr;
prev = curr;
curr = *bP++;
*dP++ = (prev & curr) + (((prev ^ curr) & highBitsMask) >> 1);
*dP++ = curr;
prev = curr;
}
srcPtr += srcRowBytes;
dstPtr += dstRowBytes;
}
}
void S9xBlitPixTV1x2 (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
uint8 *dstPtr2 = dstPtr + dstRowBytes;
dstRowBytes <<= 1;
for (; height; height--)
{
uint32 *dP1 = (uint32 *) dstPtr, *dP2 = (uint32 *) dstPtr2, *bP = (uint32 *) srcPtr;
uint32 product, darkened;
for (int i = 0; i < (width >> 1); i++)
{
product = *dP1++ = *bP++;
darkened = (product = (product >> 1) & colorMask);
darkened += (product = (product >> 1) & colorMask);
*dP2++ = darkened;
}
srcPtr += srcRowBytes;
dstPtr += dstRowBytes;
dstPtr2 += dstRowBytes;
}
}
void S9xBlitPixTV2x2 (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
uint8 *dstPtr2 = dstPtr + dstRowBytes, *deltaPtr = XDelta;
dstRowBytes <<= 1;
for (; height; height--)
{
uint32 *dP1 = (uint32 *) dstPtr, *dP2 = (uint32 *) dstPtr2, *bP = (uint32 *) srcPtr, *xP = (uint32 *) deltaPtr;
uint32 currentPixel, nextPixel, currentDelta, nextDelta, colorA, colorB, product, darkened;
for (int i = 0; i < (width >> 1) - 1; i++)
{
currentPixel = *bP;
currentDelta = *xP;
nextPixel = *(bP + 1);
nextDelta = *(xP + 1);
if ((currentPixel != currentDelta) || (nextPixel != nextDelta))
{
*xP = *bP;
#ifdef MSB_FIRST
colorA = (currentPixel >> 16) & 0xFFFF;
colorB = (currentPixel ) & 0xFFFF;
#else
colorA = (currentPixel ) & 0xFFFF;
colorB = (currentPixel >> 16) & 0xFFFF;
#endif
#ifdef MSB_FIRST
*dP1 = product = (colorA << 16) | ((((colorA >> 1) & colorMask) + ((colorB >> 1) & colorMask) + (colorA & colorB & lowPixelMask)) );
#else
*dP1 = product = (colorA ) | ((((colorA >> 1) & colorMask) + ((colorB >> 1) & colorMask) + (colorA & colorB & lowPixelMask)) << 16);
#endif
darkened = (product = ((product >> 1) & colorMask));
darkened += (product = ((product >> 1) & colorMask));
darkened += (product >> 1) & colorMask;
*dP2 = darkened;
#ifdef MSB_FIRST
colorA = (nextPixel >> 16) & 0xFFFF;
#else
colorA = (nextPixel ) & 0xFFFF;
#endif
#ifdef MSB_FIRST
*(dP1 + 1) = product = (colorB << 16) | ((((colorA >> 1) & colorMask) + ((colorB >> 1) & colorMask) + (colorA & colorB & lowPixelMask)) );
#else
*(dP1 + 1) = product = (colorB ) | ((((colorA >> 1) & colorMask) + ((colorB >> 1) & colorMask) + (colorA & colorB & lowPixelMask)) << 16);
#endif
darkened = (product = ((product >> 1) & colorMask));
darkened += (product = ((product >> 1) & colorMask));
darkened += (product >> 1) & colorMask;
*(dP2 + 1) = darkened;
}
bP++;
xP++;
dP1 += 2;
dP2 += 2;
}
// Last 2 Pixels
currentPixel = *bP;
currentDelta = *xP;
if (currentPixel != currentDelta)
{
*xP = *bP;
#ifdef MSB_FIRST
colorA = (currentPixel >> 16) & 0xFFFF;
colorB = (currentPixel ) & 0xFFFF;
#else
colorA = (currentPixel ) & 0xFFFF;
colorB = (currentPixel >> 16) & 0xFFFF;
#endif
#ifdef MSB_FIRST
*dP1 = product = (colorA << 16) | ((((colorA >> 1) & colorMask) + ((colorB >> 1) & colorMask) + (colorA & colorB & lowPixelMask)) );
#else
*dP1 = product = (colorA ) | ((((colorA >> 1) & colorMask) + ((colorB >> 1) & colorMask) + (colorA & colorB & lowPixelMask)) << 16);
#endif
darkened = (product = ((product >> 1) & colorMask));
darkened += (product = ((product >> 1) & colorMask));
darkened += (product >> 1) & colorMask;
*dP2 = darkened;
*(dP1 + 1) = product = (colorB << 16) | colorB;
darkened = (product = ((product >> 1) & colorMask));
darkened += (product = ((product >> 1) & colorMask));
darkened += (product >> 1) & colorMask;
*(dP2 + 1) = darkened;
}
srcPtr += srcRowBytes;
deltaPtr += srcRowBytes;
dstPtr += dstRowBytes;
dstPtr2 += dstRowBytes;
}
}
void S9xBlitPixMixedTV1x2 (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
uint8 *dstPtr2 = dstPtr + dstRowBytes, *srcPtr2 = srcPtr + srcRowBytes;
dstRowBytes <<= 1;
for (; height > 1; height--)
{
uint16 *dP1 = (uint16 *) dstPtr, *dP2 = (uint16 *) dstPtr2, *bP1 = (uint16 *) srcPtr, *bP2 = (uint16 *) srcPtr2;
uint16 prev, next, mixed;
for (int i = 0; i < width; i++)
{
prev = *bP1++;
next = *bP2++;
mixed = prev + next + ((prev ^ next) & lowPixelMask);
*dP1++ = prev;
*dP2++ = (mixed >> 1) - (mixed >> 4 & qlowPixelMask);
}
srcPtr += srcRowBytes;
srcPtr2 += srcRowBytes;
dstPtr += dstRowBytes;
dstPtr2 += dstRowBytes;
}
// Last 1 line
uint16 *dP1 = (uint16 *) dstPtr, *dP2 = (uint16 *) dstPtr2, *bP1 = (uint16 *) srcPtr;
uint16 prev, mixed;
for (int i = 0; i < width; i++)
{
prev = *bP1++;
mixed = prev + ((prev ^ 0) & lowPixelMask);
*dP1++ = prev;
*dP2++ = (mixed >> 1) - (mixed >> 4 & qlowPixelMask);
}
}
void S9xBlitPixSmooth2x2 (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
uint8 *dstPtr2 = dstPtr + dstRowBytes, *deltaPtr = XDelta;
uint32 lastLinePix[SNES_WIDTH << 1];
uint8 lastLineChg[SNES_WIDTH >> 1];
int lineBytes = width << 1;
dstRowBytes <<= 1;
memset(lastLinePix, 0, sizeof(lastLinePix));
memset(lastLineChg, 0, sizeof(lastLineChg));
for (; height; height--)
{
uint32 *dP1 = (uint32 *) dstPtr, *dP2 = (uint32 *) dstPtr2, *bP = (uint32 *) srcPtr, *xP = (uint32 *) deltaPtr;
uint32 *lL = lastLinePix;
uint8 *lC = lastLineChg;
uint32 currentPixel, nextPixel, currentDelta, nextDelta, lastPix, lastChg, thisChg, currentPixA, currentPixB, colorA, colorB, colorC;
uint16 savePixel;
savePixel = *(uint16 *) (srcPtr + lineBytes);
*(uint16 *) (srcPtr + lineBytes) = *(uint16 *) (srcPtr + lineBytes - 2);
*(uint32 *) (deltaPtr + lineBytes) = *(uint32 *) (srcPtr + lineBytes);
nextPixel = *bP++;
nextDelta = *xP++;
for (int i = 0; i < (width >> 1); i++)
{
currentPixel = nextPixel;
currentDelta = nextDelta;
nextPixel = *bP++;
nextDelta = *xP++;
lastChg = *lC;
thisChg = (nextPixel - nextDelta) | (currentPixel - currentDelta);
#ifdef MSB_FIRST
colorA = (currentPixel >> 16) & 0xFFFF;
colorB = (currentPixel ) & 0xFFFF;
colorC = (nextPixel >> 16) & 0xFFFF;
currentPixA = (colorA << 16) | ((((colorA >> 1) & colorMask) + ((colorB >> 1) & colorMask) + (colorA & colorB & lowPixelMask)) );
currentPixB = (colorB << 16) | ((((colorC >> 1) & colorMask) + ((colorB >> 1) & colorMask) + (colorC & colorB & lowPixelMask)) );
#else
colorA = (currentPixel ) & 0xFFFF;
colorB = (currentPixel >> 16) & 0xFFFF;
colorC = (nextPixel ) & 0xFFFF;
currentPixA = (colorA ) | ((((colorA >> 1) & colorMask) + ((colorB >> 1) & colorMask) + (colorA & colorB & lowPixelMask)) << 16);
currentPixB = (colorB ) | ((((colorC >> 1) & colorMask) + ((colorB >> 1) & colorMask) + (colorC & colorB & lowPixelMask)) << 16);
#endif
if (thisChg | lastChg)
{
xP[-2] = currentPixel;
lastPix = lL[0];
dP1[0] = ((currentPixA >> 1) & colorMask) + ((lastPix >> 1) & colorMask) + (currentPixA & lastPix & lowPixelMask);
dP2[0] = currentPixA;
lL[0] = currentPixA;
lastPix = lL[1];
dP1[1] = ((currentPixB >> 1) & colorMask) + ((lastPix >> 1) & colorMask) + (currentPixB & lastPix & lowPixelMask);
dP2[1] = currentPixB;
lL[1] = currentPixB;
*lC++ = (thisChg != 0);
}
else
{
lL[0] = currentPixA;
lL[1] = currentPixB;
*lC++ = 0;
}
lL += 2;
dP2 += 2;
dP1 += 2;
}
*(uint16 *) (srcPtr + lineBytes) = savePixel;
srcPtr += srcRowBytes;
deltaPtr += srcRowBytes;
dstPtr += dstRowBytes;
dstPtr2 += dstRowBytes;
}
}
void S9xBlitPixSuper2xSaI16 (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
Super2xSaI(srcPtr, srcRowBytes, dstPtr, dstRowBytes, width, height);
}
void S9xBlitPix2xSaI16 (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
_2xSaI(srcPtr, srcRowBytes, dstPtr, dstRowBytes, width, height);
}
void S9xBlitPixSuperEagle16 (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
SuperEagle(srcPtr, srcRowBytes, dstPtr, dstRowBytes, width, height);
}
void S9xBlitPixEPX16 (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
EPX_16(srcPtr, srcRowBytes, dstPtr, dstRowBytes, width, height);
}
void S9xBlitPixHQ2x16 (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
HQ2X_16(srcPtr, srcRowBytes, dstPtr, dstRowBytes, width, height);
}
void S9xBlitPixHQ3x16 (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
HQ3X_16(srcPtr, srcRowBytes, dstPtr, dstRowBytes, width, height);
}
void S9xBlitPixHQ4x16 (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
HQ4X_16(srcPtr, srcRowBytes, dstPtr, dstRowBytes, width, height);
}
void S9xBlitPixNTSC16 (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
snes_ntsc_blit(ntsc, (SNES_NTSC_IN_T const *) srcPtr, srcRowBytes >> 1, 0, width, height, dstPtr, dstRowBytes);
}
void S9xBlitPixHiResNTSC16 (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
snes_ntsc_blit_hires(ntsc, (SNES_NTSC_IN_T const *) srcPtr, srcRowBytes >> 1, 0, width, height, dstPtr, dstRowBytes);
}

@ -0,0 +1,41 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _blit_h_
#define _blit_h_
#include "2xsai.h"
#include "epx.h"
#include "hq2x.h"
#include "snes_ntsc.h"
bool8 S9xBlitFilterInit (void);
void S9xBlitFilterDeinit (void);
void S9xBlitClearDelta (void);
bool8 S9xBlitNTSCFilterInit (void);
void S9xBlitNTSCFilterDeinit (void);
void S9xBlitNTSCFilterSet (const snes_ntsc_setup_t *);
void S9xBlitPixSimple1x1 (uint8 *, int, uint8 *, int, int, int);
void S9xBlitPixSimple1x2 (uint8 *, int, uint8 *, int, int, int);
void S9xBlitPixSimple2x1 (uint8 *, int, uint8 *, int, int, int);
void S9xBlitPixSimple2x2 (uint8 *, int, uint8 *, int, int, int);
void S9xBlitPixBlend1x1 (uint8 *, int, uint8 *, int, int, int);
void S9xBlitPixBlend2x1 (uint8 *, int, uint8 *, int, int, int);
void S9xBlitPixTV1x2 (uint8 *, int, uint8 *, int, int, int);
void S9xBlitPixTV2x2 (uint8 *, int, uint8 *, int, int, int);
void S9xBlitPixMixedTV1x2 (uint8 *, int, uint8 *, int, int, int);
void S9xBlitPixSmooth2x2 (uint8 *, int, uint8 *, int, int, int);
void S9xBlitPixSuperEagle16 (uint8 *, int, uint8 *, int, int, int);
void S9xBlitPix2xSaI16 (uint8 *, int, uint8 *, int, int, int);
void S9xBlitPixSuper2xSaI16 (uint8 *, int, uint8 *, int, int, int);
void S9xBlitPixEPX16 (uint8 *, int, uint8 *, int, int, int);
void S9xBlitPixHQ2x16 (uint8 *, int, uint8 *, int, int, int);
void S9xBlitPixHQ3x16 (uint8 *, int, uint8 *, int, int, int);
void S9xBlitPixHQ4x16 (uint8 *, int, uint8 *, int, int, int);
void S9xBlitPixNTSC16 (uint8 *, int, uint8 *, int, int, int);
void S9xBlitPixHiResNTSC16 (uint8 *, int, uint8 *, int, int, int);
#endif

@ -0,0 +1,258 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "snes9x.h"
#include "epx.h"
void EPX_16 (uint8 *srcPtr, int srcRowBytes, uint8 *dstPtr, int dstRowBytes, int width, int height)
{
uint16 colorX, colorA, colorB, colorC, colorD;
uint16 *sP, *uP, *lP;
uint32 *dP1, *dP2;
int w;
height -= 2;
// D
// A X C
// B
// top edge
sP = (uint16 *) srcPtr;
lP = (uint16 *) (srcPtr + srcRowBytes);
dP1 = (uint32 *) dstPtr;
dP2 = (uint32 *) (dstPtr + dstRowBytes);
// left edge
colorX = *sP;
colorC = *++sP;
colorB = *lP++;
if ((colorX != colorC) && (colorB != colorX))
{
#ifdef MSB_FIRST
*dP1 = (colorX << 16) + colorX;
*dP2 = (colorX << 16) + ((colorB == colorC) ? colorB : colorX);
#else
*dP1 = colorX + (colorX << 16);
*dP2 = colorX + (((colorB == colorC) ? colorB : colorX) << 16);
#endif
}
else
*dP1 = *dP2 = (colorX << 16) + colorX;
dP1++;
dP2++;
//
for (w = width - 2; w; w--)
{
colorA = colorX;
colorX = colorC;
colorC = *++sP;
colorB = *lP++;
if ((colorA != colorC) && (colorB != colorX))
{
#ifdef MSB_FIRST
*dP1 = (colorX << 16) + colorX;
*dP2 = (((colorA == colorB) ? colorA : colorX) << 16) + ((colorB == colorC) ? colorB : colorX);
#else
*dP1 = colorX + (colorX << 16);
*dP2 = ((colorA == colorB) ? colorA : colorX) + (((colorB == colorC) ? colorB : colorX) << 16);
#endif
}
else
*dP1 = *dP2 = (colorX << 16) + colorX;
dP1++;
dP2++;
}
// right edge
colorA = colorX;
colorX = colorC;
colorB = *lP;
if ((colorA != colorX) && (colorB != colorX))
{
#ifdef MSB_FIRST
*dP1 = (colorX << 16) + colorX;
*dP2 = (((colorA == colorB) ? colorA : colorX) << 16) + colorX;
#else
*dP1 = colorX + (colorX << 16);
*dP2 = ((colorA == colorB) ? colorA : colorX) + (colorX << 16);
#endif
}
else
*dP1 = *dP2 = (colorX << 16) + colorX;
srcPtr += srcRowBytes;
dstPtr += dstRowBytes << 1;
//
for (; height; height--)
{
sP = (uint16 *) srcPtr;
uP = (uint16 *) (srcPtr - srcRowBytes);
lP = (uint16 *) (srcPtr + srcRowBytes);
dP1 = (uint32 *) dstPtr;
dP2 = (uint32 *) (dstPtr + dstRowBytes);
// left edge
colorX = *sP;
colorC = *++sP;
colorB = *lP++;
colorD = *uP++;
if ((colorX != colorC) && (colorB != colorD))
{
#ifdef MSB_FIRST
*dP1 = (colorX << 16) + ((colorC == colorD) ? colorC : colorX);
*dP2 = (colorX << 16) + ((colorB == colorC) ? colorB : colorX);
#else
*dP1 = colorX + (((colorC == colorD) ? colorC : colorX) << 16);
*dP2 = colorX + (((colorB == colorC) ? colorB : colorX) << 16);
#endif
}
else
*dP1 = *dP2 = (colorX << 16) + colorX;
dP1++;
dP2++;
//
for (w = width - 2; w; w--)
{
colorA = colorX;
colorX = colorC;
colorC = *++sP;
colorB = *lP++;
colorD = *uP++;
if ((colorA != colorC) && (colorB != colorD))
{
#ifdef MSB_FIRST
*dP1 = (((colorD == colorA) ? colorD : colorX) << 16) + ((colorC == colorD) ? colorC : colorX);
*dP2 = (((colorA == colorB) ? colorA : colorX) << 16) + ((colorB == colorC) ? colorB : colorX);
#else
*dP1 = ((colorD == colorA) ? colorD : colorX) + (((colorC == colorD) ? colorC : colorX) << 16);
*dP2 = ((colorA == colorB) ? colorA : colorX) + (((colorB == colorC) ? colorB : colorX) << 16);
#endif
}
else
*dP1 = *dP2 = (colorX << 16) + colorX;
dP1++;
dP2++;
}
// right edge
colorA = colorX;
colorX = colorC;
colorB = *lP;
colorD = *uP;
if ((colorA != colorX) && (colorB != colorD))
{
#ifdef MSB_FIRST
*dP1 = (((colorD == colorA) ? colorD : colorX) << 16) + colorX;
*dP2 = (((colorA == colorB) ? colorA : colorX) << 16) + colorX;
#else
*dP1 = ((colorD == colorA) ? colorD : colorX) + (colorX << 16);
*dP2 = ((colorA == colorB) ? colorA : colorX) + (colorX << 16);
#endif
}
else
*dP1 = *dP2 = (colorX << 16) + colorX;
srcPtr += srcRowBytes;
dstPtr += dstRowBytes << 1;
}
// bottom edge
sP = (uint16 *) srcPtr;
uP = (uint16 *) (srcPtr - srcRowBytes);
dP1 = (uint32 *) dstPtr;
dP2 = (uint32 *) (dstPtr + dstRowBytes);
// left edge
colorX = *sP;
colorC = *++sP;
colorD = *uP++;
if ((colorX != colorC) && (colorX != colorD))
{
#ifdef MSB_FIRST
*dP1 = (colorX << 16) + ((colorC == colorD) ? colorC : colorX);
*dP2 = (colorX << 16) + colorX;
#else
*dP1 = colorX + (((colorC == colorD) ? colorC : colorX) << 16);
*dP2 = colorX + (colorX << 16);
#endif
}
else
*dP1 = *dP2 = (colorX << 16) + colorX;
dP1++;
dP2++;
//
for (w = width - 2; w; w--)
{
colorA = colorX;
colorX = colorC;
colorC = *++sP;
colorD = *uP++;
if ((colorA != colorC) && (colorX != colorD))
{
#ifdef MSB_FIRST
*dP1 = (((colorD == colorA) ? colorD : colorX) << 16) + ((colorC == colorD) ? colorC : colorX);
*dP2 = (colorX << 16) + colorX;
#else
*dP1 = ((colorD == colorA) ? colorD : colorX) + (((colorC == colorD) ? colorC : colorX) << 16);
*dP2 = colorX + (colorX << 16);
#endif
}
else
*dP1 = *dP2 = (colorX << 16) + colorX;
dP1++;
dP2++;
}
// right edge
colorA = colorX;
colorX = colorC;
colorD = *uP;
if ((colorA != colorX) && (colorX != colorD))
{
#ifdef MSB_FIRST
*dP1 = (((colorD == colorA) ? colorD : colorX) << 16) + colorX;
*dP2 = (colorX << 16) + colorX;
#else
*dP1 = ((colorD == colorA) ? colorD : colorX) + (colorX << 16);
*dP2 = colorX + (colorX << 16);
#endif
}
else
*dP1 = *dP2 = (colorX << 16) + colorX;
}

@ -0,0 +1,12 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _epx_h_
#define _epx_h_
void EPX_16 (uint8 *, int, uint8 *, int, int, int);
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,16 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _hq2x_h_
#define _hq2x_h_
bool8 S9xBlitHQ2xFilterInit (void);
void S9xBlitHQ2xFilterDeinit (void);
void HQ2X_16 (uint8 *, uint32, uint8 *, uint32, int, int);
void HQ3X_16 (uint8 *, uint32, uint8 *, uint32, int, int);
void HQ4X_16 (uint8 *, uint32, uint8 *, uint32, int, int);
#endif

@ -0,0 +1,504 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

@ -0,0 +1,390 @@
/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */
#include "snes_ntsc.h"
/* Copyright (C) 2006-2007 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
snes_ntsc_setup_t const snes_ntsc_monochrome = { 0,-1, 0, 0,.2, 0,.2,-.2,-.2,-1, 1, 0, 0 };
snes_ntsc_setup_t const snes_ntsc_composite = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 };
snes_ntsc_setup_t const snes_ntsc_svideo = { 0, 0, 0, 0,.2, 0,.2, -1, -1, 0, 1, 0, 0 };
snes_ntsc_setup_t const snes_ntsc_rgb = { 0, 0, 0, 0,.2, 0,.7, -1, -1,-1, 1, 0, 0 };
#define alignment_count 3
#define burst_count 3
#define rescale_in 8
#define rescale_out 7
#define artifacts_mid 1.0f
#define fringing_mid 1.0f
#define std_decoder_hue 0
#define rgb_bits 7 /* half normal range to allow for doubled hires pixels */
#define gamma_size 32
#include "snes_ntsc_impl.h"
unsigned int snes_ntsc_scanline_offset = 0;
unsigned short snes_ntsc_scanline_mask = 0xffff;
/* 3 input pixels -> 8 composite samples */
pixel_info_t const snes_ntsc_pixels [alignment_count] = {
{ PIXEL_OFFSET( -4, -9 ), { 1, 1, .6667f, 0 } },
{ PIXEL_OFFSET( -2, -7 ), { .3333f, 1, 1, .3333f } },
{ PIXEL_OFFSET( 0, -5 ), { 0, .6667f, 1, 1 } },
};
static void merge_kernel_fields( snes_ntsc_rgb_t* io )
{
int n;
for ( n = burst_size; n; --n )
{
snes_ntsc_rgb_t p0 = io [burst_size * 0] + rgb_bias;
snes_ntsc_rgb_t p1 = io [burst_size * 1] + rgb_bias;
snes_ntsc_rgb_t p2 = io [burst_size * 2] + rgb_bias;
/* merge colors without losing precision */
io [burst_size * 0] =
((p0 + p1 - ((p0 ^ p1) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
io [burst_size * 1] =
((p1 + p2 - ((p1 ^ p2) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
io [burst_size * 2] =
((p2 + p0 - ((p2 ^ p0) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
++io;
}
}
static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out )
{
int n;
for ( n = burst_count; n; --n )
{
unsigned i;
for ( i = 0; i < rgb_kernel_size / 2; i++ )
{
snes_ntsc_rgb_t error = color -
out [i ] - out [(i+12)%14+14] - out [(i+10)%14+28] -
out [i + 7] - out [i + 5 +14] - out [i + 3 +28];
DISTRIBUTE_ERROR( i+3+28, i+5+14, i+7 );
}
out += alignment_count * rgb_kernel_size;
}
}
void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup )
{
int merge_fields;
int entry;
init_t impl;
if ( !setup )
setup = &snes_ntsc_composite;
init( &impl, setup );
merge_fields = setup->merge_fields;
if ( setup->artifacts <= -1 && setup->fringing <= -1 )
merge_fields = 1;
for ( entry = 0; entry < snes_ntsc_palette_size; entry++ )
{
/* Reduce number of significant bits of source color. Clearing the
low bits of R and B were least notictable. Modifying green was too
noticeable. */
int ir = entry >> 8 & 0x1E;
int ig = entry >> 4 & 0x1F;
int ib = entry << 1 & 0x1E;
#if SNES_NTSC_BSNES_COLORTBL
if ( setup->bsnes_colortbl )
{
int bgr15 = (ib << 10) | (ig << 5) | ir;
unsigned long rgb16 = setup->bsnes_colortbl [bgr15];
ir = rgb16 >> 11 & 0x1E;
ig = rgb16 >> 6 & 0x1F;
ib = rgb16 & 0x1E;
}
#endif
{
float rr = impl.to_float [ir];
float gg = impl.to_float [ig];
float bb = impl.to_float [ib];
float y, i, q = RGB_TO_YIQ( rr, gg, bb, y, i );
int r, g, b = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, r, g );
snes_ntsc_rgb_t rgb = PACK_RGB( r, g, b );
snes_ntsc_rgb_t* out = ntsc->table [entry];
gen_kernel( &impl, y, i, q, out );
if ( merge_fields )
merge_kernel_fields( out );
correct_errors( rgb, out );
}
}
}
#ifndef SNES_NTSC_NO_BLITTERS
void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width,
int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch )
{
int chunk_count = (in_width - 1) / snes_ntsc_in_chunk;
for ( ; in_height; --in_height )
{
SNES_NTSC_IN_T const* line_in = input;
SNES_NTSC_BEGIN_ROW( ntsc, burst_phase,
snes_ntsc_black, snes_ntsc_black, SNES_NTSC_ADJ_IN( *line_in ) );
snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out;
int n;
++line_in;
for ( n = chunk_count; n; --n )
{
/* order of input and output pixels must not be altered */
SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) );
SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) );
SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) );
SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
line_in += 3;
line_out += 7;
}
/* finish final pixels */
SNES_NTSC_COLOR_IN( 0, snes_ntsc_black );
SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 1, snes_ntsc_black );
SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 2, snes_ntsc_black );
SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
burst_phase = (burst_phase + 1) % snes_ntsc_burst_count;
input += in_row_width;
rgb_out = (char*) rgb_out + out_pitch;
}
}
void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width,
int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch )
{
int chunk_count = (in_width - 2) / (snes_ntsc_in_chunk * 2);
for ( ; in_height; --in_height )
{
SNES_NTSC_IN_T const* line_in = input;
SNES_NTSC_HIRES_ROW( ntsc, burst_phase,
snes_ntsc_black, snes_ntsc_black, snes_ntsc_black,
SNES_NTSC_ADJ_IN( line_in [0] ),
SNES_NTSC_ADJ_IN( line_in [1] ) );
snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out;
int n;
line_in += 2;
for ( n = chunk_count; n; --n )
{
/* twice as many input pixels per chunk */
SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) );
SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) );
SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) );
SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 3, SNES_NTSC_ADJ_IN( line_in [3] ) );
SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 4, SNES_NTSC_ADJ_IN( line_in [4] ) );
SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 5, SNES_NTSC_ADJ_IN( line_in [5] ) );
SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
line_in += 6;
line_out += 7;
}
SNES_NTSC_COLOR_IN( 0, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 1, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 2, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 3, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 4, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 5, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
burst_phase = (burst_phase + 1) % snes_ntsc_burst_count;
input += in_row_width;
rgb_out = (char*) rgb_out + out_pitch;
}
}
/* 12.5% scanlines like in snes_ntsc example instead of zsnes's 25% */
#define PIXEL_OUT( x ) \
SNES_NTSC_RGB_OUT( x, value, SNES_NTSC_OUT_DEPTH ); \
line_outa[x] = value; \
line_outb[x] = value - (value >> snes_ntsc_scanline_offset & snes_ntsc_scanline_mask);
void snes_ntsc_blit_scanlines( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width,
int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch )
{
unsigned value;
int chunk_count = (in_width - 1) / snes_ntsc_in_chunk;
for ( ; in_height; --in_height )
{
SNES_NTSC_IN_T const* line_in = input;
SNES_NTSC_BEGIN_ROW( ntsc, burst_phase,
snes_ntsc_black, snes_ntsc_black, SNES_NTSC_ADJ_IN( *line_in ) );
snes_ntsc_out_t * restrict line_outa = (snes_ntsc_out_t *) rgb_out;
snes_ntsc_out_t * restrict line_outb = (snes_ntsc_out_t *) ((char *) line_outa + out_pitch);
int n;
++line_in;
for ( n = chunk_count; n; --n )
{
/* order of input and output pixels must not be altered */
SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) );
PIXEL_OUT (0);
PIXEL_OUT (1);
SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) );
PIXEL_OUT (2);
PIXEL_OUT (3);
SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) );
PIXEL_OUT (4);
PIXEL_OUT (5);
PIXEL_OUT (6);
line_in += 3;
line_outa += 7;
line_outb += 7;
}
/* finish final pixels */
SNES_NTSC_COLOR_IN( 0, snes_ntsc_black );
PIXEL_OUT (0);
PIXEL_OUT (1);
SNES_NTSC_COLOR_IN( 1, snes_ntsc_black );
PIXEL_OUT (2);
PIXEL_OUT (3);
SNES_NTSC_COLOR_IN( 2, snes_ntsc_black );
PIXEL_OUT (4);
PIXEL_OUT (5);
PIXEL_OUT (6);
burst_phase = (burst_phase + 1) % snes_ntsc_burst_count;
input += in_row_width;
rgb_out = (char*) rgb_out + 2 * out_pitch;
}
}
#define PIXEL_OUT_HIRES( x ) \
SNES_NTSC_HIRES_OUT( x, value, SNES_NTSC_OUT_DEPTH ); \
line_outa[x] = value; \
line_outb[x] = value - (value >> snes_ntsc_scanline_offset & snes_ntsc_scanline_mask);
void snes_ntsc_blit_hires_scanlines( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width,
int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch )
{
int chunk_count = (in_width - 2) / (snes_ntsc_in_chunk * 2);
unsigned value;
for ( ; in_height; --in_height )
{
SNES_NTSC_IN_T const* line_in = input;
SNES_NTSC_HIRES_ROW( ntsc, burst_phase,
snes_ntsc_black, snes_ntsc_black, snes_ntsc_black,
SNES_NTSC_ADJ_IN( line_in [0] ),
SNES_NTSC_ADJ_IN( line_in [1] ) );
snes_ntsc_out_t* restrict line_outa = (snes_ntsc_out_t*) rgb_out;
snes_ntsc_out_t* restrict line_outb = (snes_ntsc_out_t*) ((char *) line_outa + out_pitch);
int n;
line_in += 2;
for ( n = chunk_count; n; --n )
{
/* twice as many input pixels per chunk */
SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) );
PIXEL_OUT_HIRES (0);
SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) );
PIXEL_OUT_HIRES (1);
SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) );
PIXEL_OUT_HIRES (2);
SNES_NTSC_COLOR_IN( 3, SNES_NTSC_ADJ_IN( line_in [3] ) );
PIXEL_OUT_HIRES (3);
SNES_NTSC_COLOR_IN( 4, SNES_NTSC_ADJ_IN( line_in [4] ) );
PIXEL_OUT_HIRES (4);
SNES_NTSC_COLOR_IN( 5, SNES_NTSC_ADJ_IN( line_in [5] ) );
PIXEL_OUT_HIRES (5);
PIXEL_OUT_HIRES (6);
line_in += 6;
line_outa += 7;
line_outb += 7;
}
SNES_NTSC_COLOR_IN( 0, snes_ntsc_black );
PIXEL_OUT_HIRES (0);
SNES_NTSC_COLOR_IN( 1, snes_ntsc_black );
PIXEL_OUT_HIRES (1);
SNES_NTSC_COLOR_IN( 2, snes_ntsc_black );
PIXEL_OUT_HIRES (2);
SNES_NTSC_COLOR_IN( 3, snes_ntsc_black );
PIXEL_OUT_HIRES (3);
SNES_NTSC_COLOR_IN( 4, snes_ntsc_black );
PIXEL_OUT_HIRES (4);
SNES_NTSC_COLOR_IN( 5, snes_ntsc_black );
PIXEL_OUT_HIRES (5);
PIXEL_OUT_HIRES (6);
burst_phase = (burst_phase + 1) % snes_ntsc_burst_count;
input += in_row_width;
rgb_out = (char*) rgb_out + out_pitch * 2;
}
}
#endif

@ -0,0 +1,222 @@
/* SNES NTSC video filter */
/* snes_ntsc 0.2.2 */
#ifndef SNES_NTSC_H
#define SNES_NTSC_H
#include "snes_ntsc_config.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Image parameters, ranging from -1.0 to 1.0. Actual internal values shown
in parenthesis and should remain fairly stable in future versions. */
typedef struct snes_ntsc_setup_t
{
/* Basic parameters */
double hue; /* -1 = -180 degrees +1 = +180 degrees */
double saturation; /* -1 = grayscale (0.0) +1 = oversaturated colors (2.0) */
double contrast; /* -1 = dark (0.5) +1 = light (1.5) */
double brightness; /* -1 = dark (0.5) +1 = light (1.5) */
double sharpness; /* edge contrast enhancement/blurring */
/* Advanced parameters */
double gamma; /* -1 = dark (1.5) +1 = light (0.5) */
double resolution; /* image resolution */
double artifacts; /* artifacts caused by color changes */
double fringing; /* color artifacts caused by brightness changes */
double bleed; /* color bleed (color resolution reduction) */
int merge_fields; /* if 1, merges even and odd fields together to reduce flicker */
float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */
unsigned long const* bsnes_colortbl; /* undocumented; set to 0 */
} snes_ntsc_setup_t;
/* Video format presets */
extern snes_ntsc_setup_t const snes_ntsc_composite; /* color bleeding + artifacts */
extern snes_ntsc_setup_t const snes_ntsc_svideo; /* color bleeding only */
extern snes_ntsc_setup_t const snes_ntsc_rgb; /* crisp image */
extern snes_ntsc_setup_t const snes_ntsc_monochrome;/* desaturated + artifacts */
/* Scanline values */
extern unsigned int snes_ntsc_scanline_offset;
extern unsigned short snes_ntsc_scanline_mask;
/* Initializes and adjusts parameters. Can be called multiple times on the same
snes_ntsc_t object. Can pass NULL for either parameter. */
typedef struct snes_ntsc_t snes_ntsc_t;
void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup );
/* Filters one or more rows of pixels. Input pixel format is set by SNES_NTSC_IN_FORMAT
and output RGB depth is set by SNES_NTSC_OUT_DEPTH. Both default to 16-bit RGB.
In_row_width is the number of pixels to get to the next input row. Out_pitch
is the number of *bytes* to get to the next output row. */
void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input,
long in_row_width, int burst_phase, int in_width, int in_height,
void* rgb_out, long out_pitch );
void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input,
long in_row_width, int burst_phase, int in_width, int in_height,
void* rgb_out, long out_pitch );
void snes_ntsc_blit_scanlines( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input,
long in_row_width, int burst_phase, int in_width, int in_height,
void* rgb_out, long out_pitch );
void snes_ntsc_blit_hires_scanlines( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input,
long in_row_width, int burst_phase, int in_width, int in_height,
void* rgb_out, long out_pitch );
/* Number of output pixels written by low-res blitter for given input width. Width
might be rounded down slightly; use SNES_NTSC_IN_WIDTH() on result to find rounded
value. Guaranteed not to round 256 down at all. */
#define SNES_NTSC_OUT_WIDTH( in_width ) \
((((in_width) - 1) / snes_ntsc_in_chunk + 1) * snes_ntsc_out_chunk)
/* Number of low-res input pixels that will fit within given output width. Might be
rounded down slightly; use SNES_NTSC_OUT_WIDTH() on result to find rounded
value. */
#define SNES_NTSC_IN_WIDTH( out_width ) \
(((out_width) / snes_ntsc_out_chunk - 1) * snes_ntsc_in_chunk + 1)
/* Interface for user-defined custom blitters */
enum { snes_ntsc_in_chunk = 3 }; /* number of input pixels read per chunk */
enum { snes_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */
enum { snes_ntsc_black = 0 }; /* palette index for black */
enum { snes_ntsc_burst_count = 3 }; /* burst phase cycles through 0, 1, and 2 */
/* Begins outputting row and starts three pixels. First pixel will be cut off a bit.
Use snes_ntsc_black for unused pixels. Declares variables, so must be before first
statement in a block (unless you're using C++). */
#define SNES_NTSC_BEGIN_ROW( ntsc, burst, pixel0, pixel1, pixel2 ) \
char const* ktable = \
(char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\
SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, SNES_NTSC_IN_FORMAT, ktable )
/* Begins input pixel */
#define SNES_NTSC_COLOR_IN( index, color ) \
SNES_NTSC_COLOR_IN_( index, color, SNES_NTSC_IN_FORMAT, ktable )
/* Generates output pixel. Bits can be 24, 16, 15, 14, 32 (treated as 24), or 0:
24: RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8 RGB)
16: RRRRRGGG GGGBBBBB (5-6-5 RGB)
15: RRRRRGG GGGBBBBB (5-5-5 RGB)
14: BBBBBGG GGGRRRRR (5-5-5 BGR, native SNES format)
0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format; x = junk bits) */
#define SNES_NTSC_RGB_OUT( index, rgb_out, bits ) \
SNES_NTSC_RGB_OUT_14_( index, rgb_out, bits, 1 )
/* Hires equivalents */
#define SNES_NTSC_HIRES_ROW( ntsc, burst, pixel1, pixel2, pixel3, pixel4, pixel5 ) \
char const* ktable = \
(char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\
unsigned const snes_ntsc_pixel1_ = (pixel1);\
snes_ntsc_rgb_t const* kernel1 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel1_ );\
unsigned const snes_ntsc_pixel2_ = (pixel2);\
snes_ntsc_rgb_t const* kernel2 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel2_ );\
unsigned const snes_ntsc_pixel3_ = (pixel3);\
snes_ntsc_rgb_t const* kernel3 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel3_ );\
unsigned const snes_ntsc_pixel4_ = (pixel4);\
snes_ntsc_rgb_t const* kernel4 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel4_ );\
unsigned const snes_ntsc_pixel5_ = (pixel5);\
snes_ntsc_rgb_t const* kernel5 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel5_ );\
snes_ntsc_rgb_t const* kernel0 = kernel1;\
snes_ntsc_rgb_t const* kernelx0;\
snes_ntsc_rgb_t const* kernelx1 = kernel1;\
snes_ntsc_rgb_t const* kernelx2 = kernel1;\
snes_ntsc_rgb_t const* kernelx3 = kernel1;\
snes_ntsc_rgb_t const* kernelx4 = kernel1;\
snes_ntsc_rgb_t const* kernelx5 = kernel1
#define SNES_NTSC_HIRES_OUT( x, rgb_out, bits ) {\
snes_ntsc_rgb_t raw_ =\
kernel0 [ x ] + kernel2 [(x+5)%7+14] + kernel4 [(x+3)%7+28] +\
kernelx0 [(x+7)%7+7] + kernelx2 [(x+5)%7+21] + kernelx4 [(x+3)%7+35] +\
kernel1 [(x+6)%7 ] + kernel3 [(x+4)%7+14] + kernel5 [(x+2)%7+28] +\
kernelx1 [(x+6)%7+7] + kernelx3 [(x+4)%7+21] + kernelx5 [(x+2)%7+35];\
SNES_NTSC_CLAMP_( raw_, 0 );\
SNES_NTSC_RGB_OUT_( rgb_out, (bits), 0 );\
}
/* private */
enum { snes_ntsc_entry_size = 128 };
enum { snes_ntsc_palette_size = 0x2000 };
typedef unsigned long snes_ntsc_rgb_t;
struct snes_ntsc_t {
snes_ntsc_rgb_t table [snes_ntsc_palette_size] [snes_ntsc_entry_size];
};
enum { snes_ntsc_burst_size = snes_ntsc_entry_size / snes_ntsc_burst_count };
#define SNES_NTSC_RGB16( ktable, n ) \
(snes_ntsc_rgb_t const*) (ktable + ((n & 0x001E) | (n >> 1 & 0x03E0) | (n >> 2 & 0x3C00)) * \
(snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t)))
#define SNES_NTSC_RGB15( ktable, n ) \
(snes_ntsc_rgb_t const*) (ktable + ((n & 0x001E) | (n >> 0 & 0x03E0) | (n >> 1 & 0x3C00)) * \
(snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t)))
#define SNES_NTSC_BGR15( ktable, n ) \
(snes_ntsc_rgb_t const*) (ktable + ((n << 9 & 0x3C00) | (n & 0x03E0) | (n >> 10 & 0x001E)) * \
(snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t)))
/* common 3->7 ntsc macros */
#define SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, ENTRY, table ) \
unsigned const snes_ntsc_pixel0_ = (pixel0);\
snes_ntsc_rgb_t const* kernel0 = ENTRY( table, snes_ntsc_pixel0_ );\
unsigned const snes_ntsc_pixel1_ = (pixel1);\
snes_ntsc_rgb_t const* kernel1 = ENTRY( table, snes_ntsc_pixel1_ );\
unsigned const snes_ntsc_pixel2_ = (pixel2);\
snes_ntsc_rgb_t const* kernel2 = ENTRY( table, snes_ntsc_pixel2_ );\
snes_ntsc_rgb_t const* kernelx0;\
snes_ntsc_rgb_t const* kernelx1 = kernel0;\
snes_ntsc_rgb_t const* kernelx2 = kernel0
#define SNES_NTSC_RGB_OUT_14_( x, rgb_out, bits, shift ) {\
snes_ntsc_rgb_t raw_ =\
kernel0 [x ] + kernel1 [(x+12)%7+14] + kernel2 [(x+10)%7+28] +\
kernelx0 [(x+7)%14] + kernelx1 [(x+ 5)%7+21] + kernelx2 [(x+ 3)%7+35];\
SNES_NTSC_CLAMP_( raw_, shift );\
SNES_NTSC_RGB_OUT_( rgb_out, bits, shift );\
}
/* common ntsc macros */
#define snes_ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1))
#define snes_ntsc_clamp_mask (snes_ntsc_rgb_builder * 3 / 2)
#define snes_ntsc_clamp_add (snes_ntsc_rgb_builder * 0x101)
#define SNES_NTSC_CLAMP_( io, shift ) {\
snes_ntsc_rgb_t sub = (io) >> (9-(shift)) & snes_ntsc_clamp_mask;\
snes_ntsc_rgb_t clamp = snes_ntsc_clamp_add - sub;\
io |= clamp;\
clamp -= sub;\
io &= clamp;\
}
#define SNES_NTSC_COLOR_IN_( index, color, ENTRY, table ) {\
unsigned color_;\
kernelx##index = kernel##index;\
kernel##index = (color_ = (color), ENTRY( table, color_ ));\
}
/* x is always zero except in snes_ntsc library */
#define SNES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\
if ( bits == 16 )\
rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\
if ( bits == 24 || bits == 32 )\
rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\
if ( bits == 15 )\
rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\
if ( bits == 14 )\
rgb_out = (raw_>>(24-x)& 0x001F)|(raw_>>(9-x)&0x03E0)|(raw_<<(6+x)&0x7C00);\
if ( bits == 0 )\
rgb_out = raw_ << x;\
}
#ifdef __cplusplus
}
#endif
#endif

@ -0,0 +1,32 @@
/* Configure library by modifying this file */
#ifndef SNES_NTSC_CONFIG_H
#define SNES_NTSC_CONFIG_H
#if !defined(SNES9X_GTK) && !defined(_WIN32) && !defined(__LIBRETRO__)
/* Format of source pixels */
#define SNES_NTSC_IN_FORMAT SNES_NTSC_RGB15
/* #define SNES_NTSC_IN_FORMAT SNES_NTSC_RGB16 */
/* #define SNES_NTSC_IN_FORMAT SNES_NTSC_BGR15 */
/* The following affect the built-in blitter only; a custom blitter can
handle things however it wants. */
/* Bits per pixel of output. Can be 15, 16, 32, or 24 (same as 32). */
#define SNES_NTSC_OUT_DEPTH 15
#else
#define SNES_NTSC_IN_FORMAT SNES_NTSC_RGB16
#define SNES_NTSC_OUT_DEPTH 16
#endif
/* Type of input pixel values */
#define SNES_NTSC_IN_T unsigned short
/* Each raw pixel input value is passed through this. You might want to mask
the pixel index if you use the high bits as flags, etc. */
#define SNES_NTSC_ADJ_IN( in ) in
/* For each pixel, this is the basic operation:
output_color = SNES_NTSC_ADJ_IN( SNES_NTSC_IN_T ) */
#endif

@ -0,0 +1,439 @@
/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */
/* Common implementation of NTSC filters */
#include <assert.h>
#include <math.h>
/* Copyright (C) 2006 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#define DISABLE_CORRECTION 0
#undef PI
#define PI 3.14159265358979323846f
#ifndef LUMA_CUTOFF
#define LUMA_CUTOFF 0.20
#endif
#ifndef gamma_size
#define gamma_size 1
#endif
#ifndef rgb_bits
#define rgb_bits 8
#endif
#ifndef artifacts_max
#define artifacts_max (artifacts_mid * 1.5f)
#endif
#ifndef fringing_max
#define fringing_max (fringing_mid * 2)
#endif
#ifndef STD_HUE_CONDITION
#define STD_HUE_CONDITION( setup ) 1
#endif
#define ext_decoder_hue (std_decoder_hue + 15)
#define rgb_unit (1 << rgb_bits)
#define rgb_offset (rgb_unit * 2 + 0.5f)
enum { burst_size = snes_ntsc_entry_size / burst_count };
enum { kernel_half = 16 };
enum { kernel_size = kernel_half * 2 + 1 };
typedef struct init_t
{
float to_rgb [burst_count * 6];
float to_float [gamma_size];
float contrast;
float brightness;
float artifacts;
float fringing;
float kernel [rescale_out * kernel_size * 2];
} init_t;
#define ROTATE_IQ( i, q, sin_b, cos_b ) {\
float t;\
t = i * cos_b - q * sin_b;\
q = i * sin_b + q * cos_b;\
i = t;\
}
static void init_filters( init_t* impl, snes_ntsc_setup_t const* setup )
{
#if rescale_out > 1
float kernels [kernel_size * 2];
#else
float* const kernels = impl->kernel;
#endif
/* generate luma (y) filter using sinc kernel */
{
/* sinc with rolloff (dsf) */
float const rolloff = 1 + (float) setup->sharpness * (float) 0.032;
float const maxh = 32;
float const pow_a_n = (float) pow( rolloff, maxh );
float sum;
int i;
/* quadratic mapping to reduce negative (blurring) range */
float to_angle = (float) setup->resolution + 1;
to_angle = PI / maxh * (float) LUMA_CUTOFF * (to_angle * to_angle + 1);
kernels [kernel_size * 3 / 2] = maxh; /* default center value */
for ( i = 0; i < kernel_half * 2 + 1; i++ )
{
int x = i - kernel_half;
float angle = x * to_angle;
/* instability occurs at center point with rolloff very close to 1.0 */
if ( x || pow_a_n > (float) 1.056 || pow_a_n < (float) 0.981 )
{
float rolloff_cos_a = rolloff * (float) cos( angle );
float num = 1 - rolloff_cos_a -
pow_a_n * (float) cos( maxh * angle ) +
pow_a_n * rolloff * (float) cos( (maxh - 1) * angle );
float den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff;
float dsf = num / den;
kernels [kernel_size * 3 / 2 - kernel_half + i] = dsf - (float) 0.5;
}
}
/* apply blackman window and find sum */
sum = 0;
for ( i = 0; i < kernel_half * 2 + 1; i++ )
{
float x = PI * 2 / (kernel_half * 2) * i;
float blackman = 0.42f - 0.5f * (float) cos( x ) + 0.08f * (float) cos( x * 2 );
sum += (kernels [kernel_size * 3 / 2 - kernel_half + i] *= blackman);
}
/* normalize kernel */
sum = 1.0f / sum;
for ( i = 0; i < kernel_half * 2 + 1; i++ )
{
int x = kernel_size * 3 / 2 - kernel_half + i;
kernels [x] *= sum;
/* assert( kernels [x] == kernels [x] ); catch numerical instability */
}
}
/* generate chroma (iq) filter using gaussian kernel */
{
float const cutoff_factor = -0.03125f;
float cutoff = (float) setup->bleed;
int i;
if ( cutoff < 0 )
{
/* keep extreme value accessible only near upper end of scale (1.0) */
cutoff *= cutoff;
cutoff *= cutoff;
cutoff *= cutoff;
cutoff *= -30.0f / 0.65f;
}
cutoff = cutoff_factor - 0.65f * cutoff_factor * cutoff;
for ( i = -kernel_half; i <= kernel_half; i++ )
kernels [kernel_size / 2 + i] = (float) exp( i * i * cutoff );
/* normalize even and odd phases separately */
for ( i = 0; i < 2; i++ )
{
float sum = 0;
int x;
for ( x = i; x < kernel_size; x += 2 )
sum += kernels [x];
sum = 1.0f / sum;
for ( x = i; x < kernel_size; x += 2 )
{
kernels [x] *= sum;
/* assert( kernels [x] == kernels [x] ); catch numerical instability */
}
}
}
/*
printf( "luma:\n" );
for ( i = kernel_size; i < kernel_size * 2; i++ )
printf( "%f\n", kernels [i] );
printf( "chroma:\n" );
for ( i = 0; i < kernel_size; i++ )
printf( "%f\n", kernels [i] );
*/
/* generate linear rescale kernels */
#if rescale_out > 1
{
float weight = 1.0f;
float* out = impl->kernel;
int n = rescale_out;
do
{
float remain = 0;
int i;
weight -= 1.0f / rescale_in;
for ( i = 0; i < kernel_size * 2; i++ )
{
float cur = kernels [i];
float m = cur * weight;
*out++ = m + remain;
remain = cur - m;
}
}
while ( --n );
}
#endif
}
static float const default_decoder [6] =
{ 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f };
static void init( init_t* impl, snes_ntsc_setup_t const* setup )
{
impl->brightness = (float) setup->brightness * (0.5f * rgb_unit) + rgb_offset;
impl->contrast = (float) setup->contrast * (0.5f * rgb_unit) + rgb_unit;
#ifdef default_palette_contrast
if ( !setup->palette )
impl->contrast *= default_palette_contrast;
#endif
impl->artifacts = (float) setup->artifacts;
if ( impl->artifacts > 0 )
impl->artifacts *= artifacts_max - artifacts_mid;
impl->artifacts = impl->artifacts * artifacts_mid + artifacts_mid;
impl->fringing = (float) setup->fringing;
if ( impl->fringing > 0 )
impl->fringing *= fringing_max - fringing_mid;
impl->fringing = impl->fringing * fringing_mid + fringing_mid;
init_filters( impl, setup );
/* generate gamma table */
if ( gamma_size > 1 )
{
float const to_float = 1.0f / (gamma_size - (gamma_size > 1));
float const gamma = 1.1333f - (float) setup->gamma * 0.5f;
/* match common PC's 2.2 gamma to TV's 2.65 gamma */
int i;
for ( i = 0; i < gamma_size; i++ )
impl->to_float [i] =
(float) pow( i * to_float, gamma ) * impl->contrast + impl->brightness;
}
/* setup decoder matricies */
{
float hue = (float) setup->hue * PI + PI / 180 * ext_decoder_hue;
float sat = (float) setup->saturation + 1;
float const* decoder = setup->decoder_matrix;
if ( !decoder )
{
decoder = default_decoder;
if ( STD_HUE_CONDITION( setup ) )
hue += PI / 180 * (std_decoder_hue - ext_decoder_hue);
}
{
float s = (float) sin( hue ) * sat;
float c = (float) cos( hue ) * sat;
float* out = impl->to_rgb;
int n;
n = burst_count;
do
{
float const* in = decoder;
int n = 3;
do
{
float i = *in++;
float q = *in++;
*out++ = i * c - q * s;
*out++ = i * s + q * c;
}
while ( --n );
if ( burst_count <= 1 )
break;
ROTATE_IQ( s, c, 0.866025f, -0.5f ); /* +120 degrees */
}
while ( --n );
}
}
}
/* kernel generation */
#define RGB_TO_YIQ( r, g, b, y, i ) (\
(y = (r) * 0.299f + (g) * 0.587f + (b) * 0.114f),\
(i = (r) * 0.596f - (g) * 0.275f - (b) * 0.321f),\
((r) * 0.212f - (g) * 0.523f + (b) * 0.311f)\
)
#define YIQ_TO_RGB( y, i, q, to_rgb, type, r, g ) (\
r = (type) (y + to_rgb [0] * i + to_rgb [1] * q),\
g = (type) (y + to_rgb [2] * i + to_rgb [3] * q),\
(type) (y + to_rgb [4] * i + to_rgb [5] * q)\
)
#define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1)
enum { rgb_kernel_size = burst_size / alignment_count };
enum { rgb_bias = rgb_unit * 2 * snes_ntsc_rgb_builder };
typedef struct pixel_info_t
{
int offset;
float negate;
float kernel [4];
} pixel_info_t;
#if rescale_in > 1
#define PIXEL_OFFSET_( ntsc, scaled ) \
(kernel_size / 2 + ntsc + (scaled != 0) + (rescale_out - scaled) % rescale_out + \
(kernel_size * 2 * scaled))
#define PIXEL_OFFSET( ntsc, scaled ) \
PIXEL_OFFSET_( ((ntsc) - (scaled) / rescale_out * rescale_in),\
(((scaled) + rescale_out * 10) % rescale_out) ),\
(1.0f - (((ntsc) + 100) & 2))
#else
#define PIXEL_OFFSET( ntsc, scaled ) \
(kernel_size / 2 + (ntsc) - (scaled)),\
(1.0f - (((ntsc) + 100) & 2))
#endif
extern pixel_info_t const snes_ntsc_pixels [alignment_count];
/* Generate pixel at all burst phases and column alignments */
static void gen_kernel( init_t* impl, float y, float i, float q, snes_ntsc_rgb_t* out )
{
/* generate for each scanline burst phase */
float const* to_rgb = impl->to_rgb;
int burst_remain = burst_count;
y -= rgb_offset;
do
{
/* Encode yiq into *two* composite signals (to allow control over artifacting).
Convolve these with kernels which: filter respective components, apply
sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack
into integer. Based on algorithm by NewRisingSun. */
pixel_info_t const* pixel = snes_ntsc_pixels;
int alignment_remain = alignment_count;
do
{
/* negate is -1 when composite starts at odd multiple of 2 */
float const yy = y * impl->fringing * pixel->negate;
float const ic0 = (i + yy) * pixel->kernel [0];
float const qc1 = (q + yy) * pixel->kernel [1];
float const ic2 = (i - yy) * pixel->kernel [2];
float const qc3 = (q - yy) * pixel->kernel [3];
float const factor = impl->artifacts * pixel->negate;
float const ii = i * factor;
float const yc0 = (y + ii) * pixel->kernel [0];
float const yc2 = (y - ii) * pixel->kernel [2];
float const qq = q * factor;
float const yc1 = (y + qq) * pixel->kernel [1];
float const yc3 = (y - qq) * pixel->kernel [3];
float const* k = &impl->kernel [pixel->offset];
int n;
++pixel;
for ( n = rgb_kernel_size; n; --n )
{
float i = k[0]*ic0 + k[2]*ic2;
float q = k[1]*qc1 + k[3]*qc3;
float y = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 +
k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset;
if ( rescale_out <= 1 )
k--;
else if ( k < &impl->kernel [kernel_size * 2 * (rescale_out - 1)] )
k += kernel_size * 2 - 1;
else
k -= kernel_size * 2 * (rescale_out - 1) + 2;
{
int r, g, b = YIQ_TO_RGB( y, i, q, to_rgb, int, r, g );
*out++ = PACK_RGB( r, g, b ) - rgb_bias;
}
}
}
while ( alignment_count > 1 && --alignment_remain );
if ( burst_count <= 1 )
break;
to_rgb += 6;
ROTATE_IQ( i, q, -0.866025f, -0.5f ); /* -120 degrees */
}
while ( --burst_remain );
}
static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out );
#if DISABLE_CORRECTION
#define CORRECT_ERROR( a ) { out [i] += rgb_bias; }
#define DISTRIBUTE_ERROR( a, b, c ) { out [i] += rgb_bias; }
#else
#define CORRECT_ERROR( a ) { out [a] += error; }
#define DISTRIBUTE_ERROR( a, b, c ) {\
snes_ntsc_rgb_t fourth = (error + 2 * snes_ntsc_rgb_builder) >> 2;\
fourth &= (rgb_bias >> 1) - snes_ntsc_rgb_builder;\
fourth -= rgb_bias >> 2;\
out [a] += fourth;\
out [b] += fourth;\
out [c] += fourth;\
out [i] += error - (fourth * 3);\
}
#endif
#define RGB_PALETTE_OUT( rgb, out_ )\
{\
unsigned char* out = (out_);\
snes_ntsc_rgb_t clamped = (rgb);\
SNES_NTSC_CLAMP_( clamped, (8 - rgb_bits) );\
out [0] = (unsigned char) (clamped >> 21);\
out [1] = (unsigned char) (clamped >> 11);\
out [2] = (unsigned char) (clamped >> 1);\
}
/* blitter related */
#ifndef restrict
#if defined (__GNUC__)
#define restrict __restrict__
#elif defined (_MSC_VER) && _MSC_VER > 1300
#define restrict __restrict
#else
/* no support for restricted pointers */
#define restrict
#endif
#endif
#include <limits.h>
#if SNES_NTSC_OUT_DEPTH <= 16
#if USHRT_MAX == 0xFFFF
typedef unsigned short snes_ntsc_out_t;
#else
#error "Need 16-bit int type"
#endif
#else
#if UINT_MAX == 0xFFFFFFFF
typedef unsigned int snes_ntsc_out_t;
#elif ULONG_MAX == 0xFFFFFFFF
typedef unsigned long snes_ntsc_out_t;
#else
#error "Need 32-bit int type"
#endif
#endif

@ -0,0 +1,621 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS

File diff suppressed because it is too large Load Diff

@ -0,0 +1,80 @@
// ****************************************************************************
// * This file is part of the xBRZ project. It is distributed under *
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// * *
// * Additionally and as a special exception, the author gives permission *
// * to link the code of this program with the following libraries *
// * (or with modified versions that use the same licenses), and distribute *
// * linked combinations including the two: MAME, FreeFileSync, Snes9x *
// * You must obey the GNU General Public License in all respects for all of *
// * the code used other than MAME, FreeFileSync, Snes9x. *
// * If you modify this file, you may extend this exception to your version *
// * of the file, but you are not obligated to do so. If you do not wish to *
// * do so, delete this exception statement from your version. *
// ****************************************************************************
#ifndef XBRZ_HEADER_3847894708239054
#define XBRZ_HEADER_3847894708239054
#include "port.h"
//#include <cstddef> //size_t
//#include <cstdint> //uint32_t
#include <limits>
#include "xbrz_config.h"
namespace xbrz
{
/*
-------------------------------------------------------------------------
| xBRZ: "Scale by rules" - high quality image upscaling filter by Zenju |
-------------------------------------------------------------------------
using a modified approach of xBR:
http://board.byuu.org/viewtopic.php?f=10&t=2248
- new rule set preserving small image features
- highly optimized for performance
- support alpha channel
- support multithreading
- support 64-bit architectures
- support processing image slices
- support scaling up to 6xBRZ
*/
enum class ColorFormat //from high bits -> low bits, 8 bit per channel
{
RGB, //8 bit for each red, green, blue, upper 8 bits unused
ARGB, //including alpha channel, BGRA byte order on little-endian machines
ARGB_UNBUFFERED, //like ARGB, but without the one-time buffer creation overhead (ca. 100 - 300 ms) at the expense of a slightly slower scaling time
};
const int SCALE_FACTOR_MAX = 6;
/*
-> map source (srcWidth * srcHeight) to target (scale * width x scale * height) image, optionally processing a half-open slice of rows [yFirst, yLast) only
-> support for source/target pitch in bytes!
-> if your emulator changes only a few image slices during each cycle (e.g. DOSBox) then there's no need to run xBRZ on the complete image:
Just make sure you enlarge the source image slice by 2 rows on top and 2 on bottom (this is the additional range the xBRZ algorithm is using during analysis)
CAVEAT: If there are multiple changed slices, make sure they do not overlap after adding these additional rows in order to avoid a memory race condition
in the target image data if you are using multiple threads for processing each enlarged slice!
THREAD-SAFETY: - parts of the same image may be scaled by multiple threads as long as the [yFirst, yLast) ranges do not overlap!
- there is a minor inefficiency for the first row of a slice, so avoid processing single rows only; suggestion: process at least 8-16 rows
*/
void scale(size_t factor, //valid range: 2 - SCALE_FACTOR_MAX
const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight,
ColorFormat colFmt,
const ScalerCfg& cfg = ScalerCfg(),
int yFirst = 0, int yLast = std::numeric_limits<int>::max()); //slice of source image
void bilinearScale(const uint32_t* src, int srcWidth, int srcHeight,
/**/ uint32_t* trg, int trgWidth, int trgHeight);
void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight,
/**/ uint32_t* trg, int trgWidth, int trgHeight);
//parameter tuning
bool equalColorTest(uint32_t col1, uint32_t col2, ColorFormat colFmt, double luminanceWeight, double equalColorTolerance);
}
#endif

@ -0,0 +1,34 @@
// ****************************************************************************
// * This file is part of the xBRZ project. It is distributed under *
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// * *
// * Additionally and as a special exception, the author gives permission *
// * to link the code of this program with the following libraries *
// * (or with modified versions that use the same licenses), and distribute *
// * linked combinations including the two: MAME, FreeFileSync, Snes9x *
// * You must obey the GNU General Public License in all respects for all of *
// * the code used other than MAME, FreeFileSync, Snes9x. *
// * If you modify this file, you may extend this exception to your version *
// * of the file, but you are not obligated to do so. If you do not wish to *
// * do so, delete this exception statement from your version. *
// ****************************************************************************
#ifndef XBRZ_CONFIG_HEADER_284578425345
#define XBRZ_CONFIG_HEADER_284578425345
//do NOT include any headers here! used by xBRZ_dll!!!
namespace xbrz
{
struct ScalerCfg
{
double luminanceWeight = 1;
double equalColorTolerance = 30;
double dominantDirectionThreshold = 3.6;
double steepDirectionThreshold = 2.2;
double newTestAttribute = 0; //unused; test new parameters
};
}
#endif

@ -0,0 +1,272 @@
// ****************************************************************************
// * This file is part of the xBRZ project. It is distributed under *
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// * *
// * Additionally and as a special exception, the author gives permission *
// * to link the code of this program with the following libraries *
// * (or with modified versions that use the same licenses), and distribute *
// * linked combinations including the two: MAME, FreeFileSync, Snes9x *
// * You must obey the GNU General Public License in all respects for all of *
// * the code used other than MAME, FreeFileSync, Snes9x. *
// * If you modify this file, you may extend this exception to your version *
// * of the file, but you are not obligated to do so. If you do not wish to *
// * do so, delete this exception statement from your version. *
// ****************************************************************************
#ifndef XBRZ_TOOLS_H_825480175091875
#define XBRZ_TOOLS_H_825480175091875
#include <cassert>
#include <algorithm>
#include <type_traits>
namespace xbrz
{
template <uint32_t N> inline
unsigned char getByte(uint32_t val) { return static_cast<unsigned char>((val >> (8 * N)) & 0xff); }
inline unsigned char getAlpha(uint32_t pix) { return getByte<3>(pix); }
inline unsigned char getRed (uint32_t pix) { return getByte<2>(pix); }
inline unsigned char getGreen(uint32_t pix) { return getByte<1>(pix); }
inline unsigned char getBlue (uint32_t pix) { return getByte<0>(pix); }
inline uint32_t makePixel(unsigned char a, unsigned char r, unsigned char g, unsigned char b) { return (a << 24) | (r << 16) | (g << 8) | b; }
inline uint32_t makePixel( unsigned char r, unsigned char g, unsigned char b) { return (r << 16) | (g << 8) | b; }
inline uint32_t rgb555to888(uint16_t pix) { return ((pix & 0x7C00) << 9) | ((pix & 0x03E0) << 6) | ((pix & 0x001F) << 3); }
inline uint32_t rgb565to888(uint16_t pix) { return ((pix & 0xF800) << 8) | ((pix & 0x07E0) << 5) | ((pix & 0x001F) << 3); }
inline uint16_t rgb888to555(uint32_t pix) { return static_cast<uint16_t>(((pix & 0xF80000) >> 9) | ((pix & 0x00F800) >> 6) | ((pix & 0x0000F8) >> 3)); }
inline uint16_t rgb888to565(uint32_t pix) { return static_cast<uint16_t>(((pix & 0xF80000) >> 8) | ((pix & 0x00FC00) >> 5) | ((pix & 0x0000F8) >> 3)); }
template <class Pix> inline
Pix* byteAdvance(Pix* ptr, int bytes)
{
using PixNonConst = typename std::remove_cv<Pix>::type;
using PixByte = typename std::conditional<std::is_same<Pix, PixNonConst>::value, char, const char>::type;
static_assert(std::is_integral<PixNonConst>::value, "Pix* is expected to be cast-able to char*");
return reinterpret_cast<Pix*>(reinterpret_cast<PixByte*>(ptr) + bytes);
}
//fill block with the given color
template <class Pix> inline
void fillBlock(Pix* trg, int pitch, Pix col, int blockWidth, int blockHeight)
{
//for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch))
// std::fill(trg, trg + blockWidth, col);
for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch))
for (int x = 0; x < blockWidth; ++x)
trg[x] = col;
}
//nearest-neighbor (going over target image - slow for upscaling, since source is read multiple times missing out on cache! Fast for similar image sizes!)
template <class PixSrc, class PixTrg, class PixConverter>
void nearestNeighborScale(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch,
/**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch,
int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/)
{
static_assert(std::is_integral<PixSrc>::value, "PixSrc* is expected to be cast-able to char*");
static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*");
static_assert(std::is_same<decltype(pixCvrt(PixSrc())), PixTrg>::value, "PixConverter returning wrong pixel format");
if (srcPitch < srcWidth * static_cast<int>(sizeof(PixSrc)) ||
trgPitch < trgWidth * static_cast<int>(sizeof(PixTrg)))
{
assert(false);
return;
}
yFirst = std::max(yFirst, 0);
yLast = std::min(yLast, trgHeight);
if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return;
for (int y = yFirst; y < yLast; ++y)
{
const int ySrc = srcHeight * y / trgHeight;
const PixSrc* const srcLine = byteAdvance(src, ySrc * srcPitch);
PixTrg* const trgLine = byteAdvance(trg, y * trgPitch);
for (int x = 0; x < trgWidth; ++x)
{
const int xSrc = srcWidth * x / trgWidth;
trgLine[x] = pixCvrt(srcLine[xSrc]);
}
}
}
//nearest-neighbor (going over source image - fast for upscaling, since source is read only once
template <class PixSrc, class PixTrg, class PixConverter>
void nearestNeighborScaleOverSource(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch,
/**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch,
int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/)
{
static_assert(std::is_integral<PixSrc>::value, "PixSrc* is expected to be cast-able to char*");
static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*");
static_assert(std::is_same<decltype(pixCvrt(PixSrc())), PixTrg>::value, "PixConverter returning wrong pixel format");
if (srcPitch < srcWidth * static_cast<int>(sizeof(PixSrc)) ||
trgPitch < trgWidth * static_cast<int>(sizeof(PixTrg)))
{
assert(false);
return;
}
yFirst = std::max(yFirst, 0);
yLast = std::min(yLast, srcHeight);
if (yFirst >= yLast || trgWidth <= 0 || trgHeight <= 0) return;
for (int y = yFirst; y < yLast; ++y)
{
//mathematically: ySrc = floor(srcHeight * yTrg / trgHeight)
// => search for integers in: [ySrc, ySrc + 1) * trgHeight / srcHeight
//keep within for loop to support MT input slices!
const int yTrgFirst = ( y * trgHeight + srcHeight - 1) / srcHeight; //=ceil(y * trgHeight / srcHeight)
const int yTrgLast = ((y + 1) * trgHeight + srcHeight - 1) / srcHeight; //=ceil(((y + 1) * trgHeight) / srcHeight)
const int blockHeight = yTrgLast - yTrgFirst;
if (blockHeight > 0)
{
const PixSrc* srcLine = byteAdvance(src, y * srcPitch);
/**/ PixTrg* trgLine = byteAdvance(trg, yTrgFirst * trgPitch);
int xTrgFirst = 0;
for (int x = 0; x < srcWidth; ++x)
{
const int xTrgLast = ((x + 1) * trgWidth + srcWidth - 1) / srcWidth;
const int blockWidth = xTrgLast - xTrgFirst;
if (blockWidth > 0)
{
xTrgFirst = xTrgLast;
const auto trgPix = pixCvrt(srcLine[x]);
fillBlock(trgLine, trgPitch, trgPix, blockWidth, blockHeight);
trgLine += blockWidth;
}
}
}
}
}
template <class PixTrg, class PixConverter>
void bilinearScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch,
/**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch,
int yFirst, int yLast, PixConverter pixCvrt /*convert uint32_t to PixTrg*/)
{
static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*");
static_assert(std::is_same<decltype(pixCvrt(uint32_t())), PixTrg>::value, "PixConverter returning wrong pixel format");
if (srcPitch < srcWidth * static_cast<int>(sizeof(uint32_t)) ||
trgPitch < trgWidth * static_cast<int>(sizeof(PixTrg)))
{
assert(false);
return;
}
yFirst = std::max(yFirst, 0);
yLast = std::min(yLast, trgHeight);
if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return;
const double scaleX = static_cast<double>(trgWidth ) / srcWidth;
const double scaleY = static_cast<double>(trgHeight) / srcHeight;
//perf notes:
// -> double-based calculation is (slightly) faster than float
// -> precalculation gives significant boost; std::vector<> memory allocation is negligible!
struct CoeffsX
{
int x1 = 0;
int x2 = 0;
double xx1 = 0;
double x2x = 0;
};
std::vector<CoeffsX> buf(trgWidth);
for (int x = 0; x < trgWidth; ++x)
{
const int x1 = srcWidth * x / trgWidth;
int x2 = x1 + 1;
if (x2 == srcWidth) --x2;
const double xx1 = x / scaleX - x1;
const double x2x = 1 - xx1;
CoeffsX tmp;
tmp.x1 = x1;
tmp.x2 = x2;
tmp.xx1 = xx1;
tmp.x2x = x2x;
buf[x] = tmp;
}
for (int y = yFirst; y < yLast; ++y)
{
const int y1 = srcHeight * y / trgHeight;
int y2 = y1 + 1;
if (y2 == srcHeight) --y2;
const double yy1 = y / scaleY - y1;
const double y2y = 1 - yy1;
const uint32_t* const srcLine = byteAdvance(src, y1 * srcPitch);
const uint32_t* const srcLineNext = byteAdvance(src, y2 * srcPitch);
PixTrg* const trgLine = byteAdvance(trg, y * trgPitch);
for (int x = 0; x < trgWidth; ++x)
{
//perf: do NOT "simplify" the variable layout without measurement!
const int x1 = buf[x].x1;
const int x2 = buf[x].x2;
const double xx1 = buf[x].xx1;
const double x2x = buf[x].x2x;
const double x2xy2y = x2x * y2y;
const double xx1y2y = xx1 * y2y;
const double x2xyy1 = x2x * yy1;
const double xx1yy1 = xx1 * yy1;
auto interpolate = [=](int offset)
{
/*
https://en.wikipedia.org/wiki/Bilinear_interpolation
(c11(x2 - x) + c21(x - x1)) * (y2 - y ) +
(c12(x2 - x) + c22(x - x1)) * (y - y1)
*/
const auto c11 = (srcLine [x1] >> (8 * offset)) & 0xff;
const auto c21 = (srcLine [x2] >> (8 * offset)) & 0xff;
const auto c12 = (srcLineNext[x1] >> (8 * offset)) & 0xff;
const auto c22 = (srcLineNext[x2] >> (8 * offset)) & 0xff;
return c11 * x2xy2y + c21 * xx1y2y +
c12 * x2xyy1 + c22 * xx1yy1;
};
const double bi = interpolate(0);
const double gi = interpolate(1);
const double ri = interpolate(2);
const double ai = interpolate(3);
const auto b = static_cast<uint32_t>(bi + 0.5);
const auto g = static_cast<uint32_t>(gi + 0.5);
const auto r = static_cast<uint32_t>(ri + 0.5);
const auto a = static_cast<uint32_t>(ai + 0.5);
const uint32_t trgPix = (a << 24) | (r << 16) | (g << 8) | b;
trgLine[x] = pixCvrt(trgPix);
}
}
}
}
#endif //XBRZ_TOOLS_H_825480175091875

@ -0,0 +1,154 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _FONT_H_
#define _FONT_H_
static const char *font[] =
{
//2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678
" . . . . .. . . ",
" .#. .#.#. . . ... .#. . . .##. .#. .#. . . . . ",
" .#. .#.#. .#.#. .###. .#..#. .#. .#. .#. .#. .#.#. .#. .#. ",
" .#. .#.#. .#####. .#.#. ..#. .#.#. .#. .#. .#. .#. ..#.. .... .#. ",
" .#. . . .#.#. .###. .#.. .#. . .#. .#. .###. .#####. .. .####. .. .#. ",
" . .#####. .#.#. .#..#. .#.#. .#. .#. .#. ..#.. .##. .... .##. .#. ",
" .#. .#.#. .###. . .#. .#.#. .#. .#. .#.#. .#. .#. .##. . ",
" . . . ... . . . . . . . . .#. .. ",
" . ",
//2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678
" . . .. .... . .... .. .... .. .. . ",
" .#. .#. .##. .####. .#. .####. .##. .####. .##. .##. .. .. . . .#. ",
".#.#. .##. .#..#. ...#. .##. .#... .#.. ...#. .#..#. .#..#. .##. .##. .#. .... .#. .#.#. ",
".#.#. .#. . .#. .##. .#.#. .###. .###. .#. .##. .#..#. .##. .##. .#. .####. .#. ..#. ",
".#.#. .#. .#. ...#. .####. ...#. .#..#. .#. .#..#. .###. .. .. .#. .... .#. .#. ",
".#.#. .#. .#.. .#..#. ..#. .#..#. .#..#. .#. .#..#. ..#. .##. .##. .#. .####. .#. . ",
" .#. .###. .####. .##. .#. .##. .##. .#. .##. .##. .##. .#. .#. .... .#. .#. ",
" . ... .... .. . .. .. . .. .. .. .#. . . . ",
" . ",
//2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678
" .. .. ... .. ... .... .... .. . . ... . . . . . . . . .. ",
" .##. .##. .###. .##. .###. .####. .####. .##. .#..#. .###. .#. .#..#. .#. .#. .#. .#. .#. .##. ",
".#..#. .#..#. .#..#. .#..#. .#..#. .#... .#... .#..#. .#..#. .#. .#. .#.#. .#. .##.##. .##..#. .#..#. ",
".#.##. .#..#. .###. .#. . .#..#. .###. .###. .#... .####. .#. .#. .##. .#. .#.#.#. .#.#.#. .#..#. ",
".#.##. .####. .#..#. .#. . .#..#. .#.. .#.. .#.##. .#..#. .#. . .#. .##. .#. .#...#. .#.#.#. .#..#. ",
".#... .#..#. .#..#. .#..#. .#..#. .#... .#. .#..#. .#..#. .#. .#..#. .#.#. .#... .#. .#. .#..##. .#..#. ",
" .##. .#..#. .###. .##. .###. .####. .#. .###. .#..#. .###. .##. .#..#. .####. .#. .#. .#. .#. .##. ",
" .. . . ... .. ... .... . ... . . ... .. . . .... . . . . .. ",
" ",
//2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678
" ... .. ... .. ... . . . . . . . . . . .... ... ... . ",
".###. .##. .###. .##. .###. .#. .#. .#. .#. .#. .#. .#..#. .#.#. .####. .###. . .###. .#. ",
".#..#. .#..#. .#..#. .#..#. .#. .#. .#. .#. .#. .#...#. .#..#. .#.#. ...#. .#.. .#. ..#. .#.#. ",
".#..#. .#..#. .#..#. .#.. .#. .#. .#. .#. .#. .#.#.#. .##. .#.#. .#. .#. .#. .#. . . ",
".###. .#..#. .###. ..#. .#. .#. .#. .#. .#. .#.#.#. .#..#. .#. .#. .#. .#. .#. ",
".#.. .##.#. .#.#. .#..#. .#. .#...#. .#.#. .##.##. .#..#. .#. .#... .#.. .#. ..#. .... ",
".#. .##. .#..#. .##. .#. .###. .#. .#. .#. .#..#. .#. .####. .###. . .###. .####. ",
" . ..#. . . .. . ... . . . . . . .... ... ... .... ",
" . ",
//2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678
" .. . . . . . . . .. ",
".##. .#. .#. .#. .#. .#. .#. .#. .##. ",
" .#. ... .#.. .. ..#. .. .#.#. ... .#.. .. . .#.. .#. .. .. ... .. ",
" .#. .###. .###. .##. .###. .##. .#.. .###. .###. .##. .#. .#.#. .#. .##.##. .###. .##. ",
" . .#..#. .#..#. .#.. .#..#. .#.##. .###. .#..#. .#..#. .#. .#. .##. .#. .#.#.#. .#..#. .#..#. ",
" .#.##. .#..#. .#.. .#..#. .##.. .#. .##. .#..#. .#. ..#. .#.#. .#. .#...#. .#..#. .#..#. ",
" .#.#. .###. .##. .###. .##. .#. .#... .#..#. .###. .#.#. .#..#. .###. .#. .#. .#..#. .##. ",
" . . ... .. ... .. . .###. . . ... .#. . . ... . . . . .. ",
" ... . ",
//2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678
" . . . . . . ",
" .#. .#. .#. .#. .#.#. ",
" ... ... ... ... .#. . . . . . . . . . . .... .#. .#. .#. .#.#. ",
".###. .###. .###. .###. .###. .#..#. .#.#. .#...#. .#..#. .#..#. .####. .##. .#. .##. . . ",
".#..#. .#..#. .#..#. .##.. .#. .#..#. .#.#. .#.#.#. .##. .#..#. ..#. .#. .#. .#. ",
".#..#. .#..#. .#. . ..##. .#.. .#..#. .#.#. .#.#.#. .##. .#.#. .#.. .#. .#. .#. ",
".###. .###. .#. .###. .##. .###. .#. .#.#. .#..#. .#. .####. .#. .#. .#. ",
".#.. ..#. . ... .. ... . . . . . .#. .... . . . ",
" . . . ",
//2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
//2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
//2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678
" .. ..... ",
" .##. .#####. ... . . . . .. ",
" .#. . .. ....#. .###. .#. .#. .#. .#.. .##. . . . ",
" .#. .#. .##. .#####. .#. .#. .###. ... .###. .###. .. .#. .#.#.#. ",
" . .#. .#. . .##. ....#. .#. .##. .#.#. .###. .#. .##.#. .##. .##. .#.#.#. ",
" .#. . .#. .#. .. ...#. .#. .#. ..#. .#. .##. .#.. ..#. .#. ...#. ",
" .#.#. .##. .#. .###. .#. .#. .#. .###. .#. .#. .####. .##. .##. ",
" .#. .. .#. ... . . . ... . . .... .. .. ",
" . . ",
//2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678
" .... . . ... . . . .... . . . .. . ..... . . . ",
" .####. .#. ..#.. .###. ...#. ..#.. ..#.. .####. .#... .... .#.#. .##..#. .#####. .#... .#. .#. ",
" .... ...#. .#. .#####. .#. .#####. .#####. .#####. .#..#. .####. .####. .#####. .. .#. ....#. .#####. .#. .#. ",
".####. .##. .## .#...#. .#. ...#. ..#.#. ..#.. .# .#. .#..#. ...#. .#.#. .##..#. .#. .#..#. .#..#. ",
" .... .#. .#.# . .#. .#. .##. .#..#. .#####. .#. .#. . .#. .#. ..#. .. .#. .#. .#.#. . .#. ",
" .#. .#. ..#. ..#.. .#.#. .#..#. ..#.. . .#. .#. ...#. .#. ...#. .#.#. .#... ...#. ",
" .#. .#. .##. .#####. .#..#. .#..#. .#. .#. .#. .####. .#. .###. .#. .#. .###. .###. ",
" . . .. ..... . . . . . . . .... . ... . . ... ... ",
" ",
//2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678
" .... .. . . . ... . . ... .... . . . ..... . . ",
" .####. ..##. .#.#.#. .###. .#. ..#.. .###. .####. ..#.. .#. . . .#. .. .#####. .#. ..#.. ..... ",
" .#..#. .###. .#.#.#. ..... .#. .#####. ... ...#. .#####. .#. .#.#. .#..##. ....#. .#.#. .#####. .#####. ",
" .####. ..#. .#.#.#. .#####. .##. ..#.. .#.#. ....#. .#. .#.#. .###.. .#. .#..#. ..#.. .#. ",
".#...#. .####. . ..#. ..#.. .#.#. .#. .#. .###. .#. .#. .#. .#.. .#. . .#. .#.#.#. .#.#. ",
" . .#. ..#. ...#. .#. .#.. ..#. ..... .#.#. .#.#.#. .#. .#. .#. .#.... ..#. .#. .#.#.#. .#. ",
" .#. .##. .###. .#. .#. .##. .#####. .#. .#. ..#.. .#. .#. .#. .####. .##. .#. ..#.. .#. ",
" . .. ... . . .. ..... . . . . . . .... .. . . . ",
" ",
//2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678
" .. . . ... . .... .... . . . . . ..... . . . . ",
" .##. .#. .#. .###. .#... ... .####. .####. .#..#. .#.#. .#. ..... .#####. ....#. .#.#. .#. ",
" ..#. .#. . .#. .#. .#.##. .###. ...#. ..... .#..#. .#.#. .#. .#####. .#...#. .###.#. .#.#. .#.#. ",
" .##. .#. . .#.#. .#####. .##.#. .#. .###. .#####. .#..#. .#.#. .#. . .#...#. . .#. ....#. . . .#. ",
" ..#. .#..#. .##. ..#.. .#.#. ..#. ..#. ....#. . .#. .#.#. .#..#. .#...#. .#. .#. . ",
" .##. .####. ..#.#. .#.. .#. ...#. ...#. ..#. ..#. .#.#. .#.#. .#####. ..#. ...#. ",
" ..#. ...#. .##. . .###. .#. .#####. .####. .##. .##. .#..##. .##. .#...#. .##. .###. ",
" . . .. ... . ..... .... .. ... . .. .. . . .. ... ",
" ",
//2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678
" ",
" . . ",
" .#. .#. . ..... ",
" .##. .##. .#. .#####. ",
" .###. .###. .###. .###. ",
" .##. .##. .#####. .#. ",
" .#. .#. ..... . ",
" . . ",
" ",
//2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678/2345678
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" "
};
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,815 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "snes9x.h"
#include "memmap.h"
#include "fxinst.h"
#include "fxemu.h"
static void FxReset (struct FxInfo_s *);
static void fx_readRegisterSpace (void);
static void fx_writeRegisterSpace (void);
static void fx_updateRamBank (uint8);
static void fx_dirtySCBR (void);
static bool8 fx_checkStartAddress (void);
static uint32 FxEmulate (uint32);
static void FxCacheWriteAccess (uint16);
static void FxFlushCache (void);
void S9xInitSuperFX (void)
{
memset((uint8 *) &GSU, 0, sizeof(struct FxRegs_s));
}
void S9xResetSuperFX (void)
{
// FIXME: Snes9x only runs the SuperFX at the end of every line.
// 5823405 is a magic number that seems to work for most games.
SuperFX.speedPerLine = (uint32) (5823405 * ((1.0 / (float) Memory.ROMFramesPerSecond) / ((float) (Timings.V_Max))));
SuperFX.oneLineDone = FALSE;
SuperFX.vFlags = 0;
CPU.IRQExternal = FALSE;
FxReset(&SuperFX);
}
void S9xSetSuperFX (uint8 byte, uint16 address)
{
switch (address)
{
case 0x3030:
if ((Memory.FillRAM[0x3030] ^ byte) & FLG_G)
{
Memory.FillRAM[0x3030] = byte;
if (byte & FLG_G)
{
if (!SuperFX.oneLineDone)
{
S9xSuperFXExec();
SuperFX.oneLineDone = TRUE;
}
}
else
FxFlushCache();
}
else
Memory.FillRAM[0x3030] = byte;
break;
case 0x3031:
Memory.FillRAM[0x3031] = byte;
break;
case 0x3033:
Memory.FillRAM[0x3033] = byte;
break;
case 0x3034:
Memory.FillRAM[0x3034] = byte & 0x7f;
break;
case 0x3036:
Memory.FillRAM[0x3036] = byte & 0x7f;
break;
case 0x3037:
Memory.FillRAM[0x3037] = byte;
break;
case 0x3038:
Memory.FillRAM[0x3038] = byte;
fx_dirtySCBR();
break;
case 0x3039:
Memory.FillRAM[0x3039] = byte;
break;
case 0x303a:
Memory.FillRAM[0x303a] = byte;
break;
case 0x303b:
break;
case 0x303c:
Memory.FillRAM[0x303c] = byte;
fx_updateRamBank(byte);
break;
case 0x303f:
Memory.FillRAM[0x303f] = byte;
break;
case 0x301f:
Memory.FillRAM[0x301f] = byte;
Memory.FillRAM[0x3000 + GSU_SFR] |= FLG_G;
if (!SuperFX.oneLineDone)
{
S9xSuperFXExec();
SuperFX.oneLineDone = TRUE;
}
break;
default:
Memory.FillRAM[address] = byte;
if (address >= 0x3100)
FxCacheWriteAccess(address);
break;
}
}
uint8 S9xGetSuperFX (uint16 address)
{
uint8 byte;
byte = Memory.FillRAM[address];
if (address == 0x3031)
{
CPU.IRQExternal = FALSE;
Memory.FillRAM[0x3031] = byte & 0x7f;
}
return (byte);
}
void S9xSuperFXExec (void)
{
if ((Memory.FillRAM[0x3000 + GSU_SFR] & FLG_G) && (Memory.FillRAM[0x3000 + GSU_SCMR] & 0x18) == 0x18)
{
FxEmulate(((Memory.FillRAM[0x3000 + GSU_CLSR] & 1) ? (SuperFX.speedPerLine * 5 / 2) : SuperFX.speedPerLine) * Settings.SuperFXClockMultiplier / 100);
uint16 GSUStatus = Memory.FillRAM[0x3000 + GSU_SFR] | (Memory.FillRAM[0x3000 + GSU_SFR + 1] << 8);
if ((GSUStatus & (FLG_G | FLG_IRQ)) == FLG_IRQ)
CPU.IRQExternal = TRUE;
}
}
static void FxReset (struct FxInfo_s *psFxInfo)
{
// Clear all internal variables
memset((uint8 *) &GSU, 0, sizeof(struct FxRegs_s));
// Set default registers
GSU.pvSreg = GSU.pvDreg = &R0;
// Set RAM and ROM pointers
GSU.pvRegisters = psFxInfo->pvRegisters;
GSU.nRamBanks = psFxInfo->nRamBanks;
GSU.pvRam = psFxInfo->pvRam;
GSU.nRomBanks = psFxInfo->nRomBanks;
GSU.pvRom = psFxInfo->pvRom;
GSU.vPrevScreenHeight = ~0;
GSU.vPrevMode = ~0;
// The GSU can't access more than 2mb (16mbits)
if (GSU.nRomBanks > 0x20)
GSU.nRomBanks = 0x20;
// Clear FxChip register space
memset(GSU.pvRegisters, 0, 0x300);
// Set FxChip version Number
GSU.pvRegisters[0x3b] = 0;
// Make ROM bank table
for (int i = 0; i < 256; i++)
{
uint32 b = i & 0x7f;
if (b >= 0x40)
{
if (GSU.nRomBanks > 1)
b %= GSU.nRomBanks;
else
b &= 1;
GSU.apvRomBank[i] = &GSU.pvRom[b << 16];
}
else
{
b %= GSU.nRomBanks * 2;
GSU.apvRomBank[i] = &GSU.pvRom[(b << 16) + 0x200000];
}
}
// Make RAM bank table
for (int i = 0; i < 4; i++)
{
GSU.apvRamBank[i] = &GSU.pvRam[(i % GSU.nRamBanks) << 16];
GSU.apvRomBank[0x70 + i] = GSU.apvRamBank[i];
}
// Start with a nop in the pipe
GSU.vPipe = 0x01;
// Set pointer to GSU cache
GSU.pvCache = &GSU.pvRegisters[0x100];
fx_readRegisterSpace();
}
static void fx_readRegisterSpace (void)
{
static uint32 avHeight[] = { 128, 160, 192, 256 };
static uint32 avMult[] = { 16, 32, 32, 64 };
uint8 *p;
int n;
GSU.vErrorCode = 0;
// Update R0-R15
p = GSU.pvRegisters;
for (int i = 0; i < 16; i++)
{
GSU.avReg[i] = *p++;
GSU.avReg[i] += ((uint32) (*p++)) << 8;
}
// Update other registers
p = GSU.pvRegisters;
GSU.vStatusReg = (uint32) p[GSU_SFR];
GSU.vStatusReg |= ((uint32) p[GSU_SFR + 1]) << 8;
GSU.vPrgBankReg = (uint32) p[GSU_PBR];
GSU.vRomBankReg = (uint32) p[GSU_ROMBR];
GSU.vRamBankReg = ((uint32) p[GSU_RAMBR]) & (FX_RAM_BANKS - 1);
GSU.vCacheBaseReg = (uint32) p[GSU_CBR];
GSU.vCacheBaseReg |= ((uint32) p[GSU_CBR + 1]) << 8;
// Update status register variables
GSU.vZero = !(GSU.vStatusReg & FLG_Z);
GSU.vSign = (GSU.vStatusReg & FLG_S) << 12;
GSU.vOverflow = (GSU.vStatusReg & FLG_OV) << 16;
GSU.vCarry = (GSU.vStatusReg & FLG_CY) >> 2;
// Set bank pointers
GSU.pvRamBank = GSU.apvRamBank[GSU.vRamBankReg & 0x3];
GSU.pvRomBank = GSU.apvRomBank[GSU.vRomBankReg];
GSU.pvPrgBank = GSU.apvRomBank[GSU.vPrgBankReg];
// Set screen pointers
GSU.pvScreenBase = &GSU.pvRam[USEX8(p[GSU_SCBR]) << 10];
n = (int) (!!(p[GSU_SCMR] & 0x04));
n |= ((int) (!!(p[GSU_SCMR] & 0x20))) << 1;
GSU.vScreenHeight = GSU.vScreenRealHeight = avHeight[n];
GSU.vMode = p[GSU_SCMR] & 0x03;
if (n == 3)
GSU.vScreenSize = (256 / 8) * (256 / 8) * 32;
else
GSU.vScreenSize = (GSU.vScreenHeight / 8) * (256 / 8) * avMult[GSU.vMode];
if (GSU.vPlotOptionReg & 0x10) // OBJ Mode (for drawing into sprites)
GSU.vScreenHeight = 256;
if (GSU.pvScreenBase + GSU.vScreenSize > GSU.pvRam + (GSU.nRamBanks * 65536))
GSU.pvScreenBase = GSU.pvRam + (GSU.nRamBanks * 65536) - GSU.vScreenSize;
GSU.pfPlot = fx_PlotTable[GSU.vMode];
GSU.pfRpix = fx_PlotTable[GSU.vMode + 5];
fx_OpcodeTable[0x04c] = GSU.pfPlot;
fx_OpcodeTable[0x14c] = GSU.pfRpix;
fx_OpcodeTable[0x24c] = GSU.pfPlot;
fx_OpcodeTable[0x34c] = GSU.pfRpix;
fx_computeScreenPointers();
//fx_backupCache();
}
static void fx_writeRegisterSpace (void)
{
uint8 *p;
p = GSU.pvRegisters;
for (int i = 0; i < 16; i++)
{
*p++ = (uint8) GSU.avReg[i];
*p++ = (uint8) (GSU.avReg[i] >> 8);
}
// Update status register
if (USEX16(GSU.vZero) == 0)
SF(Z);
else
CF(Z);
if (GSU.vSign & 0x8000)
SF(S);
else
CF(S);
if (GSU.vOverflow >= 0x8000 || GSU.vOverflow < -0x8000)
SF(OV);
else
CF(OV);
if (GSU.vCarry)
SF(CY);
else
CF(CY);
p = GSU.pvRegisters;
p[GSU_SFR] = (uint8) GSU.vStatusReg;
p[GSU_SFR + 1] = (uint8) (GSU.vStatusReg >> 8);
p[GSU_PBR] = (uint8) GSU.vPrgBankReg;
p[GSU_ROMBR] = (uint8) GSU.vRomBankReg;
p[GSU_RAMBR] = (uint8) GSU.vRamBankReg;
p[GSU_CBR] = (uint8) GSU.vCacheBaseReg;
p[GSU_CBR + 1] = (uint8) (GSU.vCacheBaseReg >> 8);
//fx_restoreCache();
}
// Update RamBankReg and RAM Bank pointer
static void fx_updateRamBank (uint8 byte)
{
// Update BankReg and Bank pointer
GSU.vRamBankReg = (uint32) byte & (FX_RAM_BANKS - 1);
GSU.pvRamBank = GSU.apvRamBank[byte & 0x3];
}
// SCBR write seen. We need to update our cached screen pointers
static void fx_dirtySCBR (void)
{
GSU.vSCBRDirty = TRUE;
}
static bool8 fx_checkStartAddress (void)
{
// Check if we start inside the cache
if (GSU.bCacheActive && R15 >= GSU.vCacheBaseReg && R15 < (GSU.vCacheBaseReg + 512))
return (TRUE);
/*
// Check if we're in an unused area
if (GSU.vPrgBankReg < 0x40 && R15 < 0x8000)
return (FALSE);
*/
if (GSU.vPrgBankReg >= 0x60 && GSU.vPrgBankReg <= 0x6f)
return (FALSE);
if (GSU.vPrgBankReg >= 0x74)
return (FALSE);
// Check if we're in RAM and the RAN flag is not set
if (GSU.vPrgBankReg >= 0x70 && GSU.vPrgBankReg <= 0x73 && !(SCMR & (1 << 3)))
return (FALSE);
// If not, we're in ROM, so check if the RON flag is set
if (!(SCMR & (1 << 4)))
return (FALSE);
return (TRUE);
}
// Execute until the next stop instruction
static uint32 FxEmulate (uint32 nInstructions)
{
uint32 vCount;
// Read registers and initialize GSU session
fx_readRegisterSpace();
// Check if the start address is valid
if (!fx_checkStartAddress())
{
CF(G);
fx_writeRegisterSpace();
/*
GSU.vIllegalAddress = (GSU.vPrgBankReg << 24) | R15;
return (FX_ERROR_ILLEGAL_ADDRESS);
*/
return (0);
}
// Execute GSU session
CF(IRQ);
/*
if (GSU.bBreakPoint)
vCount = fx_run_to_breakpoint(nInstructions);
else
*/
vCount = fx_run(nInstructions);
// Store GSU registers
fx_writeRegisterSpace();
// Check for error code
if (GSU.vErrorCode)
return (GSU.vErrorCode);
else
return (vCount);
}
void fx_computeScreenPointers (void)
{
if (GSU.vMode != GSU.vPrevMode || GSU.vPrevScreenHeight != GSU.vScreenHeight || GSU.vSCBRDirty)
{
GSU.vSCBRDirty = FALSE;
// Make a list of pointers to the start of each screen column
switch (GSU.vScreenHeight)
{
case 128:
switch (GSU.vMode)
{
case 0:
for (int i = 0; i < 32; i++)
{
GSU.apvScreen[i] = GSU.pvScreenBase + (i << 4);
GSU.x[i] = i << 8;
}
break;
case 1:
for (int i = 0; i < 32; i++)
{
GSU.apvScreen[i] = GSU.pvScreenBase + (i << 5);
GSU.x[i] = i << 9;
}
break;
case 2:
case 3:
for (int i = 0; i < 32; i++)
{
GSU.apvScreen[i] = GSU.pvScreenBase + (i << 6);
GSU.x[i] = i << 10;
}
break;
}
break;
case 160:
switch (GSU.vMode)
{
case 0:
for (int i = 0; i < 32; i++)
{
GSU.apvScreen[i] = GSU.pvScreenBase + (i << 4);
GSU.x[i] = (i << 8) + (i << 6);
}
break;
case 1:
for (int i = 0; i < 32; i++)
{
GSU.apvScreen[i] = GSU.pvScreenBase + (i << 5);
GSU.x[i] = (i << 9) + (i << 7);
}
break;
case 2:
case 3:
for (int i = 0; i < 32; i++)
{
GSU.apvScreen[i] = GSU.pvScreenBase + (i << 6);
GSU.x[i] = (i << 10) + (i << 8);
}
break;
}
break;
case 192:
switch (GSU.vMode)
{
case 0:
for (int i = 0; i < 32; i++)
{
GSU.apvScreen[i] = GSU.pvScreenBase + (i << 4);
GSU.x[i] = (i << 8) + (i << 7);
}
break;
case 1:
for (int i = 0; i < 32; i++)
{
GSU.apvScreen[i] = GSU.pvScreenBase + (i << 5);
GSU.x[i] = (i << 9) + (i << 8);
}
break;
case 2:
case 3:
for (int i = 0; i < 32; i++)
{
GSU.apvScreen[i] = GSU.pvScreenBase + (i << 6);
GSU.x[i] = (i << 10) + (i << 9);
}
break;
}
break;
case 256:
switch (GSU.vMode)
{
case 0:
for (int i = 0; i < 32; i++)
{
GSU.apvScreen[i] = GSU.pvScreenBase + ((i & 0x10) << 9) + ((i & 0xf) << 8);
GSU.x[i] = ((i & 0x10) << 8) + ((i & 0xf) << 4);
}
break;
case 1:
for (int i = 0; i < 32; i++)
{
GSU.apvScreen[i] = GSU.pvScreenBase + ((i & 0x10) << 10) + ((i & 0xf) << 9);
GSU.x[i] = ((i & 0x10) << 9) + ((i & 0xf) << 5);
}
break;
case 2:
case 3:
for (int i = 0; i < 32; i++)
{
GSU.apvScreen[i] = GSU.pvScreenBase + ((i & 0x10) << 11) + ((i & 0xf) << 10);
GSU.x[i] = ((i & 0x10) << 10) + ((i & 0xf) << 6);
}
break;
}
break;
}
GSU.vPrevMode = GSU.vMode;
GSU.vPrevScreenHeight = GSU.vScreenHeight;
}
}
// Write access to the cache
static void FxCacheWriteAccess (uint16 vAddress)
{
/*
if (!GSU.bCacheActive)
{
uint8 v = GSU.pvCache[GSU.pvCache[vAddress & 0x1ff];
fx_setCache();
GSU.pvCache[GSU.pvCache[vAddress & 0x1ff] = v;
}
*/
if ((vAddress & 0x00f) == 0x00f)
GSU.vCacheFlags |= 1 << ((vAddress & 0x1f0) >> 4);
}
static void FxFlushCache (void)
{
GSU.vCacheFlags = 0;
GSU.vCacheBaseReg = 0;
GSU.bCacheActive = FALSE;
//GSU.vPipe = 0x1;
}
void fx_flushCache (void)
{
//fx_restoreCache();
GSU.vCacheFlags = 0;
GSU.bCacheActive = FALSE;
}
/*
static void fx_setCache (void)
{
uint32 c;
GSU.bCacheActive = TRUE;
GSU.pvRegisters[0x3e] &= 0xf0;
c = (uint32) GSU.pvRegisters[0x3e];
c |= ((uint32) GSU.pvRegisters[0x3f]) << 8;
if (c == GSU.vCacheBaseReg)
return;
GSU.vCacheBaseReg = c;
GSU.vCacheFlags = 0;
if (c < (0x10000 - 512))
{
const uint8 *t = &ROM(c);
memcpy(GSU.pvCache, t, 512);
}
else
{
const uint8 *t1, *t2;
uint32 i = 0x10000 - c;
t1 = &ROM(c);
t2 = &ROM(0);
memcpy(GSU.pvCache, t1, i);
memcpy(&GSU.pvCache[i], t2, 512 - i);
}
}
*/
/*
static void fx_backupCache (void)
{
uint32 v = GSU.vCacheFlags;
uint32 c = USEX16(GSU.vCacheBaseReg);
if (v)
{
for (int i = 0; i < 32; i++)
{
if (v & 1)
{
if (c < (0x10000 - 16))
{
uint8 *t = &GSU.pvPrgBank[c];
memcpy(&GSU.avCacheBackup[i << 4], t, 16);
memcpy(t, &GSU.pvCache[i << 4], 16);
}
else
{
uint8 *t1, *t2;
uint32 a = 0x10000 - c;
t1 = &GSU.pvPrgBank[c];
t2 = &GSU.pvPrgBank[0];
memcpy(&GSU.avCacheBackup[i << 4], t1, a);
memcpy(t1, &GSU.pvCache[i << 4], a);
memcpy(&GSU.avCacheBackup[(i << 4) + a], t2, 16 - a);
memcpy(t2, &GSU.pvCache[(i << 4) + a], 16 - a);
}
}
c = USEX16(c + 16);
v >>= 1;
}
}
}
*/
/*
static void fx_restoreCache()
{
uint32 v = GSU.vCacheFlags;
uint32 c = USEX16(GSU.vCacheBaseReg);
if (v)
{
for (int i = 0; i < 32; i++)
{
if (v & 1)
{
if (c < (0x10000 - 16))
{
uint8 *t = &GSU.pvPrgBank[c];
memcpy(t, &GSU.avCacheBackup[i << 4], 16);
memcpy(&GSU.pvCache[i << 4], t, 16);
}
else
{
uint8 *t1, *t2;
uint32 a = 0x10000 - c;
t1 = &GSU.pvPrgBank[c];
t2 = &GSU.pvPrgBank[0];
memcpy(t1, &GSU.avCacheBackup[i << 4], a);
memcpy(&GSU.pvCache[i << 4], t1, a);
memcpy(t2, &GSU.avCacheBackup[(i << 4) + a], 16 - a);
memcpy(&GSU.pvCache[(i << 4) + a], t2, 16 - a);
}
}
c = USEX16(c + 16);
v >>= 1;
}
}
}
*/
// Breakpoints
/*
static void FxBreakPointSet (uint32 vAddress)
{
GSU.bBreakPoint = TRUE;
GSU.vBreakPoint = USEX16(vAddress);
}
*/
/*
static void FxBreakPointClear (void)
{
GSU.bBreakPoint = FALSE;
}
*/
// Step by step execution
/*
static uint32 FxStepOver (uint32 nInstructions)
{
uint32 vCount;
fx_readRegisterSpace();
if (!fx_checkStartAddress())
{
CF(G);
#if 0
GSU.vIllegalAddress = (GSU.vPrgBankReg << 24) | R15;
return (FX_ERROR_ILLEGAL_ADDRESS);
#else
return (0);
#endif
}
if (PIPE >= 0xf0)
GSU.vStepPoint = USEX16(R15 + 3);
else
if ((PIPE >= 0x05 && PIPE <= 0x0f) || (PIPE >= 0xa0 && PIPE <= 0xaf))
GSU.vStepPoint = USEX16(R15 + 2);
else
GSU.vStepPoint = USEX16(R15 + 1);
vCount = fx_step_over(nInstructions);
fx_writeRegisterSpace();
if (GSU.vErrorCode)
return (GSU.vErrorCode);
else
return (vCount);
}
*/
// Errors
/*
static int FxGetErrorCode (void)
{
return (GSU.vErrorCode);
}
*/
/*
static int FxGetIllegalAddress (void)
{
return (GSU.vIllegalAddress);
}
*/
// Access to internal registers
/*
static uint32 FxGetColorRegister (void)
{
return (GSU.vColorReg & 0xff);
}
*/
/*
static uint32 FxGetPlotOptionRegister (void)
{
return (GSU.vPlotOptionReg & 0x1f);
}
*/
/*
static uint32 FxGetSourceRegisterIndex (void)
{
return (GSU.pvSreg - GSU.avReg);
}
*/
/*
static uint32 FxGetDestinationRegisterIndex (void)
{
return (GSU.pvDreg - GSU.avReg);
}
*/
// Get the byte currently in the pipe
/*
static uint8 FxPipe (void)
{
return (GSU.vPipe);
}
*/

@ -0,0 +1,37 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _FXEMU_H_
#define _FXEMU_H_
#define FX_BREAKPOINT (-1)
#define FX_ERROR_ILLEGAL_ADDRESS (-2)
// The FxInfo_s structure, the link between the FxEmulator and the Snes Emulator
struct FxInfo_s
{
uint32 vFlags;
uint8 *pvRegisters; // 768 bytes located in the memory at address 0x3000
uint32 nRamBanks; // Number of 64kb-banks in GSU-RAM/BackupRAM (banks 0x70-0x73)
uint8 *pvRam; // Pointer to GSU-RAM
uint32 nRomBanks; // Number of 32kb-banks in Cart-ROM
uint8 *pvRom; // Pointer to Cart-ROM
uint32 speedPerLine;
bool8 oneLineDone;
};
extern struct FxInfo_s SuperFX;
void S9xInitSuperFX (void);
void S9xResetSuperFX (void);
void S9xSuperFXExec (void);
void S9xSetSuperFX (uint8, uint16);
uint8 S9xGetSuperFX (uint16);
void fx_flushCache (void);
void fx_computeScreenPointers (void);
uint32 fx_run (uint32);
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,371 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _FXINST_H_
#define _FXINST_H_
/*
* FxChip(GSU) register space specification
* (Register address space 3000-32ff)
*
* The 16 generic 16 bit registers:
* (Some have a special function in special circumstances)
* 3000 - R0 default source/destination register
* 3002 - R1 pixel plot X position register
* 3004 - R2 pixel plot Y position register
* 3006 - R3
* 3008 - R4 lower 16 bit result of lmult
* 300a - R5
* 300c - R6 multiplier for fmult and lmult
* 300e - R7 fixed point texel X position for merge
* 3010 - R8 fixed point texel Y position for merge
* 3012 - R9
* 3014 - R10
* 3016 - R11 return address set by link
* 3018 - R12 loop counter
* 301a - R13 loop point address
* 301c - R14 rom address for getb, getbh, getbl, getbs
* 301e - R15 program counter
*
* 3020-302f - unused
*
* Other internal registers
* 3030 - SFR status flag register (16bit)
* 3032 - unused
* 3033 - BRAMR Backup RAM register (8bit)
* 3034 - PBR program bank register (8bit)
* 3035 - unused
* 3036 - ROMBR rom bank register (8bit)
* 3037 - CFGR control flags register (8bit)
* 3038 - SCBR screen base register (8bit)
* 3039 - CLSR clock speed register (8bit)
* 303a - SCMR screen mode register (8bit)
* 303b - VCR version code register (8bit) (read only)
* 303c - RAMBR ram bank register (8bit)
* 303d - unused
* 303e - CBR cache base register (16bit)
*
* 3040-30ff - unused
*
* 3100-32ff - CACHERAM 512 bytes of GSU cache memory
*
* SFR status flag register bits:
* 0 -
* 1 Z Zero flag
* 2 CY Carry flag
* 3 S Sign flag
* 4 OV Overflow flag
* 5 G Go flag (set to 1 when the GSU is running)
* 6 R Set to 1 when reading ROM using R14 address
* 7 -
* 8 ALT1 Mode set-up flag for the next instruction
* 9 ALT2 Mode set-up flag for the next instruction
* 10 IL Immediate lower 8-bit flag
* 11 IH Immediate higher 8-bit flag
* 12 B Set to 1 when the WITH instruction is executed
* 13 -
* 14 -
* 15 IRQ Set to 1 when GSU caused an interrupt
* Set to 0 when read by 658c16
*
* BRAMR = 0, BackupRAM is disabled
* BRAMR = 1, BackupRAM is enabled
*
* CFGR control flags register bits:
* 0 -
* 1 -
* 2 -
* 3 -
* 4 -
* 5 MS0 Multiplier speed, 0=standard, 1=high speed
* 6 -
* 7 IRQ Set to 1 when GSU interrupt request is masked
*
* CLSR clock speed register bits:
* 0 CLSR clock speed, 0 = 10.7Mhz, 1 = 21.4Mhz
*
* SCMR screen mode register bits:
* 0 MD0 color depth mode bit 0
* 1 MD1 color depth mode bit 1
* 2 HT0 screen height bit 1
* 3 RAN RAM access control
* 4 RON ROM access control
* 5 HT1 screen height bit 2
* 6 -
* 7 -
*
* RON = 0 SNES CPU has ROM access
* RON = 1 GSU has ROM access
*
* RAN = 0 SNES has game pak RAM access
* RAN = 1 GSU has game pak RAM access
*
* HT1 HT0 Screen height mode
* 0 0 128 pixels high
* 0 1 160 pixels high
* 1 0 192 pixels high
* 1 1 OBJ mode
*
* MD1 MD0 Color depth mode
* 0 0 4 color mode
* 0 1 16 color mode
* 1 0 not used
* 1 1 256 color mode
*
* CBR cache base register bits:
* 15-4 Specify base address for data to cache from ROM or RAM
* 3-0 Are 0 when address is read
*
* Write access to the program counter (301e) from
* the SNES-CPU will start the GSU, and it will not
* stop until it reaches a stop instruction.
*
*/
// Number of banks in GSU RAM
#define FX_RAM_BANKS 4
// Emulate proper R14 ROM access (slower, but safer)
#define FX_DO_ROMBUFFER
// Address checking (definately slow)
//#define FX_ADDRESS_CHECK
struct FxRegs_s
{
// FxChip registers
uint32 avReg[16]; // 16 Generic registers
uint32 vColorReg; // Internal color register
uint32 vPlotOptionReg; // Plot option register
uint32 vStatusReg; // Status register
uint32 vPrgBankReg; // Program bank index register
uint32 vRomBankReg; // Rom bank index register
uint32 vRamBankReg; // Ram bank index register
uint32 vCacheBaseReg; // Cache base address register
uint32 vCacheFlags; // Saying what parts of the cache was written to
uint32 vLastRamAdr; // Last RAM address accessed
uint32 *pvDreg; // Pointer to current destination register
uint32 *pvSreg; // Pointer to current source register
uint8 vRomBuffer; // Current byte read by R14
uint8 vPipe; // Instructionset pipe
uint32 vPipeAdr; // The address of where the pipe was read from
// Status register optimization stuff
uint32 vSign; // v & 0x8000
uint32 vZero; // v == 0
uint32 vCarry; // a value of 1 or 0
int32 vOverflow; // (v >= 0x8000 || v < -0x8000)
// Other emulator variables
int32 vErrorCode;
uint32 vIllegalAddress;
uint8 bBreakPoint;
uint32 vBreakPoint;
uint32 vStepPoint;
uint8 *pvRegisters; // 768 bytes located in the memory at address 0x3000
uint32 nRamBanks; // Number of 64kb-banks in FxRam (Don't confuse it with SNES-Ram!!!)
uint8 *pvRam; // Pointer to FxRam
uint32 nRomBanks; // Number of 32kb-banks in Cart-ROM
uint8 *pvRom; // Pointer to Cart-ROM
uint32 vMode; // Color depth/mode
uint32 vPrevMode; // Previous depth
uint8 *pvScreenBase;
uint8 *apvScreen[32]; // Pointer to each of the 32 screen colums
int32 x[32];
uint32 vScreenHeight; // 128, 160, 192 or 256 (could be overriden by cmode)
uint32 vScreenRealHeight; // 128, 160, 192 or 256
uint32 vPrevScreenHeight;
uint32 vScreenSize;
void (*pfPlot) (void);
void (*pfRpix) (void);
uint8 *pvRamBank; // Pointer to current RAM-bank
uint8 *pvRomBank; // Pointer to current ROM-bank
uint8 *pvPrgBank; // Pointer to current program ROM-bank
uint8 *apvRamBank[FX_RAM_BANKS]; // Ram bank table (max 256kb)
uint8 *apvRomBank[256]; // Rom bank table
uint8 bCacheActive;
uint8 *pvCache; // Pointer to the GSU cache
uint8 avCacheBackup[512]; // Backup of ROM when the cache has replaced it
uint32 vCounter;
uint32 vInstCount;
uint32 vSCBRDirty; // If SCBR is written, our cached screen pointers need updating
uint8 *avRegAddr; // To reference avReg in snapshot.cpp
};
extern struct FxRegs_s GSU;
// GSU registers
#define GSU_R0 0x000
#define GSU_R1 0x002
#define GSU_R2 0x004
#define GSU_R3 0x006
#define GSU_R4 0x008
#define GSU_R5 0x00a
#define GSU_R6 0x00c
#define GSU_R7 0x00e
#define GSU_R8 0x010
#define GSU_R9 0x012
#define GSU_R10 0x014
#define GSU_R11 0x016
#define GSU_R12 0x018
#define GSU_R13 0x01a
#define GSU_R14 0x01c
#define GSU_R15 0x01e
#define GSU_SFR 0x030
#define GSU_BRAMR 0x033
#define GSU_PBR 0x034
#define GSU_ROMBR 0x036
#define GSU_CFGR 0x037
#define GSU_SCBR 0x038
#define GSU_CLSR 0x039
#define GSU_SCMR 0x03a
#define GSU_VCR 0x03b
#define GSU_RAMBR 0x03c
#define GSU_CBR 0x03e
#define GSU_CACHERAM 0x100
// SFR flags
#define FLG_Z (1 << 1)
#define FLG_CY (1 << 2)
#define FLG_S (1 << 3)
#define FLG_OV (1 << 4)
#define FLG_G (1 << 5)
#define FLG_R (1 << 6)
#define FLG_ALT1 (1 << 8)
#define FLG_ALT2 (1 << 9)
#define FLG_IL (1 << 10)
#define FLG_IH (1 << 11)
#define FLG_B (1 << 12)
#define FLG_IRQ (1 << 15)
// Test flag
#define TF(a) (GSU.vStatusReg & FLG_##a)
#define CF(a) (GSU.vStatusReg &= ~FLG_##a)
#define SF(a) (GSU.vStatusReg |= FLG_##a)
// Test and set flag if condition, clear if not
#define TS(a, b) GSU.vStatusReg = ((GSU.vStatusReg & (~FLG_##a)) | ((!!(##b)) * FLG_##a))
// Testing ALT1 & ALT2 bits
#define ALT0 (!TF(ALT1) && !TF(ALT2))
#define ALT1 ( TF(ALT1) && !TF(ALT2))
#define ALT2 (!TF(ALT1) && TF(ALT2))
#define ALT3 ( TF(ALT1) && TF(ALT2))
// Sign extend from 8/16 bit to 32 bit
#define SEX8(a) ((int32) ((int8) (a)))
#define SEX16(a) ((int32) ((int16) (a)))
// Unsign extend from 8/16 bit to 32 bit
#define USEX8(a) ((uint32) ((uint8) (a)))
#define USEX16(a) ((uint32) ((uint16) (a)))
#define SUSEX16(a) ((int32) ((uint16) (a)))
// Set/Clr Sign and Zero flag
#define TSZ(num) TS(S, ((num) & 0x8000)); TS(Z, (!USEX16(num)))
// Clear flags
#define CLRFLAGS GSU.vStatusReg &= ~(FLG_ALT1 | FLG_ALT2 | FLG_B); GSU.pvDreg = GSU.pvSreg = &R0
// Read current RAM-Bank
#define RAM(adr) GSU.pvRamBank[USEX16(adr)]
// Read current ROM-Bank
#define ROM(idx) GSU.pvRomBank[USEX16(idx)]
// Access the current value in the pipe
#define PIPE GSU.vPipe
// Access data in the current program bank
#define PRGBANK(idx) GSU.pvPrgBank[USEX16(idx)]
// Update pipe from ROM
#if 0
#define FETCHPIPE { PIPE = PRGBANK(R15); GSU.vPipeAdr = (GSU.vPrgBankReg << 16) + R15; }
#else
#define FETCHPIPE { PIPE = PRGBANK(R15); }
#endif
// ABS
#define ABS(x) ((x) < 0 ? -(x) : (x))
// Access source register
#define SREG (*GSU.pvSreg)
// Access destination register
#define DREG (*GSU.pvDreg)
#ifndef FX_DO_ROMBUFFER
// Don't read R14
#define READR14
// Don't test and/or read R14
#define TESTR14
#else
// Read R14
#define READR14 GSU.vRomBuffer = ROM(R14)
// Test and/or read R14
#define TESTR14 if (GSU.pvDreg == &R14) READR14
#endif
// Access to registers
#define R0 GSU.avReg[0]
#define R1 GSU.avReg[1]
#define R2 GSU.avReg[2]
#define R3 GSU.avReg[3]
#define R4 GSU.avReg[4]
#define R5 GSU.avReg[5]
#define R6 GSU.avReg[6]
#define R7 GSU.avReg[7]
#define R8 GSU.avReg[8]
#define R9 GSU.avReg[9]
#define R10 GSU.avReg[10]
#define R11 GSU.avReg[11]
#define R12 GSU.avReg[12]
#define R13 GSU.avReg[13]
#define R14 GSU.avReg[14]
#define R15 GSU.avReg[15]
#define SFR GSU.vStatusReg
#define PBR GSU.vPrgBankReg
#define ROMBR GSU.vRomBankReg
#define RAMBR GSU.vRamBankReg
#define CBR GSU.vCacheBaseReg
#define SCBR USEX8(GSU.pvRegisters[GSU_SCBR])
#define SCMR USEX8(GSU.pvRegisters[GSU_SCMR])
#define COLR GSU.vColorReg
#define POR GSU.vPlotOptionReg
#define BRAMR USEX8(GSU.pvRegisters[GSU_BRAMR])
#define VCR USEX8(GSU.pvRegisters[GSU_VCR])
#define CFGR USEX8(GSU.pvRegisters[GSU_CFGR])
#define CLSR USEX8(GSU.pvRegisters[GSU_CLSR])
// Execute instruction from the pipe, and fetch next byte to the pipe
#define FX_STEP \
{ \
uint32 vOpcode = (uint32) PIPE; \
FETCHPIPE; \
(*fx_OpcodeTable[(GSU.vStatusReg & 0x300) | vOpcode])(); \
}
extern void (*fx_PlotTable[]) (void);
extern void (*fx_OpcodeTable[]) (void);
// Set this define if branches are relative to the instruction in the delay slot (I think they are)
#define BRANCH_DELAY_RELATIVE
#endif

@ -0,0 +1,857 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef _GETSET_H_
#define _GETSET_H_
#include "cpuexec.h"
#include "dsp.h"
#include "sa1.h"
#include "spc7110.h"
#include "c4.h"
#include "obc1.h"
#include "seta.h"
#include "bsx.h"
#include "msu1.h"
#define addCyclesInMemoryAccess \
if (!CPU.InDMAorHDMA) \
{ \
CPU.Cycles += speed; \
while (CPU.Cycles >= CPU.NextEvent) \
S9xDoHEventProcessing(); \
}
#define addCyclesInMemoryAccess_x2 \
if (!CPU.InDMAorHDMA) \
{ \
CPU.Cycles += speed << 1; \
while (CPU.Cycles >= CPU.NextEvent) \
S9xDoHEventProcessing(); \
}
extern uint8 OpenBus;
static inline int32 memory_speed (uint32 address)
{
if (address & 0x408000)
{
if (address & 0x800000)
return (CPU.FastROMSpeed);
return (SLOW_ONE_CYCLE);
}
if ((address + 0x6000) & 0x4000)
return (SLOW_ONE_CYCLE);
if ((address - 0x4000) & 0x7e00)
return (ONE_CYCLE);
return (TWO_CYCLES);
}
inline uint8 S9xGetByte (uint32 Address)
{
int block = (Address & 0xffffff) >> MEMMAP_SHIFT;
uint8 *GetAddress = Memory.Map[block];
int32 speed = memory_speed(Address);
uint8 byte;
if (GetAddress >= (uint8 *) CMemory::MAP_LAST)
{
byte = *(GetAddress + (Address & 0xffff));
addCyclesInMemoryAccess;
return (byte);
}
switch ((pint) GetAddress)
{
case CMemory::MAP_CPU:
byte = S9xGetCPU(Address & 0xffff);
addCyclesInMemoryAccess;
return (byte);
case CMemory::MAP_PPU:
if (CPU.InDMAorHDMA && (Address & 0xff00) == 0x2100)
return (OpenBus);
byte = S9xGetPPU(Address & 0xffff);
addCyclesInMemoryAccess;
return (byte);
case CMemory::MAP_LOROM_SRAM:
case CMemory::MAP_SA1RAM:
// Address & 0x7fff : offset into bank
// Address & 0xff0000 : bank
// bank >> 1 | offset : SRAM address, unbound
// unbound & SRAMMask : SRAM offset
byte = *(Memory.SRAM + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Memory.SRAMMask));
addCyclesInMemoryAccess;
return (byte);
case CMemory::MAP_LOROM_SRAM_B:
byte = *(Multi.sramB + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Multi.sramMaskB));
addCyclesInMemoryAccess;
return (byte);
case CMemory::MAP_HIROM_SRAM:
case CMemory::MAP_RONLY_SRAM:
byte = *(Memory.SRAM + (((Address & 0x7fff) - 0x6000 + ((Address & 0x1f0000) >> 3)) & Memory.SRAMMask));
addCyclesInMemoryAccess;
return (byte);
case CMemory::MAP_BWRAM:
byte = *(Memory.BWRAM + ((Address & 0x7fff) - 0x6000));
addCyclesInMemoryAccess;
return (byte);
case CMemory::MAP_DSP:
byte = S9xGetDSP(Address & 0xffff);
addCyclesInMemoryAccess;
return (byte);
case CMemory::MAP_SPC7110_ROM:
byte = S9xGetSPC7110Byte(Address);
addCyclesInMemoryAccess;
return (byte);
case CMemory::MAP_SPC7110_DRAM:
byte = S9xGetSPC7110(0x4800);
addCyclesInMemoryAccess;
return (byte);
case CMemory::MAP_C4:
byte = S9xGetC4(Address & 0xffff);
addCyclesInMemoryAccess;
return (byte);
case CMemory::MAP_OBC_RAM:
byte = S9xGetOBC1(Address & 0xffff);
addCyclesInMemoryAccess;
return (byte);
case CMemory::MAP_SETA_DSP:
byte = S9xGetSetaDSP(Address);
addCyclesInMemoryAccess;
return (byte);
case CMemory::MAP_SETA_RISC:
byte = S9xGetST018(Address);
addCyclesInMemoryAccess;
return (byte);
case CMemory::MAP_BSX:
byte = S9xGetBSX(Address);
addCyclesInMemoryAccess;
return (byte);
case CMemory::MAP_NONE:
default:
byte = OpenBus;
addCyclesInMemoryAccess;
return (byte);
}
}
inline uint16 S9xGetWord (uint32 Address, enum s9xwrap_t w = WRAP_NONE)
{
uint16 word;
uint32 mask = MEMMAP_MASK & (w == WRAP_PAGE ? 0xff : (w == WRAP_BANK ? 0xffff : 0xffffff));
if ((Address & mask) == mask)
{
PC_t a;
word = OpenBus = S9xGetByte(Address);
switch (w)
{
case WRAP_PAGE:
a.xPBPC = Address;
a.B.xPCl++;
return (word | (S9xGetByte(a.xPBPC) << 8));
case WRAP_BANK:
a.xPBPC = Address;
a.W.xPC++;
return (word | (S9xGetByte(a.xPBPC) << 8));
case WRAP_NONE:
default:
return (word | (S9xGetByte(Address + 1) << 8));
}
}
int block = (Address & 0xffffff) >> MEMMAP_SHIFT;
uint8 *GetAddress = Memory.Map[block];
int32 speed = memory_speed(Address);
if (GetAddress >= (uint8 *) CMemory::MAP_LAST)
{
word = READ_WORD(GetAddress + (Address & 0xffff));
addCyclesInMemoryAccess_x2;
return (word);
}
switch ((pint) GetAddress)
{
case CMemory::MAP_CPU:
word = S9xGetCPU(Address & 0xffff);
addCyclesInMemoryAccess;
word |= S9xGetCPU((Address + 1) & 0xffff) << 8;
addCyclesInMemoryAccess;
return (word);
case CMemory::MAP_PPU:
if (CPU.InDMAorHDMA)
{
word = OpenBus = S9xGetByte(Address);
return (word | (S9xGetByte(Address + 1) << 8));
}
word = S9xGetPPU(Address & 0xffff);
addCyclesInMemoryAccess;
word |= S9xGetPPU((Address + 1) & 0xffff) << 8;
addCyclesInMemoryAccess;
return (word);
case CMemory::MAP_LOROM_SRAM:
case CMemory::MAP_SA1RAM:
if (Memory.SRAMMask >= MEMMAP_MASK)
word = READ_WORD(Memory.SRAM + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Memory.SRAMMask));
else
word = (*(Memory.SRAM + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Memory.SRAMMask))) |
((*(Memory.SRAM + (((((Address + 1) & 0xff0000) >> 1) | ((Address + 1) & 0x7fff)) & Memory.SRAMMask))) << 8);
addCyclesInMemoryAccess_x2;
return (word);
case CMemory::MAP_LOROM_SRAM_B:
if (Multi.sramMaskB >= MEMMAP_MASK)
word = READ_WORD(Multi.sramB + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Multi.sramMaskB));
else
word = (*(Multi.sramB + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Multi.sramMaskB))) |
((*(Multi.sramB + (((((Address + 1) & 0xff0000) >> 1) | ((Address + 1) & 0x7fff)) & Multi.sramMaskB))) << 8);
addCyclesInMemoryAccess_x2;
return (word);
case CMemory::MAP_HIROM_SRAM:
case CMemory::MAP_RONLY_SRAM:
if (Memory.SRAMMask >= MEMMAP_MASK)
word = READ_WORD(Memory.SRAM + (((Address & 0x7fff) - 0x6000 + ((Address & 0x1f0000) >> 3)) & Memory.SRAMMask));
else
word = (*(Memory.SRAM + (((Address & 0x7fff) - 0x6000 + ((Address & 0x1f0000) >> 3)) & Memory.SRAMMask)) |
(*(Memory.SRAM + ((((Address + 1) & 0x7fff) - 0x6000 + (((Address + 1) & 0x1f0000) >> 3)) & Memory.SRAMMask)) << 8));
addCyclesInMemoryAccess_x2;
return (word);
case CMemory::MAP_BWRAM:
word = READ_WORD(Memory.BWRAM + ((Address & 0x7fff) - 0x6000));
addCyclesInMemoryAccess_x2;
return (word);
case CMemory::MAP_DSP:
word = S9xGetDSP(Address & 0xffff);
addCyclesInMemoryAccess;
word |= S9xGetDSP((Address + 1) & 0xffff) << 8;
addCyclesInMemoryAccess;
return (word);
case CMemory::MAP_SPC7110_ROM:
word = S9xGetSPC7110Byte(Address);
addCyclesInMemoryAccess;
word |= S9xGetSPC7110Byte(Address + 1) << 8;
addCyclesInMemoryAccess;
return (word);
case CMemory::MAP_SPC7110_DRAM:
word = S9xGetSPC7110(0x4800);
addCyclesInMemoryAccess;
word |= S9xGetSPC7110(0x4800) << 8;
addCyclesInMemoryAccess;
return (word);
case CMemory::MAP_C4:
word = S9xGetC4(Address & 0xffff);
addCyclesInMemoryAccess;
word |= S9xGetC4((Address + 1) & 0xffff) << 8;
addCyclesInMemoryAccess;
return (word);
case CMemory::MAP_OBC_RAM:
word = S9xGetOBC1(Address & 0xffff);
addCyclesInMemoryAccess;
word |= S9xGetOBC1((Address + 1) & 0xffff) << 8;
addCyclesInMemoryAccess;
return (word);
case CMemory::MAP_SETA_DSP:
word = S9xGetSetaDSP(Address);
addCyclesInMemoryAccess;
word |= S9xGetSetaDSP(Address + 1) << 8;
addCyclesInMemoryAccess;
return (word);
case CMemory::MAP_SETA_RISC:
word = S9xGetST018(Address);
addCyclesInMemoryAccess;
word |= S9xGetST018(Address + 1) << 8;
addCyclesInMemoryAccess;
return (word);
case CMemory::MAP_BSX:
word = S9xGetBSX(Address);
addCyclesInMemoryAccess;
word |= S9xGetBSX(Address + 1) << 8;
addCyclesInMemoryAccess;
return (word);
case CMemory::MAP_NONE:
default:
word = OpenBus | (OpenBus << 8);
addCyclesInMemoryAccess_x2;
return (word);
}
}
inline void S9xSetByte (uint8 Byte, uint32 Address)
{
int block = (Address & 0xffffff) >> MEMMAP_SHIFT;
uint8 *SetAddress = Memory.WriteMap[block];
int32 speed = memory_speed(Address);
if (SetAddress >= (uint8 *) CMemory::MAP_LAST)
{
*(SetAddress + (Address & 0xffff)) = Byte;
addCyclesInMemoryAccess;
return;
}
switch ((pint) SetAddress)
{
case CMemory::MAP_CPU:
S9xSetCPU(Byte, Address & 0xffff);
addCyclesInMemoryAccess;
return;
case CMemory::MAP_PPU:
if (CPU.InDMAorHDMA && (Address & 0xff00) == 0x2100)
return;
S9xSetPPU(Byte, Address & 0xffff);
addCyclesInMemoryAccess;
return;
case CMemory::MAP_LOROM_SRAM:
if (Memory.SRAMMask)
{
*(Memory.SRAM + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Memory.SRAMMask)) = Byte;
CPU.SRAMModified = TRUE;
}
addCyclesInMemoryAccess;
return;
case CMemory::MAP_LOROM_SRAM_B:
if (Multi.sramMaskB)
{
*(Multi.sramB + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Multi.sramMaskB)) = Byte;
CPU.SRAMModified = TRUE;
}
addCyclesInMemoryAccess;
return;
case CMemory::MAP_HIROM_SRAM:
if (Memory.SRAMMask)
{
*(Memory.SRAM + (((Address & 0x7fff) - 0x6000 + ((Address & 0x1f0000) >> 3)) & Memory.SRAMMask)) = Byte;
CPU.SRAMModified = TRUE;
}
addCyclesInMemoryAccess;
return;
case CMemory::MAP_BWRAM:
*(Memory.BWRAM + ((Address & 0x7fff) - 0x6000)) = Byte;
CPU.SRAMModified = TRUE;
addCyclesInMemoryAccess;
return;
case CMemory::MAP_SA1RAM:
*(Memory.SRAM + (Address & 0xffff)) = Byte;
addCyclesInMemoryAccess;
return;
case CMemory::MAP_DSP:
S9xSetDSP(Byte, Address & 0xffff);
addCyclesInMemoryAccess;
return;
case CMemory::MAP_C4:
S9xSetC4(Byte, Address & 0xffff);
addCyclesInMemoryAccess;
return;
case CMemory::MAP_OBC_RAM:
S9xSetOBC1(Byte, Address & 0xffff);
addCyclesInMemoryAccess;
return;
case CMemory::MAP_SETA_DSP:
S9xSetSetaDSP(Byte, Address);
addCyclesInMemoryAccess;
return;
case CMemory::MAP_SETA_RISC:
S9xSetST018(Byte, Address);
addCyclesInMemoryAccess;
return;
case CMemory::MAP_BSX:
S9xSetBSX(Byte, Address);
addCyclesInMemoryAccess;
return;
case CMemory::MAP_NONE:
default:
addCyclesInMemoryAccess;
return;
}
}
inline void S9xSetWord (uint16 Word, uint32 Address, enum s9xwrap_t w = WRAP_NONE, enum s9xwriteorder_t o = WRITE_01)
{
uint32 mask = MEMMAP_MASK & (w == WRAP_PAGE ? 0xff : (w == WRAP_BANK ? 0xffff : 0xffffff));
if ((Address & mask) == mask)
{
PC_t a;
if (!o)
S9xSetByte((uint8) Word, Address);
switch (w)
{
case WRAP_PAGE:
a.xPBPC = Address;
a.B.xPCl++;
S9xSetByte(Word >> 8, a.xPBPC);
break;
case WRAP_BANK:
a.xPBPC = Address;
a.W.xPC++;
S9xSetByte(Word >> 8, a.xPBPC);
break;
case WRAP_NONE:
default:
S9xSetByte(Word >> 8, Address + 1);
break;
}
if (o)
S9xSetByte((uint8) Word, Address);
return;
}
int block = (Address & 0xffffff) >> MEMMAP_SHIFT;
uint8 *SetAddress = Memory.WriteMap[block];
int32 speed = memory_speed(Address);
if (SetAddress >= (uint8 *) CMemory::MAP_LAST)
{
WRITE_WORD(SetAddress + (Address & 0xffff), Word);
addCyclesInMemoryAccess_x2;
return;
}
switch ((pint) SetAddress)
{
case CMemory::MAP_CPU:
if (o)
{
S9xSetCPU(Word >> 8, (Address + 1) & 0xffff);
addCyclesInMemoryAccess;
S9xSetCPU((uint8) Word, Address & 0xffff);
addCyclesInMemoryAccess;
return;
}
else
{
S9xSetCPU((uint8) Word, Address & 0xffff);
addCyclesInMemoryAccess;
S9xSetCPU(Word >> 8, (Address + 1) & 0xffff);
addCyclesInMemoryAccess;
return;
}
case CMemory::MAP_PPU:
if (CPU.InDMAorHDMA)
{
if ((Address & 0xff00) != 0x2100)
S9xSetPPU((uint8) Word, Address & 0xffff);
if (((Address + 1) & 0xff00) != 0x2100)
S9xSetPPU(Word >> 8, (Address + 1) & 0xffff);
return;
}
if (o)
{
S9xSetPPU(Word >> 8, (Address + 1) & 0xffff);
addCyclesInMemoryAccess;
S9xSetPPU((uint8) Word, Address & 0xffff);
addCyclesInMemoryAccess;
return;
}
else
{
S9xSetPPU((uint8) Word, Address & 0xffff);
addCyclesInMemoryAccess;
S9xSetPPU(Word >> 8, (Address + 1) & 0xffff);
addCyclesInMemoryAccess;
return;
}
case CMemory::MAP_LOROM_SRAM:
if (Memory.SRAMMask)
{
if (Memory.SRAMMask >= MEMMAP_MASK)
WRITE_WORD(Memory.SRAM + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Memory.SRAMMask), Word);
else
{
*(Memory.SRAM + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Memory.SRAMMask)) = (uint8) Word;
*(Memory.SRAM + (((((Address + 1) & 0xff0000) >> 1) | ((Address + 1) & 0x7fff)) & Memory.SRAMMask)) = Word >> 8;
}
CPU.SRAMModified = TRUE;
}
addCyclesInMemoryAccess_x2;
return;
case CMemory::MAP_LOROM_SRAM_B:
if (Multi.sramMaskB)
{
if (Multi.sramMaskB >= MEMMAP_MASK)
WRITE_WORD(Multi.sramB + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Multi.sramMaskB), Word);
else
{
*(Multi.sramB + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Multi.sramMaskB)) = (uint8) Word;
*(Multi.sramB + (((((Address + 1) & 0xff0000) >> 1) | ((Address + 1) & 0x7fff)) & Multi.sramMaskB)) = Word >> 8;
}
CPU.SRAMModified = TRUE;
}
addCyclesInMemoryAccess_x2;
return;
case CMemory::MAP_HIROM_SRAM:
if (Memory.SRAMMask)
{
if (Memory.SRAMMask >= MEMMAP_MASK)
WRITE_WORD(Memory.SRAM + (((Address & 0x7fff) - 0x6000 + ((Address & 0x1f0000) >> 3)) & Memory.SRAMMask), Word);
else
{
*(Memory.SRAM + (((Address & 0x7fff) - 0x6000 + ((Address & 0x1f0000) >> 3)) & Memory.SRAMMask)) = (uint8) Word;
*(Memory.SRAM + ((((Address + 1) & 0x7fff) - 0x6000 + (((Address + 1) & 0x1f0000) >> 3)) & Memory.SRAMMask)) = Word >> 8;
}
CPU.SRAMModified = TRUE;
}
addCyclesInMemoryAccess_x2;
return;
case CMemory::MAP_BWRAM:
WRITE_WORD(Memory.BWRAM + ((Address & 0x7fff) - 0x6000), Word);
CPU.SRAMModified = TRUE;
addCyclesInMemoryAccess_x2;
return;
case CMemory::MAP_SA1RAM:
WRITE_WORD(Memory.SRAM + (Address & 0xffff), Word);
addCyclesInMemoryAccess_x2;
return;
case CMemory::MAP_DSP:
if (o)
{
S9xSetDSP(Word >> 8, (Address + 1) & 0xffff);
addCyclesInMemoryAccess;
S9xSetDSP((uint8) Word, Address & 0xffff);
addCyclesInMemoryAccess;
return;
}
else
{
S9xSetDSP((uint8) Word, Address & 0xffff);
addCyclesInMemoryAccess;
S9xSetDSP(Word >> 8, (Address + 1) & 0xffff);
addCyclesInMemoryAccess;
return;
}
case CMemory::MAP_C4:
if (o)
{
S9xSetC4(Word >> 8, (Address + 1) & 0xffff);
addCyclesInMemoryAccess;
S9xSetC4((uint8) Word, Address & 0xffff);
addCyclesInMemoryAccess;
return;
}
else
{
S9xSetC4((uint8) Word, Address & 0xffff);
addCyclesInMemoryAccess;
S9xSetC4(Word >> 8, (Address + 1) & 0xffff);
addCyclesInMemoryAccess;
return;
}
case CMemory::MAP_OBC_RAM:
if (o)
{
S9xSetOBC1(Word >> 8, (Address + 1) & 0xffff);
addCyclesInMemoryAccess;
S9xSetOBC1((uint8) Word, Address & 0xffff);
addCyclesInMemoryAccess;
return;
}
else
{
S9xSetOBC1((uint8) Word, Address & 0xffff);
addCyclesInMemoryAccess;
S9xSetOBC1(Word >> 8, (Address + 1) & 0xffff);
addCyclesInMemoryAccess;
return;
}
case CMemory::MAP_SETA_DSP:
if (o)
{
S9xSetSetaDSP(Word >> 8, Address + 1);
addCyclesInMemoryAccess;
S9xSetSetaDSP((uint8) Word, Address);
addCyclesInMemoryAccess;
return;
}
else
{
S9xSetSetaDSP((uint8) Word, Address);
addCyclesInMemoryAccess;
S9xSetSetaDSP(Word >> 8, Address + 1);
addCyclesInMemoryAccess;
return;
}
case CMemory::MAP_SETA_RISC:
if (o)
{
S9xSetST018(Word >> 8, Address + 1);
addCyclesInMemoryAccess;
S9xSetST018((uint8) Word, Address);
addCyclesInMemoryAccess;
return;
}
else
{
S9xSetST018((uint8) Word, Address);
addCyclesInMemoryAccess;
S9xSetST018(Word >> 8, Address + 1);
addCyclesInMemoryAccess;
return;
}
case CMemory::MAP_BSX:
if (o)
{
S9xSetBSX(Word >> 8, Address + 1);
addCyclesInMemoryAccess;
S9xSetBSX((uint8) Word, Address);
addCyclesInMemoryAccess;
return;
}
else
{
S9xSetBSX((uint8) Word, Address);
addCyclesInMemoryAccess;
S9xSetBSX(Word >> 8, Address + 1);
addCyclesInMemoryAccess;
return;
}
case CMemory::MAP_NONE:
default:
addCyclesInMemoryAccess_x2;
return;
}
}
inline void S9xSetPCBase (uint32 Address)
{
Registers.PBPC = Address & 0xffffff;
ICPU.ShiftedPB = Address & 0xff0000;
uint8 *GetAddress = Memory.Map[(int)((Address & 0xffffff) >> MEMMAP_SHIFT)];
CPU.MemSpeed = memory_speed(Address);
CPU.MemSpeedx2 = CPU.MemSpeed << 1;
if (GetAddress >= (uint8 *) CMemory::MAP_LAST)
{
CPU.PCBase = GetAddress;
return;
}
switch ((pint) GetAddress)
{
case CMemory::MAP_LOROM_SRAM:
if ((Memory.SRAMMask & MEMMAP_MASK) != MEMMAP_MASK)
CPU.PCBase = NULL;
else
CPU.PCBase = Memory.SRAM + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Memory.SRAMMask) - (Address & 0xffff);
return;
case CMemory::MAP_LOROM_SRAM_B:
if ((Multi.sramMaskB & MEMMAP_MASK) != MEMMAP_MASK)
CPU.PCBase = NULL;
else
CPU.PCBase = Multi.sramB + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Multi.sramMaskB) - (Address & 0xffff);
return;
case CMemory::MAP_HIROM_SRAM:
if ((Memory.SRAMMask & MEMMAP_MASK) != MEMMAP_MASK)
CPU.PCBase = NULL;
else
CPU.PCBase = Memory.SRAM + (((Address & 0x7fff) - 0x6000 + ((Address & 0x1f0000) >> 3)) & Memory.SRAMMask) - (Address & 0xffff);
return;
case CMemory::MAP_BWRAM:
CPU.PCBase = Memory.BWRAM - 0x6000 - (Address & 0x8000);
return;
case CMemory::MAP_SA1RAM:
CPU.PCBase = Memory.SRAM;
return;
case CMemory::MAP_SPC7110_ROM:
CPU.PCBase = S9xGetBasePointerSPC7110(Address);
return;
case CMemory::MAP_C4:
CPU.PCBase = S9xGetBasePointerC4(Address & 0xffff);
return;
case CMemory::MAP_OBC_RAM:
CPU.PCBase = S9xGetBasePointerOBC1(Address & 0xffff);
return;
case CMemory::MAP_BSX:
CPU.PCBase = S9xGetBasePointerBSX(Address);
return;
case CMemory::MAP_NONE:
default:
CPU.PCBase = NULL;
return;
}
}
inline uint8 * S9xGetBasePointer (uint32 Address)
{
uint8 *GetAddress = Memory.Map[(Address & 0xffffff) >> MEMMAP_SHIFT];
if (GetAddress >= (uint8 *) CMemory::MAP_LAST)
return (GetAddress);
switch ((pint) GetAddress)
{
case CMemory::MAP_LOROM_SRAM:
if ((Memory.SRAMMask & MEMMAP_MASK) != MEMMAP_MASK)
return (NULL);
return (Memory.SRAM + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Memory.SRAMMask) - (Address & 0xffff));
case CMemory::MAP_LOROM_SRAM_B:
if ((Multi.sramMaskB & MEMMAP_MASK) != MEMMAP_MASK)
return (NULL);
return (Multi.sramB + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Multi.sramMaskB) - (Address & 0xffff));
case CMemory::MAP_HIROM_SRAM:
if ((Memory.SRAMMask & MEMMAP_MASK) != MEMMAP_MASK)
return (NULL);
return (Memory.SRAM + (((Address & 0x7fff) - 0x6000 + ((Address & 0x1f0000) >> 3)) & Memory.SRAMMask) - (Address & 0xffff));
case CMemory::MAP_BWRAM:
return (Memory.BWRAM - 0x6000 - (Address & 0x8000));
case CMemory::MAP_SA1RAM:
return (Memory.SRAM);
case CMemory::MAP_SPC7110_ROM:
return (S9xGetBasePointerSPC7110(Address));
case CMemory::MAP_C4:
return (S9xGetBasePointerC4(Address & 0xffff));
case CMemory::MAP_OBC_RAM:
return (S9xGetBasePointerOBC1(Address & 0xffff));
case CMemory::MAP_NONE:
default:
return (NULL);
}
}
inline uint8 * S9xGetMemPointer (uint32 Address)
{
uint8 *GetAddress = Memory.Map[(Address & 0xffffff) >> MEMMAP_SHIFT];
if (GetAddress >= (uint8 *) CMemory::MAP_LAST)
return (GetAddress + (Address & 0xffff));
switch ((pint) GetAddress)
{
case CMemory::MAP_LOROM_SRAM:
if ((Memory.SRAMMask & MEMMAP_MASK) != MEMMAP_MASK)
return (NULL);
return (Memory.SRAM + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Memory.SRAMMask));
case CMemory::MAP_LOROM_SRAM_B:
if ((Multi.sramMaskB & MEMMAP_MASK) != MEMMAP_MASK)
return (NULL);
return (Multi.sramB + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Multi.sramMaskB));
case CMemory::MAP_HIROM_SRAM:
if ((Memory.SRAMMask & MEMMAP_MASK) != MEMMAP_MASK)
return (NULL);
return (Memory.SRAM + (((Address & 0x7fff) - 0x6000 + ((Address & 0x1f0000) >> 3)) & Memory.SRAMMask));
case CMemory::MAP_BWRAM:
return (Memory.BWRAM - 0x6000 + (Address & 0x7fff));
case CMemory::MAP_SA1RAM:
return (Memory.SRAM + (Address & 0xffff));
case CMemory::MAP_SPC7110_ROM:
return (S9xGetBasePointerSPC7110(Address) + (Address & 0xffff));
case CMemory::MAP_C4:
return (S9xGetMemPointerC4(Address & 0xffff));
case CMemory::MAP_OBC_RAM:
return (S9xGetMemPointerOBC1(Address & 0xffff));
case CMemory::MAP_NONE:
default:
return (NULL);
}
}
#endif

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save