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,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 “better C” 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("Joypad1 A"), 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>
|
||||
MyReportButttons();<br>
|
||||
S9xMainLoop();<br>
|
||||
}</code>
|
||||
</p>
|
||||
<p>
|
||||
<code>void MyReportButtons (void)<br>
|
||||
{<br>
|
||||
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 “switch-on” except the S-RAM area is preserved (“hardware reset”). 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 “software reset” 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 “q” 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(&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,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,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…
Reference in new issue