mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-09 01:09:46 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04c608a21e | ||
|
|
8a75015d7e | ||
|
|
330a329abc | ||
|
|
a21f8c074c | ||
|
|
c899461cf1 | ||
|
|
512b53f808 | ||
|
|
2fb2da047c | ||
|
|
cdbd9e0d2c | ||
|
|
745e41a6ee | ||
|
|
ab40ecbb9b | ||
|
|
72ead4d2a6 | ||
|
|
aef110f35d | ||
|
|
e99c9c57b0 | ||
|
|
9c273abec3 | ||
|
|
6cc9f5a416 | ||
|
|
69c20711fc | ||
|
|
539aa9b7d6 | ||
|
|
5d7dbb7eed | ||
|
|
6176287392 | ||
|
|
ce2e6f8c19 | ||
|
|
76ab41c69c | ||
|
|
928d76b0b5 | ||
|
|
c1949d8eb5 | ||
|
|
6946437115 | ||
|
|
76a5f8e1da | ||
|
|
bf9aae53d0 | ||
|
|
1a5559a24b | ||
|
|
af5fa1a1c8 | ||
|
|
4c367fd32f | ||
|
|
2d714d1a9c | ||
|
|
8dbd2135b7 | ||
|
|
c62e2a254c | ||
|
|
dfcd7d4fce | ||
|
|
fa1c21ba3d | ||
|
|
a584782a86 | ||
|
|
6185edc43b | ||
|
|
7772fdbf0a | ||
|
|
fd8bcad5a1 | ||
|
|
da7a79b21e | ||
|
|
639ebe6285 | ||
|
|
9be2f9bb92 | ||
|
|
ce71fff00c | ||
|
|
a91b24acf1 | ||
|
|
c5d316274a | ||
|
|
8444f52843 | ||
|
|
d775c0968b | ||
|
|
c76b87cdb2 | ||
|
|
2197df8cca | ||
|
|
324a192109 | ||
|
|
4a9b77a5e1 | ||
|
|
a27c2a2b3a | ||
|
|
34124fd1cc | ||
|
|
f6b58b076b | ||
|
|
b4b026db28 | ||
|
|
05898a6a04 | ||
|
|
331c4956b0 | ||
|
|
d6c42e59e4 | ||
|
|
58661451ed | ||
|
|
e808d2c5ab | ||
|
|
445acba894 | ||
|
|
07fa6bc265 | ||
|
|
3e5ed40801 | ||
|
|
10b747c305 | ||
|
|
2ed2ed34bc | ||
|
|
41b61ee6d5 | ||
|
|
a960aeef52 | ||
|
|
1eb0a1659a | ||
|
|
c19a2ac1b2 | ||
|
|
8642d35e5f | ||
|
|
80db392140 | ||
|
|
a2400ad43a | ||
|
|
e8cd9b3e04 | ||
|
|
e04922c4d5 | ||
|
|
69795d578a | ||
|
|
8433666dc2 | ||
|
|
ecdce5e92b | ||
|
|
0b4fea8ce5 | ||
|
|
5bcef4741a | ||
|
|
3b18a3f808 | ||
|
|
7d327a2c8a | ||
|
|
07bafb6a51 | ||
|
|
1bb1669b03 | ||
|
|
a752b85d6f | ||
|
|
a02c0f80de | ||
|
|
50ceb088ef | ||
|
|
65618ce1e2 | ||
|
|
4691971416 | ||
|
|
98d9eb7786 | ||
|
|
431aba5a1a | ||
|
|
78e5332361 | ||
|
|
687bfe8efc | ||
|
|
7cc6d517a2 | ||
|
|
3bd385e4a8 | ||
|
|
de2d7d86c9 | ||
|
|
a0d8ce25c5 | ||
|
|
557ecb5319 | ||
|
|
8cc98f94ae | ||
|
|
7e5d749adc | ||
|
|
675d57b6fa | ||
|
|
28cfa89970 | ||
|
|
8668f98fa5 | ||
|
|
8f772cdb01 | ||
|
|
229e16df98 | ||
|
|
36290b9705 | ||
|
|
014d0130f5 | ||
|
|
d6465d0daa | ||
|
|
802a902cd3 | ||
|
|
2bf4e28db5 | ||
|
|
1469299363 |
No files matched your search
+11
@@ -0,0 +1,11 @@
|
||||
# yaml-language-server: $schema=https://raw.githubusercontent.com/BlankSpruce/gersemi/master/gersemi/configuration.schema.json
|
||||
|
||||
color: false
|
||||
definitions: [ares/CMakeLists.txt]
|
||||
line_length: 120
|
||||
indent: 2
|
||||
list_expansion: favour-inlining
|
||||
quiet: false
|
||||
unsafe: false
|
||||
warn_about_unknown_commands: false
|
||||
workers: 10
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env sh
|
||||
set -euo pipefail
|
||||
|
||||
# configure
|
||||
cmake --preset $TARGET_PRESET
|
||||
|
||||
# change into the build directory
|
||||
pushd build
|
||||
|
||||
# build; pipe output to render warnings neatly
|
||||
xcodebuild -configuration RelWithDebInfo \
|
||||
DEBUG_INFORMATION_FORMAT="dwarf-with-dsym" \
|
||||
2>&1 | xcbeautify --renderer github-actions
|
||||
|
||||
popd
|
||||
|
||||
# package debug symbols
|
||||
|
||||
# move dependency dSYMs to build output directory
|
||||
ditto .deps/ares-deps-macos-universal/lib/*.dSYM build/desktop-ui/RelWithDebInfo
|
||||
|
||||
mkdir build/dsyms
|
||||
|
||||
# ares.app.dSYM already exists alongside ares.app; package it with the rest
|
||||
mv build/desktop-ui/RelWithDebInfo/*.dSYM build/dSYMs/
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# configure
|
||||
cmake --preset $TARGET_PRESET
|
||||
|
||||
# change into the build directory
|
||||
pushd build
|
||||
|
||||
ninja
|
||||
|
||||
popd
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env sh
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$CROSS_COMPILE" = true ]; then
|
||||
cmake --preset $NATIVE_PRESET -B build_native
|
||||
pushd build_native
|
||||
# build sourcery natively so it may be invoked during cross-compilation
|
||||
cmake --build . --target sourcery --config RelWithDebInfo
|
||||
popd
|
||||
fi
|
||||
|
||||
cmake --preset $TARGET_PRESET
|
||||
pushd build
|
||||
cmake --build . --config RelWithDebInfo
|
||||
|
||||
if [ "$CROSS_COMPILE" = true ]; then
|
||||
cp ../.deps/ares-deps-windows-arm64/lib/*.pdb desktop-ui/rundir/
|
||||
else
|
||||
cp ../.deps/ares-deps-windows-x64/lib/*.pdb desktop-ui/rundir/
|
||||
fi
|
||||
|
||||
mkdir PDBs
|
||||
mv desktop-ui/rundir/*.pdb PDBs/
|
||||
popd
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
set -eu
|
||||
case ${GITHUB_REF} in
|
||||
refs/tags/*) suffix="-${GITHUB_REF#refs/tags/}" ;;
|
||||
refs/heads/master) suffix="-nightly" ;;
|
||||
*) suffix="" ;;
|
||||
esac
|
||||
|
||||
bindir="${GITHUB_WORKSPACE}/bin"
|
||||
|
||||
# Hack: Workaround for GitHub artifacts losing attributes.
|
||||
chmod +x ${bindir}/ares-macos-universal/ares.app/Contents/MacOS/ares
|
||||
|
||||
for package in macos-universal
|
||||
do
|
||||
mkdir "${package}"
|
||||
cd "${package}"
|
||||
|
||||
# Package ares.
|
||||
outdir=ares${suffix}
|
||||
mkdir ${outdir}
|
||||
mkdir ${outdir}-dSYMs
|
||||
cp -a ${bindir}/ares-${package}-dSYMs/*.dSYM ${outdir}-dSYMs
|
||||
cp -a ${bindir}/ares-${package}/*.app ${outdir}
|
||||
zip -r ../ares-${package}.zip ${outdir}
|
||||
zip -r ../ares-${package}-dSYMs.zip ${outdir}-dSYMs
|
||||
|
||||
cd -
|
||||
done
|
||||
|
||||
for package in windows-x64 windows-clang-cl-x64 windows-clang-cl-arm64
|
||||
do
|
||||
mkdir "${package}"
|
||||
cd "${package}"
|
||||
|
||||
# Package ares.
|
||||
outdir=ares${suffix}
|
||||
mkdir ${outdir}
|
||||
mkdir ${outdir}-PDBs
|
||||
cp -a ${bindir}/ares-${package}-PDBs/*.pdb ${outdir}-PDBs
|
||||
zip -r ../ares-${package}-PDBs.zip ${outdir}-PDBs
|
||||
rm -rf ${bindir}/ares-${package}/PDBs
|
||||
cp -a ${bindir}/ares-${package}/* ${outdir}
|
||||
zip -r ../ares-${package}.zip ${outdir}
|
||||
cd -
|
||||
done
|
||||
+13
-31
@@ -51,12 +51,8 @@ jobs:
|
||||
windres: rc
|
||||
shell: bash
|
||||
msvc-arch: x64_arm64
|
||||
- name: macos-compat
|
||||
os: macos-12
|
||||
compiler: clang++
|
||||
shell: sh
|
||||
- name: macos-latest
|
||||
os: macos-latest
|
||||
- name: macos-universal
|
||||
os: macos-15
|
||||
compiler: clang++
|
||||
shell: sh
|
||||
- name: ubuntu
|
||||
@@ -120,22 +116,14 @@ jobs:
|
||||
- name: Install macOS Dependencies
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
if [ $ImageOS == "macos14" ]; then
|
||||
sudo xcode-select -s /Applications/Xcode_15.2.app/Contents/Developer
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.13
|
||||
echo "Set Xcode version to 15.2 for macos-latest build."
|
||||
elif [ $ImageOS == "macos12" ]; then
|
||||
sudo xcode-select -s /Applications/Xcode_13.4.app/Contents/Developer
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.9
|
||||
echo "Set Xcode version to 13.4 for macos-compat build."
|
||||
fi
|
||||
brew install make ninja cmake
|
||||
sudo xcode-select -s /Applications/Xcode_16.1.0.app/Contents/Developer
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.13
|
||||
brew install ninja cmake
|
||||
pip install --no-input setuptools
|
||||
rustup toolchain install nightly
|
||||
rustup default nightly
|
||||
rustup target add x86_64-apple-darwin
|
||||
rustup target add aarch64-apple-darwin
|
||||
echo "MAKE=gmake" >> $GITHUB_ENV
|
||||
pushd thirdparty/SDL
|
||||
./build-SDL.sh
|
||||
popd
|
||||
@@ -171,9 +159,9 @@ jobs:
|
||||
certificatesha1: '${{ secrets.WIN_CERTHASH }}'
|
||||
certificatename: '${{ secrets.WIN_CERTNAME }}'
|
||||
folder: 'desktop-ui/out'
|
||||
timestampUrl: 'http://timestamp.digicert.com'
|
||||
timestampUrl: 'http://timestamp.digicert.com'
|
||||
- name: "macOS: notarize"
|
||||
if: runner.os == 'macOS' && github.event_name != 'pull_request'
|
||||
if: false
|
||||
continue-on-error: ${{ github.repository != 'ares-emulator/ares' }}
|
||||
run: |
|
||||
ditto -c -k --keepParent desktop-ui/out/ares.app /tmp/ares.zip
|
||||
@@ -191,7 +179,7 @@ jobs:
|
||||
|
||||
|
||||
release:
|
||||
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')
|
||||
if: false
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build
|
||||
@@ -216,10 +204,9 @@ jobs:
|
||||
bindir="${GITHUB_WORKSPACE}/bin"
|
||||
|
||||
# Hack: Workaround for GitHub artifacts losing attributes.
|
||||
chmod +x ${bindir}/ares-macos-latest/ares.app/Contents/MacOS/ares
|
||||
chmod +x ${bindir}/ares-macos-compat/ares.app/Contents/MacOS/ares
|
||||
chmod +x ${bindir}/ares-macos-universal/ares.app/Contents/MacOS/ares
|
||||
|
||||
for package in windows windows-msvc-arm64 macos-latest macos-compat
|
||||
for package in windows windows-msvc-arm64 macos-universal
|
||||
do
|
||||
mkdir "${package}"
|
||||
cd "${package}"
|
||||
@@ -318,17 +305,12 @@ jobs:
|
||||
- name: Upload ares-windows-msvc-arm64
|
||||
uses: actions/upload-release-asset@v1
|
||||
env: { GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' }
|
||||
with: { upload_url: '${{ env.UPLOAD_URL }}', asset_path: 'ares-windows-msvc-arm64.zip', asset_name: 'ares-windows-msvc-arm64.zip', asset_content_type: 'application/zip' }
|
||||
- name: Upload ares-macos-latest
|
||||
with: { upload_url: '${{ env.UPLOAD_URL }}', asset_path: 'ares-windows-msvc-arm64.zip', asset_name: 'ares-windows-msvc-arm64.zip', asset_content_type: 'application/zip' }
|
||||
- name: Upload ares-macos-universal
|
||||
uses: actions/upload-release-asset@v1
|
||||
env: { GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' }
|
||||
with: { upload_url: '${{ env.UPLOAD_URL }}', asset_path: 'ares-macos-latest.zip', asset_name: 'ares-macos-latest.zip', asset_content_type: 'application/zip' }
|
||||
- name: Upload ares-macos-compat
|
||||
uses: actions/upload-release-asset@v1
|
||||
env: { GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' }
|
||||
with: { upload_url: '${{ env.UPLOAD_URL }}', asset_path: 'ares-macos-compat.zip', asset_name: 'ares-macos-compat.zip', asset_content_type: 'application/zip' }
|
||||
with: { upload_url: '${{ env.UPLOAD_URL }}', asset_path: 'ares-macos-universal.zip', asset_name: 'ares-macos-universal.zip', asset_content_type: 'application/zip' }
|
||||
# - name: Upload ares-ubuntu
|
||||
# uses: actions/upload-release-asset@v1
|
||||
# env: { GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' }
|
||||
# with: { upload_url: '${{ env.UPLOAD_URL }}', asset_path: 'ares-ubuntu.zip', asset_name: 'ares-ubuntu.zip', asset_content_type: 'application/zip' }
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
name: Build (New)
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
codesign:
|
||||
description: Enable codesigning
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
program:
|
||||
- ares
|
||||
config:
|
||||
- RelWithDebInfo
|
||||
platform:
|
||||
- name: windows-x64
|
||||
os: windows-latest
|
||||
compiler: clang++
|
||||
shell: 'msys2 {0}'
|
||||
msystem: clang64
|
||||
install: mingw-w64-clang-x86_64-clang
|
||||
target-cmake-preset: windows-ci-mingw-native
|
||||
- name: windows-x64-gcc
|
||||
os: windows-latest
|
||||
compiler: g++
|
||||
shell: 'msys2 {0}'
|
||||
msystem: mingw64
|
||||
install: mingw-w64-x86_64-toolchain
|
||||
target-cmake-preset: windows-ci-mingw-native
|
||||
- name: windows-clang-cl-x64
|
||||
os: windows-latest
|
||||
windres: rc
|
||||
shell: bash
|
||||
target-cmake-preset: windows-ci-cl-native
|
||||
- name: windows-clang-cl-arm64
|
||||
os: windows-latest
|
||||
windres: rc
|
||||
shell: bash
|
||||
target-cmake-preset: windows-ci-cl-cross
|
||||
native-cmake-preset: windows-ci-cl-native
|
||||
- name: windows-msvc-x64
|
||||
os: windows-latest
|
||||
windres: rc
|
||||
shell: bash
|
||||
target-cmake-preset: windows-ci-msvc-native
|
||||
- name: windows-msvc-arm64
|
||||
os: windows-latest
|
||||
windres: rc
|
||||
shell: bash
|
||||
target-cmake-preset: windows-ci-msvc-cross
|
||||
native-cmake-preset: windows-ci-msvc-native
|
||||
- name: macos-universal
|
||||
os: macos-15
|
||||
compiler: clang++
|
||||
shell: sh
|
||||
install: cmake ccache xcbeautify
|
||||
target-cmake-preset: macos-ci-universal
|
||||
- name: ubuntu
|
||||
os: ubuntu-latest
|
||||
compiler: g++
|
||||
shell: bash
|
||||
install: cmake ccache ninja-build libsdl2-dev libgtk-3-dev libao-dev libopenal-dev
|
||||
target-cmake-preset: ubuntu-ci
|
||||
name: ${{ matrix.program }}-${{ matrix.platform.name }}
|
||||
runs-on: ${{ matrix.platform.os }}
|
||||
defaults:
|
||||
run:
|
||||
shell: ${{ matrix.platform.shell }}
|
||||
steps:
|
||||
- name: Install MSYS2 Dependencies
|
||||
if: matrix.platform.shell == 'msys2 {0}'
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{ matrix.platform.msystem }}
|
||||
install: make git cmake ccache ninja ${{ matrix.platform.install }}
|
||||
- name: "macOS: Import Certificate"
|
||||
if: runner.os == 'macOS' && inputs.codesign
|
||||
uses: apple-actions/import-codesign-certs@v3
|
||||
continue-on-error: ${{ !inputs.codesign }}
|
||||
with:
|
||||
p12-file-base64: ${{ secrets.MACOS_CERTIFICATE_DATA }}
|
||||
p12-password: ${{ secrets.MACOS_CERTIFICATE_PASSPHRASE }}
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-tags: true
|
||||
fetch-depth: 0
|
||||
- name: Install Linux Dependencies
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt-get update -y -qq
|
||||
sudo apt-get install ${{ matrix.platform.install }}
|
||||
- name: Install macOS Dependencies
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
brew install ${{ matrix.platform.install }}
|
||||
- name: "Build: Linux"
|
||||
if: runner.os == 'Linux'
|
||||
run: .github/scripts/build_ubuntu.sh
|
||||
env:
|
||||
TARGET_PRESET: ${{ matrix.platform.target-cmake-preset }}
|
||||
- name: "Build: Windows"
|
||||
if: runner.os != 'macOS' && runner.os != 'Linux'
|
||||
run: .github/scripts/build_windows.sh
|
||||
env:
|
||||
CROSS_COMPILE: ${{ matrix.platform.native-cmake-preset != '' }}
|
||||
NATIVE_PRESET: ${{ matrix.platform.native-cmake-preset }}
|
||||
TARGET_PRESET: ${{ matrix.platform.target-cmake-preset }}
|
||||
- name: "Windows: sign"
|
||||
if: runner.os == 'Windows' && inputs.codesign
|
||||
uses: DanaBear/code-sign-action@98c79121b376beab8d6a9484f445089db4461bca
|
||||
with:
|
||||
certificate: '${{ secrets.WIN_CERTIFICATE }}'
|
||||
password: '${{ secrets.WIN_PASSWORD }}'
|
||||
certificatesha1: '${{ secrets.WIN_CERTHASH }}'
|
||||
certificatename: '${{ secrets.WIN_CERTNAME }}'
|
||||
folder: 'build/desktop-ui/rundir/'
|
||||
timestampUrl: 'http://timestamp.digicert.com'
|
||||
- name: "Build: macOS"
|
||||
if: runner.os == 'macOS'
|
||||
run: .github/scripts/build_macos.sh
|
||||
env:
|
||||
TARGET_PRESET: ${{ matrix.platform.target-cmake-preset }}
|
||||
MACOS_CERTIFICATE_NAME: ${{ secrets.MACOS_CERTIFICATE_NAME }}
|
||||
MACOS_NOTARIZATION_TEAMID: ${{ secrets.MACOS_NOTARIZATION_TEAMID }}
|
||||
- name: Upload Build (Windows)
|
||||
if: runner.os != 'macOS' && runner.os != 'Linux'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.program }}-${{ matrix.platform.name }}
|
||||
path: build/desktop-ui/rundir/*
|
||||
- name: Upload Build (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.program }}-${{ matrix.platform.name }}
|
||||
path: build/desktop-ui/RelWithDebInfo/
|
||||
- name: Upload Debug Symbols (Windows)
|
||||
if: runner.os != 'macOS' && runner.os != 'Linux'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.program }}-${{ matrix.platform.name }}-PDBs
|
||||
path: build/PDBs/*
|
||||
- name: Upload Debug Symbols (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.program }}-${{ matrix.platform.name }}-dSYMs
|
||||
path: build/dSYMs/*
|
||||
@@ -0,0 +1,17 @@
|
||||
name: Pull Request
|
||||
run-name: ${{ github.event.pull_request.title }} PR run
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ '*' ]
|
||||
types: [ opened, synchronize, reopened ]
|
||||
permissions:
|
||||
contents: read
|
||||
concurrency:
|
||||
group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
build-project:
|
||||
name: Build
|
||||
uses: ./.github/workflows/build_new.yml
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -0,0 +1,24 @@
|
||||
name: Push
|
||||
on:
|
||||
push:
|
||||
branches: [ '*' ]
|
||||
tags: [ 'v*' ]
|
||||
concurrency:
|
||||
group: '${{ github.workflow }} @ ${{ github.head_ref || github.ref }}'
|
||||
cancel-in-progress: true
|
||||
permissions:
|
||||
contents: write
|
||||
jobs:
|
||||
build:
|
||||
name: Build ares
|
||||
uses: ./.github/workflows/build_new.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
codesign: true
|
||||
release:
|
||||
name: Release
|
||||
needs: build
|
||||
secrets: inherit
|
||||
uses: ./.github/workflows/release.yml
|
||||
with:
|
||||
notarize: true
|
||||
@@ -0,0 +1,68 @@
|
||||
name: Release
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
notarize:
|
||||
description: Notarize build (macOS only)
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
jobs:
|
||||
release:
|
||||
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
path: 'src'
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: 'bin'
|
||||
- name: "macOS: notarize"
|
||||
if: inputs.notarize
|
||||
run: |
|
||||
ditto -c -k --keepParent ${{ github.workspace }}/bin/ares-macos-universal/ares.app /tmp/ares.zip
|
||||
xcrun notarytool submit /tmp/ares.zip --apple-id "$MACOS_NOTARIZATION_USERNAME" --password "$MACOS_NOTARIZATION_PASSWORD" --team-id "$MACOS_NOTARIZATION_TEAMID" --wait
|
||||
xcrun stapler staple ${{ github.workspace }}/bin/ares-macos-universal/ares.app
|
||||
env:
|
||||
MACOS_NOTARIZATION_USERNAME: ${{ secrets.MACOS_NOTARIZATION_USERNAME }}
|
||||
MACOS_NOTARIZATION_PASSWORD: ${{ secrets.MACOS_NOTARIZATION_PASSWORD }}
|
||||
MACOS_NOTARIZATION_TEAMID: ${{ secrets.MACOS_NOTARIZATION_TEAMID }}
|
||||
- name: Package Artifacts
|
||||
run: src/.github/scripts/package_artifacts.sh
|
||||
- name: Check Release Tag ☑️
|
||||
id: check
|
||||
run: |
|
||||
now=$(date +'%Y-%m-%d')
|
||||
subject=$(git -C src log -1 --pretty=%s)
|
||||
if [[ ${GITHUB_REF_NAME} == master ]];
|
||||
then
|
||||
echo "tag=nightly" >> $GITHUB_OUTPUT
|
||||
versionName="nightly ${now}"
|
||||
echo "versionName=${versionName}" >> $GITHUB_OUTPUT
|
||||
echo "description=${subject}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "tag=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT
|
||||
echo "versionName=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT
|
||||
description="This is ares ${GITHUB_REF_NAME}, released on ${now}."
|
||||
echo "description=${description}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Create Release 🛫
|
||||
id: create_release
|
||||
uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191
|
||||
with:
|
||||
draft: ${{ github.ref != 'refs/heads/master' }}
|
||||
tag_name: ${{ steps.check.outputs.tag }}
|
||||
name: ares ${{ steps.check.outputs.versionName }}
|
||||
body: ${{ steps.check.outputs.description }}
|
||||
files: |
|
||||
${{ github.workspace }}/ares-macos-universal.zip
|
||||
${{ github.workspace }}/ares-macos-universal-dSYMs.zip
|
||||
${{ github.workspace }}/ares-windows-x64.zip
|
||||
${{ github.workspace }}/ares-windows-x64-PDBs.zip
|
||||
${{ github.workspace }}/ares-windows-clang-cl-x64.zip
|
||||
${{ github.workspace }}/ares-windows-clang-cl-x64-PDBs.zip
|
||||
${{ github.workspace }}/ares-windows-clang-cl-arm64.zip
|
||||
${{ github.workspace }}/ares-windows-clang-cl-arm64-PDBs.zip
|
||||
+40
-16
@@ -1,18 +1,42 @@
|
||||
*.moc
|
||||
*.user
|
||||
*.xcuserdata
|
||||
.vs/
|
||||
.vscode/
|
||||
.idea/
|
||||
cmake-*/
|
||||
obj/
|
||||
out/
|
||||
obj-amd64/
|
||||
obj-arm64/
|
||||
out-amd64/
|
||||
out-arm64/
|
||||
# Exclude everything
|
||||
/*
|
||||
|
||||
# Except for default project files
|
||||
!/.github
|
||||
!/ares
|
||||
!/desktop-ui
|
||||
!/hiro
|
||||
!/cmake
|
||||
!/libco
|
||||
!/mia
|
||||
!/nall
|
||||
!/ruby
|
||||
!/scripts
|
||||
!/tests
|
||||
!/thirdparty
|
||||
!/tools
|
||||
!/GNUMakefile
|
||||
!/LICENSE
|
||||
!/README.md
|
||||
!.editorconfig
|
||||
!.gitignore
|
||||
!.gitattributes
|
||||
!.gersemirc
|
||||
!deps.json
|
||||
!CMakeLists.txt
|
||||
!CMakePresets.json
|
||||
|
||||
thirdparty/SDL/SDL
|
||||
thirdparty/SDL/libSDL2-2.0.0.dylib
|
||||
macos-xcode/
|
||||
.swiftpm
|
||||
*.xcodeproj
|
||||
desktop-ui/out
|
||||
desktop-ui/obj
|
||||
ruby/video/metal/shaders.metallib
|
||||
ruby/video/metal/shaders.ir
|
||||
ares/ares/resource/resource.cpp
|
||||
ares/ares/resource/resource.hpp
|
||||
hiro/resource/resource.cpp
|
||||
hiro/resource/resource.hpp
|
||||
desktop-ui/resource/resource.cpp
|
||||
desktop-ui/resource/resource.hpp
|
||||
tests/arm7tdmi/tests
|
||||
tests/m68000/tests
|
||||
@@ -0,0 +1,55 @@
|
||||
cmake_minimum_required(VERSION 3.28...3.30)
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/bootstrap.cmake" NO_POLICY_SCOPE)
|
||||
|
||||
project(ares VERSION ${ARES_VERSION_CANONICAL})
|
||||
|
||||
include(compilerconfig)
|
||||
include(defaults)
|
||||
include(helpers)
|
||||
|
||||
add_subdirectory(cmake)
|
||||
|
||||
add_subdirectory(thirdparty)
|
||||
|
||||
add_subdirectory(nall)
|
||||
add_subdirectory(libco)
|
||||
add_subdirectory(ruby)
|
||||
add_subdirectory(hiro)
|
||||
|
||||
# gersemi: off
|
||||
set(
|
||||
ARES_CORES
|
||||
a26 fc sfc sg ms md ps1 pce ng msx cv myvision gb gba ws ngp spec n64
|
||||
CACHE STRING LIST
|
||||
)
|
||||
# gersemi: on
|
||||
|
||||
add_subdirectory(ares)
|
||||
add_subdirectory(mia)
|
||||
add_subdirectory(desktop-ui)
|
||||
|
||||
option(ARES_BUILD_OPTIONAL_TARGETS "Include supplemental tools and tests" OFF)
|
||||
|
||||
if(ARES_BUILD_OPTIONAL_TARGETS)
|
||||
add_subdirectory(tests/arm7tdmi)
|
||||
add_subdirectory(tests/i8080)
|
||||
add_subdirectory(tests/m68000)
|
||||
if(NOT OS_WINDOWS AND NOT OS_MACOS)
|
||||
add_subdirectory(tools/genius)
|
||||
else()
|
||||
target_disable_subproject(genius "genius (database editor)")
|
||||
endif()
|
||||
add_subdirectory(tools/mame2bml)
|
||||
else()
|
||||
target_disable_subproject(arm7tdmi "arm7tdmi processor test harness")
|
||||
target_disable_subproject(i8080 "i8080 processor test harness")
|
||||
target_disable_subproject(m68000 "m68000 processor test harness")
|
||||
target_disable_subproject(mame2bml "mame2bml (MAME manifest converter)")
|
||||
target_disable_subproject(genius "genius (database editor)")
|
||||
endif()
|
||||
|
||||
add_subdirectory(tools/sourcery)
|
||||
|
||||
message_configuration()
|
||||
return()
|
||||
@@ -0,0 +1,176 @@
|
||||
{
|
||||
"version": 3,
|
||||
"cmakeMinimumRequired": {
|
||||
"major": 3,
|
||||
"minor": 28,
|
||||
"patch": 0
|
||||
},
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "baseCacheVars",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"ARES_PROFILE_ACCURACY": false,
|
||||
"ARES_BUILD_LOCAL": true,
|
||||
"ENABLE_CCACHE": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "macos",
|
||||
"displayName": "macOS",
|
||||
"description": "Xcode project (host architecture by default)",
|
||||
"inherits": ["baseCacheVars"],
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Darwin"
|
||||
},
|
||||
"generator": "Xcode",
|
||||
"binaryDir": "${sourceDir}/build_macos",
|
||||
"cacheVariables": {
|
||||
"ARES_CODESIGN_IDENTITY": {"type": "STRING", "value": "$penv{CODESIGN_IDENT}"},
|
||||
"ARES_CODESIGN_TEAM": {"type": "STRING", "value": "$penv{CODESIGN_TEAM}"},
|
||||
"ARES_BUILD_OPTIONAL_TARGETS": true,
|
||||
"ENABLE_CCACHE": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows",
|
||||
"displayName": "Windows (Visual Studio, ClangCL)",
|
||||
"description": "Visual Studio project using ClangCL (host architecture)",
|
||||
"inherits": ["baseCacheVars"],
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
},
|
||||
"binaryDir": "${sourceDir}/build_vsclang",
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"toolset": "ClangCL",
|
||||
"cacheVariables": {
|
||||
"ENABLE_CCACHE": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc",
|
||||
"displayName": "Windows (Visual Studio, MSVC)",
|
||||
"description": "Visual Studio project using MSVC (host architecture)",
|
||||
"inherits": ["baseCacheVars"],
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
},
|
||||
"binaryDir": "${sourceDir}/build_msvc",
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"cacheVariables": {
|
||||
"ENABLE_CCACHE": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "macos-ci-universal",
|
||||
"displayName": "macOS for CI",
|
||||
"description": "Universal binary for building on Github Actions",
|
||||
"inherits": ["baseCacheVars"],
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Darwin"
|
||||
},
|
||||
"generator": "Xcode",
|
||||
"binaryDir": "${sourceDir}/build",
|
||||
"cacheVariables": {
|
||||
"CMAKE_OSX_ARCHITECTURES": {"type": "STRING", "value": "x86_64;arm64"},
|
||||
"CMAKE_OSX_DEPLOYMENT_TARGET": {"type": "STRING", "value": "10.13"},
|
||||
"ARES_CODESIGN_IDENTITY": {"type": "STRING", "value": "$penv{MACOS_CERTIFICATE_NAME}"},
|
||||
"ARES_CODESIGN_TEAM": {"type": "STRING", "value": "$penv{MACOS_NOTARIZATION_TEAMID}"},
|
||||
"ENABLE_CCACHE": true,
|
||||
"ARES_BUILD_LOCAL": false,
|
||||
"ARES_UNITY_CORES": true,
|
||||
"ARES_BUILD_OPTIONAL_TARGETS": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows-ci-base",
|
||||
"hidden": true,
|
||||
"inherits": ["baseCacheVars"],
|
||||
"binaryDir": "${sourceDir}/build",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": {"type": "STRING", "value": "RelWithDebInfo"},
|
||||
"ENABLE_CCACHE": true,
|
||||
"ARES_BUILD_LOCAL": false,
|
||||
"ARES_BUILD_OPTIONAL_TARGETS": true,
|
||||
"ARES_UNITY_CORES": true,
|
||||
"ARES_PRECOMPILE_HEADERS": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows-ci-mingw-native",
|
||||
"displayName": "Windows for CI (x64 MSYS2/MinGW Clang)",
|
||||
"description": "Single-arch binary for building on Github Actions",
|
||||
"inherits": ["windows-ci-base"],
|
||||
"generator": "Ninja"
|
||||
},
|
||||
{
|
||||
"name": "windows-ci-cl-native",
|
||||
"displayName": "Windows for CI (x64 ClangCL)",
|
||||
"description": "Single-arch binary for building on Github Actions",
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"inherits": ["windows-ci-base"],
|
||||
"toolset": "ClangCL"
|
||||
},
|
||||
{
|
||||
"name": "windows-ci-cl-cross",
|
||||
"displayName": "Windows for CI (arm64 ClangCL)",
|
||||
"description": "Single-arch binary for building on Github Actions",
|
||||
"inherits": ["windows-ci-base"],
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"architecture": "arm64",
|
||||
"toolset": "ClangCL",
|
||||
"cacheVariables": {
|
||||
"ARES_CROSSCOMPILING": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows-ci-msvc-native",
|
||||
"displayName": "Windows for CI (x64 MSVC)",
|
||||
"description": "Single-arch binary for building on Github Actions",
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"inherits": ["windows-ci-base"]
|
||||
},
|
||||
{
|
||||
"name": "windows-ci-msvc-cross",
|
||||
"displayName": "Windows for CI (arm64 MSVC)",
|
||||
"description": "Single-arch binary for building on Github Actions",
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"architecture": "arm64",
|
||||
"inherits": ["windows-ci-base"],
|
||||
"cacheVariables": {
|
||||
"ARES_CROSSCOMPILING": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ubuntu-ci",
|
||||
"displayName": "Ubuntu CI (x64 gcc)",
|
||||
"description": "Single-arch binary for building on Github Actions",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/build",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": {"type": "STRING", "value": "RelWithDebInfo"},
|
||||
"ENABLE_CCACHE": true,
|
||||
"ARES_BUILD_LOCAL": false,
|
||||
"ARES_BUILD_OPTIONAL_TARGETS": true,
|
||||
"ARES_PRECOMPILE_HEADERS": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "windows",
|
||||
"configurePreset": "windows",
|
||||
"displayName": "Windows build (host architecture)",
|
||||
"description": "Windows build (host architecture)",
|
||||
"configuration": "RelWithDebInfo"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
----------------------------------------------------------------------
|
||||
ares
|
||||
|
||||
Copyright (c) 2004-2021 ares team, Near et al
|
||||
Copyright (c) 2004-2024 ares team, Near et al
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||
<img src="https://github.com/ares-emulator/ares/blob/master/ares/ares/resource/logo@2x.png" width="350"/>
|
||||
|
||||
[](https://github.com/higan-emu/ares/blob/master/LICENSE)
|
||||
|
||||
@@ -17,127 +17,17 @@ Nightly Builds
|
||||
Automated, untested builds of ares are available for Windows and macOS as a [pre-release](https://github.com/higan-emu/ares/releases/tag/nightly).
|
||||
Only the latest nightly build is kept.
|
||||
|
||||
Prerequisites
|
||||
Building ares
|
||||
-------------
|
||||
|
||||
### *nix building
|
||||
Build instructions are available on the ares wiki. See build instructions for:
|
||||
|
||||
###### Minimum required packages:
|
||||
```
|
||||
g++ make pkg-config libgtk-3-dev libcanberra-gtk-module libgl-dev libasound2-dev
|
||||
```
|
||||
###### Additional Audio Drivers
|
||||
ares supports additional audio drivers besides the ALSA drivers included above. Installing these additional packages will allow them to be selected in Settings > Drivers:
|
||||
`libao-dev libopenal-dev`
|
||||
|
||||
###### GTK2 & GTK3
|
||||
By default, GTK3 is used, but support for GTK2 is available. You will need to install the additional package `libgtk2.0-dev` as well as specifying the command line option `hiro=gtk2` at compile time.
|
||||
|
||||
###### SDL2 for input
|
||||
If you would like to use SDL for input (e.g. for using a controller), you will need to install the `libsdl2-dev` and `libsdl2-2.0-0` packages and perform a clean build of ares.
|
||||
You should then be able to select SDL for input in the Settings > Drivers menu.
|
||||
|
||||
##### Building with clang
|
||||
|
||||
clang++ is now the preferred compiler for ares. If clang is detected on Windows/macOS/BSD, it will be selected by default. On Linux and other platforms, g++ remains the default if present. To build with clang, it is necessary to install both the `clang` and `lld` packages. If you would like to manually specify a compiler, you can use the following option: `compiler=[g++|clang++]`
|
||||
|
||||
##### Librashader Support
|
||||
If you do not want to include librashader support, you can pass the following option to the `make` command to skip these requirements using: `librashader=false`
|
||||
|
||||
Librashader requires rust in order to build. You can install it with the following command:
|
||||
```
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
source ~/.cargo/env
|
||||
rustup toolchain install nightly
|
||||
```
|
||||
|
||||
In order to build librashader, change into the `ares/thirdparty/librashader` directory and run the script `build-librashader.sh`
|
||||
Note that once the build completes, it will instruct you to run three copy commands to install the library on your system. These paths may be different depending on Linux distribution.
|
||||
|
||||
--------------
|
||||
|
||||
### Windows building
|
||||
|
||||
To build on Windows, using MSYS2 is recommended which can be download [here](https://www.msys2.org/). Follow the instructions
|
||||
on this page to install and setup an appropriate MINGW64 environment. Running the command:
|
||||
```
|
||||
pacman -S --needed base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-SDL2
|
||||
```
|
||||
from the MSYS2 MSYS terminal should setup everything you need to compile ares. Note that in order to compile, you will want to be in a MINGW64 terminal window after install and setup is complete.
|
||||
|
||||
##### Building with clang
|
||||
|
||||
clang is available through Visual Studio (or Build Tools for Visual Studio) through its installer and can be used to build ares. You will still need to supply GNU make in this instance. MSYS2 also offers a clang environment. You will want to make sure you select the clangw64 option during installation of MSYS2 which should provide and additional CLANG64 pre-configured environment. Install the clang toolchain package from the MSYS2 terminal:
|
||||
```
|
||||
pacman -S mingw-w64-clang-x86_64-toolchain mingw-w64-clang-x86_64-SDL2
|
||||
```
|
||||
Once complete, open a CLANG64 terminal window and proceed with building ares.
|
||||
|
||||
###### Debug Symbols
|
||||
When building with clang, by default symbols will be generated for debug builds using an MSVC compatible format (CodeView) for use with Windows debugging tools. In order to generate GDB compatible symbols, specify the following option: `symformat=gdb`
|
||||
|
||||
###### Librashader Support
|
||||
ares has introduced support for the [librashader](https://github.com/SnowflakePowered/librashader) library, which is a preprocessor, compiler, and runtime for RetroArch 'slang' shaders. If you are not interested in working with librashaders, you can pass the following option to the `make` command to skip these requirements using: `librashader=false`
|
||||
|
||||
Building the librashader library on Windows requires Rust. Follow these steps to prepare your system and build the librashader library:
|
||||
|
||||
1. Download and execute the rustup-init.exe installer from: https://rustup.rs/
|
||||
2. Open a new Windows terminal and run the following commands:
|
||||
```
|
||||
rustup toolchain install nightly
|
||||
rustup default nightly
|
||||
```
|
||||
3. With Rust successfully installed, you will need to update your PATH environment variable within your MSYS2 environment. In an MSYS2 shell, open $HOME/.bash_profile and append something like the following (note you will need to replace both instances of %USER_NAME% with the real user name you used to install Rust):
|
||||
```
|
||||
if [ -d "/C/Users/%USER_NAME%/.cargo/bin" ] ; then
|
||||
PATH="/C/Users/%USER_NAME%/.cargo/bin:${PATH}"
|
||||
fi
|
||||
```
|
||||
4. Open a new MSYS2 based shell for the compiler you are using (MinGW64|CLANG64|UCRT64), execute `rustup` to validate the path is set properly within your MSYS2 environment.
|
||||
5. In your MSYS2 compiler shell, `cd` into the `ares/thirdparty/librashader` directory
|
||||
6. Execute the build script, build should complete successfully:
|
||||
```
|
||||
./build-librashader.sh
|
||||
```
|
||||
|
||||
This only needs to be done once, or anytime the librashader library is updated. Once built, ares will build with librashader support by default requiring no additional flags.
|
||||
|
||||
Compilation
|
||||
-----------
|
||||
|
||||
Check out the source code by running this command:
|
||||
|
||||
```
|
||||
git clone https://github.com/ares-emulator/ares.git
|
||||
```
|
||||
|
||||
From the root of the project directory run:
|
||||
|
||||
```
|
||||
make -j4
|
||||
```
|
||||
|
||||
`-j#` indicates number of parallel build processes, and shouldn't be set higher than N-1 cores on your processor. Specifying this option can significantly decrease the time to build this project. There are multiple build types available and it defaults to an 'optimized' build. Build types can be specified using: `build=[debug|stable|release|minified|optimized]`
|
||||
|
||||
Build options can be found in the following two make files: nall/GNUmakefile desktop-ui/GNUmakefile
|
||||
|
||||
To start compilation from the beginning, run the following prior to compiling:
|
||||
|
||||
```
|
||||
make clean
|
||||
```
|
||||
|
||||
#### Building specific cores
|
||||
If you would like to build a subset of cores, you can specify the `cores="core1 core2"` option. Currently available cores:
|
||||
```
|
||||
a26 fc sfc n64 sg ms md ps1 pce ng msx cv myvision gb gba ws ngp spec
|
||||
```
|
||||
|
||||
Build Output
|
||||
------------
|
||||
|
||||
There is a single binary produced at the end of compilation which can be found in `desktop-ui/out`. On OS's besides Linux, the `Database` & `Shader` directories are copied over here as well. On Linux, running `make install` after compilation will copy these directories and binary into suitable locations (see desktop-ui/GNUmakefile for details). Alternatively, these directories can be copied from `thirdparty/slang-shaders/*` into a `Shader` directory, and by copying `mia/Database/*`
|
||||
* [Windows](https://github.com/ares-emulator/ares/wiki/Build-Instructions-For-Windows)
|
||||
* [macOS](https://github.com/ares-emulator/ares/wiki/Build-Instructions-For-macOS)
|
||||
* [Linux](https://github.com/ares-emulator/ares/wiki/Build-Instructions-For-Linux)
|
||||
* [BSD](https://github.com/ares-emulator/ares/wiki/Build-Instructions-For-BSD)
|
||||
|
||||
For legacy Makefile build instructions, see [here](https://github.com/ares-emulator/ares/wiki/Legacy-Build-Instructions).
|
||||
|
||||
Command-line options
|
||||
--------------------
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
add_library(ares STATIC ares/ares.cpp ares/memory/fixed-allocator.cpp)
|
||||
|
||||
add_library(ares::ares ALIAS ares)
|
||||
|
||||
option(ARES_PROFILE_ACCURACY "Build to prefer emulation accuracy over performance in certain specific areas" OFF)
|
||||
option(ARES_PRECOMPILE_HEADERS "Precompile ares/ares.hpp header for performance" OFF)
|
||||
option(
|
||||
ARES_UNITY_CORES
|
||||
"\
|
||||
Build each core as one translation unit. Decreases cold build times but increases incremental build times. Slightly \
|
||||
increases performance.
|
||||
"
|
||||
OFF
|
||||
)
|
||||
|
||||
add_sourcery_command(ares ares/resource)
|
||||
|
||||
if(ARES_PRECOMPILE_HEADERS)
|
||||
target_precompile_headers(ares PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:ares/ares.hpp>")
|
||||
endif()
|
||||
|
||||
target_link_libraries(ares PUBLIC libco sljit nall)
|
||||
|
||||
if(ARES_PROFILE_ACCURACY)
|
||||
target_compile_definitions(ares PUBLIC PROFILE_ACCURACY)
|
||||
else()
|
||||
target_compile_definitions(ares PUBLIC PROFILE_PERFORMANCE)
|
||||
endif()
|
||||
|
||||
target_sources(
|
||||
ares
|
||||
PRIVATE ares/ares.cpp ares/ares.hpp ares/inline.hpp ares/platform.hpp ares/random.hpp ares/types.hpp
|
||||
)
|
||||
|
||||
target_sources(
|
||||
ares
|
||||
PRIVATE
|
||||
ares/debug/debug.cpp
|
||||
ares/debug/debug.hpp
|
||||
ares/memory/fixed-allocator.cpp
|
||||
ares/memory/fixed-allocator.hpp
|
||||
ares/memory/memory.hpp
|
||||
ares/memory/readable.hpp
|
||||
ares/memory/writable.hpp
|
||||
)
|
||||
|
||||
target_sources(
|
||||
ares
|
||||
PRIVATE ares/scheduler/scheduler.cpp ares/scheduler/scheduler.hpp ares/scheduler/thread.cpp ares/scheduler/thread.hpp
|
||||
)
|
||||
|
||||
target_sources(
|
||||
ares
|
||||
PRIVATE
|
||||
ares/node/attribute.hpp
|
||||
ares/node/class.hpp
|
||||
ares/node/node.cpp
|
||||
ares/node/node.hpp
|
||||
ares/node/object.hpp
|
||||
ares/node/peripheral.hpp
|
||||
ares/node/port.hpp
|
||||
ares/node/system.hpp
|
||||
)
|
||||
|
||||
target_sources(ares PRIVATE ares/node/audio/audio.hpp ares/node/audio/stream.cpp ares/node/audio/stream.hpp)
|
||||
|
||||
target_sources(ares PRIVATE ares/node/component/component.hpp ares/node/component/real-time-clock.hpp)
|
||||
|
||||
target_sources(
|
||||
ares
|
||||
PRIVATE
|
||||
ares/node/input/axis.hpp
|
||||
ares/node/input/button.hpp
|
||||
ares/node/input/input.hpp
|
||||
ares/node/input/rumble.hpp
|
||||
ares/node/input/trigger.hpp
|
||||
)
|
||||
|
||||
target_sources(
|
||||
ares
|
||||
PRIVATE
|
||||
ares/node/setting/boolean.hpp
|
||||
ares/node/setting/integer.hpp
|
||||
ares/node/setting/natural.hpp
|
||||
ares/node/setting/real.hpp
|
||||
ares/node/setting/setting.hpp
|
||||
ares/node/setting/string.hpp
|
||||
)
|
||||
|
||||
target_sources(
|
||||
ares
|
||||
PRIVATE
|
||||
ares/node/video/screen.cpp
|
||||
ares/node/video/screen.hpp
|
||||
ares/node/video/sprite.cpp
|
||||
ares/node/video/sprite.hpp
|
||||
ares/node/video/video.hpp
|
||||
)
|
||||
|
||||
if(OS_WINDOWS)
|
||||
include(cmake/os-windows.cmake)
|
||||
elseif(OS_MACOS)
|
||||
include(cmake/os-macos.cmake)
|
||||
elseif(OS_LINUX OR OS_FREEBSD OR OS_OPENBSD)
|
||||
include(cmake/os-linux.cmake)
|
||||
endif()
|
||||
|
||||
macro(ares_components)
|
||||
list(APPEND ares.components ${ARGV})
|
||||
set(ares.components ${ares.components} PARENT_SCOPE)
|
||||
endmacro()
|
||||
|
||||
macro(ares_add_sources)
|
||||
set(options "")
|
||||
set(oneValueArgs CORE)
|
||||
set(multiValueArgs PRIMARY INCLUDED UNITY)
|
||||
cmake_parse_arguments(AAS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
target_sources(ares PRIVATE ${AAS_INCLUDED} ${AAS_PRIMARY})
|
||||
|
||||
if(ARES_UNITY_CORES)
|
||||
list(APPEND AAS_INCLUDED ${AAS_PRIMARY})
|
||||
target_sources(ares PRIVATE ${AAS_UNITY})
|
||||
endif()
|
||||
|
||||
foreach(arg IN ITEMS ${AAS_INCLUDED})
|
||||
list(APPEND ARES_HEADER_SOURCES "${AAS_CORE}/${arg}")
|
||||
endforeach()
|
||||
set(ARES_HEADER_SOURCES ${ARES_HEADER_SOURCES} PARENT_SCOPE)
|
||||
endmacro()
|
||||
|
||||
list(TRANSFORM ARES_CORES STRIP)
|
||||
|
||||
if(a26 IN_LIST ARES_CORES)
|
||||
enable_core("Atari 2600")
|
||||
add_subdirectory(a26)
|
||||
else()
|
||||
disable_core("Atari 2600")
|
||||
endif()
|
||||
|
||||
if(fc IN_LIST ARES_CORES)
|
||||
enable_core("NES / Famicom")
|
||||
add_subdirectory(fc)
|
||||
else()
|
||||
disable_core("NES / Famicom")
|
||||
endif()
|
||||
|
||||
if(sfc IN_LIST ARES_CORES)
|
||||
enable_core("SNES / Super Famicom")
|
||||
add_subdirectory(sfc)
|
||||
else()
|
||||
disable_core("SNES / Super Famicom")
|
||||
endif()
|
||||
|
||||
if(n64 IN_LIST ARES_CORES)
|
||||
enable_core("Nintendo 64")
|
||||
add_subdirectory(n64)
|
||||
else()
|
||||
disable_core("Nintendo 64")
|
||||
endif()
|
||||
|
||||
if(sg IN_LIST ARES_CORES)
|
||||
enable_core("Sega SG-1000")
|
||||
add_subdirectory(sg)
|
||||
else()
|
||||
disable_core("Sega SG-1000")
|
||||
endif()
|
||||
|
||||
if(ms IN_LIST ARES_CORES)
|
||||
enable_core("Sega Master System / Mark III")
|
||||
add_subdirectory(ms)
|
||||
else()
|
||||
disable_core("Sega Master System / Mark III")
|
||||
endif()
|
||||
|
||||
if(md IN_LIST ARES_CORES)
|
||||
enable_core("Sega Mega Drive / Genesis")
|
||||
add_subdirectory(md)
|
||||
else()
|
||||
disable_core("Sega Mega Drive / Genesis")
|
||||
endif()
|
||||
|
||||
if(saturn IN_LIST ARES_CORES)
|
||||
enable_core("Sega Saturn")
|
||||
add_subdirectory(saturn)
|
||||
else()
|
||||
disable_core("Sega Saturn")
|
||||
endif()
|
||||
|
||||
if(ps1 IN_LIST ARES_CORES)
|
||||
enable_core("PlayStation")
|
||||
add_subdirectory(ps1)
|
||||
else()
|
||||
disable_core("Playstation")
|
||||
endif()
|
||||
|
||||
if(pce IN_LIST ARES_CORES)
|
||||
enable_core("PC-Engine / TurboGrafx")
|
||||
add_subdirectory(pce)
|
||||
else()
|
||||
disable_core("PC-Engine / TurboGrafx")
|
||||
endif()
|
||||
|
||||
if(msx IN_LIST ARES_CORES)
|
||||
enable_core("Microsoft MSX")
|
||||
add_subdirectory(msx)
|
||||
else()
|
||||
disable_core("Microsoft MSX")
|
||||
endif()
|
||||
|
||||
if(cv IN_LIST ARES_CORES)
|
||||
enable_core("ColecoVision")
|
||||
add_subdirectory(cv)
|
||||
else()
|
||||
disable_core("ColecoVision")
|
||||
endif()
|
||||
|
||||
if(myvision IN_LIST ARES_CORES)
|
||||
enable_core("My Vision")
|
||||
add_subdirectory(myvision)
|
||||
else()
|
||||
disable_core("My Vision")
|
||||
endif()
|
||||
|
||||
if(gb IN_LIST ARES_CORES)
|
||||
enable_core("Game Boy / Game Boy Color")
|
||||
add_subdirectory(gb)
|
||||
else()
|
||||
disable_core("Game Boy / Game Boy Color")
|
||||
endif()
|
||||
|
||||
if(gba IN_LIST ARES_CORES)
|
||||
enable_core("Game Boy Advance")
|
||||
add_subdirectory(gba)
|
||||
else()
|
||||
disable_core("Game Boy Advance")
|
||||
endif()
|
||||
|
||||
if(ws IN_LIST ARES_CORES)
|
||||
enable_core("WonderSwan / WonderSwan Color")
|
||||
add_subdirectory(ws)
|
||||
else()
|
||||
disable_core("WonderSwan / WonderSwan Color")
|
||||
endif()
|
||||
|
||||
if(ng IN_LIST ARES_CORES)
|
||||
enable_core("Neo Geo (AES/MVS)")
|
||||
add_subdirectory(ng)
|
||||
else()
|
||||
disable_core("Neo Geo (AES/MVS)")
|
||||
endif()
|
||||
|
||||
if(ngp IN_LIST ARES_CORES)
|
||||
enable_core("Neo Geo Pocket / Neo Geo Pocket Color")
|
||||
add_subdirectory(ngp)
|
||||
else()
|
||||
disable_core("Neo Geo Pocket / Neo Geo Pocket Color")
|
||||
endif()
|
||||
|
||||
if(spec IN_LIST ARES_CORES)
|
||||
enable_core("ZX Spectrum")
|
||||
add_subdirectory(spec)
|
||||
else()
|
||||
disable_core("ZX Spectrum")
|
||||
endif()
|
||||
|
||||
add_subdirectory(component)
|
||||
|
||||
foreach(c IN LISTS ARES_CORES)
|
||||
# target_enable_feature(ares "${c} core" CORE_$<UPPER_CASE:${c}>)
|
||||
target_compile_definitions(ares PUBLIC CORE_$<UPPER_CASE:${c}>)
|
||||
endforeach()
|
||||
|
||||
get_target_property(ares_SOURCES ares SOURCES)
|
||||
|
||||
set_source_files_properties(
|
||||
ares
|
||||
ares/debug/debug.cpp
|
||||
ares/node/audio/stream.cpp
|
||||
ares/node/node.cpp
|
||||
ares/node/video/screen.cpp
|
||||
ares/node/video/sprite.cpp
|
||||
ares/scheduler/thread.cpp
|
||||
ares/scheduler/scheduler.cpp
|
||||
PROPERTIES HEADER_FILE_ONLY TRUE
|
||||
)
|
||||
|
||||
set_source_files_properties(
|
||||
n64/vulkan/parallel-rdp/vulkan/shader.cpp
|
||||
n64/vulkan/parallel-rdp/vulkan/descriptor_set.cpp
|
||||
n64/vulkan/parallel-rdp/vulkan/device.cpp
|
||||
n64/vulkan/parallel-rdp/vulkan/command_buffer.cpp
|
||||
n64/vulkan/parallel-rdp/vulkan/render_pass.cpp
|
||||
PROPERTIES SKIP_PRECOMPILE_HEADERS ON
|
||||
)
|
||||
|
||||
target_sources(ares PRIVATE cmake/os-macos.cmake cmake/os-windows.cmake cmake/os-linux.cmake)
|
||||
|
||||
set_source_files_properties(ares ${ARES_HEADER_SOURCES} PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||
|
||||
set_source_files_properties(ares ares/ares.cpp ares/memory/fixed-allocator.cpp PROPERTIES HEADER_FILE_ONLY FALSE)
|
||||
|
||||
target_include_directories(ares INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${ares_SOURCES})
|
||||
@@ -0,0 +1,98 @@
|
||||
ares_components(mos6502)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
a26
|
||||
UNITY #
|
||||
a26.cpp
|
||||
PRIMARY #
|
||||
system/system.cpp
|
||||
controller/controller.cpp
|
||||
cartridge/cartridge.cpp
|
||||
cpu/cpu.cpp
|
||||
tia/tia.cpp
|
||||
riot/riot.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
a26
|
||||
INCLUDED #
|
||||
a26.hpp
|
||||
CMakeLists.txt
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
a26
|
||||
INCLUDED #
|
||||
cartridge/cartridge.hpp
|
||||
cartridge/serialization.cpp
|
||||
cartridge/slot.cpp
|
||||
cartridge/slot.hpp
|
||||
cartridge/board/atari16k.cpp
|
||||
cartridge/board/atari32k.cpp
|
||||
cartridge/board/atari8k.cpp
|
||||
cartridge/board/board.cpp
|
||||
cartridge/board/board.hpp
|
||||
cartridge/board/commavid.cpp
|
||||
cartridge/board/linear.cpp
|
||||
cartridge/board/parker-bros.cpp
|
||||
cartridge/board/tigervision.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
a26
|
||||
INCLUDED #
|
||||
controller/controller.hpp
|
||||
controller/port.cpp
|
||||
controller/port.hpp
|
||||
controller/gamepad/gamepad.cpp
|
||||
controller/gamepad/gamepad.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
a26
|
||||
INCLUDED #
|
||||
cpu/cpu.hpp
|
||||
cpu/debugger.cpp
|
||||
cpu/memory.cpp
|
||||
cpu/serialization.cpp
|
||||
cpu/timing.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
a26
|
||||
INCLUDED #
|
||||
riot/io.cpp
|
||||
riot/riot.hpp
|
||||
riot/serialization.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
a26
|
||||
INCLUDED #
|
||||
system/controls.cpp
|
||||
system/serialization.cpp
|
||||
system/system.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
a26
|
||||
INCLUDED #
|
||||
tia/audio.cpp
|
||||
tia/ball.cpp
|
||||
tia/color.cpp
|
||||
tia/io.cpp
|
||||
tia/missile.cpp
|
||||
tia/player.cpp
|
||||
tia/playfield.cpp
|
||||
tia/serialization.cpp
|
||||
tia/tia.hpp
|
||||
tia/write-queue.cpp
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
#include <a26/cartridge/cartridge.cpp>
|
||||
#include <a26/controller/controller.cpp>
|
||||
#include <a26/cpu/cpu.cpp>
|
||||
#include <a26/riot/riot.cpp>
|
||||
#include <a26/system/system.cpp>
|
||||
#include <a26/tia/tia.cpp>
|
||||
+1
-1
@@ -10,7 +10,7 @@ Platform* platform = nullptr;
|
||||
bool _runAhead = false;
|
||||
|
||||
const string Name = "ares";
|
||||
const string Version = "141";
|
||||
const string Version = "142";
|
||||
const string Copyright = "ares team, Near";
|
||||
const string License = "ISC";
|
||||
const string LicenseURI = "https://opensource.org/licenses/ISC";
|
||||
|
||||
+1
-1
@@ -15,6 +15,7 @@
|
||||
#include <nall/endian.hpp>
|
||||
#include <nall/hashset.hpp>
|
||||
#include <nall/image.hpp>
|
||||
#include <nall/instruction-set.hpp>
|
||||
#include <nall/literals.hpp>
|
||||
#include <nall/priority-queue.hpp>
|
||||
#include <nall/queue.hpp>
|
||||
@@ -78,4 +79,3 @@ namespace ares {
|
||||
#include <ares/memory/fixed-allocator.hpp>
|
||||
#include <ares/memory/readable.hpp>
|
||||
#include <ares/memory/writable.hpp>
|
||||
#include <ares/resource/resource.hpp>
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
namespace ares::Memory {
|
||||
|
||||
constexpr u32 fixedBufferSize = 128_MiB;
|
||||
constexpr u32 fixedBufferSize = 64_MiB;
|
||||
|
||||
#if defined(STATIC_ALLOCATION)
|
||||
u8 fixedBuffer[fixedBufferSize + 64_KiB];
|
||||
|
||||
@@ -83,7 +83,7 @@ protected:
|
||||
f64 _luminance = 1.0;
|
||||
u32 _fillColor = 0;
|
||||
bool _colorBleed = false;
|
||||
bool _colorBleedWidth = 1;
|
||||
u32 _colorBleedWidth = 1;
|
||||
bool _interframeBlending = false;
|
||||
bool _overscan = true;
|
||||
u32 _rotation = 0; //counter-clockwise (90 = left, 270 = right)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
target_link_options(ares PRIVATE LINKER:-export-dynamic)
|
||||
|
||||
target_sources(ares PRIVATE cmake/os-linux.cmake)
|
||||
@@ -0,0 +1 @@
|
||||
target_sources(ares PRIVATE cmake/os-macos.cmake)
|
||||
@@ -0,0 +1,5 @@
|
||||
target_link_libraries(
|
||||
ares
|
||||
PRIVATE uuid kernel32 user32 gdi32 comctl32 comdlg32 shell32
|
||||
)
|
||||
target_sources(ares PRIVATE cmake/os-windows.cmake)
|
||||
@@ -0,0 +1,471 @@
|
||||
list(REMOVE_DUPLICATES ares.components)
|
||||
|
||||
ares_add_sources(INCLUDED CMakeLists.txt)
|
||||
|
||||
macro(component_sources)
|
||||
set(options "")
|
||||
set(oneValueArgs COMPONENT PRIMARY)
|
||||
set(multiValueArgs INCLUDED)
|
||||
|
||||
cmake_parse_arguments(CPS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(${CPS_COMPONENT} IN_LIST ares.components)
|
||||
target_sources(ares PRIVATE ${CPS_PRIMARY})
|
||||
foreach(arg IN ITEMS ${CPS_INCLUDED})
|
||||
list(APPEND ARES_HEADER_SOURCES "component/${arg}")
|
||||
target_sources(ares PRIVATE ${arg})
|
||||
endforeach()
|
||||
endif()
|
||||
set(ARES_HEADER_SOURCES ${ARES_HEADER_SOURCES} PARENT_SCOPE)
|
||||
endmacro()
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
ay38910
|
||||
PRIMARY
|
||||
audio/ay38910/ay38910.cpp
|
||||
INCLUDED
|
||||
audio/ay38910/ay38910.hpp
|
||||
audio/ay38910/serialization.cpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
msm5205
|
||||
PRIMARY
|
||||
audio/msm5205/msm5205.cpp
|
||||
INCLUDED
|
||||
audio/msm5205/msm5205.hpp
|
||||
audio/msm5205/serialization.cpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
sn76489
|
||||
PRIMARY
|
||||
audio/sn76489/sn76489.cpp
|
||||
INCLUDED
|
||||
audio/sn76489/sn76489.hpp
|
||||
audio/sn76489/serialization.cpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
t6w28
|
||||
PRIMARY
|
||||
audio/t6w28/t6w28.cpp
|
||||
INCLUDED
|
||||
audio/t6w28/t6w28.hpp
|
||||
audio/t6w28/serialization.cpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
ym2149
|
||||
PRIMARY
|
||||
audio/ym2149/ym2149.cpp
|
||||
INCLUDED
|
||||
audio/ym2149/ym2149.hpp
|
||||
audio/ym2149/serialization.cpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
ym2413
|
||||
PRIMARY
|
||||
audio/ym2413/ym2413.cpp
|
||||
INCLUDED
|
||||
audio/ym2413/constants.cpp
|
||||
audio/ym2413/io.cpp
|
||||
audio/ym2413/operator.cpp
|
||||
audio/ym2413/serialization.cpp
|
||||
audio/ym2413/voice.cpp
|
||||
audio/ym2413/ym2413.hpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
ym2612
|
||||
PRIMARY
|
||||
audio/ym2612/ym2612.cpp
|
||||
INCLUDED
|
||||
audio/ym2612/channel.cpp
|
||||
audio/ym2612/constants.cpp
|
||||
audio/ym2612/io.cpp
|
||||
audio/ym2612/serialization.cpp
|
||||
audio/ym2612/timer.cpp
|
||||
audio/ym2612/ym2612.hpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
m24c
|
||||
PRIMARY
|
||||
eeprom/m24c/m24c.cpp
|
||||
INCLUDED
|
||||
eeprom/m24c/m24c.hpp
|
||||
eeprom/m24c/serialization.cpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
m93lcx6
|
||||
PRIMARY
|
||||
eeprom/m93lcx6/m93lcx6.cpp
|
||||
INCLUDED
|
||||
eeprom/m93lcx6/m93lcx6.hpp
|
||||
eeprom/m93lcx6/serialization.cpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
i8255
|
||||
PRIMARY
|
||||
io/i8255/i8255.cpp
|
||||
INCLUDED
|
||||
io/i8255/i8255.hpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
arm7tdmi
|
||||
PRIMARY
|
||||
processor/arm7tdmi/arm7tdmi.cpp
|
||||
INCLUDED
|
||||
processor/arm7tdmi/algorithms.cpp
|
||||
processor/arm7tdmi/arm7tdmi.hpp
|
||||
processor/arm7tdmi/disassembler.cpp
|
||||
processor/arm7tdmi/instruction.cpp
|
||||
processor/arm7tdmi/instructions-arm.cpp
|
||||
processor/arm7tdmi/instructions-thumb.cpp
|
||||
processor/arm7tdmi/memory.cpp
|
||||
processor/arm7tdmi/registers.cpp
|
||||
processor/arm7tdmi/serialization.cpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
gsu
|
||||
PRIMARY
|
||||
processor/gsu/gsu.cpp
|
||||
INCLUDED
|
||||
processor/gsu/disassembler.cpp
|
||||
processor/gsu/gsu.hpp
|
||||
processor/gsu/instruction.cpp
|
||||
processor/gsu/instructions.cpp
|
||||
processor/gsu/registers.hpp
|
||||
processor/gsu/serialization.cpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
hg51b
|
||||
PRIMARY
|
||||
processor/hg51b/hg51b.cpp
|
||||
INCLUDED
|
||||
processor/hg51b/disassembler.cpp
|
||||
processor/hg51b/hg51b.hpp
|
||||
processor/hg51b/instruction.cpp
|
||||
processor/hg51b/instructions.cpp
|
||||
processor/hg51b/registers.cpp
|
||||
processor/hg51b/serialization.cpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
huc6280
|
||||
PRIMARY
|
||||
processor/huc6280/huc6280.cpp
|
||||
INCLUDED
|
||||
processor/huc6280/algorithms.cpp
|
||||
processor/huc6280/disassembler.cpp
|
||||
processor/huc6280/huc6280.hpp
|
||||
processor/huc6280/instruction.cpp
|
||||
processor/huc6280/instructions.cpp
|
||||
processor/huc6280/memory.cpp
|
||||
processor/huc6280/serialization.cpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
i8080
|
||||
PRIMARY
|
||||
processor/i8080/i8080.cpp
|
||||
INCLUDED
|
||||
processor/i8080/algorithms.cpp
|
||||
processor/i8080/disassembler.cpp
|
||||
processor/i8080/i8080.hpp
|
||||
processor/i8080/instruction.cpp
|
||||
processor/i8080/instructions.cpp
|
||||
processor/i8080/memory.cpp
|
||||
processor/i8080/registers.hpp
|
||||
processor/i8080/serialization.cpp
|
||||
)
|
||||
component_sources(
|
||||
COMPONENT
|
||||
m68hc05
|
||||
PRIMARY
|
||||
processor/m68hc05/m68hc05.cpp
|
||||
INCLUDED
|
||||
processor/m68hc05/algorithms.cpp
|
||||
processor/m68hc05/disassembler.cpp
|
||||
processor/m68hc05/instruction.cpp
|
||||
processor/m68hc05/instructions.cpp
|
||||
processor/m68hc05/m68hc05.hpp
|
||||
processor/m68hc05/memory.cpp
|
||||
processor/m68hc05/serialization.cpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
m68000
|
||||
PRIMARY
|
||||
processor/m68000/m68000.cpp
|
||||
INCLUDED
|
||||
processor/m68000/algorithms.cpp
|
||||
processor/m68000/conditions.cpp
|
||||
processor/m68000/disassembler.cpp
|
||||
processor/m68000/effective-address.cpp
|
||||
processor/m68000/instruction.cpp
|
||||
processor/m68000/instructions.cpp
|
||||
processor/m68000/m68000.hpp
|
||||
processor/m68000/memory.cpp
|
||||
processor/m68000/registers.cpp
|
||||
processor/m68000/serialization.cpp
|
||||
processor/m68000/traits.cpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
mos6502
|
||||
PRIMARY
|
||||
processor/mos6502/mos6502.cpp
|
||||
INCLUDED
|
||||
processor/mos6502/addresses.cpp
|
||||
processor/mos6502/algorithms.cpp
|
||||
processor/mos6502/disassembler.cpp
|
||||
processor/mos6502/instruction.cpp
|
||||
processor/mos6502/instructions.cpp
|
||||
processor/mos6502/memory.cpp
|
||||
processor/mos6502/mos6502.hpp
|
||||
processor/mos6502/serialization.cpp
|
||||
)
|
||||
component_sources(
|
||||
COMPONENT
|
||||
sh2
|
||||
PRIMARY
|
||||
processor/sh2/sh2.cpp
|
||||
INCLUDED
|
||||
processor/sh2/accuracy.hpp
|
||||
processor/sh2/decoder.hpp
|
||||
processor/sh2/disassembler.cpp
|
||||
processor/sh2/exceptions.cpp
|
||||
processor/sh2/instruction.cpp
|
||||
processor/sh2/instructions.cpp
|
||||
processor/sh2/recompiler.cpp
|
||||
processor/sh2/serialization.cpp
|
||||
processor/sh2/sh2.hpp
|
||||
processor/sh2/sh7604/bus.cpp
|
||||
processor/sh2/sh7604/cache.cpp
|
||||
processor/sh2/sh7604/dma.cpp
|
||||
processor/sh2/sh7604/interrupts.cpp
|
||||
processor/sh2/sh7604/io.cpp
|
||||
processor/sh2/sh7604/serial.cpp
|
||||
processor/sh2/sh7604/sh7604.cpp
|
||||
processor/sh2/sh7604/sh7604.hpp
|
||||
processor/sh2/sh7604/timer.cpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
sm5k
|
||||
PRIMARY
|
||||
processor/sm5k/sm5k.cpp
|
||||
INCLUDED
|
||||
processor/sm5k/disassembler.cpp
|
||||
processor/sm5k/instruction.cpp
|
||||
processor/sm5k/instructions.cpp
|
||||
processor/sm5k/memory.cpp
|
||||
processor/sm5k/serialization.cpp
|
||||
processor/sm5k/sm5k.hpp
|
||||
processor/sm5k/timer.cpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
sm83
|
||||
PRIMARY
|
||||
processor/sm83/sm83.cpp
|
||||
INCLUDED
|
||||
processor/sm83/algorithms.cpp
|
||||
processor/sm83/disassembler.cpp
|
||||
processor/sm83/instruction.cpp
|
||||
processor/sm83/instructions.cpp
|
||||
processor/sm83/memory.cpp
|
||||
processor/sm83/registers.hpp
|
||||
processor/sm83/serialization.cpp
|
||||
processor/sm83/sm83.hpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
spc700
|
||||
PRIMARY
|
||||
processor/spc700/spc700.cpp
|
||||
INCLUDED
|
||||
processor/spc700/algorithms.cpp
|
||||
processor/spc700/disassembler.cpp
|
||||
processor/spc700/instruction.cpp
|
||||
processor/spc700/instructions.cpp
|
||||
processor/spc700/memory.cpp
|
||||
processor/spc700/serialization.cpp
|
||||
processor/spc700/spc700.hpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
ssp1601
|
||||
PRIMARY
|
||||
processor/ssp1601/ssp1601.cpp
|
||||
INCLUDED
|
||||
processor/ssp1601/algorithms.cpp
|
||||
processor/ssp1601/disassembler.cpp
|
||||
processor/ssp1601/instructions.cpp
|
||||
processor/ssp1601/memory.cpp
|
||||
processor/ssp1601/registers.cpp
|
||||
processor/ssp1601/serialization.cpp
|
||||
processor/ssp1601/ssp1601.hpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
tlcs900h
|
||||
PRIMARY
|
||||
processor/tlcs900h/tlcs900h.cpp
|
||||
INCLUDED
|
||||
processor/tlcs900h/algorithms.cpp
|
||||
processor/tlcs900h/conditions.cpp
|
||||
processor/tlcs900h/control-registers.cpp
|
||||
processor/tlcs900h/disassembler.cpp
|
||||
processor/tlcs900h/dma.cpp
|
||||
processor/tlcs900h/instruction.cpp
|
||||
processor/tlcs900h/instructions.cpp
|
||||
processor/tlcs900h/memory.cpp
|
||||
processor/tlcs900h/prefetch.cpp
|
||||
processor/tlcs900h/registers.cpp
|
||||
processor/tlcs900h/serialization.cpp
|
||||
processor/tlcs900h/tlcs900h.hpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
upd96050
|
||||
PRIMARY
|
||||
processor/upd96050/upd96050.cpp
|
||||
INCLUDED
|
||||
processor/upd96050/disassembler.cpp
|
||||
processor/upd96050/instructions.cpp
|
||||
processor/upd96050/memory.cpp
|
||||
processor/upd96050/serialization.cpp
|
||||
processor/upd96050/upd96050.hpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
v30mz
|
||||
PRIMARY
|
||||
processor/v30mz/v30mz.cpp
|
||||
INCLUDED
|
||||
processor/v30mz/algorithms.cpp
|
||||
processor/v30mz/disassembler.cpp
|
||||
processor/v30mz/instruction.cpp
|
||||
processor/v30mz/instructions-adjust.cpp
|
||||
processor/v30mz/instructions-alu.cpp
|
||||
processor/v30mz/instructions-exec.cpp
|
||||
processor/v30mz/instructions-flag.cpp
|
||||
processor/v30mz/instructions-group.cpp
|
||||
processor/v30mz/instructions-misc.cpp
|
||||
processor/v30mz/instructions-move.cpp
|
||||
processor/v30mz/instructions-string.cpp
|
||||
processor/v30mz/memory.cpp
|
||||
processor/v30mz/modrm.cpp
|
||||
processor/v30mz/prefetch.cpp
|
||||
processor/v30mz/registers.cpp
|
||||
processor/v30mz/serialization.cpp
|
||||
processor/v30mz/v30mz.hpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
wdc65816
|
||||
PRIMARY
|
||||
processor/wdc65816/wdc65816.cpp
|
||||
INCLUDED
|
||||
processor/wdc65816/algorithms.cpp
|
||||
processor/wdc65816/disassembler.cpp
|
||||
processor/wdc65816/instruction.cpp
|
||||
processor/wdc65816/instruction.hpp
|
||||
processor/wdc65816/instructions-modify.cpp
|
||||
processor/wdc65816/instructions-other.cpp
|
||||
processor/wdc65816/instructions-pc.cpp
|
||||
processor/wdc65816/instructions-read.cpp
|
||||
processor/wdc65816/instructions-write.cpp
|
||||
processor/wdc65816/memory.cpp
|
||||
processor/wdc65816/registers.hpp
|
||||
processor/wdc65816/serialization.cpp
|
||||
processor/wdc65816/wdc65816.hpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
z80
|
||||
PRIMARY
|
||||
processor/z80/z80.cpp
|
||||
INCLUDED
|
||||
processor/z80/algorithms.cpp
|
||||
processor/z80/disassembler.cpp
|
||||
processor/z80/instruction.cpp
|
||||
processor/z80/instructions.cpp
|
||||
processor/z80/memory.cpp
|
||||
processor/z80/registers.hpp
|
||||
processor/z80/serialization.cpp
|
||||
processor/z80/z80.hpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
sst39sf0x0
|
||||
PRIMARY
|
||||
flash/sst39sf0x0/sst39sf0x0.cpp
|
||||
INCLUDED
|
||||
flash/sst39sf0x0/sst39sf0x0.hpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
tms9918
|
||||
PRIMARY
|
||||
video/tms9918/tms9918.cpp
|
||||
INCLUDED
|
||||
video/tms9918/background.cpp
|
||||
video/tms9918/dac.cpp
|
||||
video/tms9918/io.cpp
|
||||
video/tms9918/serialization.cpp
|
||||
video/tms9918/sprite.cpp
|
||||
video/tms9918/tms9918.hpp
|
||||
)
|
||||
|
||||
component_sources(
|
||||
COMPONENT
|
||||
v9938
|
||||
PRIMARY
|
||||
video/v9938/v9938.cpp
|
||||
INCLUDED
|
||||
video/v9938/background.cpp
|
||||
video/v9938/commands.cpp
|
||||
video/v9938/dac.cpp
|
||||
video/v9938/io.cpp
|
||||
video/v9938/serialization.cpp
|
||||
video/v9938/sprite.cpp
|
||||
video/v9938/v9938.hpp
|
||||
)
|
||||
@@ -29,24 +29,25 @@ auto YM2612::Channel::Operator::updateKeyState() -> void {
|
||||
auto YM2612::Channel::Operator::runEnvelope() -> void {
|
||||
if(ym2612.envelope.clock & (1 << envelope.divider) - 1) return;
|
||||
|
||||
if(envelope.state == Attack && envelope.value == 0) {
|
||||
envelope.state = Decay;
|
||||
updateEnvelope();
|
||||
}
|
||||
|
||||
u32 sustain = envelope.sustainLevel < 15 ? envelope.sustainLevel << 5 : 0x1f << 5;
|
||||
if(envelope.state == Decay && envelope.value >= sustain) {
|
||||
envelope.state = Sustain;
|
||||
updateEnvelope();
|
||||
}
|
||||
|
||||
u32 value = ym2612.envelope.clock >> envelope.divider;
|
||||
u32 step = envelope.steps >> ((~value & 7) << 2) & 0xf;
|
||||
u32 sustain = envelope.sustainLevel < 15 ? envelope.sustainLevel << 5 : 0x1f << 5;
|
||||
|
||||
if(envelope.state == Attack) {
|
||||
if(envelope.value == 0) {
|
||||
envelope.state = Decay;
|
||||
updateEnvelope();
|
||||
} else if(envelope.rate < 62) {
|
||||
// will stop updating if attack rate is increased to upper threshold during attack phase (confirmed behavior)
|
||||
envelope.value += ~u16(envelope.value) * step >> 4;
|
||||
}
|
||||
// will stop updating if attack rate is increased to upper threshold during attack phase (confirmed behavior)
|
||||
if(envelope.rate < 62) envelope.value += ~u16(envelope.value) * step >> 4;
|
||||
}
|
||||
if(envelope.state != Attack) {
|
||||
if(envelope.state == Decay && envelope.value >= sustain) {
|
||||
envelope.state = Sustain;
|
||||
updateEnvelope();
|
||||
}
|
||||
if(ssg.enable) step = envelope.value < 0x200 ? step << 2 : 0; //SSG results in a 4x faster envelope
|
||||
envelope.value = min(envelope.value + step, 0x3ff);
|
||||
}
|
||||
@@ -82,8 +83,6 @@ auto YM2612::Channel::Operator::runPhase() -> void {
|
||||
}
|
||||
|
||||
auto YM2612::Channel::Operator::updateEnvelope() -> void {
|
||||
u32 key = min(max((u32)pitch.value, 0x300), 0x4ff);
|
||||
u32 ksr = (octave.value << 2) + ((key - 0x300) >> 7);
|
||||
u32 rate = 0;
|
||||
|
||||
if(envelope.state == Attack) rate += (envelope.attackRate << 1);
|
||||
@@ -91,7 +90,7 @@ auto YM2612::Channel::Operator::updateEnvelope() -> void {
|
||||
if(envelope.state == Sustain) rate += (envelope.sustainRate << 1);
|
||||
if(envelope.state == Release) rate += (envelope.releaseRate << 1);
|
||||
|
||||
rate += (ksr >> 3 - envelope.keyScale) * (rate > 0);
|
||||
rate += (keyScale >> 3 - envelope.rateScaling) * (rate > 0);
|
||||
rate = min(rate, 63);
|
||||
|
||||
auto& entry = envelopeRates[rate >> 2];
|
||||
@@ -107,15 +106,15 @@ auto YM2612::Channel::Operator::updatePitch() -> void {
|
||||
pitch.value = channel.mode ? pitch.reload : channel[3].pitch.reload;
|
||||
octave.value = channel.mode ? octave.reload : channel[3].octave.reload;
|
||||
|
||||
u32 key = min(max((u32)pitch.value, 0x300), 0x4ff);
|
||||
keyScale = (octave.value << 2) + ((key - 0x300) >> 7);
|
||||
|
||||
updatePhase();
|
||||
updateEnvelope(); //due to key scaling
|
||||
}
|
||||
|
||||
auto YM2612::Channel::Operator::updatePhase() -> void {
|
||||
u32 key = min(max((u32)pitch.value, 0x300), 0x4ff);
|
||||
u32 ksr = (octave.value << 2) + ((key - 0x300) >> 7);
|
||||
u32 tuning = detune & 3 ? detunes[(detune & 3) - 1][ksr & 7] >> (3 - (ksr >> 3)) : 0;
|
||||
|
||||
u32 tuning = detune & 3 ? detunes[(detune & 3) - 1][keyScale & 7] >> (3 - (keyScale >> 3)) : 0;
|
||||
u32 lfo = ym2612.lfo.clock >> 2 & 0x1f;
|
||||
s32 pm = (pitch.value * vibratos[channel.vibrato][lfo & 15] >> 9) * (lfo > 15 ? -1 : 1);
|
||||
|
||||
@@ -131,7 +130,7 @@ auto YM2612::Channel::Operator::updateLevel() -> void {
|
||||
bool invert = ssg.attack != ssg.invert && envelope.state != Release;
|
||||
n10 value = ssg.enable && invert ? 0x200 - envelope.value : 0 + envelope.value;
|
||||
|
||||
outputLevel = ((totalLevel << 3) + value + (lfoEnable ? lfo << 1 >> depth : 0)) << 3;
|
||||
outputLevel = (totalLevel << 3) + value + (lfo << 1 >> depth) << 3;
|
||||
}
|
||||
|
||||
auto YM2612::Channel::power() -> void {
|
||||
@@ -149,6 +148,7 @@ auto YM2612::Channel::power() -> void {
|
||||
op.keyOn = 0;
|
||||
op.keyLine = 0;
|
||||
op.lfoEnable = 0;
|
||||
op.keyScale = 0;
|
||||
op.detune = 0;
|
||||
op.multiple = 0;
|
||||
op.totalLevel = 0;
|
||||
@@ -175,7 +175,7 @@ auto YM2612::Channel::power() -> void {
|
||||
op.envelope.steps = 0;
|
||||
op.envelope.value = 0x3ff;
|
||||
|
||||
op.envelope.keyScale = 0;
|
||||
op.envelope.rateScaling = 0;
|
||||
op.envelope.attackRate = 0;
|
||||
op.envelope.decayRate = 0;
|
||||
op.envelope.sustainRate = 0;
|
||||
|
||||
@@ -14,8 +14,10 @@ auto YM2612::writeData(n8 data) -> void {
|
||||
case 0x022: {
|
||||
lfo.rate = data.bit(0,2);
|
||||
lfo.enable = data.bit(3);
|
||||
lfo.clock = 0;
|
||||
lfo.divider = 0;
|
||||
if(!lfo.enable) {
|
||||
lfo.clock = 0;
|
||||
lfo.divider = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -114,10 +116,10 @@ auto YM2612::writeData(n8 data) -> void {
|
||||
break;
|
||||
}
|
||||
|
||||
//key scaling, attack rate
|
||||
//rate scaling, attack rate
|
||||
case 0x050: {
|
||||
op.envelope.attackRate = data.bit(0,4);
|
||||
op.envelope.keyScale = data.bit(6,7);
|
||||
op.envelope.rateScaling = data.bit(6,7);
|
||||
channel[index].updateEnvelope();
|
||||
channel[index].updatePhase();
|
||||
break;
|
||||
|
||||
@@ -44,6 +44,7 @@ auto YM2612::Channel::Operator::serialize(serializer& s) -> void {
|
||||
s(keyOn);
|
||||
s(keyLine);
|
||||
s(lfoEnable);
|
||||
s(keyScale);
|
||||
s(detune);
|
||||
s(multiple);
|
||||
s(totalLevel);
|
||||
@@ -68,7 +69,7 @@ auto YM2612::Channel::Operator::serialize(serializer& s) -> void {
|
||||
s(envelope.divider);
|
||||
s(envelope.steps);
|
||||
s(envelope.value);
|
||||
s(envelope.keyScale);
|
||||
s(envelope.rateScaling);
|
||||
s(envelope.attackRate);
|
||||
s(envelope.decayRate);
|
||||
s(envelope.sustainRate);
|
||||
|
||||
@@ -13,6 +13,25 @@ auto YM2612::clock() -> array<i16[2]> {
|
||||
s32 left = 0;
|
||||
s32 right = 0;
|
||||
|
||||
timerA.run();
|
||||
timerB.run();
|
||||
|
||||
if(++envelope.divider == 3) {
|
||||
envelope.divider = 0;
|
||||
if(!++envelope.clock) ++envelope.clock; // 12-bit counter: 1..4095 - zero-value is skipped (confirmed behavior)
|
||||
}
|
||||
|
||||
if(lfo.enable && ++lfo.divider >= lfoDividers[lfo.rate]) {
|
||||
lfo.divider = 0;
|
||||
lfo.clock++;
|
||||
for(auto& channel : channels) {
|
||||
for(auto& op : channel.operators) {
|
||||
op.updatePhase(); //due to vibrato
|
||||
op.updateLevel(); //due to tremolo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& channel : channels) {
|
||||
auto& op = channel.operators;
|
||||
|
||||
@@ -30,12 +49,18 @@ auto YM2612::clock() -> array<i16[2]> {
|
||||
return y < 0x1a00 ? pow2[y & 0x1ff] << 2 >> (y >> 9) : 0; // -78 dB floor
|
||||
};
|
||||
|
||||
s32 feedback = modMask & op[0].prior + op[0].priorBuffer >> 9 - channel.feedback;
|
||||
s32 accumulator = 0;
|
||||
|
||||
op[0].priorBuffer = op[0].prior; // only need to buffer the output for feedback
|
||||
for(auto n : range(4)) op[n].prior = op[n].output;
|
||||
|
||||
for(auto& op : channel.operators) {
|
||||
op.runPhase();
|
||||
if(envelope.divider) continue;
|
||||
op.runEnvelope();
|
||||
}
|
||||
|
||||
s32 feedback = modMask & op[0].prior + op[0].priorBuffer >> 9 - channel.feedback;
|
||||
s32 accumulator = 0;
|
||||
|
||||
op[0].output = wave(0, feedback * (channel.feedback > 0));
|
||||
|
||||
// Note: Despite not emulating per cycle, operator pipelining has been accounted for.
|
||||
@@ -122,33 +147,6 @@ auto YM2612::clock() -> array<i16[2]> {
|
||||
//if(channel.rightEnable) right += voiceData;
|
||||
}
|
||||
|
||||
timerA.run();
|
||||
timerB.run();
|
||||
|
||||
if(lfo.enable && ++lfo.divider == lfoDividers[lfo.rate]) {
|
||||
lfo.divider = 0;
|
||||
lfo.clock++;
|
||||
for(auto& channel : channels) {
|
||||
for(auto& op : channel.operators) {
|
||||
op.updatePhase(); //due to vibrato
|
||||
op.updateLevel(); //due to tremolo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(++envelope.divider == 3) {
|
||||
envelope.divider = 0;
|
||||
if(!++envelope.clock) ++envelope.clock; // 12-bit counter: 1..4095 - zero-value is skipped (confirmed behavior)
|
||||
}
|
||||
|
||||
for(auto& channel : channels) {
|
||||
for(auto& op : channel.operators) {
|
||||
op.runPhase();
|
||||
if(envelope.divider) continue;
|
||||
op.runEnvelope();
|
||||
}
|
||||
}
|
||||
|
||||
return {sclamp<16>(left), sclamp<16>(right)};
|
||||
}
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@ protected:
|
||||
n1 keyOn = 0;
|
||||
n1 keyLine = 0;
|
||||
n1 lfoEnable = 0;
|
||||
n5 keyScale = 0;
|
||||
n3 detune = 0;
|
||||
n4 multiple = 0;
|
||||
n7 totalLevel = 0;
|
||||
@@ -140,7 +141,7 @@ protected:
|
||||
n32 steps = 0;
|
||||
n10 value = 0x3ff;
|
||||
|
||||
n2 keyScale = 0;
|
||||
n2 rateScaling = 0;
|
||||
n5 attackRate = 0;
|
||||
n5 decayRate = 0;
|
||||
n5 sustainRate = 0;
|
||||
|
||||
@@ -22,6 +22,8 @@ struct ARM7TDMI {
|
||||
virtual auto get(u32 mode, n32 address) -> n32 = 0;
|
||||
virtual auto getDebugger(u32 mode, n32 address) -> n32 { return get(mode, address); }
|
||||
virtual auto set(u32 mode, n32 address, n32 word) -> void = 0;
|
||||
virtual auto lock() -> void { return; }
|
||||
virtual auto unlock() -> void { return; }
|
||||
|
||||
//arm7tdmi.cpp
|
||||
ARM7TDMI();
|
||||
@@ -56,6 +58,7 @@ struct ARM7TDMI {
|
||||
auto TST(n4) -> bool;
|
||||
|
||||
//instruction.cpp
|
||||
auto reload() -> void;
|
||||
auto fetch() -> void;
|
||||
auto instruction() -> void;
|
||||
auto exception(u32 mode, n32 address) -> void;
|
||||
@@ -214,6 +217,7 @@ struct ARM7TDMI {
|
||||
n32 address;
|
||||
n32 instruction;
|
||||
b1 thumb; //not used by fetch stage
|
||||
b1 irq; //not used by fetch stage
|
||||
};
|
||||
|
||||
n1 reload = 1;
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
auto ARM7TDMI::reload() -> void {
|
||||
u32 mask = !cpsr().t ? 3 : 1;
|
||||
u32 size = !cpsr().t ? Word : Half;
|
||||
|
||||
pipeline.reload = false;
|
||||
r(15).data &= ~mask;
|
||||
pipeline.fetch.address = r(15) & ~mask;
|
||||
pipeline.fetch.instruction = read(Prefetch | size | Nonsequential, pipeline.fetch.address);
|
||||
fetch();
|
||||
}
|
||||
|
||||
auto ARM7TDMI::fetch() -> void {
|
||||
pipeline.execute = pipeline.decode;
|
||||
pipeline.execute.irq = pipeline.execute.irq & irq;
|
||||
pipeline.decode = pipeline.fetch;
|
||||
pipeline.decode.thumb = cpsr().t;
|
||||
pipeline.decode.irq = !cpsr().i;
|
||||
|
||||
u32 sequential = Sequential;
|
||||
if(pipeline.nonsequential) {
|
||||
@@ -18,19 +31,10 @@ auto ARM7TDMI::fetch() -> void {
|
||||
}
|
||||
|
||||
auto ARM7TDMI::instruction() -> void {
|
||||
u32 mask = !cpsr().t ? 3 : 1;
|
||||
u32 size = !cpsr().t ? Word : Half;
|
||||
|
||||
if(pipeline.reload) {
|
||||
pipeline.reload = false;
|
||||
r(15).data &= ~mask;
|
||||
pipeline.fetch.address = r(15) & ~mask;
|
||||
pipeline.fetch.instruction = read(Prefetch | size | Nonsequential, pipeline.fetch.address);
|
||||
fetch();
|
||||
}
|
||||
if(pipeline.reload) reload();
|
||||
fetch();
|
||||
|
||||
if(irq && !cpsr().i) {
|
||||
if(pipeline.execute.irq) {
|
||||
exception(PSR::IRQ, 0x18);
|
||||
if(pipeline.execute.thumb) r(14).data += 2;
|
||||
return;
|
||||
@@ -419,9 +423,9 @@ auto ARM7TDMI::thumbInitialize() -> void {
|
||||
bind(opcode, AdjustStack, immediate, mode);
|
||||
}
|
||||
|
||||
for(n3 _ : range(8))
|
||||
for(n4 d : range(16))
|
||||
for(n4 m : range(16)) {
|
||||
auto opcode = pattern("0100 0111 0??? ?---") | _ << 0 | m << 3;
|
||||
auto opcode = pattern("0100 0111 ???? ?---") | d.bit(0,2) << 0 | m << 3 | d.bit(3) << 7;
|
||||
bind(opcode, BranchExchange, m);
|
||||
}
|
||||
|
||||
|
||||
@@ -131,9 +131,11 @@ auto ARM7TDMI::armInstructionLoadRegister
|
||||
|
||||
auto ARM7TDMI::armInstructionMemorySwap
|
||||
(n4 m, n4 d, n4 n, n1 byte) -> void {
|
||||
lock();
|
||||
n32 word = load((byte ? Byte : Word) | Nonsequential, r(n));
|
||||
store((byte ? Byte : Word) | Nonsequential, r(n), r(m));
|
||||
r(d) = word;
|
||||
unlock();
|
||||
}
|
||||
|
||||
auto ARM7TDMI::armInstructionMoveHalfImmediate
|
||||
@@ -300,8 +302,8 @@ auto ARM7TDMI::armInstructionMultiplyLong
|
||||
n64 rd = rm * rs;
|
||||
if(accumulate) rd += (n64)r(h) << 32 | (n64)r(l) << 0;
|
||||
|
||||
r(h) = rd >> 32;
|
||||
r(l) = rd >> 0;
|
||||
r(h) = rd >> 32;
|
||||
|
||||
if(save) {
|
||||
cpsr().z = rd == 0;
|
||||
|
||||
@@ -62,10 +62,13 @@ auto ARM7TDMI::Pipeline::serialize(serializer& s) -> void {
|
||||
s(fetch.address);
|
||||
s(fetch.instruction);
|
||||
s(fetch.thumb);
|
||||
s(fetch.irq);
|
||||
s(decode.address);
|
||||
s(decode.instruction);
|
||||
s(decode.thumb);
|
||||
s(decode.irq);
|
||||
s(execute.address);
|
||||
s(execute.instruction);
|
||||
s(execute.thumb);
|
||||
s(execute.irq);
|
||||
}
|
||||
@@ -37,56 +37,56 @@ auto GSU::disassembleOpcode(char* output) -> void {
|
||||
auto GSU::disassembleALT0(char* output) -> void {
|
||||
char t[256] = "";
|
||||
switch(op0) {
|
||||
case (0x00): sprintf(t, "stop"); break;
|
||||
case (0x01): sprintf(t, "nop"); break;
|
||||
case (0x02): sprintf(t, "cache"); break;
|
||||
case (0x03): sprintf(t, "lsr"); break;
|
||||
case (0x04): sprintf(t, "rol"); break;
|
||||
case (0x05): sprintf(t, "bra %+d", (s8)op1); break;
|
||||
case (0x06): sprintf(t, "blt %+d", (s8)op1); break;
|
||||
case (0x07): sprintf(t, "bge %+d", (s8)op1); break;
|
||||
case (0x08): sprintf(t, "bne %+d", (s8)op1); break;
|
||||
case (0x09): sprintf(t, "beq %+d", (s8)op1); break;
|
||||
case (0x0a): sprintf(t, "bpl %+d", (s8)op1); break;
|
||||
case (0x0b): sprintf(t, "bmi %+d", (s8)op1); break;
|
||||
case (0x0c): sprintf(t, "bcc %+d", (s8)op1); break;
|
||||
case (0x0d): sprintf(t, "bcs %+d", (s8)op1); break;
|
||||
case (0x0e): sprintf(t, "bvc %+d", (s8)op1); break;
|
||||
case (0x0f): sprintf(t, "bvs %+d", (s8)op1); break;
|
||||
case16(0x10): sprintf(t, "to r%u", op0 & 15); break;
|
||||
case16(0x20): sprintf(t, "with r%u", op0 & 15); break;
|
||||
case12(0x30): sprintf(t, "stw (r%u)", op0 & 15); break;
|
||||
case (0x3c): sprintf(t, "loop"); break;
|
||||
case (0x3d): sprintf(t, "alt1"); break;
|
||||
case (0x3e): sprintf(t, "alt2"); break;
|
||||
case (0x3f): sprintf(t, "alt3"); break;
|
||||
case12(0x40): sprintf(t, "ldw (r%u)", op0 & 15); break;
|
||||
case (0x4c): sprintf(t, "plot"); break;
|
||||
case (0x4d): sprintf(t, "swap"); break;
|
||||
case (0x4e): sprintf(t, "color"); break;
|
||||
case (0x4f): sprintf(t, "not"); break;
|
||||
case16(0x50): sprintf(t, "add r%u", op0 & 15); break;
|
||||
case16(0x60): sprintf(t, "sub r%u", op0 & 15); break;
|
||||
case (0x70): sprintf(t, "merge"); break;
|
||||
case15(0x71): sprintf(t, "and r%u", op0 & 15); break;
|
||||
case16(0x80): sprintf(t, "mult r%u", op0 & 15); break;
|
||||
case (0x90): sprintf(t, "sbk"); break;
|
||||
case4 (0x91): sprintf(t, "link #%u", op0 & 15); break;
|
||||
case (0x95): sprintf(t, "sex"); break;
|
||||
case (0x96): sprintf(t, "asr"); break;
|
||||
case (0x97): sprintf(t, "ror"); break;
|
||||
case6 (0x98): sprintf(t, "jmp r%u", op0 & 15); break;
|
||||
case (0x9e): sprintf(t, "lob"); break;
|
||||
case (0x9f): sprintf(t, "fmult"); break;
|
||||
case16(0xa0): sprintf(t, "ibt r%u,#$%.2x", op0 & 15, op1 & 255); break;
|
||||
case16(0xb0): sprintf(t, "from r%u", op0 & 15); break;
|
||||
case (0xc0): sprintf(t, "hib"); break;
|
||||
case15(0xc1): sprintf(t, "or r%u", op0 & 15); break;
|
||||
case15(0xd0): sprintf(t, "inc r%u", op0 & 15); break;
|
||||
case (0xdf): sprintf(t, "getc"); break;
|
||||
case15(0xe0): sprintf(t, "dec r%u", op0 & 15); break;
|
||||
case (0xef): sprintf(t, "getb"); break;
|
||||
case16(0xf0): sprintf(t, "iwt r%u,#$%.2x%.2x", op0 & 15, op2 & 255, op1 & 255); break;
|
||||
case (0x00):snprintf(t, sizeof(t), "stop"); break;
|
||||
case (0x01):snprintf(t, sizeof(t), "nop"); break;
|
||||
case (0x02):snprintf(t, sizeof(t), "cache"); break;
|
||||
case (0x03):snprintf(t, sizeof(t), "lsr"); break;
|
||||
case (0x04):snprintf(t, sizeof(t), "rol"); break;
|
||||
case (0x05):snprintf(t, sizeof(t), "bra %+d", (s8)op1); break;
|
||||
case (0x06):snprintf(t, sizeof(t), "blt %+d", (s8)op1); break;
|
||||
case (0x07):snprintf(t, sizeof(t), "bge %+d", (s8)op1); break;
|
||||
case (0x08):snprintf(t, sizeof(t), "bne %+d", (s8)op1); break;
|
||||
case (0x09):snprintf(t, sizeof(t), "beq %+d", (s8)op1); break;
|
||||
case (0x0a):snprintf(t, sizeof(t), "bpl %+d", (s8)op1); break;
|
||||
case (0x0b):snprintf(t, sizeof(t), "bmi %+d", (s8)op1); break;
|
||||
case (0x0c):snprintf(t, sizeof(t), "bcc %+d", (s8)op1); break;
|
||||
case (0x0d):snprintf(t, sizeof(t), "bcs %+d", (s8)op1); break;
|
||||
case (0x0e):snprintf(t, sizeof(t), "bvc %+d", (s8)op1); break;
|
||||
case (0x0f):snprintf(t, sizeof(t), "bvs %+d", (s8)op1); break;
|
||||
case16(0x10):snprintf(t, sizeof(t), "to r%u", op0 & 15); break;
|
||||
case16(0x20):snprintf(t, sizeof(t), "with r%u", op0 & 15); break;
|
||||
case12(0x30):snprintf(t, sizeof(t), "stw (r%u)", op0 & 15); break;
|
||||
case (0x3c):snprintf(t, sizeof(t), "loop"); break;
|
||||
case (0x3d):snprintf(t, sizeof(t), "alt1"); break;
|
||||
case (0x3e):snprintf(t, sizeof(t), "alt2"); break;
|
||||
case (0x3f):snprintf(t, sizeof(t), "alt3"); break;
|
||||
case12(0x40):snprintf(t, sizeof(t), "ldw (r%u)", op0 & 15); break;
|
||||
case (0x4c):snprintf(t, sizeof(t), "plot"); break;
|
||||
case (0x4d):snprintf(t, sizeof(t), "swap"); break;
|
||||
case (0x4e):snprintf(t, sizeof(t), "color"); break;
|
||||
case (0x4f):snprintf(t, sizeof(t), "not"); break;
|
||||
case16(0x50):snprintf(t, sizeof(t), "add r%u", op0 & 15); break;
|
||||
case16(0x60):snprintf(t, sizeof(t), "sub r%u", op0 & 15); break;
|
||||
case (0x70):snprintf(t, sizeof(t), "merge"); break;
|
||||
case15(0x71):snprintf(t, sizeof(t), "and r%u", op0 & 15); break;
|
||||
case16(0x80):snprintf(t, sizeof(t), "mult r%u", op0 & 15); break;
|
||||
case (0x90):snprintf(t, sizeof(t), "sbk"); break;
|
||||
case4 (0x91):snprintf(t, sizeof(t), "link #%u", op0 & 15); break;
|
||||
case (0x95):snprintf(t, sizeof(t), "sex"); break;
|
||||
case (0x96):snprintf(t, sizeof(t), "asr"); break;
|
||||
case (0x97):snprintf(t, sizeof(t), "ror"); break;
|
||||
case6 (0x98):snprintf(t, sizeof(t), "jmp r%u", op0 & 15); break;
|
||||
case (0x9e):snprintf(t, sizeof(t), "lob"); break;
|
||||
case (0x9f):snprintf(t, sizeof(t), "fmult"); break;
|
||||
case16(0xa0):snprintf(t, sizeof(t), "ibt r%u,#$%.2x", op0 & 15, op1 & 255); break;
|
||||
case16(0xb0):snprintf(t, sizeof(t), "from r%u", op0 & 15); break;
|
||||
case (0xc0):snprintf(t, sizeof(t), "hib"); break;
|
||||
case15(0xc1):snprintf(t, sizeof(t), "or r%u", op0 & 15); break;
|
||||
case15(0xd0):snprintf(t, sizeof(t), "inc r%u", op0 & 15); break;
|
||||
case (0xdf):snprintf(t, sizeof(t), "getc"); break;
|
||||
case15(0xe0):snprintf(t, sizeof(t), "dec r%u", op0 & 15); break;
|
||||
case (0xef):snprintf(t, sizeof(t), "getb"); break;
|
||||
case16(0xf0):snprintf(t, sizeof(t), "iwt r%u,#$%.2x%.2x", op0 & 15, op2 & 255, op1 & 255); break;
|
||||
}
|
||||
strcat(output, t);
|
||||
}
|
||||
@@ -94,56 +94,56 @@ auto GSU::disassembleALT0(char* output) -> void {
|
||||
auto GSU::disassembleALT1(char* output) -> void {
|
||||
char t[256] = "";
|
||||
switch(op0) {
|
||||
case (0x00): sprintf(t, "stop"); break;
|
||||
case (0x01): sprintf(t, "nop"); break;
|
||||
case (0x02): sprintf(t, "cache"); break;
|
||||
case (0x03): sprintf(t, "lsr"); break;
|
||||
case (0x04): sprintf(t, "rol"); break;
|
||||
case (0x05): sprintf(t, "bra %+d", (s8)op1); break;
|
||||
case (0x06): sprintf(t, "blt %+d", (s8)op1); break;
|
||||
case (0x07): sprintf(t, "bge %+d", (s8)op1); break;
|
||||
case (0x08): sprintf(t, "bne %+d", (s8)op1); break;
|
||||
case (0x09): sprintf(t, "beq %+d", (s8)op1); break;
|
||||
case (0x0a): sprintf(t, "bpl %+d", (s8)op1); break;
|
||||
case (0x0b): sprintf(t, "bmi %+d", (s8)op1); break;
|
||||
case (0x0c): sprintf(t, "bcc %+d", (s8)op1); break;
|
||||
case (0x0d): sprintf(t, "bcs %+d", (s8)op1); break;
|
||||
case (0x0e): sprintf(t, "bvc %+d", (s8)op1); break;
|
||||
case (0x0f): sprintf(t, "bvs %+d", (s8)op1); break;
|
||||
case16(0x10): sprintf(t, "to r%u", op0 & 15); break;
|
||||
case16(0x20): sprintf(t, "with r%u", op0 & 15); break;
|
||||
case12(0x30): sprintf(t, "stb (r%u)", op0 & 15); break;
|
||||
case (0x3c): sprintf(t, "loop"); break;
|
||||
case (0x3d): sprintf(t, "alt1"); break;
|
||||
case (0x3e): sprintf(t, "alt2"); break;
|
||||
case (0x3f): sprintf(t, "alt3"); break;
|
||||
case12(0x40): sprintf(t, "ldb (r%u)", op0 & 15); break;
|
||||
case (0x4c): sprintf(t, "rpix"); break;
|
||||
case (0x4d): sprintf(t, "swap"); break;
|
||||
case (0x4e): sprintf(t, "cmode"); break;
|
||||
case (0x4f): sprintf(t, "not"); break;
|
||||
case16(0x50): sprintf(t, "adc r%u", op0 & 15); break;
|
||||
case16(0x60): sprintf(t, "sbc r%u", op0 & 15); break;
|
||||
case (0x70): sprintf(t, "merge"); break;
|
||||
case15(0x71): sprintf(t, "bic r%u", op0 & 15); break;
|
||||
case16(0x80): sprintf(t, "umult r%u", op0 & 15); break;
|
||||
case (0x90): sprintf(t, "sbk"); break;
|
||||
case4 (0x91): sprintf(t, "link #%u", op0 & 15); break;
|
||||
case (0x95): sprintf(t, "sex"); break;
|
||||
case (0x96): sprintf(t, "div2"); break;
|
||||
case (0x97): sprintf(t, "ror"); break;
|
||||
case6 (0x98): sprintf(t, "ljmp r%u", op0 & 15); break;
|
||||
case (0x9e): sprintf(t, "lob"); break;
|
||||
case (0x9f): sprintf(t, "lmult"); break;
|
||||
case16(0xa0): sprintf(t, "lms r%u,(#$%.4x)", op0 & 15, op1 << 1); break;
|
||||
case16(0xb0): sprintf(t, "from r%u", op0 & 15); break;
|
||||
case (0xc0): sprintf(t, "hib"); break;
|
||||
case15(0xc1): sprintf(t, "xor r%u", op0 & 15); break;
|
||||
case15(0xd0): sprintf(t, "inc r%u", op0 & 15); break;
|
||||
case (0xdf): sprintf(t, "getc"); break;
|
||||
case15(0xe0): sprintf(t, "dec r%u", op0 & 15); break;
|
||||
case (0xef): sprintf(t, "getbh"); break;
|
||||
case16(0xf0): sprintf(t, "lm r%u", op0 & 15); break;
|
||||
case (0x00):snprintf(t, sizeof(t), "stop"); break;
|
||||
case (0x01):snprintf(t, sizeof(t), "nop"); break;
|
||||
case (0x02):snprintf(t, sizeof(t), "cache"); break;
|
||||
case (0x03):snprintf(t, sizeof(t), "lsr"); break;
|
||||
case (0x04):snprintf(t, sizeof(t), "rol"); break;
|
||||
case (0x05):snprintf(t, sizeof(t), "bra %+d", (s8)op1); break;
|
||||
case (0x06):snprintf(t, sizeof(t), "blt %+d", (s8)op1); break;
|
||||
case (0x07):snprintf(t, sizeof(t), "bge %+d", (s8)op1); break;
|
||||
case (0x08):snprintf(t, sizeof(t), "bne %+d", (s8)op1); break;
|
||||
case (0x09):snprintf(t, sizeof(t), "beq %+d", (s8)op1); break;
|
||||
case (0x0a):snprintf(t, sizeof(t), "bpl %+d", (s8)op1); break;
|
||||
case (0x0b):snprintf(t, sizeof(t), "bmi %+d", (s8)op1); break;
|
||||
case (0x0c):snprintf(t, sizeof(t), "bcc %+d", (s8)op1); break;
|
||||
case (0x0d):snprintf(t, sizeof(t), "bcs %+d", (s8)op1); break;
|
||||
case (0x0e):snprintf(t, sizeof(t), "bvc %+d", (s8)op1); break;
|
||||
case (0x0f):snprintf(t, sizeof(t), "bvs %+d", (s8)op1); break;
|
||||
case16(0x10):snprintf(t, sizeof(t), "to r%u", op0 & 15); break;
|
||||
case16(0x20):snprintf(t, sizeof(t), "with r%u", op0 & 15); break;
|
||||
case12(0x30):snprintf(t, sizeof(t), "stb (r%u)", op0 & 15); break;
|
||||
case (0x3c):snprintf(t, sizeof(t), "loop"); break;
|
||||
case (0x3d):snprintf(t, sizeof(t), "alt1"); break;
|
||||
case (0x3e):snprintf(t, sizeof(t), "alt2"); break;
|
||||
case (0x3f):snprintf(t, sizeof(t), "alt3"); break;
|
||||
case12(0x40):snprintf(t, sizeof(t), "ldb (r%u)", op0 & 15); break;
|
||||
case (0x4c):snprintf(t, sizeof(t), "rpix"); break;
|
||||
case (0x4d):snprintf(t, sizeof(t), "swap"); break;
|
||||
case (0x4e):snprintf(t, sizeof(t), "cmode"); break;
|
||||
case (0x4f):snprintf(t, sizeof(t), "not"); break;
|
||||
case16(0x50):snprintf(t, sizeof(t), "adc r%u", op0 & 15); break;
|
||||
case16(0x60):snprintf(t, sizeof(t), "sbc r%u", op0 & 15); break;
|
||||
case (0x70):snprintf(t, sizeof(t), "merge"); break;
|
||||
case15(0x71):snprintf(t, sizeof(t), "bic r%u", op0 & 15); break;
|
||||
case16(0x80):snprintf(t, sizeof(t), "umult r%u", op0 & 15); break;
|
||||
case (0x90):snprintf(t, sizeof(t), "sbk"); break;
|
||||
case4 (0x91):snprintf(t, sizeof(t), "link #%u", op0 & 15); break;
|
||||
case (0x95):snprintf(t, sizeof(t), "sex"); break;
|
||||
case (0x96):snprintf(t, sizeof(t), "div2"); break;
|
||||
case (0x97):snprintf(t, sizeof(t), "ror"); break;
|
||||
case6 (0x98):snprintf(t, sizeof(t), "ljmp r%u", op0 & 15); break;
|
||||
case (0x9e):snprintf(t, sizeof(t), "lob"); break;
|
||||
case (0x9f):snprintf(t, sizeof(t), "lmult"); break;
|
||||
case16(0xa0):snprintf(t, sizeof(t), "lms r%u,(#$%.4x)", op0 & 15, op1 << 1); break;
|
||||
case16(0xb0):snprintf(t, sizeof(t), "from r%u", op0 & 15); break;
|
||||
case (0xc0):snprintf(t, sizeof(t), "hib"); break;
|
||||
case15(0xc1):snprintf(t, sizeof(t), "xor r%u", op0 & 15); break;
|
||||
case15(0xd0):snprintf(t, sizeof(t), "inc r%u", op0 & 15); break;
|
||||
case (0xdf):snprintf(t, sizeof(t), "getc"); break;
|
||||
case15(0xe0):snprintf(t, sizeof(t), "dec r%u", op0 & 15); break;
|
||||
case (0xef):snprintf(t, sizeof(t), "getbh"); break;
|
||||
case16(0xf0):snprintf(t, sizeof(t), "lm r%u", op0 & 15); break;
|
||||
}
|
||||
strcat(output, t);
|
||||
}
|
||||
@@ -151,56 +151,56 @@ auto GSU::disassembleALT1(char* output) -> void {
|
||||
auto GSU::disassembleALT2(char* output) -> void {
|
||||
char t[256] = "";
|
||||
switch(op0) {
|
||||
case (0x00): sprintf(t, "stop"); break;
|
||||
case (0x01): sprintf(t, "nop"); break;
|
||||
case (0x02): sprintf(t, "cache"); break;
|
||||
case (0x03): sprintf(t, "lsr"); break;
|
||||
case (0x04): sprintf(t, "rol"); break;
|
||||
case (0x05): sprintf(t, "bra %+d", (s8)op1); break;
|
||||
case (0x06): sprintf(t, "blt %+d", (s8)op1); break;
|
||||
case (0x07): sprintf(t, "bge %+d", (s8)op1); break;
|
||||
case (0x08): sprintf(t, "bne %+d", (s8)op1); break;
|
||||
case (0x09): sprintf(t, "beq %+d", (s8)op1); break;
|
||||
case (0x0a): sprintf(t, "bpl %+d", (s8)op1); break;
|
||||
case (0x0b): sprintf(t, "bmi %+d", (s8)op1); break;
|
||||
case (0x0c): sprintf(t, "bcc %+d", (s8)op1); break;
|
||||
case (0x0d): sprintf(t, "bcs %+d", (s8)op1); break;
|
||||
case (0x0e): sprintf(t, "bvc %+d", (s8)op1); break;
|
||||
case (0x0f): sprintf(t, "bvs %+d", (s8)op1); break;
|
||||
case16(0x10): sprintf(t, "to r%u", op0 & 15); break;
|
||||
case16(0x20): sprintf(t, "with r%u", op0 & 15); break;
|
||||
case12(0x30): sprintf(t, "stw (r%u)", op0 & 15); break;
|
||||
case (0x3c): sprintf(t, "loop"); break;
|
||||
case (0x3d): sprintf(t, "alt1"); break;
|
||||
case (0x3e): sprintf(t, "alt2"); break;
|
||||
case (0x3f): sprintf(t, "alt3"); break;
|
||||
case12(0x40): sprintf(t, "ldw (r%u)", op0 & 15); break;
|
||||
case (0x4c): sprintf(t, "plot"); break;
|
||||
case (0x4d): sprintf(t, "swap"); break;
|
||||
case (0x4e): sprintf(t, "color"); break;
|
||||
case (0x4f): sprintf(t, "not"); break;
|
||||
case16(0x50): sprintf(t, "add #%u", op0 & 15); break;
|
||||
case16(0x60): sprintf(t, "sub #%u", op0 & 15); break;
|
||||
case (0x70): sprintf(t, "merge"); break;
|
||||
case15(0x71): sprintf(t, "and #%u", op0 & 15); break;
|
||||
case16(0x80): sprintf(t, "mult #%u", op0 & 15); break;
|
||||
case (0x90): sprintf(t, "sbk"); break;
|
||||
case4 (0x91): sprintf(t, "link #%u", op0 & 15); break;
|
||||
case (0x95): sprintf(t, "sex"); break;
|
||||
case (0x96): sprintf(t, "asr"); break;
|
||||
case (0x97): sprintf(t, "ror"); break;
|
||||
case6 (0x98): sprintf(t, "jmp r%u", op0 & 15); break;
|
||||
case (0x9e): sprintf(t, "lob"); break;
|
||||
case (0x9f): sprintf(t, "fmult"); break;
|
||||
case16(0xa0): sprintf(t, "sms r%u,(#$%.4x)", op0 & 15, op1 << 1); break;
|
||||
case16(0xb0): sprintf(t, "from r%u", op0 & 15); break;
|
||||
case (0xc0): sprintf(t, "hib"); break;
|
||||
case15(0xc1): sprintf(t, "or #%u", op0 & 15); break;
|
||||
case15(0xd0): sprintf(t, "inc r%u", op0 & 15); break;
|
||||
case (0xdf): sprintf(t, "ramb"); break;
|
||||
case15(0xe0): sprintf(t, "dec r%u", op0 & 15); break;
|
||||
case (0xef): sprintf(t, "getbl"); break;
|
||||
case16(0xf0): sprintf(t, "sm r%u", op0 & 15); break;
|
||||
case (0x00):snprintf(t, sizeof(t), "stop"); break;
|
||||
case (0x01):snprintf(t, sizeof(t), "nop"); break;
|
||||
case (0x02):snprintf(t, sizeof(t), "cache"); break;
|
||||
case (0x03):snprintf(t, sizeof(t), "lsr"); break;
|
||||
case (0x04):snprintf(t, sizeof(t), "rol"); break;
|
||||
case (0x05):snprintf(t, sizeof(t), "bra %+d", (s8)op1); break;
|
||||
case (0x06):snprintf(t, sizeof(t), "blt %+d", (s8)op1); break;
|
||||
case (0x07):snprintf(t, sizeof(t), "bge %+d", (s8)op1); break;
|
||||
case (0x08):snprintf(t, sizeof(t), "bne %+d", (s8)op1); break;
|
||||
case (0x09):snprintf(t, sizeof(t), "beq %+d", (s8)op1); break;
|
||||
case (0x0a):snprintf(t, sizeof(t), "bpl %+d", (s8)op1); break;
|
||||
case (0x0b):snprintf(t, sizeof(t), "bmi %+d", (s8)op1); break;
|
||||
case (0x0c):snprintf(t, sizeof(t), "bcc %+d", (s8)op1); break;
|
||||
case (0x0d):snprintf(t, sizeof(t), "bcs %+d", (s8)op1); break;
|
||||
case (0x0e):snprintf(t, sizeof(t), "bvc %+d", (s8)op1); break;
|
||||
case (0x0f):snprintf(t, sizeof(t), "bvs %+d", (s8)op1); break;
|
||||
case16(0x10):snprintf(t, sizeof(t), "to r%u", op0 & 15); break;
|
||||
case16(0x20):snprintf(t, sizeof(t), "with r%u", op0 & 15); break;
|
||||
case12(0x30):snprintf(t, sizeof(t), "stw (r%u)", op0 & 15); break;
|
||||
case (0x3c):snprintf(t, sizeof(t), "loop"); break;
|
||||
case (0x3d):snprintf(t, sizeof(t), "alt1"); break;
|
||||
case (0x3e):snprintf(t, sizeof(t), "alt2"); break;
|
||||
case (0x3f):snprintf(t, sizeof(t), "alt3"); break;
|
||||
case12(0x40):snprintf(t, sizeof(t), "ldw (r%u)", op0 & 15); break;
|
||||
case (0x4c):snprintf(t, sizeof(t), "plot"); break;
|
||||
case (0x4d):snprintf(t, sizeof(t), "swap"); break;
|
||||
case (0x4e):snprintf(t, sizeof(t), "color"); break;
|
||||
case (0x4f):snprintf(t, sizeof(t), "not"); break;
|
||||
case16(0x50):snprintf(t, sizeof(t), "add #%u", op0 & 15); break;
|
||||
case16(0x60):snprintf(t, sizeof(t), "sub #%u", op0 & 15); break;
|
||||
case (0x70):snprintf(t, sizeof(t), "merge"); break;
|
||||
case15(0x71):snprintf(t, sizeof(t), "and #%u", op0 & 15); break;
|
||||
case16(0x80):snprintf(t, sizeof(t), "mult #%u", op0 & 15); break;
|
||||
case (0x90):snprintf(t, sizeof(t), "sbk"); break;
|
||||
case4 (0x91):snprintf(t, sizeof(t), "link #%u", op0 & 15); break;
|
||||
case (0x95):snprintf(t, sizeof(t), "sex"); break;
|
||||
case (0x96):snprintf(t, sizeof(t), "asr"); break;
|
||||
case (0x97):snprintf(t, sizeof(t), "ror"); break;
|
||||
case6 (0x98):snprintf(t, sizeof(t), "jmp r%u", op0 & 15); break;
|
||||
case (0x9e):snprintf(t, sizeof(t), "lob"); break;
|
||||
case (0x9f):snprintf(t, sizeof(t), "fmult"); break;
|
||||
case16(0xa0):snprintf(t, sizeof(t), "sms r%u,(#$%.4x)", op0 & 15, op1 << 1); break;
|
||||
case16(0xb0):snprintf(t, sizeof(t), "from r%u", op0 & 15); break;
|
||||
case (0xc0):snprintf(t, sizeof(t), "hib"); break;
|
||||
case15(0xc1):snprintf(t, sizeof(t), "or #%u", op0 & 15); break;
|
||||
case15(0xd0):snprintf(t, sizeof(t), "inc r%u", op0 & 15); break;
|
||||
case (0xdf):snprintf(t, sizeof(t), "ramb"); break;
|
||||
case15(0xe0):snprintf(t, sizeof(t), "dec r%u", op0 & 15); break;
|
||||
case (0xef):snprintf(t, sizeof(t), "getbl"); break;
|
||||
case16(0xf0):snprintf(t, sizeof(t), "sm r%u", op0 & 15); break;
|
||||
}
|
||||
strcat(output, t);
|
||||
}
|
||||
@@ -208,56 +208,56 @@ auto GSU::disassembleALT2(char* output) -> void {
|
||||
auto GSU::disassembleALT3(char* output) -> void {
|
||||
char t[256] = "";
|
||||
switch(op0) {
|
||||
case (0x00): sprintf(t, "stop"); break;
|
||||
case (0x01): sprintf(t, "nop"); break;
|
||||
case (0x02): sprintf(t, "cache"); break;
|
||||
case (0x03): sprintf(t, "lsr"); break;
|
||||
case (0x04): sprintf(t, "rol"); break;
|
||||
case (0x05): sprintf(t, "bra %+d", (s8)op1); break;
|
||||
case (0x06): sprintf(t, "blt %+d", (s8)op1); break;
|
||||
case (0x07): sprintf(t, "bge %+d", (s8)op1); break;
|
||||
case (0x08): sprintf(t, "bne %+d", (s8)op1); break;
|
||||
case (0x09): sprintf(t, "beq %+d", (s8)op1); break;
|
||||
case (0x0a): sprintf(t, "bpl %+d", (s8)op1); break;
|
||||
case (0x0b): sprintf(t, "bmi %+d", (s8)op1); break;
|
||||
case (0x0c): sprintf(t, "bcc %+d", (s8)op1); break;
|
||||
case (0x0d): sprintf(t, "bcs %+d", (s8)op1); break;
|
||||
case (0x0e): sprintf(t, "bvc %+d", (s8)op1); break;
|
||||
case (0x0f): sprintf(t, "bvs %+d", (s8)op1); break;
|
||||
case16(0x10): sprintf(t, "to r%u", op0 & 15); break;
|
||||
case16(0x20): sprintf(t, "with r%u", op0 & 15); break;
|
||||
case12(0x30): sprintf(t, "stb (r%u)", op0 & 15); break;
|
||||
case (0x3c): sprintf(t, "loop"); break;
|
||||
case (0x3d): sprintf(t, "alt1"); break;
|
||||
case (0x3e): sprintf(t, "alt2"); break;
|
||||
case (0x3f): sprintf(t, "alt3"); break;
|
||||
case12(0x40): sprintf(t, "ldb (r%u)", op0 & 15); break;
|
||||
case (0x4c): sprintf(t, "rpix"); break;
|
||||
case (0x4d): sprintf(t, "swap"); break;
|
||||
case (0x4e): sprintf(t, "cmode"); break;
|
||||
case (0x4f): sprintf(t, "not"); break;
|
||||
case16(0x50): sprintf(t, "adc #%u", op0 & 15); break;
|
||||
case16(0x60): sprintf(t, "cmp r%u", op0 & 15); break;
|
||||
case (0x70): sprintf(t, "merge"); break;
|
||||
case15(0x71): sprintf(t, "bic #%u", op0 & 15); break;
|
||||
case16(0x80): sprintf(t, "umult #%u", op0 & 15); break;
|
||||
case (0x90): sprintf(t, "sbk"); break;
|
||||
case4 (0x91): sprintf(t, "link #%u", op0 & 15); break;
|
||||
case (0x95): sprintf(t, "sex"); break;
|
||||
case (0x96): sprintf(t, "div2"); break;
|
||||
case (0x97): sprintf(t, "ror"); break;
|
||||
case6 (0x98): sprintf(t, "ljmp r%u", op0 & 15); break;
|
||||
case (0x9e): sprintf(t, "lob"); break;
|
||||
case (0x9f): sprintf(t, "lmult"); break;
|
||||
case16(0xa0): sprintf(t, "lms r%u", op0 & 15); break;
|
||||
case16(0xb0): sprintf(t, "from r%u", op0 & 15); break;
|
||||
case (0xc0): sprintf(t, "hib"); break;
|
||||
case15(0xc1): sprintf(t, "xor #%u", op0 & 15); break;
|
||||
case15(0xd0): sprintf(t, "inc r%u", op0 & 15); break;
|
||||
case (0xdf): sprintf(t, "romb"); break;
|
||||
case15(0xe0): sprintf(t, "dec r%u", op0 & 15); break;
|
||||
case (0xef): sprintf(t, "getbs"); break;
|
||||
case16(0xf0): sprintf(t, "lm r%u", op0 & 15); break;
|
||||
case (0x00):snprintf(t, sizeof(t), "stop"); break;
|
||||
case (0x01):snprintf(t, sizeof(t), "nop"); break;
|
||||
case (0x02):snprintf(t, sizeof(t), "cache"); break;
|
||||
case (0x03):snprintf(t, sizeof(t), "lsr"); break;
|
||||
case (0x04):snprintf(t, sizeof(t), "rol"); break;
|
||||
case (0x05):snprintf(t, sizeof(t), "bra %+d", (s8)op1); break;
|
||||
case (0x06):snprintf(t, sizeof(t), "blt %+d", (s8)op1); break;
|
||||
case (0x07):snprintf(t, sizeof(t), "bge %+d", (s8)op1); break;
|
||||
case (0x08):snprintf(t, sizeof(t), "bne %+d", (s8)op1); break;
|
||||
case (0x09):snprintf(t, sizeof(t), "beq %+d", (s8)op1); break;
|
||||
case (0x0a):snprintf(t, sizeof(t), "bpl %+d", (s8)op1); break;
|
||||
case (0x0b):snprintf(t, sizeof(t), "bmi %+d", (s8)op1); break;
|
||||
case (0x0c):snprintf(t, sizeof(t), "bcc %+d", (s8)op1); break;
|
||||
case (0x0d):snprintf(t, sizeof(t), "bcs %+d", (s8)op1); break;
|
||||
case (0x0e):snprintf(t, sizeof(t), "bvc %+d", (s8)op1); break;
|
||||
case (0x0f):snprintf(t, sizeof(t), "bvs %+d", (s8)op1); break;
|
||||
case16(0x10):snprintf(t, sizeof(t), "to r%u", op0 & 15); break;
|
||||
case16(0x20):snprintf(t, sizeof(t), "with r%u", op0 & 15); break;
|
||||
case12(0x30):snprintf(t, sizeof(t), "stb (r%u)", op0 & 15); break;
|
||||
case (0x3c):snprintf(t, sizeof(t), "loop"); break;
|
||||
case (0x3d):snprintf(t, sizeof(t), "alt1"); break;
|
||||
case (0x3e):snprintf(t, sizeof(t), "alt2"); break;
|
||||
case (0x3f):snprintf(t, sizeof(t), "alt3"); break;
|
||||
case12(0x40):snprintf(t, sizeof(t), "ldb (r%u)", op0 & 15); break;
|
||||
case (0x4c):snprintf(t, sizeof(t), "rpix"); break;
|
||||
case (0x4d):snprintf(t, sizeof(t), "swap"); break;
|
||||
case (0x4e):snprintf(t, sizeof(t), "cmode"); break;
|
||||
case (0x4f):snprintf(t, sizeof(t), "not"); break;
|
||||
case16(0x50):snprintf(t, sizeof(t), "adc #%u", op0 & 15); break;
|
||||
case16(0x60):snprintf(t, sizeof(t), "cmp r%u", op0 & 15); break;
|
||||
case (0x70):snprintf(t, sizeof(t), "merge"); break;
|
||||
case15(0x71):snprintf(t, sizeof(t), "bic #%u", op0 & 15); break;
|
||||
case16(0x80):snprintf(t, sizeof(t), "umult #%u", op0 & 15); break;
|
||||
case (0x90):snprintf(t, sizeof(t), "sbk"); break;
|
||||
case4 (0x91):snprintf(t, sizeof(t), "link #%u", op0 & 15); break;
|
||||
case (0x95):snprintf(t, sizeof(t), "sex"); break;
|
||||
case (0x96):snprintf(t, sizeof(t), "div2"); break;
|
||||
case (0x97):snprintf(t, sizeof(t), "ror"); break;
|
||||
case6 (0x98):snprintf(t, sizeof(t), "ljmp r%u", op0 & 15); break;
|
||||
case (0x9e):snprintf(t, sizeof(t), "lob"); break;
|
||||
case (0x9f):snprintf(t, sizeof(t), "lmult"); break;
|
||||
case16(0xa0):snprintf(t, sizeof(t), "lms r%u", op0 & 15); break;
|
||||
case16(0xb0):snprintf(t, sizeof(t), "from r%u", op0 & 15); break;
|
||||
case (0xc0):snprintf(t, sizeof(t), "hib"); break;
|
||||
case15(0xc1):snprintf(t, sizeof(t), "xor #%u", op0 & 15); break;
|
||||
case15(0xd0):snprintf(t, sizeof(t), "inc r%u", op0 & 15); break;
|
||||
case (0xdf):snprintf(t, sizeof(t), "romb"); break;
|
||||
case15(0xe0):snprintf(t, sizeof(t), "dec r%u", op0 & 15); break;
|
||||
case (0xef):snprintf(t, sizeof(t), "getbs"); break;
|
||||
case16(0xf0):snprintf(t, sizeof(t), "lm r%u", op0 & 15); break;
|
||||
}
|
||||
strcat(output, t);
|
||||
}
|
||||
|
||||
@@ -338,28 +338,27 @@ template<u32 Size> auto M68000::instructionBTST(EffectiveAddress with) -> void {
|
||||
}
|
||||
|
||||
auto M68000::instructionCHK(DataRegister compare, EffectiveAddress maximum) -> void {
|
||||
auto source = read<Word>(maximum);
|
||||
auto target = read<Word>(compare);
|
||||
auto result = (n64)target - source;
|
||||
r.c = sign<Word>(result >> 1) < 0;
|
||||
r.v = sign<Word>((target ^ source) & (target ^ result)) < 0;
|
||||
r.z = clip<Word>(result) == 0;
|
||||
r.n = sign<Word>(result) < 0;
|
||||
r.n ^= r.v;
|
||||
prefetch();
|
||||
auto source = read<Word>(compare);
|
||||
auto target = read<Word>(maximum);
|
||||
CMP<Word>(source, target);
|
||||
idle(2);
|
||||
bool bound = r.n || r.v;
|
||||
CMP<Word>(0, source);
|
||||
idle(2);
|
||||
|
||||
if(!r.n && !r.z) {
|
||||
if(bound) {
|
||||
prefetched();
|
||||
return exception(Exception::BoundsCheck, Vector::BoundsCheck);
|
||||
}
|
||||
|
||||
r.z = clip<Word>(target) == 0;
|
||||
r.n = sign<Word>(target) < 0;
|
||||
idle(2);
|
||||
|
||||
if(r.n) {
|
||||
idle(2);
|
||||
prefetched();
|
||||
return exception(Exception::BoundsCheck, Vector::BoundsCheck);
|
||||
}
|
||||
|
||||
idle(6);
|
||||
prefetch();
|
||||
}
|
||||
|
||||
template<u32 Size> auto M68000::instructionCLR(EffectiveAddress with) -> void {
|
||||
|
||||
@@ -43,7 +43,6 @@ auto M68000::power() -> void {
|
||||
auto M68000::supervisor() -> bool {
|
||||
if(r.s) return true;
|
||||
|
||||
r.pc -= 4;
|
||||
exception(Exception::Unprivileged, Vector::Unprivileged);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ auto SH2::Recompiler::emit(u32 address) -> Block* {
|
||||
u16 instruction = instructions[index++];
|
||||
if(callInstructionPrologue) {
|
||||
mov32(reg(1), imm(instruction));
|
||||
call(&SH2::instructionPrologue);
|
||||
call(&SH2::instructionPrologueTrampoline);
|
||||
}
|
||||
auto branch = emitInstruction(instruction);
|
||||
inDelaySlot = branch == Branch::Slot;
|
||||
|
||||
@@ -65,8 +65,8 @@ auto SH2::power(bool reset) -> void {
|
||||
|
||||
if constexpr(Accuracy::Recompiler) {
|
||||
if(!reset) {
|
||||
auto buffer = ares::Memory::FixedAllocator::get().tryAcquire(64_MiB);
|
||||
recompiler.allocator.resize(64_MiB, bump_allocator::executable, buffer);
|
||||
auto buffer = ares::Memory::FixedAllocator::get().tryAcquire(32_MiB);
|
||||
recompiler.allocator.resize(32_MiB, bump_allocator::executable, buffer);
|
||||
}
|
||||
recompiler.reset();
|
||||
}
|
||||
|
||||
@@ -249,6 +249,10 @@ struct SH2 {
|
||||
s32 cyclesUntilRecompilerExit = 0;
|
||||
s32 recompilerStepCycles = 0;
|
||||
|
||||
auto instructionPrologueTrampoline(u16 instruction) -> void {
|
||||
return instructionPrologue(instruction); //virtual function call
|
||||
}
|
||||
|
||||
struct Recompiler : recompiler::generic {
|
||||
SH2& self;
|
||||
Recompiler(SH2& self) : self(self), generic(allocator) {}
|
||||
@@ -279,7 +283,8 @@ struct SH2 {
|
||||
auto reset() -> void {
|
||||
generation = 0;
|
||||
blocks.reset();
|
||||
for(u32 index : range(1 << 24)) pools[index] = nullptr;
|
||||
pools.reallocate(1 << 24);
|
||||
pools.fill();
|
||||
}
|
||||
|
||||
auto invalidateCached() -> void {
|
||||
@@ -307,7 +312,7 @@ struct SH2 {
|
||||
bump_allocator allocator;
|
||||
hashset<BlockHashPair> blocks;
|
||||
u16 instructions[1 << 7];
|
||||
Pool* pools[1 << 24];
|
||||
vector<Pool*> pools;
|
||||
} recompiler{*this};
|
||||
|
||||
#include "sh7604/sh7604.hpp"
|
||||
|
||||
@@ -105,8 +105,6 @@ auto SH2::Cache::purge() -> void {
|
||||
}
|
||||
|
||||
auto SH2::Cache::power() -> void {
|
||||
purge(4 * 256);
|
||||
|
||||
for(n6 n : range(64)) {
|
||||
if(n.bit(5) == 1 && n.bit(4) == 1 && n.bit(3) == 1) { lruSelect[n] = 0; continue; }
|
||||
if(n.bit(5) == 0 && n.bit(2) == 1 && n.bit(1) == 1) { lruSelect[n] = 1; continue; }
|
||||
|
||||
@@ -216,7 +216,7 @@
|
||||
//watchdog timer
|
||||
struct WDT {
|
||||
//timer.cpp
|
||||
auto run() -> void;
|
||||
auto run(int cycles) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
@@ -282,7 +282,7 @@
|
||||
maybe<SH2&> self;
|
||||
|
||||
//timer.cpp
|
||||
auto run() -> void;
|
||||
auto run(int cycles) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
@@ -1,43 +1,45 @@
|
||||
auto SH2::FRT::run() -> void {
|
||||
auto SH2::FRT::run(int cycles) -> void {
|
||||
static constexpr u32 frequencies[4] = {8, 32, 128, 1};
|
||||
auto frequency = frequencies[tcr.cks];
|
||||
|
||||
if(++counter >= frequencies[tcr.cks]) {
|
||||
counter -= frequencies[tcr.cks];
|
||||
if(++frc == 0) {
|
||||
ftcsr.ovf = 1;
|
||||
if(tier.ovie) {
|
||||
pendingOutputIRQ = 1;
|
||||
}
|
||||
}
|
||||
if(frc == ocra) {
|
||||
ftcsr.ocfa = 1;
|
||||
if(tier.ociae) {
|
||||
pendingOutputIRQ = 1;
|
||||
}
|
||||
if(ftcsr.cclra) {
|
||||
frc = 0;
|
||||
}
|
||||
}
|
||||
if(frc == ocrb) {
|
||||
ftcsr.ocfb = 1;
|
||||
if(tier.ocibe) {
|
||||
pendingOutputIRQ = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
counter += cycles;
|
||||
auto increments = counter / frequency;
|
||||
counter -= increments * frequency;
|
||||
|
||||
if (!increments) return;
|
||||
|
||||
auto prevFrc = frc;
|
||||
frc += increments;
|
||||
|
||||
ftcsr.ovf |= (frc < prevFrc);
|
||||
ftcsr.ocfa |= (u32(ocra - 1 - prevFrc) < increments);
|
||||
ftcsr.ocfb |= (u32(ocrb - 1 - prevFrc) < increments);
|
||||
|
||||
pendingOutputIRQ |= (tier.ovie & ftcsr.ovf) |
|
||||
(tier.ociae & ftcsr.ocfa) |
|
||||
(tier.ocibe & ftcsr.ocfb);
|
||||
|
||||
if (ftcsr.cclra && ftcsr.ocfa) frc = 0;
|
||||
}
|
||||
auto SH2::WDT::run() -> void {
|
||||
|
||||
auto SH2::WDT::run(int cycles) -> void {
|
||||
static constexpr u32 frequencies[8] = {2, 64, 128, 256, 512, 1024, 4096, 8192};
|
||||
|
||||
if(!wtcsr.tme) return;
|
||||
if(wtcsr.wtit) return; // Watchdog mode currently unsupported
|
||||
if(!wtcsr.tme || wtcsr.wtit) return; //watchdog mode (wtit) currently unsupported
|
||||
|
||||
if(++counter >= frequencies[wtcsr.cks]) {
|
||||
counter -= frequencies[wtcsr.cks];
|
||||
auto frequency = frequencies[wtcsr.cks];
|
||||
|
||||
if(++wtcnt == 0) {
|
||||
wtcsr.ovf = 1;
|
||||
pendingIRQ = 1;
|
||||
}
|
||||
counter += cycles;
|
||||
auto increments = counter / frequency;
|
||||
counter %= frequency;
|
||||
|
||||
if(!increments) return;
|
||||
|
||||
auto prevWtcnt = wtcnt;
|
||||
wtcnt += increments;
|
||||
|
||||
if (wtcnt < prevWtcnt) {
|
||||
wtcsr.ovf = 1;
|
||||
pendingIRQ = 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
ares_components(z80 tms9918 sn76489)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
cv
|
||||
UNITY #
|
||||
cv.cpp
|
||||
PRIMARY #
|
||||
cpu/cpu.cpp
|
||||
vdp/vdp.cpp
|
||||
psg/psg.cpp
|
||||
system/system.cpp
|
||||
cartridge/cartridge.cpp
|
||||
controller/controller.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
cv
|
||||
INCLUDED #
|
||||
cv.hpp
|
||||
CMakeLists.txt
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
cv
|
||||
INCLUDED #
|
||||
cartridge/cartridge.hpp
|
||||
cartridge/serialization.cpp
|
||||
cartridge/slot.cpp
|
||||
cartridge/slot.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
cv
|
||||
INCLUDED #
|
||||
cartridge/board/board.cpp
|
||||
cartridge/board/board.hpp
|
||||
cartridge/board/coleco.cpp
|
||||
cartridge/board/megacart.cpp
|
||||
cartridge/board/xin1.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
cv
|
||||
INCLUDED #
|
||||
controller/controller.hpp
|
||||
controller/port.cpp
|
||||
controller/port.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
cv
|
||||
INCLUDED #
|
||||
controller/gamepad/gamepad.cpp
|
||||
controller/gamepad/gamepad.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
cv
|
||||
INCLUDED #
|
||||
cpu/cpu.hpp
|
||||
cpu/debugger.cpp
|
||||
cpu/memory.cpp
|
||||
cpu/serialization.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
cv
|
||||
INCLUDED #
|
||||
psg/psg.hpp
|
||||
psg/serialization.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
cv
|
||||
INCLUDED #
|
||||
system/controls.cpp
|
||||
system/serialization.cpp
|
||||
system/system.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
cv
|
||||
INCLUDED #
|
||||
vdp/color.cpp
|
||||
vdp/serialization.cpp
|
||||
vdp/vdp.hpp
|
||||
)
|
||||
@@ -0,0 +1,8 @@
|
||||
//started: 2019-02-19
|
||||
|
||||
#include <cv/cpu/cpu.cpp>
|
||||
#include <cv/vdp/vdp.cpp>
|
||||
#include <cv/psg/psg.cpp>
|
||||
#include <cv/system/system.cpp>
|
||||
#include <cv/cartridge/cartridge.cpp>
|
||||
#include <cv/controller/controller.cpp>
|
||||
+4
-4
@@ -44,10 +44,10 @@ auto VDP::frame() -> void {
|
||||
screen->setSize(284, 243);
|
||||
screen->setViewport(0, 0, 284, 243);
|
||||
} else {
|
||||
int x = 16;
|
||||
int y = 16;
|
||||
int width = 284 - 32;
|
||||
int height = 243 - 32;
|
||||
int x = 13;
|
||||
int y = 27;
|
||||
int width = 284 - 28;
|
||||
int height = 243 - 51;
|
||||
|
||||
screen->setSize(width, height);
|
||||
screen->setViewport(x, y, width, height);
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
ares_components(
|
||||
mos6502
|
||||
ym2149
|
||||
ym2413
|
||||
m24c
|
||||
sst39sf0x0
|
||||
)
|
||||
|
||||
target_link_libraries(ares PRIVATE ymfm)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
fc
|
||||
UNITY #
|
||||
fc.cpp
|
||||
PRIMARY #
|
||||
system/system.cpp
|
||||
controller/controller.cpp
|
||||
expansion/expansion.cpp
|
||||
cartridge/cartridge.cpp
|
||||
cpu/cpu.cpp
|
||||
apu/apu.cpp
|
||||
ppu/ppu.cpp
|
||||
fds/fds.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(CORE fc INCLUDED fc.hpp CMakeLists.txt)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
fc
|
||||
INCLUDED #
|
||||
apu/apu.hpp
|
||||
apu/dmc.cpp
|
||||
apu/envelope.cpp
|
||||
apu/framecounter.cpp
|
||||
apu/length.cpp
|
||||
apu/noise.cpp
|
||||
apu/pulse.cpp
|
||||
apu/serialization.cpp
|
||||
apu/sweep.cpp
|
||||
apu/triangle.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
fc
|
||||
INCLUDED #
|
||||
cartridge/cartridge.hpp
|
||||
cartridge/serialization.cpp
|
||||
cartridge/slot.cpp
|
||||
cartridge/slot.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
fc
|
||||
INCLUDED #
|
||||
cartridge/board/action53.cpp
|
||||
cartridge/board/ave-nina-001.cpp
|
||||
cartridge/board/ave-nina-06.cpp
|
||||
cartridge/board/bandai-74161.cpp
|
||||
cartridge/board/bandai-fcg.cpp
|
||||
cartridge/board/bandai-karaoke.cpp
|
||||
cartridge/board/bandai-lz93d50.cpp
|
||||
cartridge/board/bandai-oeka.cpp
|
||||
cartridge/board/board.cpp
|
||||
cartridge/board/board.hpp
|
||||
cartridge/board/camerica-bf909x.cpp
|
||||
cartridge/board/colordreams-74x377.cpp
|
||||
cartridge/board/gtrom.cpp
|
||||
cartridge/board/hvc-axrom.cpp
|
||||
cartridge/board/hvc-bnrom.cpp
|
||||
cartridge/board/hvc-cnrom.cpp
|
||||
cartridge/board/hvc-exrom.cpp
|
||||
cartridge/board/hvc-fmr.cpp
|
||||
cartridge/board/hvc-fxrom.cpp
|
||||
cartridge/board/hvc-gxrom.cpp
|
||||
cartridge/board/hvc-hkrom.cpp
|
||||
cartridge/board/hvc-nrom.cpp
|
||||
cartridge/board/hvc-pxrom.cpp
|
||||
cartridge/board/hvc-sxrom.cpp
|
||||
cartridge/board/hvc-txrom.cpp
|
||||
cartridge/board/hvc-uxrom.cpp
|
||||
cartridge/board/inl-nsf.cpp
|
||||
cartridge/board/irem-g101.cpp
|
||||
cartridge/board/irem-h3001.cpp
|
||||
cartridge/board/irem-lrog017.cpp
|
||||
cartridge/board/irem-tam-s1.cpp
|
||||
cartridge/board/jaleco-jf05.cpp
|
||||
cartridge/board/jaleco-jf11.cpp
|
||||
cartridge/board/jaleco-jf16.cpp
|
||||
cartridge/board/jaleco-jf17.cpp
|
||||
cartridge/board/jaleco-jf23.cpp
|
||||
cartridge/board/konami-vrc1.cpp
|
||||
cartridge/board/konami-vrc2.cpp
|
||||
cartridge/board/konami-vrc3.cpp
|
||||
cartridge/board/konami-vrc4.cpp
|
||||
cartridge/board/konami-vrc5.cpp
|
||||
cartridge/board/konami-vrc6.cpp
|
||||
cartridge/board/konami-vrc7.cpp
|
||||
cartridge/board/magicfloor.cpp
|
||||
cartridge/board/mlt-action52.cpp
|
||||
cartridge/board/namco-118.cpp
|
||||
cartridge/board/namco-163.cpp
|
||||
cartridge/board/namco-340.cpp
|
||||
cartridge/board/sunsoft-1.cpp
|
||||
cartridge/board/sunsoft-2.cpp
|
||||
cartridge/board/sunsoft-3.cpp
|
||||
cartridge/board/sunsoft-4.cpp
|
||||
cartridge/board/sunsoft-5b.cpp
|
||||
cartridge/board/taito-tc0190.cpp
|
||||
cartridge/board/taito-tc0690.cpp
|
||||
cartridge/board/taito-x1-005.cpp
|
||||
cartridge/board/taito-x1-017.cpp
|
||||
cartridge/board/tengen-80003x.cpp
|
||||
cartridge/board/unl-bmc.cpp
|
||||
cartridge/board/unl-sachen-74ls374n.cpp
|
||||
cartridge/board/unl-sachen-sa0037.cpp
|
||||
cartridge/board/unl-txc.cpp
|
||||
cartridge/board/unrom-512.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
fc
|
||||
INCLUDED #
|
||||
cartridge/board/ic/jv001.hpp
|
||||
cartridge/board/ic/txc05-00002-010.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
fc
|
||||
INCLUDED #
|
||||
controller/controller.hpp
|
||||
controller/port.cpp
|
||||
controller/port.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
fc
|
||||
INCLUDED #
|
||||
controller/gamepad/gamepad.cpp
|
||||
controller/gamepad/gamepad.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
fc
|
||||
INCLUDED #
|
||||
cpu/cpu.hpp
|
||||
cpu/debugger.cpp
|
||||
cpu/memory.cpp
|
||||
cpu/serialization.cpp
|
||||
cpu/timing.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
fc
|
||||
INCLUDED #
|
||||
expansion/expansion.hpp
|
||||
expansion/port.cpp
|
||||
expansion/port.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
fc
|
||||
INCLUDED #
|
||||
expansion/epsm/epsm.cpp
|
||||
expansion/epsm/epsm.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
fc
|
||||
INCLUDED #
|
||||
expansion/family-keyboard/family-keyboard.cpp
|
||||
expansion/family-keyboard/family-keyboard.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
fc
|
||||
INCLUDED #
|
||||
fds/audio.cpp
|
||||
fds/audio.hpp
|
||||
fds/drive.cpp
|
||||
fds/drive.hpp
|
||||
fds/fds.hpp
|
||||
fds/serialization.cpp
|
||||
fds/timer.cpp
|
||||
fds/timer.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
fc
|
||||
INCLUDED #
|
||||
ppu/color.cpp
|
||||
ppu/debugger.cpp
|
||||
ppu/memory.cpp
|
||||
ppu/ppu.hpp
|
||||
ppu/render.cpp
|
||||
ppu/scroll.cpp
|
||||
ppu/serialization.cpp
|
||||
ppu/sprite.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
fc
|
||||
INCLUDED #
|
||||
system/controls.cpp
|
||||
system/serialization.cpp
|
||||
system/system.hpp
|
||||
)
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <fc/fc.hpp>
|
||||
#include <ares/resource/resource.hpp>
|
||||
|
||||
namespace ares::Famicom {
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ auto CPU::read(n16 address) -> n8 {
|
||||
}
|
||||
|
||||
auto CPU::write(n16 address, n8 data) -> void {
|
||||
writeBus(address, io.openBus = data);
|
||||
writeBus(address, data);
|
||||
io.openBus = data;
|
||||
step(rate());
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
//started: 2011-09-05
|
||||
|
||||
#include <fc/system/system.cpp>
|
||||
#include <fc/controller/controller.cpp>
|
||||
#include <fc/expansion/expansion.cpp>
|
||||
#include <fc/cartridge/cartridge.cpp>
|
||||
#include <fc/cpu/cpu.cpp>
|
||||
#include <fc/apu/apu.cpp>
|
||||
#include <fc/ppu/ppu.cpp>
|
||||
#include <fc/fds/fds.cpp>
|
||||
+6
-3
@@ -81,6 +81,12 @@ auto FDS::connect() -> void {
|
||||
auto FDS::disconnect() -> void {
|
||||
if(!node) return;
|
||||
|
||||
save();
|
||||
pak.reset();
|
||||
node.reset();
|
||||
}
|
||||
|
||||
auto FDS::save() -> void {
|
||||
if(disk1.sideA)
|
||||
if(auto fp = pak->write("disk1.sideA")) {
|
||||
disk1.sideA.save(fp);
|
||||
@@ -100,9 +106,6 @@ auto FDS::disconnect() -> void {
|
||||
if(auto fp = pak->write("disk2.sideB")) {
|
||||
disk2.sideB.save(fp);
|
||||
}
|
||||
|
||||
pak.reset();
|
||||
node.reset();
|
||||
}
|
||||
|
||||
auto FDS::change(string value) -> void {
|
||||
|
||||
@@ -34,6 +34,7 @@ struct FDS {
|
||||
auto change(string value) -> void;
|
||||
auto change() -> void;
|
||||
|
||||
auto save() -> void;
|
||||
auto poll() -> void;
|
||||
auto main() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
@@ -81,7 +81,10 @@ auto PPU::writeIO(n16 address, n8 data) -> void {
|
||||
|
||||
//PPUCTRL
|
||||
case 0:
|
||||
scroll.nametable = data.bit(0,1);
|
||||
scroll.nametable = data.bit(0,1);
|
||||
if (io.lx == 257 && rendering())
|
||||
var.nametableX = cpu.io.openBus.bit(0);
|
||||
|
||||
io.vramIncrement = data.bit(2) ? 32 : 1;
|
||||
io.spriteAddress = data.bit(3) ? 0x1000 : 0x0000;
|
||||
io.bgAddress = data.bit(4) ? 0x1000 : 0x0000;
|
||||
@@ -141,6 +144,9 @@ auto PPU::writeIO(n16 address, n8 data) -> void {
|
||||
if(scroll.latch++ == 0) {
|
||||
scroll.fineX = data.bit(0,2);
|
||||
scroll.tileX = data.bit(3,7);
|
||||
|
||||
if (io.lx == 257 && rendering())
|
||||
var.tileX = cpu.io.openBus.bit(3,7);
|
||||
} else {
|
||||
scroll.fineY = data.bit(0,2);
|
||||
scroll.tileY = data.bit(3,7);
|
||||
@@ -151,6 +157,8 @@ auto PPU::writeIO(n16 address, n8 data) -> void {
|
||||
case 6:
|
||||
if(scroll.latch++ == 0) {
|
||||
scroll.addressHi = data.bit(0,5);
|
||||
if (io.lx == 257 && rendering())
|
||||
var.nametable = cpu.io.openBus.bit(2,3);
|
||||
} else {
|
||||
scroll.addressLo = data.bit(0,7);
|
||||
scroll.transferDelay = 3;
|
||||
|
||||
+7
-4
@@ -97,11 +97,14 @@ auto PPU::frame() -> void {
|
||||
screen->setViewport(0, 0, 283, displayHeight());
|
||||
} else {
|
||||
int x = 16;
|
||||
int y = 8;
|
||||
int width = 283 - 33;
|
||||
int height = displayHeight() - 16;
|
||||
int y = 0;
|
||||
int width = 283 - 27;
|
||||
int height = displayHeight() - 2;
|
||||
|
||||
if(Region::PAL()) height -= 48;
|
||||
if(Region::PAL()) {
|
||||
x += 2;
|
||||
height -= 46;
|
||||
}
|
||||
|
||||
screen->setSize(width, height);
|
||||
screen->setViewport(x, y, width, height);
|
||||
|
||||
@@ -25,8 +25,8 @@ auto PPU::renderPixel() -> void {
|
||||
u32 objectPalette = 0;
|
||||
bool objectPriority = 0;
|
||||
|
||||
//PAL systems blank the topmost scanline and the first and last 2px of active display
|
||||
if(Region::PAL()) if(io.ly == 0 || x < 2 || x > 254) return;
|
||||
//PAL systems technically blank the topmost scanline and a 2px column on each side of active display.
|
||||
if(Region::PAL()) if(io.ly == 0 || x < 2 || x > 253) return;
|
||||
|
||||
palette |= latch.tiledataLo & mask ? 1 : 0;
|
||||
palette |= latch.tiledataHi & mask ? 2 : 0;
|
||||
|
||||
@@ -85,6 +85,7 @@ auto System::load(Node::System& root, string name) -> bool {
|
||||
auto System::save() -> void {
|
||||
if(!node) return;
|
||||
cartridge.save();
|
||||
if(fds.present) fds.save();
|
||||
}
|
||||
|
||||
auto System::unload() -> void {
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
ares_components(sm83 m93lcx6)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
gb
|
||||
UNITY #
|
||||
gb.cpp
|
||||
PRIMARY #
|
||||
system/system.cpp
|
||||
cartridge/cartridge.cpp
|
||||
bus/bus.cpp
|
||||
cpu/cpu.cpp
|
||||
ppu/ppu.cpp
|
||||
apu/apu.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
gb
|
||||
INCLUDED #
|
||||
gb.hpp
|
||||
CMakeLists.txt
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
gb
|
||||
INCLUDED #
|
||||
apu/apu.hpp
|
||||
apu/io.cpp
|
||||
apu/noise.cpp
|
||||
apu/sequencer.cpp
|
||||
apu/serialization.cpp
|
||||
apu/square1.cpp
|
||||
apu/square2.cpp
|
||||
apu/wave.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
gb
|
||||
INCLUDED #
|
||||
bus/bus.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
gb
|
||||
INCLUDED #
|
||||
cartridge/cartridge.hpp
|
||||
cartridge/memory.cpp
|
||||
cartridge/serialization.cpp
|
||||
cartridge/slot.cpp
|
||||
cartridge/slot.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
gb
|
||||
INCLUDED #
|
||||
cartridge/board/board.cpp
|
||||
cartridge/board/board.hpp
|
||||
cartridge/board/huc1.cpp
|
||||
cartridge/board/huc3.cpp
|
||||
cartridge/board/linear.cpp
|
||||
cartridge/board/mbc1.cpp
|
||||
cartridge/board/mbc1m.cpp
|
||||
cartridge/board/mbc2.cpp
|
||||
cartridge/board/mbc3.cpp
|
||||
cartridge/board/mbc5.cpp
|
||||
cartridge/board/mbc6.cpp
|
||||
cartridge/board/mbc7.cpp
|
||||
cartridge/board/mmm01.cpp
|
||||
cartridge/board/none.cpp
|
||||
cartridge/board/tama.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
gb
|
||||
INCLUDED #
|
||||
cpu/cpu.hpp
|
||||
cpu/debugger.cpp
|
||||
cpu/io.cpp
|
||||
cpu/memory.cpp
|
||||
cpu/serialization.cpp
|
||||
cpu/timing.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
gb
|
||||
INCLUDED #
|
||||
system/controls.cpp
|
||||
system/serialization.cpp
|
||||
system/system.hpp
|
||||
)
|
||||
@@ -0,0 +1,8 @@
|
||||
//started: 2010-12-27
|
||||
|
||||
#include <gb/system/system.cpp>
|
||||
#include <gb/cartridge/cartridge.cpp>
|
||||
#include <gb/bus/bus.cpp>
|
||||
#include <gb/cpu/cpu.cpp>
|
||||
#include <gb/ppu/ppu.cpp>
|
||||
#include <gb/apu/apu.cpp>
|
||||
@@ -0,0 +1,122 @@
|
||||
ares_components(arm7tdmi)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
gba
|
||||
UNITY #
|
||||
gba.cpp
|
||||
PRIMARY #
|
||||
memory/memory.cpp
|
||||
system/system.cpp
|
||||
cartridge/cartridge.cpp
|
||||
player/player.cpp
|
||||
cpu/cpu.cpp
|
||||
display/display.cpp
|
||||
ppu/ppu.cpp
|
||||
apu/apu.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
gba
|
||||
INCLUDED #
|
||||
gba.hpp
|
||||
CMakeLists.txt
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
gba
|
||||
INCLUDED #
|
||||
apu/apu.hpp
|
||||
apu/fifo.cpp
|
||||
apu/io.cpp
|
||||
apu/noise.cpp
|
||||
apu/sequencer.cpp
|
||||
apu/serialization.cpp
|
||||
apu/square.cpp
|
||||
apu/square1.cpp
|
||||
apu/square2.cpp
|
||||
apu/wave.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
gba
|
||||
INCLUDED #
|
||||
cartridge/cartridge.hpp
|
||||
cartridge/eeprom.cpp
|
||||
cartridge/flash.cpp
|
||||
cartridge/memory.hpp
|
||||
cartridge/mrom.cpp
|
||||
cartridge/serialization.cpp
|
||||
cartridge/slot.cpp
|
||||
cartridge/slot.hpp
|
||||
cartridge/sram.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
gba
|
||||
INCLUDED #
|
||||
cpu/bus.cpp
|
||||
cpu/cpu.hpp
|
||||
cpu/debugger.cpp
|
||||
cpu/dma.cpp
|
||||
cpu/io.cpp
|
||||
cpu/keypad.cpp
|
||||
cpu/memory.cpp
|
||||
cpu/prefetch.cpp
|
||||
cpu/serialization.cpp
|
||||
cpu/timer.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
gba
|
||||
INCLUDED #
|
||||
display/display.hpp
|
||||
display/io.cpp
|
||||
display/serialization.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
gba
|
||||
INCLUDED #
|
||||
memory/memory.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
gba
|
||||
INCLUDED #
|
||||
player/player.hpp
|
||||
player/serialization.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
gba
|
||||
INCLUDED #
|
||||
ppu/background.cpp
|
||||
ppu/color.cpp
|
||||
ppu/dac.cpp
|
||||
ppu/debugger.cpp
|
||||
ppu/io.cpp
|
||||
ppu/memory.cpp
|
||||
ppu/object.cpp
|
||||
ppu/ppu.hpp
|
||||
ppu/serialization.cpp
|
||||
ppu/window.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
gba
|
||||
INCLUDED #
|
||||
system/bios.cpp
|
||||
system/controls.cpp
|
||||
system/serialization.cpp
|
||||
system/system.hpp
|
||||
)
|
||||
@@ -7,6 +7,7 @@ ares.objects += ares-gba-player
|
||||
ares.objects += ares-gba-cpu
|
||||
ares.objects += ares-gba-ppu
|
||||
ares.objects += ares-gba-apu
|
||||
ares.objects += ares-gba-display
|
||||
|
||||
$(object.path)/ares-gba-memory.o: $(ares.path)/gba/memory/memory.cpp
|
||||
$(object.path)/ares-gba-system.o: $(ares.path)/gba/system/system.cpp
|
||||
@@ -15,3 +16,4 @@ $(object.path)/ares-gba-player.o: $(ares.path)/gba/player/player.cpp
|
||||
$(object.path)/ares-gba-cpu.o: $(ares.path)/gba/cpu/cpu.cpp
|
||||
$(object.path)/ares-gba-ppu.o: $(ares.path)/gba/ppu/ppu.cpp
|
||||
$(object.path)/ares-gba-apu.o: $(ares.path)/gba/apu/apu.cpp
|
||||
$(object.path)/ares-gba-display.o: $(ares.path)/gba/display/display.cpp
|
||||
+1
-1
@@ -142,7 +142,7 @@ auto APU::readIO(n32 address) -> n8 {
|
||||
|
||||
}
|
||||
|
||||
return cpu.pipeline.fetch.instruction.byte(address & 1);
|
||||
return cpu.openBus.get(Byte, address);
|
||||
}
|
||||
|
||||
auto APU::writeIO(n32 address, n8 data) -> void {
|
||||
|
||||
+143
-44
@@ -1,42 +1,76 @@
|
||||
auto CPU::sleep() -> void {
|
||||
dmaRun();
|
||||
ARM7TDMI::irq = irq.synchronizer;
|
||||
prefetchStep(1);
|
||||
}
|
||||
|
||||
template <bool UseDebugger>
|
||||
inline auto CPU::getBus(u32 mode, n32 address) -> n32 {
|
||||
u32 clocks = _wait(mode, address);
|
||||
u32 word = pipeline.fetch.instruction;
|
||||
u32 word;
|
||||
|
||||
if(address >= 0x1000'0000) {
|
||||
if constexpr(!UseDebugger) prefetchStep(clocks);
|
||||
} else if(address & 0x0800'0000) {
|
||||
if(memory.biosSwap && address < 0x0400'0000) address ^= 0x0200'0000;
|
||||
|
||||
switch(address >> 24) {
|
||||
|
||||
case 0x00: case 0x01:
|
||||
if constexpr(!UseDebugger) prefetchStep(1);
|
||||
word = bios.read(mode, address);
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
if constexpr(!UseDebugger) prefetchStep(waitEWRAM(mode));
|
||||
word = readEWRAM(mode, address);
|
||||
break;
|
||||
|
||||
case 0x03:
|
||||
if constexpr(!UseDebugger) prefetchStep(1);
|
||||
word = readIWRAM(mode, address);
|
||||
break;
|
||||
|
||||
case 0x04:
|
||||
if constexpr(!UseDebugger) prefetchStep(1);
|
||||
if((address & 0xffff'fc00) == 0x0400'0000) word = bus.io[address & 0x3ff]->readIO(mode, address);
|
||||
else if((address & 0xff00'ffff) == 0x0400'0800) word = ((IO*)this)->readIO(mode, 0x0400'0800 | (address & 3));
|
||||
else return openBus.get(mode, address);
|
||||
break;
|
||||
|
||||
//timings for VRAM and PRAM are handled in memory.cpp
|
||||
case 0x05: word = readPRAM<UseDebugger>(mode, address); break;
|
||||
case 0x06: word = readVRAM<UseDebugger>(mode, address); break;
|
||||
|
||||
case 0x07:
|
||||
if constexpr(!UseDebugger) prefetchStep(1);
|
||||
word = ppu.readOAM(mode, address);
|
||||
break;
|
||||
|
||||
case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
|
||||
if(mode & Prefetch && wait.prefetch) {
|
||||
dmaRun();
|
||||
prefetchSync(address);
|
||||
word = prefetchRead(mode);
|
||||
prefetchStep(1);
|
||||
word = prefetchRead();
|
||||
if(mode & Word) word |= prefetchRead() << 16;
|
||||
} else {
|
||||
if constexpr(!UseDebugger) prefetchReset();
|
||||
if constexpr(!UseDebugger) step(clocks);
|
||||
if constexpr(!UseDebugger) step(waitCartridge(mode, address));
|
||||
word = cartridge.read(mode, address);
|
||||
}
|
||||
} else {
|
||||
if(memory.biosSwap && address < 0x0400'0000) address ^= 0x0200'0000;
|
||||
if constexpr(!UseDebugger) prefetchStep(clocks);
|
||||
if(auto result = platform->cheat(address)) return *result;
|
||||
if(address < 0x0200'0000) word = bios.read(mode, address);
|
||||
else if(address < 0x0300'0000) word = readEWRAM(mode, address);
|
||||
else if(address < 0x0400'0000) word = readIWRAM(mode, address);
|
||||
else if(address >= 0x0700'0000) word = ppu.readOAM(mode, address);
|
||||
else if(address >= 0x0600'0000) word = ppu.readVRAM(mode, address);
|
||||
else if(address >= 0x0500'0000) word = ppu.readPRAM(mode, address);
|
||||
else if((address & 0xffff'fc00) == 0x0400'0000) word = bus.io[address & 0x3ff]->readIO(mode, address);
|
||||
else if((address & 0xff00'ffff) == 0x0400'0800) word = ((IO*)this)->readIO(mode, 0x0400'0800 | (address & 3));
|
||||
break;
|
||||
|
||||
default:
|
||||
if constexpr(!UseDebugger) prefetchStep(1);
|
||||
return openBus.get(mode, address);
|
||||
|
||||
}
|
||||
|
||||
openBus.set(mode, address, word);
|
||||
|
||||
if(auto result = platform->cheat(address)) return *result;
|
||||
return word;
|
||||
}
|
||||
|
||||
auto CPU::get(u32 mode, n32 address) -> n32 {
|
||||
dmaRun();
|
||||
ARM7TDMI::irq = irq.synchronizer;
|
||||
return getBus<false>(mode, address);
|
||||
}
|
||||
|
||||
@@ -45,36 +79,73 @@ auto CPU::getDebugger(u32 mode, n32 address) -> n32 {
|
||||
}
|
||||
|
||||
auto CPU::set(u32 mode, n32 address, n32 word) -> void {
|
||||
u32 clocks = _wait(mode, address);
|
||||
dmaRun();
|
||||
ARM7TDMI::irq = irq.synchronizer;
|
||||
|
||||
if(address >= 0x1000'0000) {
|
||||
prefetchStep(clocks);
|
||||
} else if(address & 0x0800'0000) {
|
||||
prefetchReset();
|
||||
step(clocks);
|
||||
cartridge.write(mode, address, word);
|
||||
} else {
|
||||
if(memory.biosSwap && address < 0x0400'0000) address ^= 0x0200'0000;
|
||||
prefetchStep(clocks);
|
||||
if(address < 0x0200'0000) bios.write(mode, address, word);
|
||||
else if(address < 0x0300'0000) writeEWRAM(mode, address, word);
|
||||
else if(address < 0x0400'0000) writeIWRAM(mode, address, word);
|
||||
else if(address >= 0x0700'0000) ppu.writeOAM(mode, address, word);
|
||||
else if(address >= 0x0600'0000) ppu.writeVRAM(mode, address, word);
|
||||
else if(address >= 0x0500'0000) ppu.writePRAM(mode, address, word);
|
||||
else if((address & 0xffff'fc00) == 0x0400'0000) bus.io[address & 0x3ff]->writeIO(mode, address, word);
|
||||
if(memory.biosSwap && address < 0x0400'0000) address ^= 0x0200'0000;
|
||||
|
||||
switch(address >> 24) {
|
||||
|
||||
case 0x00: case 0x01:
|
||||
prefetchStep(1);
|
||||
bios.write(mode, address, word);
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
prefetchStep(waitEWRAM(mode));
|
||||
writeEWRAM(mode, address, word);
|
||||
break;
|
||||
|
||||
case 0x03:
|
||||
prefetchStep(1);
|
||||
writeIWRAM(mode, address, word);
|
||||
break;
|
||||
|
||||
case 0x04:
|
||||
prefetchStep(1);
|
||||
if((address & 0xffff'fc00) == 0x0400'0000) bus.io[address & 0x3ff]->writeIO(mode, address, word);
|
||||
else if((address & 0xff00'ffff) == 0x0400'0800) ((IO*)this)->writeIO(mode, 0x0400'0800 | (address & 3), word);
|
||||
break;
|
||||
|
||||
//timings for VRAM and PRAM are handled in memory.cpp
|
||||
case 0x05: writePRAM(mode, address, word); break;
|
||||
case 0x06: writeVRAM(mode, address, word); break;
|
||||
|
||||
case 0x07:
|
||||
prefetchStep(1);
|
||||
synchronize(ppu);
|
||||
ppu.writeOAM(mode, address, word);
|
||||
break;
|
||||
|
||||
case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
|
||||
prefetchReset();
|
||||
step(waitCartridge(mode, address));
|
||||
cartridge.write(mode, address, word);
|
||||
break;
|
||||
|
||||
default:
|
||||
prefetchStep(1);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
openBus.set(mode, address, word);
|
||||
}
|
||||
|
||||
auto CPU::_wait(u32 mode, n32 address) -> u32 {
|
||||
if(address >= 0x1000'0000) return 1; //unmapped
|
||||
if(address < 0x0200'0000) return 1;
|
||||
if(address < 0x0300'0000) return (16 - memory.ewramWait) * (mode & Word ? 2 : 1);
|
||||
if(address < 0x0500'0000) return 1;
|
||||
if(address < 0x0700'0000) return mode & Word ? 2 : 1;
|
||||
if(address < 0x0800'0000) return 1;
|
||||
auto CPU::lock() -> void {
|
||||
dmaRun();
|
||||
context.busLocked = true;
|
||||
}
|
||||
|
||||
auto CPU::unlock() -> void {
|
||||
context.busLocked = false;
|
||||
}
|
||||
|
||||
auto CPU::waitEWRAM(u32 mode) -> u32 {
|
||||
return (16 - memory.ewramWait) * (mode & Word ? 2 : 1);
|
||||
}
|
||||
|
||||
auto CPU::waitCartridge(u32 mode, n32 address) -> u32 {
|
||||
static u32 timings[] = {5, 4, 3, 9};
|
||||
u32 n = timings[wait.nwait[address >> 25 & 3]];
|
||||
u32 s = wait.swait[address >> 25 & 3];
|
||||
@@ -93,3 +164,31 @@ auto CPU::_wait(u32 mode, n32 address) -> u32 {
|
||||
if(mode & Word) clocks += s; //16-bit bus requires two transfers for words
|
||||
return clocks;
|
||||
}
|
||||
|
||||
auto CPU::OpenBus::get(u32 mode, n32 address) -> n32 {
|
||||
if(mode & Word) address &= ~3;
|
||||
if(mode & Half) address &= ~1;
|
||||
return data >> (8 * (address & 3));
|
||||
}
|
||||
|
||||
auto CPU::OpenBus::set(u32 mode, n32 address, n32 word) -> void {
|
||||
if(address >> 24 == 0x3) {
|
||||
//open bus from IWRAM only overwrites part of the last IWRAM value accessed
|
||||
if(mode & Word) {
|
||||
iwramData = word;
|
||||
} else if(mode & Half) {
|
||||
if(address & 2) {
|
||||
iwramData.bit(16,31) = (n16)word;
|
||||
} else {
|
||||
iwramData.bit( 0,15) = (n16)word;
|
||||
}
|
||||
} else if(mode & Byte) {
|
||||
iwramData.byte(address & 3) = (n8)word;
|
||||
}
|
||||
data = iwramData;
|
||||
} else {
|
||||
if(mode & Byte) word = (word & 0xff) * 0x01010101;
|
||||
if(mode & Half) word = (word & 0xffff) * 0x00010001;
|
||||
data = word;
|
||||
}
|
||||
}
|
||||
+7
-10
@@ -30,8 +30,6 @@ auto CPU::unload() -> void {
|
||||
}
|
||||
|
||||
auto CPU::main() -> void {
|
||||
ARM7TDMI::irq = irq.synchronizer[0];
|
||||
|
||||
if(stopped()) {
|
||||
if(!keypad.conditionMet) {
|
||||
stepIRQ();
|
||||
@@ -45,6 +43,7 @@ auto CPU::main() -> void {
|
||||
}
|
||||
|
||||
if(halted()) {
|
||||
dmaRun();
|
||||
if(!(irq.enable[0] & irq.flag[0])) {
|
||||
return step(4);
|
||||
}
|
||||
@@ -57,7 +56,7 @@ auto CPU::main() -> void {
|
||||
}
|
||||
|
||||
auto CPU::dmaRun() -> void {
|
||||
if(!context.dmaActive && !context.prefetchActive) {
|
||||
if(!context.dmaActive && !context.busLocked) {
|
||||
context.dmaActive = true;
|
||||
while(dma[0].run() | dma[1].run() | dma[2].run() | dma[3].run());
|
||||
if(context.dmaRan) {
|
||||
@@ -74,17 +73,15 @@ auto CPU::setInterruptFlag(u32 source) -> void {
|
||||
}
|
||||
|
||||
inline auto CPU::stepIRQ() -> void {
|
||||
irq.synchronizer[0] = irq.synchronizer[1];
|
||||
irq.synchronizer[1] = irq.ime && (irq.enable[0] & irq.flag[0]);
|
||||
irq.synchronizer = irq.ime[0] && (irq.enable[0] & irq.flag[0]);
|
||||
irq.enable[0] = irq.enable[1];
|
||||
irq.flag[0] = irq.flag[1];
|
||||
irq.ime[0] = irq.ime[1];
|
||||
}
|
||||
|
||||
auto CPU::step(u32 clocks) -> void {
|
||||
if(!clocks) return;
|
||||
|
||||
dmaRun();
|
||||
|
||||
dma[0].waiting = max(0, dma[0].waiting - (s32)clocks);
|
||||
dma[1].waiting = max(0, dma[1].waiting - (s32)clocks);
|
||||
dma[2].waiting = max(0, dma[2].waiting - (s32)clocks);
|
||||
@@ -111,13 +108,13 @@ auto CPU::step(u32 clocks) -> void {
|
||||
}
|
||||
|
||||
Thread::step(clocks);
|
||||
Thread::synchronize(ppu, player);
|
||||
Thread::synchronize(display, player);
|
||||
|
||||
//occasionally synchronize with APU in case CPU has not recently interacted with it
|
||||
//occasionally synchronize with PPU and APU in case CPU has not recently interacted with them
|
||||
static u32 counter = 0;
|
||||
counter += clocks;
|
||||
if(counter >= 1024) {
|
||||
Thread::synchronize(apu);
|
||||
Thread::synchronize(ppu, apu);
|
||||
counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
+22
-5
@@ -56,7 +56,7 @@ struct CPU : ARM7TDMI, Thread, IO {
|
||||
auto prefetchSync(n32 address) -> void;
|
||||
auto prefetchStep(u32 clocks) -> void;
|
||||
auto prefetchReset() -> void;
|
||||
auto prefetchRead(u32 mode) -> n32;
|
||||
auto prefetchRead() -> n16;
|
||||
|
||||
//bus.cpp
|
||||
auto sleep() -> void override;
|
||||
@@ -64,7 +64,10 @@ struct CPU : ARM7TDMI, Thread, IO {
|
||||
auto get(u32 mode, n32 address) -> n32 override;
|
||||
auto getDebugger(u32 mode, n32 address) -> n32 override;
|
||||
auto set(u32 mode, n32 address, n32 word) -> void override;
|
||||
auto _wait(u32 mode, n32 address) -> u32;
|
||||
auto lock() -> void override;
|
||||
auto unlock() -> void override;
|
||||
auto waitEWRAM(u32 mode) -> u32;
|
||||
auto waitCartridge(u32 mode, n32 address) -> u32;
|
||||
|
||||
//io.cpp
|
||||
auto readIO(n32 address) -> n8 override;
|
||||
@@ -76,6 +79,12 @@ struct CPU : ARM7TDMI, Thread, IO {
|
||||
auto readEWRAM(u32 mode, n32 address) -> n32;
|
||||
auto writeEWRAM(u32 mode, n32 address, n32 word) -> void;
|
||||
|
||||
template<bool UseDebugger> auto readPRAM(u32 mode, n32 address) -> n32;
|
||||
auto writePRAM(u32 mode, n32 address, n32 word) -> void;
|
||||
|
||||
template<bool UseDebugger> auto readVRAM(u32 mode, n32 address) -> n32;
|
||||
auto writeVRAM(u32 mode, n32 address, n32 word) -> void;
|
||||
|
||||
//dma.cpp
|
||||
auto dmaVblank() -> void;
|
||||
auto dmaHblank() -> void;
|
||||
@@ -205,8 +214,8 @@ struct CPU : ARM7TDMI, Thread, IO {
|
||||
} joybus;
|
||||
|
||||
struct IRQ {
|
||||
n1 ime;
|
||||
n1 synchronizer[2];
|
||||
n1 ime[2];
|
||||
n1 synchronizer;
|
||||
n16 enable[2];
|
||||
n16 flag[2];
|
||||
} irq;
|
||||
@@ -227,6 +236,13 @@ struct CPU : ARM7TDMI, Thread, IO {
|
||||
n4 unknown2;
|
||||
} memory;
|
||||
|
||||
struct OpenBus {
|
||||
auto get(u32 mode, n32 address) -> n32;
|
||||
auto set(u32 mode, n32 address, n32 word) -> void;
|
||||
n32 data;
|
||||
n32 iwramData;
|
||||
} openBus;
|
||||
|
||||
struct {
|
||||
auto empty() const { return addr == load; }
|
||||
auto full() const { return load - addr == 16; }
|
||||
@@ -236,6 +252,7 @@ struct CPU : ARM7TDMI, Thread, IO {
|
||||
n32 addr; //read location of slot buffer
|
||||
n32 load; //write location of slot buffer
|
||||
i32 wait = 1; //number of clocks before next slot load
|
||||
n1 stopped = 1;
|
||||
} prefetch;
|
||||
|
||||
struct Context {
|
||||
@@ -246,8 +263,8 @@ struct CPU : ARM7TDMI, Thread, IO {
|
||||
n1 dmaRan;
|
||||
n1 dmaRomAccess;
|
||||
n1 dmaActive;
|
||||
n1 prefetchActive;
|
||||
n1 timerLatched;
|
||||
n1 busLocked;
|
||||
} context;
|
||||
};
|
||||
|
||||
|
||||
+3
-3
@@ -212,7 +212,7 @@ auto CPU::readIO(n32 address) -> n8 {
|
||||
case 0x0400'0207: return 0;
|
||||
|
||||
//IME
|
||||
case 0x0400'0208: return irq.ime;
|
||||
case 0x0400'0208: return irq.ime[0];
|
||||
case 0x0400'0209: return 0;
|
||||
|
||||
//zero
|
||||
@@ -244,7 +244,7 @@ auto CPU::readIO(n32 address) -> n8 {
|
||||
|
||||
}
|
||||
|
||||
return cpu.pipeline.fetch.instruction.byte(address & 1);
|
||||
return cpu.openBus.get(Byte, address);
|
||||
}
|
||||
|
||||
auto CPU::writeIO(n32 address, n8 data) -> void {
|
||||
@@ -442,7 +442,7 @@ auto CPU::writeIO(n32 address, n8 data) -> void {
|
||||
return;
|
||||
|
||||
//IME
|
||||
case 0x0400'0208: irq.ime = data.bit(0); return;
|
||||
case 0x0400'0208: irq.ime[1] = data.bit(0); return;
|
||||
case 0x0400'0209: return;
|
||||
|
||||
//POSTFLG, HALTCNT
|
||||
|
||||
@@ -47,3 +47,61 @@ auto CPU::writeEWRAM(u32 mode, n32 address, n32 word) -> void {
|
||||
|
||||
ewram[address & 0x3ffff] = word;
|
||||
}
|
||||
|
||||
template <bool UseDebugger>
|
||||
auto CPU::readPRAM(u32 mode, n32 address) -> n32 {
|
||||
if(mode & Word) return readPRAM<UseDebugger>(Half, address & ~2) << 0 | readPRAM<UseDebugger>(Half, address | 2) << 16;
|
||||
|
||||
//stall until PPU is no longer accessing PRAM (minimum 1 cycle)
|
||||
do {
|
||||
prefetchStep(1);
|
||||
synchronize(ppu);
|
||||
} while(ppu.pramContention());
|
||||
|
||||
return ppu.readPRAM(mode, address);
|
||||
}
|
||||
|
||||
auto CPU::writePRAM(u32 mode, n32 address, n32 word) -> void {
|
||||
if(mode & Word) {
|
||||
writePRAM(Half, address & ~2, word >> 0);
|
||||
writePRAM(Half, address | 2, word >> 16);
|
||||
return;
|
||||
}
|
||||
|
||||
//stall until PPU is no longer accessing PRAM (minimum 1 cycle)
|
||||
do {
|
||||
prefetchStep(1);
|
||||
synchronize(ppu);
|
||||
} while(ppu.pramContention());
|
||||
|
||||
ppu.writePRAM(mode, address, word);
|
||||
}
|
||||
|
||||
template <bool UseDebugger>
|
||||
auto CPU::readVRAM(u32 mode, n32 address) -> n32 {
|
||||
if(mode & Word) return readVRAM<UseDebugger>(Half, address & ~2) << 0 | readVRAM<UseDebugger>(Half, address | 2) << 16;
|
||||
|
||||
//stall until PPU is no longer accessing VRAM (minimum 1 cycle)
|
||||
do {
|
||||
prefetchStep(1);
|
||||
synchronize(ppu);
|
||||
} while(ppu.vramContention(address));
|
||||
|
||||
return ppu.readVRAM(mode, address);
|
||||
}
|
||||
|
||||
auto CPU::writeVRAM(u32 mode, n32 address, n32 word) -> void {
|
||||
if(mode & Word) {
|
||||
writeVRAM(Half, address & ~2, word >> 0);
|
||||
writeVRAM(Half, address | 2, word >> 16);
|
||||
return;
|
||||
}
|
||||
|
||||
//stall until PPU is no longer accessing VRAM (minimum 1 cycle)
|
||||
do {
|
||||
prefetchStep(1);
|
||||
synchronize(ppu);
|
||||
} while(ppu.vramContention(address));
|
||||
|
||||
ppu.writeVRAM(mode, address, word);
|
||||
}
|
||||
+16
-23
@@ -3,52 +3,45 @@ auto CPU::prefetchSync(n32 address) -> void {
|
||||
|
||||
if(prefetch.wait == 1) step(1);
|
||||
|
||||
prefetch.stopped = false;
|
||||
prefetch.addr = address;
|
||||
prefetch.load = address;
|
||||
prefetch.wait = _wait(Half | Nonsequential, prefetch.load);
|
||||
prefetch.wait = waitCartridge(Half | Nonsequential, prefetch.load);
|
||||
}
|
||||
|
||||
auto CPU::prefetchStep(u32 clocks) -> void {
|
||||
step(clocks);
|
||||
if(!wait.prefetch || context.dmaRomAccess || (prefetch.addr == 0)) return;
|
||||
if(!wait.prefetch || context.dmaRomAccess || prefetch.stopped) return;
|
||||
|
||||
while(!prefetch.full() && clocks--) {
|
||||
if(--prefetch.wait) continue;
|
||||
prefetch.slot[prefetch.load >> 1 & 7] = cartridge.read(Half, prefetch.load);
|
||||
prefetch.load += 2;
|
||||
prefetch.wait = _wait(Half | Sequential, prefetch.load);
|
||||
prefetch.wait = waitCartridge(Half | Sequential, prefetch.load);
|
||||
}
|
||||
|
||||
if(prefetch.full()) prefetch.stopped = true;
|
||||
}
|
||||
|
||||
auto CPU::prefetchReset() -> void {
|
||||
if(prefetch.wait == 1) step(1);
|
||||
|
||||
prefetch.stopped = true;
|
||||
prefetch.wait = 0;
|
||||
prefetch.addr = 0;
|
||||
prefetch.load = 0;
|
||||
prefetch.wait = 0;
|
||||
}
|
||||
|
||||
auto CPU::prefetchRead(u32 mode) -> n32 {
|
||||
//run prefetcher for 1 cycle or until buffer contains an instruction, blocking DMA
|
||||
context.prefetchActive = 1;
|
||||
if(prefetch.empty() && (mode & Word)) {
|
||||
prefetchStep(prefetch.wait + _wait(Half | Sequential, prefetch.load + 2));
|
||||
} else if(prefetch.empty() && (mode & Half)) {
|
||||
prefetchStep(prefetch.wait);
|
||||
} else if((prefetch.size() == 1) && (mode & Word)) {
|
||||
prefetchStep(prefetch.wait);
|
||||
} else {
|
||||
prefetchStep(1);
|
||||
}
|
||||
context.prefetchActive = 0;
|
||||
auto CPU::prefetchRead() -> n16 {
|
||||
if(prefetch.empty()) prefetchStep(prefetch.wait);
|
||||
|
||||
if(prefetch.full()) prefetch.wait = _wait(Half | Sequential, prefetch.load);
|
||||
|
||||
n32 word = prefetch.slot[prefetch.addr >> 1 & 7];
|
||||
n16 word = prefetch.slot[prefetch.addr >> 1 & 7];
|
||||
prefetch.addr += 2;
|
||||
if(mode & Word) {
|
||||
word |= prefetch.slot[prefetch.addr >> 1 & 7] << 16;
|
||||
prefetch.addr += 2;
|
||||
|
||||
if(prefetch.stopped && prefetch.empty()) {
|
||||
prefetch.stopped = false;
|
||||
prefetch.wait = waitCartridge(Half | Nonsequential, prefetch.load);
|
||||
}
|
||||
|
||||
return word;
|
||||
}
|
||||
@@ -83,8 +83,8 @@ auto CPU::serialize(serializer& s) -> void {
|
||||
s(joybus.sendFlag);
|
||||
s(joybus.generalFlag);
|
||||
|
||||
s(irq.ime);
|
||||
for(auto& flag : irq.synchronizer) s(flag);
|
||||
for(auto& flag : irq.ime) s(flag);
|
||||
s(irq.synchronizer);
|
||||
for(auto& flag : irq.enable) s(flag);
|
||||
for(auto& flag : irq.flag) s(flag);
|
||||
|
||||
@@ -100,10 +100,14 @@ auto CPU::serialize(serializer& s) -> void {
|
||||
s(memory.ewramWait);
|
||||
s(memory.unknown2);
|
||||
|
||||
s(openBus.data);
|
||||
s(openBus.iwramData);
|
||||
|
||||
s(prefetch.slot);
|
||||
s(prefetch.addr);
|
||||
s(prefetch.load);
|
||||
s(prefetch.wait);
|
||||
s(prefetch.stopped);
|
||||
|
||||
s(context.clock);
|
||||
s(context.halted);
|
||||
@@ -112,6 +116,6 @@ auto CPU::serialize(serializer& s) -> void {
|
||||
s(context.dmaRan);
|
||||
s(context.dmaRomAccess);
|
||||
s(context.dmaActive);
|
||||
s(context.prefetchActive);
|
||||
s(context.timerLatched);
|
||||
s(context.busLocked);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
#include <gba/gba.hpp>
|
||||
|
||||
//The only PPU state the CPU needs on every cycle is raised IRQs and DMAs,
|
||||
//which occur independently of the render process.
|
||||
//Display exists to put these events on a separate thread,
|
||||
//so the CPU and PPU can run out-of-order.
|
||||
|
||||
//hdraw: 1006 cycles
|
||||
//hblank: 226 cycles
|
||||
//scanline: 1232 cycles
|
||||
|
||||
//vdraw: 160 scanlines (197120 cycles)
|
||||
//vblank: 68 scanlines ( 83776 cycles)
|
||||
//frame: 228 scanlines (280896 cycles)
|
||||
|
||||
namespace ares::GameBoyAdvance {
|
||||
|
||||
Display display;
|
||||
#include "io.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto Display::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Object>("Display");
|
||||
}
|
||||
|
||||
auto Display::unload() -> void {
|
||||
node.reset();
|
||||
}
|
||||
|
||||
auto Display::step(u32 clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
Thread::synchronize(cpu);
|
||||
}
|
||||
|
||||
auto Display::main() -> void {
|
||||
cpu.keypad.run();
|
||||
|
||||
io.vblank = io.vcounter >= 160 && io.vcounter <= 226;
|
||||
|
||||
step(1);
|
||||
|
||||
io.vcoincidence = io.vcounter == io.vcompare;
|
||||
|
||||
if(io.vcounter == 160) {
|
||||
if(io.irqvblank) cpu.setInterruptFlag(CPU::Interrupt::VBlank);
|
||||
}
|
||||
|
||||
step(1);
|
||||
|
||||
if(io.irqvcoincidence) {
|
||||
if(io.vcoincidence) cpu.setInterruptFlag(CPU::Interrupt::VCoincidence);
|
||||
}
|
||||
|
||||
if(io.vcounter == 160) {
|
||||
cpu.dmaVblank();
|
||||
}
|
||||
|
||||
step(3);
|
||||
|
||||
if(io.vcounter == 162) {
|
||||
if(videoCapture) cpu.dma[3].enable = 0;
|
||||
videoCapture = !videoCapture && cpu.dma[3].timingMode == 3 && cpu.dma[3].enable;
|
||||
}
|
||||
if(io.vcounter >= 2 && io.vcounter < 162 && videoCapture) cpu.dmaHDMA();
|
||||
|
||||
step(1002);
|
||||
|
||||
io.hblank = 1;
|
||||
|
||||
step(1);
|
||||
if(io.irqhblank) cpu.setInterruptFlag(CPU::Interrupt::HBlank);
|
||||
|
||||
step(1);
|
||||
if(io.vcounter < 160) cpu.dmaHblank();
|
||||
|
||||
step(223);
|
||||
io.hblank = 0;
|
||||
if(++io.vcounter == 228) io.vcounter = 0;
|
||||
}
|
||||
|
||||
auto Display::power() -> void {
|
||||
Thread::create(system.frequency(), {&Display::main, this});
|
||||
|
||||
for(u32 n = 0x004; n <= 0x007; n++) bus.io[n] = this;
|
||||
|
||||
io = {};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
struct Display : Thread, IO {
|
||||
Node::Object node;
|
||||
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
auto step(u32 clocks) -> void;
|
||||
auto main() -> void;
|
||||
|
||||
auto power() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readIO(n32 address) -> n8;
|
||||
auto writeIO(n32 address, n8 byte) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct IO {
|
||||
n1 vblank;
|
||||
n1 hblank;
|
||||
n1 vcoincidence;
|
||||
n1 irqvblank;
|
||||
n1 irqhblank;
|
||||
n1 irqvcoincidence;
|
||||
n8 vcompare;
|
||||
|
||||
n16 vcounter;
|
||||
} io;
|
||||
|
||||
n1 videoCapture = 0;
|
||||
};
|
||||
|
||||
extern Display display;
|
||||
@@ -0,0 +1,40 @@
|
||||
auto Display::readIO(n32 address) -> n8 {
|
||||
switch(address) {
|
||||
|
||||
//DISPSTAT
|
||||
case 0x0400'0004: return (
|
||||
io.vblank << 0
|
||||
| io.hblank << 1
|
||||
| io.vcoincidence << 2
|
||||
| io.irqvblank << 3
|
||||
| io.irqhblank << 4
|
||||
| io.irqvcoincidence << 5
|
||||
);
|
||||
case 0x0400'0005: return (
|
||||
io.vcompare
|
||||
);
|
||||
|
||||
//VCOUNT
|
||||
case 0x0400'0006: return io.vcounter.byte(0);
|
||||
case 0x0400'0007: return io.vcounter.byte(1);
|
||||
|
||||
}
|
||||
|
||||
return cpu.openBus.get(Byte, address);
|
||||
}
|
||||
|
||||
auto Display::writeIO(n32 address, n8 data) -> void {
|
||||
switch(address) {
|
||||
|
||||
//DISPSTAT
|
||||
case 0x0400'0004:
|
||||
io.irqvblank = data.bit(3);
|
||||
io.irqhblank = data.bit(4);
|
||||
io.irqvcoincidence = data.bit(5);
|
||||
return;
|
||||
case 0x0400'0005:
|
||||
io.vcompare = data;
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
auto Display::serialize(serializer& s) -> void {
|
||||
Thread::serialize(s);
|
||||
|
||||
s(io.vblank);
|
||||
s(io.hblank);
|
||||
s(io.vcoincidence);
|
||||
s(io.irqvblank);
|
||||
s(io.irqhblank);
|
||||
s(io.irqvcoincidence);
|
||||
s(io.vcompare);
|
||||
s(io.vcounter);
|
||||
|
||||
s(videoCapture);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
//started: 2012-03-19
|
||||
|
||||
#include <gba/memory/memory.cpp>
|
||||
#include <gba/system/system.cpp>
|
||||
#include <gba/cartridge/cartridge.cpp>
|
||||
#include <gba/player/player.cpp>
|
||||
#include <gba/cpu/cpu.cpp>
|
||||
#include <gba/display/display.cpp>
|
||||
#include <gba/ppu/ppu.cpp>
|
||||
#include <gba/apu/apu.cpp>
|
||||
@@ -35,4 +35,5 @@ namespace ares::GameBoyAdvance {
|
||||
#include <gba/cpu/cpu.hpp>
|
||||
#include <gba/ppu/ppu.hpp>
|
||||
#include <gba/apu/apu.hpp>
|
||||
#include <gba/display/display.hpp>
|
||||
}
|
||||
@@ -42,7 +42,7 @@ auto IO::writeIO(u32 mode, n32 address, n32 word) -> void {
|
||||
|
||||
struct UnmappedIO : IO {
|
||||
auto readIO(n32 address) -> n8 override {
|
||||
return cpu.pipeline.fetch.instruction.byte(address & 1);
|
||||
return cpu.openBus.get(Byte, address);
|
||||
}
|
||||
|
||||
auto writeIO(n32 address, n8 byte) -> void override {
|
||||
|
||||
+93
-62
@@ -10,17 +10,20 @@ auto PPU::Background::setEnable(n1 status) -> void {
|
||||
}
|
||||
|
||||
auto PPU::Background::scanline(u32 y) -> void {
|
||||
memory::move(io.enable, io.enable + 1, sizeof(io.enable) - 1);
|
||||
mosaicOffset = 0;
|
||||
for(auto& pixel : output) pixel = {};
|
||||
}
|
||||
|
||||
auto PPU::Background::outputPixel(u32 x, u32 y) -> void {
|
||||
//horizontal mosaic
|
||||
if(!io.mosaic || !mosaicOffset) {
|
||||
mosaicOffset = 1 + io.mosaicWidth;
|
||||
mosaic = output[x];
|
||||
}
|
||||
mosaicOffset--;
|
||||
}
|
||||
|
||||
auto PPU::Background::run(u32 x, u32 y) -> void {
|
||||
output = {};
|
||||
if(ppu.blank() || !io.enable[0]) {
|
||||
mosaic = {};
|
||||
return;
|
||||
}
|
||||
|
||||
switch(id) {
|
||||
case PPU::BG0:
|
||||
if(io.mode <= 1) { linear(x, y); break; }
|
||||
@@ -32,25 +35,50 @@ auto PPU::Background::run(u32 x, u32 y) -> void {
|
||||
|
||||
case PPU::BG2:
|
||||
if(io.mode == 0) { linear(x, y); break; }
|
||||
if(io.mode <= 2) { affine(x, y); break; }
|
||||
if(io.mode <= 2) { affineFetchTileMap(x, y); affineFetchTileData(x, y); break; }
|
||||
if(io.mode <= 5) { bitmap(x, y); break; }
|
||||
break;
|
||||
|
||||
case PPU::BG3:
|
||||
if(io.mode == 0) { linear(x, y); break; }
|
||||
if(io.mode == 2) { affine(x, y); break; }
|
||||
if(io.mode == 2) { affineFetchTileMap(x, y); affineFetchTileData(x, y); break; }
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//horizontal mosaic
|
||||
if(!io.mosaic || !mosaicOffset) {
|
||||
mosaicOffset = 1 + io.mosaicWidth;
|
||||
mosaic = output;
|
||||
}
|
||||
mosaicOffset--;
|
||||
auto PPU::Background::linearFetchTileMap() -> void {
|
||||
n6 tx = fx >> 3;
|
||||
n6 ty = fy >> 3;
|
||||
|
||||
u32 offset = (ty & 31) << 5 | (tx & 31);
|
||||
if(io.screenSize.bit(0) && (tx & 32)) offset += 32 << 5;
|
||||
if(io.screenSize.bit(1) && (ty & 32)) offset += 32 << 5 + io.screenSize.bit(0);
|
||||
offset = (io.screenBase << 11) + (offset << 1);
|
||||
|
||||
n16 tilemap = ppu.readVRAM_BG(Half, offset);
|
||||
latch.character = tilemap.bit(0,9);
|
||||
latch.hflip = tilemap.bit(10);
|
||||
latch.vflip = tilemap.bit(11);
|
||||
latch.palette = tilemap.bit(12,15);
|
||||
}
|
||||
|
||||
auto PPU::Background::linearFetchTileData() -> void {
|
||||
n3 px = fx;
|
||||
n3 py = fy;
|
||||
if(latch.hflip) px = ~px;
|
||||
if(latch.vflip) py = ~py;
|
||||
|
||||
u32 tileOffset = ((latch.character << 5) + (py << 2)) << io.colorMode;
|
||||
u32 pixelOffset = px >> (1 - io.colorMode);
|
||||
u32 offset = (io.characterBase << 14) + tileOffset + pixelOffset;
|
||||
|
||||
latch.data = ppu.readVRAM_BG(Half, offset);
|
||||
}
|
||||
|
||||
auto PPU::Background::linear(u32 x, u32 y) -> void {
|
||||
if(x > 239) return;
|
||||
if(ppu.blank() || !io.enable[0]) return;
|
||||
|
||||
if(x == 0) {
|
||||
if(!io.mosaic || (y % (1 + io.mosaicHeight)) == 0) {
|
||||
vmosaic = y;
|
||||
@@ -62,45 +90,40 @@ auto PPU::Background::linear(u32 x, u32 y) -> void {
|
||||
n3 px = fx;
|
||||
n3 py = fy;
|
||||
|
||||
if(x == 0 || px == 0) {
|
||||
n6 tx = fx >> 3;
|
||||
n6 ty = fy >> 3;
|
||||
|
||||
u32 offset = (ty & 31) << 5 | (tx & 31);
|
||||
if(io.screenSize.bit(0) && (tx & 32)) offset += 32 << 5;
|
||||
if(io.screenSize.bit(1) && (ty & 32)) offset += 32 << 5 + io.screenSize.bit(0);
|
||||
offset = (io.screenBase << 11) + (offset << 1);
|
||||
|
||||
n16 tilemap = ppu.readVRAM(Half, offset);
|
||||
latch.character = tilemap.bit(0,9);
|
||||
latch.hflip = tilemap.bit(10);
|
||||
latch.vflip = tilemap.bit(11);
|
||||
latch.palette = tilemap.bit(12,15);
|
||||
if(x == 0 || px == 0) linearFetchTileMap();
|
||||
if(x == 0) {
|
||||
linearFetchTileData();
|
||||
} else if(io.colorMode == 0 && (px & 3) == 0) {
|
||||
linearFetchTileData();
|
||||
} else if(io.colorMode == 1 && (px & 1) == 0) {
|
||||
linearFetchTileData();
|
||||
}
|
||||
|
||||
if(latch.hflip) px = ~px;
|
||||
if(latch.vflip) py = ~py;
|
||||
|
||||
n16 color = latch.data;
|
||||
if(io.colorMode == 0) {
|
||||
u32 offset = (io.characterBase << 14) + (latch.character << 5) + (py << 2) + (px >> 1);
|
||||
if(n4 color = ppu.readVRAM_BG(Byte, offset) >> (px & 1 ? 4 : 0)) {
|
||||
output.enable = true;
|
||||
output.priority = io.priority;
|
||||
output.color = ppu.pram[latch.palette << 4 | color];
|
||||
}
|
||||
color >>= (px & 3) * 4;
|
||||
color = (n4)color;
|
||||
} else {
|
||||
u32 offset = (io.characterBase << 14) + (latch.character << 6) + (py << 3) + (px);
|
||||
if(n8 color = ppu.readVRAM_BG(Byte, offset)) {
|
||||
output.enable = true;
|
||||
output.priority = io.priority;
|
||||
output.color = ppu.pram[color];
|
||||
}
|
||||
color >>= (px & 1) * 8;
|
||||
color = (n8)color;
|
||||
}
|
||||
|
||||
if(color) {
|
||||
if(io.colorMode == 0) color |= latch.palette << 4;
|
||||
output[x].enable = true;
|
||||
output[x].priority = io.priority;
|
||||
output[x].color = color;
|
||||
}
|
||||
|
||||
fx++;
|
||||
}
|
||||
|
||||
auto PPU::Background::affine(u32 x, u32 y) -> void {
|
||||
auto PPU::Background::affineFetchTileMap(u32 x, u32 y) -> void {
|
||||
if(x > 239) return;
|
||||
if(ppu.blank() || !io.enable[0]) return;
|
||||
|
||||
if(x == 0) {
|
||||
if(!io.mosaic || (y % (1 + io.mosaicHeight)) == 0) {
|
||||
hmosaic = io.lx;
|
||||
@@ -110,24 +133,29 @@ auto PPU::Background::affine(u32 x, u32 y) -> void {
|
||||
fy = vmosaic;
|
||||
}
|
||||
|
||||
u32 screenSize = 16 << io.screenSize;
|
||||
u32 screenWrap = (1 << (io.affineWrap ? 7 + io.screenSize : 20)) - 1;
|
||||
affine.screenSize = 16 << io.screenSize;
|
||||
affine.screenWrap = (1 << (io.affineWrap ? 7 + io.screenSize : 20)) - 1;
|
||||
|
||||
u32 cx = (fx >> 8) & screenWrap;
|
||||
u32 cy = (fy >> 8) & screenWrap;
|
||||
affine.cx = (fx >> 8) & affine.screenWrap;
|
||||
affine.cy = (fy >> 8) & affine.screenWrap;
|
||||
|
||||
u32 tx = cx >> 3;
|
||||
u32 ty = cy >> 3;
|
||||
affine.tx = affine.cx >> 3;
|
||||
affine.ty = affine.cy >> 3;
|
||||
|
||||
n3 px = cx;
|
||||
n3 py = cy;
|
||||
affine.character = ppu.readVRAM_BG(Byte, (io.screenBase << 11) + affine.ty * affine.screenSize + affine.tx);
|
||||
}
|
||||
|
||||
if(tx < screenSize && ty < screenSize) {
|
||||
n8 character = ppu.readVRAM(Byte, (io.screenBase << 11) + ty * screenSize + tx);
|
||||
if(n8 color = ppu.readVRAM_BG(Byte, (io.characterBase << 14) + (character << 6) + (py << 3) + px)) {
|
||||
output.enable = true;
|
||||
output.priority = io.priority;
|
||||
output.color = ppu.pram[color];
|
||||
auto PPU::Background::affineFetchTileData(u32 x, u32 y) -> void {
|
||||
if(x > 239) return;
|
||||
if(ppu.blank() || !io.enable[0]) return;
|
||||
|
||||
if(affine.tx < affine.screenSize && affine.ty < affine.screenSize) {
|
||||
n3 px = affine.cx;
|
||||
n3 py = affine.cy;
|
||||
if(n8 color = ppu.readVRAM_BG(Byte, (io.characterBase << 14) + (affine.character << 6) + (py << 3) + px)) {
|
||||
output[x].enable = true;
|
||||
output[x].priority = io.priority;
|
||||
output[x].color = color;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +169,9 @@ auto PPU::Background::affine(u32 x, u32 y) -> void {
|
||||
}
|
||||
|
||||
auto PPU::Background::bitmap(u32 x, u32 y) -> void {
|
||||
if(x > 239) return;
|
||||
if(ppu.blank() || !io.enable[0]) return;
|
||||
|
||||
if(x == 0) {
|
||||
if(!io.mosaic || (y % (1 + io.mosaicHeight)) == 0) {
|
||||
hmosaic = io.lx;
|
||||
@@ -165,10 +196,10 @@ auto PPU::Background::bitmap(u32 x, u32 y) -> void {
|
||||
n15 color = ppu.readVRAM_BG(mode, baseAddress + (offset << depth));
|
||||
|
||||
if(depth || color) { //8bpp color 0 is transparent; 15bpp color is always opaque
|
||||
if(depth == 0) color = ppu.pram[color];
|
||||
output.enable = true;
|
||||
output.priority = io.priority;
|
||||
output.color = color;
|
||||
if(depth) output[x].directColor = true;
|
||||
output[x].enable = true;
|
||||
output[x].priority = io.priority;
|
||||
output[x].color = color;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +217,7 @@ auto PPU::Background::power(u32 id) -> void {
|
||||
|
||||
io = {};
|
||||
latch = {};
|
||||
output = {};
|
||||
for(auto& pixel : output) pixel = {};
|
||||
mosaic = {};
|
||||
mosaicOffset = 0;
|
||||
hmosaic = 0;
|
||||
|
||||
+49
-24
@@ -1,5 +1,13 @@
|
||||
auto PPU::DAC::run(u32 x, u32 y) -> n15 {
|
||||
if(ppu.blank()) return 0x7fff;
|
||||
auto PPU::DAC::scanline(u32 y) -> void {
|
||||
line = ppu.screen->pixels().data() + y * 240;
|
||||
}
|
||||
|
||||
auto PPU::DAC::upperLayer(u32 x, u32 y) -> void {
|
||||
if(ppu.blank()) {
|
||||
color = 0x7fff;
|
||||
blending = false;
|
||||
return;
|
||||
}
|
||||
|
||||
//determine active window
|
||||
n1 active[6] = {true, true, true, true, true, true}; //enable all layers if no windows are enabled
|
||||
@@ -11,16 +19,15 @@ auto PPU::DAC::run(u32 x, u32 y) -> n15 {
|
||||
}
|
||||
|
||||
//priority sorting: find topmost two pixels
|
||||
Pixel layers[6] = {
|
||||
ppu.objects.mosaic,
|
||||
ppu.bg0.mosaic,
|
||||
ppu.bg1.mosaic,
|
||||
ppu.bg2.mosaic,
|
||||
ppu.bg3.mosaic,
|
||||
{true, 3, ppu.pram[0]},
|
||||
};
|
||||
layers[OBJ] = ppu.objects.mosaic;
|
||||
layers[BG0] = ppu.bg0.mosaic;
|
||||
layers[BG1] = ppu.bg1.mosaic;
|
||||
layers[BG2] = ppu.bg2.mosaic;
|
||||
layers[BG3] = ppu.bg3.mosaic;
|
||||
layers[SFX] = {true, 3, 0};
|
||||
|
||||
u32 aboveLayer = 5, belowLayer = 5;
|
||||
aboveLayer = 5;
|
||||
belowLayer = 5;
|
||||
for(s32 priority = 3; priority >= 0; priority--) {
|
||||
for(s32 layer = 5; layer >= 0; layer--) {
|
||||
if(layers[layer].enable && layers[layer].priority == priority && active[layer]) {
|
||||
@@ -31,26 +38,44 @@ auto PPU::DAC::run(u32 x, u32 y) -> n15 {
|
||||
}
|
||||
|
||||
auto above = layers[aboveLayer];
|
||||
auto below = layers[belowLayer];
|
||||
auto eva = min(16u, (u32)io.blendEVA);
|
||||
auto evb = min(16u, (u32)io.blendEVB);
|
||||
auto evy = min(16u, (u32)io.blendEVY);
|
||||
n15 color = above.color;
|
||||
color = pramLookup(above);
|
||||
|
||||
//color blending
|
||||
if(active[SFX] || (above.translucent && io.blendBelow[belowLayer])) {
|
||||
if(above.translucent && io.blendBelow[belowLayer]) {
|
||||
color = blend(above.color, eva, below.color, evb);
|
||||
} else if(io.blendMode == 1 && io.blendAbove[aboveLayer] && io.blendBelow[belowLayer]) {
|
||||
color = blend(above.color, eva, below.color, evb);
|
||||
if(above.translucent && io.blendBelow[belowLayer]) {
|
||||
blending = true;
|
||||
return;
|
||||
}
|
||||
if(active[SFX]) {
|
||||
auto evy = min(16u, (u32)io.blendEVY);
|
||||
if(io.blendMode == 1 && io.blendAbove[aboveLayer] && io.blendBelow[belowLayer]) {
|
||||
blending = true;
|
||||
return;
|
||||
} else if(io.blendMode == 2 && io.blendAbove[aboveLayer]) {
|
||||
color = blend(above.color, 16 - evy, 0x7fff, evy);
|
||||
color = blend(color, 16 - evy, 0x7fff, evy);
|
||||
} else if(io.blendMode == 3 && io.blendAbove[aboveLayer]) {
|
||||
color = blend(above.color, 16 - evy, 0x0000, evy);
|
||||
color = blend(color, 16 - evy, 0x0000, evy);
|
||||
}
|
||||
}
|
||||
|
||||
return color;
|
||||
blending = false;
|
||||
}
|
||||
|
||||
auto PPU::DAC::lowerLayer(u32 x, u32 y) -> void {
|
||||
if(blending) {
|
||||
auto below = layers[belowLayer];
|
||||
auto eva = min(16u, (u32)io.blendEVA);
|
||||
auto evb = min(16u, (u32)io.blendEVB);
|
||||
|
||||
color = blend(color, eva, pramLookup(below), evb);
|
||||
}
|
||||
|
||||
line[x] = color;
|
||||
}
|
||||
|
||||
inline auto PPU::DAC::pramLookup(Pixel& layer) -> n15 {
|
||||
if(layer.directColor) return layer.color;
|
||||
ppu.pramAccessed = true;
|
||||
return ppu.pram[layer.color];
|
||||
}
|
||||
|
||||
auto PPU::DAC::blend(n15 above, u32 eva, n15 below, u32 evb) -> n15 {
|
||||
|
||||
+5
-28
@@ -1,4 +1,6 @@
|
||||
auto PPU::readIO(n32 address) -> n8 {
|
||||
cpu.synchronize(ppu);
|
||||
|
||||
switch(address) {
|
||||
|
||||
//DISPCNT
|
||||
@@ -25,23 +27,6 @@ auto PPU::readIO(n32 address) -> n8 {
|
||||
case 0x0400'0002: return io.greenSwap;
|
||||
case 0x0400'0003: return 0;
|
||||
|
||||
//DISPSTAT
|
||||
case 0x0400'0004: return (
|
||||
io.vblank << 0
|
||||
| io.hblank << 1
|
||||
| io.vcoincidence << 2
|
||||
| io.irqvblank << 3
|
||||
| io.irqhblank << 4
|
||||
| io.irqvcoincidence << 5
|
||||
);
|
||||
case 0x0400'0005: return (
|
||||
io.vcompare
|
||||
);
|
||||
|
||||
//VCOUNT
|
||||
case 0x0400'0006: return io.vcounter.byte(0);
|
||||
case 0x0400'0007: return io.vcounter.byte(1);
|
||||
|
||||
//BG0CNT
|
||||
case 0x0400'0008: return bg0.io.priority << 0 | bg0.io.characterBase << 2 | bg0.io.unused << 4 | bg0.io.mosaic << 6 | bg0.io.colorMode << 7;
|
||||
case 0x0400'0009: return bg0.io.screenBase << 0 | bg0.io.affineWrap << 5 | bg0.io.screenSize << 6;
|
||||
@@ -99,10 +84,12 @@ auto PPU::readIO(n32 address) -> n8 {
|
||||
|
||||
}
|
||||
|
||||
return cpu.pipeline.fetch.instruction.byte(address & 1);
|
||||
return cpu.openBus.get(Byte, address);
|
||||
}
|
||||
|
||||
auto PPU::writeIO(n32 address, n8 data) -> void {
|
||||
cpu.synchronize(ppu);
|
||||
|
||||
switch(address) {
|
||||
|
||||
//DISPCNT
|
||||
@@ -135,16 +122,6 @@ auto PPU::writeIO(n32 address, n8 data) -> void {
|
||||
case 0x0400'0003:
|
||||
return;
|
||||
|
||||
//DISPSTAT
|
||||
case 0x0400'0004:
|
||||
io.irqvblank = data.bit(3);
|
||||
io.irqhblank = data.bit(4);
|
||||
io.irqvcoincidence = data.bit(5);
|
||||
return;
|
||||
case 0x0400'0005:
|
||||
io.vcompare = data;
|
||||
return;
|
||||
|
||||
//BG0CNT
|
||||
case 0x0400'0008:
|
||||
bg0.io.priority = data.bit(0,1);
|
||||
|
||||
+31
-30
@@ -1,20 +1,34 @@
|
||||
auto PPU::readVRAM_BG(u32 mode, n32 address) -> n32 {
|
||||
auto PPU::releaseBus() -> void {
|
||||
pramAccessed = false;
|
||||
vramAccessedBG = false;
|
||||
}
|
||||
|
||||
auto PPU::pramContention() -> bool {
|
||||
return pramAccessed;
|
||||
}
|
||||
|
||||
auto PPU::vramContention(n32 address) -> bool {
|
||||
address &= 0x1ffff;
|
||||
if(Background::IO::mode < 3 && address >= 0x10000) return false;
|
||||
else if(address >= 0x14000) return false;
|
||||
return vramAccessedBG;
|
||||
}
|
||||
|
||||
auto PPU::readVRAM_BG(u32 mode, n32 address) -> n16 {
|
||||
if(Background::IO::mode < 3 && address >= 0x10000) return 0;
|
||||
else if(address >= 0x14000) return 0;
|
||||
vramAccessedBG = true;
|
||||
|
||||
return readVRAM(mode, address);
|
||||
}
|
||||
|
||||
auto PPU::readVRAM(u32 mode, n32 address) -> n32 {
|
||||
auto PPU::readVRAM(u32 mode, n32 address) -> n16 {
|
||||
address &= 0x1ffff;
|
||||
if(Background::IO::mode >= 3 && address < 0x1c000 && address >= 0x18000) return 0;
|
||||
|
||||
address &= (address & 0x10000) ? 0x17fff : 0x0ffff;
|
||||
|
||||
if(mode & Word) {
|
||||
address &= ~3;
|
||||
return vram[address + 0] << 0 | vram[address + 1] << 8 | vram[address + 2] << 16 | vram[address + 3] << 24;
|
||||
} else if(mode & Half) {
|
||||
if(mode & Half) {
|
||||
address &= ~1;
|
||||
return vram[address + 0] << 0 | vram[address + 1] << 8;
|
||||
} else if(mode & Byte) {
|
||||
@@ -24,52 +38,39 @@ auto PPU::readVRAM(u32 mode, n32 address) -> n32 {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto PPU::writeVRAM(u32 mode, n32 address, n32 word) -> void {
|
||||
auto PPU::writeVRAM(u32 mode, n32 address, n16 half) -> void {
|
||||
address &= 0x1ffff;
|
||||
if(Background::IO::mode >= 3 && address < 0x1c000 && address >= 0x18000) return;
|
||||
|
||||
address &= (address & 0x10000) ? 0x17fff : 0x0ffff;
|
||||
|
||||
if(mode & Word) {
|
||||
address &= ~3;
|
||||
vram[address + 0] = word >> 0;
|
||||
vram[address + 1] = word >> 8;
|
||||
vram[address + 2] = word >> 16;
|
||||
vram[address + 3] = word >> 24;
|
||||
} else if(mode & Half) {
|
||||
if(mode & Half) {
|
||||
address &= ~1;
|
||||
vram[address + 0] = word >> 0;
|
||||
vram[address + 1] = word >> 8;
|
||||
vram[address + 0] = half >> 0;
|
||||
vram[address + 1] = half >> 8;
|
||||
} else if(mode & Byte) {
|
||||
//8-bit writes to OBJ section of VRAM are ignored
|
||||
if(Background::IO::mode <= 2 && address >= 0x10000) return;
|
||||
if(Background::IO::mode <= 5 && address >= 0x14000) return;
|
||||
|
||||
address &= ~1;
|
||||
vram[address + 0] = (n8)word;
|
||||
vram[address + 1] = (n8)word;
|
||||
vram[address + 0] = (n8)half;
|
||||
vram[address + 1] = (n8)half;
|
||||
}
|
||||
}
|
||||
|
||||
auto PPU::readPRAM(u32 mode, n32 address) -> n32 {
|
||||
if(mode & Word) return readPRAM(Half, address & ~2) << 0 | readPRAM(Half, address | 2) << 16;
|
||||
auto PPU::readPRAM(u32 mode, n32 address) -> n16 {
|
||||
if(mode & Byte) return readPRAM(Half, address) >> ((address & 1) * 8);
|
||||
return pram[address >> 1 & 511];
|
||||
}
|
||||
|
||||
auto PPU::writePRAM(u32 mode, n32 address, n32 word) -> void {
|
||||
if(mode & Word) {
|
||||
writePRAM(Half, address & ~2, word >> 0);
|
||||
writePRAM(Half, address | 2, word >> 16);
|
||||
return;
|
||||
}
|
||||
|
||||
auto PPU::writePRAM(u32 mode, n32 address, n16 half) -> void {
|
||||
if(mode & Byte) {
|
||||
word = (n8)word;
|
||||
return writePRAM(Half, address, word << 8 | word << 0);
|
||||
half = (n8)half;
|
||||
return writePRAM(Half, address, half << 8 | half << 0);
|
||||
}
|
||||
|
||||
pram[address >> 1 & 511] = (n16)word;
|
||||
pram[address >> 1 & 511] = (n16)half;
|
||||
}
|
||||
|
||||
auto PPU::readOAM(u32 mode, n32 address) -> n32 {
|
||||
|
||||
@@ -4,7 +4,6 @@ auto PPU::Objects::setEnable(n1 status) -> void {
|
||||
}
|
||||
|
||||
auto PPU::Objects::scanline(u32 y) -> void {
|
||||
memory::move(io.enable, io.enable + 1, sizeof(io.enable) - 1);
|
||||
if(y >= 160) return;
|
||||
|
||||
mosaicOffset = 0;
|
||||
@@ -81,7 +80,7 @@ auto PPU::Objects::scanline(u32 y) -> void {
|
||||
}
|
||||
}
|
||||
|
||||
auto PPU::Objects::run(u32 x, u32 y) -> void {
|
||||
auto PPU::Objects::outputPixel(u32 x, u32 y) -> void {
|
||||
output = {};
|
||||
if(ppu.blank() || !io.enable[0]) {
|
||||
mosaic = {};
|
||||
@@ -90,7 +89,6 @@ auto PPU::Objects::run(u32 x, u32 y) -> void {
|
||||
|
||||
auto& buffer = lineBuffers[y & 1];
|
||||
output = buffer[x];
|
||||
output.color = ppu.pram[output.color];
|
||||
|
||||
//horizontal mosaic
|
||||
if(!mosaicOffset) {
|
||||
|
||||
+111
-74
@@ -1,15 +1,5 @@
|
||||
#include <gba/gba.hpp>
|
||||
|
||||
//pixel: 4 cycles
|
||||
|
||||
//hdraw: 46 cycle wait period, then 240 pixels (total: 1006 cycles)
|
||||
//hblank: 226 cycles
|
||||
//scanline: 1232 cycles
|
||||
|
||||
//vdraw: 160 scanlines (197120 cycles)
|
||||
//vblank: 68 scanlines ( 83776 cycles)
|
||||
//frame: 228 scanlines (280896 cycles)
|
||||
|
||||
namespace ares::GameBoyAdvance {
|
||||
|
||||
PPU ppu;
|
||||
@@ -82,15 +72,58 @@ inline auto PPU::blank() -> bool {
|
||||
|
||||
auto PPU::step(u32 clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
Thread::synchronize(cpu);
|
||||
Thread::synchronize(cpu, display);
|
||||
}
|
||||
|
||||
template<u32 Cycle>
|
||||
auto PPU::cycleLinear(u32 x, u32 y) -> void {
|
||||
n3 mode = PPU::Background::IO::mode;
|
||||
if constexpr(Cycle == 0) if(mode <= 1) bg0.linear(x, y);
|
||||
if constexpr(Cycle == 1) if(mode <= 1) bg1.linear(x, y);
|
||||
if constexpr(Cycle == 2) if(mode == 0) bg2.linear(x, y);
|
||||
if constexpr(Cycle == 3) if(mode == 0) bg3.linear(x, y);
|
||||
}
|
||||
|
||||
template<u32 Cycle>
|
||||
auto PPU::cycleAffine(u32 x, u32 y) -> void {
|
||||
n3 mode = PPU::Background::IO::mode;
|
||||
if constexpr(Cycle == 0) if( mode == 2) bg3.affineFetchTileMap(x, y);
|
||||
if constexpr(Cycle == 1) if( mode == 2) bg3.affineFetchTileData(x, y);
|
||||
if constexpr(Cycle == 2) if(mode == 1 || mode == 2) bg2.affineFetchTileMap(x, y);
|
||||
if constexpr(Cycle == 3) if(mode == 1 || mode == 2) bg2.affineFetchTileData(x, y);
|
||||
}
|
||||
|
||||
auto PPU::cycleBitmap(u32 x, u32 y) -> void {
|
||||
n3 mode = PPU::Background::IO::mode;
|
||||
if(mode >= 3 && mode <= 5) bg2.bitmap(x, y);
|
||||
}
|
||||
|
||||
auto PPU::cycleUpperLayer(u32 x, u32 y) -> void {
|
||||
ppu.bg0.outputPixel(x, y);
|
||||
ppu.bg1.outputPixel(x, y);
|
||||
ppu.bg2.outputPixel(x, y);
|
||||
ppu.bg3.outputPixel(x, y);
|
||||
ppu.objects.outputPixel(x, y);
|
||||
window0.run(x, y);
|
||||
window1.run(x, y);
|
||||
window2.output = objects.output.window;
|
||||
window3.output = true;
|
||||
dac.upperLayer(x, y);
|
||||
}
|
||||
|
||||
template<u32 Cycle>
|
||||
auto PPU::cycle(u32 y) -> void {
|
||||
if constexpr(Cycle >= 31 && Cycle <= 1005 ) cycleLinear<(Cycle - 31) & 3>((Cycle - 31) / 4, y);
|
||||
if constexpr(Cycle >= 31 && Cycle <= 1005 ) cycleAffine<(Cycle - 31) & 3>((Cycle - 31) / 4, y);
|
||||
if constexpr(Cycle >= 31 && Cycle <= 1005 && (Cycle - 31) % 4 == 3) cycleBitmap((Cycle - 31) / 4, y);
|
||||
if constexpr(Cycle >= 46 && Cycle <= 1005 && (Cycle - 46) % 4 == 0) cycleUpperLayer((Cycle - 46) / 4, y);
|
||||
if constexpr(Cycle >= 46 && Cycle <= 1005 && (Cycle - 46) % 4 == 2) dac.lowerLayer((Cycle - 46) / 4, y);
|
||||
step(1);
|
||||
releaseBus();
|
||||
}
|
||||
|
||||
auto PPU::main() -> void {
|
||||
cpu.keypad.run();
|
||||
|
||||
io.vblank = io.vcounter >= 160 && io.vcounter <= 226;
|
||||
|
||||
if(io.vcounter == 0) {
|
||||
if(display.io.vcounter == 0) {
|
||||
frame();
|
||||
|
||||
bg2.io.lx = bg2.io.x;
|
||||
@@ -100,74 +133,78 @@ auto PPU::main() -> void {
|
||||
bg3.io.ly = bg3.io.y;
|
||||
}
|
||||
|
||||
step(1);
|
||||
step(31);
|
||||
|
||||
io.vcoincidence = io.vcounter == io.vcompare;
|
||||
|
||||
if(io.vcounter == 160) {
|
||||
if(io.irqvblank) cpu.setInterruptFlag(CPU::Interrupt::VBlank);
|
||||
}
|
||||
|
||||
step(1);
|
||||
|
||||
if(io.irqvcoincidence) {
|
||||
if(io.vcoincidence) cpu.setInterruptFlag(CPU::Interrupt::VCoincidence);
|
||||
}
|
||||
|
||||
if(io.vcounter == 160) {
|
||||
cpu.dmaVblank();
|
||||
}
|
||||
|
||||
step(3);
|
||||
|
||||
if(io.vcounter == 162) {
|
||||
if(videoCapture) cpu.dma[3].enable = 0;
|
||||
videoCapture = !videoCapture && cpu.dma[3].timingMode == 3 && cpu.dma[3].enable;
|
||||
}
|
||||
if(io.vcounter >= 2 && io.vcounter < 162 && videoCapture) cpu.dmaHDMA();
|
||||
|
||||
step(41);
|
||||
|
||||
u32 y = io.vcounter;
|
||||
u32 y = display.io.vcounter;
|
||||
memory::move(io.forceBlank, io.forceBlank + 1, sizeof(io.forceBlank) - 1);
|
||||
memory::move(bg0.io.enable, bg0.io.enable + 1, sizeof(bg0.io.enable) - 1);
|
||||
memory::move(bg1.io.enable, bg1.io.enable + 1, sizeof(bg1.io.enable) - 1);
|
||||
memory::move(bg2.io.enable, bg2.io.enable + 1, sizeof(bg2.io.enable) - 1);
|
||||
memory::move(bg3.io.enable, bg3.io.enable + 1, sizeof(bg3.io.enable) - 1);
|
||||
memory::move(objects.io.enable, objects.io.enable + 1, sizeof(objects.io.enable) - 1);
|
||||
bg0.scanline(y);
|
||||
bg1.scanline(y);
|
||||
bg2.scanline(y);
|
||||
bg3.scanline(y);
|
||||
objects.scanline((y + 1) % 228);
|
||||
dac.scanline(y);
|
||||
|
||||
if(y < 160) {
|
||||
auto line = screen->pixels().data() + y * 240;
|
||||
for(u32 x : range(240)) {
|
||||
bg0.run(x, y);
|
||||
bg1.run(x, y);
|
||||
bg2.run(x, y);
|
||||
bg3.run(x, y);
|
||||
objects.run(x, y);
|
||||
window0.run(x, y);
|
||||
window1.run(x, y);
|
||||
window2.output = objects.output.window;
|
||||
window3.output = true;
|
||||
n15 color = dac.run(x, y);
|
||||
line[x] = color;
|
||||
if(accurate) step(4);
|
||||
if(accurate) {
|
||||
#define cycles01(index) cycle<index>(y)
|
||||
#define cycles02(index) cycles01(index); cycles01(index + 1)
|
||||
#define cycles04(index) cycles02(index); cycles02(index + 2)
|
||||
#define cycles08(index) cycles04(index); cycles04(index + 4)
|
||||
#define cycles16(index) cycles08(index); cycles08(index + 8)
|
||||
#define cycles32(index) cycles16(index); cycles16(index + 16)
|
||||
#define cycles64(index) cycles32(index); cycles32(index + 32)
|
||||
|
||||
//cycle 31 - start rendering backgrounds
|
||||
cycles01( 31);
|
||||
cycles02( 32);
|
||||
cycles04( 34);
|
||||
cycles08( 38);
|
||||
|
||||
//cycle 46 - start pixel output
|
||||
cycles64( 46);
|
||||
cycles64(110);
|
||||
cycles64(174);
|
||||
cycles64(238);
|
||||
cycles64(302);
|
||||
cycles64(366);
|
||||
cycles64(430);
|
||||
cycles64(494);
|
||||
cycles64(558);
|
||||
cycles64(622);
|
||||
cycles64(686);
|
||||
cycles64(750);
|
||||
cycles64(814);
|
||||
cycles64(878);
|
||||
cycles64(942);
|
||||
|
||||
#undef cycles02
|
||||
#undef cycles04
|
||||
#undef cycles08
|
||||
#undef cycles16
|
||||
#undef cycles32
|
||||
#undef cycles64
|
||||
} else {
|
||||
for(u32 x : range(240)) {
|
||||
bg0.run(x, y);
|
||||
bg1.run(x, y);
|
||||
bg2.run(x, y);
|
||||
bg3.run(x, y);
|
||||
cycleUpperLayer(x, y);
|
||||
dac.lowerLayer(x, y);
|
||||
}
|
||||
releaseBus();
|
||||
step(975);
|
||||
}
|
||||
if(!accurate) step(960);
|
||||
} else {
|
||||
step(960);
|
||||
step(975);
|
||||
}
|
||||
|
||||
step(1);
|
||||
io.hblank = 1;
|
||||
|
||||
step(1);
|
||||
if(io.irqhblank) cpu.setInterruptFlag(CPU::Interrupt::HBlank);
|
||||
|
||||
step(1);
|
||||
if(io.vcounter < 160) cpu.dmaHblank();
|
||||
|
||||
step(223);
|
||||
io.hblank = 0;
|
||||
if(++io.vcounter == 228) io.vcounter = 0;
|
||||
step(226);
|
||||
}
|
||||
|
||||
auto PPU::frame() -> void {
|
||||
@@ -183,8 +220,8 @@ auto PPU::power() -> void {
|
||||
for(u32 n = 0x000; n <= 0x055; n++) bus.io[n] = this;
|
||||
|
||||
for(u32 n = 0; n < 96 * 1024; n++) vram[n] = 0x00;
|
||||
for(u32 n = 0; n < 1024; n += 2) writePRAM(n, Half, 0x0000);
|
||||
for(u32 n = 0; n < 1024; n += 2) writeOAM(n, Half, 0x0000);
|
||||
for(u32 n = 0; n < 1024; n += 2) writePRAM(Half, n, 0x0000);
|
||||
for(u32 n = 0; n < 1024; n += 2) writeOAM(Half, n, 0x0000);
|
||||
|
||||
io = {};
|
||||
for(auto& object : this->object) object = {};
|
||||
|
||||
+52
-21
@@ -37,6 +37,11 @@ struct PPU : Thread, IO {
|
||||
auto blank() -> bool;
|
||||
|
||||
auto step(u32 clocks) -> void;
|
||||
template<u32> auto cycleLinear(u32 x, u32 y) -> void;
|
||||
template<u32> auto cycleAffine(u32 x, u32 y) -> void;
|
||||
auto cycleBitmap(u32 x, u32 y) -> void;
|
||||
auto cycleUpperLayer(u32 x, u32 y) -> void;
|
||||
template<u32> auto cycle(u32 y) -> void;
|
||||
auto main() -> void;
|
||||
|
||||
auto frame() -> void;
|
||||
@@ -48,12 +53,16 @@ struct PPU : Thread, IO {
|
||||
auto writeIO(n32 address, n8 byte) -> void;
|
||||
|
||||
//memory.cpp
|
||||
auto readVRAM(u32 mode, n32 address) -> n32;
|
||||
auto readVRAM_BG(u32 mode, n32 address) -> n32;
|
||||
auto writeVRAM(u32 mode, n32 address, n32 word) -> void;
|
||||
auto releaseBus() -> void;
|
||||
auto pramContention() -> bool;
|
||||
auto vramContention(n32 address) -> bool;
|
||||
|
||||
auto readPRAM(u32 mode, n32 address) -> n32;
|
||||
auto writePRAM(u32 mode, n32 address, n32 word) -> void;
|
||||
auto readVRAM(u32 mode, n32 address) -> n16;
|
||||
auto readVRAM_BG(u32 mode, n32 address) -> n16;
|
||||
auto writeVRAM(u32 mode, n32 address, n16 half) -> void;
|
||||
|
||||
auto readPRAM(u32 mode, n32 address) -> n16;
|
||||
auto writePRAM(u32 mode, n32 address, n16 half) -> void;
|
||||
|
||||
auto readOAM(u32 mode, n32 address) -> n32;
|
||||
auto writeOAM(u32 mode, n32 address, n32 word) -> void;
|
||||
@@ -76,16 +85,6 @@ private:
|
||||
n1 gameBoyColorMode;
|
||||
n1 forceBlank[4];
|
||||
n1 greenSwap;
|
||||
|
||||
n1 vblank;
|
||||
n1 hblank;
|
||||
n1 vcoincidence;
|
||||
n1 irqvblank;
|
||||
n1 irqhblank;
|
||||
n1 irqvcoincidence;
|
||||
n8 vcompare;
|
||||
|
||||
n16 vcounter;
|
||||
} io;
|
||||
|
||||
struct Pixel {
|
||||
@@ -93,6 +92,9 @@ private:
|
||||
n2 priority;
|
||||
n15 color;
|
||||
|
||||
//BG2 only
|
||||
n1 directColor;
|
||||
|
||||
//OBJ only
|
||||
n1 translucent;
|
||||
n1 mosaic;
|
||||
@@ -103,9 +105,13 @@ private:
|
||||
//background.cpp
|
||||
auto setEnable(n1 status) -> void;
|
||||
auto scanline(u32 y) -> void;
|
||||
auto outputPixel(u32 x, u32 y) -> void;
|
||||
auto run(u32 x, u32 y) -> void;
|
||||
auto linearFetchTileMap() -> void;
|
||||
auto linearFetchTileData() -> void;
|
||||
auto linear(u32 x, u32 y) -> void;
|
||||
auto affine(u32 x, u32 y) -> void;
|
||||
auto affineFetchTileMap(u32 x, u32 y) -> void;
|
||||
auto affineFetchTileData(u32 x, u32 y) -> void;
|
||||
auto bitmap(u32 x, u32 y) -> void;
|
||||
auto power(u32 id) -> void;
|
||||
|
||||
@@ -152,9 +158,21 @@ private:
|
||||
n1 hflip;
|
||||
n1 vflip;
|
||||
n4 palette;
|
||||
|
||||
n16 data;
|
||||
} latch;
|
||||
|
||||
Pixel output;
|
||||
struct Affine {
|
||||
u32 screenSize;
|
||||
u32 screenWrap;
|
||||
u32 cx;
|
||||
u32 cy;
|
||||
u32 tx;
|
||||
u32 ty;
|
||||
n8 character;
|
||||
} affine;
|
||||
|
||||
Pixel output[240];
|
||||
Pixel mosaic;
|
||||
u32 mosaicOffset;
|
||||
|
||||
@@ -169,7 +187,7 @@ private:
|
||||
//object.cpp
|
||||
auto setEnable(n1 status) -> void;
|
||||
auto scanline(u32 y) -> void;
|
||||
auto run(u32 x, u32 y) -> void;
|
||||
auto outputPixel(u32 x, u32 y) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
//object.cpp
|
||||
@@ -216,7 +234,10 @@ private:
|
||||
|
||||
struct DAC {
|
||||
//dac.cpp
|
||||
auto run(u32 x, u32 y) -> n15;
|
||||
auto scanline(u32 y) -> void;
|
||||
auto upperLayer(u32 x, u32 y) -> void;
|
||||
auto lowerLayer(u32 x, u32 y) -> void;
|
||||
auto pramLookup(Pixel& layer) -> n15;
|
||||
auto blend(n15 above, u32 eva, n15 below, u32 evb) -> n15;
|
||||
auto power() -> void;
|
||||
|
||||
@@ -232,6 +253,15 @@ private:
|
||||
n5 blendEVB;
|
||||
n5 blendEVY;
|
||||
} io;
|
||||
|
||||
u32 aboveLayer;
|
||||
u32 belowLayer;
|
||||
n15 color;
|
||||
n1 blending;
|
||||
|
||||
Pixel layers[6];
|
||||
|
||||
u32* line = nullptr;
|
||||
} dac;
|
||||
|
||||
struct Object {
|
||||
@@ -270,8 +300,9 @@ private:
|
||||
i16 pc;
|
||||
i16 pd;
|
||||
} objectParam[32];
|
||||
|
||||
n1 videoCapture = 0;
|
||||
|
||||
bool pramAccessed;
|
||||
bool vramAccessedBG;
|
||||
};
|
||||
|
||||
extern PPU ppu;
|
||||
@@ -7,16 +7,6 @@ auto PPU::serialize(serializer& s) -> void {
|
||||
s(io.gameBoyColorMode);
|
||||
for(auto& flag : io.forceBlank) s(flag);
|
||||
s(io.greenSwap);
|
||||
s(io.vblank);
|
||||
s(io.hblank);
|
||||
s(io.vcoincidence);
|
||||
s(io.irqvblank);
|
||||
s(io.irqhblank);
|
||||
s(io.irqvcoincidence);
|
||||
s(io.vcompare);
|
||||
s(io.vcounter);
|
||||
|
||||
s(videoCapture);
|
||||
|
||||
s(Background::IO::mode);
|
||||
s(Background::IO::frame);
|
||||
@@ -34,6 +24,9 @@ auto PPU::serialize(serializer& s) -> void {
|
||||
s(dac);
|
||||
for(auto& object : this->object) s(object);
|
||||
for(auto& param : this->objectParam) s(param);
|
||||
|
||||
s(pramAccessed);
|
||||
s(vramAccessedBG);
|
||||
}
|
||||
|
||||
auto PPU::Background::serialize(serializer& s) -> void {
|
||||
|
||||
@@ -17,7 +17,7 @@ auto BIOS::readROM(n25 address) -> n32 {
|
||||
auto BIOS::read(u32 mode, n25 address) -> n32 {
|
||||
//unmapped memory
|
||||
if(address >= 0x0000'4000) {
|
||||
return cpu.pipeline.fetch.instruction; //0000'4000-01ff'ffff
|
||||
return cpu.openBus.get(mode, address); //0000'4000-01ff'ffff
|
||||
}
|
||||
|
||||
//GBA BIOS is read-protected; only the BIOS itself can read its own memory
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
static const string SerializerVersion = "v140.3";
|
||||
static const string SerializerVersion = "v141.6";
|
||||
|
||||
auto System::serialize(bool synchronize) -> serializer {
|
||||
if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);
|
||||
@@ -44,5 +44,6 @@ auto System::serialize(serializer& s, bool synchronize) -> void {
|
||||
s(cpu);
|
||||
s(ppu);
|
||||
s(apu);
|
||||
s(display);
|
||||
s(player);
|
||||
}
|
||||
@@ -71,6 +71,7 @@ auto System::load(Node::System& root, string name) -> bool {
|
||||
cpu.load(node);
|
||||
ppu.load(node);
|
||||
apu.load(node);
|
||||
display.load(node);
|
||||
cartridgeSlot.load(node);
|
||||
return true;
|
||||
}
|
||||
@@ -87,6 +88,7 @@ auto System::unload() -> void {
|
||||
cpu.unload();
|
||||
ppu.unload();
|
||||
apu.unload();
|
||||
display.unload();
|
||||
cartridgeSlot.unload();
|
||||
pak.reset();
|
||||
node.reset();
|
||||
@@ -100,6 +102,7 @@ auto System::power(bool reset) -> void {
|
||||
cpu.power();
|
||||
ppu.power();
|
||||
apu.power();
|
||||
display.power();
|
||||
cartridge.power();
|
||||
scheduler.power(cpu);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
ares_components(
|
||||
m68000
|
||||
z80
|
||||
sh2
|
||||
ssp1601
|
||||
sn76489
|
||||
ym2612
|
||||
m24c
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
md
|
||||
UNITY #
|
||||
md.cpp
|
||||
PRIMARY #
|
||||
bus/bus.cpp
|
||||
cpu/cpu.cpp
|
||||
apu/apu.cpp
|
||||
vdp/vdp.cpp
|
||||
opn2/opn2.cpp
|
||||
m32x/m32x.cpp
|
||||
mcd/mcd.cpp
|
||||
system/system.cpp
|
||||
cartridge/cartridge.cpp
|
||||
controller/controller.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
md
|
||||
INCLUDED #
|
||||
md.hpp
|
||||
CMakeLists.txt
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
md
|
||||
INCLUDED #
|
||||
apu/apu.hpp
|
||||
apu/bus.cpp
|
||||
apu/debugger.cpp
|
||||
apu/serialization.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
md
|
||||
INCLUDED #
|
||||
bus/bus.hpp
|
||||
bus/inline.hpp
|
||||
bus/serialization.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
md
|
||||
INCLUDED #
|
||||
cartridge/cartridge.hpp
|
||||
cartridge/serialization.cpp
|
||||
cartridge/slot.cpp
|
||||
cartridge/slot.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
md
|
||||
INCLUDED #
|
||||
cartridge/board/banked.cpp
|
||||
cartridge/board/board.cpp
|
||||
cartridge/board/board.hpp
|
||||
cartridge/board/debugger.cpp
|
||||
cartridge/board/game-genie.cpp
|
||||
cartridge/board/j-cart.cpp
|
||||
cartridge/board/linear.cpp
|
||||
cartridge/board/mega-32x.cpp
|
||||
cartridge/board/realtec.cpp
|
||||
cartridge/board/standard.cpp
|
||||
cartridge/board/svp.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
md
|
||||
INCLUDED #
|
||||
controller/controller.hpp
|
||||
controller/port.cpp
|
||||
controller/port.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
md
|
||||
INCLUDED #
|
||||
controller/control-pad/control-pad.cpp
|
||||
controller/control-pad/control-pad.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
md
|
||||
INCLUDED #
|
||||
controller/fighting-pad/fighting-pad.cpp
|
||||
controller/fighting-pad/fighting-pad.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
md
|
||||
INCLUDED #
|
||||
cpu/bus.cpp
|
||||
cpu/cpu.hpp
|
||||
cpu/debugger.cpp
|
||||
cpu/io.cpp
|
||||
cpu/serialization.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
md
|
||||
INCLUDED #
|
||||
m32x/bus-external.cpp
|
||||
m32x/bus-internal.cpp
|
||||
m32x/debugger.cpp
|
||||
m32x/io-external.cpp
|
||||
m32x/io-internal.cpp
|
||||
m32x/m32x.hpp
|
||||
m32x/pwm.cpp
|
||||
m32x/serialization.cpp
|
||||
m32x/sh7604.cpp
|
||||
m32x/vdp.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
md
|
||||
INCLUDED #
|
||||
mcd/bus-external.cpp
|
||||
mcd/bus-internal.cpp
|
||||
mcd/cdc-transfer.cpp
|
||||
mcd/cdc.cpp
|
||||
mcd/cdd-dac.cpp
|
||||
mcd/cdd.cpp
|
||||
mcd/debugger.cpp
|
||||
mcd/gpu.cpp
|
||||
mcd/io-external.cpp
|
||||
mcd/io-internal.cpp
|
||||
mcd/irq.cpp
|
||||
mcd/mcd.hpp
|
||||
mcd/pcm.cpp
|
||||
mcd/serialization.cpp
|
||||
mcd/timer.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
md
|
||||
INCLUDED #
|
||||
opn2/opn2.hpp
|
||||
opn2/serialization.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
md
|
||||
INCLUDED #
|
||||
system/controls.cpp
|
||||
system/serialization.cpp
|
||||
system/system.hpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
md
|
||||
INCLUDED #
|
||||
vdp/color.cpp
|
||||
vdp/dac.cpp
|
||||
vdp/debugger.cpp
|
||||
vdp/dma.cpp
|
||||
vdp/fifo.cpp
|
||||
vdp/io.cpp
|
||||
vdp/irq.cpp
|
||||
vdp/layer.cpp
|
||||
vdp/layers.cpp
|
||||
vdp/main.cpp
|
||||
vdp/memory.cpp
|
||||
vdp/prefetch.cpp
|
||||
vdp/psg.cpp
|
||||
vdp/serialization.cpp
|
||||
vdp/sprite.cpp
|
||||
vdp/vdp.hpp
|
||||
vdp/window.cpp
|
||||
)
|
||||
|
||||
ares_add_sources(
|
||||
CORE #
|
||||
md
|
||||
INCLUDED #
|
||||
vdp-performance/background.cpp
|
||||
vdp-performance/color.cpp
|
||||
vdp-performance/debugger.cpp
|
||||
vdp-performance/dma.cpp
|
||||
vdp-performance/io.cpp
|
||||
vdp-performance/irq.cpp
|
||||
vdp-performance/main.cpp
|
||||
vdp-performance/memory.cpp
|
||||
vdp-performance/object.cpp
|
||||
vdp-performance/psg.cpp
|
||||
vdp-performance/render.cpp
|
||||
vdp-performance/serialization.cpp
|
||||
vdp-performance/sprite.cpp
|
||||
vdp-performance/vdp.cpp
|
||||
vdp-performance/vdp.hpp
|
||||
)
|
||||
@@ -27,6 +27,7 @@ auto M32X::readExternalIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
|
||||
//bank set
|
||||
if(address == 0xa15104) {
|
||||
data.bit(0,1) = io.romBank;
|
||||
data.bit(2, 15) = 0;
|
||||
}
|
||||
|
||||
//data request control
|
||||
@@ -87,20 +88,23 @@ auto M32X::readExternalIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
|
||||
|
||||
//PWM left channel pulse width
|
||||
if(address == 0xa15134) {
|
||||
data = pwm.lfifoLatch;
|
||||
data.bit(15) = pwm.lfifo.full();
|
||||
data.bit(0,12) = pwm.lfifoLatch;
|
||||
data.bit(14) = pwm.lfifo.empty();
|
||||
data.bit(15) = pwm.lfifo.full();
|
||||
}
|
||||
|
||||
//PWM right channel pulse width
|
||||
if(address == 0xa15136) {
|
||||
data = pwm.rfifoLatch;
|
||||
data.bit(15) = pwm.rfifo.full();
|
||||
data.bit(0,12) = pwm.rfifoLatch;
|
||||
data.bit(14) = pwm.rfifo.empty();
|
||||
data.bit(15) = pwm.rfifo.full();
|
||||
}
|
||||
|
||||
//PWM mono pulse width
|
||||
if(address == 0xa15138) {
|
||||
data = pwm.mfifoLatch;
|
||||
data.bit(15) = pwm.lfifo.full() || pwm.rfifo.full();
|
||||
data.bit(0,12) = pwm.mfifoLatch;
|
||||
data.bit(14) = pwm.lfifo.empty() && pwm.rfifo.empty();
|
||||
data.bit(15) = pwm.lfifo.full() || pwm.rfifo.full();
|
||||
}
|
||||
|
||||
//bitmap mode
|
||||
@@ -190,12 +194,17 @@ auto M32X::writeExternalIO(n1 upper, n1 lower, n24 address, n16 data) -> void {
|
||||
if(lower) {
|
||||
dreq.vram = data.bit(0);
|
||||
dreq.dma = data.bit(1);
|
||||
dreq.active = data.bit(2);
|
||||
if(!dreq.active) {
|
||||
if(dreq.active > data.bit(2)) {
|
||||
// Night Trap 32X: delay between the last fifo write and flush on disable
|
||||
if(cpu.active()) cpu.wait(1);
|
||||
// Note: The current fifo implementation is incorrect (hw uses dual-block fifo)
|
||||
// but dreq dma works fine as is for normal cases.
|
||||
|
||||
dreq.fifo.flush();
|
||||
shm.dmac.dreq[0] = 0;
|
||||
shs.dmac.dreq[0] = 0;
|
||||
}
|
||||
dreq.active = data.bit(2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,11 +234,14 @@ auto M32X::writeExternalIO(n1 upper, n1 lower, n24 address, n16 data) -> void {
|
||||
|
||||
//FIFO
|
||||
if(address == 0xa15112) {
|
||||
if(!lower || !upper) debug(unusual, "[DREQ] fifo byte write ", lower ? "(odd)" : "(even)");
|
||||
if(dreq.active && !dreq.fifo.full()) {
|
||||
dreq.fifo.write(data);
|
||||
if(!--dreq.length) dreq.active = 0;
|
||||
shm.dmac.dreq[0] = !dreq.fifo.empty();
|
||||
shs.dmac.dreq[0] = !dreq.fifo.empty();
|
||||
} else {
|
||||
debug(unusual, "[DREQ] missed fifo write");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,14 +263,12 @@ auto M32X::writeExternalIO(n1 upper, n1 lower, n24 address, n16 data) -> void {
|
||||
if(lower) {
|
||||
pwm.lmode = data.bit(0,1);
|
||||
pwm.rmode = data.bit(2,3);
|
||||
if(!pwm.lmode) pwm.lsample = 0;
|
||||
if(!pwm.rmode) pwm.rsample = 0;
|
||||
pwm.mono = data.bit(4);
|
||||
//pwm.dreqIRQ = data.bit(7) = readonly;
|
||||
}
|
||||
if(upper) {
|
||||
pwm.timer = data.bit(8,11);
|
||||
pwm.periods = 0;
|
||||
//pwm.timer = data.bit(8,11) = readonly;
|
||||
//pwm.periods = 0 = readonly;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,7 +280,7 @@ auto M32X::writeExternalIO(n1 upper, n1 lower, n24 address, n16 data) -> void {
|
||||
|
||||
//PWM left channel pulse width
|
||||
if(address == 0xa15134) {
|
||||
if(upper) pwm.lfifoLatch.byte(1) = data.byte(1);
|
||||
if(upper) pwm.lfifoLatch.bit(8,11) = data.bit(8,11);
|
||||
if(lower) {
|
||||
pwm.lfifoLatch.byte(0) = data.byte(0);
|
||||
pwm.lfifo.write(pwm.lfifoLatch);
|
||||
@@ -279,7 +289,7 @@ auto M32X::writeExternalIO(n1 upper, n1 lower, n24 address, n16 data) -> void {
|
||||
|
||||
//PWM right channel pulse width
|
||||
if(address == 0xa15136) {
|
||||
if(upper) pwm.rfifoLatch.byte(1) = data.byte(1);
|
||||
if(upper) pwm.rfifoLatch.bit(8,11) = data.bit(8,11);
|
||||
if(lower) {
|
||||
pwm.rfifoLatch.byte(0) = data.byte(0);
|
||||
pwm.rfifo.write(pwm.rfifoLatch);
|
||||
@@ -288,7 +298,7 @@ auto M32X::writeExternalIO(n1 upper, n1 lower, n24 address, n16 data) -> void {
|
||||
|
||||
//PWM mono pulse width
|
||||
if(address == 0xa15138) {
|
||||
if(upper) pwm.mfifoLatch.byte(1) = data.byte(1);
|
||||
if(upper) pwm.mfifoLatch.bit(8,11) = data.bit(8,11);
|
||||
if(lower) {
|
||||
pwm.mfifoLatch.byte(0) = data.byte(0);
|
||||
pwm.lfifo.write(pwm.mfifoLatch);
|
||||
|
||||
@@ -91,20 +91,23 @@ auto M32X::readInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> n16 {
|
||||
|
||||
//PWM left channel pulse width
|
||||
if(address == 0x4034) {
|
||||
data = pwm.lfifoLatch;
|
||||
data.bit(15) = pwm.lfifo.full();
|
||||
data.bit(0,12) = pwm.lfifoLatch;
|
||||
data.bit(14) = pwm.lfifo.empty();
|
||||
data.bit(15) = pwm.lfifo.full();
|
||||
}
|
||||
|
||||
//PWM right channel pulse width
|
||||
if(address == 0x4036) {
|
||||
data = pwm.rfifoLatch;
|
||||
data.bit(15) = pwm.rfifo.full();
|
||||
data.bit(0,12) = pwm.rfifoLatch;
|
||||
data.bit(14) = pwm.rfifo.empty();
|
||||
data.bit(15) = pwm.rfifo.full();
|
||||
}
|
||||
|
||||
//PWM mono pulse width
|
||||
if(address == 0x4038) {
|
||||
data = pwm.mfifoLatch;
|
||||
data.bit(15) = pwm.lfifo.full() || pwm.rfifo.full();
|
||||
data.bit(0,12) = pwm.mfifoLatch;
|
||||
data.bit(14) = pwm.lfifo.empty() && pwm.rfifo.empty();
|
||||
data.bit(15) = pwm.lfifo.full() || pwm.rfifo.full();
|
||||
}
|
||||
|
||||
//bitmap mode
|
||||
@@ -236,8 +239,6 @@ auto M32X::writeInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> void {
|
||||
if(lower) {
|
||||
pwm.lmode = data.bit(0,1);
|
||||
pwm.rmode = data.bit(2,3);
|
||||
if(!pwm.lmode) pwm.lsample = 0;
|
||||
if(!pwm.rmode) pwm.rsample = 0;
|
||||
pwm.mono = data.bit(4);
|
||||
pwm.dreqIRQ = data.bit(7);
|
||||
if(!pwm.dreqIRQ) {
|
||||
@@ -259,21 +260,30 @@ auto M32X::writeInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> void {
|
||||
|
||||
//PWM left channel pulse width
|
||||
if(address == 0x4034) {
|
||||
pwm.lfifoLatch = data;
|
||||
pwm.lfifo.write(pwm.lfifoLatch);
|
||||
if(upper) pwm.lfifoLatch.bit(8,11) = data.bit(8,11);
|
||||
if(lower) {
|
||||
pwm.lfifoLatch.byte(0) = data.byte(0);
|
||||
pwm.lfifo.write(pwm.lfifoLatch);
|
||||
}
|
||||
}
|
||||
|
||||
//PWM right channel pulse width
|
||||
if(address == 0x4036) {
|
||||
pwm.rfifoLatch = data;
|
||||
pwm.rfifo.write(pwm.rfifoLatch);
|
||||
if(upper) pwm.rfifoLatch.bit(8,11) = data.bit(8,11);
|
||||
if(lower) {
|
||||
pwm.rfifoLatch.byte(0) = data.byte(0);
|
||||
pwm.rfifo.write(pwm.rfifoLatch);
|
||||
}
|
||||
}
|
||||
|
||||
//PWM mono pulse width
|
||||
if(address == 0x4038) {
|
||||
pwm.mfifoLatch = data;
|
||||
pwm.lfifo.write(pwm.mfifoLatch);
|
||||
pwm.rfifo.write(pwm.mfifoLatch);
|
||||
if(upper) pwm.mfifoLatch.bit(8,11) = data.bit(8,11);
|
||||
if(lower) {
|
||||
pwm.mfifoLatch.byte(0) = data.byte(0);
|
||||
pwm.lfifo.write(pwm.mfifoLatch);
|
||||
pwm.rfifo.write(pwm.mfifoLatch);
|
||||
}
|
||||
}
|
||||
|
||||
//bitmap mode
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
//Mega 32X
|
||||
#include "nall/dsp/iir/dc-removal.hpp"
|
||||
|
||||
struct M32X {
|
||||
Node::Object node;
|
||||
@@ -123,6 +124,7 @@ struct M32X {
|
||||
|
||||
struct PWM : Thread {
|
||||
Node::Audio::Stream stream;
|
||||
nall::DSP::IIR::DCRemoval dcfilter_l, dcfilter_r;
|
||||
|
||||
//pwm.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
@@ -130,6 +132,7 @@ struct M32X {
|
||||
auto main() -> void;
|
||||
auto step(u32 clocks) -> void;
|
||||
auto power(bool reset) -> void;
|
||||
auto updateFrequency() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
+58
-29
@@ -1,7 +1,7 @@
|
||||
auto M32X::PWM::load(Node::Object parent) -> void {
|
||||
stream = parent->append<Node::Audio::Stream>("PWM");
|
||||
stream->setChannels(2);
|
||||
stream->setFrequency(44'100);
|
||||
updateFrequency();
|
||||
}
|
||||
|
||||
auto M32X::PWM::unload(Node::Object parent) -> void {
|
||||
@@ -10,36 +10,56 @@ auto M32X::PWM::unload(Node::Object parent) -> void {
|
||||
}
|
||||
|
||||
auto M32X::PWM::main() -> void {
|
||||
n12 clocks = cycle-1;
|
||||
if(clocks && (lmode.bit(0)^lmode.bit(1) || rmode.bit(0)^rmode.bit(1))) {
|
||||
if(lmode == 1) lsample = lfifo.read();
|
||||
if(lmode == 2) lsample = rfifo.read();
|
||||
n12 clocks = cycle - 1;
|
||||
if(clocks == 0) clocks++;
|
||||
|
||||
if(rmode == 1) rsample = rfifo.read();
|
||||
if(rmode == 2) rsample = lfifo.read();
|
||||
|
||||
lfifoLatch.bit(14) = lfifo.empty();
|
||||
rfifoLatch.bit(14) = rfifo.empty();
|
||||
mfifoLatch.bit(14) = lfifoLatch.bit(14) & rfifoLatch.bit(14);
|
||||
|
||||
if(periods++ == n4(timer-1)) {
|
||||
periods = 0;
|
||||
m32x.shm.irq.pwm.active = 1;
|
||||
m32x.shs.irq.pwm.active = 1;
|
||||
m32x.shm.dmac.dreq[1] = dreqIRQ;
|
||||
m32x.shs.dmac.dreq[1] = dreqIRQ;
|
||||
//check if cycle rate has changed and update sample rate accordingly
|
||||
static n12 previousCycle = cycle;
|
||||
if (cycle != previousCycle) {
|
||||
previousCycle = cycle;
|
||||
updateFrequency();
|
||||
}
|
||||
}
|
||||
|
||||
counter += max(1, clocks);
|
||||
while(counter >= 522) {
|
||||
counter -= 522;
|
||||
auto left = cycle > 0 ? lsample / (f32)cycle : 0;
|
||||
auto right = cycle > 0 ? lsample / (f32)cycle : 0;
|
||||
// skip action if cycle == 1 (illegal setting: "PWM will no longer operate")
|
||||
if (cycle != 1 && (lmode.bit(0) ^ lmode.bit(1) || rmode.bit(0) ^ rmode.bit(1))) {
|
||||
lsample = lfifo.read();
|
||||
rsample = rfifo.read();
|
||||
|
||||
if (periods++ == n4(timer - 1)) {
|
||||
periods = 0;
|
||||
m32x.shm.irq.pwm.active = 1;
|
||||
m32x.shs.irq.pwm.active = 1;
|
||||
m32x.shm.dmac.dreq[1] = dreqIRQ;
|
||||
m32x.shs.dmac.dreq[1] = dreqIRQ;
|
||||
}
|
||||
}
|
||||
|
||||
i12 outL, outR;
|
||||
|
||||
if (lmode == 0) outL = 0;
|
||||
if (lmode == 1) outL = lsample;
|
||||
if (lmode == 2) outL = rsample;
|
||||
if (lmode == 3) outL = 0; // illegal mode
|
||||
|
||||
if (rmode == 0) outR = 0;
|
||||
if (rmode == 1) outR = rsample;
|
||||
if (rmode == 2) outR = lsample;
|
||||
if (rmode == 3) outR = 0; // illegal mode
|
||||
|
||||
auto left = outL / (f32)clocks;
|
||||
auto right = outR / (f32)clocks;
|
||||
|
||||
// filter DC offset to normalize output and limit popping
|
||||
left = dcfilter_l.process(left);
|
||||
right = dcfilter_r.process(right);
|
||||
|
||||
// handle clipping due to improper settings
|
||||
if(abs(left) > 1.0) left /= abs(left);
|
||||
if(abs(right) > 1.0) right /= abs(right);
|
||||
|
||||
stream->frame(left, right);
|
||||
}
|
||||
|
||||
step(clocks);
|
||||
step(clocks);
|
||||
}
|
||||
|
||||
auto M32X::PWM::step(u32 clocks) -> void {
|
||||
@@ -61,7 +81,16 @@ auto M32X::PWM::power(bool reset) -> void {
|
||||
rsample = 0;
|
||||
lfifo.flush();
|
||||
rfifo.flush();
|
||||
lfifoLatch = 0x4000; // empty
|
||||
rfifoLatch = 0x4000; // empty
|
||||
mfifoLatch = 0x4000; // empty
|
||||
lfifoLatch = 0;
|
||||
rfifoLatch = 0;
|
||||
mfifoLatch = 0;
|
||||
updateFrequency();
|
||||
dcfilter_l.reset();
|
||||
dcfilter_r.reset();
|
||||
}
|
||||
|
||||
auto M32X::PWM::updateFrequency() -> void {
|
||||
n12 clocks = cycle - 1;
|
||||
if(clocks == 0) clocks++;
|
||||
stream->setFrequency((system.frequency() / 7.0) * 3.0 / clocks);
|
||||
}
|
||||
@@ -50,13 +50,8 @@ auto M32X::SH7604::instructionPrologue(u16 instruction) -> void {
|
||||
}
|
||||
|
||||
auto M32X::SH7604::step(u32 clocks) -> void {
|
||||
if(clocks > 0) {
|
||||
auto cycles = clocks;
|
||||
while (--cycles) {
|
||||
SH2::frt.run();
|
||||
SH2::wdt.run();
|
||||
}
|
||||
}
|
||||
SH2::frt.run(clocks);
|
||||
SH2::wdt.run(clocks);
|
||||
|
||||
Thread::step(clocks);
|
||||
cyclesUntilSh2Sync -= clocks;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
//started: 2016-07-08
|
||||
|
||||
#include <md/bus/bus.cpp>
|
||||
#include <md/cpu/cpu.cpp>
|
||||
#include <md/apu/apu.cpp>
|
||||
#include <md/vdp/vdp.cpp>
|
||||
#include <md/opn2/opn2.cpp>
|
||||
#include <md/m32x/m32x.cpp>
|
||||
#include <md/mcd/mcd.cpp>
|
||||
#include <md/system/system.cpp>
|
||||
#include <md/cartridge/cartridge.cpp>
|
||||
#include <md/controller/controller.cpp>
|
||||
@@ -1,4 +1,4 @@
|
||||
static const string SerializerVersion = "v135";
|
||||
static const string SerializerVersion = "v141.1";
|
||||
|
||||
auto System::serialize(bool synchronize) -> serializer {
|
||||
if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);
|
||||
|
||||
+50
-5
@@ -55,16 +55,61 @@ template<bool _h40, bool draw> auto VDP::DAC::pixel(u32 x) -> void {
|
||||
output<_h40>(pixel.backdrop << 11 | mode << 9 | color);
|
||||
}
|
||||
|
||||
auto pixelIndex(n9 hpos) -> maybe<u32> {
|
||||
if(vdp.h40()) {
|
||||
if(hpos < 0x00d || hpos > 0x167) return nothing;
|
||||
return (hpos-0x00d)*4;
|
||||
} else {
|
||||
if(hpos < 0x00b || hpos > 0x125) return nothing;
|
||||
return (hpos-0x00b)*5;
|
||||
}
|
||||
}
|
||||
|
||||
template<u8 _size, u16 _h32Pos, u16 _h40Pos> inline auto VDP::DAC::fillBorder(n8 ofst) -> void {
|
||||
if(!pixels) return;
|
||||
if(ofst >= _size) return;
|
||||
|
||||
u32 hpos = (vdp.h40() ? _h40Pos : _h32Pos) + ofst;
|
||||
u32 idx = pixelIndex(hpos)();
|
||||
n32 px = 0b101 << 9 | vdp.cram.color(vdp.io.backgroundColor);
|
||||
for(auto n : range((_size-ofst)*(vdp.h40()?4:5)))
|
||||
pixels[idx+n] = px;
|
||||
}
|
||||
|
||||
auto VDP::DAC::fillLeftBorder(n8 ofst) -> void {
|
||||
fillBorder<13,0x00b,0x00d>(ofst);
|
||||
}
|
||||
|
||||
auto VDP::DAC::fillRightBorder(n8 ofst) -> void {
|
||||
fillBorder<14,0x118,0x15a>(ofst);
|
||||
}
|
||||
|
||||
auto VDP::DAC::dot(n9 hpos, n9 color) -> void {
|
||||
if(!pixels) return;
|
||||
|
||||
if(auto i = pixelIndex(hpos)) {
|
||||
u32 index = i();
|
||||
n32 px = 0b101 << 9 | color;
|
||||
pixels[index++] = px;
|
||||
pixels[index++] = px;
|
||||
pixels[index++] = px;
|
||||
pixels[index++] = px;
|
||||
if(vdp.h40()) return;
|
||||
pixels[index++] = px;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool _h40> auto VDP::DAC::output(n32 color) -> void {
|
||||
*pixels++ = color;
|
||||
*pixels++ = color;
|
||||
*pixels++ = color;
|
||||
*pixels++ = color;
|
||||
*active++ = color;
|
||||
*active++ = color;
|
||||
*active++ = color;
|
||||
*active++ = color;
|
||||
if(_h40) return;
|
||||
*pixels++ = color;
|
||||
*active++ = color;
|
||||
}
|
||||
|
||||
auto VDP::DAC::power(bool reset) -> void {
|
||||
test = {};
|
||||
pixels = nullptr;
|
||||
active = nullptr;
|
||||
}
|
||||
+23
-9
@@ -9,13 +9,21 @@ auto VDP::DMA::synchronize() -> void {
|
||||
}
|
||||
}
|
||||
|
||||
auto VDP::DMA::fetch() -> void {
|
||||
if(active && !read) {
|
||||
auto address = mode.bit(0) << 23 | source << 1;
|
||||
data = bus.read(1, 1, address);
|
||||
read = 1;
|
||||
}
|
||||
}
|
||||
|
||||
auto VDP::DMA::run() -> bool {
|
||||
if(vdp.command.pending && !wait) {
|
||||
if(mode <= 1 && !vdp.fifo.full()) {
|
||||
if(mode <= 1 && !vdp.fifo.full() && read) {
|
||||
return load(), true;
|
||||
} else if(mode == 2 && vdp.fifo.empty()) {
|
||||
} else if(mode == 2 && vdp.fifo.empty() && !vdp.state.rambusy) {
|
||||
return fill(), true;
|
||||
} else if(mode == 3) {
|
||||
} else if(mode == 3 && !vdp.state.rambusy) {
|
||||
return copy(), true;
|
||||
}
|
||||
}
|
||||
@@ -23,15 +31,16 @@ auto VDP::DMA::run() -> bool {
|
||||
}
|
||||
|
||||
auto VDP::DMA::load() -> void {
|
||||
if(delay > 0) { delay--; return; }
|
||||
read = 0;
|
||||
vdp.fifo.write(vdp.command.target, vdp.command.address, data);
|
||||
|
||||
auto address = mode.bit(0) << 23 | source << 1;
|
||||
auto data = bus.read(1, 1, address);
|
||||
vdp.writeDataPort(data);
|
||||
vdp.debugger.dmaLoad(address, vdp.command.target, vdp.command.address, data);
|
||||
|
||||
source.bit(0,15)++;
|
||||
vdp.command.address += vdp.command.increment;
|
||||
if(--length == 0) {
|
||||
vdp.command.pending = 0;
|
||||
vdp.command.pending = 0; wait = 1; preload = 0;
|
||||
synchronize();
|
||||
}
|
||||
}
|
||||
@@ -42,12 +51,14 @@ auto VDP::DMA::fill() -> void {
|
||||
case 3: vdp.cram.write(vdp.command.address >> 1, data); break;
|
||||
case 5: vdp.vsram.write(vdp.command.address >> 1, data); break;
|
||||
}
|
||||
vdp.state.rambusy = 1;
|
||||
|
||||
vdp.debugger.dmaFill(vdp.command.target, vdp.command.address, data);
|
||||
|
||||
source.bit(0,15)++;
|
||||
vdp.command.address += vdp.command.increment;
|
||||
if(--length == 0) {
|
||||
vdp.command.pending = 0;
|
||||
vdp.command.pending = 0; wait = 1;
|
||||
synchronize();
|
||||
}
|
||||
}
|
||||
@@ -57,17 +68,20 @@ auto VDP::DMA::copy() -> void {
|
||||
if(!read) {
|
||||
read = 1;
|
||||
data = vdp.vram.readByte(source ^ 1);
|
||||
vdp.state.rambusy = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
read = 0;
|
||||
vdp.vram.writeByte(vdp.command.address ^ 1, data);
|
||||
vdp.state.rambusy = 1;
|
||||
|
||||
vdp.debugger.dmaCopy(source, vdp.command.target, vdp.command.address ^ 1, data);
|
||||
|
||||
source.bit(0,15)++;
|
||||
vdp.command.address += vdp.command.increment;
|
||||
if(--length == 0) {
|
||||
vdp.command.pending = 0;
|
||||
vdp.command.pending = 0; wait = 1;
|
||||
synchronize();
|
||||
}
|
||||
}
|
||||
|
||||
+34
-9
@@ -1,4 +1,19 @@
|
||||
auto VDP::FIFO::tick() -> void {
|
||||
for(auto& slot : slots)
|
||||
if(!slot.empty() && slot.latency > 0)
|
||||
slot.latency--;
|
||||
}
|
||||
|
||||
auto VDP::FIFO::advance() -> void {
|
||||
if(vdp.command.pending && vdp.dma.mode == 2) {
|
||||
if(slots[0].target == 1)
|
||||
vdp.dma.data = slots[0].data;
|
||||
else
|
||||
vdp.dma.data = slots[1].data; // fill data taken from next fifo slot (late fetch)
|
||||
vdp.dma.read = 1;
|
||||
vdp.dma.wait = 0; // start pending DMA if necessary
|
||||
}
|
||||
|
||||
swap(slots[0], slots[1]);
|
||||
swap(slots[1], slots[2]);
|
||||
swap(slots[2], slots[3]);
|
||||
@@ -6,6 +21,8 @@ auto VDP::FIFO::advance() -> void {
|
||||
|
||||
auto VDP::FIFO::run() -> bool {
|
||||
if(empty()) return false;
|
||||
if(slots[0].latency > 0) return false;
|
||||
if(vdp.dma.active && vdp.dma.preload > 0) return false;
|
||||
|
||||
if(slots[0].target == 1 && vdp.vram.mode == 0) {
|
||||
if(slots[0].lower) {
|
||||
@@ -16,10 +33,22 @@ auto VDP::FIFO::run() -> bool {
|
||||
if(slots[0].upper) {
|
||||
slots[0].upper = 0;
|
||||
vdp.vram.writeByte(slots[0].address, slots[0].data.byte(1));
|
||||
if(vdp.command.pending && vdp.dma.mode == 2) {
|
||||
vdp.dma.data = slots[0].data;
|
||||
vdp.dma.wait = 0; // start pending DMA
|
||||
}
|
||||
return advance(), true;
|
||||
}
|
||||
}
|
||||
|
||||
// If the target is VRAM read, there is assumed to be no effect.
|
||||
// However, fifo timing must account for VRAM byte access (as above).
|
||||
if(slots[0].target == 0 && vdp.vram.mode == 0) {
|
||||
debug(unusual, "[VDP::FIFO] write target = 0 (VRAM read)");
|
||||
if(slots[0].lower) {
|
||||
slots[0].lower = 0;
|
||||
// null action
|
||||
return true;
|
||||
}
|
||||
if(slots[0].upper) {
|
||||
slots[0].upper = 0;
|
||||
// null action
|
||||
return advance(), true;
|
||||
}
|
||||
}
|
||||
@@ -33,11 +62,6 @@ auto VDP::FIFO::run() -> bool {
|
||||
else
|
||||
debug(unusual, "[VDP::FIFO] write target = 0x", hex(slots[0].target));
|
||||
|
||||
if(vdp.command.pending && vdp.dma.mode == 2) {
|
||||
vdp.dma.data = slots[1].data; // fill data taken from next fifo slot (late fetch)
|
||||
vdp.dma.wait = 0; // start pending DMA
|
||||
}
|
||||
|
||||
slots[0].lower = 0;
|
||||
slots[0].upper = 0;
|
||||
return advance(), true;
|
||||
@@ -60,6 +84,7 @@ auto VDP::FIFO::write(n4 target, n17 address, n16 data) -> void {
|
||||
slot.data = data;
|
||||
slot.upper = 1;
|
||||
slot.lower = 1;
|
||||
slot.latency = 2;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-3
@@ -154,7 +154,7 @@ auto VDP::readControlPort() -> n16 {
|
||||
result.bit( 0) = Region::PAL();
|
||||
result.bit( 1) = command.pending;
|
||||
result.bit( 2) = hblank();
|
||||
result.bit( 3) = vblank() || !io.displayEnable;
|
||||
result.bit( 3) = vblank() || !displayEnable();
|
||||
result.bit( 4) = io.interlaceMode.bit(0) && field();
|
||||
result.bit( 5) = sprite.collision;
|
||||
result.bit( 6) = sprite.overflow;
|
||||
@@ -187,8 +187,13 @@ auto VDP::writeControlPort(n16 data) -> void {
|
||||
|
||||
prefetch.read(command.target, command.address);
|
||||
|
||||
if(command.pending && dma.mode == 1) dma.delay = 4; // based on measurement noted by Mask of Destiny
|
||||
dma.wait = dma.mode == 2;
|
||||
if(command.pending && dma.mode != 2) {
|
||||
// dma preload init is tuned based on Direct Color DMA demos; lower values lead to instability
|
||||
if(dma.mode < 2) dma.preload = 7;
|
||||
dma.read = 0;
|
||||
dma.wait = 0;
|
||||
}
|
||||
|
||||
dma.synchronize();
|
||||
return;
|
||||
}
|
||||
|
||||
+168
-135
@@ -3,25 +3,74 @@ auto VDP::step(u32 clocks) -> void {
|
||||
Thread::synchronize(cpu);
|
||||
}
|
||||
|
||||
template<bool _h40> auto VDP::tick() -> void {
|
||||
step(cycles[0] + cycles[1]);
|
||||
cycles += 2;
|
||||
state.hcounter++;
|
||||
template<bool _h40> auto VDP::fullslotStep() -> void {
|
||||
// EDCLK hybrid rate : 15cyc @ MClk/5 + 2cyc @ MClk/4
|
||||
static u8 EDCLK[17] = {5,5,5,5,5, 5,5,5,5,5, 5,5,5,5,5, 4,4};
|
||||
if(( _h40 && latch.clockSelect && hcounter() >= 0xe6 && hcounter() <= 0xf6) ||
|
||||
(!_h40 && latch.clockSelect && hcounter() >= 0xec && hcounter() <= 0xfc)) {
|
||||
u32 q1 = EDCLK[state.edclkPos];
|
||||
state.edclkPos = (state.edclkPos+1) % 17;
|
||||
u32 q2 = EDCLK[state.edclkPos];
|
||||
state.edclkPos = (state.edclkPos+1) % 17;
|
||||
u32 q3 = EDCLK[state.edclkPos];
|
||||
state.edclkPos = (state.edclkPos+1) % 17;
|
||||
u32 q4 = EDCLK[state.edclkPos];
|
||||
state.edclkPos = (state.edclkPos+1) % 17;
|
||||
step(q1+q2+q3+q4);
|
||||
return;
|
||||
}
|
||||
if(_h40)
|
||||
step(4+4+4+4); // MClk/4
|
||||
else
|
||||
step(5+5+5+5); // MClk/5
|
||||
}
|
||||
|
||||
if(_h40) {
|
||||
if(hcounter() == 0x00) hblank(0), vedge();
|
||||
else if(hcounter() == 0xa3) vtick();
|
||||
else if(hcounter() == 0xb3) hblank(1);
|
||||
else if(hcounter() == 0xb6) state.hcounter = 0xe4;
|
||||
} else {
|
||||
if(hcounter() == 0x00) hblank(0), vedge();
|
||||
else if(hcounter() == 0x83) vtick();
|
||||
else if(hcounter() == 0x93) hblank(1);
|
||||
else if(hcounter() == 0x94) state.hcounter = 0xe9;
|
||||
template<bool _h40, bool _refresh> auto VDP::tick() -> void {
|
||||
// Run DMA here -- fifo & prefetch have ram priority, so somes ops may be blocked
|
||||
dma.run();
|
||||
|
||||
fullslotStep<_h40>();
|
||||
htick<_h40>(); // +2 pixels
|
||||
|
||||
if(cram.bus.active) {
|
||||
vdp.dac.dot(hcounter()*2+1, cram.bus.data);
|
||||
|
||||
// DAC dot artifacts may be drawn continuously in the case of consectutive writes.
|
||||
// We can detect for this by checking for an impending CRAM write thru the fifo.
|
||||
// If refresh or fifo delay occurs, the data will not be updated, resulting in an extended dot.
|
||||
// Note: we're not currently checking for back-to-back slots when display is enabled.
|
||||
if(displayEnable() || fifo.slots[0].empty() || fifo.slots[0].target != 3)
|
||||
cram.bus.active = 0;
|
||||
else {
|
||||
if(fifo.slots[0].latency > 1 || vram.refreshing) cram.bus.data = vdp.cram.color(vdp.io.backgroundColor);
|
||||
vdp.dac.dot(hcounter()*2+2, cram.bus.data);
|
||||
}
|
||||
}
|
||||
|
||||
irq.poll();
|
||||
vram.refreshing = 0;
|
||||
// There is reportedly a latch effect when enabling the display, but it might be a fixed delay
|
||||
// rather than a wait until the fifo clears. So, this is precautionary and not necessily correct.
|
||||
if(latch.displayEnable > io.displayEnable || fifo.empty())
|
||||
latch.displayEnable = io.displayEnable;
|
||||
|
||||
if(_refresh) {
|
||||
vram.refreshing = 1;
|
||||
|
||||
// The start of a DMA load will be aligned if it coincides with a refresh slot.
|
||||
// The duration may differ between H32 & H40 due to pixel clock, or this may just
|
||||
// be the result of some other emulation inaccuracy. Either way, this works for now.
|
||||
if(dma.active && dma.preload > 0) dma.preload = h40()?6:4;
|
||||
} else {
|
||||
fifo.tick();
|
||||
|
||||
// When display is blanked, DMA load fetch may be performed in every slot
|
||||
// except for refresh slots and any slot immediately following refresh.
|
||||
if(dma.active && !vram.refreshing) dma.fetch();
|
||||
vram.refreshing = 0;
|
||||
|
||||
if(dma.active && dma.preload > 0) dma.preload--;
|
||||
}
|
||||
|
||||
state.rambusy = 1;
|
||||
}
|
||||
|
||||
auto VDP::vblankcheck() -> void {
|
||||
@@ -35,6 +84,26 @@ auto VDP::vblankcheck() -> void {
|
||||
}
|
||||
}
|
||||
|
||||
template<bool _h40> auto VDP::htick() -> void {
|
||||
state.hcounter++;
|
||||
|
||||
if(_h40) {
|
||||
if(hcounter() == 0x00) vedge();
|
||||
else if(hcounter() == 0x05) hblank(0);
|
||||
else if(hcounter() == 0xa5) vtick();
|
||||
else if(hcounter() == 0xb3) hblank(1);
|
||||
else if(hcounter() == 0xb6) state.hcounter = 0xe4;
|
||||
} else {
|
||||
if(hcounter() == 0x00) vedge();
|
||||
else if(hcounter() == 0x05) hblank(0);
|
||||
else if(hcounter() == 0x85) vtick();
|
||||
else if(hcounter() == 0x93) hblank(1);
|
||||
else if(hcounter() == 0x94) state.hcounter = 0xe9;
|
||||
}
|
||||
|
||||
irq.poll();
|
||||
}
|
||||
|
||||
auto VDP::vtick() -> void {
|
||||
if(vblank()) {
|
||||
irq.hblank.counter = irq.hblank.frequency;
|
||||
@@ -44,15 +113,10 @@ auto VDP::vtick() -> void {
|
||||
debugger.interrupt(CPU::Interrupt::HorizontalBlank);
|
||||
}
|
||||
|
||||
state.vcounter++;
|
||||
if(v28()) {
|
||||
if(vcounter() == 0x0eb && Region::NTSC()) state.vcounter = 0x1e5;
|
||||
if(vcounter() == 0x103 && Region::PAL ()) state.vcounter = 0x1ca;
|
||||
}
|
||||
if(v30()) {
|
||||
if(vcounter() == 0x200 && Region::NTSC()) state.vcounter = 0x000;
|
||||
if(vcounter() == 0x10b && Region::PAL ()) state.vcounter = 0x1d2;
|
||||
}
|
||||
if(vcounter() == state.bottomline)
|
||||
state.vcounter = state.topline;
|
||||
else
|
||||
state.vcounter++;
|
||||
|
||||
vblankcheck();
|
||||
}
|
||||
@@ -68,6 +132,7 @@ auto VDP::hblank(bool line) -> void {
|
||||
|
||||
auto VDP::vblank(bool line) -> void {
|
||||
irq.vblank.transitioned |= state.vblank ^ line;
|
||||
if(state.vblank > line) state.topline = vcounter();
|
||||
state.vblank = line;
|
||||
}
|
||||
|
||||
@@ -87,37 +152,40 @@ auto VDP::vedge() -> void {
|
||||
}
|
||||
|
||||
auto VDP::slot() -> void {
|
||||
if(!fifo.run()) prefetch.run();
|
||||
dma.run();
|
||||
}
|
||||
|
||||
auto VDP::refresh() -> void {
|
||||
vram.refreshing = 1;
|
||||
state.rambusy = 0;
|
||||
if(!(state.rambusy = fifo.run()))
|
||||
state.rambusy = prefetch.run();
|
||||
}
|
||||
|
||||
auto VDP::main() -> void {
|
||||
latch.displayWidth = io.displayWidth;
|
||||
latch.clockSelect = io.clockSelect;
|
||||
state.edclkPos = 0;
|
||||
if(h32()) mainH32();
|
||||
else
|
||||
if(h40()) mainH40();
|
||||
if(vcounter() == 0) {
|
||||
if(vcounter() == state.bottomline) {
|
||||
screen->setColorBleedWidth(latch.displayWidth ? 4 : 5);
|
||||
latch.interlace = io.interlaceMode == 3;
|
||||
latch.overscan = io.overscan;
|
||||
frame();
|
||||
state.field ^= 1;
|
||||
updateScreenParams();
|
||||
}
|
||||
}
|
||||
|
||||
auto VDP::mainH32() -> void {
|
||||
auto pixels = dac.pixels = vdp.pixels();
|
||||
cycles = &cyclesH32[edclk()][0];
|
||||
dac.pixels = vdp.pixels();
|
||||
auto pixels = dac.active = dac.pixels+13*5;
|
||||
state.hcounter = 0;
|
||||
|
||||
sprite.begin();
|
||||
if(dac.pixels) blocks<false, true>();
|
||||
else blocks<false, false>();
|
||||
|
||||
if(Mega32X()) m32x.vdp.scanline(pixels, vcounter());
|
||||
if(dac.pixels) {
|
||||
blocks<false, true>();
|
||||
if(Mega32X()) m32x.vdp.scanline(pixels, vcounter());
|
||||
} else {
|
||||
blocks<false, false>();
|
||||
}
|
||||
|
||||
tick<false>(); slot();
|
||||
tick<false>(); slot();
|
||||
@@ -125,24 +193,30 @@ auto VDP::mainH32() -> void {
|
||||
layers.vscrollFetch();
|
||||
sprite.end();
|
||||
|
||||
for(auto cycle : range(13)) {
|
||||
for(auto cycle : range(4)) {
|
||||
tick<false>(); sprite.patternFetch(cycle + 0);
|
||||
}
|
||||
tick<false>(); slot();
|
||||
for(auto cycle : range(13)) {
|
||||
tick<false>(); sprite.patternFetch(cycle + 13);
|
||||
tick<false>(); sprite.patternFetch(cycle + 4); sprite.scan();
|
||||
}
|
||||
// Placement of this free slot conflicts with documentation by Nemesis which has it 4 slots earlier,
|
||||
// but this works more reliably with the Direct Color DMA demos.
|
||||
tick<false>(); slot();
|
||||
// window begin call (reg latch) is placed here due to garbage line edge case in International Superstar Soccer Deluxe (E)
|
||||
window.begin();
|
||||
for(auto cycle : range(9)) {
|
||||
tick<false>(); sprite.patternFetch(cycle + 17); sprite.scan();
|
||||
}
|
||||
tick<false>(); slot();
|
||||
|
||||
layerA.begin();
|
||||
layerB.begin();
|
||||
window.begin();
|
||||
|
||||
tick<false>(); layers.hscrollFetch();
|
||||
tick<false>(); sprite.patternFetch(26);
|
||||
tick<false>(); sprite.patternFetch(27);
|
||||
tick<false>(); sprite.patternFetch(28);
|
||||
tick<false>(); sprite.patternFetch(29);
|
||||
tick<false>(); sprite.patternFetch(26); sprite.scan();
|
||||
tick<false>(); sprite.patternFetch(27); sprite.scan();
|
||||
tick<false>(); sprite.patternFetch(28); sprite.scan();
|
||||
tick<false>(); sprite.patternFetch(29); sprite.scan();
|
||||
|
||||
layers.vscrollFetch(-1);
|
||||
layerA.attributesFetch();
|
||||
@@ -150,24 +224,31 @@ auto VDP::mainH32() -> void {
|
||||
window.attributesFetch(-1);
|
||||
|
||||
tick<false>(); layerA.mappingFetch(-1);
|
||||
tick<false>(); !displayEnable() ? refresh() : sprite.patternFetch(30);
|
||||
tick<false>(); layerA.patternFetch( 0);
|
||||
tick<false>(); layerA.patternFetch( 1);
|
||||
if(!displayEnable()) {
|
||||
tick<false,true>(); //refresh
|
||||
} else {
|
||||
tick<false>(); sprite.patternFetch(30); sprite.scan();
|
||||
}
|
||||
tick<false>(); layerA.patternFetch( 0); sprite.scan();
|
||||
tick<false>(); layerA.patternFetch( 1); sprite.scan();
|
||||
tick<false>(); layerB.mappingFetch(-1);
|
||||
tick<false>(); sprite.patternFetch(31);
|
||||
tick<false>(); layerB.patternFetch( 0);
|
||||
tick<false>(); layerB.patternFetch( 1);
|
||||
tick<false>(); sprite.patternFetch(31); sprite.scan();
|
||||
tick<false>(); layerB.patternFetch( 0); sprite.scan();
|
||||
tick<false>(); layerB.patternFetch( 1); sprite.scan();
|
||||
}
|
||||
|
||||
auto VDP::mainH40() -> void {
|
||||
auto pixels = dac.pixels = vdp.pixels();
|
||||
cycles = &cyclesH40[edclk()][0];
|
||||
dac.pixels = vdp.pixels();
|
||||
auto pixels = dac.active = dac.pixels+13*4;
|
||||
state.hcounter = 0;
|
||||
|
||||
sprite.begin();
|
||||
if(dac.pixels) blocks<true, true>();
|
||||
else blocks<true, false>();
|
||||
|
||||
if(Mega32X()) m32x.vdp.scanline(pixels, vcounter());
|
||||
if(dac.pixels) {
|
||||
blocks<true, true>();
|
||||
if(Mega32X()) m32x.vdp.scanline(pixels, vcounter());
|
||||
} else {
|
||||
blocks<true, false>();
|
||||
}
|
||||
|
||||
tick<true>(); slot();
|
||||
tick<true>(); slot();
|
||||
@@ -175,12 +256,15 @@ auto VDP::mainH40() -> void {
|
||||
layers.vscrollFetch();
|
||||
sprite.end();
|
||||
|
||||
for(auto cycle : range(23)) {
|
||||
for(auto cycle : range(4)) {
|
||||
tick<true>(); sprite.patternFetch(cycle + 0);
|
||||
}
|
||||
for(auto cycle : range(19)) {
|
||||
tick<true>(); sprite.patternFetch(cycle + 4); sprite.scan();
|
||||
}
|
||||
tick<true>(); slot();
|
||||
for(auto cycle : range(11)) {
|
||||
tick<true>(); sprite.patternFetch(cycle + 23);
|
||||
tick<true>(); sprite.patternFetch(cycle + 23); sprite.scan();
|
||||
}
|
||||
|
||||
layerA.begin();
|
||||
@@ -188,10 +272,10 @@ auto VDP::mainH40() -> void {
|
||||
window.begin();
|
||||
|
||||
tick<true>(); layers.hscrollFetch();
|
||||
tick<true>(); sprite.patternFetch(34);
|
||||
tick<true>(); sprite.patternFetch(35);
|
||||
tick<true>(); sprite.patternFetch(36);
|
||||
tick<true>(); sprite.patternFetch(37);
|
||||
tick<true>(); sprite.patternFetch(34); sprite.scan();
|
||||
tick<true>(); sprite.patternFetch(35); sprite.scan();
|
||||
tick<true>(); sprite.patternFetch(36); sprite.scan();
|
||||
tick<true>(); sprite.patternFetch(37); sprite.scan();
|
||||
|
||||
layers.vscrollFetch(-1);
|
||||
layerA.attributesFetch();
|
||||
@@ -199,25 +283,34 @@ auto VDP::mainH40() -> void {
|
||||
window.attributesFetch(-1);
|
||||
|
||||
tick<true>(); layerA.mappingFetch(-1);
|
||||
tick<true>(); !displayEnable() ? refresh() : sprite.patternFetch(38);
|
||||
tick<true>(); layerA.patternFetch( 0);
|
||||
tick<true>(); layerA.patternFetch( 1);
|
||||
if(!displayEnable()) {
|
||||
tick<true,true>(); //refresh
|
||||
} else {
|
||||
tick<true>(); sprite.patternFetch(38); sprite.scan();
|
||||
}
|
||||
tick<true>(); layerA.patternFetch( 0); sprite.scan();
|
||||
tick<true>(); layerA.patternFetch( 1); sprite.scan();
|
||||
tick<true>(); layerB.mappingFetch(-1);
|
||||
tick<true>(); sprite.patternFetch(39);
|
||||
tick<true>(); layerB.patternFetch( 0);
|
||||
tick<true>(); layerB.patternFetch( 1);
|
||||
tick<true>(); sprite.patternFetch(39); sprite.scan();
|
||||
tick<true>(); layerB.patternFetch( 0); sprite.scan();
|
||||
tick<true>(); layerB.patternFetch( 1); sprite.scan();
|
||||
}
|
||||
|
||||
template<bool _h40, bool _pixels> auto VDP::blocks() -> void {
|
||||
bool den = displayEnable();
|
||||
bool vc = vcounter() == 0x1ff;
|
||||
bool top = vcounter() == state.topline;
|
||||
dac.fillLeftBorder();
|
||||
for(auto block : range(_h40 ? 20 : 16)) {
|
||||
layers.vscrollFetch(block);
|
||||
layerA.attributesFetch();
|
||||
layerB.attributesFetch();
|
||||
window.attributesFetch(block);
|
||||
tick<_h40>(); layerA.mappingFetch(block);
|
||||
tick<_h40>(); (block & 3) != 3 ? slot() : refresh();
|
||||
if((block & 3) == 3) {
|
||||
tick<_h40,true>(); //refresh
|
||||
} else {
|
||||
tick<_h40>(); slot();
|
||||
}
|
||||
bool den = displayEnable();
|
||||
tick<_h40>(); layerA.patternFetch(block * 2 + 2);
|
||||
tick<_h40>(); layerA.patternFetch(block * 2 + 3);
|
||||
tick<_h40>(); layerB.mappingFetch(block);
|
||||
@@ -226,73 +319,13 @@ template<bool _h40, bool _pixels> auto VDP::blocks() -> void {
|
||||
tick<_h40>(); layerB.patternFetch(block * 2 + 3);
|
||||
|
||||
if(_pixels) {
|
||||
if(!den || vc) {
|
||||
if(!den || top) {
|
||||
for(auto pixel: range(16)) dac.pixel<_h40, false>(block * 16 + pixel);
|
||||
} else {
|
||||
for(auto pixel: range(16)) dac.pixel<_h40, true>(block * 16 + pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
dac.fillRightBorder();
|
||||
}
|
||||
|
||||
//timings are approximations; exact positions of slow/normal/fast cycles are not known
|
||||
auto VDP::generateCycleTimings() -> void {
|
||||
//full lines
|
||||
//==========
|
||||
|
||||
//H32/DCLK: 342 slow + 0 normal + 0 fast = 3420 cycles
|
||||
for(auto cycle : range(342)) cyclesH32[0][cycle * 1] = 10;
|
||||
|
||||
//H32/EDCLK: 21 slow + 3 normal + 318 fast = 2781 cycles
|
||||
for(auto cycle : range(342)) cyclesH32[1][cycle * 1] = 8;
|
||||
for(auto cycle : range( 24)) cyclesH32[1][cycle * 14] = 10;
|
||||
for(auto cycle : range( 3)) cyclesH32[1][cycle * 14] = 9;
|
||||
|
||||
//H40/DCLK: 0 slow + 0 normal + 420 fast = 3360 cycles
|
||||
for(auto cycle : range(420)) cyclesH40[0][cycle * 1] = 8;
|
||||
|
||||
//H40/EDCLK: 28 slow + 4 normal + 388 fast = 3420 cycles
|
||||
for(auto cycle : range(420)) cyclesH40[1][cycle * 1] = 8;
|
||||
for(auto cycle : range( 32)) cyclesH40[1][cycle * 13] = 10;
|
||||
for(auto cycle : range( 4)) cyclesH40[1][cycle * 13] = 9;
|
||||
|
||||
//half lines
|
||||
//==========
|
||||
|
||||
//H32/DCLK: 171 slow + 0 normal + 0 fast = 1710 cycles
|
||||
for(auto cycle : range(171)) halvesH32[0][cycle * 1] = 10;
|
||||
|
||||
//H32/EDCLK: 10 slow + 2 normal + 159 fast = 1390 cycles
|
||||
for(auto cycle : range(171)) halvesH32[1][cycle * 1] = 8;
|
||||
for(auto cycle : range( 12)) halvesH32[1][cycle * 14] = 10;
|
||||
for(auto cycle : range( 2)) halvesH32[1][cycle * 14] = 9;
|
||||
|
||||
//H40/DCLK: 0 slow + 0 normal + 210 fast = 1680 cycles
|
||||
for(auto cycle : range(210)) halvesH40[0][cycle * 1] = 8;
|
||||
|
||||
//H40/EDCLK: 14 slow + 2 normal + 194 fast = 1710 cycles
|
||||
for(auto cycle : range(210)) halvesH40[1][cycle * 1] = 8;
|
||||
for(auto cycle : range( 16)) halvesH40[1][cycle * 13] = 10;
|
||||
for(auto cycle : range( 2)) halvesH40[1][cycle * 13] = 9;
|
||||
|
||||
//active even half lines
|
||||
//======================
|
||||
|
||||
//H32/DCLK: 171 slow + 0 normal + 0 fast = 1710 cycles
|
||||
for(auto cycle : range(171)) extrasH32[0][cycle * 1] = 10;
|
||||
|
||||
//H32/EDCLK: 21 slow + 3 normal + 147 fast = 1413 cycles
|
||||
for(auto cycle : range(171)) extrasH32[1][cycle * 1] = 8;
|
||||
for(auto cycle : range( 24)) extrasH32[1][cycle * 7] = 10;
|
||||
for(auto cycle : range( 3)) extrasH32[1][cycle * 7] = 9;
|
||||
|
||||
//H40/DCLK: 0 slow + 0 normal + 210 fast = 1680 cycles
|
||||
for(auto cycle : range(171)) extrasH40[0][cycle * 1] = 8;
|
||||
|
||||
//H40/EDCLK: 28 slow + 4 normal + 178 fast = 1740 cycles
|
||||
for(auto cycle : range(171)) extrasH40[1][cycle * 1] = 8;
|
||||
for(auto cycle : range( 32)) extrasH40[1][cycle * 5] = 10;
|
||||
for(auto cycle : range( 4)) extrasH40[1][cycle * 5] = 9;
|
||||
|
||||
cycles = nullptr;
|
||||
}
|
||||
Loaded 100 of 617 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user