Showing preview only (1,040K chars total). Download the full file or copy to clipboard to get everything.
Repository: 100thCoin/TriCNES
Branch: main
Commit: f4551b3ed1d2
Files: 47
Total size: 1007.7 KB
Directory structure:
gitextract_r4m8qsp8/
├── .gitattributes
├── .gitignore
├── 6502Documentation.cs
├── App.config
├── Emulator.cs
├── LICENSE
├── Program.cs
├── Properties/
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── README.md
├── TriCNES.csproj
├── TriCNES.sln
├── forms/
│ ├── TASProperties.Designer.cs
│ ├── TASProperties.cs
│ ├── TASProperties.resx
│ ├── TASProperties3ct.Designer.cs
│ ├── TASProperties3ct.cs
│ ├── TASProperties3ct.resx
│ ├── TriCHexEditor.Designer.cs
│ ├── TriCHexEditor.cs
│ ├── TriCHexEditor.resx
│ ├── TriCNESGUI.Designer.cs
│ ├── TriCNESGUI.cs
│ ├── TriCNESGUI.resx
│ ├── TriCNTViewer.Designer.cs
│ ├── TriCNTViewer.cs
│ ├── TriCNTViewer.resx
│ ├── TriCTASTimeline.Designer.cs
│ ├── TriCTASTimeline.cs
│ ├── TriCTASTimeline.resx
│ ├── TriCTraceLogger.Designer.cs
│ ├── TriCTraceLogger.cs
│ └── TriCTraceLogger.resx
├── mappers/
│ ├── Mapper_AOROM.cs
│ ├── Mapper_CNROM.cs
│ ├── Mapper_FDS.cs
│ ├── Mapper_FME7.cs
│ ├── Mapper_MMC1.cs
│ ├── Mapper_MMC2.cs
│ ├── Mapper_MMC3.cs
│ ├── Mapper_NROM.cs
│ ├── Mapper_NULL.cs
│ └── Mapper_UxROM.cs
└── packages.config
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
# Auto detect text files and perform LF normalization
* text=auto
================================================
FILE: .gitignore
================================================
## 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/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
*.env
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
[Dd]ebug/x64/
[Dd]ebugPublic/x64/
[Rr]elease/x64/
[Rr]eleases/x64/
bin/x64/
obj/x64/
[Dd]ebug/x86/
[Dd]ebugPublic/x86/
[Rr]elease/x86/
[Rr]eleases/x86/
bin/x86/
obj/x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
[Aa][Rr][Mm]64[Ee][Cc]/
bld/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/
# Build results on 'Bin' directories
**/[Bb]in/*
# Uncomment if you have tasks that rely on *.refresh files to move binaries
# (https://github.com/github/gitignore/pull/3736)
#!**/[Bb]in/*.refresh
# 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.*
*.trx
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Approval Tests result files
*.received.*
# 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/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.idb
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
# but not Directory.Build.rsp, as it configures directory-level build defaults
!Directory.Build.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.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
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_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 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# 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
MSBuild_Logs/
# AWS SAM Build and Temporary Artifacts folder
.aws-sam
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
**/.mfractor/
# Local History for Visual Studio
**/.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
**/.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
*.nes
*.fm3
*.fm2
*.fmv
*.3ct
*.bk2
bin/Debug/TriCNES.exe
bin/Debug/TriCNES.exe.config
bin/Debug/TriCNES.pdb
bin/Release/TriCNES.exe
bin/Release/TriCNES.exe.config
bin/Release/TriCNES.pdb
.vs/TriCNES/FileContentIndex/a1f39354-40ca-44d4-a1f4-22570f4e355b.vsidx
.vs/TriCNES/FileContentIndex/4bef6b04-ae46-46c1-a5d2-a4ddde2871fe.vsidx
*.vsidx
*.vsidx
*.cache
/obj
*.vsidx
*.vsidx
*.tasproj
/bin/Release/roms
/bin
================================================
FILE: 6502Documentation.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TriCNES
{
public class Op
{
public byte code;
public string mnemonic;
public string mode;
public int length;
public int affectedFlags;
public string CycleByCycle;
public string InstructionDocumentation;
public Op(byte c, string m, string mo, int l, int a, string d, string i)
{
code = c;
mnemonic = m;
mode = mo;
length = l;
affectedFlags = a;
CycleByCycle = d;
InstructionDocumentation = i;
}
}
public static class Documentation
{
// This class is exlusively referenced for debugging and trace logging information.
// It's also possible I mistyped some numbers here and there, and probably shouldn't be 100% trusted.
//cycle information from https://www.atarihq.com/danb/files/64doc.txt
static int cFlag = 1;
static int zFlag = 2;
static int iFlag = 4;
static int dFlag = 8;
static int vFlag = 64;
static int nFlag = 128;
static int aChanges = 256;
static int xChanges = 512;
static int yChanges = 1024;
static int stackPChanges = 2048;
static int pcChanges = 4096;
static int memChanges = 8192;
static string[] CycleDocs =
{
//0 BRK
" # address R/W description\r\n --- ------- --- -----------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R read next instruction byte (and throw it away), increment PC\r\n 3 $0100,S W push PCH on stack, decrement S\r\n 4 $0100,S W push PCL on stack, decrement S\r\n 5 $0100,S W push P on stack (with B flag set), decrement S\r\n 6 $FFFE R fetch PCL\r\n 7 $FFFF R fetch PCH"
,
//1 RTI
" # address R/W description\r\n --- ------- --- -----------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R read next instruction byte (and throw it away)\r\n 3 $0100,S R increment S\r\n 4 $0100,S R pull P from stack, increment S\r\n 5 $0100,S R pull PCL from stack, increment S\r\n 6 $0100,S R pull PCH from stack"
,
//2 RTS
" # address R/W description\r\n --- ------- --- -----------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R read next instruction byte (and throw it away)\r\n 3 $0100,S R increment S\r\n 4 $0100,S R pull PCL from stack, increment S\r\n 5 $0100,S R pull PCH from stack\r\n 6 PC R increment PC"
,
//3 PHA, PHP
" # address R/W description\r\n --- ------- --- -----------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R read next instruction byte (and throw it away)\r\n 3 $0100,S W push register on stack, decrement S"
,
//4 PLA, PLP
" # address R/W description\r\n --- ------- --- -----------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R read next instruction byte (and throw it away)\r\n 3 $0100,S R increment S\r\n 4 $0100,S R pull register from stack"
,
//5 JSR
" # address R/W description\r\n --- ------- --- -------------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch low address byte, increment PC\r\n 3 $0100,S R internal operation (predecrement S?)\r\n 4 $0100,S W push PCH on stack, decrement S\r\n 5 $0100,S W push PCL on stack, decrement S\r\n 6 PC R copy low address byte to PCL, fetch high address byte to PCH"
,
//6 Accumulator or implied addressing
" Accumulator or implied addressing\r\n\r\n # address R/W description\r\n --- ------- --- -----------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R read next instruction byte (and throw it away)"
,
//7 Immediate addressing
" # address R/W description\r\n --- ------- --- ------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch value, increment PC"
,
// --Absolute Instructions--
//8 JMP
" # address R/W description\r\n --- ------- --- -------------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch low address byte, increment PC\r\n 3 PC R copy low address byte to PCL, fetch high address byte to PCH"
,
//9 Read instructions (LDA, LDX, LDY, EOR, AND, ORA, ADC, SBC, CMP, BIT, LAX, NOP)
" # address R/W description\r\n --- ------- --- ------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch low byte of address, increment PC\r\n 3 PC R fetch high byte of address, increment PC\r\n 4 address R read from effective address"
,
//10 Read-Modify-Write instructions (ASL, LSR, ROL, ROR, INC, DEC, SLO, SRE, RLA, RRA, ISC, DCP)
" # address R/W description\r\n --- ------- --- ------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch low byte of address, increment PC\r\n 3 PC R fetch high byte of address, increment PC\r\n 4 address R read from effective address\r\n 5 address W write the value back to effective address, and do the operation on it\r\n 6 address W write the new value to effective address"
,
//11 Write instructions (STA, STX, STY, SAX)
" # address R/W description\r\n --- ------- --- ------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch low byte of address, increment PC\r\n 3 PC R fetch high byte of address, increment PC\r\n 4 address W write register to effective address"
,
// --Zero page addressing--
//12 Read instructions (LDA, LDX, LDY, EOR, AND, ORA, ADC, SBC, CMP, BIT, LAX, NOP)
" # address R/W description\r\n --- ------- --- ------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch address, increment PC\r\n 3 address R read from effective address"
,
//13 Read-Modify-Write instructions (ASL, LSR, ROL, ROR, INC, DEC, SLO, SRE, RLA, RRA, ISC, DCP)
" # address R/W description\r\n --- ------- --- ------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch address, increment PC\r\n 3 address R read from effective address\r\n 4 address W write the value back to effective address, and do the operation on it\r\n 5 address W write the new value to effective address"
,
//14 Write instructions (STA, STX, STY, SAX)
" # address R/W description\r\n --- ------- --- ------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch address, increment PC\r\n 3 address W write register to effective address"
,
// --Zero page indexed addressing--
//15 Read instructions (LDA, LDX, LDY, EOR, AND, ORA, ADC, SBC, CMP, BIT, LAX, NOP)
" # address R/W description\r\n --- --------- --- ------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch address, increment PC\r\n 3 address R read from address, add index register to it\r\n 4 address+I* R read from effective address\r\n\r\n Notes: I denotes either index register (X or Y).\r\n\r\n * The high byte of the effective address is always zero,\r\n i.e. page boundary crossings are not handled."
,
//16 Read-Modify-Write instructions (ASL, LSR, ROL, ROR, INC, DEC, SLO, SRE, RLA, RRA, ISC, DCP)
" # address R/W description\r\n --- --------- --- ---------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch address, increment PC\r\n 3 address R read from address, add index register X to it\r\n 4 address+X* R read from effective address\r\n 5 address+X* W write the value back to effective address, and do the operation on it\r\n 6 address+X* W write the new value to effective address\r\n\r\n Note: * The high byte of the effective address is always zero,\r\n i.e. page boundary crossings are not handled."
,
//17 Write instructions (STA, STX, STY, SAX)
" # address R/W description\r\n --- --------- --- -------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch address, increment PC\r\n 3 address R read from address, add index register to it\r\n 4 address+I* W write to effective address\r\n\r\n Notes: I denotes either index register (X or Y).\r\n\r\n * The high byte of the effective address is always zero,\r\n i.e. page boundary crossings are not handled."
,
// --Absolute indexed addressing--
//18 Read instructions (LDA, LDX, LDY, EOR, AND, ORA, ADC, SBC, CMP, BIT, LAX, LAE, SHS, NOP)
" # address R/W description\r\n --- --------- --- ------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch low byte of address, increment PC\r\n 3 PC R fetch high byte of address, add index register to low address byte, increment PC\r\n 4 address+I* R read from effective address, fix the high byte of effective address\r\n 5+ address+I R re-read from effective address\r\n\r\n Notes: I denotes either index register (X or Y).\r\n\r\n * The high byte of the effective address may be invalid\r\n at this time, i.e. it may be smaller by $100.\r\n\r\n + This cycle will be executed only if the effective address\r\n was invalid during cycle #4, i.e. page boundary was crossed."
,
//19 Read-Modify-Write instructions (ASL, LSR, ROL, ROR, INC, DEC, SLO, SRE, RLA, RRA, ISC, DCP)
" # address R/W description\r\n --- --------- --- ------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch low byte of address, increment PC\r\n 3 PC R fetch high byte of address, add index register X to low address byte, increment PC\r\n 4 address+X* R read from effective address, fix the high byte of effective address\r\n 5 address+X R re-read from effective address\r\n 6 address+X W write the value back to effective address, and do the operation on it\r\n 7 address+X W write the new value to effective address\r\n\r\n Notes: * The high byte of the effective address may be invalid\r\n at this time, i.e. it may be smaller by $100."
,
//20 Write instructions (STA, STX, STY, SHA, SHX, SHY)
" # address R/W description\r\n --- --------- --- ------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch low byte of address, increment PC\r\n 3 PC R fetch high byte of address, add index register to low address byte, increment PC\r\n 4 address+I* R read from effective address, fix the high byte of effective address\r\n 5 address+I W write to effective address\r\n\r\n Notes: I denotes either index register (X or Y).\r\n\r\n * The high byte of the effective address may be invalid\r\n at this time, i.e. it may be smaller by $100. Because\r\n the processor cannot undo a write to an invalid\r\n address, it always reads from the address first."
,
//21 Relative addressing (BCC, BCS, BNE, BEQ, BPL, BMI, BVC, BVS)
" # address R/W description\r\n --- --------- --- ---------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch operand, increment PC. If branch is not taken, the instruction has ended.\r\n 3+ PC R If branch is taken, add operand to PCL.\r\n 4! PC* R Fix PCH.\r\n Notes: * The high byte of Program Counter (PCH) may be invalid\r\n at this time, i.e. it may be smaller or bigger by $100.\r\n\r\n + If branch is taken, this cycle will be executed.\r\n\r\n ! If branch occurs to different page, this cycle will be\r\n executed."
,
// --Indexed indirect addressing--
//22 Read instructions (LDA, ORA, EOR, AND, ADC, CMP, SBC, LAX)
" # address R/W description\r\n --- ----------- --- ------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch pointer address, increment PC\r\n 3 pointer R read from the address, add X to it\r\n 4 pointer+X R fetch effective address low\r\n 5 pointer+X+1 R fetch effective address high\r\n 6 address R read from effective address\r\n\r\n Note: The effective address is always fetched from zero page,\r\n i.e. the zero page boundary crossing is not handled."
,
//23 Read-Modify-Write instructions (SLO, SRE, RLA, RRA, ISC, DCP)
" # address R/W description\r\n --- ----------- --- ------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch pointer address, increment PC\r\n 3 pointer R read from the address, add X to it\r\n 4 pointer+X R fetch effective address low\r\n 5 pointer+X+1 R fetch effective address high\r\n 6 address R read from effective address\r\n 7 address W write the value back to effective address, and do the operation on it\r\n 8 address W write the new value to effective address\r\n\r\n Note: The effective address is always fetched from zero page,\r\n i.e. the zero page boundary crossing is not handled."
,
//24 Write instructions (STA, SAX)
" # address R/W description\r\n --- ----------- --- ------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch pointer address, increment PC\r\n 3 pointer R read from the address, add X to it\r\n 4 pointer+X R fetch effective address low\r\n 5 pointer+X+1 R fetch effective address high\r\n 6 address W write to effective address\r\n\r\n Note: The effective address is always fetched from zero page,\r\n i.e. the zero page boundary crossing is not handled."
,
// --Indirect indexed addressing--
//25 Read instructions (LDA, EOR, AND, ORA, ADC, SBC, CMP)
" # address R/W description\r\n --- ----------- --- ------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch pointer address, increment PC\r\n 3 pointer R fetch effective address low\r\n 4 pointer+1 R fetch effective address high, add Y to low byte of effective address\r\n 5 address+Y* R read from effective address, fix high byte of effective address\r\n 6+ address+Y R read from effective address\r\n\r\n Notes: The effective address is always fetched from zero page,\r\n i.e. the zero page boundary crossing is not handled.\r\n\r\n * The high byte of the effective address may be invalid\r\n at this time, i.e. it may be smaller by $100.\r\n\r\n + This cycle will be executed only if the effective address\r\n was invalid during cycle #5, i.e. page boundary was crossed."
,
//26 Read-Modify-Write instructions (SLO, SRE, RLA, RRA, ISC, DCP)
" # address R/W description\r\n --- ----------- --- ------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch pointer address, increment PC\r\n 3 pointer R fetch effective address low\r\n 4 pointer+1 R fetch effective address high, add Y to low byte of effective address\r\n 5 address+Y* R read from effective address, fix high byte of effective address\r\n 6 address+Y R read from effective address\r\n 7 address+Y W write the value back to effective address, and do the operation on it\r\n 8 address+Y W write the new value to effective address\r\n\r\n Notes: The effective address is always fetched from zero page,\r\n i.e. the zero page boundary crossing is not handled.\r\n\r\n * The high byte of the effective address may be invalid\r\n at this time, i.e. it may be smaller by $100."
,
//27 Write instructions (STA, SHA)
" # address R/W description\r\n --- ----------- --- ------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch pointer address, increment PC\r\n 3 pointer R fetch effective address low\r\n 4 pointer+1 R fetch effective address high, add Y to low byte of effective address\r\n 5 address+Y* R read from effective address, fix high byte of effective address\r\n 6 address+Y W write to effective address\r\n\r\n Notes: The effective address is always fetched from zero page,\r\n i.e. the zero page boundary crossing is not handled.\r\n\r\n * The high byte of the effective address may be invalid\r\n at this time, i.e. it may be smaller by $100."
,
// --Absolute indirect addressing--
//28 JMP
" # address R/W description\r\n --- --------- --- ------------------------------------------\r\n 1 PC R fetch opcode, increment PC\r\n 2 PC R fetch pointer address low, increment PC\r\n 3 PC R fetch pointer address high, increment PC\r\n 4 pointer R fetch low address to latch\r\n 5 pointer+1* R fetch PCH, copy latch to PCL\r\n\r\n Note: * The PCH will always be fetched from the same page\r\n than PCL, i.e. page boundary crossing is not handled.\r\n\r\n How Real Programmers Acknowledge Interrupts"
,
//29
//HLT
" # address R/W description\r\n --- --------- --- ------------------------------------------\r\n 1 PC R fetch opcode, does not increment PC\r\n 2 PC R fetch opcode, does not increment PC\r\n 3 PC R fetch opcode, does not increment PC\r\n 4 PC R fetch opcode, does not increment PC\r\n 5 PC R fetch opcode, does not increment PC\r\n 6 PC R fetch opcode, does not increment PC\r\n 7 PC R fetch opcode, does not increment PC\r\n ... PC R fetch opcode, does not increment PC\r\n\r\n Notes: This process goes on forever."
};
// this explains what each isntruction does by their pnuemonic
static string[] InstructionDocs =
{
//0 BRK
"Break\n\nPushes PC to the stack.\n\nPushes processor status to the stack.\nPC' = ($FFFE)\nSP' = SP-3"
,
//1 ORA
"Bitwise OR with Accumulator\n\nA' = A|M\n\nZFlag' = (A'==0)\nNFlag' = (A'>=0x80)"
,
//2 HLT
"Halt\n\nHalts the processor."
,
//3 NOP
"No operation."
,
//4 ASL
"Arithmetic Shift Left\n\nM' = M<<1\n\nCflag' = (M>=0x80)\nZflag' = (M'==0)\nNflag' = (M'>=0x80)"
,
//5 SLO
"Arithemtic Shift Left then Bitwise OR with Accumulator\n\nM' = M<<1\nA' = A|M'\n\nCflag' = (M>=0x80)\nZflag' = (A'==0)\nNflag' = (A'>=0x80)"
,
//6 PHP
"Push Processor\n\nPushes processor status to the stack.\n\nSP' = SP-1"
,
//7 ANC
"Bitwise AND with Accumulator then Set Carry if Negative\n\nA' = A & M\n\nCflag' = (A'>=0x80)\nZflag' = (A'==0)\nNflag' = (A'>=0x80)"
,
//8 BPL
"Branch on Plus\n\nIf the negative flag is not set, branch.\n\nPC' = PC + (!NFlag ? SignedOperand : 0)"
,
//9 CLC
"Clear Carry Flag\n\nCFlag' = false"
,
//10 JSR
"Jump to Subroutine\n\nPushes PC to the stack\nPC' = Operand\nSP' = SP-2"
,
//11 AND
"Bitwise AND with Accumulator\n\nA' = A&M\n\nZFlag = (A'==0)\nNFlag' = (A'>=0x80)"
,
//12 RLA
"Rotate Left then Bitwise AND with Accumulator\n\nM' = M<<1 + CFlag\nA' = A&M'\n\nCflag' = (M>=0x80)\nZflag' = (A'==0)\nNflag' = (A'>=0x80)"
,
//13 BIT
"Bit Test\n\nZFlag = (A=M)\nNFlag = ((M>>7)&1==1)\nVFlag = ((M>>6)&1==1)"
,
//14 ROL
"Rotate Left\n\nM' = M<<1 + CFlag\n\nCflag' = (M>=0x80)\nZflag' = (M'==0)\nNflag' = (M'>=0x80)"
,
//15 PLP
"Pull Processor\n\nPulls processor status from the stack.\n\nSP' = SP+1"
,
//16 BMI
"Branch on Minus\n\nIf the negative flag is set, branch.\n\nPC' = PC + (NFlag ? SignedOperand : 0)"
,
//17 SEC
"Set Carry Flag\n\nCFlag' = true"
,
//18 RTI
"Return from Interrupt\n\nPulls the processor from the stack.\nPulls PC from the stack.\n\nSP' = SP+3"
,
//19 EOR
"Bitwise Exclusive OR with Accumulator\n\nA' = A^M\n\nZFlag' = (A'==0)\nNFlag' = (A'>=0x80)"
,
//20 SRE
"Logical Shift Right then Bitwise Exclusive OR with Accumulator\n\nM' = M>>2\nA' = A^M'\n\nCFlag' = (M&1==1)\nZflag' = (A'==0)\nNflag' = (A'>=0x80)"
,
//21 LSR
"Logical Shift Right\n\nM' = M>>2\n\nCFlag' = (M&1==1)\nZflag' = (M'==0)\nNflag' = (M'>=0x80)"
,
//22 PHA
"Push A\n\nPushes A to the stack.\n\nSP' = SP-1"
,
//23 ASR
"Bitwise AND with Accumulator then Logical Shift Right Accumulator\n\nA' = ((A&M)>>1)\nCFlag' = ((A&M)&1==1)\nZflag' = (A'==0)\nNflag' = (A'>=0x80)"
,
//24 JMP
"Jump\n\nPC' = M"
,
//25 BVC
"Branch on Overflow Clear\n\nIf the overflow flag is not set, branch.\n\nPC' = PC + (!VFlag ? SignedOperand : 0)"
,
//26 CLI
"Clear Interrupt Disable Flag\n\nIFlag' = false"
,
//27 RTS
"Return from Subroutine\n\nPulls the PC from the stack.\\SP' = SP + 2"
,
//28 ADC
"Add with Carry\n\nA' = A + M + CFlag\n\nVFlag' = ((A ^ (M + A + Carry)) & ((M + A + Carry) & M) & 0x80) == 0x80\nCFlag' = A + M > 0xFF\nZflag' = (A'==0)\nNflag' = (A'>=0x80)"
,
//29 RRA
"Rotate Right then Add With Carry\n\nM' = (M>>1)+Cflag*0x80\n\nVFlag' = ((A ^ (M + A + Carry)) & ((M + A + Carry) & M) & 0x80) == 0x80\nCFlag' = A + M > 0xFF\nZflag' = (A'==0)\nNflag' = (A'>=0x80)"
,
//30 ROR
"Rotate Right\n\nM' = M>>1 + CFlag*0x80\n\nCflag' = (M&1)\nZflag' = (M'==0)\nNflag' = (M'>=0x80)"
,
//31 PLA
"Pull A\n\nPull A from the stack\n\nSP' = SP+1"
,
//32 ARR
"Bitwise AND with A then Rotate A and check bits\n\nA' = ((A&M)>>1)+Carry*0x80\n\nCFlag = ((A'>>6)&1==1)\nVFlag = ((A'>>5)&1==1)\nZFlag' = (A'==0)\nNFlag' = (A'>=0x80)"
,
//33 BVS
"Branch on Overflow Set\n\nIf the overflow flag is set, branch.\n\nPC' = PC + (VFlag ? SignedOperand : 0)"
,
//34 SEI
"Set Interrupt Disable Flag\n\nIFlag' = true"
,
//35 STA
"Store A\n\nM' = A"
,
//36 SAX
"Store A and X\n\nM' = A&X"
,
//37 STY
"Store Y\n\nM' = Y"
,
//38 STX
"Store X\n\nM' = X"
,
//39 DEY
"Decrement Y\n\nY' = Y-1\n\nZFlag' = (Y'==0)\nNFlag' = (Y'>=0x80)"
,
//40 TXA
"Transfer X to A\n\nA' = X\n\nZFlag' = (A'==0)\nNFlag' = (A'>=0x80)"
,
//41 ANE
"Bitwise OR A with Magic then Bitwise AND with X AND with Memory\n\nA' = (A | magic) & X & M\n\n *Note: 'Magic' depends on the chip manufacturer\n 'Magic' is usually 00, EE, EF, FE, or FF"
,
//42 BCC
"Branch on Carry Clear\n\nIf the carry flag is not set, branch.\n\nPC' = PC + (!CFlag ? SignedOperand : 0)"
,
//43 TXS
"Transfer X to Stack Pointer\n\nSP' = X"
,
//44 SHA
"Store Bitwise AND X with A AND the High Byte of the Operand Plus 1\n\nM' = A & X & (HIGH(Arg)+1)"
,
//45 TYA
"Transfer Y to A\n\nA' = Y\n\nZFlag' = (A'==0)\nNFlag' = (A'>=0x80)"
,
//46 SHY
"Store Bitwise AND Y with The High Byte of the Operand Plus 1\n\nM' = Y & (HIGH(Arg)+1)"
,
//47 SHS
"Transfer Bitwise AND A with X to Stack Pointer then Store Bitwise And Stack Pointer with the High Byte of the Operand Plus 1\n\nSP' = A&X\nM' = SP'&(HIGH(Arg)+1)"
,
//48 SHX
"Store Bitwise AND X with The High Byte of the Operand Plus 1\n\nM' = X & (HIGH(Arg)+1)"
,
//49 LDY
"Load Y\n\nY' = M\n\nZFlag' = (Y'==0)\nNFlag' = (Y'>=0x80)"
,
//50 LDA
"Load A\n\nA' = M\n\nZFlag' = (A'==0)\nNFlag' = (A'>=0x80)"
,
//51 LDX
"Load X\n\nX' = M\n\nZFlag' = (X'==0)\nNFlag' = (X'>=0x80)"
,
//52 LAX
"Load A X\n\nA' = M\nX' = M\n\nZFlag' = (X'==0)\nNFlag' = (X'>=0x80)"
,
//53 TAY
"Transfer A to Y\n\nY' = A\n\nZFlag' = (Y'==0)\nNFlag' = (Y'>=0x80)"
,
//54 TAX
"Transfer A to X\n\nX' = A\n\nZFlag' = (X'==0)\nNFlag' = (X'>=0x80)"
,
//55 LXA
"Bitwise AND with A then Transfer A to X\n\nA' = A&M\nX'=A'\n\nZFlag' = (X'==0)\nNFlag' = (X'>=0x80)"
,
//56 BCS
"Branch on Carry Set\n\nIf the carry flag is set, branch.\n\nPC' = PC + (CFlag ? SignedOperand : 0)"
,
//57 CLV
"Clear Overflow\n\nVFlag = false"
,
//58 TSX
"Transfer Stack Pointer to X\n\nX' = SP"
,
//59 LAS
"Transfer Bitwise AND with Stack Pointer to A, X, and Stack Pointer\n\nA' = M&SP\nX' = M&SP\nSP' = M&SP"
,
//60 CPY
"Compare Y\n\nZFlag' = (Y==M)\nCFlag' = (Y>=M)\nNFlag' = (Y-M)>0x80"
,
//61 CMP
"Compare A\n\nZFlag' = (A==M)\nCFlag' = (A>=M)\nNFlag' = (A-M)>0x80"
,
//62 DCP
"Decrement then Compare A\n\nM' = M-1\nZFlag' = (A==M')\nCFlag' = (A>=M')\nNFlag' = (A-M')>0x80"
,
//63 DEC
"Decrement\n\nM' = M-1\n\nZFlag' = (M'==0)\nNFlag' = (M'>=0x80)"
,
//64 INY
"Increment Y\n\nY' = Y+1\n\nZFlag' = (Y'==0)\nNFlag' = (Y'>=0x80)"
,
//65 DEX
"Decrement X\n\nX' = X-1\n\nZFlag' = (X'==0)\nNFlag' = (X'>=0x80)"
,
//66 AXS
"Load X with Subtraction with Bitwise AND X with A\n\nX' = (A&X)-M\n\nZFlag' = (X==M)\nCFlag' = (X>=M)\nNFlag' = (X-M)>0x80"
,
//67 BNE
"Branch on Not Equal\n\nIf the zero flag is not set, branch.\n\nPC' = PC + (!ZFlag ? SignedOperand : 0)"
,
//68 CLD
"Clear Decimal Flag\n\nDFlag = false"
,
//69 CPX
"Compare X\n\nZFlag' = (X==M)\nCFlag' = (X>=M)\nNFlag' = (X-M)>0x80"
,
//70 SBC
"Subtract with Carry\n\nA' = A+(0xFF-M)+CFlag\n\nVFlag' = ((A ^ (M + A + Carry)) & ((M + A + Carry) & M) & 0x80) == 0x80\nCFlag' = A + M > 0xFF\nZflag' = (A'==0)\nNflag' = (A'>=0x80)"
,
//71 ISC
"Increment then subtract from accumulator\n\nM' = M+1\nA' = A+(0xFF-M')+CFlag\n\nVFlag' = ((A ^ (M' + A + Carry)) & ((M' + A + Carry) & M') & 0x80) == 0x80\nCFlag' = A + M' > 0xFF\nZflag' = (M'==0)\nNflag' = (M'>=0x80)"
,
//72 INC
"Increment\n\nM' = M+1\n\nZFlag' = (A'==0)\nNFlag' = (A'>=0x80)"
,
//73 INX
"Increment\n\nX' = X+1\n\nZFlag' = (X'==0)\nNFlag' = (X'>=0x80)"
,
//74 BEQ
"Branch on Equal\n\nIf the zero flag is set, branch.\n\nPC' = PC + (ZFlag ? SignedOperand : 0)"
,
//75 SED
"Set Decimal\n\nDFlag = true"
};
// this table is referenced in the debugging stuff.
// basically, for each index into this array, you can fetch an opcode's name, addressing mode, what flags/registers it can modify, documentation, and the number of cycles before a read/write.
public static Op[] OpDocs = {
new Op(0x00,"BRK","i" ,2,stackPChanges | pcChanges ,CycleDocs[0] ,InstructionDocs[0]),
new Op(0x01,"ORA","(d,x)" ,2,nFlag | zFlag | aChanges ,CycleDocs[22],InstructionDocs[1]),
new Op(0x02,"HLT","i" ,1,0 ,CycleDocs[29],InstructionDocs[2]),
new Op(0x03,"SLO","(d,x)" ,2,nFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[23],InstructionDocs[5]),
new Op(0x04,"NOP","d" ,2,0 ,CycleDocs[12],InstructionDocs[3]),
new Op(0x05,"ORA","d" ,2,nFlag | zFlag | aChanges ,CycleDocs[12],InstructionDocs[1]),
new Op(0x06,"ASL","d" ,2,nFlag | zFlag | cFlag | memChanges ,CycleDocs[13],InstructionDocs[4]),
new Op(0x07,"SLO","d" ,2,nFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[13],InstructionDocs[5]),
new Op(0x08,"PHP","i" ,1,stackPChanges ,CycleDocs[3] ,InstructionDocs[6]),
new Op(0x09,"ORA","#v" ,2,nFlag | zFlag | aChanges ,CycleDocs[7] ,InstructionDocs[1]),
new Op(0x0A,"ASL","A" ,1,nFlag | zFlag | cFlag | aChanges ,CycleDocs[6] ,InstructionDocs[4]),
new Op(0x0B,"ANC","#v" ,2,nFlag | zFlag | cFlag | aChanges ,CycleDocs[7] ,InstructionDocs[7]),
new Op(0x0C,"NOP","a" ,3,0 ,CycleDocs[9] ,InstructionDocs[3]),
new Op(0x0D,"ORA","a" ,3,nFlag | zFlag | aChanges ,CycleDocs[9] ,InstructionDocs[1]),
new Op(0x0E,"ASL","a" ,3,nFlag | zFlag | cFlag | memChanges ,CycleDocs[10],InstructionDocs[4]),
new Op(0x0F,"SLO","a" ,3,nFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[10],InstructionDocs[5]),
new Op(0x10,"BPL","r" ,2,pcChanges ,CycleDocs[21],InstructionDocs[8]),
new Op(0x11,"ORA","(d),y" ,2,nFlag | zFlag | aChanges ,CycleDocs[25],InstructionDocs[1]),
new Op(0x12,"HLT","i" ,1,0 ,CycleDocs[29],InstructionDocs[2]),
new Op(0x13,"SLO","(d),y" ,2,nFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[26],InstructionDocs[5]),
new Op(0x14,"NOP","d,x" ,2,0 ,CycleDocs[15],InstructionDocs[3]),
new Op(0x15,"ORA","d,x" ,2,nFlag | zFlag | aChanges ,CycleDocs[15],InstructionDocs[1]),
new Op(0x16,"ASL","d,x" ,2,nFlag | zFlag | cFlag | memChanges ,CycleDocs[16],InstructionDocs[4]),
new Op(0x17,"SLO","d,x" ,2,nFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[16],InstructionDocs[5]),
new Op(0x18,"CLC","i" ,1,cFlag ,CycleDocs[6] ,InstructionDocs[9]),
new Op(0x19,"ORA","a,y" ,3,nFlag | zFlag | aChanges ,CycleDocs[18],InstructionDocs[1]),
new Op(0x1A,"NOP","i" ,1,0 ,CycleDocs[6] ,InstructionDocs[3]),
new Op(0x1B,"SLO","a,y" ,3,nFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[19],InstructionDocs[5]),
new Op(0x1C,"NOP","a,x" ,3,0 ,CycleDocs[18],InstructionDocs[3]),
new Op(0x1D,"ORA","a,x" ,3,nFlag | zFlag | aChanges ,CycleDocs[18],InstructionDocs[1]),
new Op(0x1E,"ASL","a,x" ,3,nFlag | zFlag | cFlag | memChanges ,CycleDocs[19],InstructionDocs[4]),
new Op(0x1F,"SLO","a,x" ,3,nFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[19],InstructionDocs[5]),
new Op(0x20,"JSR","a" ,3,stackPChanges | pcChanges ,CycleDocs[5] ,InstructionDocs[10]),
new Op(0x21,"AND","(d,x)" ,2,nFlag | zFlag | aChanges ,CycleDocs[22],InstructionDocs[11]),
new Op(0x22,"HLT","i" ,1,0 ,CycleDocs[29],InstructionDocs[2]),
new Op(0x23,"RLA","(d,x)" ,2,nFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[23],InstructionDocs[12]),
new Op(0x24,"BIT","d" ,2,nFlag | zFlag | cFlag | vFlag ,CycleDocs[12],InstructionDocs[13]),
new Op(0x25,"AND","d" ,2,nFlag | zFlag | aChanges ,CycleDocs[12],InstructionDocs[11]),
new Op(0x26,"ROL","d" ,2,nFlag | zFlag | cFlag | memChanges ,CycleDocs[13],InstructionDocs[14]),
new Op(0x27,"RLA","d" ,2,nFlag | zFlag | cFlag | aChanges ,CycleDocs[13],InstructionDocs[12]),
new Op(0x28,"PLP","i" ,1,cFlag|zFlag|iFlag|dFlag|vFlag|nFlag,CycleDocs[4],InstructionDocs[15]),
new Op(0x29,"AND","#v" ,2,nFlag | zFlag | aChanges ,CycleDocs[7] ,InstructionDocs[11]),
new Op(0x2A,"ROL","A" ,1,nFlag | zFlag | cFlag | aChanges ,CycleDocs[6] ,InstructionDocs[14]),
new Op(0x2B,"ANC","#v" ,2,nFlag | zFlag | cFlag | aChanges ,CycleDocs[7] ,InstructionDocs[7]),
new Op(0x2C,"BIT","a" ,3,nFlag | zFlag | cFlag | vFlag ,CycleDocs[9] ,InstructionDocs[13]),
new Op(0x2D,"AND","a" ,3,nFlag | zFlag | aChanges ,CycleDocs[9] ,InstructionDocs[11]),
new Op(0x2E,"ROL","a" ,3,nFlag | zFlag | cFlag | memChanges ,CycleDocs[10],InstructionDocs[14]),
new Op(0x2F,"RLA","a" ,3,nFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[10],InstructionDocs[12]),
new Op(0x30,"BMI","r" ,2,pcChanges ,CycleDocs[21],InstructionDocs[16]),
new Op(0x31,"AND","(d),y" ,2,nFlag | zFlag | aChanges ,CycleDocs[25],InstructionDocs[11]),
new Op(0x32,"HLT","i" ,1,0 ,CycleDocs[29],InstructionDocs[2]),
new Op(0x33,"RLA","(d),y" ,2,nFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[26],InstructionDocs[12]),
new Op(0x34,"NOP","d,x" ,2,0 ,CycleDocs[15],InstructionDocs[3]),
new Op(0x35,"AND","d,x" ,2,nFlag | zFlag | aChanges ,CycleDocs[15],InstructionDocs[11]),
new Op(0x36,"ROL","d,x" ,2,nFlag | zFlag | cFlag | memChanges ,CycleDocs[16],InstructionDocs[14]),
new Op(0x37,"RLA","d,x" ,2,nFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[16],InstructionDocs[12]),
new Op(0x38,"SEC","i" ,1,cFlag ,CycleDocs[6] ,InstructionDocs[17]),
new Op(0x39,"AND","a,y" ,3,nFlag | zFlag | aChanges ,CycleDocs[18],InstructionDocs[11]),
new Op(0x3A,"NOP","i" ,1,0 ,CycleDocs[6] ,InstructionDocs[3]),
new Op(0x3B,"RLA","a,y" ,3,nFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[19],InstructionDocs[12]),
new Op(0x3C,"NOP","a,x" ,3,0 ,CycleDocs[18],InstructionDocs[3]),
new Op(0x3D,"AND","a,x" ,3,nFlag | zFlag | aChanges ,CycleDocs[18],InstructionDocs[11]),
new Op(0x3E,"ROL","a,x" ,3,nFlag | zFlag | cFlag | memChanges ,CycleDocs[19],InstructionDocs[14]),
new Op(0x3F,"RLA","a,x" ,3,nFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[19],InstructionDocs[12]),
new Op(0x40,"RTI","i" ,1,0xFF | stackPChanges | pcChanges ,CycleDocs[1] ,InstructionDocs[18]),
new Op(0x41,"EOR","(d,x)" ,2,nFlag | zFlag | aChanges ,CycleDocs[22],InstructionDocs[19]),
new Op(0x42,"HLT","i" ,1,0 ,CycleDocs[29],InstructionDocs[2]),
new Op(0x43,"SRE","(d,x)" ,2,nFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[23],InstructionDocs[20]),
new Op(0x44,"NOP","d" ,2,0 ,CycleDocs[12],InstructionDocs[3]),
new Op(0x45,"EOR","d" ,2,nFlag | zFlag | aChanges ,CycleDocs[12],InstructionDocs[19]),
new Op(0x46,"LSR","d" ,2,nFlag | zFlag | cFlag | memChanges ,CycleDocs[13],InstructionDocs[21]),
new Op(0x47,"SRE","d" ,2,nFlag | zFlag | cFlag ,CycleDocs[13],InstructionDocs[20]),
new Op(0x48,"PHA","i" ,1,stackPChanges ,CycleDocs[3] ,InstructionDocs[22]),
new Op(0x49,"EOR","#v" ,2,nFlag | zFlag ,CycleDocs[7] ,InstructionDocs[19]),
new Op(0x4A,"LSR","A" ,1,nFlag | zFlag | cFlag | aChanges ,CycleDocs[6] ,InstructionDocs[21]),
new Op(0x4B,"ASR","#v" ,2,nFlag | zFlag | cFlag | aChanges ,CycleDocs[7] ,InstructionDocs[23]),
new Op(0x4C,"JMP","a" ,3,0 ,CycleDocs[8] ,InstructionDocs[24]),
new Op(0x4D,"EOR","a" ,3,nFlag | zFlag | aChanges ,CycleDocs[9] ,InstructionDocs[19]),
new Op(0x4E,"LSR","a" ,3,nFlag | zFlag | cFlag | memChanges ,CycleDocs[10],InstructionDocs[21]),
new Op(0x4F,"SRE","a" ,3,nFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[10],InstructionDocs[20]),
new Op(0x50,"BVC","r" ,2,pcChanges ,CycleDocs[21],InstructionDocs[25]),
new Op(0x51,"EOR","(d),y" ,2,nFlag | zFlag | aChanges ,CycleDocs[25],InstructionDocs[19]),
new Op(0x52,"HLT","i" ,1,0 ,CycleDocs[29],InstructionDocs[2]),
new Op(0x53,"SRE","(d),y" ,2,nFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[26],InstructionDocs[20]),
new Op(0x54,"NOP","d,x" ,2,0 ,CycleDocs[15],InstructionDocs[3]),
new Op(0x55,"EOR","d,x" ,2,nFlag | zFlag | aChanges ,CycleDocs[15],InstructionDocs[19]),
new Op(0x56,"LSR","d,x" ,2,nFlag | zFlag | cFlag | memChanges ,CycleDocs[16],InstructionDocs[21]),
new Op(0x57,"SRE","d,x" ,2,nFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[16],InstructionDocs[20]),
new Op(0x58,"CLI","i" ,1,iFlag ,CycleDocs[6] ,InstructionDocs[26]),
new Op(0x59,"EOR","a,y" ,3,nFlag | zFlag | aChanges ,CycleDocs[18],InstructionDocs[19]),
new Op(0x5A,"NOP","i" ,1,0 ,CycleDocs[6] ,InstructionDocs[3]),
new Op(0x5B,"SRE","a,y" ,3,nFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[19],InstructionDocs[20]),
new Op(0x5C,"NOP","a,x" ,3,0 ,CycleDocs[18],InstructionDocs[3]),
new Op(0x5D,"EOR","a,x" ,3,nFlag | zFlag | aChanges ,CycleDocs[18],InstructionDocs[19]),
new Op(0x5E,"LSR","a,x" ,3,nFlag | zFlag | cFlag | memChanges ,CycleDocs[19],InstructionDocs[21]),
new Op(0x5F,"SRE","a,x" ,3,nFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[19],InstructionDocs[20]),
new Op(0x60,"RTS","i" ,1,stackPChanges | pcChanges ,CycleDocs[2] ,InstructionDocs[27]),
new Op(0x61,"ADC","(d,x)" ,2,nFlag | vFlag | zFlag | cFlag | aChanges ,CycleDocs[22],InstructionDocs[28]),
new Op(0x62,"HLT","i" ,1,0 ,CycleDocs[29],InstructionDocs[2]),
new Op(0x63,"RRA","(d,x)" ,2,nFlag | vFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[23],InstructionDocs[29]),
new Op(0x64,"NOP","d" ,2,0 ,CycleDocs[12],InstructionDocs[3]),
new Op(0x65,"ADC","d" ,2,nFlag | vFlag | zFlag | cFlag | aChanges ,CycleDocs[12],InstructionDocs[28]),
new Op(0x66,"ROR","d" ,2,nFlag | zFlag | cFlag | memChanges ,CycleDocs[13],InstructionDocs[30]),
new Op(0x67,"RRA","d" ,2,nFlag | vFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[13],InstructionDocs[29]),
new Op(0x68,"PLA","i" ,1,stackPChanges | aChanges ,CycleDocs[4] ,InstructionDocs[31]),
new Op(0x69,"ADC","#v" ,2,nFlag | vFlag | zFlag | cFlag | aChanges ,CycleDocs[7] ,InstructionDocs[28]),
new Op(0x6A,"ROR","A" ,1,nFlag | zFlag | cFlag | aChanges ,CycleDocs[6] ,InstructionDocs[30]),
new Op(0x6B,"ARR","#v" ,2,nFlag | vFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[7] ,InstructionDocs[32]),
new Op(0x6C,"JMP","(a)" ,3,0 ,CycleDocs[28],InstructionDocs[24]),
new Op(0x6D,"ADC","a" ,3,nFlag | vFlag | zFlag | cFlag | aChanges ,CycleDocs[9] ,InstructionDocs[28]),
new Op(0x6E,"ROR","a" ,3,nFlag | zFlag | cFlag | memChanges ,CycleDocs[10],InstructionDocs[30]),
new Op(0x6F,"RRA","a" ,3,nFlag | vFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[10],InstructionDocs[29]),
new Op(0x70,"BVS","r" ,2,pcChanges ,CycleDocs[21],InstructionDocs[33]),
new Op(0x71,"ADC","(d),y" ,2,nFlag | vFlag | zFlag | cFlag | aChanges ,CycleDocs[25],InstructionDocs[28]),
new Op(0x72,"HLT","i" ,1,0 ,CycleDocs[29],InstructionDocs[2]),
new Op(0x73,"RRA","(d),y" ,2,nFlag | vFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[26],InstructionDocs[29]),
new Op(0x74,"NOP","d,x" ,2,0 ,CycleDocs[15],InstructionDocs[3]),
new Op(0x75,"ADC","d,x" ,2,nFlag | vFlag | zFlag | cFlag | aChanges ,CycleDocs[15],InstructionDocs[28]),
new Op(0x76,"ROR","d,x" ,2,nFlag | zFlag | cFlag | memChanges ,CycleDocs[16],InstructionDocs[30]),
new Op(0x77,"RRA","d,x" ,2,nFlag | vFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[16],InstructionDocs[29]),
new Op(0x78,"SEI","i" ,1,iFlag ,CycleDocs[6] ,InstructionDocs[34]),
new Op(0x79,"ADC","a,y" ,3,nFlag | vFlag | zFlag | cFlag | aChanges ,CycleDocs[18],InstructionDocs[28]),
new Op(0x7A,"NOP","i" ,1,0 ,CycleDocs[6] ,InstructionDocs[3]),
new Op(0x7B,"RRA","a,y" ,3,nFlag | vFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[19],InstructionDocs[29]),
new Op(0x7C,"NOP","a,x" ,3,0 ,CycleDocs[18],InstructionDocs[3]),
new Op(0x7D,"ADC","a,x" ,3,nFlag | vFlag | zFlag | cFlag | aChanges ,CycleDocs[18],InstructionDocs[28]),
new Op(0x7E,"ROR","a,x" ,3,nFlag | zFlag | cFlag | memChanges ,CycleDocs[19],InstructionDocs[30]),
new Op(0x7F,"RRA","a,x" ,3,nFlag | vFlag | zFlag | cFlag | aChanges | memChanges,CycleDocs[19],InstructionDocs[29]),
new Op(0x80,"NOP","#v" ,2,0 ,CycleDocs[7] ,InstructionDocs[3]),
new Op(0x81,"STA","(d,x)" ,2,memChanges ,CycleDocs[24],InstructionDocs[35]),
new Op(0x82,"NOP","#v" ,2,0 ,CycleDocs[7] ,InstructionDocs[3]),
new Op(0x83,"SAX","(d,x)" ,2,memChanges ,CycleDocs[24],InstructionDocs[36]),
new Op(0x84,"STY","d" ,2,memChanges ,CycleDocs[14],InstructionDocs[37]),
new Op(0x85,"STA","d" ,2,memChanges ,CycleDocs[14],InstructionDocs[35]),
new Op(0x86,"STX","d" ,2,memChanges ,CycleDocs[14],InstructionDocs[38]),
new Op(0x87,"SAX","d" ,2,memChanges ,CycleDocs[14],InstructionDocs[36]),
new Op(0x88,"DEY","i" ,1,yChanges | nFlag | zFlag ,CycleDocs[6] ,InstructionDocs[39]),
new Op(0x89,"NOP","#v" ,2,0 ,CycleDocs[7] ,InstructionDocs[3]),
new Op(0x8A,"TXA","i" ,1,aChanges | nFlag | zFlag ,CycleDocs[7] ,InstructionDocs[40]),
new Op(0x8B,"ANE","#v" ,2,aChanges | nFlag | zFlag ,CycleDocs[7] ,InstructionDocs[41]),
new Op(0x8C,"STY","a" ,3,memChanges ,CycleDocs[11],InstructionDocs[37]),
new Op(0x8D,"STA","a" ,3,memChanges ,CycleDocs[11],InstructionDocs[35]),
new Op(0x8E,"STX","a" ,3,memChanges ,CycleDocs[11],InstructionDocs[38]),
new Op(0x8F,"SAX","a" ,3,memChanges ,CycleDocs[11],InstructionDocs[36]),
new Op(0x90,"BCC","r" ,2,pcChanges ,CycleDocs[21],InstructionDocs[42]),
new Op(0x91,"STA","(d),y" ,2,memChanges ,CycleDocs[27],InstructionDocs[35]),
new Op(0x92,"HLT","i" ,1,memChanges ,CycleDocs[29],InstructionDocs[2]),
new Op(0x93,"SHA","(d),y" ,2,memChanges ,CycleDocs[27],InstructionDocs[44]),
new Op(0x94,"STY","d,x" ,2,memChanges ,CycleDocs[17],InstructionDocs[37]),
new Op(0x95,"STA","d,x" ,2,memChanges ,CycleDocs[17],InstructionDocs[35]),
new Op(0x96,"STX","d,y" ,2,memChanges ,CycleDocs[17],InstructionDocs[38]),
new Op(0x97,"SAX","d,y" ,2,memChanges ,CycleDocs[17],InstructionDocs[36]),
new Op(0x98,"TYA","i" ,1,aChanges | nFlag | zFlag ,CycleDocs[6] ,InstructionDocs[45]),
new Op(0x99,"STA","a,y" ,3,memChanges ,CycleDocs[20],InstructionDocs[35]),
new Op(0x9A,"TXS","i" ,1,stackPChanges ,CycleDocs[6] ,InstructionDocs[43]),
new Op(0x9B,"SHS","a,y" ,3,stackPChanges | memChanges,CycleDocs[20],InstructionDocs[47]),
new Op(0x9C,"SHY","a,x" ,3,memChanges ,CycleDocs[20],InstructionDocs[46]),
new Op(0x9D,"STA","a,x" ,3,memChanges ,CycleDocs[20],InstructionDocs[35]),
new Op(0x9E,"SHX","a,y" ,3,memChanges ,CycleDocs[20],InstructionDocs[48]),
new Op(0x9F,"SHA","a,y" ,3,memChanges ,CycleDocs[20],InstructionDocs[44]),
new Op(0xA0,"LDY","#v" ,2,yChanges | nFlag | zFlag ,CycleDocs[7] ,InstructionDocs[49]),
new Op(0xA1,"LDA","(d,x)" ,2,aChanges | nFlag | zFlag ,CycleDocs[22],InstructionDocs[50]),
new Op(0xA2,"LDX","#v" ,2,xChanges | nFlag | zFlag ,CycleDocs[7] ,InstructionDocs[51]),
new Op(0xA3,"LAX","(d,x)" ,2,xChanges | aChanges | nFlag | zFlag ,CycleDocs[22],InstructionDocs[52]),
new Op(0xA4,"LDY","d" ,2,yChanges | nFlag | zFlag ,CycleDocs[12],InstructionDocs[49]),
new Op(0xA5,"LDA","d" ,2,aChanges | nFlag | zFlag ,CycleDocs[12],InstructionDocs[50]),
new Op(0xA6,"LDX","d" ,2,xChanges | nFlag | zFlag ,CycleDocs[12],InstructionDocs[51]),
new Op(0xA7,"LAX","d" ,2,xChanges | aChanges | nFlag | zFlag ,CycleDocs[12],InstructionDocs[52]),
new Op(0xA8,"TAY","i" ,1,yChanges | nFlag | zFlag ,CycleDocs[6] ,InstructionDocs[53]),
new Op(0xA9,"LDA","#v" ,2,aChanges | nFlag | zFlag ,CycleDocs[7] ,InstructionDocs[50]),
new Op(0xAA,"TAX","i" ,1,xChanges | nFlag | zFlag ,CycleDocs[6] ,InstructionDocs[54]),
new Op(0xAB,"LXA","#v" ,2,xChanges | aChanges | nFlag | zFlag ,CycleDocs[7] ,InstructionDocs[55]),
new Op(0xAC,"LDY","a" ,3,yChanges | nFlag | zFlag ,CycleDocs[9] ,InstructionDocs[49]),
new Op(0xAD,"LDA","a" ,3,aChanges | nFlag | zFlag ,CycleDocs[9] ,InstructionDocs[50]),
new Op(0xAE,"LDX","a" ,3,xChanges | nFlag | zFlag ,CycleDocs[10],InstructionDocs[51]),
new Op(0xAF,"LAX","a" ,3,xChanges | aChanges | nFlag | zFlag ,CycleDocs[10],InstructionDocs[52]),
new Op(0xB0,"BCS","r" ,2,pcChanges ,CycleDocs[21],InstructionDocs[56]),
new Op(0xB1,"LDA","(d),y" ,2,aChanges | nFlag | zFlag ,CycleDocs[25],InstructionDocs[50]),
new Op(0xB2,"HLT","i" ,1,0 ,CycleDocs[29],InstructionDocs[2]),
new Op(0xB3,"LAX","(d),y" ,2,xChanges | aChanges | nFlag | zFlag ,CycleDocs[25],InstructionDocs[52]),
new Op(0xB4,"LDY","d,x" ,2,yChanges | nFlag | zFlag ,CycleDocs[15],InstructionDocs[49]),
new Op(0xB5,"LDA","d,x" ,2,aChanges | nFlag | zFlag ,CycleDocs[15],InstructionDocs[50]),
new Op(0xB6,"LDX","d,y" ,2,xChanges | nFlag | zFlag ,CycleDocs[15],InstructionDocs[51]),
new Op(0xB7,"LAX","d,y" ,2,xChanges | aChanges | nFlag | zFlag ,CycleDocs[15],InstructionDocs[52]),
new Op(0xB8,"CLV","i" ,1,vFlag ,CycleDocs[6] ,InstructionDocs[57]),
new Op(0xB9,"LDA","a,y" ,3,aChanges | nFlag | zFlag ,CycleDocs[18],InstructionDocs[50]),
new Op(0xBA,"TSX","i" ,1,xChanges | nFlag | zFlag ,CycleDocs[6] ,InstructionDocs[58]),
new Op(0xBB,"LAS","a,y" ,3,nFlag | zFlag | aChanges | xChanges | stackPChanges ,CycleDocs[18],InstructionDocs[59]),
new Op(0xBC,"LDY","a,x" ,3,yChanges | nFlag | zFlag ,CycleDocs[18],InstructionDocs[49]),
new Op(0xBD,"LDA","a,x" ,3,aChanges | nFlag | zFlag ,CycleDocs[18],InstructionDocs[50]),
new Op(0xBE,"LDX","a,y" ,3,xChanges | nFlag | zFlag ,CycleDocs[18],InstructionDocs[51]),
new Op(0xBF,"LAX","a,y" ,3,xChanges | aChanges | nFlag | zFlag ,CycleDocs[18],InstructionDocs[52]),
new Op(0xC0,"CPY","#v" ,2,nFlag | zFlag | cFlag ,CycleDocs[7] ,InstructionDocs[60]),
new Op(0xC1,"CMP","(d,x)" ,2,nFlag | zFlag | cFlag ,CycleDocs[22],InstructionDocs[61]),
new Op(0xC2,"NOP","#v" ,2,0 ,CycleDocs[7] ,InstructionDocs[3]),
new Op(0xC3,"DCP","(d,x)" ,2,memChanges | nFlag | zFlag | cFlag ,CycleDocs[23],InstructionDocs[62]),
new Op(0xC4,"CPY","d" ,2,nFlag | zFlag | cFlag ,CycleDocs[12],InstructionDocs[60]),
new Op(0xC5,"CMP","d" ,2,nFlag | zFlag | cFlag ,CycleDocs[12],InstructionDocs[61]),
new Op(0xC6,"DEC","d" ,2,memChanges | nFlag | zFlag ,CycleDocs[13],InstructionDocs[63]),
new Op(0xC7,"DCP","d" ,2,memChanges | nFlag | zFlag | cFlag ,CycleDocs[13],InstructionDocs[62]),
new Op(0xC8,"INY","i" ,1,yChanges | nFlag | zFlag ,CycleDocs[6] ,InstructionDocs[64]),
new Op(0xC9,"CMP","#v" ,2,nFlag | zFlag | cFlag ,CycleDocs[7] ,InstructionDocs[61]),
new Op(0xCA,"DEX","i" ,1,xChanges | nFlag | zFlag ,CycleDocs[6] ,InstructionDocs[65]),
new Op(0xCB,"AXS","#v" ,2,memChanges | nFlag | cFlag | zFlag ,CycleDocs[7] ,InstructionDocs[66]),
new Op(0xCC,"CPY","a" ,3,nFlag | zFlag | cFlag ,CycleDocs[9] ,InstructionDocs[60]),
new Op(0xCD,"CMP","a" ,3,nFlag | zFlag | cFlag ,CycleDocs[9] ,InstructionDocs[61]),
new Op(0xCE,"DEC","a" ,3,memChanges | nFlag | zFlag ,CycleDocs[10],InstructionDocs[63]),
new Op(0xCF,"DCP","a" ,3,memChanges | nFlag | zFlag | cFlag ,CycleDocs[10],InstructionDocs[62]),
new Op(0xD0,"BNE","r" ,2,pcChanges ,CycleDocs[21],InstructionDocs[67]),
new Op(0xD1,"CMP","(d),y" ,2,nFlag | zFlag | cFlag ,CycleDocs[25],InstructionDocs[61]),
new Op(0xD2,"HLT","i" ,1,0 ,CycleDocs[29],InstructionDocs[2]),
new Op(0xD3,"DCP","(d),y" ,2,memChanges | nFlag | zFlag | cFlag ,CycleDocs[26],InstructionDocs[62]),
new Op(0xD4,"NOP","d,x" ,2,0 ,CycleDocs[15],InstructionDocs[3]),
new Op(0xD5,"CMP","d,x" ,2,nFlag | zFlag | cFlag ,CycleDocs[15],InstructionDocs[61]),
new Op(0xD6,"DEC","d,x" ,2,memChanges | nFlag | zFlag ,CycleDocs[16],InstructionDocs[63]),
new Op(0xD7,"DCP","d,x" ,2,memChanges | nFlag | zFlag | cFlag ,CycleDocs[16],InstructionDocs[62]),
new Op(0xD8,"CLD","i" ,1,dFlag ,CycleDocs[6] ,InstructionDocs[68]),
new Op(0xD9,"CMP","a,y" ,3,nFlag | zFlag | cFlag ,CycleDocs[18],InstructionDocs[61]),
new Op(0xDA,"NOP","i" ,1,0 ,CycleDocs[6] ,InstructionDocs[3]),
new Op(0xDB,"DCP","a,x" ,3,memChanges | nFlag | zFlag | cFlag ,CycleDocs[19],InstructionDocs[62]),
new Op(0xDC,"NOP","a,x" ,3,0 ,CycleDocs[18],InstructionDocs[3]),
new Op(0xDD,"CMP","a,x" ,3,nFlag | zFlag | cFlag ,CycleDocs[18],InstructionDocs[61]),
new Op(0xDE,"DEC","a,x" ,3,memChanges | nFlag | zFlag ,CycleDocs[19],InstructionDocs[63]),
new Op(0xDF,"DCP","a,x" ,3,memChanges | nFlag | zFlag | cFlag ,CycleDocs[19],InstructionDocs[62]),
new Op(0xE0,"CPX","#v" ,2,nFlag | zFlag | cFlag ,CycleDocs[7] ,InstructionDocs[69]),
new Op(0xE1,"SBC","(d,x)" ,2,aChanges | nFlag | zFlag | cFlag | vFlag ,CycleDocs[22],InstructionDocs[70]),
new Op(0xE2,"NOP","#v" ,2,0 ,CycleDocs[7] ,InstructionDocs[3]),
new Op(0xE3,"ISC","(d,x)" ,2,aChanges | memChanges | nFlag | zFlag | cFlag | vFlag,CycleDocs[23],InstructionDocs[71]),
new Op(0xE4,"CPX","d" ,2,nFlag | zFlag | cFlag ,CycleDocs[12],InstructionDocs[69]),
new Op(0xE5,"SBC","d" ,2,aChanges | nFlag | zFlag | cFlag | vFlag ,CycleDocs[12],InstructionDocs[70]),
new Op(0xE6,"INC","d" ,2,memChanges | nFlag | zFlag ,CycleDocs[13],InstructionDocs[72]),
new Op(0xE7,"ISC","d" ,2,aChanges | memChanges | nFlag | zFlag | cFlag | vFlag,CycleDocs[13],InstructionDocs[71]),
new Op(0xE8,"INX","i" ,1,xChanges | nFlag | zFlag ,CycleDocs[6] ,InstructionDocs[73]),
new Op(0xE9,"SBC","#v" ,2,aChanges | nFlag | zFlag | cFlag | vFlag ,CycleDocs[7] ,InstructionDocs[70]),
new Op(0xEA,"NOP","i" ,1,0 ,CycleDocs[6] ,InstructionDocs[3]),
new Op(0xEB,"SBC","#v" ,2,aChanges | nFlag | zFlag | cFlag | vFlag ,CycleDocs[7] ,InstructionDocs[70]),
new Op(0xEC,"CPX","a" ,3,nFlag | zFlag | cFlag ,CycleDocs[9] ,InstructionDocs[69]),
new Op(0xED,"SBC","a" ,3,aChanges | nFlag | zFlag | cFlag | vFlag ,CycleDocs[9] ,InstructionDocs[70]),
new Op(0xEE,"INC","a" ,3,memChanges | nFlag | zFlag ,CycleDocs[10],InstructionDocs[72]),
new Op(0xEF,"ISC","a" ,3,aChanges | memChanges | nFlag | zFlag | cFlag | vFlag,CycleDocs[10],InstructionDocs[71]),
new Op(0xF0,"BEQ","r" ,2,pcChanges ,CycleDocs[21],InstructionDocs[74]),
new Op(0xF1,"SBC","(d),y" ,2,aChanges | nFlag | zFlag | cFlag | vFlag ,CycleDocs[25],InstructionDocs[70]),
new Op(0xF2,"HLT","i" ,1,0 ,CycleDocs[29],InstructionDocs[2]),
new Op(0xF3,"ISC","(d),y" ,2,aChanges | memChanges | nFlag | zFlag | cFlag | vFlag,CycleDocs[26],InstructionDocs[71]),
new Op(0xF4,"NOP","d,x" ,2,0 ,CycleDocs[15],InstructionDocs[3]),
new Op(0xF5,"SBC","d,x" ,2,aChanges | nFlag | zFlag | cFlag | vFlag ,CycleDocs[15],InstructionDocs[70]),
new Op(0xF6,"INC","d,x" ,2,memChanges | nFlag | zFlag ,CycleDocs[16],InstructionDocs[72]),
new Op(0xF7,"ISC","d,x" ,2,aChanges | memChanges | nFlag | zFlag | cFlag | vFlag,CycleDocs[16],InstructionDocs[71]),
new Op(0xF8,"SED","i" ,1,dFlag ,CycleDocs[6] ,InstructionDocs[75]),
new Op(0xF9,"SBC","a,y" ,3,aChanges | nFlag | zFlag | cFlag | vFlag ,CycleDocs[18],InstructionDocs[70]),
new Op(0xFA,"NOP","i" ,1,0 ,CycleDocs[6] ,InstructionDocs[3]),
new Op(0xFB,"ISC","a,x" ,3,aChanges | memChanges | nFlag | zFlag | cFlag | vFlag,CycleDocs[19],InstructionDocs[71]),
new Op(0xFC,"NOP","a,x" ,3,0 ,CycleDocs[18],InstructionDocs[3]),
new Op(0xFD,"SBC","a,x" ,3,aChanges | nFlag | zFlag | cFlag | vFlag ,CycleDocs[18],InstructionDocs[70]),
new Op(0xFE,"INC","a,x" ,3,memChanges | nFlag | zFlag ,CycleDocs[19],InstructionDocs[72]),
new Op(0xFF,"ISC","a,x" ,3,aChanges | memChanges | nFlag | zFlag | cFlag | vFlag,CycleDocs[19],InstructionDocs[71])
};
}
}
================================================
FILE: App.config
================================================
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<dllmap dll="SDL2" os="windows" target="SDL2.dll"/>
<dllmap dll="SDL2" os="osx" target="libSDL2.dylib"/>
<dllmap dll="SDL2" os="linux" target="libSDL2-2.0.so.0"/>
</configuration>
================================================
FILE: Emulator.cs
================================================
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using TriCNES.mappers;
namespace TriCNES
{
// Coin's Contrabulous Cartswapulator!
public class Cartridge
{
// Since I made this emulator with mid-instruction cartridge swapping in mind, the cartridge class holds information about the cartridge that would persist when swapped in and out.
public Emulator Emu; // Mostly for triggering / clearing the IRQ from mapper function.
public string Name; // For debugging
public byte[] ROM; // The entire .nes file
public byte[] PRGROM; // The entire program rom portion of the .nes file
public byte[] CHRROM; // The entire character rom portion of the .nes file
public byte MemoryMapper; // Header info: what mapper chip is this cartridge using?
public byte SubMapper; // Header Info: what variant of the mapper chip are we using?
public byte PRG_Size; // Header info: how many kb of PRG data does this cartridge have?
public byte CHR_Size; // Header info: how many kb of CHR data does this cartridge have?
public byte PRG_SizeMinus1; // PRG_Size-1; This is frequently used when grabbing data from PRG banks
public byte[] CHRRAM; // If this cartridge has character RAM, this array is used.
public bool UsingCHRRAM; // Header info: CHR RAM doesn't exist on all cartridges.
public byte[] PRGRAM; // PRG RAM / Battery backed save RAM.
public bool AlternativeNametableArrangement; // Header info: Some mapper chips support "alternative nametable arrangements", which are mapper-specific.
public byte[] PRGVRAM; // PRG VRAM, for the alternative nametable arrangements.
public Cartridge(string filepath) // Constructor from file path
{
ROM = File.ReadAllBytes(filepath); // Reads the file from the provided file path, and stores every byte into an array.
// The iNES header isn't actually part of the physical cartridge.
// Rather, the values of the iNES header are manually added to provide extra information to emulators.
// Info such as "what mapper chip", "how many CHR banks?" and even "how should we mirror the nametables?" are part of this header.
MemoryMapper = (byte)(ROM[7] & 0xF0); // Parsing the iNES header to determine what mapper chip this cartridge uses.
MemoryMapper |= (byte)(ROM[6] >> 4); // The upper nybble of byte 6, bitwise OR with the upper nybble of byte 7.
SubMapper = (byte)((ROM[8] & 0xF0) >> 4);
PRG_Size = ROM[4]; // Parsing the iNES header to determine how many kb of PRG data exists on this cartridge.
CHR_Size = ROM[5]; // Parsing the iNES header to determine how many kb of CHR data exists on this cartridge.
PRG_SizeMinus1 = (byte)(PRG_Size - 1); // This value is occasionally used whenever a mapper has a fixed bank from the end of the PRG data, like address $E000 in the MMC3 chip.
UsingCHRRAM = CHR_Size == 0; // If CHR_Size == 0, this is using CHR RAM
PRGROM = new byte[PRG_Size * 0x4000]; // 0x4000 bytes of PRG ROM, multiplied by byte 4 of the iNES header.
CHRROM = new byte[CHR_Size * 0x2000]; // 0x2000 bytes of CHR ROM, multiplied by byte 5 of the iNES header.
CHRRAM = new byte[0x2000]; // CHR RAM always has 2 kibibytes
NametableHorizontalMirroring = ((ROM[6] & 1) == 0); // The style in which the nametable is mirrored is part of the iNES header.
AlternativeNametableArrangement = ((ROM[6] & 8) != 0); // Some mappers support other arrangements.
if (AlternativeNametableArrangement)
{
PRGVRAM = new byte[0x800];
}
Array.Copy(ROM, 0x10, PRGROM, 0, PRGROM.Length); // This sets up the PRG ROM array with the values from the .nes file
Array.Copy(ROM, 0x10 + PRGROM.Length, CHRROM, 0, CHRROM.Length); // This sets up the CHR ROM array with the values from the .nes file
// at this point, the ROM byte array is no longer needed, so null it to free up its memory.
ROM = null;
PRGRAM = new byte[0x2000]; // PRG RAM probably has different lengths depending on the mapper, but this emulator doesn't yet support any mappers in which that length isn't 2 kibibytes.
Name = filepath; // For debugging, it's nice to see the file name sometimes.
switch (MemoryMapper)
{
default:
case 0: MapperChip = new Mapper_NROM(); break;
case 1: MapperChip = new Mapper_MMC1(); break;
case 2: MapperChip = new Mapper_UxROM(); break;
case 3: MapperChip = new Mapper_CNROM(); break;
case 4: MapperChip = new Mapper_MMC3(); break;
case 7: MapperChip = new Mapper_AOROM(); break;
case 9: MapperChip = new Mapper_MMC2(); break;
case 69: MapperChip = new Mapper_FME7(); break;
}
MapperChip.Cart = this;
}
public DiskDrive FDS; // The famicom disk system disk drive.
public Cartridge(string filepath, string FDSBIOS_filepath)
{
ROM = File.ReadAllBytes(FDSBIOS_filepath); // Reads the file from the provided file path, and stores every byte into an array.
FDS = new DiskDrive();
FDS.InsertDisk(filepath);
PRGRAM = new byte[0x8000]; // The FDS has 32Kib of PRG RAM!
CHRRAM = new byte[0x2000]; // and 8 Kib of CHR RAM.
Name = filepath; // For debugging, it's nice to see the file name sometimes.
MapperChip = new Mapper_FDS(ROM);
MapperChip.Cart = this;
FDS.Cart = this;
}
public bool NametableHorizontalMirroring;
public Mapper MapperChip;
}
public class Mapper
{
public Cartridge Cart;
public byte dataBus;
public byte observedDataBus;
public bool dataPinsAreNotFloating;
public bool observedDataPinsAreNotFloating;
// Default to NROM behavior.
public virtual void FetchPRG(ushort Address, bool Observe)
{
bool notFloating = false;
byte data = 0;
if (!Observe) { dataPinsAreNotFloating = false; } else { observedDataPinsAreNotFloating = false; }
// Observing can happen on a different thread, so we need to ensure that observing doesn't overwrite the data bus or floating pins status.
if (Address >= 0x8000)
{
data = Cart.PRGROM[Address & (Cart.PRGROM.Length - 1)]; // Get the address from the ROM file. If the ROM only has $4000 bytes, this will make addresses > $BFFF mirrors of $8000 through $BFFF.
notFloating = true;
}
//open bus
if (notFloating)
{
EndFetchPRG(Observe, data);
}
return;
}
public virtual void StorePRG(ushort Address, byte Input)
{
}
public virtual byte FetchCHR(ushort Address, bool Observe)
{
return Cart.CHRROM[Address & 0x1FFF];
}
public virtual byte FetchPPU()
{
// This will always use the upper 8 bits of the address bus | the octal latch. This replaces the lower 8 bits of the address bus.
ushort Address = (ushort)((Cart.Emu.PPU_AddressBus & 0x3F00) | Cart.Emu.PPU_OctalLatch);
bool CIRAM = Address >= 0x2000;
if (!CIRAM)
{
if (Cart.UsingCHRRAM)
{
Cart.Emu.PPU_AddressBus &= 0xFF00;
Cart.Emu.PPU_AddressBus |= Cart.CHRRAM[Address];
}
else
{
//Pattern Table
Cart.Emu.PPU_AddressBus &= 0xFF00;
Cart.Emu.PPU_AddressBus |= Cart.MapperChip.FetchCHR(Address, false);
}
}
else // if the VRAM address is >= $2000, we need to consider nametable mirroring.
{
Address = MirrorNametable(Address);
Address &= 0x7FF;
Cart.Emu.PPU_AddressBus &= 0xFF00;
Cart.Emu.PPU_AddressBus |= Cart.Emu.VRAM[Address];
}
return (byte)Cart.Emu.PPU_AddressBus;
}
public virtual ushort MirrorNametable(ushort Address)
{
if (!Cart.NametableHorizontalMirroring)
{
return (ushort)(Address & 0x37FF); // mask away $0800
}
else // horizontal
{
return (ushort)((Address & 0x33FF) | ((Address & 0x0800) >> 1)); // mask away $0C00, bit 10 becomes the former bit 11
}
}
public virtual List<byte> SaveMapperRegisters()
{
List<byte> State = new List<byte>();
foreach (Byte b in Cart.PRGRAM) { State.Add(b); }
foreach (Byte b in Cart.CHRRAM) { State.Add(b); }
return State;
}
public virtual void LoadMapperRegisters(List<byte> State, int startIndex, out int exitIndex)
{
int p = startIndex;
for (int i = 0; i < Cart.PRGRAM.Length; i++) { Cart.PRGRAM[i] = State[p++]; }
for (int i = 0; i < Cart.CHRRAM.Length; i++) { Cart.CHRRAM[i] = State[p++]; }
exitIndex = p;
}
public virtual void PPUClock() // runs every PPU clock. (See MMC3)
{
}
public virtual void CPUClock() // runs every CPU clock. (See Sunsoft FME-7)
{
}
public virtual void CPUClockRise() // runs every time the CPU clock rises. (See MMC3)
{
}
public virtual void FDS_ByteTransferFlag()
{
}
public virtual byte FDS_Get4025()
{
return 0;
}
protected void EndFetchPRG(bool Observe, byte data)
{
if (!Observe)
{
dataPinsAreNotFloating = true;
dataBus = data;
}
else
{
observedDataPinsAreNotFloating = true;
observedDataBus = data;
}
}
}
public class DiskDrive
{
public Cartridge Cart;
public byte[] Disk;
public byte ShiftRegister;
public byte ShiftRegisterLatch;
public bool IRQ;
public ushort clock; // every 1792 master clock cycles,
public ushort DiskAddress;
public byte DiskAddressFine;
public bool Status_ByteTransferFlag;
public void Clock()
{
clock++;
if(clock % 244 == 0)
{
if ((Cart.MapperChip.FDS_Get4025() & 0x46) == 0x44)
{
// reading
byte ShiftBit = (byte)((Disk[DiskAddress] << DiskAddressFine) & 1);
ShiftRegister <<= 1;
ShiftRegister |= ShiftBit;
DiskAddressFine++;
if (DiskAddressFine == 8)
{
DiskAddressFine = 0;
DiskAddress++;
}
}
}
if(clock == 1792)
{
clock = 0;
DiskAddressFine = 0;
ShiftRegisterLatch = ShiftRegister;
// disk drive is ready.
// raise the byte transfer flag!
Status_ByteTransferFlag = true;
Cart.MapperChip.FDS_ByteTransferFlag(); // Trigger an IRQ if $4025.7 is set.
}
}
public void InsertDisk(string filepath)
{
Disk = File.ReadAllBytes(filepath); // Reads the file from the provided file path, and stores every byte into an array.
}
}
public class Emulator
{
public Cartridge Cart; // The idea behind this emulator is that this value could be changed at any time if you so desire.
public byte PPUClock; // Counts down from 4. When it's 0, a PPU cycle occurs.
public byte CPUClock; // Counts down from 12. When it's 0, a CPU cycle occurs.
public byte MasterClock; // Counts up every master clock cycle. Resets at 24.
public byte APUAlignment; // at power on or reset, is this a get/put, and how long until the DMC DMA?
public bool APU_PutCycle = false; // The APU needs to know if this is a "get" or "put" cycle.
public byte[] OAM = new byte[0x100]; // Object Attribute Memory is 256 bytes.
public byte[] OAM2 = new byte[32]; // Secondary OAM is specifically the 8 objects being rendered on the current scanline.
public byte SecondaryOAMSize = 0; // This is a count of how many objects are currently in secondary OAM.
public byte OAM2Address = 0; // During sprite evaluation, the current SecondaryOAM Address is used to track what byte is set of a given dot.
public bool SecondaryOAMFull = false; // If full and another object exists in the same scanline, the PPU Sprite OVerflow flag is set.
public byte SpriteEvaluationTick = 0; // During sprite evaluation, there's a switch statement that determines what to do on a given dot. This determines which action to take.
public bool OAMAddressOverflowedDuringSpriteEvaluation = false; // If the OAM address overflows during sprite evaluation, there's a few bugs that can occur.
public byte[] RAM = new byte[0x800]; // There are 0x800 bytes of RAM
public byte[] VRAM = new byte[0x800]; // There are 0x800 bytes of VRAM
public byte[] PaletteRAM = new byte[0x20]; // there are 0x20 bytes of palette RAM
public ushort programCounter = 0; // The PC. What address is currently being executed?
public byte opCode = 0; // The first CPU cycle of an instruction will read the opcode. This determines how the rest of the cycles will behave.
public int totalCycles; // For debugging. This is just a count of how many CPU cycles have occurred since the console booted up.
public byte stackPointer = 0x00; // The Stack pointer is used during pushing/popping values with the stack. This determines which address will be read or written to.
public bool flag_Carry; // The Carry flag is used in BCC and BCS instructions, and is set when the result of an operation over/underflows.
public bool flag_Zero; // The Zero flag is used in BNE and BEQ instructions, and is set when the result of an operation is zero.
public bool flag_Interrupt; // The Interrupt suppression flag will suppress IRQ's.
public bool flag_Decimal; // The NES doesn't use this flag.
public bool flag_Overflow; // The Carry flag is used in BVC and BVS instructions, and is set when the result of an operation over/underflows and the sign of the result is the same as the value before the operation.
public bool flag_Negative; // The Zero flag is used in BPL and BMI instructions, and is set when the result of an operation is negative. (bit 7 is set)
byte status = 0; // This is a byte representation of all the flags.
public byte A = 0; // The Accumulator, or "A Register"
public byte X = 0; // The X Register
public byte Y = 0; // The Y Register
public byte H = 0; // The High byte of the target address. A couple unofficial instructions use this value.
public bool IgnoreH; // However, with a well-timed DMA, the H register isn't actually part of the equation on some of those.
public byte dataBus = 0; // The Data Bus.
public byte internalBus = 0; // The Data Bus (internal to address $4015)
public ushort addressBus = 0;// The Address Bus. "Where are we reading/writing"
public byte specialBus = 0; // The Special Bus is used in certain instructions. (The special bus is mostly used in half-CPU-cycles connecting the various registers to the alu)
public byte dl = 0; // Data Latch. This holds values between CPU cycles that are used in later cycles within an instruction.
public byte operationCycle = 0; // This tracks what cycle of a given instruction is being emulated. Cycle 0 fetches the opcode, and all cycles after that have specific logic depending on which cycle needs emulated next.
public ushort temporaryAddress; // I use this to temporarily modify the value of the address bus for some if statements. This is mostly for checking if the low byte under/over flows.
public static uint[] NesPalInts = {
// each uint represents the ARGB components of a color.
// there's 64 colors, but this is also how I implement specific values for the PPU's emphasis bits.
// default palette:
0xFF656565, 0xFF002A84, 0xFF1513A2, 0xFF3A019E, 0xFF59007A, 0xFF6A003E, 0xFF680800, 0xFF531D00, 0xFF323400, 0xFF0D4600, 0xFF004F00, 0xFF004C09, 0xFF003F4B, 0xFF000000, 0xFF000000, 0xFF000000,
0xFFAEAEAE, 0xFF175FD6, 0xFF4341FF, 0xFF7529FA, 0xFF9E1DCA, 0xFFB4207B, 0xFFB13322, 0xFF964E00, 0xFF6A6C00, 0xFF398400, 0xFF0F9000, 0xFF008D33, 0xFF007B8C, 0xFF000000, 0xFF000000, 0xFF000000,
0xFFFEFFFF, 0xFF66AFFF, 0xFF9390FF, 0xFFC578FF, 0xFFEE6CFF, 0xFFFF6FCA, 0xFFFF8271, 0xFFE69E25, 0xFFBABC00, 0xFF88D501, 0xFF5EE132, 0xFF47DD82, 0xFF4ACBDC, 0xFF4E4E4E, 0xFF000000, 0xFF000000,
0xFFFEFFFF, 0xFFC0DEFF, 0xFFD2D1FF, 0xFFE7C7FF, 0xFFF8C2FF, 0xFFFFC3E9, 0xFFFFCBC4, 0xFFF5D7A5, 0xFFE2E394, 0xFFCEED96, 0xFFBCF2AA, 0xFFB3F1CB, 0xFFB4E9F0, 0xFFB6B6B6, 0xFF000000, 0xFF000000,
// emphasize red:
0xFF66423E, 0xFF000D58, 0xFF150075, 0xFF380075, 0xFF560058, 0xFF670027, 0xFF680000, 0xFF530D00, 0xFF341E00, 0xFF102B00, 0xFF003000, 0xFF002B00, 0xFF001C24, 0xFF000000, 0xFF000000, 0xFF000000,
0xFFAF7E78, 0xFF19379A, 0xFF4320C1, 0xFF720FC1, 0xFF9A089A, 0xFFB10F59, 0xFFB2220F, 0xFF963700, 0xFF6C4D00, 0xFF3D5F00, 0xFF166500, 0xFF005F0C, 0xFF004B55, 0xFF000000, 0xFF000000, 0xFF000000,
0xFFFFC0B8, 0xFF6878DB, 0xFF9361FF, 0xFFC24FFF, 0xFFEA49DB, 0xFFFF4F99, 0xFFFF634E, 0xFFE77808, 0xFFBC8F00, 0xFF8DA000, 0xFF65A708, 0xFF4DA04A, 0xFF4C8D95, 0xFF4F2F2B, 0xFF000000, 0xFF000000,
0xFFFFC0B8, 0xFFC1A2C6, 0xFFD399D6, 0xFFE792D6, 0xFFF78FC6, 0xFFFF92AB, 0xFFFF9A8C, 0xFFF6A26F, 0xFFE4AC5F, 0xFFD1B35F, 0xFFC0B66F, 0xFFB7B38B, 0xFFB6ABA9, 0xFFB7857E, 0xFF000000, 0xFF000000,
// emphasize green:
0xFF395D2C, 0xFF002452, 0xFF000D6A, 0xFF140064, 0xFF2D0041, 0xFF3E0010, 0xFF3F0300, 0xFF301800, 0xFF162F00, 0xFF004200, 0xFF004C00, 0xFF004700, 0xFF003924, 0xFF000000, 0xFF000000, 0xFF000000,
0xFF71A360, 0xFF005691, 0xFF1939B1, 0xFF4020A9, 0xFF61127B, 0xFF78183A, 0xFF792C00, 0xFF654800, 0xFF426600, 0xFF1B7E00, 0xFF008D00, 0xFF00860A, 0xFF007254, 0xFF000000, 0xFF000000, 0xFF000000,
0xFFAEF099, 0xFF32A3CB, 0xFF5684EB, 0xFF7E6BE3, 0xFF9E5DB5, 0xFFB66472, 0xFFB77728, 0xFFA39400, 0xFF7FB200, 0xFF57CB00, 0xFF37D900, 0xFF1FD342, 0xFF1EBF8D, 0xFF27471C, 0xFF000000, 0xFF000000,
0xFFAEF099, 0xFF7BD0AD, 0xFF8AC3BA, 0xFF9AB9B7, 0xFFA8B3A4, 0xFFB1B689, 0xFFB2BE6A, 0xFFAACA50, 0xFF9BD643, 0xFF8BE146, 0xFF7DE65A, 0xFF74E475, 0xFF73DC94, 0xFF77AA65, 0xFF000000, 0xFF000000,
// emphasize red + green:
0xFF3F3F25, 0xFF000B46, 0xFF00005D, 0xFF18005A, 0xFF2F003F, 0xFF40000E, 0xFF410000, 0xFF320A00, 0xFF191A00, 0xFF002800, 0xFF002F00, 0xFF002A00, 0xFF001B1C, 0xFF000000, 0xFF000000, 0xFF000000,
0xFF797A55, 0xFF003581, 0xFF201F9F, 0xFF450D9C, 0xFF640478, 0xFF7B0A36, 0xFF7C1E00, 0xFF683200, 0xFF474900, 0xFF225B00, 0xFF036400, 0xFF005D00, 0xFF004A4A, 0xFF000000, 0xFF000000, 0xFF000000,
0xFFBABB8B, 0xFF3E75B7, 0xFF605ED6, 0xFF854CD2, 0xFFA443AE, 0xFFBB4A6C, 0xFFBD5D21, 0xFFA87200, 0xFF878900, 0xFF619B00, 0xFF42A400, 0xFF2B9D34, 0xFF2A8A7F, 0xFF2C2D15, 0xFF000000, 0xFF000000,
0xFFBABB8B, 0xFF879E9D, 0xFF9595AA, 0xFFA48DA8, 0xFFB18999, 0xFFBB8C7E, 0xFFBB945F, 0xFFB39D48, 0xFFA5A63B, 0xFF96AE3D, 0xFF89B14C, 0xFF7FAF67, 0xFF7FA686, 0xFF80805A, 0xFF000000, 0xFF000000,
// emphasize blue:
0xFF47477C, 0xFF001A8C, 0xFF0B0AA9, 0xFF2900A3, 0xFF410081, 0xFF4D004A, 0xFF49000D, 0xFF340400, 0xFF141500, 0xFF002800, 0xFF003300, 0xFF00331B, 0xFF002A58, 0xFF000000, 0xFF00000A, 0xFF00000A,
0xFF8584CD, 0xFF0B49E2, 0xFF3533FF, 0xFF5D1AFF, 0xFF7D0CD4, 0xFF8D0B8B, 0xFF86173A, 0xFF6B2C00, 0xFF414200, 0xFF195B00, 0xFF006904, 0xFF006A4C, 0xFF005E9E, 0xFF00000A, 0xFF00000A, 0xFF00000A,
0xFFC9C8FF, 0xFF4E8CFF, 0xFF7876FF, 0xFFA05CFF, 0xFFC14EFF, 0xFFD14DE4, 0xFFCB5A92, 0xFFAF6E4C, 0xFF848525, 0xFF5C9E2D, 0xFF3BAD5B, 0xFF2BADA5, 0xFF32A1F7, 0xFF343362, 0xFF00000A, 0xFF00000A,
0xFFC9C8FF, 0xFF96AFFF, 0xFFA8A6FF, 0xFFB89BFF, 0xFFC696FF, 0xFFCC95FF, 0xFFCA9AEA, 0xFFBEA3CD, 0xFFACACBD, 0xFF9CB7C0, 0xFF8FBDD3, 0xFF88BDF2, 0xFF8BB8FF, 0xFF8B8AD6, 0xFF00000A, 0xFF00000A,
// emphasize red + blue:
0xFF46344C, 0xFF00085C, 0xFF0B007A, 0xFF260077, 0xFF3D005C, 0xFF4A0030, 0xFF480000, 0xFF340000, 0xFF140F00, 0xFF001D00, 0xFF002400, 0xFF002200, 0xFF001829, 0xFF000000, 0xFF000000, 0xFF000000,
0xFF846B8C, 0xFF0A30A1, 0xFF3419C8, 0xFF5907C5, 0xFF7800A1, 0xFF880166, 0xFF860E23, 0xFF6B2300, 0xFF403900, 0xFF1C4C00, 0xFF005400, 0xFF00521A, 0xFF00445C, 0xFF000000, 0xFF000000, 0xFF000000,
0xFFC7A7D2, 0xFF4C6BE8, 0xFF7754FF, 0xFF9C42FF, 0xFFBB39E7, 0xFFCC3CAB, 0xFFCA4968, 0xFFAE5E23, 0xFF837500, 0xFF5E8700, 0xFF3F9023, 0xFF2E8E5F, 0xFF3080A2, 0xFF332338, 0xFF000000, 0xFF000000,
0xFFC7A7D2, 0xFF948EDB, 0xFFA685EB, 0xFFB57DEA, 0xFFC27ADB, 0xFFC97BC2, 0xFFC880A7, 0xFFBD898A, 0xFFAB927A, 0xFF9C9A7B, 0xFF8F9D8A, 0xFF889CA3, 0xFF8997BE, 0xFF8A7093, 0xFF000000, 0xFF000000,
// emphasize green + blue:
0xFF304144, 0xFF00155A, 0xFF000471, 0xFF11006B, 0xFF2A0049, 0xFF36001C, 0xFF350000, 0xFF250300, 0xFF0C1300, 0xFF002600, 0xFF003100, 0xFF002F00, 0xFF002531, 0xFF000000, 0xFF000000, 0xFF000000,
0xFF647D80, 0xFF00429E, 0xFF152CBC, 0xFF3C13B4, 0xFF5C0586, 0xFF6D074B, 0xFF6B1509, 0xFF572900, 0xFF364000, 0xFF0E5900, 0xFF006700, 0xFF006424, 0xFF005766, 0xFF000000, 0xFF000000, 0xFF000000,
0xFF9EBEC3, 0xFF2D83E1, 0xFF4E6CFF, 0xFF7653F8, 0xFF9745C9, 0xFFA7478D, 0xFFA5554A, 0xFF916A12, 0xFF6F8100, 0xFF479A00, 0xFF27A82A, 0xFF16A566, 0xFF1898A9, 0xFF1F2E30, 0xFF000000, 0xFF000000,
0xFF9EBEC3, 0xFF6FA6CF, 0xFF7D9CDC, 0xFF8E92D8, 0xFF9B8CC5, 0xFFA28DAD, 0xFFA19391, 0xFF999C7A, 0xFF8BA56D, 0xFF7AAF70, 0xFF6DB584, 0xFF66B49C, 0xFF67AEB8, 0xFF6A8386, 0xFF000000, 0xFF000000,
// emphasize red + green + blue:
0xFF343434, 0xFF00084B, 0xFF000061, 0xFF14005F, 0xFF2B0044, 0xFF380017, 0xFF360000, 0xFF270000, 0xFF0E0F00, 0xFF001D00, 0xFF002400, 0xFF002200, 0xFF001721, 0xFF000000, 0xFF000000, 0xFF000000,
0xFF6A6A6A, 0xFF003088, 0xFF1B19A7, 0xFF4007A3, 0xFF5F007F, 0xFF6F0144, 0xFF6D0E02, 0xFF592300, 0xFF383900, 0xFF134B00, 0xFF005400, 0xFF00520F, 0xFF004451, 0xFF000000, 0xFF000000, 0xFF000000,
0xFFA6A6A6, 0xFF356BC5, 0xFF5654E3, 0xFF7B42E0, 0xFF9B39BB, 0xFFAB3C80, 0xFFA9493D, 0xFF955E04, 0xFF737500, 0xFF4E8700, 0xFF2F900E, 0xFF1E8E4A, 0xFF20808D, 0xFF232323, 0xFF000000, 0xFF000000,
0xFFA6A6A6, 0xFF788EB3, 0xFF8585C0, 0xFF957DBE, 0xFFA279AF, 0xFFA87A96, 0xFFA8807B, 0xFF9F8964, 0xFF919257, 0xFF829A59, 0xFF759D68, 0xFF6E9C80, 0xFF6F979C, 0xFF707070, 0xFF000000, 0xFF000000,
// colorburst
0xFF010900
};
int chosenColor; // During screen rendering, this value is the index into the color array.
public DirectBitmap Screen = new DirectBitmap(256, 240); // This uses a class called "DirectBitmap". It's pretty much just the same as Bitmap, but I don't need to unlock/lock the bits, so it's faster.
public DirectBitmap NTSCScreen = new DirectBitmap(256 * 8, 240); // This uses a class called "DirectBitmap". It's pretty much just the same as Bitmap, but I don't need to unlock/lock the bits, so it's faster.
public DirectBitmap BorderedScreen = new DirectBitmap(341, 262); // This uses a class called "DirectBitmap". It's pretty much just the same as Bitmap, but I don't need to unlock/lock the bits, so it's faster.
public DirectBitmap BorderedNTSCScreen = new DirectBitmap(341 * 8, 262); // This uses a class called "DirectBitmap". It's pretty much just the same as Bitmap, but I don't need to unlock/lock the bits, so it's faster.
//Debugging
public bool Logging; // If set, the tracelogger will record all instructions ran.
public bool LoggingPPU;
public StringBuilder DebugLog; // This is where the tracelogger is recording.
public Emulator() // The instantiator for this class
{
RAM = new byte[0x800];
A = 0; // The A, X, and Y registers are all initialized with 0 when the console boots up.
X = 0;
Y = 0;
VRAM = new byte[0x800];
OAM = new byte[0x100];
OAM2 = new byte[32];
for (int oam2_init = 0; oam2_init < OAM2.Length; oam2_init++)
{
OAM2[oam2_init] = 0xFF;
}
// set up RAM and PPU RAM Pattern
int i = 0;
while (i < 0x800)
{
int j = i & 0x2;
bool swap = (i & 0x1F) >= 0x10;
if (j < 0x2 == !swap)
{
VRAM[i] = 0xF0;
RAM[i] = 0xF0;
}
else
{
VRAM[i] = 0x0F;
RAM[i] = 0x0F;
}
i++;
}
bool BlarggPalette = false; // There's a PPU test cartridge that expects a very specific palette when you power on the console.
if (BlarggPalette)
{
//use the palette that Blargg's NES uses
PaletteRAM[0x00] = 0x09;
PaletteRAM[0x01] = 0x01;
PaletteRAM[0x02] = 0x00;
PaletteRAM[0x03] = 0x01;
PaletteRAM[0x04] = 0x00;
PaletteRAM[0x05] = 0x02;
PaletteRAM[0x06] = 0x02;
PaletteRAM[0x07] = 0x0D;
PaletteRAM[0x08] = 0x08;
PaletteRAM[0x09] = 0x10;
PaletteRAM[0x0A] = 0x08;
PaletteRAM[0x0B] = 0x24;
PaletteRAM[0x0C] = 0x00;
PaletteRAM[0x0D] = 0x00;
PaletteRAM[0x0E] = 0x04;
PaletteRAM[0x0F] = 0x2C;
PaletteRAM[0x10] = 0x09;
PaletteRAM[0x11] = 0x01;
PaletteRAM[0x12] = 0x34;
PaletteRAM[0x13] = 0x03;
PaletteRAM[0x14] = 0x00;
PaletteRAM[0x15] = 0x04;
PaletteRAM[0x16] = 0x00;
PaletteRAM[0x17] = 0x14;
PaletteRAM[0x18] = 0x08;
PaletteRAM[0x19] = 0x3A;
PaletteRAM[0x1A] = 0x00;
PaletteRAM[0x1B] = 0x02;
PaletteRAM[0x1C] = 0x00;
PaletteRAM[0x1D] = 0x20;
PaletteRAM[0x1E] = 0x2C;
PaletteRAM[0x1F] = 0x08;
}
else // Except my actual console has a different palette than Blargg, so I use this palette instead.
{
// use the palette that my NES uses
PaletteRAM[0x00] = 0x00;
PaletteRAM[0x01] = 0x00;
PaletteRAM[0x02] = 0x28;
PaletteRAM[0x03] = 0x00;
PaletteRAM[0x04] = 0x00;
PaletteRAM[0x05] = 0x08;
PaletteRAM[0x06] = 0x00;
PaletteRAM[0x07] = 0x00;
PaletteRAM[0x08] = 0x00;
PaletteRAM[0x09] = 0x01;
PaletteRAM[0x0A] = 0x01;
PaletteRAM[0x0B] = 0x20;
PaletteRAM[0x0C] = 0x00;
PaletteRAM[0x0D] = 0x08;
PaletteRAM[0x0E] = 0x00;
PaletteRAM[0x0F] = 0x02;
PaletteRAM[0x10] = 0x00;
PaletteRAM[0x11] = 0x00;
PaletteRAM[0x12] = 0x00;
PaletteRAM[0x13] = 0x00;
PaletteRAM[0x14] = 0x00;
PaletteRAM[0x15] = 0x02;
PaletteRAM[0x16] = 0x21;
PaletteRAM[0x17] = 0x00;
PaletteRAM[0x18] = 0x00;
PaletteRAM[0x19] = 0x00;
PaletteRAM[0x1A] = 0x00;
PaletteRAM[0x1B] = 0x00;
PaletteRAM[0x1C] = 0x00;
PaletteRAM[0x1D] = 0x10;
PaletteRAM[0x1E] = 0x00;
PaletteRAM[0x1F] = 0x00;
}
programCounter = 0xFFFF; // Technically, this value is nondeterministic. It also doesn't matter where it is, as it will be initialized in the RESET instruction.
PPU_Scanline = 0; // The PPU begins on dot 0 of scanline 0
PPU_Dot = 7; // Shouldn't this be 0? I don't know why, but this passes all the tests if this is 7, so...?
PPU_OddFrame = true; // And this is technically considered an "odd" frame when it comes to even/odd frame timing.
APU_DMC_SampleAddress = 0xC000;
APU_DMC_AddressCounter = 0xC000;
APU_DMC_SampleLength = 1;
APU_DMC_ShifterBitsRemaining = 8;
switch (APUAlignment & 4)
{
default:
case 0:
{
APU_ChannelTimer_DMC = 1022;
APU_PutCycle = true;
}
break;
case 1:
{
APU_ChannelTimer_DMC = 1022;
APU_PutCycle = false;
}
break;
case 2:
{
APU_ChannelTimer_DMC = 1020;
APU_PutCycle = true;
}
break;
case 3:
{
APU_ChannelTimer_DMC = 1020;
APU_PutCycle = false;
}
break;
}
DoReset = true; // This is used to force the first instruction at power on to be the RESET instruction.
PPU_RESET = false; // I'm not even 100% certain my console has this behavior. I'll set it to false for now.
}
public bool PPU_RESET;
// when pressing the reset button, this function runs
public void Reset()
{
// The A, X, and Y registers are unchanged through reset.
// most flags go unchanged as well, but the I flag is set to 1
flag_Interrupt = true;
// Triangle phase gets reset, though I'm not yet emulating audio.
APU_DMC_Output &= 1;
// All the bits of $4015 are cleared
APU_Status_DMCInterrupt = false;
APU_Status_FrameInterrupt = false;
APU_Status_DelayedDMC = false;
APU_Status_DMC = false;
APU_Status_Noise = false;
APU_Status_Triangle = false;
APU_Status_Pulse2 = false;
APU_Status_Pulse1 = false;
APU_DMC_BytesRemaining = 0;
APU_LengthCounter_Noise = 0;
APU_LengthCounter_Triangle = 0;
APU_LengthCounter_Pulse2 = 0;
APU_LengthCounter_Pulse1 = 0;
APU_Framecounter = 0; // reset the frame counter
// PPU registers
PPUControl_NMIEnabled = false;
PPUControlIncrementMode32 = false;
PPU_Spritex16 = false;
PPU_PatternSelect_Sprites = false;
PPU_PatternSelect_Background = false;
PPU_t = 0;
PPU_Mask_Greyscale = false;
PPU_Mask_EmphasizeRed = false;
PPU_Mask_EmphasizeGreen = false;
PPU_Mask_EmphasizeBlue = false;
PPU_Mask_8PxShowBackground = false;
PPU_Mask_8PxShowSprites = false;
PPU_Mask_ShowBackground = false;
PPU_Mask_ShowSprites = false;
PPU_Update2005Delay = 0;
PPU_FineXScroll = 0;
//$2006 is unchanged
PPU_ReadBuffer = 0;
PPU_OddFrame = false;
PPU_Dot = 0;
PPU_Scanline = 0;
DoDMCDMA = false;
DoOAMDMA = false;
operationCycle = 0;
switch (APUAlignment & 4)
{
default:
case 0:
{
APU_ChannelTimer_DMC = 1022;
APU_PutCycle = true;
}
break;
case 1:
{
APU_ChannelTimer_DMC = 1022;
APU_PutCycle = false;
}
break;
case 2:
{
APU_ChannelTimer_DMC = 1020;
APU_PutCycle = true;
}
break;
case 3:
{
APU_ChannelTimer_DMC = 1020;
APU_PutCycle = false;
}
break;
}
DoReset = true;
PPU_RESET = false; // I'm not even 100% certain my console has this behavior. I'll set it to false for now.
// in theory, the CPU/PPU clock would be given random values. Let's just assume no changes.
}
public bool CPU_Read; // DMC DMA Has some specific behavior depending on if the CPU is currently reading or writing. DMA Halting fails / DMA $2007 bug.
// The BRK instruction is reused in the IRQ, NMI, and RESET logic. These bools are used both to start the instruction, and also to make sure the correct logic is used.
public bool DoBRK; // Set if the opcode is 00
public bool DoNMI; // Set if a Non Maskable Interrupt is occurring
public bool DoIRQ; // Set if an Interrupt Request is occurring
public bool DoReset; // Set when resetting the console, or power on.
public bool DoOAMDMA; // If set, the Object Acctribute Memory's Direct Memory Access will occur.
public bool FirstCycleOfOAMDMA; // The first cycle caa behave differently.
public bool DoDMCDMA; // If set, the Delta Modulation Channel's Direct Memory Access will occur.
public byte DMCDMADelay; // There's actually a slight delay between the audio chip preparing the DMA, and the CPU actually running it.
public byte CannotRunDMCDMARightNow = 0;
public byte DMAPage; // When running an OAM DMA, this is used to determine which "page" to read bytes from. Typically, this is page 2 (address $200 through $2FF)
public byte DMAAddress; // While this DMA runs, this value is incremented until it overflows.
public bool FrameAdvance_ReachedVBlank; // For debugging. If frame advancing, this is set when VBlank occurs.
public bool APU_ControllerPortsStrobing; // Set to true/false depending on the value written to $4016. When true, the buttons pressed are recorded in the shift registers.
public bool APU_ControllerPortsStrobed; // This bool prevents strobing from rushing through the TAS input log.
// This gets set to false if the controllers are unstrobed, or if the controller ports are read.
public byte ControllerPort1; // The buttons currently pressed on controller 1. These are in the "A, B, Select, Start, Up, Down, Left, Right" order.
public byte ControllerPort2; // The buttons currently pressed on controller 2. These are in the "A, B, Select, Start, Up, Down, Left, Right" order.
public byte ControllerShiftRegister1; // Controllers are read 1 bit at a time. First the A Button is read, then B, and so on.
public byte ControllerShiftRegister2; // Whenever the shift register is read, all the bits are shifted to the left, and a '1' replaces bit 0.
public byte Controller1ShiftCounter; // Subsequent CPU cycles reading from $4016 do not update the shift register.
public byte Controller2ShiftCounter; // Subsequent CPU cycles reading from $4017 do not update the shift register.
public bool LagFrame; // True if the controller port was not strobed in a frame.
public bool TASTimelineClockFiltering; // Primarily used in the TASTimeline if you are using subframe Inputs.
public void _CoreFrameAdvance()
{
// If we're running this emulator 1 frame at a time, this waits until VBlank and then returns.
FrameAdvance_ReachedVBlank = false;
LagFrame = true; // Many emulators detect "lag frames" by checking if the controller ports were strobed during this frame.
while (!FrameAdvance_ReachedVBlank)
{
_EmulatorCore();
}
}
public int CycleCountForCycleTAS = 0; // If we're running a intercycle cart swapping TAS, we need to keep track of which cycle we're on.
public void _CoreCycleAdvance()
{
// this runs 12 master clock cycles, or 1 CPU cycle.
int i = 0;
while (i < 12)
{
_EmulatorCore();
i++;
}
CycleCountForCycleTAS++;
}
public void _EmulatorCore()
{
// counters count down to 0, run the appropriate chip's logic, and the counter is reset.
// If multiple counters read 0 at the same time, there's an order of events.
// The order of events:
// CPU
// PPU
// APU
if (CPUClock == 12)
{
CPUClock = 0; // there is 1 CPU cycle for every 12 master clock cycles
_6502(); // This is where I run the CPU
totalCycles++; // for debugging mostly
Cart.MapperChip.CPUClock(); // If the mapper chip does every cpu cycle... (see FME-7)
}
if (CPUClock == 4)
{
NMILine |= PPUControl_NMIEnabled && PPUStatus_VBlank;
if (operationCycle == 0 && !(PPUStatus_VBlank && PPUControl_NMIEnabled))
{
NMILine = false;
}
}
if (CPUClock == 7) //M2 going low.
{
IRQLine = IRQ_LevelDetector;
if (APU_Status_FrameInterrupt && !APU_FrameCounterInhibitIRQ)
{
IRQ_LevelDetector = true; // if the APU frame counter flag is never cleared, you will get another IRQ when the I flag is cleared.
}
Cart.MapperChip.CPUClockRise(); // If the mapper chip does something when M2 rises... (see MMC3)
}
if (PPUClock == 4)
{
PPUClock = 0; // there is 1 PPU cycle for every 12 master clock cycles
_EmulatePPU();
if (PPUBus != 0)
{
DecayPPUDataBus();
}
}
if (PPUClock == 2)
{
_EmulateHalfPPU();
}
if (CPUClock == 0)
{
_EmulateAPU();
APU_PutCycle = !APU_PutCycle;
// the APU is actually clocked every 24 master clock cycles.
// yet there's a lot of timing that happens every cpu cycle anyway??
// If the timing needs to be exactly n and a half APU cycles, then I'll just multiply the numbers by 2 and clock this twice as fast.
}
// Decrement the clocks.
PPUClock++;
CPUClock++;
if(Cart.FDS != null)
{
Cart.FDS.Clock();
}
}
public void EmulateUntilEndOfRead()
{
// this is used during reads from some ppu registers.
// run 1.75 ppu cycles. (the actual duty cycle here would result in 1 and 7/8 ppu cycles, but my emulator doesn't worry about half-master-clock-cycles.
for (int i = 0; i < 7; i++)
{
_EmulatorCore();
}
}
public void EmulateNMasterClockCycles(int n)
{
// This does run the risk of recursion, so don't use a value of 12 or more with this.
for (int i = 0; i < n; i++)
{
_EmulatorCore();
}
}
// Audio Processing Unit Variables //
// APU Status is at address $4015
public bool APU_Status_DMCInterrupt; // Bit 7 of $4015
public bool APU_Status_FrameInterrupt;// Bit 6 of $4015
public bool APU_Status_DMC; // Bit 5 of $4015
public bool APU_Status_DelayedDMC; // Bit 5 of $4015, but with a slight delay.
public bool APU_Status_Noise; // Bit 3 of $4015
public bool APU_Status_Triangle; // Bit 2 of $4015
public bool APU_Status_Pulse2; // Bit 1 of $4015
public bool APU_Status_Pulse1; // Bit 0 of $4015
public bool Clearing_APU_FrameInterrupt;
public byte APU_DelayedDMC4015; // When writing to $4015, there's a 3 or 4 cycle delay between the APU actually changing this value.
public bool APU_ImplicitAbortDMC4015; // An edge case of the DMC DMA, where regardless of the buffer being empty, there will be a 1-cycle DMA that gets aborted 2 cycles after the load DMA ends
public bool APU_SetImplicitAbortDMC4015;// This is used to make that happen.
public byte[] APU_Register = new byte[0x10]; // Instead of making a series of variables, I made an array here for some reason.
public bool APU_FrameCounterMode; // Bit 7 of $4017 : Determines if the APU frame counter is using the 4 step or 5 step modes.
public bool APU_FrameCounterInhibitIRQ; // Bit 6 of $4017 : If set, prevents the APU from creating IRQ's
public byte APU_FrameCounterReset = 0xFF; // When resetting the APU Frame counter by writing to address $4017, there's a 3 (or 4) CPU cycle delay. (3 if it's an even cpu cycle, 4 if odd.)
public ushort APU_Framecounter = 0; // Increments every APU cycle. Since there are events that happen at half-step intervals, I actually increment this every CPU cycle and multiplied all intervals by 2.
public bool APU_QuarterFrameClock = false;// This is clocked approximately 4 times a frame, depending on the frame counter mode.
public bool APU_HalfFrameClock = false; // This is clocked approximately twice a frame, depending on the frame counter mode.
public bool APU_Envelope_StartFlag = false;
public bool APU_Envelope_DividerClock = false;
public byte APU_Envelope_DecayLevel = 0;
public byte APU_LengthCounter_Pulse1 = 0; // The length counter for the APU's Pulse 1 channel.
public byte APU_LengthCounter_Pulse2 = 0; // The length counter for the APU's Pulse 2 channel.
public byte APU_LengthCounter_Triangle = 0; // The length counter for the APU's Triangle channel.
public byte APU_LengthCounter_Noise = 0; // The length counter for the APU's Noise channel.
// When a length counter's reloaded value is set by writing to $4003, $4007, $400B, or $400F, this LookUp Table is used to determine the length based on the value written.
public static readonly byte[] APU_LengthCounterLUT = { 10, 254, 20, 2, 40, 4, 80, 6, 160, 8, 60, 10, 14, 12, 26, 14, 12, 16, 24, 18, 48, 20, 96, 22, 192, 24, 72, 26, 16, 28, 32, 30 };
public bool APU_LengthCounter_HaltPulse1 = false; // set if Bit 5 of $4000 is 1
public bool APU_LengthCounter_HaltPulse2 = false; // set if Bit 5 of $4004 is 1
public bool APU_LengthCounter_HaltTriangle = false; // set if Bit 7 of $4008 is 1
public bool APU_LengthCounter_HaltNoise = false; // set if Bit 5 of $400C is 1
public bool APU_LengthCounter_ReloadPulse1 = false; // When writing to $4003 (if the pulse 1 channel is enabled) this is set to true. The value is reloaded in the next APU cycle.
public bool APU_LengthCounter_ReloadPulse2 = false; // When writing to $4007 (if the pulse 2 channel is enabled) this is set to true. The value is reloaded in the next APU cycle.
public bool APU_LengthCounter_ReloadTriangle = false;// When writing to $400B (if the triangle channel is enabled) this is set to true. The value is reloaded in the next APU cycle.
public bool APU_LengthCounter_ReloadNoise = false; // When writing to $400F (if the noise channel is enabled) this is set to true. The value is reloaded in the next APU cycle.
public byte APU_LengthCounter_ReloadValuePulse1 = 0; // When the pulse 1 channel is reloaded, the length counter will be set to this value. Modified by writing to $4003.
public byte APU_LengthCounter_ReloadValuePulse2 = 0; // When the pulse 2 channel is reloaded, the length counter will be set to this value. Modified by writing to $4007.
public byte APU_LengthCounter_ReloadValueTriangle = 0;// When the triangle channel is reloaded, the length counter will be set to this value. Modified by writing to $400B.
public byte APU_LengthCounter_ReloadValueNoise = 0; // When the noise channel is reloaded, the length counter will be set to this value. Modified by writing to $400F.
public ushort APU_ChannelTimer_Pulse1 = 0; // Decrements every "get" cycle.
public ushort APU_ChannelTimer_Pulse2 = 0; // Decrements every "get" cycle.
public ushort APU_ChannelTimer_Triangle = 0;// Decrements every CPU cycle.
public ushort APU_ChannelTimer_Noise = 0; // Decrements every "get" cycle.
public ushort APU_ChannelTimer_DMC = 0; // Decrements every CPU cycle.
// $4010
public bool APU_DMC_EnableIRQ = false; // Will the DMC create IRQ's? Set by writing to address $4010
public bool APU_DMC_Loop = false; // Will DPCM samples loop?
public ushort APU_DMC_Rate = 428; // The default sample rate is the slowest.
// LookUp Table for how many CPU cycles are between each bit of the DPCM sample being played. (8 bits per byte, so to calculate how many cycles there are between each DMA, multiply these numbers by 8)
public static readonly ushort[] APU_DMCRateLUT = { 428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54 };
// $4011 (and DPCM stuff)
public byte APU_DMC_Output; // Directly writing here (Address $4011) will set the DMC output. This is how you play PCM audio.
// $4012
public ushort APU_DMC_SampleAddress = 0xC000; // Where the DPCM sample is being read from.
// $4013
public ushort APU_DMC_SampleLength = 0; // How many bytes are being played in this DPCM sample? (multiplied by 64, and add 1)
public ushort APU_DMC_BytesRemaining = 0; // How many bytes are left in the sample. When a sample starts or loops, this is set to APU_DMC_SampleLength.
public byte APU_DMC_Buffer = 0; // The value that goes into the shift register.
public ushort APU_DMC_AddressCounter = 0xC000; // What byte is fetched in the next DMA for DPCM audio? When a sample starts or loops, this is set to APU_DMC_SampleAddress.
public byte APU_DMC_Shifter = 0; // The 8 bits of the sample that were fetched from the DMA.
public byte APU_DMC_ShifterBitsRemaining = 8; // This tracks how many bits are left before needing to run another DMA
public bool DPCM_Up; // If the next bit of the DPCM sample is a 1, the output goes up. Otherwise it goes down.
public bool APU_Silent = true; // If the APU is not making any noise, this is set.
void _EmulateAPU()
{
// This runs every 12 master clock cycles, though has different logic for even/odd CPU cycles.
if (!APU_ControllerPortsStrobing)
{
if (Controller1ShiftCounter > 0)
{
Controller1ShiftCounter--;
if (Controller1ShiftCounter == 0)
{
ControllerShiftRegister1 <<= 1;
ControllerShiftRegister1 |= 1;
}
}
if (Controller2ShiftCounter > 0)
{
Controller2ShiftCounter--;
if (Controller2ShiftCounter == 0)
{
ControllerShiftRegister2 <<= 1;
ControllerShiftRegister2 |= 1;
}
}
}
else
{
Controller1ShiftCounter = 0;
Controller2ShiftCounter = 0;
}
if (!APU_PutCycle)
{
// If this is a get cycle, transitioning to a put cycle.
// controller reading is handled here in the APU chip.
// If a 1 was written to $4016, we are strobing the controller.
if (APU_ControllerPortsStrobing)
{
if (!APU_ControllerPortsStrobed)
{
LagFrame = false;
APU_ControllerPortsStrobed = true;
if (TASTimelineClockFiltering)
{
FrameAdvance_ReachedVBlank = true; // Obviously this isn't actually VBlank, but we want to stop emulating here anyway.
}
// this will be reset to false if:
// 1.) the controllers are un-strobed. Ready for the next strobe.
// 2.) the controller ports are read, while still strobed. This allows data to be streamed in through the A button.
if (TAS_ReadingTAS) // This is specifically how I load inputs from a TAS, and has nothing to do with actual NES behavior.
{
if (TAS_InputSequenceIndex < TAS_InputLog.Length)
{
ControllerPort1 = (byte)(TAS_InputLog[TAS_InputSequenceIndex] & 0xFF);
ControllerPort2 = (byte)((TAS_InputLog[TAS_InputSequenceIndex] & 0xFF00) >> 8);
}
else // if the TAS has ended, only provide 0 as the inputs.
{
ControllerPort1 = 0;
ControllerPort2 = 0;
}
if (ClockFiltering)
{
if (TAS_InputSequenceIndex > 0 && TAS_InputSequenceIndex < TAS_ResetLog.Length && TAS_ResetLog[TAS_InputSequenceIndex])
{
Reset();
}
TAS_InputSequenceIndex++; // Instead of using 1 input per frame, this just advances to the next input
}
}
// this sets up the shift registers with the value of the controller ports.
// If not set by the TAS, these are probably set outside this script in the script for the form.
ControllerShiftRegister1 = ControllerPort1;
ControllerShiftRegister2 = ControllerPort2;
}
}
else
{
APU_ControllerPortsStrobed = false;
}
// clock timers
APU_ChannelTimer_Pulse1--; // every APU GET cycle.
APU_ChannelTimer_Pulse2--;
APU_ChannelTimer_Noise--;
//this happens whether a sample is playing or not
APU_ChannelTimer_DMC--;
APU_ChannelTimer_DMC--; // the table is in CPU cycles, but the count is in APU cycles
if (APU_ChannelTimer_DMC == 0)
{
APU_ChannelTimer_DMC = APU_DMC_Rate;
DPCM_Up = (APU_DMC_Shifter & 1) == 1;
if (DPCM_Up)
{
if (APU_DMC_Output <= 125) // this is 7 bit, and cannot go above 127
{
APU_DMC_Output += 2;
}
}
else
{
if (APU_DMC_Output >= 2) // this is 7 bit, and cannot go below 0
{
APU_DMC_Output -= 2;
}
}
APU_DMC_Shifter >>= 1; // shift the bits in the shift register
APU_DMC_ShifterBitsRemaining--; // and decrement the "bits remaining" counter.
if (APU_DMC_ShifterBitsRemaining == 0) // If there are no bits left,
{
APU_DMC_ShifterBitsRemaining = 8; // it's time for a DMC DMA!
if (APU_DMC_BytesRemaining > 0 || APU_SetImplicitAbortDMC4015)
{
if (!DoDMCDMA && CannotRunDMCDMARightNow != 2)
{
// if playing a sample:
DoDMCDMA = true;
DMCDMA_Halt = true;
}
if (APU_SetImplicitAbortDMC4015)
{
APU_ImplicitAbortDMC4015 = true; // check for weird DMA abort behavior
APU_SetImplicitAbortDMC4015 = false;
}
APU_DMC_Shifter = APU_DMC_Buffer; // and set up the shifter with the new values.
APU_Silent = false; // The APU is not silent.
}
else
{
APU_Silent = true;
}
}
}
if (CannotRunDMCDMARightNow > 0)
{
CannotRunDMCDMARightNow -= 2;
}
}
else
{
// If this is a put cycle, transitioning to a get cycle.
if (Clearing_APU_FrameInterrupt)
{
Clearing_APU_FrameInterrupt = false;
APU_Status_FrameInterrupt = false;
IRQ_LevelDetector = false;
}
// DMC load from 4015
if (DMCDMADelay > 0)
{
DMCDMADelay--; // there's a small delay beetween the write occurring and the DMA beginning
if (DMCDMADelay == 0 && !DoDMCDMA) // if the DMA is already happening because of the timer
{
DoDMCDMA = true;
DMCDMA_Halt = true;
APU_DMC_Shifter = APU_DMC_Buffer;
APU_Silent = false;
}
}
}
if (APU_DelayedDMC4015 > 0)
{
APU_DelayedDMC4015--;
if (APU_DelayedDMC4015 == 0)
{
APU_Status_DMC = APU_Status_DelayedDMC;
if (!APU_Status_DMC)
{
APU_DMC_BytesRemaining = 0;
}
}
}
APU_ChannelTimer_Triangle--; // every CPU cycle.
// clock sequencer
if ((APU_FrameCounterReset & 0x80) == 0)
{
APU_FrameCounterReset--;
if ((APU_FrameCounterReset & 0x80) != 0)
{
APU_Framecounter = 0;
}
}
APU_Framecounter++;
// We're clocking the APU twice as fast in order to get the frame counter timing to allow the 'half APU cycle' timing.
// these numbers are just multiplied by 2.
if (APU_FrameCounterMode)
{
// 5 step
switch (APU_Framecounter)
{
default: break;
case 7457:
APU_QuarterFrameClock = true;
break;
case 14913:
APU_QuarterFrameClock = true;
APU_HalfFrameClock = true;
break;
case 22371:
APU_QuarterFrameClock = true;
break;
case 29829:
break;
case 37281:
APU_QuarterFrameClock = true;
APU_HalfFrameClock = true;
break;
case 37282:
APU_Framecounter = 0;
break;
}
}
else
{
// 4 step
switch (APU_Framecounter)
{
default: break;
case 7457:
APU_QuarterFrameClock = true;
break;
case 14913:
APU_QuarterFrameClock = true;
APU_HalfFrameClock = true;
break;
case 22371:
APU_QuarterFrameClock = true;
break;
case 29828:
APU_Status_FrameInterrupt = true;
break;
case 29829:
APU_QuarterFrameClock = true;
APU_Status_FrameInterrupt = true;
IRQ_LevelDetector |= !APU_FrameCounterInhibitIRQ;
APU_HalfFrameClock = true;
break;
case 29830:
APU_Status_FrameInterrupt = !APU_FrameCounterInhibitIRQ;
IRQ_LevelDetector |= !APU_FrameCounterInhibitIRQ;
APU_Framecounter = 0;
break;
}
}
// perform quarter frame / half frame stuff
if (APU_QuarterFrameClock)
{
APU_QuarterFrameClock = false;
if (APU_Envelope_StartFlag)
{
APU_Envelope_StartFlag = false;
APU_Envelope_DecayLevel = 15;
}
else
{
APU_Envelope_DividerClock = true;
}
}
if (APU_HalfFrameClock)
{
if (APU_LengthCounter_ReloadPulse1 && APU_LengthCounter_Pulse1 == 0) { APU_LengthCounter_Pulse1 = APU_LengthCounter_ReloadValuePulse1; } else { APU_LengthCounter_ReloadPulse1 = false; }
if (APU_LengthCounter_ReloadPulse2 && APU_LengthCounter_Pulse2 == 0) { APU_LengthCounter_Pulse2 = APU_LengthCounter_ReloadValuePulse2; } else { APU_LengthCounter_ReloadPulse2 = false; }
if (APU_LengthCounter_ReloadTriangle && APU_LengthCounter_Triangle == 0) { APU_LengthCounter_Triangle = APU_LengthCounter_ReloadValueTriangle; } else { APU_LengthCounter_ReloadTriangle = false; }
if (APU_LengthCounter_ReloadNoise && APU_LengthCounter_Noise == 0) { APU_LengthCounter_Noise = APU_LengthCounter_ReloadValueNoise; } else { APU_LengthCounter_ReloadNoise = false; }
APU_HalfFrameClock = false;
// length counters and sweep
if (!APU_Status_Pulse1) { APU_LengthCounter_Pulse1 = 0; }
if (!APU_Status_Pulse2) { APU_LengthCounter_Pulse2 = 0; }
if (!APU_Status_Triangle) { APU_LengthCounter_Triangle = 0; }
if (!APU_Status_Noise) { APU_LengthCounter_Noise = 0; }
if (APU_LengthCounter_Pulse1 != 0 && !APU_LengthCounter_HaltPulse1 && !APU_LengthCounter_ReloadPulse1)
{
APU_LengthCounter_Pulse1--;
}
if (APU_LengthCounter_Pulse2 != 0 && !APU_LengthCounter_HaltPulse2 && !APU_LengthCounter_ReloadPulse2)
{
APU_LengthCounter_Pulse2--;
}
if (APU_LengthCounter_Triangle != 0 && !APU_LengthCounter_HaltTriangle && !APU_LengthCounter_ReloadTriangle)
{
APU_LengthCounter_Triangle--;
}
if (APU_LengthCounter_Noise != 0 && !APU_LengthCounter_HaltNoise && !APU_LengthCounter_ReloadNoise)
{
APU_LengthCounter_Noise--;
}
}
else
{
if (APU_LengthCounter_ReloadPulse1) { APU_LengthCounter_Pulse1 = APU_LengthCounter_ReloadValuePulse1; }
if (APU_LengthCounter_ReloadPulse2) { APU_LengthCounter_Pulse2 = APU_LengthCounter_ReloadValuePulse2; }
if (APU_LengthCounter_ReloadTriangle) { APU_LengthCounter_Triangle = APU_LengthCounter_ReloadValueTriangle; }
if (APU_LengthCounter_ReloadNoise) { APU_LengthCounter_Noise = APU_LengthCounter_ReloadValueNoise; }
APU_LengthCounter_ReloadPulse1 = false;
APU_LengthCounter_ReloadPulse2 = false;
APU_LengthCounter_ReloadTriangle = false;
APU_LengthCounter_ReloadNoise = false;
}
APU_LengthCounter_HaltPulse1 = ((APU_Register[0] & 0x20) != 0);
APU_LengthCounter_HaltPulse2 = ((APU_Register[4] & 0x20) != 0);
APU_LengthCounter_HaltTriangle = ((APU_Register[8] & 0x80) != 0);
APU_LengthCounter_HaltNoise = ((APU_Register[0xC] & 0x20) != 0);
} // and that's it for the APU cycle
// PPU variables
public byte PPUBus; // The databus of the Picture Processing Unit
public int[] PPUBusDecay = new int[8];
const int PPUBusDecayConstant = 1786830; // 20 frames. Approximately how long it takes for the PPU bus to decay on my console.
public byte PPUOAMAddress; // The address used to index into Object Attribute Memory
public bool PPUStatus_VBlank; // This is set during Vblank, and cleared at the end, or if $2002 is read. This value can be read in address $2002
public bool PPUStatus_PendingSpriteZeroHit; // If a sprite zero hit occurs, this is set. This toggles PPUStatus_SpriteZeroHit on the next half-ppu-cycle.
public bool PPUStatus_PendingSpriteZeroHit2; // Actually theres a 1.5 dot delay on this one.
public bool PPUStatus_SpriteZeroHit; // If a sprite zero hit occurs, this is set. This value can be read in address $2002
public bool PPUStatus_SpriteZeroHit_Delayed;
public bool PPUStatus_SpriteOverflow; // If a scanline had more than 8 objects in range, this is set. This value can be read in address $2002
public bool PPUStatus_SpriteOverflow_Delayed;
public bool PPU_VSET; // This line is high for half a ppu cycle at the start of scanline 241.
public bool PPU_VSET_Latch1; // A latch used in the timing for the VBlank flag.
public bool PPU_VSET_Latch2; // A latch used in the timing for the VBlank flag.
public bool PPU_Read2002; // This clears the VBlank flag.
bool PPU_Spritex16; // Are sprites using 8x8 mode, or 8x16 mode? Set by writing to $2000
public ushort PPU_Scanline; // Which scanline is the PPU currently on
public ushort PPU_Dot; // Which dot of the scanline is the PPU currently on
public bool PPU_VRegisterChangedOutOfVBlank; // when changing the v register (Read write address) out of vblank, palettes can become corrupted
public bool PPU_OAMCorruptionRenderingDisabledOutOfVBlank; // When rendering is disabled on specific dots of visible scanlines, OAM data can become corrupted
public bool PPU_PendingOAMCorruption;// The corruption doesn't take place until rendering is re-enabled.
public byte PPU_OAMCorruptionIndex; // The object that gets corrupted depends on when the data was corrupted
// OAM corruption during OAM evaluation happens with the instant write to $2001 using the databus value. Other parts of sprite evaluation apparently do not.
public bool PPU_OAMCorruptionRenderingDisabledOutOfVBlank_Instant; // When rendering is disabled on specific dots of visible scanlines, OAM data can become corrupted
public bool PPU_OAMCorruptionRenderingEnabledOutOfVBlank; // If enabling rendering outside vblank, there are alignment specific effects.
public bool PPU_OAMEvaluationCorruptionOddCycle; // If rendering is disabled during OAM evaluation, it matters if it was on an odd or even cycle.
public bool PPU_OAMEvaluationObjectInRange; // If rendering is disabled during OAM evaluation, it matters if the most recent object evaluated was in vertical range of this scanline.
public bool PPU_OAMEvaluationObjectInXRange; // If rendering is disabled during OAM evaluation, it matters if the most recent object evaluated was in vertical range of this scanline.
public bool PPU_PaletteCorruptionRenderingDisabledOutOfVBlank; // When rendering is disabled on specific dots of visible scanlines, OAM data can become corrupted
byte PPU_AttributeLatchRegister;
ushort PPU_BackgroundAttributeShiftRegisterL; // 8 bit latch for the background tile attributes low bit plane.
ushort PPU_BackgroundAttributeShiftRegisterH; // 8 bit latch register for the background tile attributes high bit plane.
ushort PPU_BackgroundPatternShiftRegisterL; // 16 bit shift register for the background tile pattern low bit plane.
ushort PPU_BackgroundPatternShiftRegisterH; // 16 bit shift register for the background tile pattern high bit plane.
//TempPPUAddr
public byte PPU_FineXScroll; // Set when writing to address $2005. 3 bits. This is up to a 7 pixel offset when rendering the screen.
byte[] PPU_SpriteShiftRegisterL = new byte[8]; // 8 bit shift register for a sprite's low bit plane. Secondary OAM can have up to 8 object in it.
byte[] PPU_SpriteShiftRegisterH = new byte[8]; // 8 bit shift register for a sprite's high bit plane. Secondary OAM can have up to 8 object in it.
byte[] PPU_SpriteAttribute = new byte[8]; // Secondary OAM attribute values. Secondary OAM can have up to 8 objects in it.
byte[] PPU_SpritePattern = new byte[8]; // Secondary OAM pattern values. Secondary OAM can have up to 8 objects in it.
byte[] PPU_SpriteXposition = new byte[8]; // Secondary OAM x positions. Secondary OAM can have up to 8 objects in it.
byte[] PPU_SpriteYposition = new byte[8]; // Secondary OAM y positions. Secondary OAM can have up to 8 objects in it.
byte[] PPU_SpriteShifterCounter = new byte[8]; // This counter tracks how long until the objects are drawn.
bool PPU_NextScanlineContainsSpriteZero; // If this upcoming scanline contains sprite zero
bool PPU_CurrentScanlineContainsSpriteZero; // if the sprite evaluation for this current scanline contained sprite zero. Used for Sprite Zero Hit detection.
public byte PPU_SpritePatternL; // Temporary value used in sprite evaluation.
public byte PPU_SpritePatternH; // Temporary value used in sprite evaluation.
bool PPU_Mask_Greyscale; // Set by writing to $2001. If set, only use color 00, 10, 20, or 30 when drawing a pixel.
bool PPU_Mask_8PxShowBackground; // Set by writing to $2001. If set, the background will be visible in the 8 left-most pixels of the screen.
bool PPU_Mask_8PxShowSprites; // Set by writing to $2001. If set, the sprites will be visible in the 8 left-most pixels of the screen.
bool PPU_Mask_ShowBackground; // Set by writing to $2001. If set, the background will be visible. Anything that requires rendering to be enabled will run, even if it doesn't involve the background.
bool PPU_Mask_ShowSprites; // Set by writing to $2001. If set, the sprites will be visible. Anything that requires rendering to be enabled will run, even if it doesn't involve sprites.
bool PPU_Mask_EmphasizeRed; // Set by writing to $2001. Adjusts the colors on screen to be a bit more red.
bool PPU_Mask_EmphasizeGreen; // Set by writing to $2001. Adjusts the colors on screen to be a bit more green.
bool PPU_Mask_EmphasizeBlue; // Set by writing to $2001. Adjusts the colors on screen to be a bit more blue.
bool PPU_Mask_ShowBackground_Delayed; // Sprite evaluation has a 1 ppu cycle delay on checking if rendering is enabled.
bool PPU_Mask_ShowSprites_Delayed; // Sprite evaluation has a 1 ppu cycle delay on checking if rendering is enabled.
bool PPU_Mask_ShowBackground_Instant; // OAM evaluation will stop immediately if writing to $2001
bool PPU_Mask_ShowSprites_Instant; // OAM evaluation will stop immediately if writing to $2001
byte PPU_LowBitPlane; // Temporary value used in background shift register preparation.
byte PPU_HighBitPlane;// Temporary value used in background shift register preparation.
byte PPU_Attribute; // Temporary value used in background shift register preparation.
public ushort PPU_PatternAddressRegister_CHR; // PAR
public ushort PPU_PatternAddressRegister_NT; // PAR
public ushort PPU_PatternAddressRegister_AT; // PAR
public ushort PPU_PAR_MUX; // PAR
bool PPU_CanDetectSpriteZeroHit; // Only 1 sprite zero hit is allowed per frame. This gets set if a sprite zero hit occurs, and cleared at the end of vblank.
public bool PPU_A12_Prev; // The MMC3 chip's IRQ counter is changed whenever bit 12 of the PPU Address is changing from a 0 to a 1. This is recorded at the start of a PPU cycle, and checked at the end.
public bool PPU_OddFrame; // Every other frame is 1 ppu cycle shorter.
public byte DotColor; // The pixel output is delayed by 2 dots.
public byte PrevDotColor; // This is the value from last cycle.
public byte PrevPrevDotColor; // And this is from 2 cycles ago.
public byte PrevPrevPrevDotColor; // And this is from 2 cycles ago.
public int PrevPrevPrevPrevDotColor; // This is used with NTSC signal decoding.
public byte PaletteRAMAddress;
public bool ThisDotReadFromPaletteRAM;
public bool NMI_PinsSignal; // I'm using this to detect the rising edge of $2000.7 and $2002.7
public bool NMI_PreviousPinsSignal; // I'm using this to detect the rising edge of $2000.7 and $2002.7
public bool IRQ_LevelDetector; // If set, it's time to run an IRQ whenever this is detected
public bool NMILine; // Set to true if $2000.7 and $2002.7 are both set. This is checked during the second half od a CPU cycle.
public bool IRQLine; // Set during phi2 to true if the IRQ level detector is low.
bool CopyV = false; // set by writes to $2006. If it occurs on the same dot the scroll values are naturally incremented, some bugs occur.
bool SkippedPreRenderDot341 = false;
void _EmulatePPU()
{
// When writing to ppu registers, there's a slight delay before resulting action is taken.
// This delay can vary depending on the CPU/PPU alignment.
// For instance, after writing to $2006, this delay value will either be 4 or 5.
CopyV = false;
if (PPU_Update2006Delay > 0)
{
PPU_Update2006Delay--; // this counts down,
if (PPU_Update2006Delay == 0) // and when it reaches zero
{
ushort temp_Prev_V = PPU_v;
CopyV = true;
PPU_v = PPU_t; // the PPU_ReadWriteAddress is updated!
PPU_AddressBus = PPU_v; // This value is the same thing.
if ((temp_Prev_V & 0x3FFF) >= 0x3F00 && (PPU_AddressBus & 0x3FFF) < 0x3F00) // Palette corruption check. Are we leaving Palette ram?
{
if ((PPU_Scanline < 240) && PPU_Dot <= 256) // if this dot is visible
{
if ((temp_Prev_V & 0xF) != 0) // also, Palette corruption only happens if the previous address did not end in a 0
{
PPU_VRegisterChangedOutOfVBlank = true;
}
}
}
}
}
// after writing to $2005, there is either a 1 or 2 cycle delay.
if (PPU_Update2005Delay > 0)
{
PPU_Update2005Delay--;
if (PPU_Update2005Delay == 0)
{
if (!PPUAddrLatch)
{
// if this is the first write to $2005
PPU_FineXScroll = (byte)(PPU_Update2005Value & 7); // This updates the fine X scroll
PPU_t = (ushort)((PPU_t & 0b0111111111100000) | (PPU_Update2005Value >> 3)); // as well as changing the 't' register.
}
else
{
// if this is the second write to $2005
PPU_t = (ushort)((PPU_t & 0b0000110000011111) | (((PPU_Update2005Value & 0xF8) << 2) | ((PPU_Update2005Value & 7) << 12))); // this also writes to 't'
}
PPUAddrLatch = !PPUAddrLatch; // flip the latch
}
}
// Updating the scroll registers during screen rendering
if (PPU_Scanline < 240 || PPU_Scanline == 261)// if this is the pre-render line, or any line before vblank
{
if ((PPU_Mask_ShowBackground || PPU_Mask_ShowSprites))
{
if (PPU_Dot == 256) //The Y scroll is incremented on dot 256.
{
PPU_IncrementScrollY();
}
else if (PPU_Dot == 257) //The X scroll is reset on dot 257.
{
PPU_ResetXScroll();
}
if (PPU_Dot >= 280 && PPU_Dot <= 304 && PPU_Scanline == 261) //numbers from the nesdev wiki
{
PPU_ResetYScroll(); //The Y scroll is reset on every dot from 280 through 304 on the pre-render scanline.
}
}
}
// Increment the PPU dot
PPU_Dot++;
if (PPU_Dot > 340) // There are only 341 dots per scanline
{
PPU_Dot = 0; // reset the dot back to 0
PPU_Scanline++; // and increment the scanline
// Sprite zero hits rely on the previous scanline's sprite evaluation.
if (PPU_Scanline > 261) // There are 262 scanlines in a frame.
{
PPU_Scanline = 0; // reset to scanline 0.
}
}
if (PPU_Scanline == 241) // If this is the first scanline of VBLank
{
if (PPU_Dot == 0)
{
// If Address $2002 is read during the next ppu cycle, the PPU Status flags aren't set.
// These variables are used to check if Address $2002 is read during the next ppu cycle.
// I usually refer to this as the $2002 race condition.
// The more proper term would be "Vblank/NMI flag suppression".
// oh- and also if we're running a fm2 TAS file, due to FCEUX's incorrect timing of the first frame, I need to prevent this from being set just a few cycles after power on.
if (!SyncFM2)
{
PPU_PendingVBlank = true;
}
else
{
SyncFM2 = false;
}
}
if (PPU_Dot == 1)
{
PPU_RESET = false;
// else, address $2002 was read on this ppu cycle. no VBlank flag.
if (!PPU_ShowScreenBorders)
{
FrameAdvance_ReachedVBlank = true; // Emulator specific stuff. Used for frame advancing to detect the frame has ended, and nothing else.
}
if (!ClockFiltering) // specifically for TASing stuff. Increment the index for the input log.
{
if (TAS_ReadingTAS && TAS_InputSequenceIndex > 0 && TAS_InputSequenceIndex < TAS_ResetLog.Length && TAS_ResetLog[TAS_InputSequenceIndex])
{
Reset();
}
// If this was using "SubFrame", TAS_InputSequenceIndex is incremented whenever the controller is strobed.
// Instead, I increment the index here at the start of vblank.
TAS_InputSequenceIndex++;
}
}
}
else if (PPU_Scanline == 242 && PPU_Dot == 1)
{
if (PPU_ShowScreenBorders && !PPU_DecodeSignal) // if we're showing the boarders, we need to wait for 2 more scanlines to render.
{
FrameAdvance_ReachedVBlank = true; // Emulator specific stuff. Used for frame advancing to detect the frame has ended, and nothing else.
}
}
else if (PPU_Scanline == 260 && PPU_Dot == 340)
{
PPU_OddFrame = !PPU_OddFrame; // I guess this could happen on pretty much any cycle?
}
else if (PPU_Scanline == 261 && PPU_Dot == 1)
{
// On dot 1 of the pre-render scanline, all of these flags are cleared.
// You might be looking at the results of my "$2002 Flag Clear Timing" test from the AccuracyCoin test ROM and thinking, "Hold on. That can't be right!"
// Well, it is. You see, PPUStatus_VBlank is read at the beginning of the read, while PPUStatus_SpriteZeroHit and PPUStatus_SpriteOverflow are read at the end of the read.
// This means about 1 and 7/8 ppu cycles pass between the start of the read and the end, so thes values are seemingly cleared on different cycles, but they are in-fact cleared at the same time.
PPUStatus_VBlank = false;
PPU_CanDetectSpriteZeroHit = true;
PPUStatus_SpriteZeroHit = false;
PPUStatus_SpriteOverflow = false;
PPUStatus_SpriteZeroHit_Delayed = false;
}
else if (PPU_Scanline == 0 && PPU_Dot == 1)
{
if (PPU_ShowScreenBorders && PPU_DecodeSignal) // if we're showing the boarders, we need to wait for scanline 0.
{
FrameAdvance_ReachedVBlank = true; // Emulator specific stuff. Used for frame advancing to detect the frame has ended, and nothing else.
}
}
PPU_VSET_Latch1 = !PPU_VSET; // VSET_Latch1 is latched with /VSET on the first half of a PPU cycle.
if (PPU_VSET && !PPU_VSET_Latch2)
{
PPUStatus_VBlank = true;
}
if (PPU_Read2002)
{
PPU_Read2002 = false;
PPUStatus_VBlank = false;
}
PPUStatus_SpriteOverflow_Delayed = PPUStatus_SpriteOverflow;
// Right now, I'm only emulating MMC3's IRQ counter in this function.
PPU_MapperSpecificFunctions();
PPU_A12_Prev = (PPU_AddressBus & 0b0001000000000000) != 0; // Record the value of the A12. This is used in the PPU_MapperSpecificFunctions(), so if this changes between here and next ppu cycle, we'll know.
if (PPU_OddFrame && (PPU_Mask_ShowBackground || PPU_Mask_ShowSprites))
{
if (PPU_Scanline == 261 && PPU_Dot == 340)
{
// On every other frame, dot 0 of scanline 0 is skipped.
// this cycle is technically (0,0), but this still makes the Nametable fetch during the last cycle of the pre-render line
PPU_Scanline = 0;
PPU_Dot = 0;
SkippedPreRenderDot341 = true;
}
}
if (PPU_OddFrame && (PPU_Mask_ShowBackground || PPU_Mask_ShowSprites) && PPU_Scanline == 0 && PPU_Dot == 2)
{
SkippedPreRenderDot341 = false; // This variable is used for some esoteric business on dot 1 of scanline 0.
}
// Okay, now that we're updated all those flags, let's render stuff to the screen!
// let's establish the order of operations.
// Sprite evaluation
// then calculate the color for the next dot.
//but to complicate things, the delay after writing to $2001 happens between those 2 steps, and also on a specific alignment, this delay is 1 cycle longer for sprite evaluation.
// If this is NOT phase 1
if ((CPUClock & 3) != 3)
{
// sprite evaluation has a 1 ppu cycle delay before recognizing these flags were set or cleared.
PPU_Mask_ShowBackground_Delayed = PPU_Mask_ShowBackground;
PPU_Mask_ShowSprites_Delayed = PPU_Mask_ShowSprites;
}
PPU_DATA_StateMachine();
if ((PPU_Scanline < 240 || PPU_Scanline == 261))// if this is the pre-render line, or any line before vblank
{
// Sprite evaluation
if (PPU_Scanline < 241 || PPU_Scanline == 261)
{
PPU_Render_SpriteEvaluation(); // fill in secondary OAM, and set up various arrays of sprite properties.
}
}
if ((CPUClock & 3) == 3)
{
// on phase 1,
// sprite evaluation has a 2 ppu cycle delay before recognizing these flags were set or cleared.
PPU_Mask_ShowBackground_Delayed = PPU_Mask_ShowBackground;
PPU_Mask_ShowSprites_Delayed = PPU_Mask_ShowSprites;
}
if (!PPU_Mask_ShowBackground && !PPU_Mask_ShowSprites)
{
PPU_AddressBus = PPU_v; // the address bus is always v when rendering is disabled.
// TODO: Is this occuring one ppu cycle too late???
// I specifically moved this here (outside of the following if statements) because it broke nes_reset_state_detect-letters.nes on alignment 1.
}
// after sprite evaluation, but before screen rendering...
if (PPU_Update2001Delay > 0) // if we wrote to 2001 recently
{
PPU_Update2001Delay--;
if (PPU_Update2001Delay == 0) // if we've waited enough cycles, apply the changes
{
PPU_Mask_8PxShowBackground = (PPU_Update2001Value & 0x02) != 0;
PPU_Mask_8PxShowSprites = (PPU_Update2001Value & 0x04) != 0;
PPU_Mask_ShowBackground = (PPU_Update2001Value & 0x08) != 0;
PPU_Mask_ShowSprites = (PPU_Update2001Value & 0x10) != 0;
PPU_Mask_ShowBackground_Instant = PPU_Mask_ShowBackground; // now that the PPU has updated, OAM evaluation will also recognize the change
PPU_Mask_ShowSprites_Instant = PPU_Mask_ShowSprites;
}
}
if (PPU_Update2001OAMCorruptionDelay > 0) // if we wrote to 2001 recently
{
PPU_Update2001OAMCorruptionDelay--;
if (PPU_Update2001OAMCorruptionDelay == 0) // if we've waited enough cycles, apply the changes
{
if (PPU_WasRenderingBefore2001Write && (PPU_Update2001Value & 0x08) == 0 && (PPU_Update2001Value & 0x10) == 0)
{
if ((PPU_Scanline < 240 || PPU_Scanline == 261)) // if this is the pre-render line, or any line before vblank
{
if (!PPU_PendingOAMCorruption) // due to OAM corruption occurring inside OAM evaluation before this even occurs, make sure OAM isn't already corrupt
{
PPU_OAMCorruptionRenderingDisabledOutOfVBlank = true;
}
}
}
}
}
if (PPU_Update2001EmphasisBitsDelay > 0)
{
PPU_Update2001EmphasisBitsDelay--;
if (PPU_Update2001EmphasisBitsDelay == 0)
{
PPU_Mask_Greyscale = (PPU_Update2001Value & 0x01) != 0;
PPU_Mask_EmphasizeRed = (PPU_Update2001Value & 0x20) != 0;
PPU_Mask_EmphasizeGreen = (PPU_Update2001Value & 0x40) != 0;
PPU_Mask_EmphasizeBlue = (PPU_Update2001Value & 0x80) != 0;
}
}
PrevPrevPrevDotColor = PrevPrevDotColor; // Drawing a color to the screen has a 3(?) ppu cycle delay between deciding the color, and drawing it.
PrevPrevDotColor = PrevDotColor;
PrevDotColor = DotColor; // These variables here just record the color, and swap them through these variables so it can be used 3 cycles after it was chosen.
if ((PPU_Scanline < 240 || PPU_Scanline == 261))// if this is the pre-render line, or any line before vblank
{
if ((PPU_Dot >= 1 && PPU_Dot <= 256) || (PPU_Dot >= 321 && PPU_Dot <= 336)) // if this is a visible pixel, or preparing the start of next scanline
{
if ((PPU_Mask_ShowBackground_Delayed || PPU_Mask_ShowSprites_Delayed)) // if rendering background or sprites
{
PPU_Render_ShiftRegistersAndBitPlanes(); // update shift registers for the background.
}
}
else if (PPU_Dot >= 337 || PPU_Dot == 0)
{
if ((PPU_Mask_ShowBackground_Delayed || PPU_Mask_ShowSprites_Delayed)) // if rendering background or sprites
{
PPU_Render_ShiftRegistersAndBitPlanes_DummyNT();
}
}
if ((PPU_Dot > 0 && PPU_Dot <= 256)) // if this is a visible pixel, or preparing the start of next scanline
{
if (PPU_Scanline < 241)
{
PPU_Render_CalculatePixel(false); // this determines the color of the pixel being drawn.
}
UpdateSpriteShiftRegisters(); // update shift registers for the sprites.
}
else
{
if (PPU_ShowScreenBorders) // Draw the pixels in the boarder too.
{
PPU_Render_CalculatePixel(true); // this determines the color of the pixel being drawn.
}
}
if (!PPU_ShowScreenBorders)
{
DrawToScreen();
if (PPU_DecodeSignal && (PPU_Dot == 0) && PPU_Scanline < 241)
{
ntsc_signal_of_dot_0 = ntsc_signal;
chosenColor = PaletteRAM[0x00] & 0x3F;
if (PPU_Mask_Greyscale) // if the ppu greyscale mode is active,
{
chosenColor &= 0x30; //To force greyscale, bitwise AND this color with 0x30
}
// emphasis bits
int emphasis = 0;
if (PPU_Mask_EmphasizeRed) { emphasis |= 0x40; } // if emhpasizing r, add 0x40 to the index into the palette LUT.
if (PPU_Mask_EmphasizeGreen) { emphasis |= 0x80; } // if emhpasizing g, add 0x80 to the index into the palette LUT.
if (PPU_Mask_EmphasizeBlue) { emphasis |= 0x100; } // if emhpasizing b, add 0x100 to the index into the palette LUT.
PrevPrevPrevPrevDotColor = chosenColor | emphasis; // set up samples for dot 1
PPU_SignalDecode(chosenColor | emphasis);
}
if (PPU_DecodeSignal && (PPU_Dot == 260) && PPU_Scanline < 241)
{
PPU_SignalDecode(PrevPrevPrevPrevDotColor);
}
else if (PPU_DecodeSignal && (PPU_Dot == 261) && PPU_Scanline < 241)
{
RenderNTSCScanline();
}
}
}
else if (PPU_ShowScreenBorders)
{
PPU_Render_CalculatePixel(true); // this determines the color of the pixel being drawn.
}
if (PPU_ShowScreenBorders)
{
DrawToBorderedScreen();
}
ThisDotReadFromPaletteRAM = false;
PPU_DATA_StateMachine2();
if (PPU_DecodeSignal)
{
ntsc_signal += 8;
ntsc_signal %= 12;
}
if (Logging && LoggingPPU)
{
Debug_PPU();
}
} // and that's all for the PPU cycle!
bool PPUActiveForShiftRegisterUpdate;
void _EmulateHalfPPU()
{
// Oh boy, it's time for half PPU cycles.
if ((PPU_Scanline < 240 || PPU_Scanline == 261))// if this is the pre-render line, or any line before vblank
{
if(PPU_Dot == 320)
{
}
if ((PPU_Dot > 0 && PPU_Dot <= 257) || (PPU_Dot > 320 && PPU_Dot <= 336)) // if this is a visible pixel, or preparing the start of next scanline
{
if ((PPU_Mask_ShowBackground || PPU_Mask_ShowSprites)) // if rendering background or sprites
{
PPU_UpdateBackgroundShiftRegisters(); // shift all the shift registers 1 bit
}
}
}
PPU_Render_CommitShiftRegistersAndBitPlanes();
PPU_VSET = false;
if (PPU_PendingVBlank)
{
PPU_PendingVBlank = false;
PPU_VSET = true;
}
// PPU_VSET_Latch1 gets inverted, and that becomes the state of PPU_VSET_Latch2
PPU_VSET_Latch2 = !PPU_VSET_Latch1;
if ((PPU_Mask_ShowBackground || PPU_Mask_ShowSprites) && PPU_Scanline < 240)
{
if (PPU_Dot == 0 || PPU_Dot > 320)
{
PPU_OAMBuffer = OAM2[0];
}
else if (PPU_Dot > 0 && PPU_Dot <= 64)
{
PPU_OAMBuffer = 0xFF;
}
else if (PPU_Dot <= 256)
{
PPU_OAMBuffer = PPU_OAMLatch;
}
else
{
PPU_OAMBuffer = PPU_OAMLatch;
}
}
PPUStatus_SpriteZeroHit_Delayed = PPUStatus_SpriteZeroHit;
if (PPUStatus_PendingSpriteZeroHit2)
{
PPUStatus_PendingSpriteZeroHit2 = false;
PPUStatus_SpriteZeroHit = true;
}
if (PPUStatus_PendingSpriteZeroHit)
{
PPUStatus_PendingSpriteZeroHit = false;
PPUStatus_PendingSpriteZeroHit2 = true;
}
PPU_DATA_StateMachine_Half();
}
public bool PPU_2007_Read;
public bool PPU_2007_Read_SR;
public bool[] PPU_2007_Read_Latches = new bool[5];
public bool PPU_2007_PD_RB;
public bool PPU_2007_ReadALE;
public bool PPU_2007_Read_H0_Latch;
public bool PPU_2007_Read_XRB;
public bool PPU_READ; // The lower 8 bits of the address bus are being used as data pins for a read.
public bool PPU_2007_Write;
public bool PPU_2007_Write_SR;
public bool[] PPU_2007_Write_Latches = new bool[5];
public bool PPU_2007_DB_PAR;
public bool PPU_2007_WriteALE;
public bool PPU_2007_TStep_Latch;
public bool PPU_2007_TStep;
public bool PPU_2007_BLNK_Latch;
public bool PPU_2007_PaletteRAMEnable;
public byte PPU_2007_WriteData;
public bool PPU_WRITE; // The lower 8 bits of the address bus are being used as data pins for a write.
void PPU_DATA_StateMachine()
{
bool BLNK = (!PPU_Mask_ShowBackground && !PPU_Mask_ShowSprites) || (PPU_Scanline >= 240 && PPU_Scanline < 261);
PPU_2007_BLNK_Latch = BLNK;
bool H0_DASH = (PPU_Dot - 1 & 1) != 0;
PPU_2007_PaletteRAMEnable = ((PPU_AddressBus & 0x3F00) == 0x3F00) && PPU_2007_BLNK_Latch;
PPU_2007_Read_XRB = PPU_2007_Read && PPU_2007_PaletteRAMEnable;
PPU_2007_Read_Latches[0] = PPU_2007_Read_SR;
if(PPU_2007_Read)
{
PPU_2007_Read = false; // I put this in an if statement for easier debugging / breakpoint placement.
}
PPU_2007_Read_Latches[2] = !PPU_2007_Read_Latches[1];
PPU_2007_Read_Latches[4] = !PPU_2007_Read_Latches[3];
PPU_2007_PD_RB = PPU_2007_Read_Latches[4] && !PPU_2007_Read_Latches[2];
PPU_2007_ReadALE = !PPU_2007_Read_Latches[4] && PPU_2007_Read_Latches[2];
PPU_2007_Read_H0_Latch = (PPU_Dot - 1 & 1) != 0;
PPU_READ = (PPU_2007_PD_RB || (!BLNK && PPU_2007_Read_H0_Latch)); // even ppu cycles outside of blanking always read. Also read if we are reading $2007.
PPU_2007_Write_Latches[0] = PPU_2007_Write_SR;
if (PPU_2007_Write)
{
PPU_2007_Write = false; // I put this in an if statement for easier debugging / breakpoint placement.
}
PPU_2007_Write_Latches[2] = !PPU_2007_Write_Latches[1];
PPU_2007_Write_Latches[4] = !PPU_2007_Write_Latches[3];
PPU_2007_WriteALE = !PPU_2007_Write_Latches[4] && PPU_2007_Write_Latches[2];
PPU_2007_TStep_Latch = PPU_2007_DB_PAR;
bool b = (!BLNK && !H0_DASH); // If you are on an even dot out of a blanking period
PPU_ALE = (PPU_2007_ReadALE || PPU_2007_WriteALE || b);
if ((PPU_2007_ReadALE || PPU_2007_WriteALE))
{
if (!PPU_READ)
{
PPU_AddressBus = PPU_v;
PPU_OctalLatch = (byte)PPU_AddressBus;
}
}
}
void PPU_DATA_StateMachine2()
{
if (PPU_2007_PD_RB)
{
PPU_ReadBuffer = Cart.MapperChip.FetchPPU();
if (PPU_ALE)
{
PPU_OctalLatch = (byte)PPU_AddressBus;
}
}
}
void PPU_DATA_StateMachine_Half()
{
PPU_2007_TStep = (PPU_2007_TStep_Latch || PPU_2007_PD_RB);
if (PPU_2007_TStep) // If this occurs inside PPU_DATA_StateMachine() instead, the timing is wrong, and this breaks SMB1's title screen.
{
if (!PPU_2007_BLNK_Latch)
{
PPU_IncrementScrollY();
}
else
{
PPU_v += (ushort)(PPUControlIncrementMode32 ? 32 : 1);
}
}
PPU_ALE = (PPU_2007_ReadALE || PPU_2007_WriteALE);
if (PPU_2007_PD_RB)
{
PPU_ReadBuffer = Cart.MapperChip.FetchPPU();
if (PPU_ALE)
{
// pretty sure this can never happen, but keep it just in case.
PPU_OctalLatch = (byte)PPU_AddressBus;
}
}
PPU_2007_Read_Latches[1] = !PPU_2007_Read_Latches[0];
PPU_2007_Read_Latches[3] = !PPU_2007_Read_Latches[2];
if (!PPU_2007_Read_Latches[3])
{
PPU_2007_Read_SR = false;
}
PPU_2007_Write_Latches[1] = !PPU_2007_Write_Latches[0];
PPU_2007_Write_Latches[3] = !PPU_2007_Write_Latches[2];
if (!PPU_2007_Write_Latches[3])
{
PPU_2007_Write_SR = false;
}
PPU_2007_DB_PAR = PPU_2007_Write_Latches[1] && !PPU_2007_Write_Latches[3];
PPU_WRITE = !PPU_2007_PaletteRAMEnable && PPU_2007_DB_PAR;
if (PPU_2007_DB_PAR) // Using PAR instead of PPU_WRITE, since I re-use StorePPUData() for writes to palette RAM.
{
StorePPUData(PPU_AddressBus, PPU_2007_WriteData);
}
}
void DrawToScreen()
{
if (PPU_Dot > 3 && PPU_Dot <= 259 && PPU_Scanline < 241) // the process of drawing a dot to the screen actually has a 2 ppu cycle delay, which the emphasis bits happen after
{
// in other words, the geryscale/emphasis bits can affect the color that was decided 2 ppu cycles ago.
chosenColor = PrevPrevPrevDotColor;
if (PPU_Mask_Greyscale) // if the ppu greyscale mode is active,
{
chosenColor &= 0x30; //To force greyscale, bitwise AND this color with 0x30
}
// emphasis bits
int emphasis = 0;
if (PPU_Mask_EmphasizeRed) { emphasis |= 0x40; } // if emhpasizing r, add 0x40 to the index into the palette LUT.
if (PPU_Mask_EmphasizeGreen) { emphasis |= 0x80; } // if emhpasizing g, add 0x80 to the index into the palette LUT.
if (PPU_Mask_EmphasizeBlue) { emphasis |= 0x100; } // if emhpasizing b, add 0x100 to the index into the palette LUT.
int scanline0OddFrameOffset = 0;
if (PPU_Scanline == 0 && PPU_OddFrame && (PPU_Mask_ShowBackground || PPU_Mask_ShowSprites))
{
scanline0OddFrameOffset = 1;
}
if (!PPU_DecodeSignal)
{
if (!PPU_ShowScreenBorders)
{
if (scanline0OddFrameOffset == 1 && PPU_Dot == 4)
{
// do nothing. This would be off screen.
}
else
{
Screen.SetPixel(PPU_Dot - 4 - scanline0OddFrameOffset, PPU_Scanline, unchecked((int)NesPalInts[chosenColor | emphasis])); // this sets the pixel on screen to the chosen color.
}
}
else
{
Screen.SetPixel(PPU_Dot - 4 - scanline0OddFrameOffset, PPU_Scanline, unchecked((int)NesPalInts[chosenColor | emphasis])); // this sets the pixel on screen to the chosen color.
}
}
else
{
if (PPU_Mask_Greyscale) // if the ppu greyscale mode is active,
{
chosenColor &= 0x30; //To force greyscale, bitwise AND this color with 0x30
}
PPU_SignalDecode(chosenColor | emphasis);
PrevPrevPrevPrevDotColor = chosenColor | emphasis;
}
}
if (PPU_Scanline == 0 && PPU_OddFrame && (PPU_Mask_ShowBackground || PPU_Mask_ShowSprites) && PPU_Dot == 259)
{
// draw the backdrop.
chosenColor = PaletteRAM[0];
// emphasis bits
int emphasis = 0;
if (PPU_Mask_EmphasizeRed) { emphasis |= 0x40; } // if emhpasizing r, add 0x40 to the index into the palette LUT.
if (PPU_Mask_EmphasizeGreen) { emphasis |= 0x80; } // if emhpasizing g, add 0x80 to the index into the palette LUT.
if (PPU_Mask_EmphasizeBlue) { emphasis |= 0x100; } // if emhpasizing b, add 0x100 to the index into the palette LUT.
if (!PPU_DecodeSignal)
{
Screen.SetPixel(255, PPU_Scanline, unchecked((int)NesPalInts[chosenColor | emphasis])); // this sets the pixel on screen to the chosen color.
}
else
{
if (PPU_Mask_Greyscale) // if the ppu greyscale mode is active,
{
chosenColor &= 0x30; //To force greyscale, bitwise AND this color with 0x30
}
PPU_SignalDecode(chosenColor | emphasis);
PrevPrevPrevPrevDotColor = chosenColor | emphasis;
}
}
}
void DrawToBorderedScreen()
{
int dot = PPU_Dot;
int scanline = PPU_Scanline;
int emphasis = 0;
dot -= 3;
if (dot < 0)
{
dot = 341 + dot;
scanline--;
if (scanline < 0)
{
scanline = 261;
}
}
int boarderedDot = 0;
int boarderedScanline = scanline;
if (PPU_ShowScreenBorders && dot == 325)
{
ntsc_signal_of_dot_0 = ntsc_signal;
}
if (PPU_DecodeSignal && dot == 277)
{
RenderNTSCScanline();
}
if (scanline < 241 || ((scanline == 241 && dot < 277)) || scanline == 261)
{
if (dot >= 1 && dot <= 256) // visible pixels.
{
if (scanline == 261)
{
chosenColor = 0x0F;
}
else
{
chosenColor = PrevPrevPrevDotColor;
if (PPU_Mask_Greyscale) // if the ppu greyscale mode is active,
{
chosenColor &= 0x30; //To force greyscale, bitwise AND this color with 0x30
}
// emphasis bits
if (PPU_Mask_EmphasizeRed) { emphasis |= 0x40; } // if emhpasizing r, add 0x40 to the index into the palette LUT.
if (PPU_Mask_EmphasizeGreen) { emphasis |= 0x80; } // if emhpasizing g, add 0x80 to the index into the palette LUT.
if (PPU_Mask_EmphasizeBlue) { emphasis |= 0x100; } // if emhpasizing b, add 0x100 to the index into the palette LUT.
}
boarderedDot = dot + 64;
boarderedScanline = scanline;
}
else if (dot >= 257 && dot <= 267) // right boarder
{
if (scanline == 261)
{
chosenColor = 0x0F;
}
else
{
// backdrop.
chosenColor = PrevPrevPrevDotColor;
if (PPU_Mask_Greyscale) // if the ppu greyscale mode is active,
{
chosenColor &= 0x30; //To force greyscale, bitwise AND this color with 0x30
}
// emphasis bits
if (PPU_Mask_EmphasizeRed) { emphasis |= 0x40; } // if emhpasizing r, add 0x40 to the index into the palette LUT.
if (PPU_Mask_EmphasizeGreen) { emphasis |= 0x80; } // if emhpasizing g, add 0x80 to the index into the palette LUT.
if (PPU_Mask_EmphasizeBlue) { emphasis |= 0x100; } // if emhpasizing b, add 0x100 to the index into the palette LUT.
}
boarderedDot = dot + 64;
boarderedScanline = scanline;
}
else if (dot >= 268 && dot <= 276) // front porch
{
// black.
chosenColor = 0x0F;
boarderedDot = dot + 64;
boarderedScanline = scanline;
}
else if (dot >= 277 && dot <= 301) // horizontal sync
{
// black.
chosenColor = 0x0F;
boarderedDot = dot - 277;
boarderedScanline = scanline + 1;
}
else if (dot >= 302 && dot <= 305) // back porch
{
// black.
chosenColor = 0x0F;
boarderedDot = dot - 277;
boarderedScanline = scanline + 1;
}
else if (dot >= 306 && dot <= 320) // colorburst
{
// extremely dark olive.
chosenColor = Signal_COLORBURST;
boarderedDot = dot - 277;
boarderedScanline = scanline + 1;
}
else if (dot >= 321 && dot <= 325) // back porch
{
// black.
chosenColor = 0x0F;
boarderedDot = dot - 277;
boarderedScanline = scanline + 1;
}
else if (dot == 326) // pulse
{
// backdrop in greyscale
if (ThisDotReadFromPaletteRAM)
{
chosenColor = PrevPrevPrevDotColor;
}
else
{
chosenColor = PrevPrevPrevDotColor & 0x30;
}
// emphasis bits
if (PPU_Mask_EmphasizeRed) { emphasis |= 0x40; } // if emhpasizing r, add 0x40 to the index into the palette LUT.
if (PPU_Mask_EmphasizeGreen) { emphasis |= 0x80; } // if emhpasizing g, add 0x80 to the index into the palette LUT.
if (PPU_Mask_EmphasizeBlue) { emphasis |= 0x100; } // if emhpasizing b, add 0x100 to the index into the palette LUT.
boarderedDot = dot - 277;
boarderedScanline = scanline + 1;
}
else // right boarder
{
// backdrop.
if (scanline == 261 && dot == 0)
{
chosenColor = 0x0F;
}
else
{
chosenColor = PrevPrevPrevDotColor;
if (PPU_Mask_Greyscale) // if the ppu greyscale mode is active,
{
chosenColor &= 0x30; //To force greyscale, bitwise AND this color with 0x30
}
// emphasis bits
if (PPU_Mask_EmphasizeRed) { emphasis |= 0x40; } // if emhpasizing r, add 0x40 to the index into the palette LUT.
if (PPU_Mask_EmphasizeGreen) { emphasis |= 0x80; } // if emhpasizing g, add 0x80 to the index into the palette LUT.
if (PPU_Mask_EmphasizeBlue) { emphasis |= 0x100; } // if emhpasizing b, add 0x100 to the index into the palette LUT.
}
if (dot != 0)
{
boarderedDot = dot - 277;
boarderedScanline = scanline + 1;
}
else
{
boarderedDot = dot + 64;
boarderedScanline = scanline;
}
}
}
else
{
if (scanline >= 245 && scanline <= 247)
{
// black.
chosenColor = 0x0F;
if (dot >= 277)
{
boarderedDot = dot - 277;
boarderedScanline = scanline + 1;
}
else
{
boarderedDot = dot + 64;
boarderedScanline = scanline;
}
}
else
{
// colorburst happens on this line too.
if (dot >= 306 && dot <= 320) // colorburst
{
// extremely dark olive.
chosenColor = Signal_COLORBURST;
boarderedDot = dot - 277;
boarderedScanline = scanline + 1;
}
else
{
// black.
chosenColor = 0x0F;
if (dot >= 277)
{
boarderedDot = dot - 277;
boarderedScanline = scanline + 1;
}
else
{
boarderedDot = dot + 64;
boarderedScanline = scanline;
}
}
}
}
if (PPU_Scanline == 0 && PPU_OddFrame && (PPU_Mask_ShowBackground || PPU_Mask_ShowSprites) && PPU_Dot < 277)
{
boarderedDot--;
}
if (boarderedScanline == 0x106)
{
boarderedScanline = 0;
}
if (PPU_DecodeSignal)
{
PPU_SignalDecode(chosenColor | emphasis);
}
else
{
BorderedScreen.SetPixel(boarderedDot, boarderedScanline, unchecked((int)NesPalInts[chosenColor | emphasis])); // this sets the pixel on screen to the chosen color.
}
}
public bool PPU_DecodeSignal;
public bool PPU_ShowScreenBorders;
static float[] Voltages =
{ 0.228f, 0.312f, 0.552f, 0.880f, // Signal low
0.616f, 0.840f, 1.100f, 1.100f, // Signal high
0.192f, 0.256f, 0.448f, 0.712f, // Signal low, attenuated
0.500f, 0.676f, 0.896f, 0.896f // Signal high, attenuated
};
public byte ntsc_signal;
public byte ntsc_signal_of_dot_0;
public float[] NTSC_Samples = new float[257 * 8 + 24];
public float[] Bordered_NTSC_Samples = new float[341 * 8 + 24];
static float[] Levels =
{
(Voltages[0] - Voltages[1]) / (Voltages[6] - Voltages[1]) / 12f,
(Voltages[1] - Voltages[1]) / (Voltages[6] - Voltages[1]) / 12f,
(Voltages[2] - Voltages[1]) / (Voltages[6] - Voltages[1]) / 12f,
(Voltages[3] - Voltages[1]) / (Voltages[6] - Voltages[1]) / 12f,
(Voltages[4] - Voltages[1]) / (Voltages[6] - Voltages[1]) / 12f,
(Voltages[5] - Voltages[1]) / (Voltages[6] - Voltages[1]) / 12f,
(Voltages[6] - Voltages[1]) / (Voltages[6] - Voltages[1]) / 12f,
(Voltages[7] - Voltages[1]) / (Voltages[6] - Voltages[1]) / 12f,
(Voltages[8] - Voltages[1]) / (Voltages[6] - Voltages[1]) / 12f,
(Voltages[9] - Voltages[1]) / (Voltages[6] - Voltages[1]) / 12f,
(Voltages[10] - Voltages[1]) / (Voltages[6] - Voltages[1]) / 12f,
(Voltages[11] - Voltages[1]) / (Voltages[6] - Voltages[1]) / 12f,
(Voltages[12] - Voltages[1]) / (Voltages[6] - Voltages[1]) / 12f,
(Voltages[13] - Voltages[1]) / (Voltages[6] - Voltages[1]) / 12f,
(Voltages[14] - Voltages[1]) / (Voltages[6] - Voltages[1]) / 12f,
(Voltages[15] - Voltages[1]) / (Voltages[6] - Voltages[1]) / 12f
};
float Saturation = 0.75f;
int SignalBufferWidth = 12;
static double hue = 0;
static float chroma_saturation_correction = 2.4f;
static double[] SinTable =
{
Math.Sin(Math.PI* (0 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction,
Math.Sin(Math.PI* (1 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction,
Math.Sin(Math.PI* (2 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction,
Math.Sin(Math.PI* (3 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction,
Math.Sin(Math.PI* (4 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction,
Math.Sin(Math.PI* (5 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction,
Math.Sin(Math.PI* (6 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction,
Math.Sin(Math.PI* (7 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction,
Math.Sin(Math.PI* (8 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction,
Math.Sin(Math.PI* (9 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction,
Math.Sin(Math.PI* (10 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction,
Math.Sin(Math.PI* (11 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction
};
static double[] CosTable =
{
Math.Cos(Math.PI* (0 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction,
Math.Cos(Math.PI* (1 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction,
Math.Cos(Math.PI* (2 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction,
Math.Cos(Math.PI* (3 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction,
Math.Cos(Math.PI* (4 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction,
Math.Cos(Math.PI* (5 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction,
Math.Cos(Math.PI* (6 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction,
Math.Cos(Math.PI* (7 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction,
Math.Cos(Math.PI* (8 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction,
Math.Cos(Math.PI* (9 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction,
Math.Cos(Math.PI* (10 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction,
Math.Cos(Math.PI* (11 + 3 - 0.5 + hue) / 6) * chroma_saturation_correction
};
bool InColorPhase(int col, int DecodePhase)
{
return (col + DecodePhase) % 12 < 6;
}
static float ntsc_black = 0.312f, ntsc_white = 1.100f;
static int Signal_COLORBURST = 512;
static int Signal_SYNC = 513;
static float Colorburst_High = (0.524f - Voltages[1]) / (Voltages[6] - Voltages[1]) / 12f;
static float Colorburst_Low = (0.148f - Voltages[1]) / (Voltages[6] - Voltages[1]) / 12f;
void PPU_SignalDecode(int nesColor)
{
bool boardered = PPU_ShowScreenBorders;
byte phase = ntsc_signal;
int i = 0;
while (i < 8)
{
float sample = 0;
// Decode the NES color.
if (nesColor == Signal_COLORBURST)
{
sample = InColorPhase(0x8, phase) ? Colorburst_High : Colorburst_Low;
}
else
{
int colInd = (nesColor & 0x0F); // 0..15 "cccc"
int level = (nesColor >> 4) & 3; // 0..3 "ll"
int emphasis = (nesColor >> 6); // 0..7 "eee"
if (colInd > 13) { level = 1; } // For colors 14..15, level 1 is forced.
int attenuation = (
((((emphasis & 1) != 0) && InColorPhase(0xC, phase)) ||
(((emphasis & 2) != 0) && InColorPhase(0x4, phase)) ||
(((emphasis & 4) != 0) && InColorPhase(0x8, phase))) && (colInd < 0xE)) ? 8 : 0;
float low = Levels[0 + level + attenuation];
float high = Levels[4 + level + attenuation];
if (colInd == 0) { low = high; } // For color 0, only high level is emitted
if (colInd > 12) { high = low; } // For colors 13..15, only low level is emitted
sample = InColorPhase(colInd, phase) ? high : low;
}
if (boardered)
{
int dot = PPU_Dot - 3;
if (dot < 0)
{
dot = 341 + dot;
}
if (dot >= 277)
{
dot -= 277;
}
else
{
dot += 64;
}
if (PPU_Scanline == 0 && PPU_OddFrame && (PPU_Mask_ShowBackground || PPU_Mask_ShowSprites) && PPU_Dot < 277)
{
dot--;
}
Bordered_NTSC_Samples[dot * 8 + i] = sample;
}
else if (PPU_Dot <= 256 + 3)
{
if (PPU_Dot == 0)
{
NTSC_Samples[i] = sample;
}
else
{
NTSC_Samples[(PPU_Dot - 3) * 8 + i] = sample;
}
}
phase++;
phase %= 12;
i++;
}
}
public bool PPU_ShowRawNTSCSignal;
void RenderNTSCScanline()
{
byte phase = ntsc_signal_of_dot_0;
bool bordered = PPU_ShowScreenBorders; // this value could change at any moment, so it would be nice to avoid errors due to array lengths.
int scanline0OddFrameOffset = 0;
if (PPU_Scanline == 0 && PPU_OddFrame && (PPU_Mask_ShowBackground || PPU_Mask_ShowSprites) && !bordered)
{
scanline0OddFrameOffset = 8;
}
int width = bordered ? BorderedNTSCScreen.Width : NTSCScreen.Width;
int i = 0;
while (i < width + scanline0OddFrameOffset)
{
double R = 0;
double G = 0;
double B = 0;
if (!PPU_ShowRawNTSCSignal)
{
int center = i + 8;
int begin = center - 6;
int end = center + 6;
double Y = 0;
double U = 0;
double V = 0;
for (int p = begin; p < end; ++p) // Collect and accumulate samples
{
float sample = bordered ? (Bordered_NTSC_Samples[p]) : (NTSC_Samples[p]);
Y += sample;
int rotation = (phase + p) % 12;
U += (sample * SinTable[rotation]);
V += (sample * CosTable[rotation]);
}
//U *= (0.35355339 * 2);
//V *= (0.35355339 * 2);
U = U * 0.5f + 0.5f;
V = V * 0.5f + 0.5f;
bool DebugYUV = false;
if (DebugYUV)
{
Y = 0.5;
U = (i + 0.0f) / width;
V = 1 - (PPU_Scanline / 240f);
U -= 0.5f;
V -= 0.5f;
U *= (0.35355339 * 2);
V *= (0.35355339 * 2);
U += 0.5f;
V += 0.5f;
}
// convert YUV to RGB
R = 1.164 * (Y - 16 / 256.0) + 1.596 * (V - 128 / 256.0);
G = 1.164 * (Y - 16 / 256.0) - 0.392 * (U - 128 / 256.0) - 0.813 * (V - 128 / 256.0);
B = 1.164 * (Y - 16 / 256.0) + 2.017 * (U - 128 / 256.0);
// other values ?
//double R = 1.164 * (Y - 16 / 256.0) + 1.14 * (V - 128 / 256.0);
//double G = 1.164 * (Y - 16 / 256.0) - (1 / 1.14) * (U - 128 / 256.0) - (1 / (1.14 * 1.78)) * (V - 128 / 256.0);
//double B = 1.164 * (Y - 16 / 256.0) + (1.14 * 1.78) * (U - 128 / 256.0);
// convert YUV to normalized RGB
//double R = 1.164 * (Y - 16 / 256.0) + 1 * (V - 128 / 256.0);
//double G = 1.164 * (Y - 16 / 256.0) - 0.31764705882 * (U - 128 / 256.0) - 0.68359375 * (V - 128 / 256.0);
//double B = 1.164 * (Y - 16 / 256.0) + 1 * (U - 128 / 256.0);
if (R < 0) { R = 0; }
if (R > 1) { R = 1; }
if (G < 0) { G = 0; }
if (G > 1) { G = 1; }
if (B < 0) { B = 0; }
if (B > 1) { B = 1; }
}
if (PPU_ShowScreenBorders)
{
if (PPU_ShowRawNTSCSignal)
{
R = Bordered_NTSC_Samples[i] * 12;
G = Bordered_NTSC_Samples[i] * 12;
B = Bordered_NTSC_Samples[i] * 12;
if (R < 0) { R = 0; }
if (R > 1) { R = 1; }
if (G < 0) { G = 0; }
if (G > 1) { G = 1; }
if (B < 0) { B = 0; }
if (B > 1) { B = 1; }
}
BorderedNTSCScreen.SetPixel(i, PPU_Scanline, Color.FromArgb((byte)(R * 255), (byte)(G * 255), (byte)(B * 255))); // this sets the pixel on screen to the chosen color.
}
else
{
if (PPU_ShowRawNTSCSignal)
{
R = NTSC_Samples[i + 8] * 12;
G = NTSC_Samples[i + 8] * 12;
B = NTSC_Samples[i + 8] * 12;
if (R < 0) { R = 0; }
if (R > 1) { R = 1; }
if (G < 0) { G = 0; }
if (G > 1) { G = 1; }
if (B < 0) { B = 0; }
if (B > 1) { B = 1; }
}
if (scanline0OddFrameOffset == 0)
{
NTSCScreen.SetPixel(i, PPU_Scanline, Color.FromArgb((byte)(R * 255), (byte)(G * 255), (byte)(B * 255))); // this sets the pixel on screen to the chosen color.
}
else
{
if (i >= 8)
{
NTSCScreen.SetPixel(i - 8, PPU_Scanline, Color.FromArgb((byte)(R * 255), (byte)(G * 255), (byte)(B * 255))); // this sets the pixel on screen to the chosen color.
}
}
}
i++;
}
}
void PPU_MapperSpecificFunctions()
{
Cart.MapperChip.PPUClock(); // If the mapper chip does something every ppu clock... (See MMC3)
}
// If OAM corruption is pending, it occurs on the first rendered dot.
public void CorruptOAM()
{
// basically 8 entries of OAM are getting replaced (this is considered a single "row" of OAM)
// PPU_OAMCorruptionIndex is the row that gets corrupted.
if (PPU_OAMCorruptionIndex == 0x20)
{
PPU_OAMCorruptionIndex = 0;
}
int i = 0;
while (i < 8) // 8 entries in a row
{
OAM[PPU_OAMCorruptionIndex * 8 + i] = OAM[i]; // The corrupted row is replaced with the values from row 0
i++;
}
OAM2[PPU_OAMCorruptionIndex] = OAM2[0]; // Also corrupt this byte.
// this all happens in a single cycle.
}
bool OamCorruptedOnOddCycle;
public byte PPU_OAMLatch; // is this just the ppubus?
public ushort InRangeCheck; // Is this sprite in range of htis scanline?
public byte PPU_OAMBuffer; // This is the value read from $2004, updated on half cycles.
bool NineObjectsOnThisScanline;
void PPU_Render_SpriteEvaluation()
{
bool SpriteEval_ReadOnly_PreRenderLine = false;
if (PPU_Scanline == 261)
{
SpriteEval_ReadOnly_PreRenderLine = true;
}
if ((PPU_Mask_ShowBackground_Instant || PPU_Mask_ShowSprites_Instant))
{
if (PPU_PendingOAMCorruption) // OAM corruption occurs on the visible dot after rendering was enabled. It also can happen on the pre-render line.
{
PPU_PendingOAMCorruption = false;
if (!PPU_OAMCorruptionRenderingEnabledOutOfVBlank)
{
CorruptOAM();
}
PPU_OAMCorruptionRenderingEnabledOutOfVBlank = false;
}
}
if ((PPU_Dot >= 0 && PPU_Dot <= 64)) // Dots 1 through 64, not on the pre-render line. (and also dot 0 for OAM corruption purposes)
{
// this step is clearing secondary OAM, and writing FF to each byte in the array.
if ((PPU_Dot & 1) == 1)
{ //odd cycles
if ((PPU_Mask_ShowBackground_Delayed || PPU_Mask_ShowSprites_Delayed))
{
if (SpriteEval_ReadOnly_PreRenderLine)
{
PPU_OAMLatch = OAM2[OAM2Address];
}
gitextract_r4m8qsp8/ ├── .gitattributes ├── .gitignore ├── 6502Documentation.cs ├── App.config ├── Emulator.cs ├── LICENSE ├── Program.cs ├── Properties/ │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── README.md ├── TriCNES.csproj ├── TriCNES.sln ├── forms/ │ ├── TASProperties.Designer.cs │ ├── TASProperties.cs │ ├── TASProperties.resx │ ├── TASProperties3ct.Designer.cs │ ├── TASProperties3ct.cs │ ├── TASProperties3ct.resx │ ├── TriCHexEditor.Designer.cs │ ├── TriCHexEditor.cs │ ├── TriCHexEditor.resx │ ├── TriCNESGUI.Designer.cs │ ├── TriCNESGUI.cs │ ├── TriCNESGUI.resx │ ├── TriCNTViewer.Designer.cs │ ├── TriCNTViewer.cs │ ├── TriCNTViewer.resx │ ├── TriCTASTimeline.Designer.cs │ ├── TriCTASTimeline.cs │ ├── TriCTASTimeline.resx │ ├── TriCTraceLogger.Designer.cs │ ├── TriCTraceLogger.cs │ └── TriCTraceLogger.resx ├── mappers/ │ ├── Mapper_AOROM.cs │ ├── Mapper_CNROM.cs │ ├── Mapper_FDS.cs │ ├── Mapper_FME7.cs │ ├── Mapper_MMC1.cs │ ├── Mapper_MMC2.cs │ ├── Mapper_MMC3.cs │ ├── Mapper_NROM.cs │ ├── Mapper_NULL.cs │ └── Mapper_UxROM.cs └── packages.config
SYMBOL INDEX (375 symbols across 29 files)
FILE: 6502Documentation.cs
class Op (line 9) | public class Op
method Op (line 19) | public Op(byte c, string m, string mo, int l, int a, string d, string i)
class Documentation (line 35) | public static class Documentation
FILE: Emulator.cs
class Cartridge (line 13) | public class Cartridge
method Cartridge (line 38) | public Cartridge(string filepath) // Constructor from file path
method Cartridge (line 94) | public Cartridge(string filepath, string FDSBIOS_filepath)
class Mapper (line 113) | public class Mapper
method FetchPRG (line 122) | public virtual void FetchPRG(ushort Address, bool Observe)
method StorePRG (line 142) | public virtual void StorePRG(ushort Address, byte Input)
method FetchCHR (line 145) | public virtual byte FetchCHR(ushort Address, bool Observe)
method FetchPPU (line 149) | public virtual byte FetchPPU()
method MirrorNametable (line 177) | public virtual ushort MirrorNametable(ushort Address)
method SaveMapperRegisters (line 188) | public virtual List<byte> SaveMapperRegisters()
method LoadMapperRegisters (line 195) | public virtual void LoadMapperRegisters(List<byte> State, int startInd...
method PPUClock (line 202) | public virtual void PPUClock() // runs every PPU clock. (See MMC3)
method CPUClock (line 205) | public virtual void CPUClock() // runs every CPU clock. (See Sunsoft F...
method CPUClockRise (line 208) | public virtual void CPUClockRise() // runs every time the CPU clock ri...
method FDS_ByteTransferFlag (line 212) | public virtual void FDS_ByteTransferFlag()
method FDS_Get4025 (line 215) | public virtual byte FDS_Get4025()
method EndFetchPRG (line 220) | protected void EndFetchPRG(bool Observe, byte data)
class DiskDrive (line 235) | public class DiskDrive
method Clock (line 249) | public void Clock()
method InsertDisk (line 285) | public void InsertDisk(string filepath)
class Emulator (line 292) | public class Emulator
method Emulator (line 406) | public Emulator() // The instantiator for this class
method Reset (line 561) | public void Reset()
method _CoreFrameAdvance (line 684) | public void _CoreFrameAdvance()
method _CoreCycleAdvance (line 696) | public void _CoreCycleAdvance()
method _EmulatorCore (line 708) | public void _EmulatorCore()
method EmulateUntilEndOfRead (line 779) | public void EmulateUntilEndOfRead()
method EmulateNMasterClockCycles (line 789) | public void EmulateNMasterClockCycles(int n)
method _EmulateAPU (line 886) | void _EmulateAPU()
method _EmulatePPU (line 1331) | void _EmulatePPU()
method _EmulateHalfPPU (line 1702) | void _EmulateHalfPPU()
method PPU_DATA_StateMachine (line 1790) | void PPU_DATA_StateMachine()
method PPU_DATA_StateMachine2 (line 1836) | void PPU_DATA_StateMachine2()
method PPU_DATA_StateMachine_Half (line 1848) | void PPU_DATA_StateMachine_Half()
method DrawToScreen (line 1896) | void DrawToScreen()
method DrawToBorderedScreen (line 1969) | void DrawToBorderedScreen()
method InColorPhase (line 2264) | bool InColorPhase(int col, int DecodePhase)
method PPU_SignalDecode (line 2274) | void PPU_SignalDecode(int nesColor)
method RenderNTSCScanline (line 2342) | void RenderNTSCScanline()
method PPU_MapperSpecificFunctions (line 2469) | void PPU_MapperSpecificFunctions()
method CorruptOAM (line 2475) | public void CorruptOAM()
method PPU_Render_SpriteEvaluation (line 2504) | void PPU_Render_SpriteEvaluation()
method PPU_SpriteEvaluation_GetSpriteAddress (line 3065) | void PPU_SpriteEvaluation_GetSpriteAddress(byte SecondOAMSlot)
method PPU_Render_CalculatePixel (line 3124) | void PPU_Render_CalculatePixel(bool borders)
method CorruptPalettes (line 3276) | void CorruptPalettes(byte Color, byte Palette)
method PPU_Render_ShiftRegistersAndBitPlanes (line 3602) | void PPU_Render_ShiftRegistersAndBitPlanes()
method PPU_Render_CommitShiftRegistersAndBitPlanes (line 3670) | void PPU_Render_CommitShiftRegistersAndBitPlanes()
method PPU_Render_ShiftRegistersAndBitPlanes_DummyNT (line 3714) | void PPU_Render_ShiftRegistersAndBitPlanes_DummyNT()
method PPU_CheckPAR (line 3760) | public void PPU_CheckPAR()
method Flip (line 3792) | public byte Flip(byte b)
method PPU_UpdateBackgroundShiftRegisters (line 3800) | void PPU_UpdateBackgroundShiftRegisters()
method UpdateSpriteShiftRegisters (line 3808) | void UpdateSpriteShiftRegisters()
method PPU_LoadShiftRegisters (line 3833) | void PPU_LoadShiftRegisters()
method PPU_IncrementScrollX (line 3841) | void PPU_IncrementScrollX()
method PPU_IncrementScrollY (line 3856) | void PPU_IncrementScrollY()
method PPU_ResetXScroll (line 3890) | void PPU_ResetXScroll()
method PPU_ResetYScroll (line 3898) | void PPU_ResetYScroll()
method DecayPPUDataBus (line 3906) | void DecayPPUDataBus()
method OAMDMA_Get (line 3943) | void OAMDMA_Get()
method OAMDMA_Halted (line 3950) | void OAMDMA_Halted()
method OAMDMA_Put (line 3955) | void OAMDMA_Put()
method DMCDMA_Get (line 3976) | void DMCDMA_Get()
method DMCDMA_Halted (line 4016) | void DMCDMA_Halted()
method DMCDMA_Put (line 4020) | void DMCDMA_Put()
method PollInterrupts (line 4027) | void PollInterrupts()
method PollInterrupts_CantDisableIRQ (line 4038) | void PollInterrupts_CantDisableIRQ()
method CompleteOperation (line 4052) | void CompleteOperation()
method _6502 (line 4060) | public void _6502()
method ResetReadPush (line 8851) | public void ResetReadPush()
method Push (line 8858) | public void Push(byte A)
method Observe (line 8911) | public byte Observe(ushort Address)
method Fetch (line 8991) | public byte Fetch(ushort Address)
method ObservePPU (line 9152) | public byte ObservePPU(ushort Address)
method PPUAddressWithMirroring (line 9200) | ushort PPUAddressWithMirroring(ushort Address)
method MapperObserve (line 9224) | byte MapperObserve(ushort Address, byte Mapper)
method MapperFetch (line 9234) | void MapperFetch(ushort Address, byte Mapper)
method ReadOAM (line 9245) | byte ReadOAM()
method Store (line 9263) | public void Store(byte Input, ushort Address)
method StorePPURegisters (line 9442) | public void StorePPURegisters(ushort Addr, byte In)
method StorePPUData (line 9701) | void StorePPUData(ushort Address, byte In)
method StartDMCSample (line 9733) | void StartDMCSample()
method GetImmediate (line 9745) | void GetImmediate()
method GetAddressAbsolute (line 9753) | void GetAddressAbsolute()
method GetAddressZeroPage (line 9769) | void GetAddressZeroPage()
method GetAddressIndOffX (line 9776) | void GetAddressIndOffX()
method GetAddressIndOffY (line 9799) | void GetAddressIndOffY(bool TakeExtraCycleOnlyIfPageBoundaryCrossed)
method GetAddressZPOffX (line 9864) | void GetAddressZPOffX()
method GetAddressZPOffY (line 9881) | void GetAddressZPOffY()
method GetAddressAbsOffX (line 9898) | void GetAddressAbsOffX(bool TakeExtraCycleIfPageBoundaryCrossed)
method GetAddressAbsOffY (line 9977) | void GetAddressAbsOffY(bool TakeExtraCycleIfPageBoundaryCrossed)
method Op_ORA (line 10064) | void Op_ORA(byte Input)
method Op_ASL (line 10072) | void Op_ASL(byte Input, ushort Address)
method Op_ASL_A (line 10082) | void Op_ASL_A()
method Op_SLO (line 10091) | void Op_SLO(byte Input, ushort Address)
method Op_AND (line 10098) | void Op_AND(byte Input)
method Op_ROL (line 10106) | void Op_ROL(byte Input, ushort Address)
method Op_ROL_A (line 10121) | void Op_ROL_A()
method Op_RLA (line 10135) | void Op_RLA(byte Input, ushort Address)
method Op_EOR (line 10142) | void Op_EOR(byte Input)
method Op_LSR (line 10150) | void Op_LSR(byte Input, ushort Address)
method Op_LSR_A (line 10160) | void Op_LSR_A()
method Op_SRE (line 10169) | void Op_SRE(byte Input, ushort Address)
method Op_ADC (line 10176) | void Op_ADC(byte Input)
method Op_ROR (line 10187) | void Op_ROR(byte Input, ushort Address)
method Op_ROR_A (line 10202) | void Op_ROR_A()
method Op_RRA (line 10215) | void Op_RRA(byte Input, ushort Address)
method Op_CMP (line 10222) | void Op_CMP(byte Input)
method Op_CPY (line 10230) | void Op_CPY(byte Input)
method Op_CPX (line 10238) | void Op_CPX(byte Input)
method Op_SBC (line 10246) | void Op_SBC(byte Input)
method Op_INC (line 10261) | void Op_INC(ushort Address)
method Op_DEC (line 10271) | void Op_DEC(ushort Address)
method Debug (line 10290) | void Debug()
method Debug_PPU (line 10463) | void Debug_PPU()
method SaveState (line 10496) | public List<Byte> SaveState()
method LoadState (line 10801) | public void LoadState(List<byte> State)
method Dispose (line 11100) | public void Dispose()
class DirectBitmap (line 11111) | public class DirectBitmap : IDisposable
method DirectBitmap (line 11123) | public DirectBitmap(int width, int height)
method SetPixel (line 11132) | public void SetPixel(int x, int y, Color color)
method SetPixel (line 11140) | public void SetPixel(int x, int y, int colorRGBA)
method GetPixel (line 11146) | public Color GetPixel(int x, int y)
method Dispose (line 11155) | public void Dispose()
FILE: Program.cs
class Program (line 9) | internal static class Program
method Main (line 14) | [STAThread]
FILE: Properties/Resources.Designer.cs
class Resources (line 22) | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resource...
method Resources (line 32) | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Mic...
FILE: Properties/Settings.Designer.cs
class Settings (line 15) | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
FILE: forms/TASProperties.Designer.cs
class TASProperties (line 3) | partial class TASProperties
method Dispose (line 14) | protected override void Dispose(bool disposing)
method InitializeComponent (line 29) | private void InitializeComponent()
FILE: forms/TASProperties.cs
class TASProperties (line 16) | public partial class TASProperties : Form
method TASProperties (line 18) | public TASProperties()
method SubframeInputs (line 28) | public bool SubframeInputs()
method UseFCEUXFrame0Timing (line 33) | public bool UseFCEUXFrame0Timing() // this only applies to TASes using...
method GetPPUClockPhase (line 38) | public byte GetPPUClockPhase()
method GetCPUClockPhase (line 43) | public byte GetCPUClockPhase()
method Init (line 50) | public void Init()
method b_RunTAS_Click (line 121) | private void b_RunTAS_Click(object sender, EventArgs e)
FILE: forms/TASProperties3ct.Designer.cs
class TASProperties3ct (line 3) | partial class TASProperties3ct
method Dispose (line 14) | protected override void Dispose(bool disposing)
method InitializeComponent (line 29) | private void InitializeComponent()
FILE: forms/TASProperties3ct.cs
class TASProperties3ct (line 15) | public partial class TASProperties3ct : Form
method TASProperties3ct (line 17) | public TASProperties3ct()
method GetPPUClockPhase (line 26) | public byte GetPPUClockPhase()
method GetCPUClockPhase (line 31) | public byte GetCPUClockPhase()
method FromRESET (line 36) | public bool FromRESET()
method Init (line 43) | public void Init()
method b_RunTAS_Click (line 56) | private void b_RunTAS_Click(object sender, EventArgs e)
method b_LoadCartridges_Click (line 89) | private void b_LoadCartridges_Click(object sender, EventArgs e)
FILE: forms/TriCHexEditor.Designer.cs
class TriCHexEditor (line 3) | partial class TriCHexEditor
method Dispose (line 14) | protected override void Dispose(bool disposing)
method InitializeComponent (line 29) | private void InitializeComponent()
FILE: forms/TriCHexEditor.cs
class TriCHexEditor (line 14) | public partial class TriCHexEditor : Form
method TriCHexEditor (line 16) | public TriCHexEditor()
method TriCHexEditor_Resize (line 37) | private void TriCHexEditor_Resize(object sender, EventArgs e)
method Scrollbar_ValueChanged (line 48) | private void Scrollbar_ValueChanged(object sender, EventArgs e)
type ScopeType (line 54) | enum ScopeType
method Update (line 64) | public void Update()
method RefreshEntireHexView (line 80) | public void RefreshEntireHexView()
method ChangeScope (line 208) | void ChangeScope(ScopeType st)
method rAMToolStripMenuItem_Click (line 238) | private void rAMToolStripMenuItem_Click(object sender, EventArgs e)
method cPUAddressSpaceToolStripMenuItem_Click (line 243) | private void cPUAddressSpaceToolStripMenuItem_Click(object sender, Eve...
method vRAMToolStripMenuItem_Click (line 248) | private void vRAMToolStripMenuItem_Click(object sender, EventArgs e)
method pPUAddressSpaceToolStripMenuItem_Click (line 253) | private void pPUAddressSpaceToolStripMenuItem_Click(object sender, Eve...
method oAMToolStripMenuItem_Click (line 258) | private void oAMToolStripMenuItem_Click(object sender, EventArgs e)
method paletteRAMToolStripMenuItem_Click (line 263) | private void paletteRAMToolStripMenuItem_Click(object sender, EventArg...
method copyToClipboardToolStripMenuItem_Click (line 268) | private void copyToClipboardToolStripMenuItem_Click(object sender, Eve...
FILE: forms/TriCNESGUI.Designer.cs
class TriCNESGUI (line 6) | partial class TriCNESGUI
method Dispose (line 17) | protected override void Dispose(bool disposing)
method InitializeComponent (line 32) | private void InitializeComponent()
FILE: forms/TriCNESGUI.cs
class TriCNESGUI (line 17) | public partial class TriCNESGUI : Form
method TriCNESGUI (line 24) | public TriCNESGUI()
method RunUpkeep (line 59) | void RunUpkeep()
method RunPostFramePhase (line 132) | void RunPostFramePhase()
method ControllerInputs (line 209) | bool[] ControllerInputs()
method RealtimeInputs (line 237) | byte RealtimeInputs()
method ClockEmulator (line 254) | void ClockEmulator(CancellationToken ct)
method RenderNametable (line 275) | public Bitmap RenderNametable()
method LoadROM (line 400) | public bool LoadROM(string FilePath)
method InsertDisk (line 448) | public void InsertDisk(string filepath)
method ClockEmulator3CT (line 456) | void ClockEmulator3CT(CancellationToken ct)
method loadROMToolStripMenuItem_Click (line 485) | private void loadROMToolStripMenuItem_Click(object sender, EventArgs e)
method loadTASToolStripMenuItem_Click (line 529) | private void loadTASToolStripMenuItem_Click(object sender, EventArgs e)
method StartTAS (line 567) | public void StartTAS()
method Start3CTTAS (line 679) | public void Start3CTTAS()
method load3ctToolStripMenuItem_Click (line 719) | private void load3ctToolStripMenuItem_Click(object sender, EventArgs e)
method resetToolStripMenuItem_Click (line 750) | private void resetToolStripMenuItem_Click(object sender, EventArgs e)
method powerCycleToolStripMenuItem_Click (line 758) | private void powerCycleToolStripMenuItem_Click(object sender, EventArg...
method screenshotToolStripMenuItem_Click (line 774) | private void screenshotToolStripMenuItem_Click(object sender, EventArg...
method pb_Screen_DragEnter (line 779) | private void pb_Screen_DragEnter(object sender, DragEventArgs e)
method pb_Screen_DragDrop (line 786) | private void pb_Screen_DragDrop(object sender, DragEventArgs e)
method TriCNESGUI_Closing (line 831) | private void TriCNESGUI_Closing(Object sender, FormClosingEventArgs e)
method phase0ToolStripMenuItem_Click (line 865) | private void phase0ToolStripMenuItem_Click(object sender, EventArgs e)
method phase1ToolStripMenuItem_Click (line 874) | private void phase1ToolStripMenuItem_Click(object sender, EventArgs e)
method phase2ToolStripMenuItem_Click (line 883) | private void phase2ToolStripMenuItem_Click(object sender, EventArgs e)
method phase3ToolStripMenuItem_Click (line 892) | private void phase3ToolStripMenuItem_Click(object sender, EventArgs e)
method RebootWithAlignment (line 901) | private void RebootWithAlignment(byte Alignment)
method trueToolStripMenuItem_Click (line 918) | private void trueToolStripMenuItem_Click(object sender, EventArgs e)
method falseToolStripMenuItem_Click (line 932) | private void falseToolStripMenuItem_Click(object sender, EventArgs e)
method showRawSignalsToolStripMenuItem_Click (line 946) | private void showRawSignalsToolStripMenuItem_Click(object sender, Even...
method ResizeWindow (line 960) | public void ResizeWindow(int scale)
method xToolStripMenuItem_Click (line 987) | private void xToolStripMenuItem_Click(object sender, EventArgs e)
method xToolStripMenuItem1_Click (line 993) | private void xToolStripMenuItem1_Click(object sender, EventArgs e)
method xToolStripMenuItem2_Click (line 999) | private void xToolStripMenuItem2_Click(object sender, EventArgs e)
method xToolStripMenuItem3_Click (line 1005) | private void xToolStripMenuItem3_Click(object sender, EventArgs e)
method xToolStripMenuItem4_Click (line 1011) | private void xToolStripMenuItem4_Click(object sender, EventArgs e)
method xToolStripMenuItem5_Click (line 1017) | private void xToolStripMenuItem5_Click(object sender, EventArgs e)
method xToolStripMenuItem6_Click (line 1023) | private void xToolStripMenuItem6_Click(object sender, EventArgs e)
method xToolStripMenuItem7_Click (line 1029) | private void xToolStripMenuItem7_Click(object sender, EventArgs e)
method traceLoggerToolStripMenuItem_Click (line 1035) | private void traceLoggerToolStripMenuItem_Click(object sender, EventAr...
method trueToolStripMenuItem1_Click (line 1049) | private void trueToolStripMenuItem1_Click(object sender, EventArgs e)
method falseToolStripMenuItem1_Click (line 1060) | private void falseToolStripMenuItem1_Click(object sender, EventArgs e)
method nametableViewerToolStripMenuItem_Click (line 1071) | private void nametableViewerToolStripMenuItem_Click(object sender, Eve...
method hexEditorToolStripMenuItem_Click (line 1084) | private void hexEditorToolStripMenuItem_Click(object sender, EventArgs e)
method saveStateToolStripMenuItem_Click (line 1099) | private void saveStateToolStripMenuItem_Click(object sender, EventArgs e)
method loadStateToolStripMenuItem_Click (line 1104) | private void loadStateToolStripMenuItem_Click(object sender, EventArgs e)
method tASTimelineToolStripMenuItem_Click (line 1112) | private void tASTimelineToolStripMenuItem_Click(object sender, EventAr...
method ParseTasFile (line 1157) | public List<ushort> ParseTasFile(string TasFilePath, out List<bool> Re...
method FamtasiaInput2Standard (line 1609) | byte FamtasiaInput2Standard(byte input)
method CreateTASTimelineEmulator (line 1629) | public void CreateTASTimelineEmulator()
method ClockTimelineEmulator (line 1676) | void ClockTimelineEmulator(CancellationToken ct)
method OtherControllerHotkeys (line 1885) | public bool[] OtherControllerHotkeys()
class PictureBoxWithInterpolationMode (line 1915) | public class PictureBoxWithInterpolationMode : PictureBox
method PictureBoxWithInterpolationMode (line 1918) | public PictureBoxWithInterpolationMode()
method OnPaint (line 1925) | protected override void OnPaint(PaintEventArgs paintEventArgs)
FILE: forms/TriCNTViewer.Designer.cs
class TriCNTViewer (line 3) | partial class TriCNTViewer
method Dispose (line 14) | protected override void Dispose(bool disposing)
method InitializeComponent (line 29) | private void InitializeComponent()
FILE: forms/TriCNTViewer.cs
class TriCNTViewer (line 13) | public partial class TriCNTViewer : Form
method TriCNTViewer (line 16) | public TriCNTViewer()
method TriCNTViewer_Closing (line 22) | private void TriCNTViewer_Closing(Object sender, FormClosingEventArgs e)
method Update (line 28) | public void Update(Bitmap b)
method UseBackdrop (line 45) | public bool UseBackdrop()
method DrawBoundary (line 50) | public bool DrawBoundary()
method OverlayScreen (line 54) | public bool OverlayScreen()
method screenshotToolStripMenuItem_Click (line 59) | private void screenshotToolStripMenuItem_Click(object sender, EventArg...
FILE: forms/TriCTASTimeline.Designer.cs
class TriCTASTimeline (line 5) | partial class TriCTASTimeline
method Dispose (line 16) | protected override void Dispose(bool disposing)
method InitializeComponent (line 31) | private void InitializeComponent()
FILE: forms/TriCTASTimeline.cs
class TriCTASTimeline (line 14) | public partial class TriCTASTimeline : Form
type Vector2 (line 32) | struct Vector2
method Vector2 (line 36) | public Vector2(int X, int Y)
method ToString (line 41) | public override string ToString()
type TimelineCell (line 47) | public struct TimelineCell
method TimelineCell (line 53) | public TimelineCell(bool t)
method TriCTASTimeline (line 63) | public TriCTASTimeline(TriCNESGUI Maingui)
method mouseWheelEvent (line 115) | private void mouseWheelEvent(object sender, MouseEventArgs e)
method TriCTASTimeline_Shown (line 129) | void TriCTASTimeline_Shown(object sender, EventArgs e)
method RefreshTopOfTimeline (line 134) | void RefreshTopOfTimeline()
method loopTimerEvent (line 187) | private void loopTimerEvent(Object source, ElapsedEventArgs e)
method autosaveEvent (line 193) | private void autosaveEvent(Object source, ElapsedEventArgs e)
method TimelineMouseHeldEvent (line 214) | public void TimelineMouseHeldEvent()
method mouseDownEvent (line 295) | private void mouseDownEvent(object sender, MouseEventArgs e)
method TimelineMouseDownEvent (line 317) | public void TimelineMouseDownEvent()
method MarkStale (line 456) | public void MarkStale(int Frame)
method mouseUpEvent (line 473) | private void mouseUpEvent(object sender, MouseEventArgs e)
method GetCellInputStatus (line 482) | bool GetCellInputStatus(Vector2 pos)
method SetCellInputStatus (line 501) | ushort SetCellInputStatus(Vector2 pos, bool state)
method Start (line 559) | public void Start()
method timelineScrollbar_ValueChanged (line 596) | private void timelineScrollbar_ValueChanged(object sender, EventArgs e)
method b_FrameAdvance_Click (line 606) | private void b_FrameAdvance_Click(object sender, EventArgs e)
method b_FrameBack_Click (line 611) | private void b_FrameBack_Click(object sender, EventArgs e)
method FrameRewind (line 616) | public void FrameRewind()
method FrameAdvance (line 650) | public void FrameAdvance()
method loadTASToolStripMenuItem_Click (line 752) | private void loadTASToolStripMenuItem_Click(object sender, EventArgs e)
method saveTASToolStripMenuItem_Click (line 812) | private void saveTASToolStripMenuItem_Click(object sender, EventArgs e)
method saveWithSavestatesToolStripMenuItem_Click (line 866) | private void saveWithSavestatesToolStripMenuItem_Click(object sender, ...
method exportTor08ToolStripMenuItem_Click (line 961) | private void exportTor08ToolStripMenuItem_Click(object sender, EventAr...
method b_play_Click (line 1014) | private void b_play_Click(object sender, EventArgs e)
method deleteFrameToolStripMenuItem_Click (line 1026) | private void deleteFrameToolStripMenuItem_Click(object sender, EventAr...
method insertFrameToolStripMenuItem_Click (line 1039) | private void insertFrameToolStripMenuItem_Click(object sender, EventAr...
method Player2 (line 1051) | public bool Player2()
method truncateMovieToolStripMenuItem_Click (line 1056) | private void truncateMovieToolStripMenuItem_Click(object sender, Event...
method b_JumptoCursor_Click (line 1078) | private void b_JumptoCursor_Click(object sender, EventArgs e)
method RefreshTimeline (line 1090) | public void RefreshTimeline()
method RedrawTimelineRow (line 1112) | public void RedrawTimelineRow(int row, bool batch)
method RecalculateTimelineRow (line 1203) | public void RecalculateTimelineRow(int row, ushort input)
method UpdateTimelineRowStatus (line 1244) | public void UpdateTimelineRowStatus(int row)
method tb_FollowDistance_Scroll (line 1266) | private void tb_FollowDistance_Scroll(object sender, EventArgs e)
method SavestateEveryFrame (line 1271) | public bool SavestateEveryFrame()
method RecordInputs (line 1275) | public bool RecordInputs()
method GrabMostRecentSavestate (line 1279) | List<byte> GrabMostRecentSavestate()
method GrabMostRecentSavestate (line 1308) | List<byte> GrabMostRecentSavestate(int TargetFrame, out bool HitTarget...
method savestateThisFrameToolStripMenuItem_Click (line 1349) | private void savestateThisFrameToolStripMenuItem_Click(object sender, ...
method TrimTempSavestates (line 1372) | public void TrimTempSavestates()
method tb_FilterForNumbers (line 1387) | private void tb_FilterForNumbers(object sender, KeyPressEventArgs e)
method tb_AutoSavestateThreshold_TextChanged (line 1395) | private void tb_AutoSavestateThreshold_TextChanged(object sender, Even...
method tb_TempSavestates_TextChanged (line 1405) | private void tb_TempSavestates_TextChanged(object sender, EventArgs e)
method ChangePlayPauseButtonText (line 1414) | public void ChangePlayPauseButtonText(string str)
method perVBlankToolStripMenuItem_Click (line 1425) | private void perVBlankToolStripMenuItem_Click(object sender, EventArgs e)
method perControllerStrobeToolStripMenuItem_Click (line 1436) | private void perControllerStrobeToolStripMenuItem_Click(object sender,...
method ResetTASAndMarkEverythingStale (line 1446) | void ResetTASAndMarkEverythingStale()
FILE: forms/TriCTraceLogger.Designer.cs
class TriCTraceLogger (line 3) | partial class TriCTraceLogger
method Dispose (line 14) | protected override void Dispose(bool disposing)
method InitializeComponent (line 29) | private void InitializeComponent()
FILE: forms/TriCTraceLogger.cs
class TriCTraceLogger (line 14) | public partial class TriCTraceLogger : Form
method TriCTraceLogger (line 18) | public TriCTraceLogger()
method TriCTraceLogger_Closing (line 24) | private void TriCTraceLogger_Closing(Object sender, FormClosingEventAr...
method Init (line 29) | public void Init()
method Update (line 34) | public void Update()
method b_ToggleButton_CheckedChanged (line 54) | private void b_ToggleButton_CheckedChanged(object sender, EventArgs e)
method cb_LogInRange_CheckedChanged (line 60) | private void cb_LogInRange_CheckedChanged(object sender, EventArgs e)
method tb_RangeLow_TextChanged (line 68) | private void tb_RangeLow_TextChanged(object sender, EventArgs e)
method tb_RangeHigh_TextChanged (line 74) | private void tb_RangeHigh_TextChanged(object sender, EventArgs e)
method OnlyDebugInRange (line 82) | public bool OnlyDebugInRange()
method ClearEveryFrame (line 86) | public bool ClearEveryFrame()
method LogPPUCycles (line 91) | public bool LogPPUCycles()
FILE: mappers/Mapper_AOROM.cs
class Mapper_AOROM (line 7) | public class Mapper_AOROM : Mapper
method FetchPRG (line 11) | public override void FetchPRG(ushort Address, bool Observe)
method StorePRG (line 32) | public override void StorePRG(ushort Address, byte Input)
method MirrorNametable (line 39) | public override ushort MirrorNametable(ushort Address)
method SaveMapperRegisters (line 52) | public override List<byte> SaveMapperRegisters()
method LoadMapperRegisters (line 60) | public override void LoadMapperRegisters(List<byte> State, int startIn...
FILE: mappers/Mapper_CNROM.cs
class Mapper_CNROM (line 6) | public class Mapper_CNROM : Mapper
method StorePRG (line 10) | public override void StorePRG(ushort Address, byte Input)
method FetchCHR (line 17) | public override byte FetchCHR(ushort Address, bool Observe)
method SaveMapperRegisters (line 21) | public override List<byte> SaveMapperRegisters()
method LoadMapperRegisters (line 29) | public override void LoadMapperRegisters(List<byte> State, int startIn...
FILE: mappers/Mapper_FDS.cs
class Mapper_FDS (line 6) | public class Mapper_FDS : Mapper
method Mapper_FDS (line 14) | public Mapper_FDS(byte[] fds_bios)
method FetchPRG (line 19) | public override void FetchPRG(ushort Address, bool Observe)
method FetchCHR (line 89) | public override byte FetchCHR(ushort Address, bool Observe)
method StorePRG (line 94) | public override void StorePRG(ushort Address, byte Input)
method FDS_ByteTransferFlag (line 125) | public override void FDS_ByteTransferFlag()
method FDS_Get4025 (line 132) | public override byte FDS_Get4025()
method SaveMapperRegisters (line 137) | public override List<byte> SaveMapperRegisters()
method LoadMapperRegisters (line 153) | public override void LoadMapperRegisters(List<byte> State, int startIn...
FILE: mappers/Mapper_FME7.cs
class Mapper_FME7 (line 6) | public class Mapper_FME7 : Mapper
method FetchPRG (line 28) | public override void FetchPRG(ushort Address, bool Observe)
method StorePRG (line 86) | public override void StorePRG(ushort Address, byte Input)
method FetchCHR (line 130) | public override byte FetchCHR(ushort Address, bool Observe)
method MirrorNametable (line 141) | public override ushort MirrorNametable(ushort Address)
method SaveMapperRegisters (line 161) | public override List<byte> SaveMapperRegisters()
method LoadMapperRegisters (line 188) | public override void LoadMapperRegisters(List<byte> State, int startIn...
method CPUClock (line 215) | public override void CPUClock()
FILE: mappers/Mapper_MMC1.cs
class Mapper_MMC1 (line 6) | public class Mapper_MMC1 : Mapper
method FetchPRG (line 15) | public override void FetchPRG(ushort Address, bool Observe)
method StorePRG (line 82) | public override void StorePRG(ushort Address, byte Input)
method FetchCHR (line 129) | public override byte FetchCHR(ushort Address, bool Observe)
method MirrorNametable (line 147) | public override ushort MirrorNametable(ushort Address)
method SaveMapperRegisters (line 168) | public override List<byte> SaveMapperRegisters()
method LoadMapperRegisters (line 181) | public override void LoadMapperRegisters(List<byte> State, int startIn...
FILE: mappers/Mapper_MMC2.cs
class Mapper_MMC2 (line 6) | public class Mapper_MMC2 : Mapper
method FetchPRG (line 17) | public override void FetchPRG(ushort Address, bool Observe)
method StorePRG (line 41) | public override void StorePRG(ushort Address, byte Input)
method FetchCHR (line 72) | public override byte FetchCHR(ushort Address, bool Observe)
method MirrorNametable (line 99) | public override ushort MirrorNametable(ushort Address)
method SaveMapperRegisters (line 111) | public override List<byte> SaveMapperRegisters()
method LoadMapperRegisters (line 125) | public override void LoadMapperRegisters(List<byte> State, int startIn...
FILE: mappers/Mapper_MMC3.cs
class Mapper_MMC3 (line 6) | public class Mapper_MMC3 : Mapper
method FetchPRG (line 25) | public override void FetchPRG(ushort Address, bool Observe)
method StorePRG (line 113) | public override void StorePRG(ushort Address, byte Input)
method FetchCHR (line 207) | public override byte FetchCHR(ushort Address, bool Observe)
method FetchPPU (line 229) | public override byte FetchPPU()
method MirrorNametable (line 278) | public override ushort MirrorNametable(ushort Address)
method SaveMapperRegisters (line 293) | public override List<byte> SaveMapperRegisters()
method LoadMapperRegisters (line 320) | public override void LoadMapperRegisters(List<byte> State, int startIn...
method PPUClock (line 348) | public override void PPUClock()
method CPUClockRise (line 396) | public override void CPUClockRise()
FILE: mappers/Mapper_NROM.cs
class Mapper_NROM (line 6) | public class Mapper_NROM : Mapper
FILE: mappers/Mapper_NULL.cs
class Mapper_NULL (line 6) | public class Mapper_NULL : Mapper
method FetchPRG (line 10) | public override void FetchPRG(ushort Address, bool Observe)
method FetchCHR (line 17) | public override byte FetchCHR(ushort Address, bool Observe)
method MirrorNametable (line 22) | public override ushort MirrorNametable(ushort Address)
method SaveMapperRegisters (line 26) | public override List<byte> SaveMapperRegisters()
method LoadMapperRegisters (line 31) | public override void LoadMapperRegisters(List<byte> State, int startIn...
FILE: mappers/Mapper_UxROM.cs
class Mapper_UxROM (line 6) | public class Mapper_UxROM : Mapper
method FetchPRG (line 10) | public override void FetchPRG(ushort Address, bool Observe)
method StorePRG (line 38) | public override void StorePRG(ushort Address, byte Input)
method SaveMapperRegisters (line 45) | public override List<byte> SaveMapperRegisters()
method LoadMapperRegisters (line 53) | public override void LoadMapperRegisters(List<byte> State, int startIn...
Condensed preview — 47 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,061K chars).
[
{
"path": ".gitattributes",
"chars": 66,
"preview": "# Auto detect text files and perform LF normalization\n* text=auto\n"
},
{
"path": ".gitignore",
"chars": 7853,
"preview": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## G"
},
{
"path": "6502Documentation.cs",
"chars": 63402,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nna"
},
{
"path": "App.config",
"chars": 238,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n\t<dllmap dll=\"SDL2\" os=\"windows\" target=\"SDL2.dll\"/>\n\t<dllmap dll"
},
{
"path": "Emulator.cs",
"chars": 522618,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Drawing;\nusing System.Drawing.Imaging;\nusing System.IO;\nus"
},
{
"path": "LICENSE",
"chars": 1070,
"preview": "MIT License\n\nCopyright (c) 2025 Chris Siebert\n\nPermission is hereby granted, free of charge, to any person obtaining a c"
},
{
"path": "Program.cs",
"chars": 520,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing System.Windows.F"
},
{
"path": "Properties/AssemblyInfo.cs",
"chars": 1382,
"preview": "using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Infor"
},
{
"path": "Properties/Resources.Designer.cs",
"chars": 2771,
"preview": "//------------------------------------------------------------------------------\n// <auto-generated>\n// This code w"
},
{
"path": "Properties/Resources.resx",
"chars": 5494,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n <!-- \n Microsoft ResX Schema \n \n Version 2.0\n \n The prim"
},
{
"path": "Properties/Settings.Designer.cs",
"chars": 1062,
"preview": "//------------------------------------------------------------------------------\n// <auto-generated>\n// This code w"
},
{
"path": "Properties/Settings.settings",
"chars": 240,
"preview": "<?xml version='1.0' encoding='utf-8'?>\n<SettingsFile xmlns=\"http://schemas.microsoft.com/VisualStudio/2004/01/settings\""
},
{
"path": "README.md",
"chars": 2929,
"preview": "# TriCNES\n\nTriCNES, or \"Coin's Contrabulous Cartswapulator\", is a Nintendo Entertainment System emulator written by Chri"
},
{
"path": "TriCNES.csproj",
"chars": 7141,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbui"
},
{
"path": "TriCNES.sln",
"chars": 1318,
"preview": "\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 17\nVisualStudioVersion = 17.10.350"
},
{
"path": "forms/TASProperties.Designer.cs",
"chars": 12814,
"preview": "namespace TriCNES\n{\n partial class TASProperties\n {\n /// <summary>\n /// Required designer variable."
},
{
"path": "forms/TASProperties.cs",
"chars": 3653,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Data;\nusing System.Drawing;\nu"
},
{
"path": "forms/TASProperties.resx",
"chars": 12551,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n <!-- \n Microsoft ResX Schema \n \n Version 2.0\n \n The prim"
},
{
"path": "forms/TASProperties3ct.Designer.cs",
"chars": 10388,
"preview": "namespace TriCNES\n{\n partial class TASProperties3ct\n {\n /// <summary>\n /// Required designer variab"
},
{
"path": "forms/TASProperties3ct.cs",
"chars": 5272,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Data;\nusing System.Drawing;\nu"
},
{
"path": "forms/TASProperties3ct.resx",
"chars": 12351,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n <!-- \n Microsoft ResX Schema \n \n Version 2.0\n \n The prim"
},
{
"path": "forms/TriCHexEditor.Designer.cs",
"chars": 10229,
"preview": "namespace TriCNES\n{\n partial class TriCHexEditor\n {\n /// <summary>\n /// Required designer variable."
},
{
"path": "forms/TriCHexEditor.cs",
"chars": 11280,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Data;\nusing System.Drawing;\nu"
},
{
"path": "forms/TriCHexEditor.resx",
"chars": 12546,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n <!-- \n Microsoft ResX Schema \n \n Version 2.0\n \n The prim"
},
{
"path": "forms/TriCNESGUI.Designer.cs",
"chars": 27054,
"preview": "using System.Threading;\nusing System.Windows.Forms;\n\nnamespace TriCNES\n{\n partial class TriCNESGUI\n {\n ///"
},
{
"path": "forms/TriCNESGUI.cs",
"chars": 84833,
"preview": "using SDL2;\nusing System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Drawing;\nusing System.Drawi"
},
{
"path": "forms/TriCNESGUI.resx",
"chars": 13065,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n <!-- \n Microsoft ResX Schema \n \n Version 2.0\n \n The prim"
},
{
"path": "forms/TriCNTViewer.Designer.cs",
"chars": 6848,
"preview": "namespace TriCNES\n{\n partial class TriCNTViewer\n {\n /// <summary>\n /// Required designer variable.\n"
},
{
"path": "forms/TriCNTViewer.cs",
"chars": 1475,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Data;\nusing System.Drawing;\nu"
},
{
"path": "forms/TriCNTViewer.resx",
"chars": 12546,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n <!-- \n Microsoft ResX Schema \n \n Version 2.0\n \n The prim"
},
{
"path": "forms/TriCTASTimeline.Designer.cs",
"chars": 24381,
"preview": "using System.Windows.Forms;\n\nnamespace TriCNES\n{\n partial class TriCTASTimeline\n {\n /// <summary>\n "
},
{
"path": "forms/TriCTASTimeline.cs",
"chars": 59246,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Drawing;\nusing System.IO;\nusing "
},
{
"path": "forms/TriCTASTimeline.resx",
"chars": 14148,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n <!-- \n Microsoft ResX Schema \n \n Version 2.0\n \n The prim"
},
{
"path": "forms/TriCTraceLogger.Designer.cs",
"chars": 8031,
"preview": "namespace TriCNES\n{\n partial class TriCTraceLogger\n {\n /// <summary>\n /// Required designer variabl"
},
{
"path": "forms/TriCTraceLogger.cs",
"chars": 2771,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Data;\nusing System.Drawing;\nu"
},
{
"path": "forms/TriCTraceLogger.resx",
"chars": 12351,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n <!-- \n Microsoft ResX Schema \n \n Version 2.0\n \n The prim"
},
{
"path": "mappers/Mapper_AOROM.cs",
"chars": 2382,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Net;\n\nnamespace TriCNES.mappers\n{\n public class Mapper_"
},
{
"path": "mappers/Mapper_CNROM.cs",
"chars": 1305,
"preview": "using System;\nusing System.Collections.Generic;\n\nnamespace TriCNES.mappers\n{\n public class Mapper_CNROM : Mapper\n "
},
{
"path": "mappers/Mapper_FDS.cs",
"chars": 5933,
"preview": "using System;\nusing System.Collections.Generic;\n\nnamespace TriCNES.mappers\n{\n public class Mapper_FDS : Mapper\n {"
},
{
"path": "mappers/Mapper_FME7.cs",
"chars": 11083,
"preview": "using System;\nusing System.Collections.Generic;\n\nnamespace TriCNES.mappers\n{\n public class Mapper_FME7 : Mapper\n "
},
{
"path": "mappers/Mapper_MMC1.cs",
"chars": 8345,
"preview": "using System;\nusing System.Collections.Generic;\n\nnamespace TriCNES.mappers\n{\n public class Mapper_MMC1 : Mapper\n "
},
{
"path": "mappers/Mapper_MMC2.cs",
"chars": 5320,
"preview": "using System;\nusing System.Collections.Generic;\n\nnamespace TriCNES.mappers\n{\n public class Mapper_MMC2 : Mapper\n "
},
{
"path": "mappers/Mapper_MMC3.cs",
"chars": 17971,
"preview": "using System;\nusing System.Collections.Generic;\n\nnamespace TriCNES.mappers\n{\n public class Mapper_MMC3 : Mapper\n "
},
{
"path": "mappers/Mapper_NROM.cs",
"chars": 155,
"preview": "using System;\nusing System.Collections.Generic;\n\nnamespace TriCNES.mappers\n{\n public class Mapper_NROM : Mapper\n "
},
{
"path": "mappers/Mapper_NULL.cs",
"chars": 1143,
"preview": "using System;\nusing System.Collections.Generic;\n\nnamespace TriCNES.mappers\n{\n public class Mapper_NULL : Mapper\n "
},
{
"path": "mappers/Mapper_UxROM.cs",
"chars": 2190,
"preview": "using System;\nusing System.Collections.Generic;\n\nnamespace TriCNES.mappers\n{\n public class Mapper_UxROM : Mapper\n "
},
{
"path": "packages.config",
"chars": 134,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n <package id=\"ppy.SDL2-CS\" version=\"1.0.82\" targetFramework=\"net48\" "
}
]
About this extraction
This page contains the full source code of the 100thCoin/TriCNES GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 47 files (1007.7 KB), approximately 227.5k tokens, and a symbol index with 375 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.