diff --git a/appveyor.yml b/appveyor.yml index be00ef14..abfbbe32 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -27,37 +27,63 @@ for: - job_name: Windows 32 build_script: - - cmd: pipelines/win32_build.bat + - cmd: perl pipelines/build.pl win32 # - # matrix: # only: # - job_name: Windows 64 -# +# # build_script: -# - cmd: pipelines/win64_build.bat -# +# - cmd: perl pipelines/build.pl win64 +# # - # matrix: # only: # - job_name: Win64 Qt -# +# # build_script: -# - cmd: pipelines/qwin64_build.bat -# +# - cmd: perl pipelines/build.pl win64-QtSDL +# # - # matrix: # only: # - job_name: Ubuntu -# +# # build_script: -# - sh: ./pipelines/linux_build.sh -# +# - sh: perl pipelines/build.pl linux +# # - # matrix: # only: # - job_name: MacOS -# +# # build_script: -# - sh: ./pipelines/macOS_build.sh +# - sh: perl pipelines/build.pl macOS +deploy: + + - provider: GitHub + tag: interim-build + release: interim-build + description: 'Interim Builds - Latest auto builds off master branch - commit: $(APPVEYOR_REPO_COMMIT)\nDate: $(APPVEYOR_REPO_COMMIT_TIMESTAMP)' + auth_token: + secure: pYXqhuxNn7vivsJ6cnWCHlORrTtaWO7fyGOvGFUNKtM2apSY44gjbAhGIlD+OdtF + artifact: $(WIN32_ARTIFACT), $(WIN64_ARTIFACT), $(WIN64_QTSDL_ARTIFACT), $(MACOS_ARTIFACT), $(LINUX_ARTIFACT) + draft: false + prerelease: true + force_update: true + on: + branch: master # release from master branch only + APPVEYOR_REPO_TAG: false # never deploy on tag push + + - provider: GitHub + description: 'Release Builds - commit: $(APPVEYOR_REPO_COMMIT)' + auth_token: + secure: pYXqhuxNn7vivsJ6cnWCHlORrTtaWO7fyGOvGFUNKtM2apSY44gjbAhGIlD+OdtF + artifact: $(WIN32_ARTIFACT), $(WIN64_ARTIFACT), $(WIN64_QTSDL_ARTIFACT), $(MACOS_ARTIFACT) + draft: false + prerelease: false + force_update: false + on: + APPVEYOR_REPO_TAG: true # deploy on tag push only diff --git a/pipelines/build.pl b/pipelines/build.pl new file mode 100755 index 00000000..7c469136 --- /dev/null +++ b/pipelines/build.pl @@ -0,0 +1,170 @@ +#!/usr/bin/perl + +use strict; +use File::Basename; +#use File::Spec; + +# +# Global Variables +# +my $platform = ""; +my $fceuVersionMajor = 1; +my $fceuVersionMinor = 0; +my $fceuVersionPatch = 0; + +foreach my $arg (@ARGV) +{ + #print $arg, "\n"; + + if ($platform eq "") + { + $platform = $arg; + } +} + +my $dirname = dirname(__FILE__); +my $projRoot = "$dirname/.."; +my $ReleaseBuild=0; +my $ReleaseVersion=""; + +#print "PATH: $ENV{PATH}\n"; +#print "Dir $dirname\n"; +# +($fceuVersionMajor, $fceuVersionMinor, $fceuVersionPatch) = getVersion(); + +($ReleaseBuild, $ReleaseVersion) = isReleaseBuild(); + +if ($ReleaseBuild) +{ + $ENV{FCEU_RELEASE_VERSION} = $ReleaseVersion; +} + +if ($platform eq "win32") +{ + build_win32(); +} +elsif ($platform eq "win64") +{ + build_win64(); +} +elsif ($platform eq "win64-QtSDL") +{ + build_win64_QtSDL(); +} +elsif ($platform eq "linux") +{ + build_ubuntu_linux(); +} +elsif ($platform eq "macOS") +{ + build_macOS(); +} +#-------------------------------------------------------------------------------------------- +# Build win32 version +#-------------------------------------------------------------------------------------------- +sub build_win32 +{ + chdir("$projRoot"); + + my $ret = system("cmd.exe /c pipelines\\\\win32_build.bat"); + + if ($ret != 0){ die "Build Errors Detected\n";} +} +#-------------------------------------------------------------------------------------------- +# Build win64 version +#-------------------------------------------------------------------------------------------- +sub build_win64 +{ + chdir("$projRoot"); + + my $ret = system("cmd.exe /c pipelines\\\\win64_build.bat"); + + if ($ret != 0){ die "Build Errors Detected\n";} +} +#-------------------------------------------------------------------------------------------- +# Build win64-Qt/SDL version +#-------------------------------------------------------------------------------------------- +sub build_win64_QtSDL +{ + chdir("$projRoot"); + + my $ret = system("cmd.exe /c pipelines\\\\qwin64_build.bat"); + + if ($ret != 0){ die "Build Errors Detected\n";} +} +#-------------------------------------------------------------------------------------------- +# Build Ubuntu Linux version +#-------------------------------------------------------------------------------------------- +sub build_ubuntu_linux +{ + chdir("$projRoot"); + + my $ret = system("./pipelines/linux_build.sh"); + + if ($ret != 0){ die "Build Errors Detected\n";} +} +#-------------------------------------------------------------------------------------------- +# Build MacOSX version +#-------------------------------------------------------------------------------------------- +sub build_macOS +{ + chdir("$projRoot"); + + my $ret = system("./pipelines/macOS_build.sh"); + + if ($ret != 0){ die "Build Errors Detected\n";} +} +#-------------------------------------------------------------------------------------------- +# Search src/version.h and retrieve version numbers +#-------------------------------------------------------------------------------------------- +sub getVersion +{ + my $versionHeader = "$projRoot/src/version.h"; + my $line; + my $major = 1; + my $minor = 0; + my $patch = 0; + open INFILE, "$versionHeader" or die "Error: Could not open file: $versionHeader\n"; + while ($line = ) + { + #print $line; + if ($line =~ m/\s*#define\s+FCEU_VERSION_MAJOR\s+(\d+)/) + { + $major = $1; + } + elsif ($line =~ m/\s*#define\s+FCEU_VERSION_MINOR\s+(\d+)/) + { + $minor = $1; + } + elsif ($line =~ m/\s*#define\s+FCEU_VERSION_PATCH\s+(\d+)/) + { + $patch = $1; + } + } + close(INFILE); + + return ( $major, $minor, $patch ); +} +#-------------------------------------------------------------------------------------------- +# Returns whether this is a release build and returns the version if detected +#-------------------------------------------------------------------------------------------- +sub isReleaseBuild +{ + my $isRelease = 0; + my $tagVersion = ""; + + if (defined($ENV{APPVEYOR_REPO_TAG_NAME})) + { + if ($ENV{APPVEYOR_REPO_TAG_NAME} =~ m/fceux-(\d+\.\d+\.\d+)/) + { + $tagVersion = $1; + $isRelease = 1; + } + elsif ($ENV{APPVEYOR_REPO_TAG_NAME} =~ m/(\d+\.\d+\.\d+)/) + { + $tagVersion = $1; + $isRelease = 1; + } + } + return ($isRelease, $tagVersion); +} diff --git a/pipelines/debpkg.pl b/pipelines/debpkg.pl index 758e5d02..0dd554af 100755 --- a/pipelines/debpkg.pl +++ b/pipelines/debpkg.pl @@ -1,8 +1,11 @@ #!/usr/bin/perl use strict; +use File::Basename; -my $VERSION="2.6.4"; +my $dirname = dirname(__FILE__); + +my $VERSION=`perl $dirname/../scripts/fceuVersion.pl`; my $INSTALL_PREFIX="/tmp/fceux"; my $CTL_FILENAME="$INSTALL_PREFIX/DEBIAN/control"; my $ARCH="amd64"; diff --git a/pipelines/linux_build.sh b/pipelines/linux_build.sh index 77922b5e..cc6cf21c 100755 --- a/pipelines/linux_build.sh +++ b/pipelines/linux_build.sh @@ -4,6 +4,7 @@ id pwd uname -a cat /etc/os-release +env SCRIPT_DIR=$( cd $(dirname $BASH_SOURCE[0]); pwd ); @@ -20,6 +21,10 @@ echo "APPVEYOR_SSH_KEY=$APPVEYOR_SSH_KEY"; echo "APPVEYOR_SSH_BLOCK=$APPVEYOR_SSH_BLOCK"; echo '****************************************' +if [ ! -z $FCEU_RELEASE_VERSION ]; then + APPVEYOR_CMAKE_FLAGS=" -DPUBLIC_RELEASE=1 "; +fi + echo '****************************************' echo '****************************************' echo '*** Installing Package Dependencies ***' @@ -119,6 +124,7 @@ cmake \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/usr \ -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON \ + $APPVEYOR_CMAKE_FLAGS \ .. make -j `nproc` make install DESTDIR=$INSTALL_PREFIX @@ -191,5 +197,13 @@ echo 'Testing Install of Package' echo '**************************************************************' sudo dpkg -i /tmp/fceux-*.deb -echo 'Pushing Debian Package to Build Artifacts' -appveyor PushArtifact /tmp/fceux-*.deb +if [ ! -z $APPVEYOR ]; then + echo 'Pushing Debian Package to Build Artifacts' + if [ -z $FCEU_RELEASE_VERSION ]; then + cp /tmp/fceux-*.deb /tmp/fceux-ubuntu-x64.deb + appveyor PushArtifact /tmp/fceux-ubuntu-x64.deb + appveyor SetVariable -Name LINUX_ARTIFACT -Value fceux-ubuntu-x64.deb + else + appveyor PushArtifact /tmp/fceux-*.deb + fi +fi diff --git a/pipelines/macOS_build.sh b/pipelines/macOS_build.sh index e4ef91fa..7b61754a 100755 --- a/pipelines/macOS_build.sh +++ b/pipelines/macOS_build.sh @@ -5,15 +5,19 @@ id pwd uname -a sw_vers +env + +SCRIPT_DIR=$( cd $(dirname $BASH_SOURCE[0]); pwd ); QT_MAJOR=5; QT_PKGNAME=qt$QT_MAJOR; -FCEUX_VERSION_MAJOR=2 -FCEUX_VERSION_MINOR=6 -FCEUX_VERSION_PATCH=4 +FCEUX_VERSION_MAJOR=`perl $SCRIPT_DIR/../scripts/fceuVersion.pl -major`; +FCEUX_VERSION_MINOR=`perl $SCRIPT_DIR/../scripts/fceuVersion.pl -minor`; +FCEUX_VERSION_PATCH=`perl $SCRIPT_DIR/../scripts/fceuVersion.pl -patch`; +FCEUX_VERSION="$FCEUX_VERSION_MAJOR.$FCEUX_VERSION_MINOR.$FCEUX_VERSION_PATCH"; SDL2_VERSION=2.0.20 -SCRIPT_DIR=$( cd $(dirname $BASH_SOURCE[0]); pwd ); +echo "Building Version: $FCEUX_VERSION"; NPROC=`getconf _NPROCESSORS_ONLN`; echo "Number of Processors: $NPROC"; @@ -34,6 +38,10 @@ echo "APPVEYOR_SSH_KEY=$APPVEYOR_SSH_KEY"; echo "APPVEYOR_SSH_BLOCK=$APPVEYOR_SSH_BLOCK"; echo '****************************************' +if [ ! -z $FCEU_RELEASE_VERSION ]; then + APPVEYOR_CMAKE_FLAGS=" -DPUBLIC_RELEASE=1 "; +fi + echo '****************************************' echo 'Install Dependency sdl2' echo '****************************************' @@ -121,13 +129,23 @@ cmake \ -DCPACK_PACKAGE_VERSION_MINOR=$FCEUX_VERSION_MINOR \ -DCPACK_PACKAGE_VERSION_PATCH=$FCEUX_VERSION_PATCH \ -DQT6=$USE_QT6 \ + $APPVEYOR_CMAKE_FLAGS \ .. || exit 1 make -j $NPROC || exit 1 #sudo make install || exit 1 # make install is already run by cpack sudo cpack -G DragNDrop || exit 1 -echo 'Pushing DMG Package to Build Artifacts' -appveyor PushArtifact fceux-*.dmg +if [ ! -z $APPVEYOR ]; then + echo 'Pushing DMG Package to Build Artifacts' + if [ -z $FCEU_RELEASE_VERSION ]; then + cp fceux-*.dmg fceux-Darwin.dmg + appveyor PushArtifact fceux-Darwin.dmg + appveyor SetVariable -Name MACOS_ARTIFACT -Value fceux-Darwin.dmg + else + appveyor PushArtifact fceux-*.dmg + appveyor SetVariable -Name MACOS_ARTIFACT -Value `ls fceux-*.dmg` + fi +fi # Debug via ssh if necessary if [ ! -z $APPVEYOR_SSH_BLOCK ]; then diff --git a/pipelines/qwin64_build.bat b/pipelines/qwin64_build.bat index 165a512c..9e829a71 100644 --- a/pipelines/qwin64_build.bat +++ b/pipelines/qwin64_build.bat @@ -8,6 +8,7 @@ call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary cd /d %CWD% +set where cmake where nmake where msbuild @@ -21,53 +22,68 @@ mkdir build cd build mkdir bin -curl -s -LO http://www.libsdl.org/release/SDL2-devel-2.0.20-VC.zip -curl -s -LO https://github.com/GyanD/codexffmpeg/releases/download/5.0/ffmpeg-5.0-full_build-shared.zip +set SDL_VERSION=2.24.1 +set FFMPEG_VERSION=5.1.2 + +curl -s -LO https://github.com/libsdl-org/SDL/releases/download/release-%SDL_VERSION%/SDL2-devel-%SDL_VERSION%-VC.zip +curl -s -LO https://github.com/GyanD/codexffmpeg/releases/download/%FFMPEG_VERSION%/ffmpeg-%FFMPEG_VERSION%-full_build-shared.zip REM rmdir /q /s SDL2 -powershell -command "Expand-Archive" SDL2-devel-2.0.20-VC.zip . -powershell -command "Expand-Archive" ffmpeg-5.0-full_build-shared.zip +powershell -command "Expand-Archive" SDL2-devel-%SDL_VERSION%-VC.zip . +powershell -command "Expand-Archive" ffmpeg-%FFMPEG_VERSION%-full_build-shared.zip -rename SDL2-2.0.20 SDL2 -move ffmpeg-5.0-full_build-shared\ffmpeg-5.0-full_build-shared ffmpeg -rmdir ffmpeg-5.0-full_build-shared -del ffmpeg-5.0-full_build-shared.zip +rename SDL2-%SDL_VERSION% SDL2 +move ffmpeg-%FFMPEG_VERSION%-full_build-shared\ffmpeg-%FFMPEG_VERSION%-full_build-shared ffmpeg +rmdir ffmpeg-%FFMPEG_VERSION%-full_build-shared +del ffmpeg-%FFMPEG_VERSION%-full_build-shared.zip set SDL_INSTALL_PREFIX=%CD% set FFMPEG_INSTALL_PREFIX=%CD% +set PUBLIC_RELEASE=0 +IF DEFINED FCEU_RELEASE_VERSION (set PUBLIC_RELEASE=1) REM cmake -h REM cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DSDL_INSTALL_PREFIX=%SDL_INSTALL_PREFIX% .. -cmake -DQT6=0 -DSDL_INSTALL_PREFIX=%SDL_INSTALL_PREFIX% -DUSE_LIBAV=1 -DFFMPEG_INSTALL_PREFIX=%FFMPEG_INSTALL_PREFIX% -G"Visual Studio 16" -T"v142" .. +cmake -DQT6=0 -DPUBLIC_RELEASE=%PUBLIC_RELEASE% -DSDL_INSTALL_PREFIX=%SDL_INSTALL_PREFIX% -DUSE_LIBAV=1 -DFFMPEG_INSTALL_PREFIX=%FFMPEG_INSTALL_PREFIX% -G"Visual Studio 16" -T"v142" .. REM nmake msbuild /m fceux.sln /p:Configuration=Release @if ERRORLEVEL 1 goto end copy src\Release\fceux.exe bin\qfceux.exe +copy %PROJECT_ROOT%\src\auxlib.lua bin\. +REM copy %PROJECT_ROOT%\src\drivers\win\lua\x64\lua51.dll bin\. +REM copy %PROJECT_ROOT%\src\drivers\win\lua\x64\lua5.1.dll bin\. copy %SDL_INSTALL_PREFIX%\SDL2\lib\x64\SDL2.dll bin\. copy %FFMPEG_INSTALL_PREFIX%\ffmpeg\bin\*.dll bin\. windeployqt --no-compiler-runtime bin\qfceux.exe +set ZIP_FILENAME=fceux-win64-QtSDL.zip +IF DEFINED FCEU_RELEASE_VERSION set ZIP_FILENAME=fceux-%FCEU_RELEASE_VERSION%-win64-QtSDL.zip + +set DEPLOY_GROUP=master +IF DEFINED APPVEYOR_REPO_TAG_NAME set DEPLOY_GROUP=%APPVEYOR_REPO_TAG_NAME% + dir bin REM Create Zip Archive -%PROJECT_ROOT%\vc\zip -X -9 -r %PROJECT_ROOT%\vc\qfceux64.zip bin +%PROJECT_ROOT%\vc\zip -X -9 -r %PROJECT_ROOT%\vc\%ZIP_FILENAME% bin @if ERRORLEVEL 1 goto end cd %PROJECT_ROOT%\output -%PROJECT_ROOT%\vc\zip -X -9 -u -r %PROJECT_ROOT%\vc\qfceux64.zip palettes luaScripts tools +%PROJECT_ROOT%\vc\zip -X -9 -u -r %PROJECT_ROOT%\vc\%ZIP_FILENAME% palettes luaScripts tools @if ERRORLEVEL 1 goto end mkdir doc copy *.chm doc\. -%PROJECT_ROOT%\vc\zip -X -9 -u -r %PROJECT_ROOT%\vc\qfceux64.zip doc +%PROJECT_ROOT%\vc\zip -X -9 -u -r %PROJECT_ROOT%\vc\%ZIP_FILENAME% doc @if ERRORLEVEL 1 goto end cd %PROJECT_ROOT% -appveyor PushArtifact %PROJECT_ROOT%\vc\qfceux64.zip +IF DEFINED APPVEYOR appveyor SetVariable -Name WIN64_QTSDL_ARTIFACT -Value %ZIP_FILENAME% +IF DEFINED APPVEYOR appveyor PushArtifact %PROJECT_ROOT%\vc\%ZIP_FILENAME% :end diff --git a/pipelines/win32_build.bat b/pipelines/win32_build.bat index 61ea22a9..bddae1f0 100644 --- a/pipelines/win32_build.bat +++ b/pipelines/win32_build.bat @@ -1,18 +1,27 @@ set PROJECT_ROOT=%~dp0.. +set BUILD_CONFIG=Release -msbuild %PROJECT_ROOT%\vc\vc14_fceux.vcxproj /p:Configuration=Release /p:Platform="Win32" +set ZIP_FILENAME=fceux-win32.zip +if defined FCEU_RELEASE_VERSION set ZIP_FILENAME=fceux-%FCEU_RELEASE_VERSION%-win32.zip +if defined FCEU_RELEASE_VERSION set BUILD_CONFIG=PublicRelease + +set DEPLOY_GROUP=master +IF DEFINED APPVEYOR_REPO_TAG_NAME set DEPLOY_GROUP=%APPVEYOR_REPO_TAG_NAME% + +msbuild %PROJECT_ROOT%\vc\vc14_fceux.vcxproj /p:Configuration=%BUILD_CONFIG% /p:Platform="Win32" @if ERRORLEVEL 1 goto end cd %PROJECT_ROOT%\vc REM Create Zip Archive cd %PROJECT_ROOT%\output -..\vc\zip -X -9 -r ..\vc\fceux.zip fceux.exe fceux.chm taseditor.chm lua5.1.dll lua51.dll 7z.dll auxlib.lua palettes luaScripts tools +..\vc\zip -X -9 -r ..\vc\%ZIP_FILENAME% fceux.exe fceux.chm taseditor.chm lua5.1.dll lua51.dll 7z.dll auxlib.lua palettes luaScripts tools @if ERRORLEVEL 1 goto end cd %PROJECT_ROOT% -appveyor PushArtifact %PROJECT_ROOT%\vc\fceux.zip +IF DEFINED APPVEYOR appveyor SetVariable -Name WIN32_ARTIFACT -Value %ZIP_FILENAME% +IF DEFINED APPVEYOR appveyor PushArtifact %PROJECT_ROOT%\vc\%ZIP_FILENAME% -:end \ No newline at end of file +:end diff --git a/pipelines/win64_build.bat b/pipelines/win64_build.bat index 3d127b8d..9a53f144 100644 --- a/pipelines/win64_build.bat +++ b/pipelines/win64_build.bat @@ -1,7 +1,15 @@ set PROJECT_ROOT=%~dp0.. +set BUILD_CONFIG=Release -msbuild %PROJECT_ROOT%\vc\vc14_fceux.vcxproj /p:Configuration=Release /p:Platform="x64" +set ZIP_FILENAME=fceux-win64.zip +if defined FCEU_RELEASE_VERSION set ZIP_FILENAME=fceux-%FCEU_RELEASE_VERSION%-win64.zip +if defined FCEU_RELEASE_VERSION set BUILD_CONFIG=PublicRelease + +set DEPLOY_GROUP=master +IF DEFINED APPVEYOR_REPO_TAG_NAME set DEPLOY_GROUP=%APPVEYOR_REPO_TAG_NAME% + +msbuild %PROJECT_ROOT%\vc\vc14_fceux.vcxproj /p:Configuration=%BUILD_CONFIG% /p:Platform="x64" @if ERRORLEVEL 1 goto end cd %PROJECT_ROOT%\vc @@ -9,13 +17,14 @@ cd %PROJECT_ROOT%\vc REM Create Zip Archive cd %PROJECT_ROOT%\output -..\vc\zip -X -9 -j ..\vc\fceux64.zip ..\vc\x64\Release\fceux64.exe ..\src\drivers\win\lua\x64\lua5.1.dll ..\src\drivers\win\lua\x64\lua51.dll ..\src\auxlib.lua ..\src\drivers\win\7z_64.dll +..\vc\zip -X -9 -j ..\vc\%ZIP_FILENAME% ..\vc\x64\%BUILD_CONFIG%\fceux64.exe ..\src\drivers\win\lua\x64\lua5.1.dll ..\src\drivers\win\lua\x64\lua51.dll ..\src\auxlib.lua ..\src\drivers\win\7z_64.dll @if ERRORLEVEL 1 goto end -..\vc\zip -X -9 -u -r ..\vc\fceux64.zip fceux.chm taseditor.chm palettes luaScripts tools +..\vc\zip -X -9 -u -r ..\vc\%ZIP_FILENAME% fceux.chm taseditor.chm palettes luaScripts tools @if ERRORLEVEL 1 goto end cd %PROJECT_ROOT% -appveyor PushArtifact %PROJECT_ROOT%\vc\fceux64.zip +IF DEFINED APPVEYOR appveyor SetVariable -Name WIN64_ARTIFACT -Value %ZIP_FILENAME% +IF DEFINED APPVEYOR appveyor PushArtifact %PROJECT_ROOT%\vc\%ZIP_FILENAME% :end diff --git a/readme.md b/readme.md index ac946fa5..ab58d383 100644 --- a/readme.md +++ b/readme.md @@ -26,11 +26,11 @@ An open source NES Emulator for Windows and Unix that features solid emulation a ## Builds and Releases Interim builds: -* Win32: [fceux.zip](https://ci.appveyor.com/api/projects/zeromus/fceux/artifacts/fceux.zip?branch=master&job=Windows%2032) -* Win64: [fceux64.zip](https://ci.appveyor.com/api/projects/zeromus/fceux/artifacts/fceux64.zip?branch=master&job=Windows%2064) -* Win64 Qt/SDL: [qfceux64.zip](https://ci.appveyor.com/api/projects/zeromus/fceux/artifacts/qfceux64.zip?branch=master&job=Win64%20Qt) -* Ubuntu: [fceux-2.6.4-amd64.deb](https://ci.appveyor.com/api/projects/zeromus/fceux/artifacts/fceux-2.6.4-amd64.deb?branch=master&job=Ubuntu) -* MacOSX: [fceux-2.6.4-Darwin.dmg](https://ci.appveyor.com/api/projects/zeromus/fceux/artifacts/fceux-2.6.4-Darwin.dmg?branch=master&job=MacOS) +* Win32: [fceux-win32.zip](https://github.com/TASEmulators/fceux/releases/download/interim-build/fceux-win32.zip) +* Win64: [fceux-win64.zip](https://github.com/TASEmulators/fceux/releases/download/interim-build/fceux-win64.zip) +* Win64 Qt/SDL: [fceux-win64-QtSDL.zip](https://github.com/TASEmulators/fceux/releases/download/interim-build/fceux-win64-QtSDL.zip) +* Ubuntu: [fceux-ubuntu-x64.deb](https://github.com/TASEmulators/fceux/releases/download/interim-build/fceux-ubuntu-x64.deb) +* MacOSX: [fceux-Darwin.dmg](https://github.com/TASEmulators/fceux/releases/download/interim-build/fceux-Darwin.dmg) * Status: [Appveyor](https://ci.appveyor.com/project/zeromus/fceux/) But you might like mesen more: https://github.com/SourMesen/Mesen diff --git a/scripts/fceuVersion.pl b/scripts/fceuVersion.pl new file mode 100755 index 00000000..67beaca0 --- /dev/null +++ b/scripts/fceuVersion.pl @@ -0,0 +1,74 @@ +#!/usr/bin/perl + +use strict; +use File::Basename; +#use File::Spec; + +my $format = 0; + +foreach my $arg (@ARGV) +{ + #print $arg, "\n"; + + if ($arg eq "-major") + { + $format = 1; + } + elsif ($arg eq "-minor") + { + $format = 2; + } + elsif ($arg eq "-patch") + { + $format = 3; + } +} + +#my $file = File::Spec->rel2abs( __FILE__ ); +my $dirname = dirname(__FILE__); +my $projRoot = "$dirname/.."; +my $versionHeader = "$projRoot/src/version.h"; +my $major = 1; +my $minor = 0; +my $patch = 0; + +#print "File: $file\n"; +#print "Dir $dirname\n"; + +my $line; + +open INFILE, "$versionHeader" or die "Error: Could not open file: $versionHeader\n"; +while ($line = ) +{ + #print $line; + if ($line =~ m/\s*#define\s+FCEU_VERSION_MAJOR\s+(\d+)/) + { + $major = $1; + } + elsif ($line =~ m/\s*#define\s+FCEU_VERSION_MINOR\s+(\d+)/) + { + $minor = $1; + } + elsif ($line =~ m/\s*#define\s+FCEU_VERSION_PATCH\s+(\d+)/) + { + $patch = $1; + } +} +close(INFILE); + +if ($format == 1) +{ + print "$major"; +} +elsif ($format == 2) +{ + print "$minor"; +} +elsif ($format == 3) +{ + print "$patch"; +} +else +{ + print "$major.$minor.$patch"; +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8ef081d4..7a6a3566 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,6 +5,10 @@ set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) +if (${PUBLIC_RELEASE}) + add_definitions( -DPUBLIC_RELEASE=1 ) +endif() + if ( ${QT6} ) message( STATUS "GUI Frontend: Qt6") set( Qt Qt6 ) @@ -28,6 +32,7 @@ else() include_directories( ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Help_INCLUDE_DIRS} ) endif() + if(WIN32) find_package(OpenGL REQUIRED) #find_package(Qt5 COMPONENTS Widgets OpenGL REQUIRED) @@ -391,6 +396,7 @@ set(SRC_CORE ${CMAKE_CURRENT_SOURCE_DIR}/boards/cheapocabra.cpp ${CMAKE_CURRENT_SOURCE_DIR}/boards/cityfighter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/boards/coolboy.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/boards/coolgirl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/boards/dance2000.cpp ${CMAKE_CURRENT_SOURCE_DIR}/boards/datalatch.cpp ${CMAKE_CURRENT_SOURCE_DIR}/boards/dream.cpp diff --git a/src/boards/coolboy.cpp b/src/boards/coolboy.cpp index e220ae80..7fde3c2a 100644 --- a/src/boards/coolboy.cpp +++ b/src/boards/coolboy.cpp @@ -21,7 +21,7 @@ * * COOLBOY cartridges use registers at address $6xxx * MINDKIDS cartridges use a solder pad labelled "5/6K" to select between $5000 and $6000 - * + * * $xxx0 * 7 bit 0 * ---- ---- @@ -29,7 +29,7 @@ * |||| |||| * |||| |+++-- PRG offset (PRG A19, A18, A17) * |||| +----- Alternate CHR A17 - * ||++------- PRG offset (PRG A24, A23) + * ||++------- PRG offset (PRG A24, A23), CHR offset (CHR A19, A18) * |+--------- PRG mask (PRG A17 from 0: MMC3; 1: offset) * +---------- CHR mask (CHR A17 from 0: MMC3; 1: alternate) * @@ -55,15 +55,16 @@ * $xxx3 * 7 bit 0 * ---- ---- - * NPxP QQRx - * || | ||| - * || | +++--- PRG offset for GNROM mode (PRG A16, A15, A14) - * || +------- 1: GNROM mode; 0: MMC3 mode - * || | (1: PRG A16...13 from QQ, L, R, CPU A14, A13 + CHR A16...10 from MMMM, PPU A12...10; - * || | 0: PRG A16...13 from MMC3 + CHR A16...A10 from MMC3 ) + * NPZP QQRx + * |||| ||| + * |||| +++--- PRG offset for GNROM mode (PRG A16, A15, A14) + * |||+------- 1: GNROM mode; 0: MMC3 mode + * |||| (1: PRG A16...13 from QQ, L, R, CPU A14, A13 + CHR A16...10 from MMMM, PPU A12...10; + * |||| 0: PRG A16...13 from MMC3 + CHR A16...A10 from MMC3 ) + * ||+-------- 1: Also enable PRG RAM in $5000-$5FFF * |+-+------- Banking mode * |+--------- "Weird MMC3 mode" - * +---------- Lockout (prevent further writes to these four registers, only works in MMC3 mode) + * +---------- Lockout (prevent further writes to all registers but the one at $xxx2, only works in MMC3 mode) * * Also some new cartridges from MINDKIDS have /WE and /OE pins connected to mapper, * which allows you to rewrite flash memory without soldering. @@ -75,25 +76,91 @@ #include "mapinc.h" #include "mmc3.h" +#include "../ines.h" -static void COOLBOYCW(uint32 A, uint8 V) { - uint32 mask = 0xFF ^ (EXPREGS[0] & 0x80); - if (EXPREGS[3] & 0x10) { - if (EXPREGS[3] & 0x40) { // Weird mode +const int ROM_CHIP = 0x00; +const int WRAM_CHIP = 0x10; +const int CFI_CHIP = 0x11; +const int FLASH_CHIP = 0x12; + +const uint32 FLASH_SECTOR_SIZE = 128 * 1024; + +extern uint8* WRAM; +static uint8* CFI = NULL; +static uint8* Flash = NULL; + +static uint8 flash_save = 0; +static uint8 flash_state = 0; +static uint16 flash_buffer_a[10]; +static uint8 flash_buffer_v[10]; +static uint8 cfi_mode = 0; + +static uint16 regs_base = 0; +static uint8 flag23 = 0; +static uint8 flag45 = 0; +static uint8 flag67 = 0; +static uint8 flag89 = 0; + +// Macronix 256-mbit memory CFI data +const uint8 cfi_data[] = +{ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x51, 0x52, 0x59, 0x02, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x27, 0x36, 0x00, 0x00, 0x03, + 0x06, 0x09, 0x13, 0x03, 0x05, 0x03, 0x02, 0x19, + 0x02, 0x00, 0x06, 0x00, 0x01, 0xFF, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0x50, 0x52, 0x49, 0x31, 0x33, 0x14, 0x02, 0x01, + 0x00, 0x08, 0x00, 0x00, 0x02, 0x95, 0xA5, 0x05, + 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +static void AA6023CW(uint32 A, uint8 V) { + if (flag89) { + /* + $xxx0 + 7 bit 0 + ---- ---- + AB.C DEEE + || | |||| + || | |+++-- PRG offset (PRG A19, A18, A17) + || | +----- Alternate CHR A17 + || +------- 1=Write-protect CHR-RAM + |+--------- PRG mask (PRG A17 from 0: MMC3; 1: offset) + +---------- CHR mask (CHR A17 from 0: MMC3; 1: alternate) + */ + if (EXPREGS[0] & 0b00010000) + SetupCartCHRMapping(0, VROM, CHRsize[0], 0); // write-protect CHR-RAM + else + SetupCartCHRMapping(0, VROM, CHRsize[0], 1); // allow CHR writes + } + + uint32 mask = 0xFF ^ (EXPREGS[0] & 0b10000000); + if (EXPREGS[3] & 0b00010000) { + if (EXPREGS[3] & 0b01000000) { // Weird mode int cbase = (MMC3_cmd & 0x80) << 5; switch (cbase ^ A) { // Don't even try do understand case 0x0400: case 0x0C00: V &= 0x7F; break; } } - // Highest bit goes from MMC3 registers when EXPREGS[3]&0x80==0 or from EXPREGS[0]&0x08 otherwise + // Highest bit goes from MMC3 registers when EXPREGS[0]&0x80==0 or from EXPREGS[0]&0x08 otherwise setchr1(A, - (V & 0x80 & mask) | ((((EXPREGS[0] & 0x08) << 4) & ~mask)) // 7th bit + (V & 0x80 & mask) | ((((EXPREGS[0] & 0b00001000) << 4) & ~mask)) // 7th bit | ((EXPREGS[2] & 0x0F) << 3) // 6-3 bits | ((A >> 10) & 7) // 2-0 bits + | ((EXPREGS[0] & 0b00110000) << 4) // There are some ROMs with 1 MiB CHR-ROM ); - } else { - if (EXPREGS[3] & 0x40) { // Weird mode, again + } + else { + if (EXPREGS[3] & 0b01000000) { // Weird mode, again int cbase = (MMC3_cmd & 0x80) << 5; switch (cbase ^ A) { // Don't even try do understand case 0x0000: V = DRegBuf[0]; break; @@ -103,18 +170,81 @@ static void COOLBOYCW(uint32 A, uint8 V) { } } // Simple MMC3 mode - // Highest bit goes from MMC3 registers when EXPREGS[3]&0x80==0 or from EXPREGS[0]&0x08 otherwise - setchr1(A, (V & mask) | (((EXPREGS[0] & 0x08) << 4) & ~mask)); + // Highest bit goes from MMC3 registers when EXPREGS[0]&0x80==0 or from EXPREGS[0]&0x08 otherwise + setchr1(A, + (V & mask) + | (((EXPREGS[0] & 0x08) << 4) & ~mask) + | ((EXPREGS[0] & 0b00110000) << 4)); // There are some ROMs with 1 MiB CHR-ROM } } -static void COOLBOYPW(uint32 A, uint8 V) { - uint32 mask = ((0x3F | (EXPREGS[1] & 0x40) | ((EXPREGS[1] & 0x20) << 2)) ^ ((EXPREGS[0] & 0x40) >> 2)) ^ ((EXPREGS[1] & 0x80) >> 2); - uint32 base = ((EXPREGS[0] & 0x07) >> 0) | ((EXPREGS[1] & 0x10) >> 1) | ((EXPREGS[1] & 0x0C) << 2) | ((EXPREGS[0] & 0x30) << 2); +static void AA6023PW(uint32 A, uint8 V) { + uint8 CREGS[] = {EXPREGS[0], EXPREGS[1], EXPREGS[2], EXPREGS[3]}; + // Submappers has scrambled bits + if (flag23) { + /* + $xxx1 + 7 bit 0 + ---- ---- + GHIL JKKx + |||| ||| + |||| +++--- PRG offset (in order: PRG A20, A21, A22) + |||+------- GNROM mode bank PRG size (0: 32 KiB bank, PRG A14=CPU A14; 1: 16 KiB bank, PRG A14=offset A14) + ||+-------- PRG mask (PRG A20 from 0: offset; 1: MMC3) + |+--------- PRG mask (PRG A19 from 0: offset; 1: MMC3) + +---------- PRG mask (PRG A18 from 0: MMC3; 1: offset) + */ + CREGS[1] = (CREGS[1] & 0b11100101) + | ((CREGS[1] & 0b00001000) << 1) // PRG A20 + | ((CREGS[1] & 0b00000010) << 2) // PRG A22 + | ((((CREGS[1] ^ 0b00010000) & 0b00010000) >> 3)); // GNROM mode bank PRG size + } + if (flag45) { + /* + $xxx0 + 7 bit 0 + ---- ---- + ABCC DEEE + |||| |||| + |||| |+++-- PRG offset (PRG A19, A18, A17) + |||| +----- Alternate CHR A17 + ||++------- PRG offset (PRG A21, A20) + |+--------- PRG mask (PRG A17 from 0: MMC3; 1: offset) + +---------- CHR mask (CHR A17 from 0: MMC3; 1: alternate) + $xxx1 + 7 bit 0 + ---- ---- + GHIx xxLx + ||| | + ||| +--- GNROM mode bank PRG size (1: 32 KiB bank, PRG A14=CPU A14; 0: 16 KiB bank, PRG A14=offset A14) + ||+-------- PRG mask (PRG A20 from 0: offset; 1: MMC3) + |+--------- PRG mask (PRG A19 from 0: offset; 1: MMC3) + +---------- PRG mask (PRG A18 from 0: MMC3; 1: offset) + */ + CREGS[1] = (CREGS[1] & 0b11100011) + | ((CREGS[0] & 0b00100000) >> 3) // PRG A21 + | (CREGS[0] & 0b00010000); // PRG A20 + CREGS[0] &= 0b11001111; + } + + uint32 mask = ((0b00111111 | (CREGS[1] & 0b01000000) | ((CREGS[1] & 0b00100000) << 2)) ^ ((CREGS[0] & 0b01000000) >> 2)) ^ ((CREGS[1] & 0b10000000) >> 2); + uint32 base = ((CREGS[0] & 0b00000111) >> 0) | ((CREGS[1] & 0b00010000) >> 1) | ((CREGS[1] & 0b00001100) << 2) | ((CREGS[0] & 0b00110000) << 2); + + if (flash_save && cfi_mode) { + setprg32r(CFI_CHIP, 0x8000, 0); + return; + } + + int chip = !flash_save ? ROM_CHIP : FLASH_CHIP; + // There are ROMs with multiple PRG ROM chips + int chip_offset = 0; + if (flag67 && EXPREGS[0] & 0b00001000) { + chip_offset += ROM_size; + } // Very weird mode // Last banks are first in this mode, ignored when MMC3_cmd&0x40 - if ((EXPREGS[3] & 0x40) && (V >= 0xFE) && !((MMC3_cmd & 0x40) != 0)) { + if ((CREGS[3] & 0b01000000) && (V >= 0xFE) && !((MMC3_cmd & 0x40) != 0)) { switch (A & 0xE000) { case 0xC000: case 0xE000: @@ -123,91 +253,234 @@ static void COOLBOYPW(uint32 A, uint8 V) { } } - // Regular MMC3 mode, internal ROM size can be up to 2048kb! - if (!(EXPREGS[3] & 0x10)) - setprg8(A, (((base << 4) & ~mask)) | (V & mask)); - else { // NROM mode + if (!(CREGS[3] & 0x10)) { + // Regular MMC3 mode but can be extended to 2MiB + setprg8r(chip, A, ((((base << 4) & ~mask)) | (V & mask)) + chip_offset); + } + else { + // NROM mode mask &= 0xF0; uint8 emask; - if ((((EXPREGS[1] & 2) != 0))) // 32kb mode - emask = (EXPREGS[3] & 0x0C) | ((A & 0x4000) >> 13); + if (CREGS[1] & 0b00000010) // 32kb mode + emask = (CREGS[3] & 0b00001100) | ((A & 0x4000) >> 13); else // 16kb mode - emask = EXPREGS[3] & 0x0E; - setprg8(A, ((base << 4) & ~mask) // 7-4 bits are from base (see below) - | (V & mask) // ... or from MM3 internal regs, depends on mask - | emask // 3-1 (or 3-2 when (EXPREGS[3]&0x0C is set) from EXPREGS[3] - | ((A & 0x2000) >> 13)); // 0th just as is + emask = CREGS[3] & 0b00001110; + setprg8r(chip, A, ( + ((base << 4) & ~mask) // 7-4 bits are from base + | (V & mask) // ... or from MM3 internal regs, depends on mask + | emask // 3-1 (or 3-2 when (EXPREGS[3]&0x0C is set) from EXPREGS[3] + | ((A & 0x2000) >> 13) // 0th just as is + ) + chip_offset); // For multi-chip ROMs } } -static DECLFW(COOLBOYWrite) { - if(A001B & 0x80) - CartBW(A,V); +static DECLFW(AA6023WramWrite) { + if (A001B & 0x80) + CartBW(A, V); +} + +static DECLFW(AA6023Write) { + if (A >= 0x6000) { + AA6023WramWrite(A, V); + } // Deny any further writes when 7th bit is 1 AND 4th is 0 if ((EXPREGS[3] & 0x90) != 0x80) { EXPREGS[A & 3] = V; - FixMMC3PRG(MMC3_cmd); - FixMMC3CHR(MMC3_cmd); } + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); } -static void COOLBOYReset(void) { +static DECLFW(AA6023FlashWrite) { + if (A < 0xC000) + MMC3_CMDWrite(A, V); + else + MMC3_IRQWrite(A, V); + + if (!flash_save) return; + if (flash_state < sizeof(flash_buffer_a) / sizeof(flash_buffer_a[0])) { + flash_buffer_a[flash_state] = A & 0xFFF; + flash_buffer_v[flash_state] = V; + flash_state++; + + // enter CFI mode + if ((flash_state == 1) && + (flash_buffer_a[0] == 0x0AAA) && (flash_buffer_v[0] == 0x98)) { + cfi_mode = 1; + flash_state = 0; + } + + // erase sector + if ((flash_state == 6) && + (flash_buffer_a[0] == 0x0AAA) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == 0x0555) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[2] == 0x0AAA) && (flash_buffer_v[2] == 0x80) && + (flash_buffer_a[3] == 0x0AAA) && (flash_buffer_v[3] == 0xAA) && + (flash_buffer_a[4] == 0x0555) && (flash_buffer_v[4] == 0x55) && + (flash_buffer_v[5] == 0x30)) { + int offset = &Page[A >> 11][A] - Flash; + int sector = offset / FLASH_SECTOR_SIZE; + for (uint32 i = sector * FLASH_SECTOR_SIZE; i < (sector + 1) * FLASH_SECTOR_SIZE; i++) + Flash[i % PRGsize[ROM_CHIP]] = 0xFF; + FCEU_printf("Flash sector #%d is erased (0x%08x - 0x%08x).\n", sector, offset, offset + FLASH_SECTOR_SIZE); + flash_state = 0; + } + + // erase chip, lol + if ((flash_state == 6) && + (flash_buffer_a[0] == 0x0AAA) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == 0x0555) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[2] == 0x0AAA) && (flash_buffer_v[2] == 0x80) && + (flash_buffer_a[3] == 0x0AAA) && (flash_buffer_v[3] == 0xAA) && + (flash_buffer_a[4] == 0x0555) && (flash_buffer_v[4] == 0x55) && + (flash_buffer_v[5] == 0x10)) { + memset(Flash, 0xFF, PRGsize[ROM_CHIP]); + FCEU_printf("Flash chip erased.\n"); + flash_state = 0; + } + + // write byte + if ((flash_state == 4) && + (flash_buffer_a[0] == 0x0AAA) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == 0x0555) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[2] == 0x0AAA) && (flash_buffer_v[2] == 0xA0)) { + int offset = &Page[A >> 11][A] - Flash; + if (CartBR(A) != 0xFF) { + FCEU_PrintError("Error: can't write to 0x%08x, flash sector is not erased.\n", offset); + } + else { + CartBW(A, V); + } + flash_state = 0; + } + } + + // not a command + if (((A & 0xFFF) != 0x0AAA) && ((A & 0xFFF) != 0x0555)) { + flash_state = 0; + } + + // reset + if (V == 0xF0) { + flash_state = 0; + cfi_mode = 0; + } + + FixMMC3PRG(MMC3_cmd); +} + +static void AA6023Reset(void) { MMC3RegReset(); EXPREGS[0] = EXPREGS[1] = EXPREGS[2] = EXPREGS[3] = 0; + flash_state = 0; + cfi_mode = 0; FixMMC3PRG(MMC3_cmd); FixMMC3CHR(MMC3_cmd); } -static void COOLBOYPower(void) { +static void AA6023Power(void) { GenMMC3Power(); EXPREGS[0] = EXPREGS[1] = EXPREGS[2] = EXPREGS[3] = 0; FixMMC3PRG(MMC3_cmd); FixMMC3CHR(MMC3_cmd); - SetWriteHandler(0x5000, 0x5fff, CartBW); // some games access random unmapped areas and crashes because of KT-008 PCB hack in MMC3 source lol - SetWriteHandler(0x6000, 0x6fff, COOLBOYWrite); + if (regs_base != 0x5000) + SetWriteHandler(0x5000, 0x5fff, CartBW); // some games access random unmapped areas and crashes because of KT-008 PCB hack in MMC3 source lol + SetWriteHandler(0x6000, 0x7fff, AA6023WramWrite); + SetWriteHandler(regs_base, regs_base + 0x0fff, AA6023Write); + SetWriteHandler(0x8000, 0xFFFF, AA6023FlashWrite); + SetReadHandler(0x8000, 0xFFFF, CartBR); } -static void MINDKIDSPower(void) { - GenMMC3Power(); - EXPREGS[0] = EXPREGS[1] = EXPREGS[2] = EXPREGS[3] = 0; +static void AA6023Restore(void) { FixMMC3PRG(MMC3_cmd); FixMMC3CHR(MMC3_cmd); - SetWriteHandler(0x5000, 0x5fff, COOLBOYWrite); +} + +static void AA6023Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + if (Flash) + FCEU_gfree(Flash); + if (CFI) + FCEU_gfree(CFI); + WRAM = Flash = CFI = NULL; +} + +void CommonInit(CartInfo* info, int submapper) +{ + GenMMC3_Init(info, 2048, info->vram_size / 1024, !info->ines2 ? 8 : (info->wram_size + info->battery_wram_size) / 1024, info->battery); + pwrap = AA6023PW; + cwrap = AA6023CW; + + switch (submapper) + { + case 2: + regs_base = 0x7000; + break; + case 0: + case 4: + case 6: + case 8: + regs_base = 0x6000; + break; + case 1: + case 3: + case 5: + case 7: + case 9: + regs_base = 0x5000; + break; + default: + FCEU_PrintError("Submapper #%d is not supported", submapper); + } + flag23 = (submapper == 2) || (submapper == 3); + flag45 = (submapper == 4) || (submapper == 5); + flag67 = (submapper == 6) || (submapper == 7); + flag89 = (submapper == 8) || (submapper == 9); + info->Power = AA6023Power; + info->Reset = AA6023Reset; + info->Close = AA6023Close; + + flash_save = info->battery; + + if (flash_save) { + CFI = (uint8*)FCEU_gmalloc(sizeof(cfi_data) * 2); + for (int i = 0; i < sizeof(cfi_data); i++) { + CFI[i * 2] = CFI[i * 2 + 1] = cfi_data[i]; + } + SetupCartPRGMapping(CFI_CHIP, CFI, sizeof(cfi_data) * 2, 0); + Flash = (uint8*)FCEU_gmalloc(PRGsize[ROM_CHIP]); + for (int i = 0; i < PRGsize[ROM_CHIP]; i++) { + Flash[i] = PRGptr[ROM_CHIP][i % PRGsize[ROM_CHIP]]; + } + SetupCartPRGMapping(FLASH_CHIP, Flash, PRGsize[ROM_CHIP], 1); + info->SaveGame[1] = Flash; + info->SaveGameLen[1] = PRGsize[ROM_CHIP]; + } + + AddExState(EXPREGS, 4, 0, "EXPR"); + if (flash_save) + { + AddExState(&flash_state, sizeof(flash_state), 0, "FLST"); + AddExState(flash_buffer_a, sizeof(flash_buffer_a), 0, "FLBA"); + AddExState(flash_buffer_v, sizeof(flash_buffer_v), 0, "FLBV"); + AddExState(&cfi_mode, sizeof(cfi_mode), 0, "CFIM"); + AddExState(Flash, PRGsize[ROM_CHIP], 0, "FLAS"); + } } // Registers at $6xxx -void COOLBOY_Init(CartInfo *info) { - GenMMC3_Init(info, 2048, 256, 8, 1); - pwrap = COOLBOYPW; - cwrap = COOLBOYCW; - info->Power = COOLBOYPower; - info->Reset = COOLBOYReset; - AddExState(EXPREGS, 4, 0, "EXPR"); +void COOLBOY_Init(CartInfo* info) { + CommonInit(info, 0); } // Registers at $5xxx -void MINDKIDS_Init(CartInfo *info) { - GenMMC3_Init(info, 2048, 256, 8, 1); - pwrap = COOLBOYPW; - cwrap = COOLBOYCW; - info->Power = MINDKIDSPower; - info->Reset = COOLBOYReset; - AddExState(EXPREGS, 4, 0, "EXPR"); +void MINDKIDS_Init(CartInfo* info) { + CommonInit(info, 1); } // For NES 2.0 loader -void SMD132_SMD133_Init(CartInfo *info) { - switch (info->submapper) - { - case 0: - COOLBOY_Init(info); - break; - case 1: - MINDKIDS_Init(info); - break; - default: - FCEU_PrintError("Unknown submapper: #%d.", info->submapper); - break; - } +void AA6023_Init(CartInfo* info) { + CommonInit(info, info->submapper); } diff --git a/src/boards/coolgirl.cpp b/src/boards/coolgirl.cpp new file mode 100644 index 00000000..f998be1c --- /dev/null +++ b/src/boards/coolgirl.cpp @@ -0,0 +1,2279 @@ +/* FCE Ultra - NES/Famicom Emulator +* +* Copyright notice for this file: +* Copyright (C) 2022 Cluster +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Very complicated homebrew multicart mapper with. +* The code is so obscured and weird because it's ported from Verilog CPLD source code: +* https://github.com/ClusterM/coolgirl-famicom-multicart/blob/master/CoolGirl_mappers.vh +* +*/ + +#include "mapinc.h" + +const uint32 SAVE_FLASH_SIZE = 1024 * 1024 * 8; +const uint32 FLASH_SECTOR_SIZE = 128 * 1024; +const int ROM_CHIP = 0x00; +const int WRAM_CHIP = 0x10; +const int FLASH_CHIP = 0x11; +const int CHR_RAM_CHIP = 0x12; +const int CFI_CHIP = 0x13; + +static uint32 WRAM_SIZE = 0; +static uint8 *WRAM = NULL; +static uint32 CHR_RAM_SIZE = 0; +static uint8 *CHR_RAM; +static uint8 *SAVE_FLASH = NULL; +static uint8* CFI; + +static uint8 sram_enabled = 0; +static uint8 sram_page = 0; // [1:0] +static uint8 can_write_chr = 0; +static uint8 map_rom_on_6000 = 0; +static uint8 flags = 0; // [2:0] +static uint8 mapper = 0; // [5:0] +static uint8 can_write_flash = 0; +static uint8 mirroring = 0; // [1:0] +static uint8 four_screen = 0; +static uint8 lockout = 0; + +static uint32 prg_base = 0; // [26:14] +static uint32 prg_mask = 0b11111000 << 14; // 11111000, 128KB // [20:14] +static uint8 prg_mode = 0; // [2:0] +static uint8 prg_bank_6000 = 0; // [7:0] +static uint8 prg_bank_a = 0; // [7:0] +static uint8 prg_bank_b = 1; // [7:0] +static uint8 prg_bank_c = ~1; // [7:0] +static uint8 prg_bank_d = ~0; // [7:0] + +static uint32 chr_mask = 0; // [18:13] +static uint8 chr_mode = 0; // [2:0] +static uint16 chr_bank_a = 0; // [8:0] +static uint16 chr_bank_b = 1; // [8:0] +static uint16 chr_bank_c = 2; // [8:0] +static uint16 chr_bank_d = 3; // [8:0] +static uint16 chr_bank_e = 4; // [8:0] +static uint16 chr_bank_f = 5; // [8:0] +static uint16 chr_bank_g = 6; // [8:0] +static uint16 chr_bank_h = 7; // [8:0] + +static uint8 TKSMIR[8]; + +static uint32 prg_bank_6000_mapped = 0; +static uint32 prg_bank_a_mapped = 0; +static uint32 prg_bank_b_mapped = 0; +static uint32 prg_bank_c_mapped = 0; +static uint32 prg_bank_d_mapped = 0; + +// for MMC2/MMC4 +static uint8 ppu_latch0 = 0; +static uint8 ppu_latch1 = 0; +// for MMC1 +static uint64 lreset = 0; +static uint8 mmc1_load_register = 0; // [5:0] +// for MMC3 +static uint8 mmc3_internal = 0; // [2:0] +// for mapper #69 +static uint8 mapper69_internal = 0; // [3:0] +// for mapper #112 +static uint8 mapper112_internal = 0; // [2:0] +// for mapper #163 +static uint8 mapper_163_latch = 0; +static uint8 mapper163_r0 = 0; // [7:0] +static uint8 mapper163_r1 = 0; // [7:0] +static uint8 mapper163_r2 = 0; // [7:0] +static uint8 mapper163_r3 = 0; // [7:0] +static uint8 mapper163_r4 = 0; // [7:0] +static uint8 mapper163_r5 = 0; // [7:0] + +// For mapper #90 +static uint8 mul1 = 0; +static uint8 mul2 = 0; + +// for MMC3 scanline-based interrupts, counts A12 rises after long A12 falls +static uint8 mmc3_irq_enabled = 0; // register to enable/disable counter +static uint8 mmc3_irq_latch = 0; // [7:0], stores counter reload latch value +static uint8 mmc3_irq_counter = 0; // [7:0], counter itself (downcounting) +static uint8 mmc3_irq_reload = 0; // flag to reload counter from latch +// for MMC5 scanline-based interrupts, counts dummy PPU reads +static uint8 mmc5_irq_enabled = 0; // register to enable/disable counter +static uint8 mmc5_irq_line = 0; // [7:0], scanline on which IRQ will be triggered +static uint8 mmc5_irq_out = 0; // stores 1 when IRQ is triggered +// for mapper #18 +static uint16 mapper18_irq_value = 0; // [15:0], counter itself (downcounting) +static uint8 mapper18_irq_control = 0; // [3:0], IRQ settings +static uint16 mapper18_irq_latch = 0; // [15:0], stores counter reload latch value +// for mapper #65 +static uint8 mapper65_irq_enabled = 0; // register to enable/disable IRQ +static uint16 mapper65_irq_value = 0; // [15:0], counter itself (downcounting) +static uint16 mapper65_irq_latch = 0; // [15:0], stores counter reload latch value +// reg mapper65_irq_out = 0; +// for Sunsoft FME-7 +static uint8 mapper69_irq_enabled = 0; // register to enable/disable IRQ +static uint8 mapper69_counter_enabled = 0; // register to enable/disable counter +static uint16 mapper69_irq_value = 0; // counter itself (downcounting) +// for VRC4 CPU-based interrupts +static uint8 vrc4_irq_value = 0; // [7:0], counter itself (upcounting) +static uint8 vrc4_irq_control = 0; // [2:0]� IRQ settings +static uint8 vrc4_irq_latch = 0; // [7:0], stores counter reload latch value +static uint8 vrc4_irq_prescaler = 0; // [6:0], prescaler counter for VRC4 +static uint8 vrc4_irq_prescaler_counter = 0; // prescaler cicles counter for VRC4 +// for VRC3 CPU-based interrupts +static uint16 vrc3_irq_value = 0; // [15:0], counter itself (upcounting) +static uint8 vrc3_irq_control = 0; // [3:0], IRQ settings +static uint16 vrc3_irq_latch = 0; // [15:0], stores counter reload latch value +// for mapper #42 (only Baby Mario) +static uint8 mapper42_irq_enabled = 0; // register to enable/disable counter +static uint16 mapper42_irq_value = 0; // [14:0], counter itself (upcounting) +// for mapper #83 +static uint8 mapper83_irq_enabled_latch = 0; +static uint8 mapper83_irq_enabled = 0; +static uint16 mapper83_irq_counter = 0; +// for mapper #90 +static uint8 mapper90_xor = 0; +// for mapper #67 +static uint8 mapper67_irq_enabled = 0; +static uint8 mapper67_irq_latch = 0; +static uint16 mapper67_irq_counter = 0; + +static uint8 flash_state = 0; +static uint16 flash_buffer_a[10]; +static uint8 flash_buffer_v[10]; +static uint8 cfi_mode = 0; + +// Micron 4-gbit memory CFI data +const uint8 cfi_data[] = +{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x52, 0x59, 0x02, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x27, 0x36, 0x00, 0x00, 0x06, + 0x06, 0x09, 0x13, 0x03, 0x05, 0x03, 0x02, 0x1E, + 0x02, 0x00, 0x06, 0x00, 0x01, 0xFF, 0x03, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0x50, 0x52, 0x49, 0x31, 0x33, 0x14, 0x02, 0x01, + 0x00, 0x08, 0x00, 0x00, 0x02, 0xB5, 0xC5, 0x05, + 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + +#define SET_BITS(target, target_bits, source, source_bits) target = set_bits(target, target_bits, get_bits(source, source_bits)) + +static inline uint8 string_to_bits(char* bitsstr, int* bits) +{ + uint8 bit1, bit2, count = 0; + for (int i = 0; i < 32; i++) + bits[i] = -1; + while (*bitsstr) + { + bit1 = 0; + bit2 = 0; + if (isdigit(*bitsstr)) + { + while (isdigit(*bitsstr)) + { + bit1 *= 10; + bit1 += *bitsstr - '0'; + bitsstr++; + } + if (*bitsstr == ':') + { + bitsstr++; + while (isdigit(*bitsstr)) + { + bit2 *= 10; + bit2 += *bitsstr - '0'; + bitsstr++; + } + if (bit2 < bit1) + for (int i = bit1; i >= bit2; i--) + { + bits[count] = i; + count++; + } + else + for (int i = bit1; i <= bit2; i++) + { + bits[count] = i; + count++; + } + } + else { + bits[count] = bit1; + count++; + } + } + else { + bitsstr++; + } + } + return count; +} + +static inline uint32 get_bits(uint32 V, char* bitsstr) +{ + uint32 result = 0; + int bits[32]; + string_to_bits(bitsstr, bits); + for (int i = 0; bits[i] >= 0; i++) + { + result <<= 1; + result |= (V >> bits[i]) & 1; + } + return result; +} + +static inline uint32 set_bits(uint32 V, char* bitsstr, uint32 new_bits) +{ + int bits[32]; + uint8 count = string_to_bits(bitsstr, bits); + for (int i = 0; i < count; i++) + { + if ((new_bits >> (count - i - 1)) & 1) + V |= 1 << bits[i]; + else + V &= ~(1 << bits[i]); + } + return V; +} + +static void COOLGIRL_Sync_PRG(void) { + prg_bank_6000_mapped = (prg_base >> 13) | (prg_bank_6000 & ((~(prg_mask >> 13) & 0xFE) | 1)); + prg_bank_a_mapped = (prg_base >> 13) | (prg_bank_a & ((~(prg_mask >> 13) & 0xFE) | 1)); + prg_bank_b_mapped = (prg_base >> 13) | (prg_bank_b & ((~(prg_mask >> 13) & 0xFE) | 1)); + prg_bank_c_mapped = (prg_base >> 13) | (prg_bank_c & ((~(prg_mask >> 13) & 0xFE) | 1)); + prg_bank_d_mapped = (prg_base >> 13) | (prg_bank_d & ((~(prg_mask >> 13) & 0xFE) | 1)); + uint8 REG_A_CHIP = (SAVE_FLASH != NULL && prg_bank_a_mapped >= 0x20000 - SAVE_FLASH_SIZE / 1024 / 8) ? FLASH_CHIP : ROM_CHIP; + uint8 REG_B_CHIP = (SAVE_FLASH != NULL && prg_bank_b_mapped >= 0x20000 - SAVE_FLASH_SIZE / 1024 / 8) ? FLASH_CHIP : ROM_CHIP; + uint8 REG_C_CHIP = (SAVE_FLASH != NULL && prg_bank_c_mapped >= 0x20000 - SAVE_FLASH_SIZE / 1024 / 8) ? FLASH_CHIP : ROM_CHIP; + uint8 REG_D_CHIP = (SAVE_FLASH != NULL && prg_bank_d_mapped >= 0x20000 - SAVE_FLASH_SIZE / 1024 / 8) ? FLASH_CHIP : ROM_CHIP; + + if (!cfi_mode || !SAVE_FLASH) + { + switch (prg_mode & 7) + { + default: + case 0: + setprg16r(REG_A_CHIP, 0x8000, prg_bank_a_mapped >> 1); + setprg16r(REG_C_CHIP, 0xC000, prg_bank_c_mapped >> 1); + break; + case 1: + setprg16r(REG_C_CHIP, 0x8000, prg_bank_c_mapped >> 1); + setprg16r(REG_A_CHIP, 0xC000, prg_bank_a_mapped >> 1); + break; + case 4: + setprg8r(REG_A_CHIP, 0x8000, prg_bank_a_mapped); + setprg8r(REG_B_CHIP, 0xA000, prg_bank_b_mapped); + setprg8r(REG_C_CHIP, 0xC000, prg_bank_c_mapped); + setprg8r(REG_D_CHIP, 0xE000, prg_bank_d_mapped); + break; + case 5: + setprg8r(REG_C_CHIP, 0x8000, prg_bank_c_mapped); + setprg8r(REG_B_CHIP, 0xA000, prg_bank_b_mapped); + setprg8r(REG_A_CHIP, 0xC000, prg_bank_a_mapped); + setprg8r(REG_D_CHIP, 0xE000, prg_bank_d_mapped); + break; + case 6: + setprg32r(REG_A_CHIP, 0x8000, prg_bank_b_mapped >> 2); + break; + case 7: + setprg32r(REG_A_CHIP, 0x8000, prg_bank_a_mapped >> 2); + break; + } + } + else { + setprg32r(CFI_CHIP, 0x8000, 0); + } + + if (!map_rom_on_6000 && WRAM) + setprg8r(WRAM_CHIP, 0x6000, sram_page); // Select SRAM page + else if (map_rom_on_6000) + setprg8(0x6000, prg_bank_6000_mapped); // Map ROM on $6000-$7FFF +} + +static void COOLGIRL_Sync_CHR(void) { + // calculate CHR shift + // wire shift_chr = ENABLE_MAPPER_021_022_023_025 && ENABLE_MAPPER_022 && (mapper == 6'b011000) && flags[1]; + int chr_shift = ((mapper == 0b011000) && (flags & 0b010)) ? 1 : 0; + + // enable or disable writes to CHR RAM, setup CHR mask + SetupCartCHRMapping(CHR_RAM_CHIP, CHR_RAM, ((((~(chr_mask >> 13) & 0x3F) + 1) * 0x2000 - 1) & (CHR_RAM_SIZE - 1)) + 1, can_write_chr); + + switch (chr_mode & 7) + { + default: + case 0: + setchr8r(0x12, chr_bank_a >> 3 >> chr_shift); + break; + case 1: + setchr4r(0x12, 0x0000, mapper_163_latch >> chr_shift); + setchr4r(0x12, 0x1000, mapper_163_latch >> chr_shift); + break; + case 2: + setchr2r(0x12, 0x0000, chr_bank_a >> 1 >> chr_shift); + TKSMIR[0] = TKSMIR[1] = chr_bank_a; + setchr2r(0x12, 0x0800, chr_bank_c >> 1 >> chr_shift); + TKSMIR[2] = TKSMIR[3] = chr_bank_c; + setchr1r(0x12, 0x1000, chr_bank_e >> chr_shift); + TKSMIR[4] = chr_bank_e; + setchr1r(0x12, 0x1400, chr_bank_f >> chr_shift); + TKSMIR[5] = chr_bank_f; + setchr1r(0x12, 0x1800, chr_bank_g >> chr_shift); + TKSMIR[6] = chr_bank_g; + setchr1r(0x12, 0x1C00, chr_bank_h >> chr_shift); + TKSMIR[7] = chr_bank_h; + break; + case 3: + setchr1r(0x12, 0x0000, chr_bank_e >> chr_shift); + TKSMIR[0] = chr_bank_e; + setchr1r(0x12, 0x0400, chr_bank_f >> chr_shift); + TKSMIR[1] = chr_bank_f; + setchr1r(0x12, 0x0800, chr_bank_g >> chr_shift); + TKSMIR[2] = chr_bank_g; + setchr1r(0x12, 0x0C00, chr_bank_h >> chr_shift); + TKSMIR[3] = chr_bank_h; + setchr2r(0x12, 0x1000, chr_bank_a >> 1 >> chr_shift); + TKSMIR[4] = TKSMIR[5] = chr_bank_a; + setchr2r(0x12, 0x1800, chr_bank_c >> 1 >> chr_shift); + TKSMIR[6] = TKSMIR[7] = chr_bank_c; + break; + case 4: + setchr4r(0x12, 0x0000, chr_bank_a >> 2 >> chr_shift); + setchr4r(0x12, 0x1000, chr_bank_e >> 2 >> chr_shift); + break; + case 5: + if (!ppu_latch0) + setchr4r(0x12, 0x0000, chr_bank_a >> 2 >> chr_shift); + else + setchr4r(0x12, 0x0000, chr_bank_b >> 2 >> chr_shift); + if (!ppu_latch1) + setchr4r(0x12, 0x1000, chr_bank_e >> 2 >> chr_shift); + else + setchr4r(0x12, 0x1000, chr_bank_f >> 2 >> chr_shift); + break; + case 6: + setchr2r(0x12, 0x0000, chr_bank_a >> 1 >> chr_shift); + setchr2r(0x12, 0x0800, chr_bank_c >> 1 >> chr_shift); + setchr2r(0x12, 0x1000, chr_bank_e >> 1 >> chr_shift); + setchr2r(0x12, 0x1800, chr_bank_g >> 1 >> chr_shift); + break; + case 7: + setchr1r(0x12, 0x0000, chr_bank_a >> chr_shift); + setchr1r(0x12, 0x0400, chr_bank_b >> chr_shift); + setchr1r(0x12, 0x0800, chr_bank_c >> chr_shift); + setchr1r(0x12, 0x0C00, chr_bank_d >> chr_shift); + setchr1r(0x12, 0x1000, chr_bank_e >> chr_shift); + setchr1r(0x12, 0x1400, chr_bank_f >> chr_shift); + setchr1r(0x12, 0x1800, chr_bank_g >> chr_shift); + setchr1r(0x12, 0x1C00, chr_bank_h >> chr_shift); + break; + } +} + +static void COOLGIRL_Sync_Mirroring(void) { + if (!four_screen) + { + if (!((mapper == 0b010100) && (flags & 1))) // Mapper #189? + setmirror((mirroring < 2) ? (mirroring ^ 1) : mirroring); + } + else { // four screen mode + vnapage[0] = CHR_RAM + 0x3F000; + vnapage[1] = CHR_RAM + 0x3F400; + vnapage[2] = CHR_RAM + 0x3F800; + vnapage[3] = CHR_RAM + 0x3FC00; + } +} + +static void COOLGIRL_Sync(void) { + COOLGIRL_Sync_PRG(); + COOLGIRL_Sync_CHR(); + COOLGIRL_Sync_Mirroring(); +} + +static DECLFW(COOLGIRL_Flash_Write) { + if (flash_state < sizeof(flash_buffer_a) / sizeof(flash_buffer_a[0])) + { + flash_buffer_a[flash_state] = A & 0xFFF; + flash_buffer_v[flash_state] = V; + flash_state++; + + // enter CFI mode + if ((flash_state == 1) && + (flash_buffer_a[0] == 0x0AAA) && (flash_buffer_v[0] == 0x98)) + { + cfi_mode = 1; + flash_state = 0; + } + + // sector erase + if ((flash_state == 6) && + (flash_buffer_a[0] == 0x0AAA) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == 0x0555) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[2] == 0x0AAA) && (flash_buffer_v[2] == 0x80) && + (flash_buffer_a[3] == 0x0AAA) && (flash_buffer_v[3] == 0xAA) && + (flash_buffer_a[4] == 0x0555) && (flash_buffer_v[4] == 0x55) && + (flash_buffer_v[5] == 0x30)) + { + int sector = prg_bank_a_mapped * 0x2000 / FLASH_SECTOR_SIZE; + uint32 sector_address = sector * FLASH_SECTOR_SIZE; + for (uint32 i = sector_address; i < sector_address + FLASH_SECTOR_SIZE; i++) + SAVE_FLASH[i % SAVE_FLASH_SIZE] = 0xFF; + FCEU_printf("Flash sector #%d is erased: 0x%08x - 0x%08x.\n", sector, sector_address, sector_address + FLASH_SECTOR_SIZE - 1); + flash_state = 0; + } + + // write byte + if ((flash_state == 4) && + (flash_buffer_a[0] == 0x0AAA) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == 0x0555) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[2] == 0x0AAA) && (flash_buffer_v[2] == 0xA0)) + { + int sector = prg_bank_a_mapped * 0x2000 / FLASH_SECTOR_SIZE; + uint32 sector_address = sector * FLASH_SECTOR_SIZE; + uint32 flash_addr = prg_bank_a_mapped * 0x2000 + (A % 0x8000); + if (SAVE_FLASH[flash_addr % SAVE_FLASH_SIZE] != 0xFF) { + FCEU_PrintError("Error: can't write to 0x%08x, flash sector is not erased.\n", flash_addr); + } + else { + SAVE_FLASH[flash_addr % SAVE_FLASH_SIZE] = V; + } + flash_state = 0; + } + } + + // not a command + if (((A & 0xFFF) != 0x0AAA) && ((A & 0xFFF) != 0x0555)) { + flash_state = 0; + } + + // reset + if (V == 0xF0) { + flash_state = 0; + cfi_mode = 0; + } + + COOLGIRL_Sync_PRG(); +} + +static DECLFW(COOLGIRL_WRITE) { + if (sram_enabled && A >= 0x6000 && A < 0x8000 && !map_rom_on_6000) + CartBW(A, V); // SRAM is enabled and writable + if (SAVE_FLASH && can_write_flash && A >= 0x8000) // writing flash + COOLGIRL_Flash_Write(A, V); + + // block two writes in a row + if ((timestampbase + timestamp) < (lreset + 2)) return; + lreset = timestampbase + timestamp; + + if (A >= 0x5000 && A < 0x6000 && !lockout) + { + //FCEU_printf("Write: %02x => %04x\n", V, A); + switch (A & 7) + { + case 0: + // {prg_base[26:22]} = cpu_data_in[4:0]; + // use bits 29-27 to simulate flash memory + SET_BITS(prg_base, "29:22", V, "7:0"); + break; + case 1: + // prg_base[21:14] = cpu_data_in[7:0]; + SET_BITS(prg_base, "21:14", V, "7:0"); + break; + case 2: + // {chr_mask[18], prg_mask[20:14]} = cpu_data_in[7:0]; + SET_BITS(chr_mask, "18", V, "7"); + SET_BITS(prg_mask, "20:14", V, "6:0"); + //FCEU_printf("REG_prg_mask: %02x\n", REG_prg_mask); + break; + case 3: + // {prg_mode[2:0], chr_bank_a[7:3]} = cpu_data_in[7:0]; + SET_BITS(prg_mode, "2:0", V, "7:5"); + SET_BITS(chr_bank_a, "7:3", V, "4:0"); + break; + case 4: + // {chr_mode[2:0], chr_mask[17:13]} = cpu_data_in[7:0]; + SET_BITS(chr_mode, "2:0", V, "7:5"); + SET_BITS(chr_mask, "17:13", V, "4:0"); + break; + case 5: + // {chr_bank_a[8], prg_bank_a[5:1], sram_page[1:0]} = cpu_data_in[7:0]; + SET_BITS(chr_bank_a, "8", V, "7"); + SET_BITS(prg_bank_a, "5:1", V, "6:2"); + SET_BITS(sram_page, "1:0", V, "1:0"); + break; + case 6: + // {flags[2:0], mapper[4:0]} = cpu_data_in[7:0]; + SET_BITS(flags, "2:0", V, "7:5"); + SET_BITS(mapper, "4:0", V, "4:0"); + break; + case 7: + // {lockout, mapper[5], four_screen, mirroring[1:0], prg_write_enabled, chr_write_enabled, sram_enabled} = cpu_data_in[7:0]; + SET_BITS(lockout, "0", V, "7"); + SET_BITS(mapper, "5", V, "6"); + SET_BITS(four_screen, "0", V, "5"); + SET_BITS(mirroring, "1:0", V, "4:3"); + SET_BITS(can_write_flash, "0", V, "2"); + SET_BITS(can_write_chr, "0", V, "1"); + SET_BITS(sram_enabled, "0", V, "0"); + if (mapper == 0b010001) prg_bank_b = ~2; // if (USE_MAPPER_009_010 && mapper == 6'b010001) prg_bank_b = 8'b11111101; + if (mapper == 0b010111) map_rom_on_6000 = 1; // if (ENABLE_MAPPER_042 && (mapper == 6'b010111)) map_rom_on_6000 <= 1; + if (mapper == 0b001110) prg_bank_b = 1; // if (USE_MAPPER_065 && mapper == 6'b001110) prg_bank_b = 1; + break; + } + } + + if (A < 0x8000) // $0000-$7FFF + { + // Mapper #163 + if (mapper == 0b000110) + { + if (A == 0x5101) // if (cpu_addr_in[14:0] == 15'h5101) + { + if (mapper163_r4 && !V) // if ((mapper163_r4 != 0) && (cpu_data_in == 0)) + mapper163_r5 ^= 1; // mapper163_r5[0] = ~mapper163_r5[0]; + mapper163_r4 = V; + } + else if (A == 0x5100 && V == 6) // if ((cpu_addr_in[14:0] == 15'h5100) && (cpu_data_in == 6)) + { + SET_BITS(prg_mode, "0", 0, "0"); // prg_mode[0] = 0; + prg_bank_b = 0b1100; // prb_bank_b = 4'b1100; + } + else { + if (get_bits(A, "14:12") == 0b101) // if (cpu_addr_in[14:12] == 3'b101) begin + { + switch (get_bits(A, "9:8")) // case (cpu_addr_in[9:8]) + { + case 2: + SET_BITS(prg_mode, "0", 1, "0"); // prg_mode[0] = 1; + SET_BITS(prg_bank_a, "7:6", V, "1:0"); // prg_bank_a[7:6] = cpu_data_in[1:0]; + mapper163_r0 = V; + break; + case 0: + SET_BITS(prg_mode, "0", 1, "0"); // prg_mode[0] = 1; + SET_BITS(prg_bank_a, "5:2", V, "3:0"); // prg_bank_a[5:2] = cpu_data_in[3:0]; + SET_BITS(chr_mode, "0", V, "7"); // chr_mode[0] = cpu_data_in[7]; + mapper163_r1 = V; + break; + case 3: + mapper163_r2 = V; // mapper163_r2 = cpu_data_in; + break; + case 1: + mapper163_r3 = V; // mapper163_r3 = cpu_data_in; + break; + } + } + } + } + + // Mapper #87 + if (mapper == 0b001100) + { + if (get_bits(A, "14:13") == 0b11) // if (cpu_addr_in[14:13] == 2'b11) // $6000-$7FFF + { + // chr_bank_a[4:3] = {cpu_data_in[0], cpu_data_in[1]}; + SET_BITS(chr_bank_a, "4:3", V, "0,1"); + } + } + + // Mapper #90 - JY + /* + if (mapper == 0b001101) + { + switch (A) + { + case 0x5800: mul1 = V; break; + case 0x5801: mul2 = V; break; + } + } + */ + + // MMC5 (not really) + if (mapper == 0b001111) + { + // case (cpu_addr_in[14:0]) + switch (get_bits(A, "14:0")) + { + case 0x5105: + if (V == 0xFF) + { + four_screen = 1; + } + else { + four_screen = 0; + // case ({cpu_data_in[4], cpu_data_in[2]}) + switch (get_bits(V, "4,2")) + { + case 0b00: // 2'b00: mirroring = 2'b10; + mirroring = 0b10; break; + case 0b01: // 2'b01: mirroring = 2'b00; + mirroring = 0b00; break; + case 0b10: // 2'b10: mirroring = 2'b01; + mirroring = 0b01; break; + case 0b11: // 2'b11: mirroring = 2'b11; + mirroring = 0b11; break; + } + } + break; + case 0x5115: + // prg_bank_a[4:0] = { cpu_data_in[4:1], 1'b0}; + SET_BITS(prg_bank_a, "4:1", V, "4:1"); + SET_BITS(prg_bank_a, "0", 0, "0"); + // prg_bank_b[4:0] = { cpu_data_in[4:1], 1'b1}; + SET_BITS(prg_bank_b, "4:1", V, "4:1"); + SET_BITS(prg_bank_b, "0", 1, "0"); + break; + case 0x5116: + // prg_bank_c[4:0] = cpu_data_in[4:0]; + SET_BITS(prg_bank_c, "4:0", V, "4:0"); + break; + case 0x5117: + // prg_bank_d[4:0] = cpu_data_in[4:0]; + SET_BITS(prg_bank_d, "4:0", V, "4:0"); + break; + case 0x5120: + // chr_bank_a[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_a, "7:0", V, "7:0"); + break; + case 0x5121: + // chr_bank_b[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_b, "7:0", V, "7:0"); + break; + case 0x5122: + // chr_bank_c[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_c, "7:0", V, "7:0"); + break; + case 0x5123: + // chr_bank_d[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_d, "7:0", V, "7:0"); + break; + case 0x5128: + // chr_bank_e[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_e, "7:0", V, "7:0"); + break; + case 0x5129: + // chr_bank_f[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_f, "7:0", V, "7:0"); + break; + case 0x512A: + // chr_bank_g[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_g, "7:0", V, "7:0"); + break; + case 0x512B: + // chr_bank_h[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_h, "7:0", V, "7:0"); + break; + case 0x5203: + // mmc5_irq_ack = 1; + X6502_IRQEnd(FCEU_IQEXT); + mmc5_irq_out = 0; + // mmc5_irq_line[7:0] = cpu_data_in[7:0]; + SET_BITS(mmc5_irq_line, "7:0", V, "7:0"); + break; + case 0x5204: + // mmc5_irq_ack = 1; + X6502_IRQEnd(FCEU_IQEXT); + mmc5_irq_out = 0; + // mmc5_irq_enabled = cpu_data_in[7]; + mmc5_irq_enabled = get_bits(V, "7"); + break; + } + } + + // Mapper #189 + if ((mapper == 0b010100) && (flags & 2)) + { + if (A >= 0x4120) // if (cpu_addr_in[14:0] >= 15'h4120) // $4120-$7FFF + { + // prg_bank_a[5:2] = cpu_data_in[3:0] | cpu_data_in[7:4]; + prg_bank_a = set_bits(prg_bank_a, "5:2", + get_bits(V, "7:4") | get_bits(V, "3:0")); + } + } + + // Mappers #79 and #146 - NINA-03/06 and Sachen 3015: (flag0 = 1) + if (mapper == 0b011011) + { + // if ({cpu_addr_in[14:13], cpu_addr_in[8]} == 3'b101) + if (get_bits(A, "14:13,8") == 0b101) + { + //chr_bank_a[5:3] = cpu_data_in[2:0]; + SET_BITS(chr_bank_a, "5:3", V, "2:0"); + //prg_bank_a[2] = cpu_data_in[3]; + SET_BITS(prg_bank_a, "2", V, "3"); + } + } + + // Mapper #133 + if (mapper == 0b011100) + { + // if ({cpu_addr_in[14:13], cpu_addr_in[8]} == 3'b101) + if (get_bits(A, "14:13,8") == 0b101) + { + //chr_bank_a[4:3] = cpu_data_in[1:0]; + SET_BITS(chr_bank_a, "4:3", V, "1:0"); + //prg_bank_a[2] = cpu_data_in[2]; + SET_BITS(prg_bank_a, "2", V, "2"); + } + } + + // Mapper #184 + if (mapper == 0b011111) + { + if (get_bits(A, "14:13") == 0b11) // if (cpu_addr_in[14:13] == 2'b11) + { + // chr_bank_a[4:2] = cpu_data_in[2:0]; + SET_BITS(chr_bank_a, "4:2", V, "2:0"); + // chr_bank_e[4:2] = {1'b1, cpu_data_in[5:4]}; + SET_BITS(chr_bank_e, "3:2", V, "5:4"); + SET_BITS(chr_bank_e, "4", 1, "0"); + } + } + + // Mapper #38 + if (mapper == 0b100000) + { + // if (cpu_addr_in[14:12] == 3'b111) + if (get_bits(A, "14:12") == 0b111) + { + // prg_bank_a[3:2] = cpu_data_in[1:0]; + SET_BITS(prg_bank_a, "3:2", V, "1:0"); + // chr_bank_a[4:3] = cpu_data_in[3:2]; + SET_BITS(chr_bank_a, "4:3", V, "3:2"); + } + } + } + else // $8000-$FFFF + { + // Mapper #2 - UxROM + // flag0 - mapper #71 - for Fire Hawk only. + // other mapper-#71 games are UxROM + if (mapper == 0b000001) + { + // if (!ENABLE_MAPPER_071 | ~flags[0] | (cpu_addr_in[14:12] != 3'b001)) + if (!(flags & 1) || (get_bits(A, "14:12") != 0b001)) + { + // prg_bank_a[UxROM_BITSIZE+1:1] = cpu_data_in[UxROM_BITSIZE:0]; + // UxROM_BITSIZE = 4 + SET_BITS(prg_bank_a, "5:1", V, "4:0"); + // if (ENABLE_MAPPER_030 && flags[1]) + if (flags & 2) + { + // One screen mirroring select, CHR RAM bank, PRG ROM bank + // mirroring[1:0] = { 1'b1, cpu_data_in[7]}; + SET_BITS(mirroring, "1", 1, "0"); + SET_BITS(mirroring, "0", V, "7"); + // chr_bank_a[1:0] = cpu_data_in[6:5]; + SET_BITS(chr_bank_a, "1:0", V, "6:5"); + } + } + else { + // CodeMasters, blah. Mirroring control used only by Fire Hawk + // mirroring[1:0] = {1'b1, cpu_data_in[4]}; + SET_BITS(mirroring, "1", 1, "0"); + SET_BITS(mirroring, "0", V, "4"); + } + } + + // Mapper #3 - CNROM + if (mapper == 0b000010) + { + // chr_bank_a[7:3] = cpu_data_in[4:0]; + SET_BITS(chr_bank_a, "7:3", V, "4:0"); + } + + // Mapper #78 - Holy Diver + if (mapper == 0b000011) + { + // prg_bank_a[3:1] = cpu_data_in[2:0]; + SET_BITS(prg_bank_a, "3:1", V, "2:0"); + // chr_bank_a[6:3] = cpu_data_in[7:4]; + SET_BITS(chr_bank_a, "6:3", V, "7:4"); + // mirroring = { 1'b0, ~cpu_data_in[3]}; + mirroring = get_bits(V, "3") ^ 1; + } + + // Mapper #97 - Irem's TAM-S1 + if (mapper == 0b000100) + { + // prg_bank_a[5:1] = cpu_data_in[4:0]; + SET_BITS(chr_bank_a, "5:1", V, "4:0"); + // mirroring = { 1'b0, ~cpu_data_in[7]}; + mirroring = get_bits(V, "7") ^ 1; + } + + // Mapper #93 - Sunsoft-2 + if (mapper == 0b000101) + { + // prg_bank_a[3:1] = { cpu_data_in[6:4] }; + SET_BITS(prg_bank_a, "3:1", V, "6:4"); + // chr_write_enabled = cpu_data_in[0]; + can_write_chr = V & 1; + } + + // Mapper #18 + if (mapper == 0b000111) + { + // case ({cpu_addr_in[14:12], cpu_addr_in[1:0]}) + switch (get_bits(A, "14:12,1:0")) + { + case 0b00000: // 5'b00000: prg_bank_a[3:0] = cpu_data_in[3:0]; // $8000 + SET_BITS(prg_bank_a, "3:0", V, "3:0"); break; + case 0b00001: // 5'b00001: prg_bank_a[7:4] = cpu_data_in[3:0]; // $8001 + SET_BITS(prg_bank_a, "7:4", V, "3:0"); break; + case 0b00010: // 5'b00010: prg_bank_b[3:0] = cpu_data_in[3:0]; // $8002 + SET_BITS(prg_bank_b, "3:0", V, "3:0"); break; + case 0b00011: // 5'b00011: prg_bank_b[7:4] = cpu_data_in[3:0]; // $8003 + SET_BITS(prg_bank_b, "7:4", V, "3:0"); break; + case 0b00100: // 5'b00100: prg_bank_c[3:0] = cpu_data_in[3:0]; // $9000 + SET_BITS(prg_bank_c, "3:0", V, "3:0"); break; + case 0b00101: // 5'b00101: prg_bank_c[7:4] = cpu_data_in[3:0]; // $9001 + SET_BITS(prg_bank_c, "7:4", V, "3:0"); break; + case 0b00110: + break; + case 0b00111: + break; + case 0b01000: // 5'b01000: chr_bank_a[3:0] = cpu_data_in[3:0]; // $A000 + SET_BITS(chr_bank_a, "3:0", V, "3:0"); break; + case 0b01001: // 5'b01001: chr_bank_a[7:4] = cpu_data_in[3:0]; // $A001 + SET_BITS(chr_bank_a, "7:4", V, "3:0"); break; + case 0b01010: // 5'b01010: chr_bank_b[3:0] = cpu_data_in[3:0]; // $A002 + SET_BITS(chr_bank_b, "3:0", V, "3:0"); break; + case 0b01011: // 5'b01011: chr_bank_b[7:4] = cpu_data_in[3:0]; // $A003 + SET_BITS(chr_bank_b, "7:4", V, "3:0"); break; + case 0b01100: // 5'b01100: chr_bank_c[3:0] = cpu_data_in[3:0]; // $B000 + SET_BITS(chr_bank_c, "3:0", V, "3:0"); break; + case 0b01101: // 5'b01101: chr_bank_c[7:4] = cpu_data_in[3:0]; // $B001 + SET_BITS(chr_bank_c, "7:4", V, "3:0"); break; + case 0b01110: // 5'b01110: chr_bank_d[3:0] = cpu_data_in[3:0]; // $B002 + SET_BITS(chr_bank_d, "3:0", V, "3:0"); break; + case 0b01111: // 5'b01111: chr_bank_d[7:4] = cpu_data_in[3:0]; // $B003 + SET_BITS(chr_bank_d, "7:4", V, "3:0"); break; + case 0b10000: // 5'b10000: chr_bank_e[3:0] = cpu_data_in[3:0]; // $C000 + SET_BITS(chr_bank_e, "3:0", V, "3:0"); break; + case 0b10001: // 5'b10001: chr_bank_e[7:4] = cpu_data_in[3:0]; // $C001 + SET_BITS(chr_bank_e, "7:4", V, "3:0"); break; + case 0b10010: // 5'b10010: chr_bank_f[3:0] = cpu_data_in[3:0]; // $C002 + SET_BITS(chr_bank_f, "3:0", V, "3:0"); break; + case 0b10011: // 5'b10011: chr_bank_f[7:4] = cpu_data_in[3:0]; // $C003 + SET_BITS(chr_bank_f, "7:4", V, "3:0"); break; + case 0b10100: // 5'b10100: chr_bank_g[3:0] = cpu_data_in[3:0]; // $D000 + SET_BITS(chr_bank_g, "3:0", V, "3:0"); break; + case 0b10101: // 5'b10101: chr_bank_g[7:4] = cpu_data_in[3:0]; // $D001 + SET_BITS(chr_bank_g, "7:4", V, "3:0"); break; + case 0b10110: // 5'b10110: chr_bank_h[3:0] = cpu_data_in[3:0]; // $D002 + SET_BITS(chr_bank_h, "3:0", V, "3:0"); break; + case 0b10111: // 5'b10111: chr_bank_h[7:4] = cpu_data_in[3:0]; // $D003 + SET_BITS(chr_bank_h, "7:4", V, "3:0"); break; + case 0b11000: // 5'b11000: mapper18_irq_latch[3:0] = cpu_data_in[3:0]; // $E000 + SET_BITS(mapper18_irq_latch, "3:0", V, "3:0"); break; + case 0b11001: // 5'b11001: mapper18_irq_latch[7:4] = cpu_data_in[3:0]; // $E001 + SET_BITS(mapper18_irq_latch, "7:4", V, "3:0"); break; + case 0b11010: // 5'b11010: mapper18_irq_latch[11:8] = cpu_data_in[3:0]; // $E002 + SET_BITS(mapper18_irq_latch, "11:8", V, "3:0"); break; + case 0b11011: // 5'b11011: mapper18_irq_latch[15:12] = cpu_data_in[3:0]; // $E003 + SET_BITS(mapper18_irq_latch, "15:12", V, "3:0"); break; + case 0b11100: // 5'b11100: begin // $F000 + // mapper18_irq_out = 0; // ack + X6502_IRQEnd(FCEU_IQEXT); + // mapper18_irq_value[15:0] = mapper18_irq_latch[15:0]; + mapper18_irq_value = mapper18_irq_latch; break; // irq_cpu_out = 0; + case 0b11101: // 5'b11101: begin // $F001 + X6502_IRQEnd(FCEU_IQEXT); // irq_cpu_control[3:0] = cpu_data_in[3:0]; + SET_BITS(mapper18_irq_control, "3:0", V, "3:0"); break; + case 0b11110: // 5'b11110 + switch (get_bits(V, "1:0")) // case (cpu_data_in[1:0]) + { + case 0b00: mirroring = 0b01; break; //2'b00: mirroring = 2'b01; // Horz + case 0b01: mirroring = 0b00; break; //2'b01: mirroring = 2'b00; // Vert + case 0b10: mirroring = 0b10; break; //2'b10: mirroring = 2'b10; // 1SsA + case 0b11: mirroring = 0b11; break; //2'b11: mirroring = 2'b11; // 1SsB + } + case 0b11111: + break; // sound + } + } + + // Mapper #7 - AxROM, mapper #241 - BNROM + if (mapper == 0b001000) + { + // AxROM_BxROM_BITSIZE = 3 + //prg_bank_a[AxROM_BxROM_BITSIZE + 2:2] = cpu_data_in[AxROM_BxROM_BITSIZE:0]; + SET_BITS(prg_bank_a, "5:2", V, "3:0"); + //if (!ENABLE_MAPPER_034_241_BxROM || !flags[0]) // BxROM? + // mirroring = { 1'b1, cpu_data_in[4]}; + if (!(flags & 1)) + mirroring = (1 << 1) | get_bits(V, "4"); + } + + // Mapper #228 - Cheetahmen II + if (mapper == 0b001001) + { + // prg_bank_a[5:2] = cpu_addr_in[10:7]; + SET_BITS(prg_bank_a, "5:2", A, "10:7"); + // chr_bank_a[7:3] = { cpu_addr_in[2:0], cpu_data_in[1:0] }; // only 256k, sorry + SET_BITS(chr_bank_a, "7:5", A, "2:0"); + SET_BITS(chr_bank_a, "4:3", V, "1:0"); + // mirroring = { 1'b0, cpu_addr_in[13]}; + mirroring = get_bits(A, "13"); + } + + // Mapper #11 - ColorDreams + if (mapper == 0b001010) + { + // prg_bank_a[3:2] = cpu_data_in[1:0]; + SET_BITS(prg_bank_a, "3:2", V, "1:0"); + // chr_bank_a[6:3] = cpu_data_in[7:4]; + SET_BITS(chr_bank_a, "6:3", V, "7:4"); + } + + // Mapper #66 - GxROM + if (mapper == 0b001011) + { + // prg_bank_a[3:2] = cpu_data_in[5:4]; + SET_BITS(prg_bank_a, "3:2", V, "5:4"); + // chr_bank_a[4:3] = cpu_data_in[1:0]; + SET_BITS(chr_bank_a, "4:3", V, "1:0"); + } + + // Mapper #90 - JY + if (mapper == 0b001101) + { + // if (cpu_addr_in[14:12] == 3'b000) // $800x + if (get_bits(A, "14:12") == 0b000) + { + // case (cpu_addr_in[1:0]) + switch (get_bits(A, "1:0")) + { + // 2'b00: prg_bank_a[5:0] = cpu_data_in[5:0]; + case 0b00: SET_BITS(prg_bank_a, "5:0", V, "5:0"); break; + // 2'b01: prg_bank_b[5:0] = cpu_data_in[5:0]; + case 0b01: SET_BITS(prg_bank_b, "5:0", V, "5:0"); break; + // 2'b10: prg_bank_c[5:0] = cpu_data_in[5:0]; + case 0b10: SET_BITS(prg_bank_c, "5:0", V, "5:0"); break; + // 2'b11: prg_bank_d[5:0] = cpu_data_in[5:0]; + case 0b11: SET_BITS(prg_bank_d, "5:0", V, "5:0"); break; + } + } + + // if (cpu_addr_in[14:12] == 3'b001) // $900x + if (get_bits(A, "14:12") == 0b001) + { + // case (cpu_addr_in[2:0]) + switch (get_bits(A, "2:0")) + { + case 0b000: // 3'b000: chr_bank_a[7:0] = cpu_data_in[7:0]; // $9000 + SET_BITS(chr_bank_a, "7:0", V, "7:0"); break; + case 0b001: // 3'b001: chr_bank_b[7:0] = cpu_data_in[7:0]; // $9001 + SET_BITS(chr_bank_b, "7:0", V, "7:0"); break; + case 0b010: // 3'b010: chr_bank_c[7:0] = cpu_data_in[7:0]; // $9002 + SET_BITS(chr_bank_c, "7:0", V, "7:0"); break; + case 0b011: // 3'b011: chr_bank_d[7:0] = cpu_data_in[7:0]; // $9003 + SET_BITS(chr_bank_d, "7:0", V, "7:0"); break; + case 0b100: // 3'b100: chr_bank_e[7:0] = cpu_data_in[7:0]; // $9004 + SET_BITS(chr_bank_e, "7:0", V, "7:0"); break; + case 0b101: // 3'b101: chr_bank_f[7:0] = cpu_data_in[7:0]; // $9005 + SET_BITS(chr_bank_f, "7:0", V, "7:0"); break; + case 0b110: // 3'b110: chr_bank_g[7:0] = cpu_data_in[7:0]; // $9006 + SET_BITS(chr_bank_g, "7:0", V, "7:0"); break; + case 0b111: // 3'b111: chr_bank_h[7:0] = cpu_data_in[7:0]; // $9007 + SET_BITS(chr_bank_h, "7:0", V, "7:0"); break; + } + } + + // if ({cpu_addr_in[14:12], cpu_addr_in[1:0]} == 5'b10101) // $D001 + if (get_bits(A, "14:12,1:0") == 0b10101) + { + // mirroring = cpu_data_in[1:0]; + SET_BITS(mirroring, "1:0", V, "1:0"); + } + + // use MMC3's IRQs + // if (cpu_addr_in[14:12] == 3'b100) // $C00x + if (get_bits(A, "14:12") == 0b100) + { + // case (cpu_addr_in[2:0]) + switch (get_bits(A, "2:0")) + { + case 0b000: + // 3'b000: mmc3_irq_enabled = cpu_data_in[0]; + if (V & 1) + { + mmc3_irq_enabled = 1; + } + else { + X6502_IRQEnd(FCEU_IQEXT); + mmc3_irq_enabled = 0; + } + break; + case 0b001: + break; // who cares about this shit? + case 0b010: + // 3'b010: mmc3_irq_enabled = 0; + mmc3_irq_enabled = 0; + X6502_IRQEnd(FCEU_IQEXT); + break; + case 0b011: + // 3'b011: mmc3_irq_enabled = 1; + mmc3_irq_enabled = 1; + break; + case 0b100: + break; // prescaler? who cares? + case 0b101: + // mmc3_irq_latch = cpu_data_in ^ mapper90_xor; + mmc3_irq_latch = V ^ mapper90_xor; + mmc3_irq_reload = 1; + break; + case 0b110: + // mapper90_xor = cpu_data_in; + mapper90_xor = V; + break; + case 0b111: + break; // meh + } + } + } + + // Mapper #65 - Irem's H3001 + if (mapper == 0b001110) + { + // case ({cpu_addr_in[14:12], cpu_addr_in[2:0]}) + switch (get_bits(A, "14:12,2:0")) + { + case 0b000000: + // 6'b000000: prg_bank_a[5:0] = cpu_data_in[5:0]; // $8000 + SET_BITS(prg_bank_a, "5:0", V, "5:0"); + break; + case 0b001001: + // 6'b001001: mirroring = {1'b0, cpu_data_in[7]}; // $9001, mirroring + mirroring = get_bits(V, "7"); + break; + case 0b001011: + // mapper65_irq_out = 0; // ack + X6502_IRQEnd(FCEU_IQEXT); // irq_cpu_out = 0; + // mapper65_irq_enabled = cpu_data_in[7]; // $9003, enable IRQ + mapper65_irq_enabled = get_bits(V, "7"); + break; + case 0b001100: + X6502_IRQEnd(FCEU_IQEXT); // mapper65_irq_out = 0; // ack + mapper65_irq_value = mapper65_irq_latch; // $9004, IRQ reload + break; + case 0b001101: // mapper65_irq_latch[15:8] = cpu_data_in; // $9005, IRQ high value + SET_BITS(mapper65_irq_latch, "15:8", V, "7:0"); break; + case 0b001110: // mapper65_irq_latch[7:0] = cpu_data_in; // $9006, IRQ low value + SET_BITS(mapper65_irq_latch, "7:0", V, "7:0"); break; + case 0b010000: // prg_bank_b[5:0] = cpu_data_in[5:0]; // $A000 + prg_bank_b = (prg_bank_b & 0b11000000) | (V & 0b00111111); break; + case 0b011000: // chr_bank_a[7:0] = cpu_data_in; // $B000 + SET_BITS(chr_bank_a, "7:0", V, "7:0"); break; + case 0b011001: // chr_bank_b[7:0] = cpu_data_in[7:0]; // $B001 + SET_BITS(chr_bank_b, "7:0", V, "7:0"); break; + case 0b011010: // chr_bank_c[7:0] = cpu_data_in[7:0]; // $B002 + SET_BITS(chr_bank_c, "7:0", V, "7:0"); break; + case 0b011011: // chr_bank_d[7:0] = cpu_data_in[7:0]; // $B003 + SET_BITS(chr_bank_d, "7:0", V, "7:0"); break; + case 0b011100: // chr_bank_e[7:0] = cpu_data_in[7:0]; // $B004 + SET_BITS(chr_bank_e, "7:0", V, "7:0"); break; + case 0b011101: // chr_bank_f[7:0] = cpu_data_in[7:0]; // $B005 + SET_BITS(chr_bank_f, "7:0", V, "7:0"); break; + case 0b011110: // chr_bank_g[7:0] = cpu_data_in[7:0]; // $B006 + SET_BITS(chr_bank_g, "7:0", V, "7:0"); break; + case 0b011111: // chr_bank_h[7:0] = cpu_data_in[7:0]; // $B007 + SET_BITS(chr_bank_h, "7:0", V, "7:0"); break; + case 0b100000: // 6'b100000: prg_bank_c[5:0] = cpu_data_in[5:0]; // $C000 + SET_BITS(prg_bank_c, "5:0", V, "5:0"); break; + } + } + + // Mapper #1 - MMC1 + /* + r0 - load register + flag0 - 16KB of SRAM (SOROM) + */ + if (mapper == 0b010000) + { + if (V & 0x80) // reset + { + mmc1_load_register = set_bits(mmc1_load_register, "5:0", 0b100000); // mmc1_load_register[5:0] = 6'b100000; + prg_mode = 0; // 0x4000 (A) + fixed last (C) + prg_bank_c = set_bits(prg_bank_c, "4:0", 0b11110); // prg_bank_c[4:0] = 5'b11110; + } + else { + // mmc1_load_register[5:0] = { cpu_data_in[0], mmc1_load_register[5:1] }; + SET_BITS(mmc1_load_register, "4:0", mmc1_load_register, "5:1"); + SET_BITS(mmc1_load_register, "5", V, "0"); + // if (mmc1_load_register[0] == 1) + if (mmc1_load_register & 1) + { + switch ((A >> 13) & 3) + { + case 0b00: // 2'b00: begin // $8000-$9FFF + if (get_bits(mmc1_load_register, "4:3") == 0b11) // if (mmc1_load_register[4:3] == 2'b11) + { + prg_mode = 0; // prg_mode = 3'b000; // 0x4000 (A) + fixed last (C) + prg_bank_c = set_bits(prg_bank_c, "4:1", 0b1111); // prg_bank_c[4:1] = 4'b1111; + } + //else if (mmc1_load_register[4:3] == 2'b10) + else if (get_bits(mmc1_load_register, "4:3") == 0b10) + { + prg_mode = 0b001; // prg_mode = 3'b001; // fixed first (C) + 0x4000 (A) + prg_bank_c = set_bits(prg_bank_c, "4:1", 0b0000); // prg_bank_c[4:0] = 4'b0000; + } + else + prg_mode = 0b111; // prg_mode = 3'b111; // 0x8000 (A) + if (get_bits(mmc1_load_register, "5")) + chr_mode = 0b100; + else + chr_mode = 0b000; + mirroring = set_bits(mirroring, "1:0", get_bits(mmc1_load_register, "2:1") ^ 0b10); + break; + case 0b01: // 2'b01 + SET_BITS(chr_bank_a, "6:2", mmc1_load_register, "5:1"); // chr_bank_a[6:2] = mmc1_load_register[5:1]; + if (flags & 1) // (flags[0]) - 16KB of SRAM + { + // PRG RAM page #2 is battery backed + sram_page = 2 | get_bits(mmc1_load_register, "4") ^ 1; // sram_page <= {1'b1, ~mmc1_load_register[4]}; + } + SET_BITS(prg_bank_a, "5", mmc1_load_register, "5"); // prg_bank_a[5] = mmc1_load_register[5]; // for SUROM, 512k PRG support + SET_BITS(prg_bank_c, "5", mmc1_load_register, "5"); // prg_bank_c[5] = mmc1_load_register[5]; // for SUROM, 512k PRG support + break; + case 0b10: // 2'b10: chr_bank_e[6:2] = mmc1_load_register[5:1]; // $C000-$DFFF + SET_BITS(chr_bank_e, "6:2", mmc1_load_register, "5:1"); + break; + case 0b11: // 2'b11 + // prg_bank_a[4:1] = r0[4:1]; + SET_BITS(prg_bank_a, "4:1", mmc1_load_register, "4:1"); + // sram_enabled = ~mmc1_load_register[5]; + sram_enabled = get_bits(mmc1_load_register, "5") ^ 1; + break; + } + mmc1_load_register = 0b100000; // mmc1_load_register[5:0] = 6'b100000; + } + } + } + + // Mapper #9 and #10 - MMC2 and MMC4 + // flag0 - 0=MMC2, 1=MMC4 + if (mapper == 0b010001) + { + switch ((A >> 12) & 7) + { + case 2: // $A000-$AFFF + if (!(flags & 1)) // MMC2 + prg_bank_a = (prg_bank_a & 0xF0) | (V & 0x0F); // prg_bank_a[3:0] = cpu_data_in[3:0]; + else // MMC4 + prg_bank_a = (prg_bank_a & 0xE1) | ((V & 0x0F) << 1); // prg_bank_a[4:0] = { cpu_data_in[3:0], 1'b0}; + break; + case 3: // $B000-$BFFF + chr_bank_a = (chr_bank_a & 0x83) | ((V & 0x1F) << 2); // chr_bank_a[6:2] = cpu_data_in[4:0]; + break; + case 4: // $C000-$CFFF + chr_bank_b = (chr_bank_b & 0x83) | ((V & 0x1F) << 2); // chr_bank_b[6:2] = cpu_data_in[4:0]; + break; + case 5: // $D000-$DFFF + chr_bank_e = (chr_bank_e & 0x83) | ((V & 0x1F) << 2); // chr_bank_b[6:2] = cpu_data_in[4:0]; + break; + case 6: // $E000-$EFFF + chr_bank_f = (chr_bank_f & 0x83) | ((V & 0x1F) << 2); // chr_bank_b[6:2] = cpu_data_in[4:0]; + break; + case 7: // $F000-$FFFF + mirroring = V & 1; + break; + } + } + + // Mapper #152 + if (mapper == 0b010010) + { + chr_bank_a = (chr_bank_a & 0x87) | ((V & 0x0F) << 3); // chr_bank_a[6:3] = cpu_data_in[3:0]; + prg_bank_a = (prg_bank_a & 0xF1) | ((V & 0x70) >> 3); // prg_bank_a[3:1] = cpu_data_in[6:4]; + mirroring = 2 | (V >> 7); // mirroring = {1'b1, cpu_data_in[7]}; + } + + // Mapper #73 - VRC3 + if (mapper == 0b010011) + { + switch (get_bits(A, "14:12")) // case (cpu_addr_in[14:12]) + { + case 0b000: // 3'b000: vrc3_irq_latch[3:0] = cpu_data_in[3:0]; // $8000-$8FFF + SET_BITS(vrc3_irq_latch, "3:0", V, "3:0"); + break; + case 0b001: // 3'b001: vrc3_irq_latch[7:4] = cpu_data_in[3:0]; // $9000-$9FFF + SET_BITS(vrc3_irq_latch, "7:4", V, "3:0"); + break; + case 0b010: // 3'b010: vrc3_irq_latch[11:8] = cpu_data_in[3:0]; // $A000-$AFFF + SET_BITS(vrc3_irq_latch, "11:8", V, "3:0"); + break; + case 0b011: // 3'b011: vrc3_irq_latch[15:12] = cpu_data_in[3:0]; // $B000-$BFFF + SET_BITS(vrc3_irq_latch, "15:12", V, "3:0"); + break; + case 0b100: // // $C000-$CFFF + X6502_IRQEnd(FCEU_IQEXT); // vrc3_irq_out = 0; // ack + SET_BITS(vrc3_irq_control, "2:0", V, "2:0"); // vrc3_irq_control[2:0] = cpu_data_in[2:0]; // mode, enabled, enabled after ack + if (vrc3_irq_control & 2) // if (vrc3_irq_control[1]) // if E is set + vrc3_irq_value = vrc3_irq_latch; // vrc3_irq_value[15:0] = vrc3_irq_latch[15:0]; // reload with latch + break; + case 0b101: // // $D000-$DFFF + X6502_IRQEnd(FCEU_IQEXT); // vrc3_irq_out = 0; // ack + SET_BITS(vrc3_irq_control, "1", vrc3_irq_control, "0"); // vrc3_irq_control[1] = vrc3_irq_control[0]; + break; + case 0b110: // $E000-$EFFF + break; + case 0b111: // 3'b111: prg_bank_a[3:1] = cpu_data_in[2:0]; // $F000-$FFFF + SET_BITS(prg_bank_a, "3:1", V, "2:0"); + break; + } + } + + // Mapper #4 - MMC3/MMC6 + /* + flag0 - TxSROM + flag1 - mapper #189 + */ + if (mapper == 0b010100) + { + // case ({cpu_addr_in[14:13], cpu_addr_in[0]}) + switch (get_bits(A, "14:13,0")) + { + case 0b000: // $8000-$9FFE, even + SET_BITS(mmc3_internal, "2:0", V, "2:0"); // mmc3_internal[2:0] = cpu_data_in[2:0]; + if (!(flags & 2) && !(flags & 4)) // if ((!USE_MAPPER_189 | ~flags[1]) & (!USE_MAPPER_206 | ~flags[2])) + { + if (get_bits(V, "6")) // if (cpu_data_in[6]) + prg_mode = 0b101; + else + prg_mode = 0b100; + } + if (!(flags & 4)) // if (!USE_MAPPER_206 | ~flags[2]) // disabled for mapper #206 + { + if (V & 0x80) // if (cpu_data_in[7]) + chr_mode = 0b011; + else + chr_mode = 0b010; + } + break; + case 0b001: // $8001-$9FFF, odd + switch (get_bits(mmc3_internal, "2:0")) + { + case 0b000: // 3'b000: chr_bank_a[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_a, "7:0", V, "7:0"); break; + case 0b001: // 3'b001: chr_bank_c[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_c, "7:0", V, "7:0"); break; + case 0b010: // 3'b010: chr_bank_e[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_e, "7:0", V, "7:0"); break; + case 0b011: // 3'b011: chr_bank_f[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_f, "7:0", V, "7:0"); break; + case 0b100: // 3'b100: chr_bank_g[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_g, "7:0", V, "7:0"); break; + case 0b101: // 3'b101: chr_bank_h[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_h, "7:0", V, "7:0"); break; + case 0b110: // 3'b110: if (!ENABLE_MAPPER_189 | ~flags[1]) prg_bank_a[(MMC3_BITSIZE-1):0] = cpu_data_in[(MMC3_BITSIZE-1):0]; + if (!(flags & 2)) + SET_BITS(prg_bank_a, "7:0", V, "7:0"); + break; + case 0b111: // 3'b111: if (!ENABLE_MAPPER_189 | ~flags[1]) prg_bank_b[(MMC3_BITSIZE-1):0] = cpu_data_in[(MMC3_BITSIZE-1):0]; + if (!(flags & 2)) + SET_BITS(prg_bank_b, "7:0", V, "7:0"); + break; + } + break; + case 0b010: // $A000-$BFFE, even (mirroring) + // if (!ENABLE_MAPPER_206 | ~flags[2]) // disabled for mapper #206 + if (!(flags & 4)) + mirroring = V & 1; // mirroring = {1'b0, cpu_data_in[0]}; + break; + case 0b011: // RAM protect... no + break; + case 0b100: // 3'b100: mmc3_irq_latch = cpu_data_in; // $C000-$DFFE, even (IRQ latch) + mmc3_irq_latch = V; break; + case 0b101: // 3'b101: mmc3_irq_reload = 1; // $C001-$DFFF, odd + mmc3_irq_reload = 1; break; + case 0b110: // 3'b110: mmc3_irq_enabled = 0; // $E000-$FFFE, even + X6502_IRQEnd(FCEU_IQEXT); + mmc3_irq_enabled = 0; + break; + case 0b111: // $E001-$FFFF, odd + if (!(flags & 4)) // if (!USE_MAPPER_206 | ~flags[2]) // disabled for mapper #206 + mmc3_irq_enabled = 1; // mmc3_irq_enabled = 1; + break; + } + } + + // Mapper #112 + /* + r0[2:0] - internal register + */ + if (mapper == 0b010101) + { + switch (get_bits(A, "14:13")) + { + case 0b00: // $8000-$9FFF + SET_BITS(mapper112_internal, "2:0", V, "2:0"); // mapper112_internal[2:0] = cpu_data_in[2:0]; + break; + case 0b01: // $A000-BFFF + switch (get_bits(mapper112_internal, "2:0")) + { + case 0b000: // 3'b000: prg_bank_a[5:0] = cpu_data_in[5:0]; + SET_BITS(prg_bank_a, "5:0", V, "5:0"); break; + case 0b001: // 3'b001: prg_bank_b[5:0] = cpu_data_in[5:0]; + SET_BITS(prg_bank_b, "5:0", V, "5:0"); break; + case 0b010: // 3'b010: chr_bank_a[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_a, "7:0", V, "7:0"); break; + case 0b011: // 3'b011: chr_bank_c[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_c, "7:0", V, "7:0"); break; + case 0b100: // 3'b100: chr_bank_e[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_e, "7:0", V, "7:0"); break; + case 0b101: // 3'b101: chr_bank_f[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_f, "7:0", V, "7:0"); break; + case 0b110: // 3'b110: chr_bank_g[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_g, "7:0", V, "7:0"); break; + case 0b111: // 3'b111: chr_bank_h[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_h, "7:0", V, "7:0"); break; + } + break; + case 0b10: // $C000-$DFFF + break; + case 0b11: // 2'b11: mirroring = {1'b0, cpu_data_in[0]}; // $E000-$FFFF + mirroring = V & 1; break; + } + } + + // Mappers #33 + #48 - Taito + // flag0=0 - #33, flag0=1 - #48 + if (mapper == 0b010110) + { + // case ({cpu_addr_in[14:13], cpu_addr_in[1:0]}) + switch (get_bits(A, "14:13,1:0")) + { + case 0b0000: + SET_BITS(prg_bank_a, "5:0", V, "5:0"); // prg_bank_a[5:0] = cpu_data_in[5:0]; // $8000, PRG Reg 0 (8k @ $8000) + if (!(flags & 1)) // if (~flags[0]) // 33 + mirroring = get_bits(V, "6"); // mirroring = {1'b0, cpu_data_in[6]}; + break; + case 0b0001: // 4'b0001: prg_bank_b[5:0] = cpu_data_in[5:0]; // $8001, PRG Reg 1 (8k @ $A000) + SET_BITS(prg_bank_b, "5:0", V, "5:0"); break; + case 0b0010: // 4'b0010: chr_bank_a[7:1] = cpu_data_in[6:0]; // $8002, CHR Reg 0 (2k @ $0000) + SET_BITS(chr_bank_a, "7:1", V, "6:0"); break; + case 0b0011: // 4'b0011: chr_bank_c[7:1] = cpu_data_in[6:0]; // $8003, CHR Reg 1 (2k @ $0800) + SET_BITS(chr_bank_c, "7:1", V, "6:0"); break; + case 0b0100: // 4'b0100: chr_bank_e[7:0] = cpu_data_in[7:0]; // $A000, CHR Reg 2 (1k @ $1000) + SET_BITS(chr_bank_e, "7:0", V, "7:0"); break; + case 0b0101: // 4'b0101: chr_bank_f[7:0] = cpu_data_in[7:0]; // $A001, CHR Reg 2 (1k @ $1400) + SET_BITS(chr_bank_f, "7:0", V, "7:0"); break; + case 0b0110: // 4'b0110: chr_bank_g[7:0] = cpu_data_in[7:0]; // $A002, CHR Reg 2 (1k @ $1800) + SET_BITS(chr_bank_g, "7:0", V, "7:0"); break; + case 0b0111: // 4'b0111: chr_bank_h[7:0] = cpu_data_in[7:0]; // $A003, CHR Reg 2 (1k @ $1C00) + SET_BITS(chr_bank_h, "7:0", V, "7:0"); break; + case 0b1100: // 4'b1100: if (flags[0]) mirroring = {1'b0, cpu_data_in[6]}; // $E000, mirroring, for mapper #48 + if (flags & 1) // 48 + mirroring = get_bits(V, "6"); // mirroring = cpu_data_in[6]; + case 0b1000: // 4'b1000: irq_scanline_latch = ~cpu_data_in; // $C000, IRQ latch + mmc3_irq_latch = set_bits(mmc3_irq_latch, "7:0", get_bits(V, "7:0") ^ 0b11111111); + break; + case 0b1001: // 4'b1001: mmc3_irq_reload = 1; // $C001, IRQ reload + mmc3_irq_reload = 1; break; + case 0b1010: // 4'b1010: mmc3_irq_enabled = 1; // $C002, IRQ enable + mmc3_irq_enabled = 1; break; + case 0b1011: // 4'b1011: mmc3_irq_enabled = 0; // $C003, IRQ disable & ack + mmc3_irq_enabled = 0; + X6502_IRQEnd(FCEU_IQEXT); // irq_cpu_out = 0; // ack + break; + } + } + + // Mapper #42 + if (mapper == 0b010111) + { + // case ({cpu_addr_in[14], cpu_addr_in[1:0]}) + switch (get_bits(A, "14,1:0")) + { + case 0: // 3'b000: chr_bank_a[7:3] = cpu_data_in[4:0]; // $8000, CHR Reg (8k @ $8000) + SET_BITS(chr_bank_a, "7:3", V, "4:0"); + break; + case 4: // 3'b100: prg_bank_6000[3:0] = cpu_data_in[3:0]; // $E000, PRG Reg (8k @ $6000) + SET_BITS(prg_bank_6000, "3:0", V, "3:0"); + break; + case 5: // 3'b101: mirroring = {1'b0, cpu_data_in[3]}; // Mirroring + mirroring = get_bits(V, "3"); + break; + case 6: // 3'b110: ... // IRQ + mapper42_irq_enabled = get_bits(V, "1"); // mapper42_irq_enabled = cpu_data_in[1]; + if (!mapper42_irq_enabled) // if (!mapper42_irq_enabled) + { + X6502_IRQEnd(FCEU_IQEXT); // irq_cpu_out = 0; + mapper42_irq_value = 0; // mapper42_irq_value = 0; + } + break; + } + } + + // Mapper #23 - VRC2/4 + /* + flag0 - switches A0 and A1 lines. 0=A0,A1 like VRC2b (mapper #23), 1=A1,A0 like VRC2a(#22), VRC2c(#25) + flag1 - divides CHR bank select by two (mapper #22, VRC2a) + */ + if (mapper == 0b011000) + { + uint8 vrc_2b_hi = + (flags & 5) == 0 ? // (!flags[0] && !flags[2]) ? + (get_bits(A, "7") | get_bits(A, "2")) // | cpu_addr_in[7] | cpu_addr_in[2]) // mapper #21 + : (flags & 5) == 1 ? //: (flags[0] && !flags[2]) ? + get_bits(A, "0") // (cpu_addr_in[0]) // mapper #22 + : (flags & 5) == 4 ? // : (!flags[0] && flags[2]) ? + (get_bits(A, "5") | get_bits(A, "3") | get_bits(A, "1")) // (cpu_addr_in[5] | cpu_addr_in[3] | cpu_addr_in[1]) // mapper #23 + : (get_bits(A, "2") | get_bits(A, "0")); // : (cpu_addr_in[2] | cpu_addr_in[0]); // mapper #25 + uint8 vrc_2b_low = + (flags & 5) == 0 ? // (!flags[0] && !flags[2]) ? + (get_bits(A, "6") | get_bits(A, "1")) // (cpu_addr_in[6] | cpu_addr_in[1]) // mapper #21 + : (flags & 5) == 1 ? // : (flags[0] && !flags[2]) ? + get_bits(A, "1") // (cpu_addr_in[1]) // mapper #22 + : (flags & 5) == 4 ?// : (!flags[0] && flags[2]) ? + (get_bits(A, "4") | get_bits(A, "2") | get_bits(A, "0")) // (cpu_addr_in[4] | cpu_addr_in[2] | cpu_addr_in[0]) // mapper #23 + : (get_bits(A, "3") | get_bits(A, "1")); // : (cpu_addr_in[3] | cpu_addr_in[1]); // mapper #25 + + // case ({ cpu_addr_in[14:12], vrc_2b_hi, vrc_2b_low }) + switch ((get_bits(A, "14:12") << 2) | (vrc_2b_hi << 1) | vrc_2b_low) + { + case 0b00000: // $8000-$8003, PRG0 + case 0b00001: + case 0b00010: + case 0b00011: + SET_BITS(prg_bank_a, "4:0", V, "4:0"); // prg_bank_a[4:0] = cpu_data_in[4:0]; + break; + case 0b00100: // $9000-$9001, mirroring + case 0b00101: + // VRC2 - using games are usually well - behaved and only write 0 or 1 to this register, + // but Wai Wai World in one instance writes $FF instead + if (V != 0b11111111) // if (cpu_data_in != 8'b11111111) mirroring = cpu_data_in[1:0]; // $9000-$9001, mirroring + SET_BITS(mirroring, "1:0", V, "1:0"); + break; + case 0b00110: // $9002-$9004, PRG swap + case 0b00111: + SET_BITS(prg_mode, "0", V, "1"); // prg_mode[0] = cpu_data_in[1]; + break; + case 0b01000: // $A000-$A003, PRG1 + case 0b01001: + case 0b01010: + case 0b01011: + SET_BITS(prg_bank_b, "4:0", V, "4:0"); // prg_bank_b[4:0] = cpu_data_in[4:0]; + break; + case 0b01100: // 5'b01100: chr_bank_a[3:0] = cpu_data_in[3:0]; // $B000, CHR0 low + SET_BITS(chr_bank_a, "3:0", V, "3:0"); break; + case 0b01101: // 5'b01101: chr_bank_a[7:4] = cpu_data_in[3:0]; // $B001, CHR0 hi + SET_BITS(chr_bank_a, "7:4", V, "3:0"); break; + case 0b01110: // 5'b01110: chr_bank_b[3:0] = cpu_data_in[3:0]; // $B002, CHR1 low + SET_BITS(chr_bank_b, "3:0", V, "3:0"); break; + case 0b01111: // 5'b01111: chr_bank_b[7:4] = cpu_data_in[3:0]; // $B003, CHR1 hi + SET_BITS(chr_bank_b, "7:4", V, "3:0"); break; + case 0b10000: // 5'b10000: chr_bank_c[3:0] = cpu_data_in[3:0]; // $C000, CHR2 low + SET_BITS(chr_bank_c, "3:0", V, "3:0"); break; + case 0b10001: // 5'b10001: chr_bank_c[7:4] = cpu_data_in[3:0]; // $C001, CHR2 hi + SET_BITS(chr_bank_c, "7:4", V, "3:0"); break; + case 0b10010: // 5'b10010: chr_bank_d[3:0] = cpu_data_in[3:0]; // $C002, CHR3 low + SET_BITS(chr_bank_d, "3:0", V, "3:0"); break; + case 0b10011: // 5'b10011: chr_bank_d[7:4] = cpu_data_in[3:0]; // $C003, CHR3 hi + SET_BITS(chr_bank_d, "7:4", V, "3:0"); break; + case 0b10100: // 5'b10100: chr_bank_e[3:0] = cpu_data_in[3:0]; // $D000, CHR4 low + SET_BITS(chr_bank_e, "3:0", V, "3:0"); break; + case 0b10101: // 5'b10101: chr_bank_e[7:4] = cpu_data_in[3:0]; // $D001, CHR4 hi + SET_BITS(chr_bank_e, "7:4", V, "3:0"); break; + case 0b10110: // 5'b10110: chr_bank_f[3:0] = cpu_data_in[3:0]; // $D002, CHR5 low + SET_BITS(chr_bank_f, "3:0", V, "3:0"); break; + case 0b10111: // 5'b10111: chr_bank_f[7:4] = cpu_data_in[3:0]; // $D003, CHR5 hi + SET_BITS(chr_bank_f, "7:4", V, "3:0"); break; + case 0b11000: // 5'b11000: chr_bank_g[3:0] = cpu_data_in[3:0]; // $E000, CHR6 low + SET_BITS(chr_bank_g, "3:0", V, "3:0"); break; + case 0b11001: // 5'b11001: chr_bank_g[7:4] = cpu_data_in[3:0]; // $E001, CHR6 hi + SET_BITS(chr_bank_g, "7:4", V, "3:0"); break; + case 0b11010: // 5'b11010: chr_bank_h[3:0] = cpu_data_in[3:0]; // $E002, CHR7 low + SET_BITS(chr_bank_h, "3:0", V, "3:0"); break; + case 0b11011: // 5'b11011: chr_bank_h[7:4] = cpu_data_in[3:0]; // $E003, CHR7 hi + SET_BITS(chr_bank_h, "7:4", V, "3:0"); break; + } + + // if (cpu_addr_in[14:12] == 3'b111) + if (get_bits(A, "14:12") == 0b111) + { + // case (vrc_2b_hi, vrc_2b_low}) + switch ((vrc_2b_hi << 1) | vrc_2b_low) + { + case 0b00: // 2'b00: vrc4_irq_latch[3:0] = cpu_data_in[3:0]; // IRQ latch low + SET_BITS(vrc4_irq_latch, "3:0", V, "3:0"); break; + case 0b01: // 2'b01: vrc4_irq_latch[7:4] = cpu_data_in[3:0]; // IRQ latch hi + SET_BITS(vrc4_irq_latch, "7:4", V, "3:0"); break; + case 0b10: // 2'b10 // IRQ control + X6502_IRQEnd(FCEU_IQEXT); // vrc4_irq_out = 0; // ack + SET_BITS(vrc4_irq_control, "2:0", V, "2:0"); // vrc4_irq_control[2:0] = cpu_data_in[2:0]; // mode, enabled, enabled after ack + if (vrc4_irq_control & 2) // if (vrc4_irq_control[1]) begin // if E is set + { + vrc4_irq_prescaler_counter = 0; // vrc4_irq_prescaler_counter[1:0] = 2'b00; // reset prescaler + vrc4_irq_prescaler = 0; // vrc4_irq_prescaler[6:0] = 7'b0000000; + SET_BITS(vrc4_irq_value, "7:0", vrc4_irq_latch, "7:0"); // vrc4_irq_value[7:0] = vrc4_irq_latch[7:0]; // reload with latch + } + break; + case 0b11: // 2'b11 // IRQ ack + X6502_IRQEnd(FCEU_IQEXT); // vrc4_irq_out = 0; + SET_BITS(vrc4_irq_control, "1", vrc4_irq_control, "0"); // vrc4_irq_control[1] = vrc4_irq_control[0]; + break; + } + } + } + + // Mapper #69 - Sunsoft FME-7 + if (mapper == 0b011001) + { + // if (cpu_addr_in[14:13] == 2'b00) mapper69_internal[3:0] = cpu_data_in[3:0]; + if (get_bits(A, "14:13") == 0b00) SET_BITS(mapper69_internal, "3:0", V, "3:0"); + // if (cpu_addr_in[14:13] == 2'b01) + if (get_bits(A, "14:13") == 0b01) + { + switch (get_bits(mapper69_internal, "3:0")) // case (mapper69_internal[3:0]) + { + case 0b0000: // 4'b0000: chr_bank_a[7:0] = cpu_data_in[7:0]; // CHR0 + SET_BITS(chr_bank_a, "7:0", V, "7:0"); break; + case 0b0001: // 4'b0001: chr_bank_b[7:0] = cpu_data_in[7:0]; // CHR1 + SET_BITS(chr_bank_b, "7:0", V, "7:0"); break; + case 0b0010: // 4'b0010: chr_bank_c[7:0] = cpu_data_in[7:0]; // CHR2 + SET_BITS(chr_bank_c, "7:0", V, "7:0"); break; + case 0b0011: // 4'b0011: chr_bank_d[7:0] = cpu_data_in[7:0]; // CHR3 + SET_BITS(chr_bank_d, "7:0", V, "7:0"); break; + case 0b0100: // 4'b0100: chr_bank_e[7:0] = cpu_data_in[7:0]; // CHR4 + SET_BITS(chr_bank_e, "7:0", V, "7:0"); break; + case 0b0101: // 4'b0101: chr_bank_f[7:0] = cpu_data_in[7:0]; // CHR5 + SET_BITS(chr_bank_f, "7:0", V, "7:0"); break; + case 0b0110: // 4'b0110: chr_bank_g[7:0] = cpu_data_in[7:0]; // CHR6 + SET_BITS(chr_bank_g, "7:0", V, "7:0"); break; + case 0b0111: // 4'b0111: chr_bank_h[7:0] = cpu_data_in[7:0]; // CHR7 + SET_BITS(chr_bank_h, "7:0", V, "7:0"); break; + case 0b1000: // 4'b1000: {sram_enabled, map_rom_on_6000, prg_bank_6000} = {cpu_data_in[7], ~cpu_data_in[6], cpu_data_in[5:0]}; // PRG0 + sram_enabled = (V >> 7) & 1; + map_rom_on_6000 = ((V >> 6) & 1) ^ 1; + prg_bank_6000 = V & 0x3F; + break; + case 0b1001: // 4'b1001: prg_bank_a[5:0] = cpu_data_in[5:0]; // PRG1 + SET_BITS(prg_bank_a, "5:0", V, "5:0"); break; + case 0b1010: // 4'b1010: prg_bank_b[5:0] = cpu_data_in[5:0]; // PRG2 + SET_BITS(prg_bank_b, "5:0", V, "5:0"); break; + case 0b1011: // 4'b1011: prg_bank_c[5:0] = cpu_data_in[5:0]; // PRG3 + SET_BITS(prg_bank_c, "5:0", V, "5:0"); break; + case 0b1100: // 4'b1100: mirroring[1:0] = cpu_data_in[1:0]; // mirroring + SET_BITS(mirroring, "1:0", V, "1:0"); break; + case 0b1101: // 4'b1101 + X6502_IRQEnd(FCEU_IQEXT); // fme7_irq_out = 0; // ack + mapper69_counter_enabled = get_bits(V, "7"); // fme7_counter_enabled = cpu_data_in[7]; + mapper69_irq_enabled = get_bits(V, "0"); // fme7_irq_enabled = cpu_data_in[0]; + break; + case 0b1110: // 4'b1110: fme7_irq_value[7:0] = cpu_data_in[7:0]; // IRQ low + SET_BITS(mapper69_irq_value, "7:0", V, "7:0"); break; + case 0b1111: // fme7_irq_value[15:8] = cpu_data_in[7:0]; // IRQ high + SET_BITS(mapper69_irq_value, "15:8", V, "7:0"); break; + } + } + } + + // Mapper #32 - Irem's G-101 + if (mapper == 0b011010) + { + switch (get_bits(A, "14:12")) + { + case 0b000: // 2'b00: prg_bank_a[5:0] = cpu_data_in[5:0]; // $8000-$8FFF, PRG0 + SET_BITS(prg_bank_a, "5:0", V, "5:0"); + break; + case 0b001: // 2'b01: {prg_mode[0], mirroring} = {cpu_data_in[1], 1'b0, cpu_data_in[0]}; // $9000-$9FFF, PRG mode, mirroring + SET_BITS(prg_mode, "0", V, "1"); + mirroring = V & 1; + break; + case 0b010: // 2'b10: prg_bank_b[5:0] = cpu_data_in[5:0]; // $A000-$AFFF, PRG1 + SET_BITS(prg_bank_b, "5:0", V, "5:0"); + break; + case 0b011: // $B000-$BFFF, CHR regs + switch (get_bits(A, "2:0")) + { + case 0b000: // 3'b000: chr_bank_a[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_a, "7:0", V, "7:0"); break; + case 0b001: // 3'b001: chr_bank_b[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_b, "7:0", V, "7:0"); break; + case 0b010: // 3'b010: chr_bank_c[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_c, "7:0", V, "7:0"); break; + case 0b011: // 3'b011: chr_bank_d[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_d, "7:0", V, "7:0"); break; + case 0b100: // 3'b100: chr_bank_e[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_e, "7:0", V, "7:0"); break; + case 0b101: // 3'b101: chr_bank_f[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_f, "7:0", V, "7:0"); break; + case 0b110: // 3'b110: chr_bank_g[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_g, "7:0", V, "7:0"); break; + case 0b111: // 3'b111: chr_bank_h[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_h, "7:0", V, "7:0"); break; + } + break; + } + } + + // Mapper #36 is assigned to TXC's PCB 01-22000-400 + if (mapper == 0b011101) + { + if (get_bits(A, "14:1") != 0b11111111111111) // (cpu_addr_in[14:1] != 14'b11111111111111) + { + // prg_bank_a[5:2] = cpu_data_in[7:4]; + SET_BITS(prg_bank_a, "5:2", V, "7:4"); + // chr_bank_a[6:3] = cpu_data_in[3:0]; + SET_BITS(chr_bank_a, "6:3", V, "3:0"); + } + } + + // Mapper #70 + if (mapper == 0b011110) + { + // prg_bank_a[4:1] = cpu_data_in[7:4]; + SET_BITS(prg_bank_a, "4:1", V, "7:4"); + // chr_bank_a[6:3] = cpu_data_in[3:0]; + SET_BITS(chr_bank_a, "6:3", V, "3:0"); + } + + // Mapper #75 - VRC1 + if (mapper == 0b100010) + { + // case (cpu_addr_in[14:12]) + switch (get_bits(A, "14:12")) + { + case 0b000: // 3'b000: prg_bank_a[3:0] = cpu_data_in[3:0]; // $8000-$8FFF + SET_BITS(prg_bank_a, "3:0", V, "3:0"); break; + case 0b001: + mirroring = V & 1; // mirroring = {1'b0, cpu_data_in[0]}; + SET_BITS(chr_bank_a, "6", V, "1"); // chr_bank_a[6] = cpu_data_in[1]; + SET_BITS(chr_bank_e, "6", V, "2"); // chr_bank_e[6] = cpu_data_in[2]; + break; + case 0b010: // 3'b010: prg_bank_b[3:0] = cpu_data_in[3:0]; // $A000-$AFFF + SET_BITS(prg_bank_b, "3:0", V, "3:0"); break; + case 0b100: // 3'b100: prg_bank_c[3:0] = cpu_data_in[3:0]; // $C000-$CFFF + SET_BITS(prg_bank_c, "3:0", V, "3:0"); break; + case 0b110: // 3'b110: �hr_bank_a[5:2] = cpu_data_in[3:0]; // $E000-$EFFF + SET_BITS(chr_bank_a, "5:2", V, "3:0"); break; + case 0b111: // 3'b111: chr_bank_e[5:2] = cpu_data_in[3:0]; // $F000-$FFFF + SET_BITS(chr_bank_e, "5:2", V, "3:0"); break; + } + } + + // Mapper #83 - Cony/Yoko + if (mapper == 0b100011) + { + // case (cpu_addr_in[9:8]) + switch (get_bits(A, "9:8")) + { + case 0b01: // $81xx + mirroring = get_bits(V, "1:0"); // mirroring <= cpu_data_in[1:0]; + mapper83_irq_enabled_latch = get_bits(V, "7"); // mapper83_irq_enabled_latch <= cpu_data_in[7]; + break; + case 0b10: // 82xx + if (!get_bits(A, "0")) // if (!cpu_addr_in[0]) + { + X6502_IRQEnd(FCEU_IQEXT); // mapper83_irq_out <= 0; + SET_BITS(mapper83_irq_counter, "7:0", V, "7:0"); // mapper83_irq_counter[7:0] <= cpu_data_in[7:0]; + } + else { + mapper83_irq_enabled = mapper83_irq_enabled_latch; //mapper83_irq_enabled <= mapper83_irq_enabled_latch; + SET_BITS(mapper83_irq_counter, "15:8", V, "7:0"); // mapper83_irq_counter[15:8] <= cpu_data_in[7:0]; + } + break; + case 0b11: + if (!get_bits(A, "4")) // if (!cpu_addr_in[4]) + { + switch (get_bits(A, "1:0")) // case (cpu_addr_in[1:0]) + { + case 0b00: SET_BITS(prg_bank_a, "7:0", V, "7:0"); break; // 2'b00: prg_bank_a[7:0] <= cpu_data_in[7:0]; + case 0b01: SET_BITS(prg_bank_b, "7:0", V, "7:0"); break; // 2'b01: prg_bank_b[7:0] <= cpu_data_in[7:0]; + case 0b10: SET_BITS(prg_bank_b, "7:0", V, "7:0"); break; // 2'b10: prg_bank_c[7:0] <= cpu_data_in[7:0]; + //case 0b11: SET_BITS(prg_bank_6000, "7:0", V, "7:0"); break; //2'b11: prg_bank_6000[7:0] <= cpu_data_in[7:0]; + } + } + else { + switch (get_bits(A, "2:0")) // case (cpu_addr_in[2:0]) + { + case 0b000: SET_BITS(chr_bank_a, "7:0", V, "7:0"); break; // 3'b000: chr_bank_a[7:0] <= cpu_data_in[7:0]; + case 0b001: SET_BITS(chr_bank_b, "7:0", V, "7:0"); break; // 3'b001: chr_bank_b[7:0] <= cpu_data_in[7:0]; + case 0b010: SET_BITS(chr_bank_c, "7:0", V, "7:0"); break; // 3'b010: chr_bank_c[7:0] <= cpu_data_in[7:0]; + case 0b011: SET_BITS(chr_bank_d, "7:0", V, "7:0"); break; // 3'b011: chr_bank_d[7:0] <= cpu_data_in[7:0]; + case 0b100: SET_BITS(chr_bank_e, "7:0", V, "7:0"); break; // 3'b100: chr_bank_e[7:0] <= cpu_data_in[7:0]; + case 0b101: SET_BITS(chr_bank_f, "7:0", V, "7:0"); break; // 3'b101: chr_bank_f[7:0] <= cpu_data_in[7:0]; + case 0b110: SET_BITS(chr_bank_g, "7:0", V, "7:0"); break; // 3'b110: chr_bank_g[7:0] <= cpu_data_in[7:0]; + case 0b111: SET_BITS(chr_bank_h, "7:0", V, "7:0"); break; // 3'b111: chr_bank_h[7:0] <= cpu_data_in[7:0]; + } + } + break; + } + } + + // Mapper #67 - Sunsoft-3 + if (mapper == 0b100100) + { + if (get_bits(A, "11")) // if (cpu_addr_in[11]) + { + switch (get_bits(A, "14:12")) // case (cpu_addr_in[14:12]) + { + case 0b000: // 3'b000: chr_bank_a[6:1] <= cpu_data_in[5:0]; // $8800 + SET_BITS(chr_bank_a, "6:1", V, "5:0"); break; + case 0b001: // 3'b001: chr_bank_c[6:1] <= cpu_data_in[5:0]; // $9800 + SET_BITS(chr_bank_c, "6:1", V, "5:0"); break; + case 0b010: // 3'b010: chr_bank_e[6:1] <= cpu_data_in[5:0]; // $A800 + SET_BITS(chr_bank_e, "6:1", V, "5:0"); break; + case 0b011: // 3'b011: chr_bank_g[6:1] <= cpu_data_in[5:0]; // $B800 + SET_BITS(chr_bank_g, "6:1", V, "5:0"); break; + case 0b100: // 3'b100: begin // $C800, IRQ load + mapper67_irq_latch = ~mapper67_irq_latch; + if (mapper67_irq_latch) + SET_BITS(mapper67_irq_counter, "15:8", V, "7:0"); // mapper67_irq_counter[15:8] <= cpu_data_in[7:0]; + else + SET_BITS(mapper67_irq_counter, "7:0", V, "7:0"); // mapper67_irq_counter[7:0] <= cpu_data_in[7:0]; + break; + case 0b101: // 3'b101: begin // $D800, IRQ enable + mapper67_irq_latch = 0; // mapper67_irq_latch <= 0; + SET_BITS(mapper67_irq_enabled, "0", V, "4"); // mapper67_irq_enabled <= cpu_data_in[4]; + break; + case 0b110: // 3'b110: mirroring[1:0] <= cpu_data_in[1:0]; // $E800 + SET_BITS(mirroring, "1:0", V, "1:0"); + break; + case 0b111: // 3'b111: prg_bank_a[4:1] <= cpu_data_in[3:0]; // $F800 + SET_BITS(prg_bank_a, "4:1", V, "3:0"); + break; + } + } + else { + // Interrupt Acknowledge ($8000) + X6502_IRQEnd(FCEU_IQEXT); // mapper67_irq_out <= 0; + } + } + + // Mapper #89 - Sunsoft-2 chip on the Sunsoft-3 board + if (mapper == 0b100101) + { + // prg_bank_a[3:1] <= cpu_data_in[6:4]; + SET_BITS(prg_bank_a, "3:1", V, "6:4"); + // chr_bank_a[6:3] <= {cpu_data_in[7], cpu_data_in[2:0]}; + SET_BITS(chr_bank_a, "6:3", V, "7,2:0"); + // mirroring[1:0] <= {1'b1, cpu_data_in[3]}; + SET_BITS(mirroring, "1", 1, "0"); + SET_BITS(mirroring, "0", V, "3"); + } + } + + COOLGIRL_Sync(); +} + +static DECLFR(MAFRAM) { + if ((mapper == 0b000000) && (A >= 0x5000) && (A < 0x6000)) + return 0; + + // Mapper #163 + // (USE_MAPPER_163 && (mapper == 5'b00110) && m2 && romsel && cpu_rw_in && ((cpu_addr_in[14:8] & 7'h77) == 7'h51)) ? + if ((mapper == 0b000110) && ((A & 0x7700) == 0x5100)) + return mapper163_r2 | mapper163_r0 | mapper163_r1 | ~mapper163_r3; // {1'b1, r2 | r0 | r1 | ~r3} + // (USE_MAPPER_163 && (mapper == 5'b00110) && m2 && romsel && cpu_rw_in && ((cpu_addr_in[14:8] & 7'h77) == 7'h55)) ? + if ((mapper == 0b000110) && ((A & 0x7700) == 0x5500)) + return (mapper163_r5 & 1) ? mapper163_r2 : mapper163_r1; // {1'b1, r5[0] ? r2 : r1} + + // MMC5 + if ((mapper == 0b001111) && (A == 0x5204)) + { + int ppuon = (PPU[1] & 0x18); + uint8 r = (!ppuon || scanline + 1 >= 241) ? 0 : 1; + uint8 p = mmc5_irq_out; + X6502_IRQEnd(FCEU_IQEXT); + mmc5_irq_out = 0; + return (p << 7) | (r << 6); + } + + // Mapper #36 is assigned to TXC's PCB 01-22000-400 + if ((mapper == 0b011101) && ((A & 0xE100) == 0x4100)) // (USE_MAPPER_036 && mapper == 5'b11101 && {cpu_addr_in[14:13], cpu_addr_in[8]} == 3'b101) ? + { + return (prg_bank_a & 0x0C) << 2; // {1'b1, 2'b00, prg_bank_a[3:2], 4'b00} + } + + // Mapper #83 - Cony/Yoko + if ((mapper == 0b100011) && ((A & 0x7000) == 0x5000)) return flags & 3; + + // Mapper #90 - JY + if ((mapper == 0b001101) && (A == 0x5800)) return (mul1 * mul2) & 0xFF; + if ((mapper == 0b001101) && (A == 0x5801)) return ((mul1 * mul2) >> 8) & 0xFF; + + if (sram_enabled && !map_rom_on_6000 && (A >= 0x6000) && (A < 0x8000)) + return CartBR(A); // SRAM + if (map_rom_on_6000 && (A >= 0x6000) && (A < 0x8000)) + return CartBR(A); // PRG + + return X.DB; // Open bus +} + +static void COOLGIRL_ScanlineCounter(void) { + // for MMC3 and MMC3-based + if (mmc3_irq_reload || !mmc3_irq_counter) + { + mmc3_irq_counter = mmc3_irq_latch; + mmc3_irq_reload = 0; + } + else + mmc3_irq_counter--; + if (!mmc3_irq_counter && mmc3_irq_enabled) + X6502_IRQBegin(FCEU_IQEXT); + + // for MMC5 + if (mmc5_irq_line == scanline + 1) + { + if (mmc5_irq_enabled) + { + X6502_IRQBegin(FCEU_IQEXT); + mmc5_irq_out = 1; + } + } + + // for mapper #163 + if (scanline == 239) + { + mapper_163_latch = 0; + COOLGIRL_Sync_CHR(); + } + else if (scanline == 127) + { + mapper_163_latch = 1; + COOLGIRL_Sync_CHR(); + } +} + +static void COOLGIRL_CpuCounter(int a) { + while (a--) + { + // Mapper #23 - VRC4 + if (vrc4_irq_control & 2) // if (ENABLE_MAPPER_021_022_023_025 & ENABLE_VRC4_INTERRUPTS & (vrc4_irq_control[1])) + { + // Cycle mode without prescaler is not used by any games? It's missed in fceux source code. + if (vrc4_irq_control & 4) // if (vrc4_irq_control[2]) // cycle mode + { + FCEU_PrintError("Cycle IRQ mode is not supported, please report to Cluster"); + vrc4_irq_value++; // {carry, vrc4_irq_value[7:0]} = vrc4_irq_value[7:0] + 1'b1; // just count IRQ value + if (vrc4_irq_value == 0) // if (carry) + { + X6502_IRQBegin(FCEU_IQEXT); // vrc4_irq_out = 1; + vrc4_irq_value = vrc4_irq_latch; // vrc4_irq_value[7:0] = vrc4_irq_latch[7:0]; + } + } + else { + vrc4_irq_prescaler++; // vrc4_irq_prescaler = vrc4_irq_prescaler + 1'b1; // count prescaler + // if ((vrc4_irq_prescaler_counter[1] == 0 && vrc4_irq_prescaler == 114) + // || (vrc4_irq_prescaler_counter[1] == 1 && vrc4_irq_prescaler == 113)) // 114, 114, 113 + if ((!(vrc4_irq_prescaler_counter & 2) && vrc4_irq_prescaler == 114) || ((vrc4_irq_prescaler_counter & 2) && vrc4_irq_prescaler == 113)) + { + vrc4_irq_prescaler = 0; // vrc4_irq_prescaler = 0; + vrc4_irq_prescaler_counter++; // vrc4_irq_prescaler_counter = vrc4_irq_prescaler_counter + 1'b1; + if (vrc4_irq_prescaler_counter == 0b11) vrc4_irq_prescaler_counter = 0; // if (vrc4_irq_prescaler_counter == 2'b11) vrc4_irq_prescaler_counter = 2'b00; + vrc4_irq_value++; // {carry, vrc4_irq_value[7:0]} = vrc4_irq_value[7:0] + 1'b1; + if (vrc4_irq_value == 0) // f (carry) + { + X6502_IRQBegin(FCEU_IQEXT); + vrc4_irq_value = vrc4_irq_latch; // irq_cpu_value[7:0] = vrc4_irq_latch[7:0]; + } + } + } + } + + // Mapper #73 - VRC3 + if (vrc3_irq_control & 2) + { + if (vrc3_irq_control & 4) // if (vrc3_irq_control[2]) + { // 8-bit mode + //vrc3_irq_value = set_bits(vrc3_irq_value, "7:0", get_bits(vrc3_irq_value, "7:0") + 1); // vrc3_irq_value[7:0] = vrc3_irq_value[7:0] + 1'b1; + vrc3_irq_value = (vrc3_irq_value & 0xFF00) | ((vrc3_irq_value + 1) & 0xFF); + //if (get_bits(vrc3_irq_value, "7:0") == 0) // if (vrc3_irq_value[7:0] == 0) + if ((vrc3_irq_value & 0xFF) == 0) + { + X6502_IRQBegin(FCEU_IQEXT); // vrc3_irq_out = 1; + //SET_BITS(vrc3_irq_value, "7:0", vrc3_irq_latch, "7:0"); // vrc3_irq_value[7:0] = vrc3_irq_latch[7:0]; + vrc3_irq_value = (vrc3_irq_value & 0xFF00) | (vrc3_irq_latch & 0xFF); + } + } + else { // 16-bit + //vrc3_irq_value = set_bits(vrc3_irq_value, "15:0", get_bits(vrc3_irq_value, "15:0") + 1); // vrc3_irq_value[15:0] = vrc3_irq_value[15:0] + 1'b1; + vrc3_irq_value += 1; + if (vrc3_irq_value == 0) // if (vrc3_irq_value[15:0] == 0) + { + X6502_IRQBegin(FCEU_IQEXT); // vrc3_irq_out = 1; + //SET_BITS(vrc3_irq_value, "15:0", vrc3_irq_latch, "15:0"); // vrc3_irq_value[15:0] = vrc3_irq_latch[15:0]; + vrc3_irq_value = vrc3_irq_latch; + } + } + } + + // Mapper #69 - Sunsoft FME-7 + // if (ENABLE_MAPPER_069 & fme7_counter_enabled) + if (mapper69_counter_enabled) + { + // {carry, fme7_irq_value[15:0]} = {0'b0, fme7_irq_value[15:0]} - 1'b1; + mapper69_irq_value--; + // if (fme7_irq_enabled && carry) fme7_irq_out = 1; + if (mapper69_irq_value == 0xFFFF) X6502_IRQBegin(FCEU_IQEXT); + } + + // Mapper #18 + if (mapper18_irq_control & 1) // if (mapper18_irq_control[0]) + { + uint8 carry; + //carry = get_bits(mapper18_irq_value, "3:0") - 1; + carry = (mapper18_irq_value & 0x0F) - 1; + //SET_BITS(mapper18_irq_value, "3:0", carry, "3:0"); + mapper18_irq_value = (mapper18_irq_value & 0xFFF0) | (carry & 0x0F); + carry = (carry >> 4) & 1; + // if (mapper18_irq_control[3] == 1'b0) + if (!(mapper18_irq_control & 0b1000)) + { + //carry = get_bits(mapper18_irq_value, "7:4") - carry; + carry = ((mapper18_irq_value >> 4) & 0x0F) - carry; + //SET_BITS(mapper18_irq_value, "7:4", carry, "3:0"); + mapper18_irq_value = (mapper18_irq_value & 0xFF0F) | ((carry & 0x0F) << 4); + carry = (carry >> 4) & 1; + } + // if (mapper18_irq_control[3:2] == 2'b00) + if (!(mapper18_irq_control & 0b1100)) + { + //carry = get_bits(mapper18_irq_value, "11:8") - carry; + carry = ((mapper18_irq_value >> 8) & 0x0F) - carry; + //SET_BITS(mapper18_irq_value, "11:8", carry, "3:0"); + mapper18_irq_value = (mapper18_irq_value & 0xF0FF) | ((carry & 0x0F) << 8); + carry = (carry >> 4) & 1; + } + // if (mapper18_irq_control[3:1] == 3'b000) + if (!(mapper18_irq_control & 0b1110)) + { + //carry = get_bits(mapper18_irq_value, "15:12") - carry; + carry = ((mapper18_irq_value >> 12) & 0x0F) - carry; + //SET_BITS(mapper18_irq_value, "15:12", carry, "3:0"); + mapper18_irq_value = (mapper18_irq_value & 0x0FFF) | ((carry & 0x0F) << 12); + carry = (carry >> 4) & 1; + } + if (carry) + X6502_IRQBegin(FCEU_IQEXT); + } + + // Mapper #65 - Irem's H3001 + // if (mapper65_irq_enabled) + if (mapper65_irq_enabled) + { + if (mapper65_irq_value != 0) // if (mapper65_irq_value[15:0] != 0) + { + mapper65_irq_value--; // mapper65_irq_value[15:0] = mapper65_irq_value[15:0] - 1; + if (!mapper65_irq_value) + X6502_IRQBegin(FCEU_IQEXT); // if (mapper65_irq_value[15:0] == 0) irq_cpu_out = 1; + } + } + + // Mapper #42 + if (mapper42_irq_enabled) + { + //mapper42_irq_value = set_bits(mapper42_irq_value, "14:0", get_bits(mapper42_irq_value, "14:0") + 1); + //if (get_bits(mapper42_irq_value, "14:13") == 0b11) + mapper42_irq_value++; + if (mapper42_irq_value >> 15) mapper42_irq_value = 0; + if (((mapper42_irq_value >> 13) & 0b11) == 0b11) + X6502_IRQBegin(FCEU_IQEXT); + else + X6502_IRQEnd(FCEU_IQEXT); + } + + // Mapper #83 - Cony/Yoko + if (mapper83_irq_enabled) + { + if (mapper83_irq_counter == 0) // if (mapper83_irq_counter == 0) + X6502_IRQBegin(FCEU_IQEXT); // mapper83_irq_out <= 1; + mapper83_irq_counter--; // mapper83_irq_counter = mapper83_irq_counter - 1'b1; + } + + // Mapper #67 - Sunsoft-3 + if (mapper67_irq_enabled) + { + mapper67_irq_counter--; // mapper67_irq_counter = mapper67_irq_counter - 1'b1; + if (mapper67_irq_counter == 0xFFFF) // if (mapper67_irq_counter == 16'hFFFF) + { + X6502_IRQBegin(FCEU_IQEXT); // mapper67_irq_out <= 1; // fire IRQ + mapper67_irq_enabled = 0; // mapper67_irq_enabled <= 0; // disable IRQ + } + } + } +} + +static void COOLGIRL_PPUHook(uint32 A) { + // For TxROM + if ((mapper == 0b010100) && (flags & 1)) + { + setmirror(MI_0 + (TKSMIR[(A & 0x1FFF) >> 10] >> 7)); + } + + // Mapper #9 and #10 - MMC2 and MMC4 + if (mapper == 0b010001) + { + if ((A >> 4) == 0xFD) // if (ppu_addr_in[13:3] == 11'b00111111011) ppu_latch0 = 0; + { + ppu_latch0 = 0; + COOLGIRL_Sync_CHR(); + } + if ((A >> 4) == 0xFE) // if (ppu_addr_in[13:3] == 11'b00111111101) ppu_latch0 = 1; + { + ppu_latch0 = 1; + COOLGIRL_Sync_CHR(); + } + if ((A >> 4) == 0x1FD) // if (ppu_addr_in[13:3] == 11'b01111111011) ppu_latch1 = 0; + { + ppu_latch1 = 0; + COOLGIRL_Sync_CHR(); + } + if ((A >> 4) == 0x1FE) // if (ppu_addr_in[13:3] == 11'b01111111101) ppu_latch1 = 1; + { + ppu_latch1 = 1; + COOLGIRL_Sync_CHR(); + } + } +} + +static void COOLGIRL_Reset(void) { + sram_enabled = 0; + sram_page = 0; + can_write_chr = 0; + map_rom_on_6000 = 0; + flags = 0; + mapper = 0; + can_write_flash = 0; + mirroring = 0; + four_screen = 0; + lockout = 0; + prg_base = 0; + prg_mask = 0b11111000 << 14; + prg_mode = 0; + prg_bank_6000 = 0; + prg_bank_a = 0; + prg_bank_b = 1; + prg_bank_c = ~1; + prg_bank_d = ~0; + chr_mask = 0; + chr_mode = 0; + chr_bank_a = 0; + chr_bank_b = 1; + chr_bank_c = 2; + chr_bank_d = 3; + chr_bank_e = 4; + chr_bank_f = 5; + chr_bank_g = 6; + chr_bank_h = 7; + ppu_latch0 = 0; + ppu_latch1 = 0; + lreset = 0; + mmc1_load_register = 0; + mmc3_internal = 0; + mapper69_internal = 0; + mapper112_internal = 0; + mapper_163_latch = 0; + mapper163_r0 = 0; + mapper163_r1 = 0; + mapper163_r2 = 0; + mapper163_r3 = 0; + mapper163_r4 = 0; + mapper163_r5 = 0; + mul1 = 0; + mul2 = 0; + mmc3_irq_enabled = 0; + mmc3_irq_latch = 0; + mmc3_irq_counter = 0; + mmc3_irq_reload = 0; + mmc5_irq_enabled = 0; + mmc5_irq_line = 0; + mmc5_irq_out = 0; + mapper18_irq_value = 0; + mapper18_irq_control = 0; + mapper18_irq_latch = 0; + mapper65_irq_enabled = 0; + mapper65_irq_value = 0; + mapper65_irq_latch = 0; + mapper69_irq_enabled = 0; + mapper69_counter_enabled = 0; + mapper69_irq_value = 0; + vrc4_irq_value = 0; + vrc4_irq_control = 0; + vrc4_irq_latch = 0; + vrc4_irq_prescaler = 0; + vrc4_irq_prescaler_counter = 0; + vrc3_irq_value = 0; + vrc3_irq_control = 0; + vrc3_irq_latch = 0; + mapper42_irq_enabled = 0; + mapper42_irq_value = 0; + mapper90_xor = 0; + flash_state = 0; + cfi_mode = 0; + COOLGIRL_Sync(); +} + +static void COOLGIRL_Power(void) { + FCEU_CheatAddRAM(32, 0x6000, WRAM); + SetReadHandler(0x4020, 0x7FFF, MAFRAM); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x4020, 0xFFFF, COOLGIRL_WRITE); + GameHBIRQHook = COOLGIRL_ScanlineCounter; + MapIRQHook = COOLGIRL_CpuCounter; + PPU_hook = COOLGIRL_PPUHook; + COOLGIRL_Reset(); +} + +static void COOLGIRL_Close(void) { + if (CHR_RAM) + FCEU_gfree(CHR_RAM); + if (WRAM) + FCEU_gfree(WRAM); + if (SAVE_FLASH) + FCEU_gfree(SAVE_FLASH); + if (CFI) + FCEU_gfree(CFI); + CHR_RAM = WRAM = SAVE_FLASH = CFI = NULL; +} + +static void COOLGIRL_Restore(int version) { + COOLGIRL_Sync(); + lreset = 0; +} + +#define ExState(var, varname) AddExState(&var, sizeof(var), 0, varname) + +void COOLGIRL_Init(CartInfo *info) { + CHR_RAM_SIZE = info->ines2 ? (info->vram_size + info->battery_vram_size) : (512 * 1024); + CHR_RAM = (uint8*)FCEU_gmalloc(CHR_RAM_SIZE); + memset(CHR_RAM, 0, CHR_RAM_SIZE); + SetupCartCHRMapping(CHR_RAM_CHIP, CHR_RAM, CHR_RAM_SIZE, 0); + AddExState(CHR_RAM, sizeof(CHR_RAM_SIZE), 0, "CHR_"); + + WRAM_SIZE = info->ines2 ? (info->wram_size + info->battery_wram_size) : (32 * 1024); + if (WRAM_SIZE > 0) { + WRAM = (uint8*)FCEU_gmalloc(WRAM_SIZE); + memset(WRAM, 0, WRAM_SIZE); + SetupCartPRGMapping(WRAM_CHIP, WRAM, WRAM_SIZE, 1); + AddExState(WRAM, 32 * 1024, 0, "SRAM"); + if (info->battery) + { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = 32 * 1024; + } + } + + if (info->battery) + { + SAVE_FLASH = (uint8*)FCEU_gmalloc(SAVE_FLASH_SIZE); + SetupCartPRGMapping(FLASH_CHIP, SAVE_FLASH, SAVE_FLASH_SIZE, 1); + AddExState(SAVE_FLASH, SAVE_FLASH_SIZE, 0, "SAVF"); + info->SaveGame[1] = SAVE_FLASH; + info->SaveGameLen[1] = SAVE_FLASH_SIZE; + } + + CFI = (uint8*)FCEU_gmalloc(sizeof(cfi_data) * 2); + for (int i = 0; i < sizeof(cfi_data); i++) + { + CFI[i * 2] = CFI[i * 2 + 1] = cfi_data[i]; + } + SetupCartPRGMapping(CFI_CHIP, CFI, sizeof(cfi_data) * 2, 0); + + ExState(sram_enabled, "SREN"); + ExState(sram_page, "SRPG"); + ExState(can_write_chr, "SRWR"); + ExState(map_rom_on_6000, "MR6K"); + ExState(flags, "FLGS"); + ExState(mapper, "MPPR"); + ExState(can_write_flash, "FLWR"); + ExState(mirroring, "MIRR"); + ExState(four_screen, "4SCR"); + ExState(lockout, "LOCK"); + ExState(prg_base, "PBAS"); + ExState(prg_mask, "PMSK"); + ExState(prg_mode, "PMOD"); + ExState(prg_bank_6000, "P6BN"); + ExState(prg_bank_a, "PABN"); + ExState(prg_bank_b, "PBBN"); + ExState(prg_bank_c, "PCBN"); + ExState(prg_bank_d, "PDBN"); + ExState(chr_mask, "CMSK"); + ExState(chr_mode, "CMOD"); + ExState(chr_bank_a, "CABN"); + ExState(chr_bank_b, "CBBN"); + ExState(chr_bank_c, "CCBN"); + ExState(chr_bank_d, "CDBN"); + ExState(chr_bank_e, "CEBN"); + ExState(chr_bank_f, "CFBN"); + ExState(chr_bank_g, "CGBN"); + ExState(chr_bank_h, "CHBN"); + ExState(ppu_latch0, "PPU0"); + ExState(ppu_latch1, "PPU1"); + ExState(lreset, "LRST"); + ExState(mmc1_load_register, "M01R"); + ExState(mmc3_internal, "M01I"); + ExState(mapper69_internal, "M69I"); + ExState(mapper112_internal, "112I"); + ExState(mapper_163_latch, "163L"); + ExState(mapper163_r0, "1630"); + ExState(mapper163_r1, "1631"); + ExState(mapper163_r2, "1632"); + ExState(mapper163_r3, "1633"); + ExState(mapper163_r4, "1634"); + ExState(mapper163_r5, "1635"); + ExState(mul1, "MUL1"); + ExState(mul2, "MUL2"); + ExState(mmc3_irq_enabled, "M4IE"); + ExState(mmc3_irq_latch, "M4IL"); + ExState(mmc3_irq_counter, "M4IC"); + ExState(mmc3_irq_reload, "M4IR"); + ExState(mmc5_irq_enabled, "M5IE"); + ExState(mmc5_irq_line, "M5IL"); + ExState(mmc5_irq_out, "M5IO"); + ExState(mapper18_irq_value, "18IV"); + ExState(mapper18_irq_control, "18IC"); + ExState(mapper18_irq_latch, "18IL"); + ExState(mapper65_irq_enabled, "65IE"); + ExState(mapper65_irq_value, "65IV"); + ExState(mapper65_irq_latch, "65IL"); + ExState(mapper69_irq_enabled, "69IE"); + ExState(mapper69_counter_enabled, "69CE"); + ExState(mapper69_irq_value, "69IV"); + ExState(vrc4_irq_value, "V4IV"); + ExState(vrc4_irq_control, "V4IC"); + ExState(vrc4_irq_latch, "V4IL"); + ExState(vrc4_irq_prescaler, "V4PP"); + ExState(vrc4_irq_prescaler_counter, "V4PC"); + ExState(vrc3_irq_value, "V3IV"); + ExState(vrc3_irq_control, "V3IC"); + ExState(vrc3_irq_latch, "V3IL"); + ExState(mapper42_irq_enabled, "42IE"); + ExState(mapper42_irq_value, "42IV"); + ExState(mapper83_irq_enabled_latch, "M83L"); + ExState(mapper83_irq_enabled, "M83I"); + ExState(mapper83_irq_counter, "M83C"); + ExState(mapper90_xor, "90XR"); + ExState(mapper67_irq_enabled, "67IE"); + ExState(mapper67_irq_latch, "67IL"); + ExState(mapper67_irq_counter, "67IC"); + ExState(flash_state, "FLST"); + ExState(flash_buffer_a, "FLBA"); + ExState(flash_buffer_v, "FLBV"); + ExState(cfi_mode, "CFIM"); + + info->Power = COOLGIRL_Power; + info->Reset = COOLGIRL_Reset; + info->Close = COOLGIRL_Close; + GameStateRestore = COOLGIRL_Restore; +} diff --git a/src/boards/unrom512.cpp b/src/boards/unrom512.cpp index 973749ea..f0f41fe4 100644 --- a/src/boards/unrom512.cpp +++ b/src/boards/unrom512.cpp @@ -1,7 +1,7 @@ /* FCE Ultra - NES/Famicom Emulator * * Copyright notice for this file: - * Copyright (C) 2014 CaitSith2 + * Copyright (C) 2014 CaitSith2, 2022 Cluster * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,7 +19,7 @@ */ /* - * Roms still using NES 1.0 format should be loaded as 32K CHR RAM. + * Roms still using NES 1.0 format should be loaded as 8K CHR RAM. * Roms defined under NES 2.0 should use the VRAM size field, defining 7, 8 or 9, based on how much VRAM should be present. * UNIF doesn't have this problem, because unique board names can define this information. * The UNIF names are UNROM-512-8K, UNROM-512-16K and UNROM-512-32K @@ -28,6 +28,7 @@ * Known games to use this board are: * Battle Kid 2: Mountain of Torment (512K PRG, 8K CHR RAM, Horizontal Mirroring, Flash disabled) * Study Hall (128K PRG (in 512K flash chip), 8K CHR RAM, Horizontal Mirroring, Flash enabled) + * Nix: The Paradox Relic (512 PRG, 8K CHR RAM, Vertical Mirroring, Flash enabled) * Although Xmas 2013 uses a different board, where LEDs can be controlled (with writes to the $8000-BFFF space), * it otherwise functions identically. */ @@ -35,211 +36,147 @@ #include "mapinc.h" #include "../ines.h" -static uint8 latche, latcheinit, bus_conflict, chrram_mask, software_id=false; +const int ROM_CHIP = 0x00; +const int CFI_CHIP = 0x10; +const int FLASH_CHIP = 0x11; + +const int FLASH_SECTOR_SIZE = 4 * 1024; + +static uint8 flash_save, flash_state, flash_id_mode, latche, bus_conflict; static uint16 latcha; -static uint8 *flashdata; -static uint32 *flash_write_count; -static uint8 *FlashPage[32]; -//static uint32 *FlashWriteCountPage[32]; -//static uint8 flashloaded = false; +static uint8 *flash_data; +static uint16 flash_buffer_a[10]; +static uint8 flash_buffer_v[10]; +static uint8 flash_id[2]; -static uint8 flash_save=0, flash_state=0, flash_mode=0, flash_bank; -static void (*WLSync)(void); -static void (*WHSync)(void); - -static INLINE void setfpageptr(int s, uint32 A, uint8 *p) { - uint32 AB = A >> 11; - int x; - - if (p) - for (x = (s >> 1) - 1; x >= 0; x--) { - FlashPage[AB + x] = p - A; - } +static void UNROM512_Sync() { + int chip; + if (flash_save) + chip = !flash_id_mode ? FLASH_CHIP : CFI_CHIP; else - for (x = (s >> 1) - 1; x >= 0; x--) { - FlashPage[AB + x] = 0; - } -} - -void setfprg16(uint32 A, uint32 V) { - if (PRGsize[0] >= 16384) { - V &= PRGmask16[0]; - setfpageptr(16, A, flashdata ? (&flashdata[V << 14]) : 0); - } else { - uint32 VA = V << 3; - int x; - - for (x = 0; x < 8; x++) - setfpageptr(2, A + (x << 11), flashdata ? (&flashdata[((VA + x) & PRGmask2[0]) << 11]) : 0); - } -} - -void inc_flash_write_count(uint8 bank, uint32 A) -{ - flash_write_count[(bank*4) + ((A&0x3000)>>12)]++; - if(!flash_write_count[(bank*4) + ((A&0x3000)>>12)]) - flash_write_count[(bank*4) + ((A&0x3000)>>12)]++; -} - -uint32 GetFlashWriteCount(uint8 bank, uint32 A) -{ - return flash_write_count[(bank*4) + ((A&0x3000)>>12)]; + chip = ROM_CHIP; + setprg16r(chip, 0x8000, latche & 0b11111); + setprg16r(chip, 0xc000, ~0); + setchr8((latche >> 5) & 0b11); + setmirror(MI_0 + ((latche >> 7) & 1)); } static void StateRestore(int version) { - WHSync(); + UNROM512_Sync(); } -static DECLFW(UNROM512LLatchWrite) +static DECLFW(UNROM512FlashWrite) { - latche = V; - latcha = A; - WLSync(); + if (flash_state < sizeof(flash_buffer_a) / sizeof(flash_buffer_a[0])) { + flash_buffer_a[flash_state] = (A & 0x3FFF) | ((latche & 1) << 14); + flash_buffer_v[flash_state] = V; + flash_state++; + + // enter flash ID mode + if ((flash_state == 2) && + (flash_buffer_a[0] == 0x5555) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == 0x2AAA) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[1] == 0x5555) && (flash_buffer_v[1] == 0x90)) { + flash_id_mode = 0; + flash_state = 0; + } + + // erase sector + if ((flash_state == 6) && + (flash_buffer_a[0] == 0x5555) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == 0x2AAA) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[2] == 0x5555) && (flash_buffer_v[2] == 0x80) && + (flash_buffer_a[3] == 0x5555) && (flash_buffer_v[3] == 0xAA) && + (flash_buffer_a[4] == 0x2AAA) && (flash_buffer_v[4] == 0x55) && + (flash_buffer_v[5] == 0x30)) { + int offset = &Page[A >> 11][A] - flash_data; + int sector = offset / FLASH_SECTOR_SIZE; + for (uint32 i = sector * FLASH_SECTOR_SIZE; i < (sector + 1) * FLASH_SECTOR_SIZE; i++) + flash_data[i % PRGsize[ROM_CHIP]] = 0xFF; + FCEU_printf("Flash sector #%d is erased (0x%08x - 0x%08x).\n", sector, offset, offset + FLASH_SECTOR_SIZE); + } + + // erase chip + if ((flash_state == 6) && + (flash_buffer_a[0] == 0x5555) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == 0x2AAA) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[2] == 0x5555) && (flash_buffer_v[2] == 0x80) && + (flash_buffer_a[3] == 0x5555) && (flash_buffer_v[3] == 0xAA) && + (flash_buffer_a[4] == 0x2AAA) && (flash_buffer_v[4] == 0x55) && + (flash_buffer_a[4] == 0x5555) && (flash_buffer_v[4] == 0x10)) { + memset(flash_data, 0xFF, PRGsize[ROM_CHIP]); + FCEU_printf("Flash chip erased.\n"); + flash_state = 0; + } + + // write byte + if ((flash_state == 4) && + (flash_buffer_a[0] == 0x5555) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == 0x2AAA) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[2] == 0x5555) && (flash_buffer_v[2] == 0xA0)) { + int offset = &Page[A >> 11][A] - flash_data; + if (CartBR(A) != 0xFF) { + FCEU_PrintError("Error: can't write to 0x%08x, flash sector is not erased.\n", offset); + } + else { + CartBW(A, V); + } + flash_state = 0; + } + } + + // not a command + if (((A & 0xFFF) != 0x0AAA) && ((A & 0xFFF) != 0x0555)) { + flash_state = 0; + } + + // reset + if (V == 0xF0) { + flash_state = 0; + flash_id_mode = 0; + } + + UNROM512_Sync(); } static DECLFW(UNROM512HLatchWrite) { if (bus_conflict) - latche = (V == CartBR(A)) ? V : 0; + latche = V & CartBR(A); else latche = V; latcha = A; - WHSync(); -} - -static DECLFR(UNROM512LatchRead) -{ - uint8 flash_id[3]={0xB5,0xB6,0xB7}; - if(software_id) - { - if(A&1) - return flash_id[ROM_size>>4]; - else - return 0xBF; - } - if(flash_save) - { - if(A < 0xC000) - { - if(GetFlashWriteCount(flash_bank,A)) - return FlashPage[A >> 11][A]; - } - else - { - if(GetFlashWriteCount(ROM_size-1,A)) - return FlashPage[A >> 11][A]; - } - } - return Page[A >> 11][A]; + UNROM512_Sync(); } static void UNROM512LatchPower(void) { - latche = latcheinit; - WHSync(); - SetReadHandler(0x8000, 0xFFFF, UNROM512LatchRead); + latche = 0; + UNROM512_Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); if(!flash_save) SetWriteHandler(0x8000, 0xFFFF, UNROM512HLatchWrite); else { - SetWriteHandler(0x8000,0xBFFF,UNROM512LLatchWrite); + SetWriteHandler(0x8000,0xBFFF,UNROM512FlashWrite); SetWriteHandler(0xC000,0xFFFF,UNROM512HLatchWrite); } } static void UNROM512LatchClose(void) { - if(flash_write_count) - FCEU_gfree(flash_write_count); - if(flashdata) - FCEU_gfree(flashdata); - flash_write_count = NULL; - flashdata = NULL; -} - - -static void UNROM512LSync() { - int erase_a[5]={0x9555,0xAAAA,0x9555,0x9555,0xAAAA}; - int erase_d[5]={0xAA,0x55,0x80,0xAA,0x55}; - int erase_b[5]={1,0,1,1,0}; - - if(flash_mode==0) - { - if((latcha == erase_a[flash_state]) && (latche == erase_d[flash_state]) && (flash_bank == erase_b[flash_state])) - { - flash_state++; - if(flash_state == 5) - { - flash_mode=1; - } - } - else if ((flash_state==2)&&(latcha==0x9555)&&(latche==0xA0)&&(flash_bank==1)) - { - flash_state++; - flash_mode=2; - } - else if ((flash_state==2)&&(latcha==0x9555)&&(latche==0x90)&&(flash_bank==1)) - { - flash_state=0; - software_id=true; - } - else - { - if(latche==0xF0) - software_id=false; - flash_state=0; - } - } - else if(flash_mode==1) //Chip Erase or Sector Erase - { - if(latche==0x30) - { - inc_flash_write_count(flash_bank,latcha); - memset(&FlashPage[(latcha & 0xF000) >> 11][latcha & 0xF000],0xFF,0x1000); - } - else if (latche==0x10) - { - for(uint32 i=0;i<(ROM_size*4);i++) - inc_flash_write_count(i>>2,i<<12); - memset(flashdata,0xFF,ROM_size*0x4000); //Erasing the rom chip as instructed. Crash rate calulated to be 99.9% :) - } - flash_state=0; - flash_mode=0; - } - else if(flash_mode==2) //Byte Program - { - if(!GetFlashWriteCount(flash_bank,latcha)) - { - inc_flash_write_count(flash_bank,latcha); - memcpy(&FlashPage[(latcha & 0xF000) >> 11][latcha & 0xF000],&Page[(latcha & 0xF000)>>11][latcha & 0xF000],0x1000); - } - FlashPage[latcha>>11][latcha]&=latche; - flash_state=0; - flash_mode=0; - } -} - -static void UNROM512HSync() -{ - flash_bank=latche&(ROM_size-1); - - setprg16(0x8000, flash_bank); - setprg16(0xc000, ~0); - setfprg16(0x8000, flash_bank); - setfprg16(0xC000, ~0); - setchr8r(0, (latche & chrram_mask) >> 5); - setmirror(MI_0+(latche>>7)); + if(flash_data) + FCEU_gfree(flash_data); + flash_data = NULL; } void UNROM512_Init(CartInfo *info) { - flash_state=0; - flash_bank=0; - flash_save=info->battery; + info->Power = UNROM512LatchPower; + info->Close = UNROM512LatchClose; + GameStateRestore = StateRestore; - if(info->vram_size == 8192) - chrram_mask = 0; - else if (info->vram_size == 16384) - chrram_mask = 0x20; - else - chrram_mask = 0x60; + flash_state = 0; + flash_id_mode = 0; + flash_save = info->battery; + bus_conflict = !info->battery; // Is it required by any game? int mirror = (head.ROM_type & 1) | ((head.ROM_type & 8) >> 2); switch (mirror) @@ -258,28 +195,29 @@ void UNROM512_Init(CartInfo *info) { break; } - bus_conflict = !info->battery; - latcheinit = 0; - WLSync = UNROM512LSync; - WHSync = UNROM512HSync; - info->Power = UNROM512LatchPower; - info->Close = UNROM512LatchClose; - GameStateRestore = StateRestore; if(flash_save) { - flashdata = (uint8*)FCEU_gmalloc(ROM_size*0x4000); - flash_write_count = (uint32*)FCEU_gmalloc(ROM_size*4*sizeof(uint32)); - info->SaveGame[0] = (uint8*)flash_write_count; - info->SaveGame[1] = flashdata; - info->SaveGameLen[0] = ROM_size*4*sizeof(uint32); - info->SaveGameLen[1] = ROM_size*0x4000; - AddExState(flash_write_count,ROM_size*4*sizeof(uint32),0,"FLASH_WRITE_COUNT"); - AddExState(flashdata,ROM_size*0x4000,0,"FLASH_DATA"); - AddExState(&flash_state,1,0,"FLASH_STATE"); - AddExState(&flash_mode,1,0,"FLASH_MODE"); - AddExState(&flash_bank,1,0,"FLASH_BANK"); - AddExState(&latcha,2,0,"LATA"); + // Allocate memory for flash + flash_data = (uint8*)FCEU_gmalloc(PRGsize[ROM_CHIP]); + // Copy ROM to flash data + for (int i = 0; i < PRGsize[ROM_CHIP]; i++) { + flash_data[i] = PRGptr[ROM_CHIP][i % PRGsize[ROM_CHIP]]; + } + SetupCartPRGMapping(FLASH_CHIP, flash_data, PRGsize[ROM_CHIP], 1); + info->SaveGame[0] = flash_data; + info->SaveGameLen[0] = PRGsize[ROM_CHIP]; + + flash_id[0] = 0xBF; + flash_id[1] = 0xB5 + (ROM_size >> 4); + SetupCartPRGMapping(CFI_CHIP, flash_id, sizeof(flash_id), 0); + + AddExState(flash_data, PRGsize[ROM_CHIP], 0, "FLSH"); + AddExState(&flash_state, 1, 0, "FLST"); + AddExState(&flash_id_mode, 1, 0, "FLMD"); + AddExState(flash_buffer_a, sizeof(flash_buffer_a), 0, "FLBA"); + AddExState(flash_buffer_v, sizeof(flash_buffer_v), 0, "FLBV"); } + AddExState(&latcha, 2, 0, "LATA"); AddExState(&latche, 1, 0, "LATC"); AddExState(&bus_conflict, 1, 0, "BUSC"); } diff --git a/src/config.cpp b/src/config.cpp index 66157634..387c2d20 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -26,7 +26,7 @@ const char *FCEUI_GetAboutString(void) "zeromus, feos\n" "\n" "Current Contributors:\n" - "CaH4e3, rainwarrior, owomomo, punkrockguy318\n" + "CaH4e3, rainwarrior, owomomo, punkrockguy318, Cluster\n" "\n" "Past Contributors:\n" "xhainingx, gocha, AnS, mjbudd77\n" diff --git a/src/drivers/Qt/ConsoleViewerGL.cpp b/src/drivers/Qt/ConsoleViewerGL.cpp index f836dd88..c877cefe 100644 --- a/src/drivers/Qt/ConsoleViewerGL.cpp +++ b/src/drivers/Qt/ConsoleViewerGL.cpp @@ -671,7 +671,7 @@ void ConsoleViewGL_t::paintGL(void) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE); + glBlendFunc(GL_ONE, GL_ZERO); //glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); if ( textureType == GL_TEXTURE_RECTANGLE ) diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index fd5119c4..58cd2eb4 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -894,7 +894,6 @@ void consoleWin_t::createMainMenu(void) QActionGroup *group; int useNativeMenuBar; int customAutofireOnFrames, customAutofireOffFrames; - ColorMenuItem *bgColorItem; //QShortcut *shortcut; menubar = new consoleMenuBar(this); @@ -1254,15 +1253,32 @@ void consoleWin_t::createMainMenu(void) optMenu->addAction(act); + optMenu->addSeparator(); + // Options -> Video BG Color fceuLoadConfigColor( "SDL.VideoBgColor", &videoBgColor ); - bgColorItem = new ColorMenuItem( tr("BG Side Panel Color"), "SDL.VideoBgColor", this ); - bgColorItem->connectColor( &videoBgColor ); + bgColorMenuItem = new ColorMenuItem( tr("BG Side Panel Color"), "SDL.VideoBgColor", this ); + bgColorMenuItem->connectColor( &videoBgColor ); - optMenu->addAction(bgColorItem); + optMenu->addAction(bgColorMenuItem); - connect( bgColorItem, SIGNAL(colorChanged(QColor&)), this, SLOT(videoBgColorChanged(QColor&)) ); + connect( bgColorMenuItem, SIGNAL(colorChanged(QColor&)), this, SLOT(videoBgColorChanged(QColor&)) ); + + // Options -> Use BG Palette for Video BG Color + g_config->getOption( "SDL.UseBgPaletteForVideo", &usePaletteForVideoBg ); + + act = new QAction(tr("Use BG Palette for Video BG Color"), this); + //act->setShortcut( QKeySequence(tr("Alt+/"))); + act->setCheckable(true); + act->setChecked( usePaletteForVideoBg ); + act->setStatusTip(tr("Use BG Palette for Video BG Color")); + //act->setIcon( style()->standardIcon( QStyle::SP_TitleBarMaxButton ) ); + connect(act, SIGNAL(triggered(bool)), this, SLOT(toggleUseBgPaletteForVideo(bool)) ); + + optMenu->addAction(act); + + bgColorMenuItem->setEnabled( !usePaletteForVideoBg ); //----------------------------------------------------------------------- // Emulation @@ -2152,6 +2168,20 @@ void consoleWin_t::toggleMenuAutoHide(bool checked) g_config->save(); } //--------------------------------------------------------------------------- +void consoleWin_t::toggleUseBgPaletteForVideo(bool checked) +{ + usePaletteForVideoBg = checked; + + g_config->setOption( "SDL.UseBgPaletteForVideo", usePaletteForVideoBg ); + g_config->save(); + + if ( !usePaletteForVideoBg ) + { + fceuLoadConfigColor( "SDL.VideoBgColor", &videoBgColor ); + } + bgColorMenuItem->setEnabled( !usePaletteForVideoBg ); +} +//--------------------------------------------------------------------------- void consoleWin_t::closeApp(void) { nes_shm->runEmulator = 0; diff --git a/src/drivers/Qt/ConsoleWindow.h b/src/drivers/Qt/ConsoleWindow.h index d4451965..20460591 100644 --- a/src/drivers/Qt/ConsoleWindow.h +++ b/src/drivers/Qt/ConsoleWindow.h @@ -27,6 +27,7 @@ #include #endif +#include "Qt/ColorMenu.h" #include "Qt/ConsoleViewerGL.h" #include "Qt/ConsoleViewerSDL.h" #include "Qt/GamePadConf.h" @@ -259,6 +260,7 @@ class consoleWin_t : public QMainWindow QTimer *gameTimer; QColor videoBgColor; + ColorMenuItem *bgColorMenuItem; std::string errorMsg; bool errorMsgValid; @@ -453,6 +455,7 @@ class consoleWin_t : public QMainWindow void winActiveChanged(void); void emuFrameFinish(void); void toggleMenuAutoHide(bool); + void toggleUseBgPaletteForVideo(bool); void videoBgColorChanged( QColor &c ); void loadRomRequestCB( QString s ); diff --git a/src/drivers/Qt/config.cpp b/src/drivers/Qt/config.cpp index fff49ff4..3f5490cf 100644 --- a/src/drivers/Qt/config.cpp +++ b/src/drivers/Qt/config.cpp @@ -535,6 +535,7 @@ InitConfig() config->addOption('f', "fullscreen", "SDL.Fullscreen", 0); config->addOption("videoDriver", "SDL.VideoDriver", 0); config->addOption("SDL.VideoBgColor", "#000000"); + config->addOption("SDL.UseBgPaletteForVideo", false); config->addOption("SDL.VideoVsync", 1); // set x/y res to 0 for automatic fullscreen resolution detection (no change) diff --git a/src/drivers/Qt/fceuWrapper.cpp b/src/drivers/Qt/fceuWrapper.cpp index 12790b69..888d5b2e 100644 --- a/src/drivers/Qt/fceuWrapper.cpp +++ b/src/drivers/Qt/fceuWrapper.cpp @@ -89,6 +89,7 @@ bool pauseAfterPlayback = false; bool suggestReadOnlyReplay = true; bool showStatusIconOpt = true; bool drawInputAidsEnable = true; +bool usePaletteForVideoBg = false; unsigned int gui_draw_area_width = 256; unsigned int gui_draw_area_height = 256; diff --git a/src/drivers/Qt/fceuWrapper.h b/src/drivers/Qt/fceuWrapper.h index 65534ae0..350480a3 100644 --- a/src/drivers/Qt/fceuWrapper.h +++ b/src/drivers/Qt/fceuWrapper.h @@ -18,6 +18,7 @@ extern bool suggestReadOnlyReplay; extern bool emulatorCycleToggle; extern bool showStatusIconOpt; extern bool drawInputAidsEnable; +extern bool usePaletteForVideoBg; extern unsigned int gui_draw_area_width; extern unsigned int gui_draw_area_height; extern unsigned int emulatorCycleCount; diff --git a/src/drivers/Qt/input.cpp b/src/drivers/Qt/input.cpp index 9bddbc7c..ff4c4f34 100644 --- a/src/drivers/Qt/input.cpp +++ b/src/drivers/Qt/input.cpp @@ -65,7 +65,7 @@ static int cspec = 0; static int buttonConfigInProgress = 0; extern int gametype; -static int DTestButton(ButtConfig *bc); +static int DTestButton(ButtConfig *bc, bool isFKB = false); //std::list gpKeySeqList; @@ -1416,12 +1416,18 @@ void ButtonConfigEnd() * Tests to see if a specified button is currently pressed. */ static int -DTestButton(ButtConfig *bc) +DTestButton(ButtConfig *bc, bool isFKB) { - if (bc->ButtType == BUTTC_KEYBOARD) { - if (g_keyState[SDL_GetScancodeFromKey(bc->ButtonNum)]) + bool ignoreKB = false; + bool fkbActv = g_fkbEnabled && (CurInputType[2] == SIFC_FKB); + + if (fkbActv) + { + ignoreKB = !isFKB; + } + if (!ignoreKB && g_keyState[SDL_GetScancodeFromKey(bc->ButtonNum)]) { bc->state = 1; return 1; @@ -1753,6 +1759,10 @@ void FCEUD_SetInput(bool fourscore, bool microphone, ESI port0, ESI port1, CurInputType[1] = port1; CurInputType[2] = fcexp; } + if (CurInputType[2] != SIFC_FKB) + { + g_fkbEnabled = false; + } replaceP2StartWithMicrophone = microphone; @@ -1880,7 +1890,7 @@ static void UpdateFKB(void) vkeyDown = getFamilyKeyboardVirtualKey(50); - leftShiftDown = DTestButton(&fkbmap[50]) || vkeyDown; + leftShiftDown = DTestButton(&fkbmap[50], true) || vkeyDown; for (x = 0; x < FAMILYKEYBOARD_NUM_BUTTONS; x++) { @@ -1896,7 +1906,7 @@ static void UpdateFKB(void) vkeyDown = getFamilyKeyboardVirtualKey(x); - if (DTestButton(&fkbmap[x]) || vkeyDown) + if (DTestButton(&fkbmap[x], true) || vkeyDown) { fkbkeys[x] = 1; diff --git a/src/drivers/Qt/sdl-video.cpp b/src/drivers/Qt/sdl-video.cpp index b3c46434..73bbd8bc 100644 --- a/src/drivers/Qt/sdl-video.cpp +++ b/src/drivers/Qt/sdl-video.cpp @@ -37,6 +37,7 @@ #include "Qt/sdl-video.h" #include "Qt/AviRecord.h" #include "Qt/fceuWrapper.h" +#include "Qt/ConsoleWindow.h" #ifdef CREATE_AVI #include "../videolog/nesvideos-piece.h" @@ -74,6 +75,7 @@ extern bool MaxSpeed; extern int input_display; extern int frame_display; extern int rerecord_display; +extern uint8 PALRAM[0x20]; /** * Attempts to destroy the graphical video display. Returns 0 on @@ -498,6 +500,19 @@ BlitScreen(uint8 *XBuf) { int i = nes_shm->pixBufIdx; + if (usePaletteForVideoBg) + { + unsigned char r, g, b; + FCEUD_GetPalette(0x80 | PALRAM[0], &r, &g, &b); + + if (consoleWindow) + { + QColor *bgColor = consoleWindow->getVideoBgColorPtr(); + + *bgColor = QColor::fromRgb(r,g,b); + } + } + doBlitScreen(XBuf, (uint8_t*)nes_shm->pixbuf[i]); nes_shm->pixBufIdx = (i+1) % NES_VIDEO_BUFLEN; diff --git a/src/drivers/win/taseditor/history.h b/src/drivers/win/taseditor/history.h index 59d83c5c..af4c14a6 100644 --- a/src/drivers/win/taseditor/history.h +++ b/src/drivers/win/taseditor/history.h @@ -108,7 +108,7 @@ public: int registerChanges(int mod_type, int start = 0, int end =-1, int size = 0, const char* comment = NULL, int consecutivenessTag = 0, RowsSelection* frameset = NULL); int registerAdjustLag(int start, int size); void registerMarkersChange(int modificationType, int start = 0, int end =-1, const char* comment = 0); - void registerBookmarkSet(int slot, BOOKMARK& backupÑopy, int oldCurrentBranch); + void registerBookmarkSet(int slot, BOOKMARK& backupcopy, int oldCurrentBranch); int registerBranching(int slot, bool markersWereChanged); void registerRecording(int frameOfChange, uint32 joypadDifferenceBits); int registerImport(MovieData& md, char* filename); diff --git a/src/drivers/win/window.cpp b/src/drivers/win/window.cpp index 82c845b5..3aae132b 100644 --- a/src/drivers/win/window.cpp +++ b/src/drivers/win/window.cpp @@ -1078,12 +1078,12 @@ void CloseGame() bool ALoad(const char *nameo, char* innerFilename, bool silent) { - FCEUFILE* patchTrial = FCEU_fopen(nameo,nullptr,"rb",nullptr,-1); + FILE* patchTrial = fopen(nameo, "rb"); if(patchTrial) { char sig[10] = {0}; - FCEU_fread(sig,1,5,patchTrial); - FCEU_fclose(patchTrial); + fread(sig,1,5,patchTrial); + fclose(patchTrial); if(!strcmp(sig,"PATCH")) { //assuming it's a patch: diff --git a/src/ines.cpp b/src/ines.cpp index 92fa0bdf..c150c3e1 100644 --- a/src/ines.cpp +++ b/src/ines.cpp @@ -729,7 +729,8 @@ BMAPPINGLocal bmap[] = { {"F-15 MMC3 Based", 259, BMCF15_Init}, {"HP10xx/H20xx Boards", 260, BMCHPxx_Init}, {"810544-CA-1", 261, BMC810544CA1_Init}, - {"SMD132/SMD133", 268, SMD132_SMD133_Init}, + {"AA6023/AA6023B", 268, AA6023_Init}, + {"COOLGIRL", 342, COOLGIRL_Init }, {"Impact Soft MMC3 Flash Board", 406, Mapper406_Init }, diff --git a/src/state.cpp b/src/state.cpp index 78279c4b..3a8fac80 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -88,7 +88,7 @@ EMUFILE_MEMORY memory_savestate; // temporary buffer for compressed data of a savestate std::vector compressed_buf; -#define SFMDATA_SIZE (64) +#define SFMDATA_SIZE (128) static SFORMAT SFMDATA[SFMDATA_SIZE]; static int SFEXINDEX; diff --git a/src/unif.cpp b/src/unif.cpp index 617b41cb..9cc59e20 100644 --- a/src/unif.cpp +++ b/src/unif.cpp @@ -475,6 +475,7 @@ static BMAPPING bmap[] = { { "FNS", FNS_Init, BMCFLAG_16KCHRR }, { "BS-400R", BS400R_Init, 0 }, { "BS-4040R", BS4040R_Init, 0 }, + { "COOLGIRL", COOLGIRL_Init, 0 }, { 0, 0, 0 } }; diff --git a/src/unif.h b/src/unif.h index b6759bdf..92fc5a36 100644 --- a/src/unif.h +++ b/src/unif.h @@ -162,7 +162,8 @@ void MINDKIDS_Init(CartInfo *info); void FNS_Init(CartInfo *info); void BS400R_Init(CartInfo *info); void BS4040R_Init(CartInfo *info); -void SMD132_SMD133_Init(CartInfo *info); +void AA6023_Init(CartInfo *info); +void COOLGIRL_Init(CartInfo* info); extern uint8 *UNIFchrrama; // Meh. So I can stop CHR RAM // bank switcherooing with certain boards... diff --git a/src/version.h b/src/version.h index dc2c1be9..01c9daae 100644 --- a/src/version.h +++ b/src/version.h @@ -62,14 +62,14 @@ #define FCEU_VERSION_MAJOR 2 #define FCEU_VERSION_MINOR 6 -#define FCEU_VERSION_PATCH 4 +#define FCEU_VERSION_PATCH 5 #define FCEU_VERSION_NUMERIC ( (FCEU_VERSION_MAJOR*10000) + (FCEU_VERSION_MINOR*100) + (FCEU_VERSION_PATCH) ) #define FCEU_VERSION_MAJOR_DECODE(x) ( (x / 10000) ) #define FCEU_VERSION_MINOR_DECODE(x) ( (x / 100) % 100 ) #define FCEU_VERSION_PATCH_DECODE(x) (x % 100) -#define FCEU_VERSION_STRING "2.6.4" FCEU_SUBVERSION_STRING FCEU_FEATURE_STRING FCEU_COMPILER +#define FCEU_VERSION_STRING "2.6.5" FCEU_SUBVERSION_STRING FCEU_FEATURE_STRING FCEU_COMPILER #define FCEU_NAME_AND_VERSION FCEU_NAME " " FCEU_VERSION_STRING #endif diff --git a/vc/vc14_fceux.vcxproj b/vc/vc14_fceux.vcxproj index 4611ee98..47b60a73 100644 --- a/vc/vc14_fceux.vcxproj +++ b/vc/vc14_fceux.vcxproj @@ -353,7 +353,7 @@ xcopy /y /d "$(ProjectDir)\..\src\drivers\win\7z_64.dll" "$(OutDir)" Speed true .;../src/drivers/win/zlib;../src/drivers/win/directx/x64;../src;../src/drivers/win/lua/include;userconfig;defaultconfig;%(AdditionalIncludeDirectories) - WIN32;WIN64;MSVC;_CRT_SECURE_NO_DEPRECATE;_WIN32_WINDOWS=0x0410;WINVER=0x0410;NETWORK;LSB_FIRST;FCEUDEF_DEBUGGER;_USE_SHARED_MEMORY_;NOMINMAX;HAS_vsnprintf;_S9XLUA_H;NDEBUG;MSVC;_CRT_SECURE_NO_DEPRECATE;_WIN32_WINDOWS=0x0410;WINVER=0x0410;NETWORK;LSB_FIRST;%(PreprocessorDefinitions) + PUBLIC_RELEASE;WIN32;WIN64;MSVC;_CRT_SECURE_NO_DEPRECATE;_WIN32_WINDOWS=0x0410;WINVER=0x0410;NETWORK;LSB_FIRST;FCEUDEF_DEBUGGER;_USE_SHARED_MEMORY_;NOMINMAX;HAS_vsnprintf;_S9XLUA_H;NDEBUG;MSVC;_CRT_SECURE_NO_DEPRECATE;_WIN32_WINDOWS=0x0410;WINVER=0x0410;NETWORK;LSB_FIRST;%(PreprocessorDefinitions) MultiThreaded @@ -473,6 +473,7 @@ xcopy /y /d "$(ProjectDir)\..\src\drivers\win\7z_64.dll" "$(OutDir)" + diff --git a/web/download.html b/web/download.html index 6491e2e6..da18903f 100644 --- a/web/download.html +++ b/web/download.html @@ -81,11 +81,11 @@

If you would like to test the current in-development version of FCEUX, interim builds are available here:

Source Code