Compare commits

...
123 Commits
Author SHA1 Message Date
Tim Hentenaar 728105b28e ps1: gpu: macro -> constexpr 2025-04-23 09:43:14 +01:00
Tim Hentenaar 3d88e24559 ps1: gpu: Ensure hi >= lo when calling std::clamp() 2025-04-22 13:41:06 +01:00
Adrian Siekierka a61ce6449f ws: support 8-bit bus width ROMs 2025-04-22 11:05:26 +01:00
jcm 63aa9ec5e4 hiro: Use GTK native file dialogs for portal compatibility 2025-04-21 09:49:36 +01:00
remutroandjcm 50e42627e2 Update mia/medium/super-famicom.cpp
Explicit cast so we hit the right constructor.

Co-authored-by: jcm <[email protected]>
2025-04-18 19:01:30 +01:00
remutro 3a46652b15 [SNES] Remove unnecessary additional string allocation 2025-04-18 19:01:30 +01:00
remutro 7528feabe8 [SNES] Update special case Sufami Turbo handling for Fuwafuwa Panic 2025-04-18 19:01:30 +01:00
remutro e57a80ea1b [SNES] Fix loading of some games using firmware by removing unnecessary data definition/reference to rom 2025-04-18 19:01:30 +01:00
User 4c0a03b35a gba: approximate PRAM access stalls in scanline renderer 2025-04-18 19:00:16 +01:00
jcm a247271e35 CI: Preserve symlinks in artifacts 2025-04-10 21:18:40 +01:00
jcm d61cfa905a Update ares-deps to 2025-04-09 release
Includes:
* Stop vendoring the librashader header on Linux
* deps.windows: Update librashader to 0.6.3
* deps.macos: Update librashader to 0.6.3
* Update slang-shaders to ref 25311dc
* Pin Rust nightly to version contemporary with librashader version
* Establish macOS deployment targets
* deps.macos: Update SDL to 3.2.10
* deps.windows: Update SDL to 3.2.10
2025-04-10 14:48:52 +01:00
jcm 9987bf18b2 ruby: Update to SDL3 2025-04-10 14:48:49 +01:00
Adrian Siekierka 8a80d32519 ws: PPU palette 4-7, 12-15 bit 0-2 reads as zero 2025-04-08 17:07:12 +01:00
remutro 5d0de5fbb3 Update MD serialization.cpp
Serialization for the MD, specific to the ym2612 audio implementation was changed, but there wasn't a version bump. This corrects this missing contribution.
2025-04-04 07:18:49 +01:00
jsgroth f06168e3f5 ym2612: fix LFO AM enabled bit not functioning 2025-04-04 06:52:02 +01:00
Adrian Siekierka 828f847efe ws: fix RTC cartridge protocol emulation 2025-04-03 18:36:34 +01:00
jcm d8c997ca8a build: Enable CHD support via build option 2025-04-03 10:47:15 +01:00
jcm 93137b76b4 mia: Add generic CD data sector read method 2025-04-03 10:47:15 +01:00
jcm b889aff3d4 desktop-ui: Don't show missing firmware error twice 2025-04-02 10:42:35 +01:00
Giovanni Bajo 70f3bfa00c n64: disable RDP readbacks from super-sampled image
This fixes some tests in the official RDP testsuite
2025-04-01 17:53:16 +01:00
User d942746f74 gb: implement undocumented PCM12 and PCM34 registers 2025-03-31 18:52:41 +01:00
jcm 1c05456bec libco: Define LIBCO_MPROTECT by default on aarch64 2025-03-27 15:36:11 +00:00
User 240363d31a gba: add renderingCycle to scanline renderer 2025-03-27 10:37:54 +00:00
User ebb3bcb37b gba: improve handling of mid-scanline writes to linear background scroll registers 2025-03-27 10:37:54 +00:00
remutro ab14fb1d10 [MS] Update PAL only entries in mia DB 2025-03-26 14:32:33 +00:00
User 7ba94fe7e4 gba: override save type in known problematic cases 2025-03-24 07:28:00 +00:00
jcm 69ea086d95 build: Remove invalid arguments from custom commands 2025-03-24 07:27:40 +00:00
jcm a510ac7e55 build: Don't search for usbhid on non-BSD 2025-03-24 07:27:40 +00:00
jcm ac98fd8d9c build: Fix macOS SDK detection for CMake 4.0 2025-03-24 07:27:40 +00:00
remutro 122820753e Add DB entry for Back to the Future III which was PAL only 2025-03-20 11:59:56 +00:00
remutro e355fbe88c [N64] Fix Id for Rampage 2 - Universal Tour in DB 2025-03-20 11:59:49 +00:00
Adrian Siekierka c1efa05d65 ws: fix regression in APU logic 2025-03-16 17:46:58 +00:00
Adrian Siekierka da071246fe v30mz: make I/O access timing marginally more accurate 2025-03-15 15:49:37 +00:00
Giovanni Bajo cca0ecf202 n64: add IPL2 checksum failure as accuracy flag
As part of the boot security, IPL2 (run by CPU), PIF and CIC collaborate
to verify that the IPL3 (stored at the beginning of the ROM) is valid
and not tampered, by running a checksum. If the checksum is invalid,
the console is halted immediately.

Ares emulates this behavior correctly. Sometimes, though, it might be
useful for development purposes to let some ROM boot even with a failing
checksum. Given that disabling the check was non-obvious, I'm adding
an accuracy compile-time flag for now.
2025-03-15 15:49:28 +00:00
remutro 17aee9071d Fix check for pif rom when loading Aleck64 games 2025-03-15 15:49:14 +00:00
User b92ae8a14c gba: improve linear background VRAM access timings 2025-03-15 07:59:34 +00:00
jcm 4ae42b2d44 desktop-ui: Fix up app icon, macOS asset compilation 2025-03-13 20:01:42 +00:00
Adrian Siekierka 2a11da200d ws: APU accuracy fixes
- the noise LFSR reset bit takes effect immediately
- channel period counters tick only when they're enabled, including
  if channel 2 is in voice mode or channel 4 is in noise mode
- the noise LFSR only ticks if channel 4 is enabled in noise mode
2025-03-09 21:32:21 +00:00
Giovanni Bajo cbf4a939aa n64: allow booting ROMs with non-standard PI DOM1 parms in the header
All commercial ROMs in N64 have the value 0x80371240 in the first 4
bytes. This has long been used by emulators both as a magic number to
"detect a N64 rom", and as a way to detect endianess of the dump.

In reality, the first byte (0x80) is unused/ignored by the console, while
the other 3 (0x371240) are just parameters used to configure cartridge
access (basically, ROM speed). There's absolutely nothing that prevents
a ROM with other values to boot. Ares currently fails to honor this
by refusing booting ROMs with different values, but this is just wrong.

To fix this, we change approach and use the CIC detection algorithm to
also detect the endianess, given that it's based on a checksum match.
If we can't find a match, we tried the two common types of byteswaps,
hoping to find a match. Eventually, we fallback to assume it's a
big-endian dump, and needs the common CIC-6102.

This means that analyze() will now only reject ROMs with size lower
than 4 KiB, but that's correct. Given that 4 KiB are loaded by the
IPL2, we assume that minimum size is required. Besides that, there is
nothing else in the ROM that can be used to "detect" a N64 ROM, so
it's correct to just let it boot and see what happens.
2025-03-09 19:20:31 +00:00
Adrian Siekierka d10a8498e2 ws: clear serial IRQs if serial block is disabled 2025-03-09 19:19:57 +00:00
Giovanni Bajo 2eb6bf6df8 gamepad: bank switch command must be ignored for single-bank paks 2025-03-05 12:21:44 +00:00
Cuyler36 35ede44b80 Support dynamic bank counts for N64 Contorller Paks 2025-03-05 12:21:44 +00:00
Giovanni Bajo 0c011fb2d4 n64: add O.D.T. to gamelist 2025-03-05 12:21:30 +00:00
Giovanni Bajo a006ef97e7 vi: make sure we refresh the screen even when VI is disabled 2025-03-04 20:07:46 +00:00
User 14a4f32997 gba: perform VRAM accesses in bitmap and affine modes even when no pixel is rendered 2025-03-04 20:06:28 +00:00
Adrian Siekierka 25c1e281cc v30mz, ws: fix HLT regression 2025-03-03 22:47:01 +00:00
invertegoandpng183 3ee7556bb7 arm7tdmi: add missing idle cycles
Co-authored-by: png183 <[email protected]>
2025-03-03 13:15:10 +00:00
invertegoandpng183 a5b11b13d0 arm7tdmi: move to status register bit 4 fix
Co-authored-by: png183 <[email protected]>
2025-03-03 13:15:10 +00:00
invertego 01e6cc1443 arm7tdmi: thumb carry flag fixes 2025-03-03 13:15:10 +00:00
invertegoandpng183 f5dace0a0f arm7tdmi: r15 fixes
Co-authored-by: png183 <[email protected]>
2025-03-03 13:15:10 +00:00
invertego 6b5ebb2882 arm7tdmi: fix disassembler mnemonic 2025-03-03 13:15:10 +00:00
Adrian Siekierka 140782662b v30mz: breaks are also delayed by one instruction 2025-03-02 18:13:58 +00:00
Adrian Siekierka 3daec1184a v30mz, ws: fix interrupt triggering edge cases on interrupt flag change 2025-03-02 18:13:58 +00:00
Adrian Siekierka 7e4386c1bf v30mz: fix interrupt triggering incorrectly around segment move instructions 2025-03-02 18:13:58 +00:00
Adrian Siekierka 8eba8ad784 ws: make level interrupts take immediate effect 2025-03-02 18:13:58 +00:00
png183 08377d02e2 gba: update list of game codes that require mirroring 2025-03-02 18:13:11 +00:00
Luke Usher ea4ad6890a md: 32X display is offset by approx 3.25 pixels to the right in H32 mode 2025-02-27 14:07:21 +00:00
Luke Usher 50be2e4e63 fc: add delay when changing FDS disk state 2025-02-27 11:48:05 +00:00
jcm effc5614bc CI: Align MSYS2 git line ending settings with system git 2025-02-23 14:47:32 +00:00
jcm 5f81464857 misc: CMake formatting pass 2025-02-22 08:50:47 +00:00
jcm bd8442605e build: Move Linux librashader path hint to package finder 2025-02-22 08:50:47 +00:00
jcm da312032de build: Prevent macOS deployment target being modified during auto-regeneration 2025-02-22 08:50:47 +00:00
jcm c509dd3964 Remove CRLFs from ares/n64/CMakeLists.txt 2025-02-22 08:50:47 +00:00
jcm 10a4d480de CI: Unset draft status on tagged releases 2025-02-22 08:50:47 +00:00
User 8c3ead7cb0 gba: latch lower ROM address lines during burst transfer 2025-02-22 08:50:32 +00:00
jcm 1b36d47554 ruby: SDL2 input fixups 2025-02-21 08:37:07 +00:00
jcm 2c940b03a1 nall: Retrieve executable path more reliably 2025-02-21 00:16:50 +00:00
User 7a5174a712 gb: tick timer on falling edge 2025-02-19 18:03:06 +00:00
remutro 274d6eb9a5 Allow for trimmed and padded versions 2025-02-19 18:02:56 +00:00
remutro de0cb099cb Update sha256 for Wonder Boy in Monster World (USA, Europe) for proper eeprom assignment 2025-02-19 18:02:56 +00:00
jcm 32fd70f7c7 build: Enable system library semantics for nall in other targets 2025-02-19 18:02:47 +00:00
Luke Usher b2251305c0 md: always sync core MD components regardless of reduced sync
Fixes VDP H/V tests in testpico when running as a 32X rom.
2025-02-18 14:25:38 +00:00
FitzRoyX 25a2602300 Update README.md 2025-02-18 08:35:00 +00:00
FitzRoyX 4480f7adec Update README.md 2025-02-18 08:35:00 +00:00
jcm 4197502402 nall: Resolve all file paths passed on command line 2025-02-18 08:34:42 +00:00
TascoDLX fb96de3eef mcd: fix subcpu reset behavior 2025-02-18 08:31:56 +00:00
Luke Usher 54c898f199 pak: saveName() needs to call name(), in case it was overridden. 2025-02-17 17:57:26 +00:00
TascoDLX e48b208de2 ym2612: fix timer behaviors 2025-02-17 12:07:24 +00:00
TascoDLX a9f9fd90e7 m32x: fix regs & reset state 2025-02-17 12:07:24 +00:00
TascoDLX deb8830c82 md: fix vdp irq timings 2025-02-17 12:07:24 +00:00
Adrian Siekierka 58af8cb64b ws: First pass at "mono" model color emulation. 2025-02-16 13:17:41 +00:00
TascoDLX c9285b5289 m32x: fix pwm serialization 2025-02-12 06:28:23 +00:00
jcm 4fc09438d2 misc: Address macOS deprecation warnings 2025-02-11 21:36:32 +00:00
jcm a0a7a53b32 build: Increment macOS deployment target to 10.15 2025-02-11 21:36:32 +00:00
User 90f1425b8e Simplify prefetcher logic 2025-02-10 20:47:44 +00:00
User 9f4446061d Initial commit 2025-02-10 20:47:44 +00:00
jcm 32e58943f0 build: Adjust CI push workflow to properly run on all branches 2025-02-10 18:03:40 +00:00
jcm bc116e70e1 build: Add legacy zconf header to .gitignore 2025-02-10 18:03:40 +00:00
jcm 992e1c08cb misc: Use shortened copyright string in About dialog 2025-02-10 18:03:40 +00:00
jcm 9dcc1a39d3 build: Statically link libc++ when using GCC on Windows 2025-02-10 18:03:40 +00:00
Luke Usher 9bbcf638dd gb: use "Game Boy" save path for both GB and GBC
Fixes using the same rom file on both GB and GBC hardware without losing save-data between them
Fixes Pokemon Stadium when loading a .gbc rom (the Transfer Pak only used the GB path)

Existing users using a custom save game path will have to manually move their saves from "Game Boy Color" to "Game Boy"
Users using the default "Save alongside rom files" will not have to take any action.
2025-02-10 16:18:42 +00:00
Luke Usher d6c8d36770 md: fix 32x blending when MD is in H32 mode 2025-02-10 12:54:49 +00:00
jcm c19cf47803 hiro/cocoa: Ensure trailing slash in directory picker result 2025-02-10 09:45:15 +00:00
remutro 1d5a5aef83 remove accidentally introduced space in line with no change 2025-02-09 18:46:25 +00:00
remutro 64ec01bd57 Proper 32X region header processing for licensed games 2025-02-09 18:46:25 +00:00
Luke Usher 82b1e88ec1 sfc: fix typos in sub-rom loading
Fixes Super Game Boy
Fixed Sufami Turbo
Fixes Satellaview/BS Memory Pak
2025-02-07 23:25:43 +00:00
Vincent Cunningham a592dc24d2 ci: Ignore nightly tag for versioning 2025-02-07 18:40:12 +00:00
Luke Usher 976b9828b6 md: Fix new emulator hang/crash in SRB2 32X port 2025-02-07 12:09:04 +00:00
Luke Usher 7f625b6348 md: more 32X PEN=0 stalls; cleaner multi-device sync 2025-02-07 11:51:48 +00:00
Luke Usher 23a9d66924 md: stall m68k when accessing 32x palette when PEN=0
Fixes colours in Chaotix
2025-02-07 11:37:52 +00:00
TascoDLX e4e9044097 m32x vdp fixes
- SH2 memory windows have been adjusted. The framebuffer areas must be mirrored up to 0x5ffffff -- fixes Toughman Contest backgrounds.
- VDP FEN & PEN flags are closer to accurate but still missing some finer timing details.
- FEN/PEN status may block vdp memory accesses. The docs seem to indicate that accesses may be held in wait until a status change, but thats not totally clear (nor validated?). For now, these accesses are blocked and logged in debug.
- FM setting (framebufferAccess) only allows vdp access by either 68k or SH2-side, so additional guards have been inserted for cram/framebuffer accesses. VDP reg accesses should be blocked as well according to docs, but this aspect needs to be reviewed.
- VDP reg latching has been implemented. The timing isnt completely accurate, but should be decent enough for now. Fixes Mars Check image test (corrupt lines from mode switching).
- Autofill timing has been implemented according to the docs. For now, this is just signaling via the FEN flag. Fixes MK II glitching et al.
2025-02-07 09:06:57 +00:00
Vincent Cunningham 04a51df859 ci: Update nightly tag to latest commit 2025-02-07 07:27:08 +00:00
Luke Usher 0c80c1e56b Merge remote-tracking branch 'origin/master' 2025-02-06 21:16:47 +00:00
Luke Usher 6f7618dc81 sh7604: log when wdt is used in watchdog mode 2025-02-06 21:16:29 +00:00
Luke Usher fdd7acb76b md: tweak 32X timing numbers
Should fix Brutal and Chaotix
2025-02-06 20:55:14 +00:00
Luke Usher 7ce2ffd010 md: tweak 32X timing numbers
Should fix Brutal and Chaotix
2025-02-06 20:54:38 +00:00
Vincent Cunningham bb22386f5f n64: Don't report interrupt exceptions to GDB
Interrupt exceptions fire multiple times per second and having to ack all of them can cause performance issues while debugging.
2025-02-06 19:04:36 +00:00
Luke Usher b372f65691 md: refactor sync between 32X components
Hopefully we don't see any negative impact from this;
I have tested many known timing-sensitive games with no ill effects locally.
2025-02-06 19:00:27 +00:00
Luke Usher 048d0e331d md: add internalStep function for 32x recompiler
Allows in-block calls to step() to become an increment which to be
actioned in bulk at the end of the recompiler block
2025-02-06 13:01:43 +00:00
jcm 1323a627b3 Update scripts and documentation 2025-02-06 06:41:02 +00:00
jcm b4ef96e4d6 Remove old macOS build script 2025-02-06 06:41:02 +00:00
jcm 8d2f88b745 build: Re-add librashader headers to source tree for Linux builds 2025-02-06 06:41:02 +00:00
jcm ca7eee17a9 build: Remove CMake platform definitions 2025-02-06 06:41:02 +00:00
jcm 9ef81df3cf build: Remove legacy gitignore entries 2025-02-06 06:41:02 +00:00
jcm 68af829f8e build: Remove legacy zconf header 2025-02-06 06:41:02 +00:00
jcm 56862e0026 build: Use configure_file to define version, copyright info 2025-02-06 06:41:02 +00:00
jcm 09d8af1c18 build: Rename configured files 2025-02-06 06:41:02 +00:00
jcm d20bc3c1b0 build: Remove legacy compatibility compile definitions 2025-02-06 06:41:02 +00:00
jcm 97c82119b5 build: Remove resource files 2025-02-06 06:41:02 +00:00
jcm b78228c5c0 build: Remove legacy workflow 2025-02-06 06:41:02 +00:00
jcm 5107ed00cb build: Remove dynamic dependencies from thirdparty 2025-02-06 06:41:02 +00:00
jcm aeff8ec8f9 build: Remove GNUmakefiles 2025-02-06 06:41:02 +00:00
Christopher Bonhage 7640e1598c n64: fix Joybus RTC read/write status byte (#1814)
As documented in the N64brew wiki:
https://n64brew.dev/wiki/Joybus_Protocol#0x07_-_Read_Real-Time_Clock_Block

The final output byte of Joybus RTC read/write commands should be the
RTC status.

This intended behavior has been confirmed with Mateusz Faderewski, the
creator of SummerCart.
2025-02-05 23:58:05 +01:00
4440 changed files with 2763 additions and 490671 deletions

No files matched your search

+1
View File
@@ -10,6 +10,7 @@ pushd build
# build; pipe output to render warnings neatly
xcodebuild -configuration RelWithDebInfo \
DEBUG_INFORMATION_FORMAT="dwarf-with-dsym" \
-parallelizeTargets \
2>&1 | xcbeautify --renderer github-actions
popd
+2 -3
View File
@@ -21,9 +21,8 @@ do
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
zip -r -y ../ares-${package}.zip ${outdir}
zip -r -y ../ares-${package}-dSYMs.zip ${outdir}-dSYMs
cd -
done
+94 -252
View File
@@ -1,10 +1,12 @@
name: Build
on:
push:
branches: [ master ]
tags: [ 'v*' ]
pull_request:
branches: [ master ]
workflow_call:
inputs:
codesign:
description: Enable codesigning
required: false
default: false
type: boolean
jobs:
build:
strategy:
@@ -12,53 +14,57 @@ jobs:
matrix:
program:
- ares
config:
- RelWithDebInfo
platform:
- name: windows
- name: windows-x64
os: windows-latest
compiler: clang++
windres: windres
shell: 'msys2 {0}'
msystem: clang64
install: mingw-w64-clang-x86_64-clang mingw-w64-clang-x86_64-pkgconf mingw-w64-clang-x86_64-SDL2
- name: windows-arm64
install: mingw-w64-clang-x86_64-clang
target-cmake-preset: windows-ci-mingw-native
- name: windows-x64-gcc
os: windows-latest
compiler: clang++ --target=aarch64-w64-windows-gnu --sysroot=/clangarm64 -resource-dir=/clangarm64/lib/clang/$(basename "$(clang++ -print-resource-dir)")
windres: windres --target=aarch64-w64-windows-gnu
compiler: g++
shell: 'msys2 {0}'
msystem: clang64
install: mingw-w64-clang-x86_64-clang mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-pkgconf mingw-w64-clang-aarch64-SDL2
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
compiler: clang++
windres: llvm-rc
windres: rc
shell: bash
msvc-arch: x64
target-cmake-preset: windows-ci-msvc-native
- name: windows-msvc-arm64
os: windows-latest
compiler: clang++ --target=aarch64-w64-windows-msvc
windres: llvm-rc
shell: bash
msvc-arch: x64_arm64
- name: windows-cl-x64
os: windows-latest
compiler: cl
windres: rc
shell: bash
msvc-arch: x64
- name: windows-cl-arm64
os: windows-latest
compiler: cl
windres: rc
shell: bash
msvc-arch: x64_arm64
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: sh
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:
@@ -70,247 +76,83 @@ jobs:
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.platform.msystem }}
install: make ${{ matrix.platform.install }}
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: ${{ secrets.MACOS_CERTIFICATE_NAME == '' }}
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: Setup MSYS2 Git Environment
if: matrix.platform.shell == 'msys2 {0}'
run: |
git config core.autocrlf true
- name: Install Linux Dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update -y -qq
sudo apt-get install libsdl2-dev libgtk-3-dev libao-dev libopenal-dev
- name: "macOS: Import Certificate"
if: runner.os == 'macOS' && github.event_name != 'pull_request'
continue-on-error: ${{ github.repository != 'ares-emulator/ares' }}
uses: wpilibsuite/import-signing-certificate@2ac4f44d28045073d23153256efbb4c4b2d8aa22 # Don't use rolling branch for security reasons
with:
certificate-data: ${{ secrets.MACOS_CERTIFICATE_DATA }}
certificate-passphrase: ${{ secrets.MACOS_CERTIFICATE_PASSPHRASE }}
keychain-name: ares-macos-keychain
keychain-password: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}
- name: Checkout source code
uses: actions/checkout@v4
- name: Install Windows Dependencies
if: runner.os == 'Windows'
run: |
export PATH="/c/Users/runneradmin/.cargo/bin:$PATH" # correct on windows-latest as of 2024-02-19
if [[ ${{ matrix.platform.name }} == *-arm64 ]]; then
rustup toolchain install nightly
rustup default nightly
rustup target add aarch64-pc-windows-msvc
pushd thirdparty/librashader
./build-librashader.sh aarch64-pc-windows-msvc
popd
else
rustup toolchain install nightly
rustup default nightly
pushd thirdparty/librashader
./build-librashader.sh
popd
fi
- name: "macOS: recover MoltenVK cache"
if: runner.os == 'macOS'
uses: actions/cache@v4
env:
cache-name: moltenvk
with:
path: thirdparty/MoltenVK/MoltenVK
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('thirdparty/MoltenVK/HEAD') }}
sudo apt-get install ${{ matrix.platform.install }}
- name: Install macOS Dependencies
if: runner.os == 'macOS'
run: |
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
pushd thirdparty/SDL
./build-SDL.sh
popd
pushd thirdparty/MoltenVK
./build-moltenvk.sh
popd
export MACOSX_DEPLOYMENT_TARGET=10.15
pushd thirdparty/librashader
./build-librashader.sh
popd
- name: Set up MSVC environment
if: matrix.platform.msvc-arch != ''
uses: ilammy/msvc-dev-cmd@v1
with:
arch: ${{ matrix.platform.msvc-arch }}
- name: Make
if: runner.os != 'macOS'
run: ${MAKE:-make} -j4 -C desktop-ui build=optimized local=false compiler="${{ matrix.platform.compiler }}" windres="${{ matrix.platform.windres }}"
- name: "macOS: Make universal app"
if: runner.os == 'macOS'
run: scripts/macos-make-universal.sh build=optimized local=false
brew install ${{ matrix.platform.install }}
- name: "Build: Linux"
if: runner.os == 'Linux'
run: .github/scripts/build_ubuntu.sh
env:
MAKEFLAGS: -j3
MACOS_CERTIFICATE_NAME: ${{ secrets.MACOS_CERTIFICATE_NAME }}
MACOS_KEYCHAIN_NAME: ares-macos-keychain
MACOS_KEYCHAIN_PASSWORD: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}
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' && github.event_name != 'pull_request'
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: 'desktop-ui/out'
folder: 'build/desktop-ui/rundir/'
timestampUrl: 'http://timestamp.digicert.com'
- name: "macOS: notarize"
if: false
continue-on-error: ${{ github.repository != 'ares-emulator/ares' }}
run: |
ditto -c -k --keepParent desktop-ui/out/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 desktop-ui/out/ares.app
- name: "Build: macOS"
if: runner.os == 'macOS'
run: .github/scripts/build_macos.sh
env:
MACOS_NOTARIZATION_USERNAME: ${{ secrets.MACOS_NOTARIZATION_USERNAME }}
MACOS_NOTARIZATION_PASSWORD: ${{ secrets.MACOS_NOTARIZATION_PASSWORD }}
TARGET_PRESET: ${{ matrix.platform.target-cmake-preset }}
MACOS_CERTIFICATE_NAME: ${{ secrets.MACOS_CERTIFICATE_NAME }}
MACOS_NOTARIZATION_TEAMID: ${{ secrets.MACOS_NOTARIZATION_TEAMID }}
- name: Upload
- name: "Compress Build Artifact (macOS)"
if: runner.os == 'macOS'
run: |
tar -cvJf ares-${{ matrix.platform.name }}.tar.xz -C build/desktop-ui/RelWithDebInfo/ .
- name: "Compress Build Artifacts (Windows)"
if: runner.os != 'macOS' && runner.os != 'Linux'
run: |
tar -cvJf ares-${{ matrix.platform.name }}.tar.xz -C build/desktop-ui/rundir/ .
- name: Upload Build
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.program }}-${{ matrix.platform.name }}
path: desktop-ui/out/*
release:
if: false
runs-on: ubuntu-latest
needs:
- build
steps:
- uses: actions/checkout@v4
path: ares-${{ matrix.platform.name }}.tar.xz
- name: Upload Debug Symbols (Windows)
if: runner.os != 'macOS' && runner.os != 'Linux'
uses: actions/upload-artifact@v4
with:
path: 'src'
- name: Download Artifacts
uses: actions/download-artifact@v4
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:
path: 'bin'
- name: Package Artifacts
run: |
set -eu
case ${GITHUB_REF} in
refs/tags/*) suffix="-${GITHUB_REF#refs/tags/}" ;;
refs/heads/master) suffix="-nightly" ;;
*) suffix="" ;;
esac
srcdir="${GITHUB_WORKSPACE}/src"
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 windows windows-msvc-arm64 macos-universal
do
mkdir "${package}"
cd "${package}"
# Package ares.
outdir=ares${suffix}
mkdir ${outdir}
cp -ar ${bindir}/ares-${package}/* ${outdir}
zip -r ../ares-${package}.zip ${outdir}
cd -
done
- name: Create Release
id: release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -eu
github_rest()
{
local method="${1}"
local url="https://api.github.com${2}"
shift 2
>&2 echo "${method} ${url}"
curl \
--fail \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token ${GITHUB_TOKEN}" \
-X "${method}" \
"${url}" \
"$@"
}
github_get_release_id_for_tag()
{
payload=$(github_rest GET "/repos/${GITHUB_REPOSITORY}/releases/tags/${1}") || return
echo "${payload}" | jq .id
}
github_delete_release_by_id()
{
github_rest DELETE "/repos/${GITHUB_REPOSITORY}/releases/${1}"
}
github_create_release()
{
local payload="{
\"tag_name\": \"${1}\",
\"target_commitish\": \"${2}\",
\"name\": \"${3}\",
\"body\": \"${4}\",
\"draft\": ${5},
\"prerelease\": ${6}
}"
github_rest POST "/repos/${GITHUB_REPOSITORY}/releases" -d "${payload}"
}
make_nightly_release()
{
github_create_release \
nightly \
"${GITHUB_SHA}" \
"ares nightly $(date +"%Y-%m-%d")" \
"Auto-generated nightly release on $(date -u +"%Y-%m-%d %T %Z")" \
false \
true
}
make_version_release()
{
github_create_release \
"${1}" \
"${GITHUB_SHA}" \
"ares ${1}" \
"This is ares ${1}, released on $(date +"%Y-%m-%d")." \
false \
false
}
case ${GITHUB_REF} in
refs/tags/*)
# Create a new version release using the current revision.
echo "UPLOAD_URL=$(make_version_release ${GITHUB_REF#refs/tags/} | jq -r .upload_url)" >> $GITHUB_ENV
;;
refs/heads/master)
# Check for an existing nightly release.
{ release_id=$(github_get_release_id_for_tag nightly); status=$?; } || true
# Delete existing nightly release if it exists.
case ${status} in
0) github_delete_release_by_id "${release_id}" ;;
22) >&2 echo "No current nightly release; skipping tag deletion." ;;
*) >&2 echo "API call failed unexpectedly." && exit 1 ;;
esac
# Create a new nightly release using the current revision.
echo "UPLOAD_URL=$(make_nightly_release | jq -r .upload_url)" >> $GITHUB_ENV
;;
esac
- name: Upload ares-windows
uses: actions/upload-release-asset@v1
env: { GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' }
with: { upload_url: '${{ env.UPLOAD_URL }}', asset_path: 'ares-windows.zip', asset_name: 'ares-windows.zip', asset_content_type: 'application/zip' }
- 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-universal
uses: actions/upload-release-asset@v1
env: { GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' }
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' }
name: ${{ matrix.program }}-${{ matrix.platform.name }}-dSYMs
path: build/dSYMs/*
-153
View File
@@ -1,153 +0,0 @@
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/*
+1 -1
View File
@@ -12,6 +12,6 @@ concurrency:
jobs:
build-project:
name: Build
uses: ./.github/workflows/build_new.yml
uses: ./.github/workflows/build.yml
permissions:
contents: read
+2 -4
View File
@@ -1,8 +1,6 @@
name: Push
on:
push:
branches: [ '*' ]
tags: [ 'v*' ]
push
concurrency:
group: '${{ github.workflow }} @ ${{ github.head_ref || github.ref }}'
cancel-in-progress: true
@@ -11,7 +9,7 @@ permissions:
jobs:
build:
name: Build ares
uses: ./.github/workflows/build_new.yml
uses: ./.github/workflows/build.yml
secrets: inherit
with:
codesign: true
+17 -2
View File
@@ -19,6 +19,15 @@ jobs:
uses: actions/download-artifact@v4
with:
path: 'bin'
- name: "Decompress Build Artifacts"
if: runner.os == 'macOS'
run: |
for package in macos-universal windows-x64 windows-clang-cl-x64 windows-clang-cl-arm64; do
pushd bin/ares-${package}
tar -xJf ares-${package}.tar.xz
rm -f ares-${package}.tar.xz
popd
done
- name: "macOS: notarize"
if: inputs.notarize
run: |
@@ -48,12 +57,18 @@ jobs:
description="This is ares ${GITHUB_REF_NAME}, released on ${now}."
echo "description=${description}" >> $GITHUB_OUTPUT
fi
- name: Update Git Tag
if: steps.check.outputs.tag == 'nightly'
id: update-tag
run: |
git -C src tag -f ${{ steps.check.outputs.tag }}
git -C src push -f origin ${{ steps.check.outputs.tag }}
- name: Create Release 🛫
id: create_release
uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191
with:
draft: ${{ github.ref != 'refs/heads/master' }}
prerelease: ${{ steps.check.outputs.tag == 'nightly' }}
tag_name: ${{ steps.check.outputs.tag }}
name: ares ${{ steps.check.outputs.versionName }}
body: ${{ steps.check.outputs.description }}
+4 -4
View File
@@ -26,10 +26,6 @@
!CMakeLists.txt
!CMakePresets.json
thirdparty/SDL/SDL
thirdparty/SDL/libSDL2-2.0.0.dylib
desktop-ui/out
desktop-ui/obj
ruby/video/metal/shaders.metallib
ruby/video/metal/shaders.ir
ares/ares/resource/resource.cpp
@@ -38,5 +34,9 @@ hiro/resource/resource.cpp
hiro/resource/resource.hpp
desktop-ui/resource/resource.cpp
desktop-ui/resource/resource.hpp
mia/resource/resource.cpp
mia/resource/resource.hpp
tests/arm7tdmi/tests
tests/m68000/tests
thirdparty/libchdr/deps/zlib-*/zconf.h
thirdparty/libchdr/deps/zlib-*/zconf.h.included
+1 -1
View File
@@ -12,7 +12,7 @@ add_subdirectory(cmake)
add_subdirectory(thirdparty)
add_subdirectory(nall)
add_subdirectory(nall/nall)
add_subdirectory(libco)
add_subdirectory(ruby)
add_subdirectory(hiro)
+1 -1
View File
@@ -81,7 +81,7 @@
"binaryDir": "${sourceDir}/build",
"cacheVariables": {
"CMAKE_OSX_ARCHITECTURES": {"type": "STRING", "value": "x86_64;arm64"},
"CMAKE_OSX_DEPLOYMENT_TARGET": {"type": "STRING", "value": "10.13"},
"CMAKE_OSX_DEPLOYMENT_TARGET": {"type": "STRING", "value": "10.15"},
"ARES_CODESIGN_IDENTITY": {"type": "STRING", "value": "$penv{MACOS_CERTIFICATE_NAME}"},
"ARES_CODESIGN_TEAM": {"type": "STRING", "value": "$penv{MACOS_NOTARIZATION_TEAMID}"},
"ENABLE_CCACHE": true,
-6
View File
@@ -1,6 +0,0 @@
targets = all clean install uninstall verbose
$(targets):
@$(MAKE) -C desktop-ui $@
.PHONY: $(targets)
+1 -1
View File
@@ -1,7 +1,7 @@
----------------------------------------------------------------------
ares
Copyright (c) 2004-2024 ares team, Near et al
Copyright (c) 2004-2025 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
+5 -5
View File
@@ -5,6 +5,8 @@
**ares** is a multi-system emulator that began development on October 14th, 2004.
It is a descendant of [higan](https://github.com/higan-emu/higan) and [bsnes](https://github.com/bsnes-emu/bsnes/), and focuses on accuracy and preservation.
It's worth noting that ares takes some uncommon design approaches that essentially trade speed for code clarity. We avoid state machines and bitmasks (when possible). Most cores end up being half the amount of code, but slower. The code is clearer and less spaghettified, especially for systems with lots of processors. C bitfields being non-portable incurs a speedhit. Windows also has a speedhit over Linux due to its ABI needing more instructions to switch contexts.
Official Releases
-----------------
@@ -14,21 +16,19 @@ Official releases are available from
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).
Automated, untested builds of ares are available for Windows and macOS as a [pre-release](https://github.com/ares-emulator/ares/releases/tag/nightly).
Only the latest nightly build is kept.
Building ares
-------------
Build instructions are available on the ares wiki. See build instructions for:
ares supports building on Windows, macOS, and various Linux/BSD distributions. See build instructions for:
* [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
--------------------
@@ -40,7 +40,7 @@ Usage: ./ares [options] game(s)
--help Displays available options and exit
--fullscreen Start in full screen mode
--system system Specify the system name
--shader shader Specify GLSL shader name to load (requires OpenGL driver)
--shader shader Specify a slang shader to load (requires OpenGL or Metal)
--setting name=value Specify a value for a setting
--dump-all-settings Show a list of all existing settings and exit
--no-file-prompt Do not prompt to load (optional) additional roms (eg: 64DD)
+3 -1
View File
@@ -1,5 +1,7 @@
add_library(ares STATIC ares/ares.cpp ares/memory/fixed-allocator.cpp)
configure_file(ares/ares.cpp.in ares/ares.cpp @ONLY)
add_library(ares::ares ALIAS ares)
option(ARES_PROFILE_ACCURACY "Build to prefer emulation accuracy over performance in certain specific areas" OFF)
@@ -29,7 +31,7 @@ endif()
target_sources(
ares
PRIVATE ares/ares.cpp ares/ares.hpp ares/inline.hpp ares/platform.hpp ares/random.hpp ares/types.hpp
PRIVATE ares/ares.cpp ares/ares.cpp.in ares/ares.hpp ares/inline.hpp ares/platform.hpp ares/random.hpp ares/types.hpp
)
target_sources(
-108
View File
@@ -1,108 +0,0 @@
ifeq ($(platform),windows)
options += $(call lib,uuid kernel32 user32 gdi32 comctl32 comdlg32 shell32)
else ifeq ($(platform),macos)
else ifneq ($(filter $(platform),linux bsd),)
options += -Wl,-export-dynamic
options += -lX11 -lXext
else
$(error "ares is a library and cannot be built directly.")
endif
ares.objects := ares ares-fixed-allocator
$(object.path)/ares.o: $(ares.path)/ares/ares.cpp
$(object.path)/ares-fixed-allocator.o: $(ares.path)/ares/memory/fixed-allocator.cpp
ifeq ($(vulkan),true)
flags += -DVULKAN
endif
ifeq ($(profile),accuracy)
flags += -DPROFILE_ACCURACY
endif
ifeq ($(profile),performance)
flags += -DPROFILE_PERFORMANCE
endif
ifneq ($(filter $(cores),a26),)
include $(ares.path)/a26/GNUmakefile
endif
ifneq ($(filter $(cores),fc),)
include $(ares.path)/fc/GNUmakefile
endif
ifneq ($(filter $(cores),sfc),)
include $(ares.path)/sfc/GNUmakefile
endif
ifneq ($(filter $(cores),n64),)
include $(ares.path)/n64/GNUmakefile
endif
ifneq ($(filter $(cores),sg),)
include $(ares.path)/sg/GNUmakefile
endif
ifneq ($(filter $(cores),ms),)
include $(ares.path)/ms/GNUmakefile
endif
ifneq ($(filter $(cores),md),)
include $(ares.path)/md/GNUmakefile
endif
ifneq ($(filter $(cores),saturn),)
include $(ares.path)/saturn/GNUmakefile
endif
ifneq ($(filter $(cores),ps1),)
include $(ares.path)/ps1/GNUmakefile
endif
ifneq ($(filter $(cores),pce),)
include $(ares.path)/pce/GNUmakefile
endif
ifneq ($(filter $(cores),msx),)
include $(ares.path)/msx/GNUmakefile
endif
ifneq ($(filter $(cores),cv),)
include $(ares.path)/cv/GNUmakefile
endif
ifneq ($(filter $(cores),myvision),)
include $(ares.path)/myvision/GNUmakefile
endif
ifneq ($(filter $(cores),gb),)
include $(ares.path)/gb/GNUmakefile
endif
ifneq ($(filter $(cores),gba),)
include $(ares.path)/gba/GNUmakefile
endif
ifneq ($(filter $(cores),ws),)
include $(ares.path)/ws/GNUmakefile
endif
ifneq ($(filter $(cores),ng),)
include $(ares.path)/ng/GNUmakefile
endif
ifneq ($(filter $(cores),ngp),)
include $(ares.path)/ngp/GNUmakefile
endif
ifneq ($(filter $(cores),spec),)
include $(ares.path)/spec/GNUmakefile
endif
include $(ares.path)/component/GNUmakefile
flags += $(foreach c,$(call strupper,$(cores)),-DCORE_$c)
ares.objects := $(ares.objects:%=$(object.path)/%.o)
-15
View File
@@ -1,15 +0,0 @@
ares.components += mos6502
ares.objects += ares-a26-system
ares.objects += ares-a26-controller
ares.objects += ares-a26-cartridge
ares.objects += ares-a26-cpu
ares.objects += ares-a26-tia
ares.objects += ares-a26-riot
$(object.path)/ares-a26-system.o: $(ares.path)/a26/system/system.cpp
$(object.path)/ares-a26-controller.o: $(ares.path)/a26/controller/controller.cpp
$(object.path)/ares-a26-cartridge.o: $(ares.path)/a26/cartridge/cartridge.cpp
$(object.path)/ares-a26-cpu.o: $(ares.path)/a26/cpu/cpu.cpp
$(object.path)/ares-a26-tia.o: $(ares.path)/a26/tia/tia.cpp
$(object.path)/ares-a26-riot.o: $(ares.path)/a26/riot/riot.cpp
+5 -5
View File
@@ -9,13 +9,13 @@ namespace ares {
Platform* platform = nullptr;
bool _runAhead = false;
const string Name = "ares";
const string Version = "142";
const string Copyright = "ares team, Near";
const string Name = "@ARES_NAME@";
const string Version = "@ARES_VERSION@";
const string Copyright = "@ARES_LEGAL_COPYRIGHT_SHORT@";
const string License = "ISC";
const string LicenseURI = "https://opensource.org/licenses/ISC";
const string Website = "ares-emu.net";
const string WebsiteURI = "https://ares-emu.net/";
const string Website = "@ARES_WEBSITE@";
const string WebsiteURI = "https://@ARES_WEBSITE@/";
const u32 SerializerSignature = 0x31545342; //"BST1" (little-endian)
}
-10
View File
@@ -1,10 +0,0 @@
build := stable
nall.path := ../../../nall
include $(nall.path)/GNUmakefile
all:
sourcery resource.bml resource.cpp resource.hpp
clean:
$(call delete,resource.cpp)
$(call delete,resource.hpp)
File diff suppressed because it is too large. Load diff
-34
View File
@@ -1,34 +0,0 @@
namespace Resource {
namespace Ares {
extern const unsigned char Icon1x[6562];
extern const unsigned char Icon2x[9662];
extern const unsigned char Logo1x[11533];
extern const unsigned char Logo2x[18991];
}
namespace Sprite {
namespace SuperFamicom {
extern const unsigned char CrosshairBlue[332];
extern const unsigned char CrosshairGreen[329];
extern const unsigned char CrosshairRed[342];
}
namespace WonderSwan {
extern const unsigned char Auxiliary0[117];
extern const unsigned char Auxiliary1[134];
extern const unsigned char Auxiliary2[136];
extern const unsigned char Headphones[167];
extern const unsigned char Initialized[136];
extern const unsigned char LowBattery[144];
extern const unsigned char Orientation0[148];
extern const unsigned char Orientation1[150];
extern const unsigned char PoweredOn[155];
extern const unsigned char Sleeping[154];
extern const unsigned char VolumeA0[129];
extern const unsigned char VolumeA1[142];
extern const unsigned char VolumeA2[146];
extern const unsigned char VolumeB0[125];
extern const unsigned char VolumeB1[137];
extern const unsigned char VolumeB2[148];
extern const unsigned char VolumeB3[153];
}
}
}
-76
View File
@@ -1,76 +0,0 @@
ares.components := $(call unique,$(ares.components))
ares.objects += $(if $(findstring ay38910,$(ares.components)),ares-audio-ay38910)
ares.objects += $(if $(findstring msm5205,$(ares.components)),ares-audio-msm5205)
ares.objects += $(if $(findstring sn76489,$(ares.components)),ares-audio-sn76489)
ares.objects += $(if $(findstring t6w28,$(ares.components)),ares-audio-t6w28)
ares.objects += $(if $(findstring ym2149,$(ares.components)),ares-audio-ym2149)
ares.objects += $(if $(findstring ym2413,$(ares.components)),ares-audio-ym2413)
ares.objects += $(if $(findstring ym2610,$(ares.components)),ares-audio-ym2610)
ares.objects += $(if $(findstring ym2612,$(ares.components)),ares-audio-ym2612)
ares.objects += $(if $(findstring m24c,$(ares.components)),ares-eeprom-m24c)
ares.objects += $(if $(findstring m93lcx6,$(ares.components)),ares-eeprom-m93lcx6)
ares.objects += $(if $(findstring i8255,$(ares.components)),ares-io-i8255)
ares.objects += $(if $(findstring arm7tdmi,$(ares.components)),ares-processor-arm7tdmi)
ares.objects += $(if $(findstring gsu,$(ares.components)),ares-processor-gsu)
ares.objects += $(if $(findstring hg51b,$(ares.components)),ares-processor-hg51b)
ares.objects += $(if $(findstring huc6280,$(ares.components)),ares-processor-huc6280)
ares.objects += $(if $(findstring i8080,$(ares.components)),ares-processor-i8080)
ares.objects += $(if $(findstring m68hc05,$(ares.components)),ares-processor-m68hc05)
ares.objects += $(if $(findstring m68000,$(ares.components)),ares-processor-m68000)
ares.objects += $(if $(findstring mos6502,$(ares.components)),ares-processor-mos6502)
ares.objects += $(if $(findstring sh2,$(ares.components)),ares-processor-sh2)
ares.objects += $(if $(findstring sm5k,$(ares.components)),ares-processor-sm5k)
ares.objects += $(if $(findstring sm83,$(ares.components)),ares-processor-sm83)
ares.objects += $(if $(findstring spc700,$(ares.components)),ares-processor-spc700)
ares.objects += $(if $(findstring ssp1601,$(ares.components)),ares-processor-ssp1601)
ares.objects += $(if $(findstring tlcs900h,$(ares.components)),ares-processor-tlcs900h)
ares.objects += $(if $(findstring upd96050,$(ares.components)),ares-processor-upd96050)
ares.objects += $(if $(findstring v30mz,$(ares.components)),ares-processor-v30mz)
ares.objects += $(if $(findstring wdc65816,$(ares.components)),ares-processor-wdc65816)
ares.objects += $(if $(findstring z80,$(ares.components)),ares-processor-z80)
ares.objects += $(if $(findstring sst39sf0x0,$(ares.components)),ares-flash-sst39sf0x0)
ares.objects += $(if $(findstring tms9918,$(ares.components)),ares-video-tms9918)
ares.objects += $(if $(findstring v9938,$(ares.components)),ares-video-v9938)
$(object.path)/ares-audio-ay38910.o: $(ares.path)/component/audio/ay38910/ay38910.cpp
$(object.path)/ares-audio-msm5205.o: $(ares.path)/component/audio/msm5205/msm5205.cpp
$(object.path)/ares-audio-sn76489.o: $(ares.path)/component/audio/sn76489/sn76489.cpp
$(object.path)/ares-audio-t6w28.o: $(ares.path)/component/audio/t6w28/t6w28.cpp
$(object.path)/ares-audio-ym2149.o: $(ares.path)/component/audio/ym2149/ym2149.cpp
$(object.path)/ares-audio-ym2413.o: $(ares.path)/component/audio/ym2413/ym2413.cpp
$(object.path)/ares-audio-ym2612.o: $(ares.path)/component/audio/ym2612/ym2612.cpp
$(object.path)/ares-eeprom-m24c.o: $(ares.path)/component/eeprom/m24c/m24c.cpp
$(object.path)/ares-eeprom-m93lcx6.o: $(ares.path)/component/eeprom/m93lcx6/m93lcx6.cpp
$(object.path)/ares-io-i8255.o: $(ares.path)/component/io/i8255/i8255.cpp
$(object.path)/ares-processor-arm7tdmi.o: $(ares.path)/component/processor/arm7tdmi/arm7tdmi.cpp
$(object.path)/ares-processor-gsu.o: $(ares.path)/component/processor/gsu/gsu.cpp
$(object.path)/ares-processor-hg51b.o: $(ares.path)/component/processor/hg51b/hg51b.cpp
$(object.path)/ares-processor-huc6280.o: $(ares.path)/component/processor/huc6280/huc6280.cpp
$(object.path)/ares-processor-i8080.o: $(ares.path)/component/processor/i8080/i8080.cpp
$(object.path)/ares-processor-m68hc05.o: $(ares.path)/component/processor/m68hc05/m68hc05.cpp
$(object.path)/ares-processor-m68000.o: $(ares.path)/component/processor/m68000/m68000.cpp
$(object.path)/ares-processor-mos6502.o: $(ares.path)/component/processor/mos6502/mos6502.cpp
$(object.path)/ares-processor-sh2.o: $(ares.path)/component/processor/sh2/sh2.cpp
$(object.path)/ares-processor-sm5k.o: $(ares.path)/component/processor/sm5k/sm5k.cpp
$(object.path)/ares-processor-sm83.o: $(ares.path)/component/processor/sm83/sm83.cpp
$(object.path)/ares-processor-spc700.o: $(ares.path)/component/processor/spc700/spc700.cpp
$(object.path)/ares-processor-ssp1601.o: $(ares.path)/component/processor/ssp1601/ssp1601.cpp
$(object.path)/ares-processor-tlcs900h.o: $(ares.path)/component/processor/tlcs900h/tlcs900h.cpp
$(object.path)/ares-processor-upd96050.o: $(ares.path)/component/processor/upd96050/upd96050.cpp
$(object.path)/ares-processor-v30mz.o: $(ares.path)/component/processor/v30mz/v30mz.cpp
$(object.path)/ares-processor-wdc65816.o: $(ares.path)/component/processor/wdc65816/wdc65816.cpp
$(object.path)/ares-processor-z80.o: $(ares.path)/component/processor/z80/z80.cpp
$(object.path)/ares-flash-sst39sf0x0.o: $(ares.path)/component/flash/sst39sf0x0/sst39sf0x0.cpp
$(object.path)/ares-video-tms9918.o: $(ares.path)/component/video/tms9918/tms9918.cpp
$(object.path)/ares-video-v9938.o: $(ares.path)/component/video/v9938/v9938.cpp
+2 -2
View File
@@ -125,7 +125,7 @@ auto YM2612::Channel::Operator::updatePhase() -> void {
auto YM2612::Channel::Operator::updateLevel() -> void {
u32 lfo = ym2612.lfo.clock & 0x40 ? ym2612.lfo.clock & 0x3f : ~ym2612.lfo.clock & 0x3f;
u32 depth = tremolos[channel.tremolo];
u32 depth = tremolos[tremoloEnable * channel.tremolo];
bool invert = ssg.attack != ssg.invert && envelope.state != Release;
n10 value = ssg.enable && invert ? 0x200 - envelope.value : 0 + envelope.value;
@@ -147,7 +147,7 @@ auto YM2612::Channel::power() -> void {
for(auto& op : operators) {
op.keyOn = 0;
op.keyLine = 0;
op.lfoEnable = 0;
op.tremoloEnable = 0;
op.keyScale = 0;
op.detune = 0;
op.multiple = 0;
+5 -9
View File
@@ -36,18 +36,14 @@ auto YM2612::writeData(n8 data) -> void {
//timer B period
case 0x026: {
timerB.period.bit(0,7) = data.bit(0,7);
// Not sure if this is the right place for it, but...
// handle a specific use case to reset the subticks on timer B
if(!timerB.enable) timerB.divider = 0;
break;
}
//timer control
case 0x027: {
//reload period on 0->1 transition
if(!timerA.enable && data.bit(0)) timerA.counter = timerA.period;
if(!timerB.enable && data.bit(1)) {
timerB.counter = timerB.period;
timerB.divider = 0;
}
timerA.enable = data.bit(0);
timerB.enable = data.bit(1);
timerA.irq = data.bit(2);
@@ -125,10 +121,10 @@ auto YM2612::writeData(n8 data) -> void {
break;
}
//LFO enable, decay rate
//LFO AM enable, decay rate
case 0x060: {
op.envelope.decayRate = data.bit(0,4);
op.lfoEnable = data.bit(7);
op.tremoloEnable = data.bit(7);
channel[index].updateEnvelope();
channel[index].updateLevel();
break;
@@ -13,12 +13,14 @@ auto YM2612::serialize(serializer& s) -> void {
s(envelope.divider);
s(timerA.enable);
s(timerA.enableLatch);
s(timerA.irq);
s(timerA.line);
s(timerA.period);
s(timerA.counter);
s(timerB.enable);
s(timerB.enableLatch);
s(timerB.irq);
s(timerB.line);
s(timerB.period);
@@ -43,7 +45,7 @@ auto YM2612::Channel::serialize(serializer& s) -> void {
auto YM2612::Channel::Operator::serialize(serializer& s) -> void {
s(keyOn);
s(keyLine);
s(lfoEnable);
s(tremoloEnable);
s(keyScale);
s(detune);
s(multiple);
+10 -11
View File
@@ -1,16 +1,15 @@
auto YM2612::TimerA::run() -> void {
if(!enable) return;
if(++counter) return;
counter = period;
line |= irq;
if(!++counter)
line |= irq & enableLatch;
if(enableLatch < enable || !counter)
counter = period;
enableLatch = enable;
}
auto YM2612::TimerB::run() -> void {
if(!enable) return;
if(++divider) return;
if(++counter) return;
counter = period;
line |= irq;
if(!++divider && !++counter)
line |= irq & enableLatch;
if(enableLatch < enable || !counter && !divider)
counter = period; // do not reset divider on reenable
enableLatch = enable;
}
+3 -1
View File
@@ -45,6 +45,7 @@ protected:
auto run() -> void;
n1 enable = 0;
n1 enableLatch = 0;
n1 irq = 0;
n1 line = 0;
n10 period = 0;
@@ -56,6 +57,7 @@ protected:
auto run() -> void;
n1 enable = 0;
n1 enableLatch = 0;
n1 irq = 0;
n1 line = 0;
n8 period = 0;
@@ -106,7 +108,7 @@ protected:
n1 keyOn = 0;
n1 keyLine = 0;
n1 lfoEnable = 0;
n1 tremoloEnable = 0;
n5 keyScale = 0;
n3 detune = 0;
n4 multiple = 0;
@@ -6,15 +6,13 @@ namespace ares {
struct ARM7TDMI {
enum : u32 {
Nonsequential = 1 << 0, //N cycle
Sequential = 1 << 1, //S cycle
Load = 1 << 0, //load operation
Store = 1 << 1, //store operation
Prefetch = 1 << 2, //instruction fetch
Byte = 1 << 3, // 8-bit access
Half = 1 << 4, //16-bit access
Word = 1 << 5, //32-bit access
Load = 1 << 6, //load operation
Store = 1 << 7, //store operation
Signed = 1 << 8, //sign-extend
Signed = 1 << 6, //sign-extend
};
virtual auto step(u32 clocks) -> void = 0;
@@ -44,6 +42,7 @@ struct ARM7TDMI {
auto load(u32 mode, n32 address) -> n32;
auto write(u32 mode, n32 address, n32 word) -> void;
auto store(u32 mode, n32 address, n32 word) -> void;
auto endBurst() -> void { nonsequential = true; return; }
//algorithms.cpp
auto ADD(n32, n32, bool) -> n32;
@@ -221,7 +220,6 @@ struct ARM7TDMI {
};
n1 reload = 1;
n1 nonsequential = 1;
Instruction fetch;
Instruction decode;
Instruction execute;
@@ -230,6 +228,7 @@ struct ARM7TDMI {
n32 opcode;
b1 carry;
b1 irq;
b1 nonsequential;
function<void (n32 opcode)> armInstruction[4096];
function<void ()> thumbInstruction[65536];
@@ -17,12 +17,12 @@ auto ARM7TDMI::disassembleInstruction(maybe<n32> pc, maybe<boolean> thumb) -> st
_pc = pc();
if(!thumb()) {
n32 opcode = getDebugger(Word | Nonsequential, _pc & ~3);
n32 opcode = getDebugger(Word, _pc & ~3);
n12 index = (opcode & 0x0ff00000) >> 16 | (opcode & 0x000000f0) >> 4;
_c = _conditions[opcode >> 28];
return pad(armDisassemble[index](opcode), -40);
} else {
n16 opcode = getDebugger(Half | Nonsequential, _pc & ~1);
n16 opcode = getDebugger(Half, _pc & ~1);
return pad(thumbDisassemble[opcode](), -40);
}
}
@@ -121,7 +121,7 @@ auto ARM7TDMI::armDisassembleDataRegisterShift
auto ARM7TDMI::armDisassembleLoadImmediate
(n8 immediate, n1 half, n4 d, n4 n, n1 writeback, n1 up, n1 pre) -> string {
string data;
if(n == 15) data = {" =0x", hex(getDebugger((half ? Half : Byte) | Nonsequential,
if(n == 15) data = {" =0x", hex(getDebugger((half ? Half : Byte),
_pc + 8 + (up ? +immediate : -immediate)), half ? 4L : 2L)};
return {"ldr", _c, half ? "sh" : "sb", " ",
@@ -150,7 +150,7 @@ auto ARM7TDMI::armDisassembleMemorySwap
auto ARM7TDMI::armDisassembleMoveHalfImmediate
(n8 immediate, n4 d, n4 n, n1 mode, n1 writeback, n1 up, n1 pre) -> string {
string data;
if(n == 15) data = {" =0x", hex(getDebugger(Half | Nonsequential, _pc + (up ? +immediate : -immediate)), 4L)};
if(n == 15) data = {" =0x", hex(getDebugger(Half, _pc + (up ? +immediate : -immediate)), 4L)};
return {mode ? "ldr" : "str", _c, "h ",
_r[d], ",[", _r[n],
@@ -173,7 +173,7 @@ auto ARM7TDMI::armDisassembleMoveHalfRegister
auto ARM7TDMI::armDisassembleMoveImmediateOffset
(n12 immediate, n4 d, n4 n, n1 mode, n1 writeback, n1 byte, n1 up, n1 pre) -> string {
string data;
if(n == 15) data = {" =0x", hex(getDebugger((byte ? Byte : Word) | Nonsequential,
if(n == 15) data = {" =0x", hex(getDebugger((byte ? Byte : Word),
_pc + 8 + (up ? +immediate : -immediate)), byte ? 2L : 4L)};
return {mode ? "ldr" : "str", _c, byte ? "b" : "", " ", _r[d], ",[", _r[n],
pre == 0 ? "]" : "",
@@ -276,7 +276,7 @@ auto ARM7TDMI::thumbDisassembleALU
auto ARM7TDMI::thumbDisassembleALUExtended
(n4 d, n4 m, n2 mode) -> string {
static const string opcode[] = {"add", "sub", "mov"};
static const string opcode[] = {"add", "cmp", "mov"};
if(d == 8 && m == 8 && mode == 2) return {"nop"};
return {opcode[mode], " ", _r[d], ",", _r[m]};
}
@@ -308,7 +308,7 @@ auto ARM7TDMI::thumbDisassembleBranchExchange
auto ARM7TDMI::thumbDisassembleBranchFarPrefix
(i11 displacementHi) -> string {
n11 displacementLo = getDebugger(Half | Nonsequential, (_pc & ~1) + 2);
n11 displacementLo = getDebugger(Half, (_pc & ~1) + 2);
i22 displacement = displacementHi << 11 | displacementLo << 0;
n32 address = _pc + 4 + displacement * 2;
return {"bl 0x", hex(address, 8L)};
@@ -340,7 +340,7 @@ auto ARM7TDMI::thumbDisassembleImmediate
auto ARM7TDMI::thumbDisassembleLoadLiteral
(n8 displacement, n3 d) -> string {
n32 address = ((_pc + 4) & ~3) + (displacement << 2);
n32 data = getDebugger(Word | Nonsequential, address);
n32 data = getDebugger(Word, address);
return {"ldr ", _r[d], ",[pc,#0x", hex(address, 8L), "] =0x", hex(data, 8L)};
}
@@ -3,9 +3,10 @@ auto ARM7TDMI::reload() -> void {
u32 size = !cpsr().t ? Word : Half;
pipeline.reload = false;
endBurst();
r(15).data &= ~mask;
pipeline.fetch.address = r(15) & ~mask;
pipeline.fetch.instruction = read(Prefetch | size | Nonsequential, pipeline.fetch.address);
pipeline.fetch.instruction = read(Prefetch | size, pipeline.fetch.address);
fetch();
}
@@ -16,18 +17,12 @@ auto ARM7TDMI::fetch() -> void {
pipeline.decode.thumb = cpsr().t;
pipeline.decode.irq = !cpsr().i;
u32 sequential = Sequential;
if(pipeline.nonsequential) {
pipeline.nonsequential = false;
sequential = Nonsequential;
}
u32 mask = !cpsr().t ? 3 : 1;
u32 size = !cpsr().t ? Word : Half;
r(15).data += size >> 3;
pipeline.fetch.address = r(15) & ~mask;
pipeline.fetch.instruction = read(Prefetch | size | sequential, pipeline.fetch.address);
pipeline.fetch.instruction = read(Prefetch | size, pipeline.fetch.address);
}
auto ARM7TDMI::instruction() -> void {
@@ -29,7 +29,7 @@ auto ARM7TDMI::armMoveToStatus(n4 field, n1 mode, n32 data) -> void {
if(field.bit(0)) {
if(mode || privileged()) {
psr.m = data.bit(0,4);
psr.m = data.bit(0,4) | 0x10;
psr.t = data.bit(5);
psr.f = data.bit(6);
psr.i = data.bit(7);
@@ -87,11 +87,12 @@ auto ARM7TDMI::armInstructionDataImmediateShift
auto ARM7TDMI::armInstructionDataRegisterShift
(n4 m, n2 type, n4 s, n4 d, n4 n, n1 save, n4 mode) -> void {
n8 rs = r(s) + (s == 15 ? 4 : 0);
n8 rs = r(s);
n32 rn = r(n) + (n == 15 ? 4 : 0);
n32 rm = r(m) + (m == 15 ? 4 : 0);
carry = cpsr().c;
idle();
switch(type) {
case 0: rm = LSL(rm, rs < 33 ? rs : (n8)33); break;
case 1: rm = LSR(rm, rs < 33 ? rs : (n8)33); break;
@@ -108,10 +109,10 @@ auto ARM7TDMI::armInstructionLoadImmediate
n32 rd = r(d);
if(pre == 1) rn = up ? rn + immediate : rn - immediate;
rd = load((half ? Half : Byte) | Nonsequential | Signed, rn);
rd = load((half ? Half : Byte) | Signed, rn);
if(pre == 0) rn = up ? rn + immediate : rn - immediate;
if(pre == 0 || writeback) r(n) = rn;
if(pre == 0 || writeback) r(n) = rn + (n == 15 ? 4 : 0);
r(d) = rd;
}
@@ -122,18 +123,19 @@ auto ARM7TDMI::armInstructionLoadRegister
n32 rd = r(d);
if(pre == 1) rn = up ? rn + rm : rn - rm;
rd = load((half ? Half : Byte) | Nonsequential | Signed, rn);
rd = load((half ? Half : Byte) | Signed, rn);
if(pre == 0) rn = up ? rn + rm : rn - rm;
if(pre == 0 || writeback) r(n) = rn;
if(pre == 0 || writeback) r(n) = rn + (n == 15 ? 4 : 0);
r(d) = rd;
}
auto ARM7TDMI::armInstructionMemorySwap
(n4 m, n4 d, n4 n, n1 byte) -> void {
n32 rm = r(m) + (m == 15 ? 4 : 0);
lock();
n32 word = load((byte ? Byte : Word) | Nonsequential, r(n));
store((byte ? Byte : Word) | Nonsequential, r(n), r(m));
n32 word = load((byte ? Byte : Word), r(n));
store((byte ? Byte : Word), r(n), rm);
r(d) = word;
unlock();
}
@@ -141,14 +143,14 @@ auto ARM7TDMI::armInstructionMemorySwap
auto ARM7TDMI::armInstructionMoveHalfImmediate
(n8 immediate, n4 d, n4 n, n1 mode, n1 writeback, n1 up, n1 pre) -> void {
n32 rn = r(n);
n32 rd = r(d);
n32 rd = r(d) + (d == 15 ? 4 : 0);
if(pre == 1) rn = up ? rn + immediate : rn - immediate;
if(mode == 1) rd = load(Half | Nonsequential, rn);
if(mode == 0) store(Half | Nonsequential, rn, rd);
if(mode == 1) rd = load(Half, rn);
if(mode == 0) store(Half, rn, rd);
if(pre == 0) rn = up ? rn + immediate : rn - immediate;
if(pre == 0 || writeback) r(n) = rn;
if(pre == 0 || writeback) r(n) = rn + (n == 15 ? 4 : 0);
if(mode == 1) r(d) = rd;
}
@@ -156,14 +158,14 @@ auto ARM7TDMI::armInstructionMoveHalfRegister
(n4 m, n4 d, n4 n, n1 mode, n1 writeback, n1 up, n1 pre) -> void {
n32 rn = r(n);
n32 rm = r(m);
n32 rd = r(d);
n32 rd = r(d) + (d == 15 ? 4 : 0);
if(pre == 1) rn = up ? rn + rm : rn - rm;
if(mode == 1) rd = load(Half | Nonsequential, rn);
if(mode == 0) store(Half | Nonsequential, rn, rd);
if(mode == 1) rd = load(Half, rn);
if(mode == 0) store(Half, rn, rd);
if(pre == 0) rn = up ? rn + rm : rn - rm;
if(pre == 0 || writeback) r(n) = rn;
if(pre == 0 || writeback) r(n) = rn + (n == 15 ? 4 : 0);
if(mode == 1) r(d) = rd;
}
@@ -173,8 +175,8 @@ auto ARM7TDMI::armInstructionMoveImmediateOffset
n32 rd = r(d) + (d == 15 ? 4 : 0);
if(pre == 1) rn = up ? rn + immediate : rn - immediate;
if(mode == 1) rd = load((byte ? Byte : Word) | Nonsequential, rn);
if(mode == 0) store((byte ? Byte : Word) | Nonsequential, rn, rd);
if(mode == 1) rd = load((byte ? Byte : Word), rn);
if(mode == 0) store((byte ? Byte : Word), rn, rd);
if(pre == 0) rn = up ? rn + immediate : rn - immediate;
if(pre == 0 || writeback) r(n) = rn;
@@ -185,33 +187,44 @@ auto ARM7TDMI::armInstructionMoveMultiple
(n16 list, n4 n, n1 mode, n1 writeback, n1 type, n1 up, n1 pre) -> void {
n32 rn = r(n);
n32 bitCount = list ? bit::count(list) : 16;
n32 rnEnd;
if(up == 1) rnEnd = r(n) + bitCount * 4; //IA,IB
if(up == 0) rnEnd = r(n) - bitCount * 4; //DA,DB
if(pre == 0 && up == 1) rn = rn + 0; //IA
if(pre == 1 && up == 1) rn = rn + 4; //IB
if(pre == 1 && up == 0) rn = rn - bitCount * 4 + 0; //DB
if(pre == 0 && up == 0) rn = rn - bitCount * 4 + 4; //DA
if(writeback && mode == 1 && !list.bit(n)) {
if(up == 1) r(n) = r(n) + bitCount * 4; //IA,IB
if(up == 0) r(n) = r(n) - bitCount * 4; //DA,DB
}
auto cpsrMode = cpsr().m;
bool usr = false;
if(type && mode == 1 && !list.bit(15)) usr = true;
if(type && mode == 0) usr = true;
if(usr) cpsr().m = PSR::USR;
u32 sequential = Nonsequential;
if(writeback && mode == 1 && !list.bit(n)) r(n) = rnEnd;
endBurst();
if(!list) {
if(mode == 1) r(15) = read(Word | sequential, rn);
if(mode == 0) write(Word | sequential, rn, r(15) + 4);
if(mode == 1) r(15) = read(Word, rn);
if(mode == 0) {
write(Word, rn, r(15) + 4);
//writeback occurs after first access
if(writeback) r(n) = rnEnd;
}
} else {
bool wroteBack = false;
for(u32 m : range(16)) {
if(!list.bit(m)) continue;
if(mode == 1) r(m) = read(Word | sequential, rn);
if(mode == 0) write(Word | sequential, rn, r(m) + (m == 15 ? 4 : 0));
if(mode == 1) r(m) = read(Word, rn);
if(mode == 0) {
write(Word, rn, r(m) + (!(n == 15 && writeback) && m == 15 ? 4 : 0));
if(!wroteBack && writeback) {
//writeback occurs after first access
r(n) = rnEnd;
wroteBack = true;
}
}
rn += 4;
sequential = Sequential;
}
}
@@ -223,19 +236,14 @@ auto ARM7TDMI::armInstructionMoveMultiple
cpsr() = spsr();
}
} else {
pipeline.nonsequential = true;
}
if(writeback && mode == 0) {
if(up == 1) r(n) = r(n) + bitCount * 4; //IA,IB
if(up == 0) r(n) = r(n) - bitCount * 4; //DA,DB
endBurst();
}
}
auto ARM7TDMI::armInstructionMoveRegisterOffset
(n4 m, n2 type, n5 shift, n4 d, n4 n, n1 mode, n1 writeback, n1 byte, n1 up, n1 pre) -> void {
n32 rm = r(m);
n32 rd = r(d);
n32 rd = r(d) + (d == 15 ? 4 : 0);
n32 rn = r(n);
carry = cpsr().c;
@@ -247,8 +255,8 @@ auto ARM7TDMI::armInstructionMoveRegisterOffset
}
if(pre == 1) rn = up ? rn + rm : rn - rm;
if(mode == 1) rd = load((byte ? Byte : Word) | Nonsequential, rn);
if(mode == 0) store((byte ? Byte : Word) | Nonsequential, rn, rd);
if(mode == 1) rd = load((byte ? Byte : Word), rn);
if(mode == 0) store((byte ? Byte : Word), rn, rd);
if(pre == 0) rn = up ? rn + rm : rn - rm;
if(pre == 0 || writeback) r(n) = rn;
@@ -275,7 +283,8 @@ auto ARM7TDMI::armInstructionMoveToStatusFromRegister
auto ARM7TDMI::armInstructionMultiply
(n4 m, n4 s, n4 n, n4 d, n1 save, n1 accumulate) -> void {
if(accumulate) idle();
r(d) = MUL(accumulate ? r(n) : 0, r(m), r(s));
n32 product = MUL(accumulate ? r(n) : 0, r(m), r(s));
if(d != 15) r(d) = product;
}
auto ARM7TDMI::armInstructionMultiplyLong
@@ -302,8 +311,8 @@ auto ARM7TDMI::armInstructionMultiplyLong
n64 rd = rm * rs;
if(accumulate) rd += (n64)r(h) << 32 | (n64)r(l) << 0;
r(l) = rd >> 0;
r(h) = rd >> 32;
if(l != 15) r(l) = rd >> 0;
if(h != 15) r(h) = rd >> 32;
if(save) {
cpsr().z = rd == 0;
@@ -1,14 +1,15 @@
auto ARM7TDMI::thumbInstructionALU
(n3 d, n3 m, n4 mode) -> void {
carry = cpsr().c;
switch(mode) {
case 0: r(d) = BIT(r(d) & r(m)); break; //AND
case 1: r(d) = BIT(r(d) ^ r(m)); break; //EOR
case 2: r(d) = BIT(LSL(r(d), r(m))); break; //LSL
case 3: r(d) = BIT(LSR(r(d), r(m))); break; //LSR
case 4: r(d) = BIT(ASR(r(d), r(m))); break; //ASR
case 2: r(d) = BIT(LSL(r(d), r(m))); idle(); break; //LSL
case 3: r(d) = BIT(LSR(r(d), r(m))); idle(); break; //LSR
case 4: r(d) = BIT(ASR(r(d), r(m))); idle(); break; //ASR
case 5: r(d) = ADD(r(d), r(m), cpsr().c); break; //ADC
case 6: r(d) = SUB(r(d), r(m), cpsr().c); break; //SBC
case 7: r(d) = BIT(ROR(r(d), r(m))); break; //ROR
case 7: r(d) = BIT(ROR(r(d), r(m))); idle(); break; //ROR
case 8: BIT(r(d) & r(m)); break; //TST
case 9: r(d) = SUB(0, r(m), 1); break; //NEG
case 10: SUB(r(d), r(m), 1); break; //CMP
@@ -24,7 +25,7 @@ auto ARM7TDMI::thumbInstructionALUExtended
(n4 d, n4 m, n2 mode) -> void {
switch(mode) {
case 0: r(d) = r(d) + r(m); break; //ADD
case 1: SUB(r(d), r(m), 1); break; //SUBS
case 1: SUB(r(d), r(m), 1); break; //CMP
case 2: r(d) = r(m); break; //MOV
}
}
@@ -92,6 +93,7 @@ auto ARM7TDMI::thumbInstructionBranchTest
auto ARM7TDMI::thumbInstructionImmediate
(n8 immediate, n3 d, n2 mode) -> void {
carry = cpsr().c;
switch(mode) {
case 0: r(d) = BIT(immediate); break; //MOV
case 1: SUB(r(d), immediate, 1); break; //CMP
@@ -103,22 +105,22 @@ auto ARM7TDMI::thumbInstructionImmediate
auto ARM7TDMI::thumbInstructionLoadLiteral
(n8 displacement, n3 d) -> void {
n32 address = (r(15) & ~3) + (displacement << 2);
r(d) = load(Word | Nonsequential, address);
r(d) = load(Word, address);
}
auto ARM7TDMI::thumbInstructionMoveByteImmediate
(n3 d, n3 n, n5 offset, n1 mode) -> void {
switch(mode) {
case 0: store(Byte | Nonsequential, r(n) + offset, r(d)); break; //STRB
case 1: r(d) = load(Byte | Nonsequential, r(n) + offset); break; //LDRB
case 0: store(Byte, r(n) + offset, r(d)); break; //STRB
case 1: r(d) = load(Byte, r(n) + offset); break; //LDRB
}
}
auto ARM7TDMI::thumbInstructionMoveHalfImmediate
(n3 d, n3 n, n5 offset, n1 mode) -> void {
switch(mode) {
case 0: store(Half | Nonsequential, r(n) + offset * 2, r(d)); break; //STRH
case 1: r(d) = load(Half | Nonsequential, r(n) + offset * 2); break; //LDRH
case 0: store(Half, r(n) + offset * 2, r(d)); break; //STRH
case 1: r(d) = load(Half, r(n) + offset * 2); break; //LDRH
}
}
@@ -126,58 +128,69 @@ auto ARM7TDMI::thumbInstructionMoveMultiple
(n8 list, n3 n, n1 mode) -> void {
n32 rn = r(n);
n32 bitCount = list ? bit::count(list) : 16;
n32 rnEnd = r(n) + bitCount * 4;
if(mode == 1 && !list.bit(n)) r(n) = r(n) + bitCount * 4;
if(mode == 1 && !list.bit(n)) r(n) = rnEnd;
u32 sequential = Nonsequential;
endBurst();
if(!list) {
if(mode == 1) r(15) = read(Word | sequential, rn);
if(mode == 0) write(Word | sequential, rn, r(15) + 2);
if(mode == 1) r(15) = read(Word, rn);
if(mode == 0) {
write(Word, rn, r(15) + 2);
//writeback occurs after first access
r(n) = rnEnd;
}
} else {
bool wroteBack = false;
for(u32 m : range(8)) {
if(!list.bit(m)) continue;
if(mode == 1) r(m) = read(Word | sequential, rn); //LDMIA
if(mode == 0) write(Word | sequential, rn, r(m)); //STMIA
if(mode == 1) r(m) = read(Word, rn); //LDMIA
if(mode == 0) {
write(Word, rn, r(m)); //STMIA
//writeback occurs after first access
if(!wroteBack) {
r(n) = rnEnd;
wroteBack = true;
}
}
rn += 4;
sequential = Sequential;
}
}
if(mode) {
idle();
} else {
pipeline.nonsequential = true;
r(n) = r(n) + bitCount * 4;
endBurst();
}
}
auto ARM7TDMI::thumbInstructionMoveRegisterOffset
(n3 d, n3 n, n3 m, n3 mode) -> void {
switch(mode) {
case 0: store(Word | Nonsequential, r(n) + r(m), r(d)); break; //STR
case 1: store(Half | Nonsequential, r(n) + r(m), r(d)); break; //STRH
case 2: store(Byte | Nonsequential, r(n) + r(m), r(d)); break; //STRB
case 3: r(d) = load(Byte | Nonsequential | Signed, r(n) + r(m)); break; //LDSB
case 4: r(d) = load(Word | Nonsequential, r(n) + r(m)); break; //LDR
case 5: r(d) = load(Half | Nonsequential, r(n) + r(m)); break; //LDRH
case 6: r(d) = load(Byte | Nonsequential, r(n) + r(m)); break; //LDRB
case 7: r(d) = load(Half | Nonsequential | Signed, r(n) + r(m)); break; //LDSH
case 0: store(Word, r(n) + r(m), r(d)); break; //STR
case 1: store(Half, r(n) + r(m), r(d)); break; //STRH
case 2: store(Byte, r(n) + r(m), r(d)); break; //STRB
case 3: r(d) = load(Byte | Signed, r(n) + r(m)); break; //LDSB
case 4: r(d) = load(Word, r(n) + r(m)); break; //LDR
case 5: r(d) = load(Half, r(n) + r(m)); break; //LDRH
case 6: r(d) = load(Byte, r(n) + r(m)); break; //LDRB
case 7: r(d) = load(Half | Signed, r(n) + r(m)); break; //LDSH
}
}
auto ARM7TDMI::thumbInstructionMoveStack
(n8 immediate, n3 d, n1 mode) -> void {
switch(mode) {
case 0: store(Word | Nonsequential, r(13) + immediate * 4, r(d)); break; //STR
case 1: r(d) = load(Word | Nonsequential, r(13) + immediate * 4); break; //LDR
case 0: store(Word, r(13) + immediate * 4, r(d)); break; //STR
case 1: r(d) = load(Word, r(13) + immediate * 4); break; //LDR
}
}
auto ARM7TDMI::thumbInstructionMoveWordImmediate
(n3 d, n3 n, n5 offset, n1 mode) -> void {
switch(mode) {
case 0: store(Word | Nonsequential, r(n) + offset * 4, r(d)); break; //STR
case 1: r(d) = load(Word | Nonsequential, r(n) + offset * 4); break; //LDR
case 0: store(Word, r(n) + offset * 4, r(d)); break; //STR
case 1: r(d) = load(Word, r(n) + offset * 4); break; //LDR
}
}
@@ -202,29 +215,28 @@ auto ARM7TDMI::thumbInstructionStackMultiple
if(mode == 1) r(13) = r(13) + bitCount * 4; //POP
if(mode == 0) sp = sp - bitCount * 4 + 0; //PUSH
u32 sequential = Nonsequential;
endBurst();
if(bitCount == 16) {
if(mode == 1) r(15) = read(Word | sequential, sp); //POP
if(mode == 0) write(Word | sequential, sp, r(15) + 2); //PUSH
if(mode == 1) r(15) = read(Word, sp); //POP
if(mode == 0) write(Word, sp, r(15) + 2); //PUSH
} else {
for(u32 m : range(8)) {
if(!list.bit(m)) continue;
if(mode == 1) r(m) = read(Word | sequential, sp); //POP
if(mode == 0) write(Word | sequential, sp, r(m)); //PUSH
if(mode == 1) r(m) = read(Word, sp); //POP
if(mode == 0) write(Word, sp, r(m)); //PUSH
sp += 4;
sequential = Sequential;
}
}
if(lrpc) {
if(mode == 1) r(15) = read(Word | sequential, sp); //POP
if(mode == 0) write(Word | sequential, sp, r(14)); //PUSH
if(mode == 1) r(15) = read(Word, sp); //POP
if(mode == 0) write(Word, sp, r(14)); //PUSH
sp += 4;
}
if(mode) {
idle();
} else {
pipeline.nonsequential = true;
endBurst();
r(13) = r(13) - bitCount * 4; //PUSH
}
}
+9 -6
View File
@@ -1,14 +1,16 @@
auto ARM7TDMI::idle() -> void {
pipeline.nonsequential = true;
endBurst();
sleep();
}
auto ARM7TDMI::read(u32 mode, n32 address) -> n32 {
return get(mode, address);
n32 word = get(mode, address);
nonsequential = false; //allows burst transfer to continue
return word;
}
auto ARM7TDMI::load(u32 mode, n32 address) -> n32 {
pipeline.nonsequential = true;
endBurst();
auto word = get(Load | mode, address);
if(mode & Half) {
address &= 1;
@@ -28,12 +30,13 @@ auto ARM7TDMI::load(u32 mode, n32 address) -> n32 {
}
auto ARM7TDMI::write(u32 mode, n32 address, n32 word) -> void {
pipeline.nonsequential = true;
return set(mode, address, word);
set(mode, address, word);
nonsequential = false; //allows burst transfer to continue
return;
}
auto ARM7TDMI::store(u32 mode, n32 address, n32 word) -> void {
pipeline.nonsequential = true;
endBurst();
if(mode & Half) { word &= 0xffff; word |= word << 16; }
if(mode & Byte) { word &= 0xff; word |= word << 8; word |= word << 16; }
return set(Store | mode, address, word);
@@ -3,6 +3,7 @@ auto ARM7TDMI::serialize(serializer& s) -> void {
s(pipeline);
s(carry);
s(irq);
s(nonsequential);
}
auto ARM7TDMI::Processor::serialize(serializer& s) -> void {
@@ -58,7 +59,6 @@ auto ARM7TDMI::PSR::serialize(serializer& s) -> void {
auto ARM7TDMI::Pipeline::serialize(serializer& s) -> void {
s(reload);
s(nonsequential);
s(fetch.address);
s(fetch.instruction);
s(fetch.thumb);
@@ -150,6 +150,7 @@ auto SH2::internalReadByte(u32 address, n8 data) -> n8 {
data.bit(5) = wdt.wtcsr.tme;
data.bit(6) = wdt.wtcsr.wtit;
data.bit(7) = wdt.wtcsr.ovf;
if(wdt.wtcsr.wtit) debug(unimplemented, "SH2: wdt.wtit = 1");
return data;
//WTCNT: watchdog timer counter
@@ -42,17 +42,20 @@ auto V30MZ::interrupt(u8 vector, InterruptSource source) -> bool {
auto V30MZ::interrupt(u8 vector) -> bool {
state.halt = false;
if(!PSW.IE) return false;
if(!state.interrupt || !PSW.IE) return false;
return interrupt(vector, InterruptSource::INT);
}
auto V30MZ::nonMaskableInterrupt() -> bool {
return interrupt(2, InterruptSource::NMI);
auto V30MZ::nonMaskableInterrupt(bool value) -> bool {
state.nmi = value;
return true;
}
#define op(id, name, ...) case id: instruction##name(__VA_ARGS__); break;
auto V30MZ::instruction() -> void {
state.brk = PSW.BRK;
state.interrupt = PSW.IE;
state.poll = 1;
state.prefix = 0;
if(state.halt) return wait(1);
@@ -317,7 +320,8 @@ auto V30MZ::instruction() -> void {
}
if(!state.prefix) prefixFlush();
if(PSW.BRK) interrupt(1, InterruptSource::SingleStep);
if(state.poll && state.nmi) interrupt(2, InterruptSource::NMI);
if(state.poll && state.brk) interrupt(1, InterruptSource::SingleStep);
}
#undef op
@@ -105,7 +105,6 @@ auto V30MZ::instructionReturnInt() -> void {
PS = pop();
PSW = pop();
flush();
state.poll = 0;
}
auto V30MZ::instructionInt3() -> void {
@@ -182,7 +181,6 @@ auto V30MZ::instructionPushFlags() -> void {
auto V30MZ::instructionPopFlags() -> void {
wait(2);
PSW = pop();
state.poll = 0;
}
auto V30MZ::instructionPushAll() -> void {
@@ -21,5 +21,4 @@ auto V30MZ::instructionClearFlag(u32 bit) -> void {
auto V30MZ::instructionSetFlag(u32 bit) -> void {
wait(4);
PSW |= 1 << bit;
if(bit == PSW.IE.bit()) state.poll = 0;
}
@@ -43,21 +43,27 @@ auto V30MZ::instructionUndefined1() -> void {
}
template<u32 size> auto V30MZ::instructionIn() -> void {
wait(6);
// TODO: The exact cycle on which I/O access is performed remains unknown.
wait(5);
setAccumulator<size>(in<size>(fetch<Byte>()));
wait(1);
}
template<u32 size> auto V30MZ::instructionOut() -> void {
// TODO: The exact cycle on which I/O access is performed remains unknown.
wait(6);
out<size>(fetch<Byte>(), getAccumulator<size>());
}
template<u32 size> auto V30MZ::instructionInDW() -> void {
wait(5);
// TODO: The exact cycle on which I/O access is performed remains unknown.
wait(4);
setAccumulator<size>(in<size>(DW));
wait(1);
}
template<u32 size> auto V30MZ::instructionOutDW() -> void {
// TODO: The exact cycle on which I/O access is performed remains unknown.
wait(5);
out<size>(DW, getAccumulator<size>());
}
@@ -14,14 +14,14 @@ auto V30MZ::instructionMoveMemSeg() -> void {
wait(1);
modRM();
setMemory<Word>(getSegment());
state.poll = 0;
}
auto V30MZ::instructionMoveSegMem() -> void {
wait(2);
modRM();
setSegment(getMemory<Word>());
if((modrm.reg & 3) == 3) state.poll = 0;
// delay interrupt polling if stack segment changed
if((modrm.reg & 3) == 2) state.poll = 0;
}
template<u32 size> auto V30MZ::instructionMoveAccMem() -> void {
@@ -2,6 +2,9 @@ auto V30MZ::serialize(serializer& s) -> void {
s(state.halt);
s(state.poll);
s(state.prefix);
s(state.interrupt);
s(state.brk);
s(state.nmi);
s(opcode);
+3
View File
@@ -26,6 +26,9 @@ auto V30MZ::power() -> void {
state.halt = 0;
state.poll = 1;
state.prefix = 0;
state.interrupt = 0;
state.brk = 0;
state.nmi = 0;
opcode = 0;
prefixFlush();
+7 -4
View File
@@ -41,7 +41,7 @@ struct V30MZ {
//instruction.cpp
auto prefixFlush() -> void;
auto interrupt(u8 vector) -> bool;
auto nonMaskableInterrupt() -> bool;
auto nonMaskableInterrupt(bool value) -> bool;
auto instruction() -> void;
//registers.cpp
@@ -237,9 +237,12 @@ struct V30MZ {
auto disassembleContext() -> string;
struct State {
bool halt; //set to true for hlt instruction; blocks execution until next interrupt
bool poll; //set to false to suppress interrupt polling between CPU instructions
bool prefix; //set to true for prefix instructions; prevents flushing of Prefix struct
bool halt; //set to true for hlt instruction; blocks execution until next interrupt
bool poll; //set to false to suppress interrupt polling between CPU instructions
bool prefix; //set to true for prefix instructions; prevents flushing of Prefix struct
bool interrupt; //interrupt enable processor status before CPU instruction execution
bool brk; //breakpoint enable processor status before CPU instruction execution
bool nmi; //non-maskable interrupt requested
} state;
u8 opcode;
-17
View File
@@ -1,17 +0,0 @@
ares.components += z80
ares.components += tms9918
ares.components += sn76489
ares.objects += ares-cv-cpu
ares.objects += ares-cv-vdp
ares.objects += ares-cv-psg
ares.objects += ares-cv-system
ares.objects += ares-cv-cartridge
ares.objects += ares-cv-controller
$(object.path)/ares-cv-cpu.o: $(ares.path)/cv/cpu/cpu.cpp
$(object.path)/ares-cv-vdp.o: $(ares.path)/cv/vdp/vdp.cpp
$(object.path)/ares-cv-psg.o: $(ares.path)/cv/psg/psg.cpp
$(object.path)/ares-cv-system.o: $(ares.path)/cv/system/system.cpp
$(object.path)/ares-cv-cartridge.o: $(ares.path)/cv/cartridge/cartridge.cpp
$(object.path)/ares-cv-controller.o: $(ares.path)/cv/controller/controller.cpp
-23
View File
@@ -1,23 +0,0 @@
ares.components += mos6502
ares.components += ym2149
ares.components += ym2413
ares.components += m24c
ares.components += sst39sf0x0
ares.objects += ares-fc-system
ares.objects += ares-fc-controller
ares.objects += ares-fc-expansion
ares.objects += ares-fc-cartridge
ares.objects += ares-fc-cpu
ares.objects += ares-fc-apu
ares.objects += ares-fc-ppu
ares.objects += ares-fc-fds
$(object.path)/ares-fc-system.o: $(ares.path)/fc/system/system.cpp
$(object.path)/ares-fc-controller.o: $(ares.path)/fc/controller/controller.cpp
$(object.path)/ares-fc-expansion.o: $(ares.path)/fc/expansion/expansion.cpp
$(object.path)/ares-fc-cartridge.o: $(ares.path)/fc/cartridge/cartridge.cpp
$(object.path)/ares-fc-cpu.o: $(ares.path)/fc/cpu/cpu.cpp
$(object.path)/ares-fc-apu.o: $(ares.path)/fc/apu/apu.cpp
$(object.path)/ares-fc-ppu.o: $(ares.path)/fc/ppu/ppu.cpp
$(object.path)/ares-fc-fds.o: $(ares.path)/fc/fds/fds.cpp
-16
View File
@@ -1,16 +0,0 @@
ares.components += sm83
ares.components += m93lcx6
ares.objects += ares-gb-system
ares.objects += ares-gb-bus
ares.objects += ares-gb-cartridge
ares.objects += ares-gb-cpu
ares.objects += ares-gb-ppu
ares.objects += ares-gb-apu
$(object.path)/ares-gb-system.o: $(ares.path)/gb/system/system.cpp
$(object.path)/ares-gb-cartridge.o: $(ares.path)/gb/cartridge/cartridge.cpp
$(object.path)/ares-gb-bus.o: $(ares.path)/gb/bus/bus.cpp
$(object.path)/ares-gb-cpu.o: $(ares.path)/gb/cpu/cpu.cpp
$(object.path)/ares-gb-ppu.o: $(ares.path)/gb/ppu/ppu.cpp
$(object.path)/ares-gb-apu.o: $(ares.path)/gb/apu/apu.cpp
+4
View File
@@ -44,6 +44,7 @@ struct APU : Thread {
n11 frequency;
bool counter;
n4 sample;
i16 output;
bool dutyOutput;
n3 phase;
@@ -77,6 +78,7 @@ struct APU : Thread {
n11 frequency;
bool counter;
n4 sample;
i16 output;
bool dutyOutput;
n3 phase;
@@ -105,6 +107,7 @@ struct APU : Thread {
bool counter;
n8 pattern[16];
n4 sample;
i16 output;
u32 length;
u32 period;
@@ -135,6 +138,7 @@ struct APU : Thread {
n3 divisor;
bool counter;
n4 sample;
i16 output;
u32 length;
n3 envelopePeriod;
+16
View File
@@ -1,4 +1,20 @@
auto APU::readIO(u32 cycle, n16 address, n8 data) -> n8 {
if(Model::GameBoyColor()) {
//PCM12
if(address == 0xff76 && cycle == 2) {
data.bit(0,3) = square1.sample;
data.bit(4,7) = square2.sample;
return data;
}
//PCM34
if(address == 0xff77 && cycle == 2) {
data.bit(0,3) = wave.sample;
data.bit(4,7) = noise.sample;
return data;
}
}
if(address < 0xff10 || address > 0xff3f) return data;
//NR10
+2 -1
View File
@@ -16,7 +16,7 @@ auto APU::Noise::run() -> void {
}
}
n4 sample = lfsr & 1 ? 0 : (u32)volume;
sample = lfsr & 1 ? 0 : (u32)volume;
if(!enable) sample = 0;
output = sample;
@@ -79,6 +79,7 @@ auto APU::Noise::serialize(serializer& s) -> void {
s(divisor);
s(counter);
s(sample);
s(output);
s(length);
s(envelopePeriod);
+2 -1
View File
@@ -14,7 +14,7 @@ auto APU::Square1::run() -> void {
}
}
n4 sample = dutyOutput ? (u32)volume : 0;
sample = dutyOutput ? (u32)volume : 0;
if(!enable) sample = 0;
output = sample;
@@ -120,6 +120,7 @@ auto APU::Square1::serialize(serializer& s) -> void {
s(frequency);
s(counter);
s(sample);
s(output);
s(dutyOutput);
s(phase);
+2 -1
View File
@@ -14,7 +14,7 @@ auto APU::Square2::run() -> void {
}
}
n4 sample = dutyOutput ? (u32)volume : 0;
sample = dutyOutput ? (u32)volume : 0;
if(!enable) sample = 0;
output = sample;
@@ -77,6 +77,7 @@ auto APU::Square2::serialize(serializer& s) -> void {
s(frequency);
s(counter);
s(sample);
s(output);
s(dutyOutput);
s(phase);
+2 -1
View File
@@ -12,7 +12,7 @@ auto APU::Wave::run() -> void {
}
static const u32 shift[] = {4, 0, 1, 2}; //0%, 100%, 50%, 25%
n4 sample = patternSample >> shift[volume];
sample = patternSample >> shift[volume];
if(!enable) sample = 0;
output = sample;
@@ -95,6 +95,7 @@ auto APU::Wave::serialize(serializer& s) -> void {
s(counter);
s(pattern);
s(sample);
s(output);
s(length);
s(period);
+2 -4
View File
@@ -66,11 +66,8 @@ struct CPU : SM83, Thread {
//timing.cpp
auto step() -> void;
auto step(u32 clocks) -> void;
auto timer262144hz() -> void;
auto timer65536hz() -> void;
auto timer16384hz() -> void;
auto timerTick() -> void;
auto timer8192hz() -> void;
auto timer4096hz() -> void;
auto timer1024hz() -> void;
auto hblank() -> void;
auto hblankTrigger() -> void;
@@ -79,6 +76,7 @@ struct CPU : SM83, Thread {
n22 clock;
n8 interruptLatch;
n1 hblankPending;
n1 timerLine;
//$ff00 JOYP
n4 joyp;
-10
View File
@@ -139,16 +139,6 @@ auto CPU::readIO(u32 cycle, n16 address, n8 data) -> n8 {
return data;
}
if(Model::GameBoyColor())
if(address == 0xff76 && cycle == 2) { //???
return 0xff;
}
if(Model::GameBoyColor())
if(address == 0xff77 && cycle == 2) { //???
return 0xff;
}
if(address == 0xffff && cycle == 2) { //IE
return status.interruptEnable;
}
+1
View File
@@ -8,6 +8,7 @@ auto CPU::serialize(serializer& s) -> void {
s(status.clock);
s(status.interruptLatch);
s(status.hblankPending);
s(status.timerLine);
s(status.joyp);
s(status.p14);
+17 -39
View File
@@ -7,14 +7,21 @@ auto CPU::step() -> void {
}
auto CPU::step(u32 clocks) -> void {
//determine which bit of DIV is used to check timer state
n32 timerBit = 9;
if(status.timerClock != 0) timerBit = status.timerClock * 2 + 1;
for(auto n : range(clocks)) {
if(!r.stop) status.div++;
if((n4 )status.div == 0) timer262144hz();
if((n6 )status.div == 0) timer65536hz();
if((n8 )status.div == 0) timer16384hz();
if((n9 )status.div == 0) timer8192hz();
if((n10)status.div == 0) timer4096hz();
if((n12)status.div == 0) timer1024hz();
//tick timer on falling edge
n1 timerLineNew = status.div.bit(timerBit) & status.timerEnable;
if(timerLineNew == 0 && status.timerLine == 1) timerTick();
if((n9 )status.div == 0) timer8192hz(); //serial
if((n12)status.div == 0) timer1024hz(); //joypad
status.timerLine = timerLineNew;
Thread::step(1);
Thread::synchronize();
@@ -25,30 +32,10 @@ auto CPU::step(u32 clocks) -> void {
}
}
auto CPU::timer262144hz() -> void {
if(status.timerEnable && status.timerClock == 1) {
if(++status.tima == 0) {
status.tima = status.tma;
raise(Interrupt::Timer);
}
}
}
auto CPU::timer65536hz() -> void {
if(status.timerEnable && status.timerClock == 2) {
if(++status.tima == 0) {
status.tima = status.tma;
raise(Interrupt::Timer);
}
}
}
auto CPU::timer16384hz() -> void {
if(status.timerEnable && status.timerClock == 3) {
if(++status.tima == 0) {
status.tima = status.tma;
raise(Interrupt::Timer);
}
auto CPU::timerTick() -> void {
if(++status.tima == 0) {
status.tima = status.tma;
raise(Interrupt::Timer);
}
}
@@ -63,15 +50,6 @@ auto CPU::timer8192hz() -> void {
}
}
auto CPU::timer4096hz() -> void {
if(status.timerEnable && status.timerClock == 0) {
if(++status.tima == 0) {
status.tima = status.tma;
raise(Interrupt::Timer);
}
}
}
auto CPU::timer1024hz() -> void {
joypPoll();
}
+1 -1
View File
@@ -1,4 +1,4 @@
static const string SerializerVersion = "v131";
static const string SerializerVersion = "v143.1";
auto System::serialize(bool synchronize) -> serializer {
if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);
-19
View File
@@ -1,19 +0,0 @@
ares.components += arm7tdmi
ares.objects += ares-gba-memory
ares.objects += ares-gba-system
ares.objects += ares-gba-cartridge
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
$(object.path)/ares-gba-cartridge.o: $(ares.path)/gba/cartridge/cartridge.cpp
$(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
+3 -32
View File
@@ -11,14 +11,12 @@ Cartridge& cartridge = cartridgeSlot.cartridge;
#include "serialization.cpp"
Cartridge::Cartridge() {
mrom.data = new n8[mrom.size = 32 * 1024 * 1024];
sram.data = new n8[sram.size = 32 * 1024];
eeprom.data = new n8[eeprom.size = 8 * 1024];
flash.data = new n8[flash.size = 128 * 1024];
}
Cartridge::~Cartridge() {
delete[] mrom.data;
delete[] sram.data;
delete[] eeprom.data;
delete[] flash.data;
@@ -37,7 +35,8 @@ auto Cartridge::connect() -> void {
if(auto fp = pak->read("program.rom")) {
mrom.size = min(32_MiB, fp->size());
fp->read({mrom.data, mrom.size});
mrom.data.allocate(mrom.size >> 1);
mrom.data.load(fp);
mrom.mirror = pak->attribute("mirror").boolean();
}
@@ -80,7 +79,7 @@ auto Cartridge::connect() -> void {
auto Cartridge::disconnect() -> void {
if(!node) return;
memory::fill<u8>(mrom.data, mrom.size);
mrom.data.reset();
memory::fill<u8>(sram.data, sram.size);
memory::fill<u8>(eeprom.data, eeprom.size);
memory::fill<u8>(flash.data, flash.size);
@@ -111,32 +110,4 @@ auto Cartridge::power() -> void {
flash.power();
}
#define RAM_ANALYZE
auto Cartridge::read(u32 mode, n32 address) -> n32 {
if(address < 0x0e00'0000) {
if(has.eeprom && (address & eeprom.mask) == eeprom.test) return eeprom.read();
return mrom.read(mode, address);
} else {
n32 word = 0xff;
if(has.sram) word = sram.read(address);
if(has.flash) word = flash.read(address);
word |= word << 8;
word |= word << 16;
return word;
}
}
auto Cartridge::write(u32 mode, n32 address, n32 word) -> void {
if(address < 0x0e00'0000) {
if(has.eeprom && (address & eeprom.mask) == eeprom.test) return eeprom.write(word & 1);
return mrom.write(mode, address, word);
} else {
if(mode & Word) word = word >> (8 * (address & 3));
if(mode & Half) word = word >> (8 * (address & 1));
if(has.sram) return sram.write(address, word);
if(has.flash) return flash.write(address, word);
}
}
}
+49 -2
View File
@@ -17,8 +17,55 @@ struct Cartridge {
auto save() -> void;
auto power() -> void;
auto read(u32 mode, n32 address) -> n32;
auto write(u32 mode, n32 address, n32 word) -> void;
template<bool UseDebugger>
auto readRom(u32 mode, n32 address) -> n32 {
if(mode & Word) {
n32 word = 0;
word |= readRom<UseDebugger>(mode & ~Word | Half, (address & ~3) + 0) << 0;
word |= readRom<UseDebugger>(Sequential | Half, (address & ~3) + 2) << 16;
return word;
}
if(has.eeprom && (address & eeprom.mask) == eeprom.test) return eeprom.read();
n32 romAddr;
if constexpr(!UseDebugger) romAddr = mrom.burstAddr(mode, address);
if constexpr( UseDebugger) romAddr = address & 0x1ff'fffe;
n16 half = mrom.read(mode, romAddr);
if constexpr(!UseDebugger) mrom.pageAddr++;
if(mode & Byte) return half.byte(address & 1);
return half;
}
auto readBackup(u32 mode, n32 address) -> n32 {
n32 word = 0xff;
if(has.sram) word = sram.read(address);
if(has.flash) word = flash.read(address);
word *= 0x01010101;
return word;
}
auto writeRom(u32 mode, n32 address, n32 word) -> void {
if(mode & Word) {
writeRom(mode & ~Word | Half, (address & ~3) + 0, word >> 0);
writeRom(Sequential | Half, (address & ~3) + 2, word >> 16);
}
if(has.eeprom && (address & eeprom.mask) == eeprom.test) return eeprom.write(word & 1);
n32 romAddr = mrom.burstAddr(mode, address);
mrom.write(mode, romAddr, word);
mrom.pageAddr++;
return;
}
auto writeBackup(u32 mode, n32 address, n32 word) -> void {
if(mode & Word) word = word >> (8 * (address & 3));
if(mode & Half) word = word >> (8 * (address & 1));
if(has.sram) return sram.write(address, word);
if(has.flash) return flash.write(address, word);
}
auto serialize(serializer&) -> void;
+6 -4
View File
@@ -1,15 +1,17 @@
struct MROM {
//mrom.cpp
auto read(u32 mode, n32 address) -> n32;
auto readBus(u32 mode, n32 address) -> n16;
auto write(u32 mode, n32 address, n32 word) -> void;
auto read(u32 mode, n32 address) -> n16;
auto write(u32 mode, n32 address, n16 half) -> void;
auto burstAddr(u32 mode, n32 address) -> n32;
//serialization.cpp
auto serialize(serializer&) -> void;
n8* data = nullptr;
Memory::Readable<n16> data;
u32 size;
u32 mask;
n16 pageAddr;
n1 burst;
bool mirror;
} mrom;
+20 -22
View File
@@ -1,28 +1,26 @@
auto Cartridge::MROM::read(u32 mode, n32 address) -> n32 {
if(mode & Word) {
n32 word = 0;
word |= readBus(mode & ~Word | Half, (address & ~3) + 0) << 0;
word |= readBus(mode & ~Word | Half, (address & ~3) + 2) << 16;
return word;
}
return readBus(mode, address);
}
auto Cartridge::MROM::readBus(u32 mode, n32 address) -> n16 {
address &= 0x01ff'ffff;
auto Cartridge::MROM::read(u32 mode, n32 address) -> n16 {
//out-of-bounds accesses
if(address >= size) {
if(!mirror) return (n16)(address >> 1);
address &= size - 1;
if(!mirror) return pageAddr;
address &= (size - 1);
}
if(mode & Half) address &= ~1;
auto p = data + address;
if(mode & Half) return p[0] << 0 | p[1] << 8;
if(mode & Byte) return p[0] << 0;
return 0; //should never occur
return data[address >> 1];
}
auto Cartridge::MROM::write(u32 mode, n32 address, n32 word) -> void {
auto Cartridge::MROM::write(u32 mode, n32 address, n16 half) -> void {
}
auto Cartridge::MROM::burstAddr(u32 mode, n32 address) -> n32 {
//use latched lower 16 bits of address lines if sequential
n8 page = address >> 17;
if(mode & Nonsequential) {
pageAddr = address >> 1;
burst = true;
}
//end burst transfer if requesting last address on page
if((address & 0x1fffe) == 0x1fffe) burst = false;
return (page << 17) | (pageAddr << 1);
}
+2
View File
@@ -8,6 +8,8 @@ auto Cartridge::serialize(serializer& s) -> void {
auto Cartridge::MROM::serialize(serializer& s) -> void {
s(size);
s(mask);
s(pageAddr);
s(burst);
}
auto Cartridge::SRAM::serialize(serializer& s) -> void {
+48 -10
View File
@@ -43,19 +43,29 @@ inline auto CPU::getBus(u32 mode, n32 address) -> n32 {
word = ppu.readOAM(mode, address);
break;
case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d:
if constexpr(UseDebugger) return cartridge.readRom<true>(mode, address);
mode = cartMode(mode, address);
context.romAccess = true;
if(mode & Prefetch && wait.prefetch) {
prefetchSync(address);
prefetchStep(1);
word = prefetchRead();
if(mode & Word) word |= prefetchRead() << 16;
} else {
if constexpr(!UseDebugger) prefetchReset();
if constexpr(!UseDebugger) step(waitCartridge(mode, address));
word = cartridge.read(mode, address);
if(context.dmaActive) context.dmaRomAccess = true;
prefetchReset();
step(waitCartridge(mode, address));
word = cartridge.readRom<false>(mode, address);
}
break;
case 0x0e: case 0x0f:
if constexpr(!UseDebugger) prefetchReset();
if constexpr(!UseDebugger) step(waitCartridge(mode, address));
word = cartridge.readBackup(mode, address);
break;
default:
if constexpr(!UseDebugger) prefetchStep(1);
return openBus.get(mode, address);
@@ -71,7 +81,10 @@ inline auto CPU::getBus(u32 mode, n32 address) -> n32 {
auto CPU::get(u32 mode, n32 address) -> n32 {
dmaRun();
ARM7TDMI::irq = irq.synchronizer;
return getBus<false>(mode, address);
context.romAccess = false;
u32 word = getBus<false>(mode, address);
if(!context.romAccess && !context.dmaActive) cartridge.mrom.burst = false;
return word;
}
auto CPU::getDebugger(u32 mode, n32 address) -> n32 {
@@ -81,6 +94,7 @@ auto CPU::getDebugger(u32 mode, n32 address) -> n32 {
auto CPU::set(u32 mode, n32 address, n32 word) -> void {
dmaRun();
ARM7TDMI::irq = irq.synchronizer;
context.romAccess = false;
if(memory.biosSwap && address < 0x0400'0000) address ^= 0x0200'0000;
@@ -117,10 +131,19 @@ auto CPU::set(u32 mode, n32 address, n32 word) -> void {
ppu.writeOAM(mode, address, word);
break;
case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d:
mode = cartMode(mode, address);
context.romAccess = true;
if(context.dmaActive) context.dmaRomAccess = true;
prefetchReset();
step(waitCartridge(mode, address));
cartridge.write(mode, address, word);
cartridge.writeRom(mode, address, word);
break;
case 0x0e: case 0x0f:
prefetchReset();
step(waitCartridge(mode, address));
cartridge.writeBackup(mode, address, word);
break;
default:
@@ -130,6 +153,7 @@ auto CPU::set(u32 mode, n32 address, n32 word) -> void {
}
openBus.set(mode, address, word);
if(!context.romAccess && !context.dmaActive) cartridge.mrom.burst = false;
}
auto CPU::lock() -> void {
@@ -158,13 +182,27 @@ auto CPU::waitCartridge(u32 mode, n32 address) -> u32 {
}
bool sequential = (mode & Sequential);
if((address & 0x1fffe) == 0) sequential = false; //N cycle on 16-bit ROM crossing 128KB page boundary (RAM S==N)
u32 clocks = sequential ? s : n;
u32 clocks = (mode & Sequential) ? s : n;
if(mode & Word) clocks += s; //16-bit bus requires two transfers for words
return clocks;
}
auto CPU::cartMode(u32 mode, n32 address) -> u32 {
//if no burst transfer is active, start a new burst transfer
if(cartridge.mrom.burst == false) return mode | Nonsequential;
//determine whether sequential access may be performed
if(!context.dmaActive) {
u32 sequential = nonsequential ? Nonsequential : Sequential;
mode |= sequential;
} else {
u32 sequential = context.dmaRomAccess ? Sequential : Nonsequential;
mode |= sequential;
}
return mode;
}
auto CPU::OpenBus::get(u32 mode, n32 address) -> n32 {
if(mode & Word) address &= ~3;
if(mode & Half) address &= ~1;
+9 -1
View File
@@ -58,11 +58,18 @@ auto CPU::main() -> void {
auto CPU::dmaRun() -> void {
if(!context.dmaActive && !context.busLocked) {
context.dmaActive = true;
while(dma[0].run() | dma[1].run() | dma[2].run() | dma[3].run());
while(true) {
if(dma[0].run()) continue;
if(dma[1].run()) continue;
if(dma[2].run()) continue;
if(dma[3].run()) continue;
break;
}
if(context.dmaRan) {
idle();
context.dmaRan = false;
context.dmaRomAccess = false;
context.dmaActiveChannel = 0; //assign burst to a channel that cannot access ROM
}
context.dmaActive = false;
}
@@ -81,6 +88,7 @@ inline auto CPU::stepIRQ() -> void {
auto CPU::step(u32 clocks) -> void {
if(!clocks) return;
context.hcounter = (context.hcounter + clocks) % 1232;
dma[0].waiting = max(0, dma[0].waiting - (s32)clocks);
dma[1].waiting = max(0, dma[1].waiting - (s32)clocks);
+5 -1
View File
@@ -68,6 +68,7 @@ struct CPU : ARM7TDMI, Thread, IO {
auto unlock() -> void override;
auto waitEWRAM(u32 mode) -> u32;
auto waitCartridge(u32 mode, n32 address) -> u32;
auto cartMode(u32 mode, n32 address) -> u32;
//io.cpp
auto readIO(n32 address) -> n8 override;
@@ -260,11 +261,14 @@ struct CPU : ARM7TDMI, Thread, IO {
n1 halted;
n1 stopped;
n1 booted; //set to true by the GBA BIOS
n1 dmaRan;
n1 romAccess;
n1 dmaRomAccess;
n1 dmaRan;
n1 dmaActive;
n2 dmaActiveChannel;
n1 timerLatched;
n1 busLocked;
n32 hcounter;
} context;
};
+6 -11
View File
@@ -10,39 +10,34 @@ inline auto CPU::DMA::run() -> bool {
auto CPU::DMA::transfer() -> void {
u32 seek = size ? 4 : 2;
u32 mode = size ? Word : Half;
mode |= latch.length() == length() ? Nonsequential : Sequential;
if(!cpu.context.dmaRan) {
cpu.context.dmaRan = true;
cpu.idle();
}
if(cpu.context.dmaActiveChannel != id) {
//channel has switched - new burst transfer must be started
cpu.context.dmaRomAccess = 0;
cpu.context.dmaActiveChannel = id;
}
if(latch.source() < 0x0200'0000) {
cpu.idle(); //cannot access BIOS
} else {
n32 addr = latch.source();
if(mode & Word) addr &= ~3;
if(mode & Half) addr &= ~1;
if(addr & 0x0800'0000) cpu.context.dmaRomAccess = true;
latch.data = cpu.get(mode, addr);
if(mode & Half) latch.data |= latch.data << 16;
}
if(mode & Nonsequential) {
if((source() & 0x0800'0000) && (target() & 0x0800'0000)) {
//ROM -> ROM transfer
mode |= Sequential;
mode ^= Nonsequential;
}
}
if(latch.target() < 0x0200'0000) {
cpu.idle(); //cannot access BIOS
} else {
n32 addr = latch.target();
if(mode & Word) addr &= ~3;
if(mode & Half) addr &= ~1;
if(addr & 0x0800'0000) cpu.context.dmaRomAccess = true;
cpu.set(mode, addr, latch.data >> (addr & 2) * 8);
}
+2 -2
View File
@@ -11,11 +11,11 @@ auto CPU::prefetchSync(n32 address) -> void {
auto CPU::prefetchStep(u32 clocks) -> void {
step(clocks);
if(!wait.prefetch || context.dmaRomAccess || prefetch.stopped) return;
if(!wait.prefetch || prefetch.stopped) return;
while(!prefetch.full() && clocks--) {
if(--prefetch.wait) continue;
prefetch.slot[prefetch.load >> 1 & 7] = cartridge.read(Half, prefetch.load);
prefetch.slot[prefetch.load >> 1 & 7] = cartridge.readRom<false>(Half | Nonsequential, prefetch.load); //todo: make access sequentiality consistent with timing used
prefetch.load += 2;
prefetch.wait = waitCartridge(Half | Sequential, prefetch.load);
}
+4 -1
View File
@@ -113,9 +113,12 @@ auto CPU::serialize(serializer& s) -> void {
s(context.halted);
s(context.stopped);
s(context.booted);
s(context.dmaRan);
s(context.romAccess);
s(context.dmaRomAccess);
s(context.dmaRan);
s(context.dmaActive);
s(context.dmaActiveChannel);
s(context.timerLatched);
s(context.busLocked);
s(context.hcounter);
}
+5 -5
View File
@@ -12,15 +12,15 @@ namespace ares::GameBoyAdvance {
auto option(string name, string value) -> bool;
enum : u32 { //mode flags for bus read, write:
Nonsequential = 1, //N cycle
Sequential = 2, //S cycle
Load = 1, //load operation
Store = 2, //store operation
Prefetch = 4, //instruction fetch (eligible for prefetch)
Byte = 8, //8-bit access
Half = 16, //16-bit access
Word = 32, //32-bit access
Load = 64, //load operation
Store = 128, //store operation
Signed = 256, //sign extended
Signed = 64, //sign extended
Nonsequential = 128, //N cycle
Sequential = 256, //S cycle
};
struct Model {
+66 -59
View File
@@ -12,6 +12,7 @@ auto PPU::Background::setEnable(n1 status) -> void {
auto PPU::Background::scanline(u32 y) -> void {
mosaicOffset = 0;
for(auto& pixel : output) pixel = {};
latch.active = false;
}
auto PPU::Background::outputPixel(u32 x, u32 y) -> void {
@@ -23,47 +24,64 @@ auto PPU::Background::outputPixel(u32 x, u32 y) -> void {
mosaicOffset--;
}
auto PPU::Background::run(u32 x, u32 y) -> void {
auto PPU::Background::run(s32 x, u32 y) -> void {
switch(id) {
case PPU::BG0:
if(io.mode <= 1) { linear(x, y); break; }
if(io.mode <= 1) { linearFetchTileMap(x, y); linearRender(x, y); break; }
break;
case PPU::BG1:
if(io.mode <= 1) { linear(x, y); break; }
if(io.mode <= 1) { linearFetchTileMap(x, y); linearRender(x, y); break; }
break;
case PPU::BG2:
if(io.mode == 0) { linear(x, y); break; }
if(io.mode == 0) { linearFetchTileMap(x, y); linearRender(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 == 0) { linearFetchTileMap(x, y); linearRender(x, y); break; }
if(io.mode == 2) { affineFetchTileMap(x, y); affineFetchTileData(x, y); break; }
break;
}
}
auto PPU::Background::linearFetchTileMap() -> void {
n6 tx = fx >> 3;
n6 ty = fy >> 3;
auto PPU::Background::linearFetchTileMap(s32 x, u32 y) -> void {
if(ppu.blank() || !io.enable[0]) return;
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);
if(x == -7) {
if(!io.mosaic || (y % (1 + io.mosaicHeight)) == 0) {
vmosaic = y;
}
}
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);
fx = x + io.hoffset;
fy = vmosaic + io.voffset;
n3 px = fx;
if(px == 0) {
//fetch tilemap
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);
latch.active = true;
latch.px = 0;
}
}
auto PPU::Background::linearFetchTileData() -> void {
n3 px = fx;
n3 px = latch.px;
n3 py = fy;
if(latch.hflip) px = ~px;
if(latch.vflip) py = ~py;
@@ -75,29 +93,13 @@ auto PPU::Background::linearFetchTileData() -> void {
latch.data = ppu.readVRAM_BG(Half, offset);
}
auto PPU::Background::linear(u32 x, u32 y) -> void {
if(x > 239) return;
auto PPU::Background::linearRender(s32 x, u32 y) -> void {
if(ppu.blank() || !io.enable[0]) return;
if(!latch.active) return;
if(x == 0) {
if(!io.mosaic || (y % (1 + io.mosaicHeight)) == 0) {
vmosaic = y;
}
fx = io.hoffset;
fy = vmosaic + io.voffset;
}
n3 px = latch.px;
n3 px = fx;
n3 py = fy;
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(((px << io.colorMode) & 3) == 0) linearFetchTileData();
if(latch.hflip) px = ~px;
@@ -111,17 +113,18 @@ auto PPU::Background::linear(u32 x, u32 y) -> void {
}
if(color) {
if(io.colorMode == 0) color |= latch.palette << 4;
output[x].enable = true;
output[x].priority = io.priority;
output[x].color = color;
if(x >= 0 && x < 240) {
if(io.colorMode == 0) color |= latch.palette << 4;
output[x].enable = true;
output[x].priority = io.priority;
output[x].color = color;
}
}
fx++;
if(++latch.px == 0) latch.active = false;
}
auto PPU::Background::affineFetchTileMap(u32 x, u32 y) -> void {
if(x > 239) return;
if(ppu.blank() || !io.enable[0]) return;
if(x == 0) {
@@ -146,16 +149,19 @@ auto PPU::Background::affineFetchTileMap(u32 x, u32 y) -> void {
}
auto PPU::Background::affineFetchTileData(u32 x, u32 y) -> void {
if(x > 239) return;
if(ppu.blank() || !io.enable[0]) return;
n3 px = affine.cx;
n3 py = affine.cy;
n8 color = ppu.readVRAM_BG(Byte, (io.characterBase << 14) + (affine.character << 6) + (py << 3) + px);
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;
if(color) {
if(x < 240) {
output[x].enable = true;
output[x].priority = io.priority;
output[x].color = color;
}
}
}
@@ -169,7 +175,6 @@ auto PPU::Background::affineFetchTileData(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) {
@@ -191,15 +196,17 @@ auto PPU::Background::bitmap(u32 x, u32 y) -> void {
u32 px = fx >> 8;
u32 py = fy >> 8;
if(px < width && py < height) {
u32 offset = py * width + px;
n15 color = ppu.readVRAM_BG(mode, baseAddress + (offset << depth));
u32 offset = py * width + px;
n15 color = ppu.readVRAM_BG(mode, baseAddress + (offset << depth));
if(px < width && py < height) {
if(depth || color) { //8bpp color 0 is transparent; 15bpp color is always opaque
if(depth) output[x].directColor = true;
output[x].enable = true;
output[x].priority = io.priority;
output[x].color = color;
if(x < 240) {
if(depth) output[x].directColor = true;
output[x].enable = true;
output[x].priority = io.priority;
output[x].color = color;
}
}
}
+10 -1
View File
@@ -4,7 +4,16 @@ auto PPU::releaseBus() -> void {
}
auto PPU::pramContention() -> bool {
return pramAccessed;
if(accurate) return pramAccessed;
//approximate timings in scanline renderer, assuming no layer blending
n3 mode = PPU::Background::IO::mode;
if(display.io.vcounter < 160 && !ppu.blank() && mode != 3 && mode != 5) {
n32 hcounter = cpu.context.hcounter;
if(hcounter < 46 || hcounter > 1005) return false;
return (hcounter & 3) == 2;
}
return false;
}
auto PPU::vramContention(n32 address) -> bool {
+54 -20
View File
@@ -75,16 +75,25 @@ auto PPU::step(u32 clocks) -> void {
Thread::synchronize(cpu, display);
}
template<u32 Cycle>
auto PPU::cycleLinear(u32 x, u32 y) -> void {
template<s32 Cycle>
auto PPU::cycleLinearMap(s32 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);
if constexpr(Cycle == 0) if(mode <= 1) bg0.linearFetchTileMap(x, y);
if constexpr(Cycle == 1) if(mode <= 1) bg1.linearFetchTileMap(x, y);
if constexpr(Cycle == 2) if(mode == 0) bg2.linearFetchTileMap(x, y);
if constexpr(Cycle == 3) if(mode == 0) bg3.linearFetchTileMap(x, y);
}
template<u32 Cycle>
template<s32 Cycle>
auto PPU::cycleLinearRender(s32 x, u32 y) -> void {
n3 mode = PPU::Background::IO::mode;
if constexpr(Cycle == 0) if(mode <= 1) bg0.linearRender(x, y);
if constexpr(Cycle == 1) if(mode <= 1) bg1.linearRender(x, y);
if constexpr(Cycle == 2) if(mode == 0) bg2.linearRender(x, y);
if constexpr(Cycle == 3) if(mode == 0) bg3.linearRender(x, y);
}
template<s32 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);
@@ -111,11 +120,12 @@ auto PPU::cycleUpperLayer(u32 x, u32 y) -> void {
dac.upperLayer(x, y);
}
template<u32 Cycle>
template<s32 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 >= 7 && Cycle <= 1037 ) cycleLinearRender<(Cycle - 7) & 3>((Cycle - 35) >> 2, y);
if constexpr(Cycle >= 3 && Cycle <= 1005 ) cycleLinearMap<(Cycle - 3) & 3>((Cycle - 31) >> 2, y);
if constexpr(Cycle >= 31 && Cycle <= 1005 ) cycleAffine<(Cycle - 31) & 3>((Cycle - 31) >> 2, y);
if constexpr(Cycle >= 31 && Cycle <= 1005 && (Cycle - 31) % 4 == 3) cycleBitmap((Cycle - 31) >> 2, 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);
@@ -133,7 +143,7 @@ auto PPU::main() -> void {
bg3.io.ly = bg3.io.y;
}
step(31);
step(3);
u32 y = display.io.vcounter;
memory::move(io.forceBlank, io.forceBlank + 1, sizeof(io.forceBlank) - 1);
@@ -159,7 +169,13 @@ auto PPU::main() -> void {
#define cycles32(index) cycles16(index); cycles16(index + 16)
#define cycles64(index) cycles32(index); cycles32(index + 32)
//cycle 31 - start rendering backgrounds
//cycle 3 - earliest possible background render cycle
cycles04( 3);
cycles08( 7);
cycles08( 15);
cycles08( 23);
//cycle 31 - start rendering backgrounds unconditionally
cycles01( 31);
cycles02( 32);
cycles04( 34);
@@ -182,6 +198,12 @@ auto PPU::main() -> void {
cycles64(878);
cycles64(942);
//cycle 1006 - finish rendering final background tiles
cycles08(1006);
cycles08(1014);
cycles08(1022);
cycles08(1030);
#undef cycles02
#undef cycles04
#undef cycles08
@@ -189,22 +211,25 @@ auto PPU::main() -> void {
#undef cycles32
#undef cycles64
} else {
step(renderingCycle);
for(s32 x : range(247)) {
bg0.run(x - 7, y);
bg1.run(x - 7, y);
bg2.run(x - 7, y);
bg3.run(x - 7, y);
}
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);
step(1035 - renderingCycle);
}
} else {
step(975);
step(1035);
}
step(226);
step(194);
}
auto PPU::frame() -> void {
@@ -237,6 +262,15 @@ auto PPU::power() -> void {
window2.power(IN2);
window3.power(OUT);
dac.power();
renderingCycle = 43; //by default, render at first cycle of pixel output
string gameID;
for(u32 index : range(4)) {
char byte = cartridge.readRom<true>(Byte, 0xac + index);
gameID.append(byte);
}
if(gameID == "AWRE") renderingCycle = 512; //Advance Wars (USA)
if(gameID == "AWRP") renderingCycle = 512; //Advance Wars (Europe) (En,Fr,De,Es)
}
}
+11 -6
View File
@@ -37,11 +37,12 @@ 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;
template<s32> auto cycleLinearMap(s32 x, u32 y) -> void;
template<s32> auto cycleLinearRender(s32 x, u32 y) -> void;
template<s32> 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;
template<s32> auto cycle(u32 y) -> void;
auto main() -> void;
auto frame() -> void;
@@ -106,10 +107,10 @@ private:
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 run(s32 x, u32 y) -> void;
auto linearFetchTileMap(s32 x, u32 y) -> void;
auto linearFetchTileData() -> void;
auto linear(u32 x, u32 y) -> void;
auto linearRender(s32 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;
@@ -160,6 +161,9 @@ private:
n4 palette;
n16 data;
n1 active;
n3 px;
} latch;
struct Affine {
@@ -303,6 +307,7 @@ private:
bool pramAccessed;
bool vramAccessedBG;
n32 renderingCycle;
};
extern PPU ppu;
+1 -1
View File
@@ -1,4 +1,4 @@
static const string SerializerVersion = "v141.6";
static const string SerializerVersion = "v143.1";
auto System::serialize(bool synchronize) -> serializer {
if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);
-29
View File
@@ -1,29 +0,0 @@
ares.components += m68000
ares.components += z80
ares.components += sh2
ares.components += ssp1601
ares.components += sn76489
ares.components += ym2612
ares.components += m24c
ares.objects += ares-md-bus
ares.objects += ares-md-cpu
ares.objects += ares-md-apu
ares.objects += ares-md-vdp
ares.objects += ares-md-opn2
ares.objects += ares-md-m32x
ares.objects += ares-md-mcd
ares.objects += ares-md-system
ares.objects += ares-md-cartridge
ares.objects += ares-md-controller
$(object.path)/ares-md-bus.o: $(ares.path)/md/bus/bus.cpp
$(object.path)/ares-md-cpu.o: $(ares.path)/md/cpu/cpu.cpp
$(object.path)/ares-md-apu.o: $(ares.path)/md/apu/apu.cpp
$(object.path)/ares-md-vdp.o: $(ares.path)/md/vdp/vdp.cpp
$(object.path)/ares-md-opn2.o: $(ares.path)/md/opn2/opn2.cpp
$(object.path)/ares-md-m32x.o: $(ares.path)/md/m32x/m32x.cpp
$(object.path)/ares-md-mcd.o: $(ares.path)/md/mcd/mcd.cpp
$(object.path)/ares-md-system.o: $(ares.path)/md/system/system.cpp
$(object.path)/ares-md-cartridge.o: $(ares.path)/md/cartridge/cartridge.cpp
$(object.path)/ares-md-controller.o: $(ares.path)/md/controller/controller.cpp
+5 -3
View File
@@ -65,7 +65,7 @@ auto CPU::step(u32 clocks) -> void {
refresh.ram += clocks;
refresh.external += clocks;
Thread::step(clocks);
cyclesUntilSync -= clocks;
cyclesUntilFullSync -= clocks;
}
inline auto CPU::idle(u32 clocks) -> void {
@@ -74,8 +74,10 @@ inline auto CPU::idle(u32 clocks) -> void {
auto CPU::wait(u32 clocks) -> void {
step(clocks);
if (cyclesUntilSync <= 0) {
cyclesUntilSync = minCyclesBetweenSyncs;
Thread::synchronize(apu, cartridge, opn2, vdp);
if (cyclesUntilFullSync <= 0) {
cyclesUntilFullSync = minCyclesBetweenSyncs;
Thread::synchronize();
}
}
+1 -1
View File
@@ -85,7 +85,7 @@ struct CPU : M68000, Thread {
int stolenMcycles = 0;
} state;
int cyclesUntilSync = 0;
int cyclesUntilFullSync = 0;
int minCyclesBetweenSyncs = 0;
};
+6 -2
View File
@@ -8,6 +8,8 @@ auto M32X::readExternal(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
}
if(address >= 0x840000 && address <= 0x87ffff) {
if(vdp.framebufferAccess) return data;
if(vdp.framebufferEngaged()) { debug(unusual, "[32X FB] 68k read while FEN==1"); return data; } // wait instead?
return vdp.bbram[address >> 1 & 0xffff];
}
@@ -40,7 +42,8 @@ auto M32X::writeExternal(n1 upper, n1 lower, n24 address, n16 data) -> void {
}
if(address >= 0x840000 && address <= 0x85ffff) {
if (vdp.framebufferAccess) return;
if(vdp.framebufferAccess) return;
if(vdp.framebufferEngaged()) { debug(unusual, "[32X FB] 68k write while FEN==1"); return; } // wait instead?
if(!data && (!upper || !lower)) return; //8-bit 0x00 writes do not go through
shm.debugger.tracer.instruction->invalidate(0x0400'0000 | address & 0x1fffe);
shs.debugger.tracer.instruction->invalidate(0x0400'0000 | address & 0x1fffe);
@@ -50,7 +53,8 @@ auto M32X::writeExternal(n1 upper, n1 lower, n24 address, n16 data) -> void {
}
if(address >= 0x860000 && address <= 0x87ffff) {
if (vdp.framebufferAccess) return;
if(vdp.framebufferAccess) return;
if(vdp.framebufferEngaged()) { debug(unusual, "[32X FB] 68k overwrite while FEN==1"); return; } // wait instead?
shm.debugger.tracer.instruction->invalidate(0x0402'0000 | address & 0x1fffe);
shs.debugger.tracer.instruction->invalidate(0x0402'0000 | address & 0x1fffe);
if(upper && data.byte(1)) vdp.bbram[address >> 1 & 0xffff].byte(1) = data.byte(1);
+30 -31
View File
@@ -1,6 +1,6 @@
auto M32X::readInternal(n1 upper, n1 lower, n32 address, n16 data) -> n16 {
if(address >= 0x0000'0000 && address <= 0x0000'3fff) {
if(shm.active()) shm.step(1); if(shs.active()) shs.step(1);
if(shm.active()) shm.internalStep(1); if(shs.active()) shs.internalStep(1);
if(shm.active()) return shm.bootROM[address >> 1];
if(shs.active()) return shs.bootROM[address >> 1];
}
@@ -9,20 +9,22 @@ auto M32X::readInternal(n1 upper, n1 lower, n32 address, n16 data) -> n16 {
return readInternalIO(upper, lower, address, data);
}
if(address >= 0x0200'0000 && address <= 0x023f'ffff) {
if(address >= 0x0200'0000 && address <= 0x03ff'ffff) {
while(dreq.vram) {
// SH2 ROM accesses stall while RV is set
if(shm.active()) shm.step(1);
if(shs.active()) shs.step(1);
//SH2 ROM accesses stall while RV is set
if(shm.active()) { shm.internalStep(1); shm.syncM68k(true); }
if(shs.active()) { shs.internalStep(1); shs.syncM68k(true); }
}
// TODO: SH2 ROM accesses need to stall while the m68k is on the bus
if(shm.active()) shm.step(6); if(shs.active()) shs.step(6);
//TODO: SH2 ROM accesses need to stall while the m68k is on the bus
if(shm.active()) shm.internalStep(6); if(shs.active()) shs.internalStep(6);
return cartridge.child->read(upper, lower, address, data);
}
if(address >= 0x0400'0000 && address <= 0x0405'ffff) {
if(shm.active()) shm.step(5); if(shs.active()) shs.step(5);
if(address >= 0x0400'0000 && address <= 0x05ff'ffff) {
if(!vdp.framebufferAccess) return data;
if(vdp.framebufferEngaged()) { debug(unusual, "[32X FB] SH2 read while FEN==1"); return data; } // wait instead?
if(shm.active()) shm.internalStep(5); if(shs.active()) shs.internalStep(5);
return vdp.bbram[address >> 1 & 0xffff];
}
@@ -38,30 +40,27 @@ auto M32X::writeInternal(n1 upper, n1 lower, n32 address, n16 data) -> void {
return writeInternalIO(upper, lower, address, data);
}
if(address >= 0x0400'0000 && address <= 0x0401'ffff) {
if (!vdp.framebufferAccess) return;
if(!data && (!upper || !lower)) return; //8-bit 0x00 writes do not go through
if(shm.active()) shm.step(4); if(shs.active()) shs.step(4);
if(upper) vdp.bbram[address >> 1 & 0xffff].byte(1) = data.byte(1);
if(lower) vdp.bbram[address >> 1 & 0xffff].byte(0) = data.byte(0);
return;
}
if(address >= 0x0400'0000 && address <= 0x05ff'ffff) {
address &= 0x0403'ffff;
if(address >= 0x0402'0000 && address <= 0x0403'ffff) {
if (!vdp.framebufferAccess) return;
if(shm.active()) shm.step(4); if(shs.active()) shs.step(4);
if(upper && data.byte(1)) vdp.bbram[address >> 1 & 0xffff].byte(1) = data.byte(1);
if(lower && data.byte(0)) vdp.bbram[address >> 1 & 0xffff].byte(0) = data.byte(0);
return;
}
if(address >= 0x0400'0000 && address <= 0x0401'ffff) {
if(!vdp.framebufferAccess) return;
if(vdp.framebufferEngaged()) { debug(unusual, "[32X FB] SH2 write while FEN==1"); return; } // wait instead?
if(!data && (!upper || !lower)) return; //8-bit 0x00 writes do not go through
if(shm.active()) shm.internalStep(4); if(shs.active()) shs.internalStep(4);
if(upper) vdp.bbram[address >> 1 & 0xffff].byte(1) = data.byte(1);
if(lower) vdp.bbram[address >> 1 & 0xffff].byte(0) = data.byte(0);
return;
}
if(address >= 0x0404'0000 && address <= 0x0405'ffff) {
if (!vdp.framebufferAccess) return;
if(!data && (!upper || !lower)) return; //8-bit 0x00 writes do not go through
if(shm.active()) shm.step(4); if(shs.active()) shs.step(4);
if(upper) vdp.bbram[address >> 1 & 0xffff].byte(1) = data.byte(1);
if(lower) vdp.bbram[address >> 1 & 0xffff].byte(0) = data.byte(0);
return;
if(address >= 0x0402'0000 && address <= 0x0403'ffff) {
if(!vdp.framebufferAccess) return;
if(vdp.framebufferEngaged()) { debug(unusual, "[32X FB] SH2 overwrite while FEN==1"); return; } // wait instead?
if(shm.active()) shm.internalStep(4); if(shs.active()) shs.internalStep(4);
if(upper && data.byte(1)) vdp.bbram[address >> 1 & 0xffff].byte(1) = data.byte(1);
if(lower && data.byte(0)) vdp.bbram[address >> 1 & 0xffff].byte(0) = data.byte(0);
return;
}
}
if(address >= 0x0600'0000 && address <= 0x0603'ffff) {
+22 -3
View File
@@ -35,7 +35,9 @@ auto M32X::readExternalIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
data.bit(0) = dreq.vram;
data.bit(1) = dreq.dma;
data.bit(2) = dreq.active;
data.bit(3,6) = 0;
data.bit(7) = dreq.fifo.full();
data.bit(8,15) = 0;
}
//68K to SH2 DREQ source address
@@ -62,9 +64,18 @@ auto M32X::readExternalIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
data.byte(0) = dreq.length.byte(0);
}
if(address == 0xa15114 ||
address == 0xa15116 ||
address == 0xa15118 ||
address == 0xa1511c ||
address == 0xa1511e) {
data = 0;
}
//TV register
if(address == 0xa1511a) {
data.bit(0) = io.cartridgeMode;
data.bit(1,15) = 0;
}
//communication
@@ -139,15 +150,20 @@ auto M32X::readExternalIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
//frame buffer control
if(address == 0xa1518a) {
data.bit( 0) = vdp.framebufferActive;
data.bit( 1) = MegaDrive::vdp.refreshing(); //framebuffer access
data.bit( 1) = MegaDrive::vdp.refreshing()
|| vdp.framebufferEngaged(); // FEN: frame buffer engaged
data.bit( 2, 12) = 0;
data.bit(13) = vdp.vblank || vdp.hblank; //palette access
data.bit(13) = !vdp.paletteEngaged(); // PEN: can access palette
data.bit(14) = vdp.hblank;
data.bit(15) = vdp.vblank;
}
//palette
if(address >= 0xa15200 && address <= 0xa153ff) {
if(vdp.framebufferAccess) return data;
while(vdp.paletteEngaged()) {
if(cpu.active()) cpu.wait(1);
}
data = vdp.cram[address >> 1 & 0xff];
}
@@ -357,7 +373,10 @@ auto M32X::writeExternalIO(n1 upper, n1 lower, n24 address, n16 data) -> void {
//palette
if(address >= 0xa15200 && address <= 0xa153ff) {
if (vdp.framebufferAccess) return;
if(vdp.framebufferAccess) return;
while(vdp.paletteEngaged()) {
if(cpu.active()) cpu.wait(1);;
}
if(upper) vdp.cram[address >> 1 & 0xff].byte(1) = data.byte(1);
if(lower) vdp.cram[address >> 1 & 0xff].byte(0) = data.byte(0);
}
+23 -11
View File
@@ -1,5 +1,5 @@
auto M32X::readInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> n16 {
if(shm.active()) shm.step(1); if(shs.active()) shs.step(1);
if(shm.active()) shm.internalStep(1); if(shs.active()) shs.internalStep(1);
//interrupt mask
if(address == 0x4000) {
@@ -63,6 +63,8 @@ auto M32X::readInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> n16 {
//FIFO
if(address == 0x4012) {
if(shm.active()) { shm.syncM68k(); }
if(shs.active()) { shs.syncM68k(); }
data = dreq.fifo.read(data);
shm.dmac.dreq[0] = !dreq.fifo.empty();
shs.dmac.dreq[0] = !dreq.fifo.empty();
@@ -71,8 +73,8 @@ auto M32X::readInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> n16 {
//communication
if(address >= 0x4020 && address <= 0x402f) {
data = communication[address >> 1 & 7];
if(shm.active()) { shm.syncOtherSh2(); shm.syncM68k(); }
if(shs.active()) { shs.syncOtherSh2(); shs.syncM68k(); }
if(shm.active()) { shm.syncAll(); }
if(shs.active()) { shs.syncAll(); }
}
//PWM control
@@ -144,15 +146,21 @@ auto M32X::readInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> n16 {
if(shm.active()) shm.syncM68k();
if(shs.active()) shs.syncM68k();
data.bit( 0) = vdp.framebufferActive;
data.bit( 1) = MegaDrive::vdp.refreshing(); //framebuffer access
data.bit(13) = vdp.vblank || vdp.hblank; //palette access
data.bit( 1) = MegaDrive::vdp.refreshing()
|| vdp.framebufferEngaged(); // FEN: frame buffer engaged
data.bit(13) = !vdp.paletteEngaged(); // PEN: can access palette
data.bit(14) = vdp.hblank;
data.bit(15) = vdp.vblank;
}
//palette
if(address >= 0x4200 && address <= 0x43ff) {
if(shm.active()) shm.step(4); if(shs.active()) shs.step(4);
if(!vdp.framebufferAccess) return data;
while(vdp.paletteEngaged()) {
if(shm.active()) { shm.internalStep(1); shm.syncAll(true); }
if(shs.active()) { shs.internalStep(1); shs.syncAll(true); }
}
if(shm.active()) shm.internalStep(4); if(shs.active()) shs.internalStep(4);
data = vdp.cram[address >> 1 & 0xff];
}
@@ -160,7 +168,7 @@ auto M32X::readInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> n16 {
}
auto M32X::writeInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> void {
if(shm.active()) shm.step(1); if(shs.active()) shs.step(1);
if(shm.active()) shm.internalStep(1); if(shs.active()) shs.internalStep(1);
//interrupt mask
if(address == 0x4000) {
@@ -228,8 +236,8 @@ auto M32X::writeInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> void {
//communication
if(address >= 0x4020 && address <= 0x402f) {
if(shm.active()) { shm.syncOtherSh2(); shm.syncM68k(); }
if(shs.active()) { shs.syncOtherSh2(); shs.syncM68k(); }
if(shm.active()) { shm.syncAll(); }
if(shs.active()) { shs.syncAll(); }
if(upper) communication[address >> 1 & 7].byte(1) = data.byte(1);
if(lower) communication[address >> 1 & 7].byte(0) = data.byte(0);
}
@@ -337,8 +345,12 @@ auto M32X::writeInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> void {
//palette
if(address >= 0x4200 && address <= 0x43ff) {
if (!vdp.framebufferAccess) return;
if(shm.active()) shm.step(4); if(shs.active()) shs.step(4);
if(!vdp.framebufferAccess) return;
while(vdp.paletteEngaged()) {
if(shm.active()) { shm.internalStep(1); shm.syncAll(true); }
if(shs.active()) { shs.internalStep(1); shs.syncAll(true); }
}
if(shm.active()) shm.internalStep(4); if(shs.active()) shs.internalStep(4);
if(upper) vdp.cram[address >> 1 & 0xff].byte(1) = data.byte(1);
if(lower) vdp.cram[address >> 1 & 0xff].byte(0) = data.byte(0);
}
+9 -5
View File
@@ -51,15 +51,12 @@ auto M32X::power(bool reset) -> void {
shs.power(reset);
vdp.power(reset);
pwm.power(reset);
n32 vec4 = io.vectorLevel4;
io = {};
if(reset) io.vectorLevel4 = vec4;
dreq = {};
for(auto& word : communication) word = 0;
io.vectorLevel4.byte(3) = vectors[0x70 >> 1].byte(1);
io.vectorLevel4.byte(2) = vectors[0x70 >> 1].byte(0);
io.vectorLevel4.byte(1) = vectors[0x72 >> 1].byte(1);
io.vectorLevel4.byte(0) = vectors[0x72 >> 1].byte(0);
//connect interfaces
shm.sci.link = shs;
shs.sci.link = shm;
@@ -76,6 +73,13 @@ auto M32X::vblank(bool line) -> void {
}
auto M32X::hblank(bool line) -> void {
if(vdp.hblank > line) {
// TODO: VDP regs should be latched 192 MClks (~82 cycles) before end of hblank (according to official docs)
vdp.latch.mode = vdp.mode;
vdp.latch.lines = vdp.lines;
vdp.latch.priority = vdp.priority;
vdp.latch.dotshift = vdp.dotshift;
}
vdp.hblank = line;
shm.irq.hint.active = 0;
shs.irq.hint.active = 0;
+15 -5
View File
@@ -40,10 +40,12 @@ struct M32X {
auto main() -> void;
auto instructionPrologue(u16 instruction) -> void override;
auto step(u32 clocks) -> void override;
auto internalStep(u32 clocks) -> void;
auto power(bool reset) -> void;
auto restart() -> void;
auto syncOtherSh2() -> void;
auto syncM68k() -> void;
auto syncAll(bool force = false) -> void;
auto syncOtherSh2(bool force = false) -> void;
auto syncM68k(bool force = false) -> void;
auto busReadByte(u32 address) -> u32 override;
auto busReadWord(u32 address) -> u32 override;
@@ -69,10 +71,8 @@ struct M32X {
s32 cyclesUntilSh2Sync = 0;
s32 cyclesUntilM68kSync = 0;
s32 cyclesUntilFullSync = 0;
s32 minCyclesBetweenSh2Syncs = 0;
s32 minCyclesBetweenM68kSyncs = 0;
s32 minCyclesBetweenFullSyncs = 0;
};
struct VDP {
@@ -104,6 +104,8 @@ struct M32X {
auto plot(u32* output, u16 color) -> void;
auto fill() -> void;
auto selectFramebuffer(n1 active) -> void;
auto framebufferEngaged() -> bool;
auto paletteEngaged() -> bool;
//serialization.cpp
auto serialize(serializer&) -> void;
@@ -118,8 +120,16 @@ struct M32X {
n1 framebufferAccess;
n1 framebufferActive;
n1 framebufferSelect;
int framebufferWait;
n1 hblank;
n1 vblank;
struct Latch {
n2 mode;
n1 lines;
n1 priority;
n1 dotshift;
} latch;
};
struct PWM : Thread {
@@ -208,7 +218,7 @@ struct M32X {
//$a15000
n1 adapterEnable;
n1 adapterReset;
n1 adapterReset = 1;
n1 resetEnable = 1;
//$a15004
+6
View File
@@ -46,12 +46,17 @@ auto M32X::VDP::serialize(serializer& s) -> void {
s(lines);
s(priority);
s(dotshift);
s(latch.mode);
s(latch.lines);
s(latch.priority);
s(latch.dotshift);
s(autofillLength);
s(autofillAddress);
s(autofillData);
s(framebufferAccess);
s(framebufferActive);
s(framebufferSelect);
s(framebufferWait);
s(hblank);
s(vblank);
selectFramebuffer(framebufferSelect);
@@ -76,4 +81,5 @@ auto M32X::PWM::serialize(serializer& s) -> void {
s(lfifoLatch);
s(rfifoLatch);
s(mfifoLatch);
updateFrequency();
}
+56 -21
View File
@@ -49,28 +49,47 @@ auto M32X::SH7604::instructionPrologue(u16 instruction) -> void {
debugger.instruction(instruction);
}
auto M32X::SH7604::internalStep(u32 clocks) -> void {
if(SH2::Accuracy::Recompiler && m32x.shm.recompiler.enabled) {
regs.CCR += clocks;
return;
}
step(clocks);
}
auto M32X::SH7604::step(u32 clocks) -> void {
SH2::frt.run(clocks);
SH2::wdt.run(clocks);
Thread::step(clocks);
cyclesUntilSh2Sync -= clocks;
cyclesUntilM68kSync -= clocks;
cyclesUntilFullSync -= clocks;
if(cyclesUntilFullSync <= 0) {
cyclesUntilFullSync = minCyclesBetweenFullSyncs;
if(m32x.shm.active()) Thread::synchronize(m32x.shs, cpu);
if(m32x.shs.active()) Thread::synchronize(m32x.shm, cpu);
m32x.vdp.framebufferWait -= min(clocks, m32x.vdp.framebufferWait);
if(cyclesUntilSh2Sync <= 0) {
cyclesUntilSh2Sync = minCyclesBetweenSh2Syncs;
if (m32x.shm.active()) Thread::synchronize(m32x.shs);
if (m32x.shs.active()) Thread::synchronize(m32x.shm);
}
if(cyclesUntilM68kSync <= 0) {
cyclesUntilM68kSync = minCyclesBetweenM68kSyncs;
Thread::synchronize(cpu);
}
}
auto M32X::SH7604::power(bool reset) -> void {
Thread::create((system.frequency() / 7.0) * 3.0, {&M32X::SH7604::main, this});
SH2::recompilerStepCycles = 20; // Minimum cycles for recompiler to run for each batch of instructions
minCyclesBetweenFullSyncs = 50; // Minimum cycles between full sync with the M68K/MD side
minCyclesBetweenSh2Syncs = 5; // Minimum Cycles between sync with the other SH2 (syncOtherSh2)
minCyclesBetweenM68kSyncs = 10; // Minimum Cycles between sync with the M68K (syncM68k)
//When tweaking these values, make sure to test the following problematic games:
// Brutal - Check for hang in attract mode
// Chaotix - Check for hang on intro screen or level loading transitions
SH2::recompilerStepCycles = 200; //Recompiler will force an exit after at least N cycles have passed
minCyclesBetweenSh2Syncs = 10; //Do not sync SH2s more than once every N cycles
minCyclesBetweenM68kSyncs = 50; //Do not sync M68K more than once every N cycles
SH2::power(reset);
irq = {};
irq.vres.enable = 1;
@@ -83,19 +102,35 @@ auto M32X::SH7604::restart() -> void {
Thread::restart({&M32X::SH7604::main, this});
}
auto M32X::SH7604::syncOtherSh2() -> void {
// avoid synchronizing if we recently have
if(cyclesUntilSh2Sync > 0) return;
if(m32x.shm.active()) Thread::synchronize(m32x.shs);
if(m32x.shs.active()) Thread::synchronize(m32x.shm);
cyclesUntilSh2Sync = minCyclesBetweenSh2Syncs;
auto M32X::SH7604::syncAll(bool force) -> void {
SH2::cyclesUntilRecompilerExit = 0;
if(SH2::Accuracy::Recompiler && m32x.shm.recompiler.enabled && force) {
cyclesUntilSh2Sync = 0;
cyclesUntilM68kSync = 0;
step(regs.CCR);
regs.CCR = 0;
}
}
auto M32X::SH7604::syncM68k() -> void {
// avoid synchronizing if we recently have
if(cyclesUntilM68kSync > 0) return;
Thread::synchronize(cpu);
cyclesUntilM68kSync = minCyclesBetweenM68kSyncs;
auto M32X::SH7604::syncOtherSh2(bool force) -> void {
SH2::cyclesUntilRecompilerExit = 0;
if(SH2::Accuracy::Recompiler && m32x.shm.recompiler.enabled && force) {
cyclesUntilSh2Sync = 0;
step(regs.CCR);
regs.CCR = 0;
}
}
auto M32X::SH7604::syncM68k(bool force) -> void {
SH2::cyclesUntilRecompilerExit = 0;
if(SH2::Accuracy::Recompiler && m32x.shm.recompiler.enabled && force) {
cyclesUntilM68kSync = 0;
step(regs.CCR);
regs.CCR = 0;
}
}
auto M32X::SH7604::busReadByte(u32 address) -> u32 {
+30 -23
View File
@@ -17,28 +17,30 @@ auto M32X::VDP::power(bool reset) -> void {
lines = 0;
priority = 0;
dotshift = 0;
latch = {};
autofillLength = 0;
autofillAddress = 0;
autofillData = 0;
framebufferAccess = 0;
framebufferActive = 0;
framebufferSelect = 0;
framebufferWait = 0;
hblank = 0;
vblank = 1;
selectFramebuffer(framebufferSelect);
}
auto M32X::VDP::scanline(u32 pixels[1280], u32 y) -> void {
if(!Mega32X() || !pixels || y >= (lines ? 240 : 224)) return;
if(mode == 1) return scanlineMode1(pixels, y);
if(mode == 2) return scanlineMode2(pixels, y);
if(mode == 3) return scanlineMode3(pixels, y);
if(!Mega32X() || !pixels || y >= (latch.lines ? 240 : 224)) return;
if(latch.mode == 1) return scanlineMode1(pixels, y);
if(latch.mode == 2) return scanlineMode2(pixels, y);
if(latch.mode == 3) return scanlineMode3(pixels, y);
}
auto M32X::VDP::scanlineMode1(u32 pixels[1280], u32 y) -> void {
u16 address = fbram[y];
for(u32 x : range(320)) {
u8 color = fbram[address + (x + dotshift >> 1) & 0xffff].byte(!(x + dotshift & 1));
u8 color = fbram[address + (x + latch.dotshift >> 1) & 0xffff].byte(!(x + latch.dotshift & 1));
plot(&pixels[x * 4], cram[color]);
}
}
@@ -66,30 +68,24 @@ auto M32X::VDP::scanlineMode3(u32 pixels[1280], u32 y) -> void {
}
auto M32X::VDP::plot(u32* output, u16 color) -> void {
n1 backdrop = output[0] >> 11;
n1 throughbit = color >> 15;
b1 opaque = color & 0x7fff;
if(priority == 0) {
//Mega Drive has priority
if(throughbit || backdrop) {
output[0] = color | 1 << 15;
output[1] = color | 1 << 15;
output[2] = color | 1 << 15;
output[3] = color | 1 << 15;
}
} else {
//Mega 32X has priority
if(!throughbit || backdrop) {
output[0] = color | 1 << 15;
output[1] = color | 1 << 15;
output[2] = color | 1 << 15;
output[3] = color | 1 << 15;
for(int i = 0; i < 4; i++) {
n1 backdrop = output[i] >> 11;
if(latch.priority == 0) {
//Mega Drive has priority
if(throughbit || backdrop) output[i] = color | 1 << 15;
} else {
//Mega 32X has priority
if(!throughbit || backdrop) output[i] = color | 1 << 15;
}
}
}
auto M32X::VDP::fill() -> void {
if(framebufferWait > 0) { debug(unusual, "[32X FILL] triggered before last fill finished"); return; }
framebufferWait = 7+3*(autofillLength+1); // according to official docs
for(u32 repeat : range(1 + autofillLength)) {
bbram[autofillAddress] = autofillData;
autofillAddress.byte(0)++;
@@ -98,9 +94,20 @@ auto M32X::VDP::fill() -> void {
auto M32X::VDP::selectFramebuffer(n1 select) -> void {
framebufferSelect = select;
if(!vblank && mode) return;
if(!vblank && latch.mode) return;
framebufferActive = select;
fbram = {dram.data() + 0x10000 * (select == 0), 0x10000};
bbram = {dram.data() + 0x10000 * (select == 1), 0x10000};
}
// back buffer access
auto M32X::VDP::framebufferEngaged() -> bool {
// TODO: 40 cycle wait at start of hblank (according to official docs)
return (latch.mode != 0 && framebufferSelect != framebufferActive) || framebufferWait > 0;
}
auto M32X::VDP::paletteEngaged() -> bool {
// TODO: not available for 24 MClks (~10 cycles) at start of hblank (according to official docs)
return !vblank && !hblank && latch.mode.bit(0);
}
+2 -2
View File
@@ -85,8 +85,8 @@ auto MCD::writeExternalIO(n1 upper, n1 lower, n24 address, n16 data) -> void {
} else {
if(io.run && !data.bit(0)) resetCpu();
io.run = data.bit(0);
io.request = data.bit(1);
io.halt = !io.run || io.request;
io.request = io.run ? data.bit(1) : 1; // asserting reset forces busreq (confirmed by eke)
io.halt = io.request;
}
}
if(upper) {
+1 -1
View File
@@ -1,4 +1,4 @@
static const string SerializerVersion = "v141.1";
static const string SerializerVersion = "v144";
auto System::serialize(bool synchronize) -> serializer {
if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);
+1 -1
View File
@@ -67,7 +67,7 @@ auto VDP::main() -> void {
} else if(hcounter() == 0x80) {
if(vcounter() < screenHeight() && !runAhead()) {
render();
m32x.vdp.scanline(pixels(), state.vcounter);
if(Mega32X()) m32x.vdp.scanline(pixels() + 18, vcounter()); //approx 3 and 1/4 pixel offset
}
step(768);
+2 -6
View File
@@ -171,12 +171,6 @@ auto VDP::readControlPort() -> n16 {
auto VDP::writeControlPort(n16 data) -> void {
//command write (lo)
// Note: A pending interrupt will be delayed when a register write
// is used to enable that interrupt. Further testing is required,
// but the current method should be sufficient to handle most cases.
// Required for Sesame Street Counting Cafe et al.
irq.delay = 2; // 4 pixel delay (4-6 M68k clocks) from this write
if(command.latch) {
command.latch = 0;
@@ -222,6 +216,7 @@ auto VDP::writeControlPort(n16 data) -> void {
io.counterLatch = data.bit(1);
io.videoMode4 = data.bit(2);
irq.hblank.enable = data.bit(4);
irq.delay = h40() ? 3 : 2; // 4-6 pixel delay (~6 M68k cycles)
io.leftColumnBlank = data.bit(5);
if(!io.videoMode4) debug(unimplemented, "[VDP] M4=0");
@@ -237,6 +232,7 @@ auto VDP::writeControlPort(n16 data) -> void {
}
dma.enable = data.bit(4);
irq.vblank.enable = data.bit(5);
irq.delay = h40() ? 3 : 2; // 4-6 pixel delay (~6 M68k cycles)
io.displayEnable = data.bit(6);
vram.mode = data.bit(7);
+1 -1
View File
@@ -30,7 +30,7 @@ auto VDP::IRQ::acknowledge(u8 level) -> void {
else
hblank.pending = 0;
}
if(level == 6) vblank.pending = 0;
if(level == 6 && vblank.enable) vblank.pending = 0;
}
auto VDP::IRQ::power(bool reset) -> void {
+3 -1
View File
@@ -110,6 +110,7 @@ auto VDP::vtick() -> void {
} else if(irq.hblank.counter-- == 0) {
irq.hblank.counter = irq.hblank.frequency;
irq.hblank.pending = 1;
irq.delay = h40() ? 3 : 2; // 4-6 pixel delay (~6 M68k cycles)
debugger.interrupt(CPU::Interrupt::HorizontalBlank);
}
@@ -147,6 +148,7 @@ auto VDP::vedge() -> void {
cartridge.vblank(1);
apu.setINT(1);
irq.vblank.pending = 1;
irq.delay = h40() ? 3 : 2; // 4-6 pixel delay (~6 M68k cycles)
debugger.interrupt(CPU::Interrupt::VerticalBlank);
}
}
@@ -182,7 +184,7 @@ auto VDP::mainH32() -> void {
sprite.begin();
if(dac.pixels) {
blocks<false, true>();
if(Mega32X()) m32x.vdp.scanline(pixels, vcounter());
if(Mega32X()) m32x.vdp.scanline(pixels + 18, vcounter()); //approx 3 and 1/4 pixel offset
} else {
blocks<false, false>();
}
-21
View File
@@ -1,21 +0,0 @@
ares.components += z80
ares.components += sn76489
ares.components += ym2413
ares.objects += ares-ms-cpu
ares.objects += ares-ms-vdp
ares.objects += ares-ms-psg
ares.objects += ares-ms-opll
ares.objects += ares-ms-system
ares.objects += ares-ms-cartridge
ares.objects += ares-ms-controller
ares.objects += ares-ms-expansion
$(object.path)/ares-ms-cpu.o: $(ares.path)/ms/cpu/cpu.cpp
$(object.path)/ares-ms-vdp.o: $(ares.path)/ms/vdp/vdp.cpp
$(object.path)/ares-ms-psg.o: $(ares.path)/ms/psg/psg.cpp
$(object.path)/ares-ms-opll.o: $(ares.path)/ms/opll/opll.cpp
$(object.path)/ares-ms-system.o: $(ares.path)/ms/system/system.cpp
$(object.path)/ares-ms-cartridge.o: $(ares.path)/ms/cartridge/cartridge.cpp
$(object.path)/ares-ms-controller.o: $(ares.path)/ms/controller/controller.cpp
$(object.path)/ares-ms-expansion.o: $(ares.path)/ms/expansion/expansion.cpp
-25
View File
@@ -1,25 +0,0 @@
ares.components += z80
ares.components += tms9918
ares.components += v9938
ares.components += ay38910
ares.components += ym2413
ares.objects += ares-msx-system
ares.objects += ares-msx-keyboard
ares.objects += ares-msx-cartridge
ares.objects += ares-msx-controller
ares.objects += ares-msx-cpu
ares.objects += ares-msx-vdp
ares.objects += ares-msx-psg
ares.objects += ares-msx-tape
ares.objects += ares-msx-rtc
$(object.path)/ares-msx-system.o: $(ares.path)/msx/system/system.cpp
$(object.path)/ares-msx-keyboard.o: $(ares.path)/msx/keyboard/keyboard.cpp
$(object.path)/ares-msx-cartridge.o: $(ares.path)/msx/cartridge/cartridge.cpp
$(object.path)/ares-msx-controller.o: $(ares.path)/msx/controller/controller.cpp
$(object.path)/ares-msx-cpu.o: $(ares.path)/msx/cpu/cpu.cpp
$(object.path)/ares-msx-vdp.o: $(ares.path)/msx/vdp/vdp.cpp
$(object.path)/ares-msx-psg.o: $(ares.path)/msx/psg/psg.cpp
$(object.path)/ares-msx-tape.o: $(ares.path)/msx/tape/tape.cpp
$(object.path)/ares-msx-rtc.o: $(ares.path)/msx/rtc/rtc.cpp
-15
View File
@@ -1,15 +0,0 @@
ares.components += z80
ares.components += tms9918
ares.components += ay38910
ares.objects += ares-myvision-cpu
ares.objects += ares-myvision-vdp
ares.objects += ares-myvision-psg
ares.objects += ares-myvision-system
ares.objects += ares-myvision-cartridge
$(object.path)/ares-myvision-cpu.o: $(ares.path)/myvision/cpu/cpu.cpp
$(object.path)/ares-myvision-vdp.o: $(ares.path)/myvision/vdp/vdp.cpp
$(object.path)/ares-myvision-psg.o: $(ares.path)/myvision/psg/psg.cpp
$(object.path)/ares-myvision-system.o: $(ares.path)/myvision/system/system.cpp
$(object.path)/ares-myvision-cartridge.o: $(ares.path)/myvision/cartridge/cartridge.cpp
+421 -421
View File
@@ -1,421 +1,421 @@
ares_components(sm5k)
ares_add_sources(
CORE #
n64
UNITY #
n64.cpp
PRIMARY #
memory/memory.cpp
system/system.cpp
cartridge/cartridge.cpp
cic/cic.cpp
controller/controller.cpp
dd/dd.cpp
mi/mi.cpp
vi/vi.cpp
ai/ai.cpp
pi/pi.cpp
pif/pif.cpp
ri/ri.cpp
si/si.cpp
rdram/rdram.cpp
cpu/cpu.cpp
rsp/rsp.cpp
rdp/rdp.cpp
aleck64/aleck64.cpp
)
target_enable_feature(ares "N64 Vulkan rendering with paraLLEl-RDP" VULKAN)
target_compile_definitions(ares PUBLIC VULKAN)
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()
ares_add_sources(
CORE #
n64
INCLUDED #
n64.hpp
CMakeLists.txt
)
ares_add_sources(
CORE #
n64
INCLUDED #
ai/ai.hpp
ai/debugger.cpp
ai/io.cpp
ai/serialization.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
aleck64/aleck64.hpp
aleck64/controls.cpp
aleck64/debugger.cpp
aleck64/serialization.cpp
aleck64/io.cpp
aleck64/vdp.cpp
aleck64/game-config/11beat.hpp
aleck64/game-config/doncdoon.hpp
aleck64/game-config/kurufev.hpp
aleck64/game-config/mayjin3.hpp
aleck64/game-config/starsldr.hpp
aleck64/game-config/twrshaft.hpp
aleck64/game-config/vivdolls.hpp
aleck64/game-config/hipai.hpp
aleck64/game-config/hipai2.hpp
aleck64/game-config/srmvs.hpp
aleck64/game-config/mtetrisc.hpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
cartridge/cartridge.hpp
cartridge/debugger.cpp
cartridge/flash.cpp
cartridge/isviewer.cpp
cartridge/joybus.cpp
cartridge/rtc.cpp
cartridge/serialization.cpp
cartridge/slot.cpp
cartridge/slot.hpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
cic/cic.hpp
cic/commands.cpp
cic/io.cpp
cic/serialization.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
controller/controller.hpp
controller/port.cpp
controller/port.hpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
controller/aleck64/aleck64.cpp
controller/aleck64/aleck64.hpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
controller/gamepad/gamepad.cpp
controller/gamepad/gamepad.hpp
controller/gamepad/transfer-pak.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
controller/mouse/mouse.cpp
controller/mouse/mouse.hpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
cpu/algorithms.cpp
cpu/context.cpp
cpu/cpu.hpp
cpu/dcache.cpp
cpu/debugger.cpp
cpu/disassembler.cpp
cpu/exceptions.cpp
cpu/interpreter-cop2.cpp
cpu/interpreter-fpu.cpp
cpu/interpreter-ipu.cpp
cpu/interpreter-scc.cpp
cpu/interpreter.cpp
cpu/memory.cpp
cpu/recompiler.cpp
cpu/serialization.cpp
cpu/tlb.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
dd/controller.cpp
dd/dd.hpp
dd/debugger.cpp
dd/drive.cpp
dd/io.cpp
dd/rtc.cpp
dd/serialization.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
memory/bus.hpp
memory/io.hpp
memory/memory.hpp
memory/lsb/readable.hpp
memory/lsb/writable.hpp
memory/msb/readable.hpp
memory/msb/writable.hpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
mi/debugger.cpp
mi/io.cpp
mi/mi.hpp
mi/serialization.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
pi/bus.hpp
pi/debugger.cpp
pi/dma.cpp
pi/io.cpp
pi/pi.hpp
pi/serialization.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
pif/debugger.cpp
pif/hle.cpp
pif/io.cpp
pif/pif.hpp
pif/serialization.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
rdp/debugger.cpp
rdp/io.cpp
rdp/rdp.hpp
rdp/render.cpp
rdp/serialization.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
rdram/debugger.cpp
rdram/io.cpp
rdram/rdram.hpp
rdram/serialization.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
ri/debugger.cpp
ri/io.cpp
ri/ri.hpp
ri/serialization.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
rsp/debugger.cpp
rsp/decoder.cpp
rsp/disassembler.cpp
rsp/dma.cpp
rsp/interpreter-ipu.cpp
rsp/interpreter-scc.cpp
rsp/interpreter-vpu.cpp
rsp/interpreter.cpp
rsp/io.cpp
rsp/recompiler.cpp
rsp/rsp.hpp
rsp/serialization.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
si/debugger.cpp
si/dma.cpp
si/io.cpp
si/serialization.cpp
si/si.hpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
system/serialization.cpp
system/system.hpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
vi/debugger.cpp
vi/io.cpp
vi/serialization.cpp
vi/vi.hpp
)
target_compile_definitions(ares PRIVATE VULKAN)
set(
_parallel_rdp_sources
vulkan/vulkan.cpp
vulkan/vulkan.hpp
vulkan/parallel-rdp/parallel-rdp/command_ring.cpp
vulkan/parallel-rdp/parallel-rdp/rdp_device.cpp
vulkan/parallel-rdp/parallel-rdp/rdp_dump_write.cpp
vulkan/parallel-rdp/parallel-rdp/rdp_renderer.cpp
vulkan/parallel-rdp/parallel-rdp/video_interface.cpp
vulkan/parallel-rdp/vulkan/buffer.cpp
vulkan/parallel-rdp/vulkan/buffer_pool.cpp
vulkan/parallel-rdp/vulkan/command_buffer.cpp
vulkan/parallel-rdp/vulkan/command_pool.cpp
vulkan/parallel-rdp/vulkan/context.cpp
vulkan/parallel-rdp/vulkan/cookie.cpp
vulkan/parallel-rdp/vulkan/descriptor_set.cpp
vulkan/parallel-rdp/vulkan/device.cpp
vulkan/parallel-rdp/vulkan/event_manager.cpp
vulkan/parallel-rdp/vulkan/fence.cpp
vulkan/parallel-rdp/vulkan/fence_manager.cpp
vulkan/parallel-rdp/vulkan/image.cpp
vulkan/parallel-rdp/vulkan/indirect_layout.cpp
vulkan/parallel-rdp/vulkan/memory_allocator.cpp
vulkan/parallel-rdp/vulkan/pipeline_event.cpp
vulkan/parallel-rdp/vulkan/query_pool.cpp
vulkan/parallel-rdp/vulkan/render_pass.cpp
vulkan/parallel-rdp/vulkan/sampler.cpp
vulkan/parallel-rdp/vulkan/semaphore.cpp
vulkan/parallel-rdp/vulkan/semaphore_manager.cpp
vulkan/parallel-rdp/vulkan/shader.cpp
vulkan/parallel-rdp/vulkan/texture/texture_format.cpp
vulkan/parallel-rdp/util/arena_allocator.cpp
vulkan/parallel-rdp/util/logging.cpp
vulkan/parallel-rdp/util/thread_id.cpp
vulkan/parallel-rdp/util/aligned_alloc.cpp
vulkan/parallel-rdp/util/timer.cpp
vulkan/parallel-rdp/util/timeline_trace_file.cpp
vulkan/parallel-rdp/util/thread_name.cpp
vulkan/parallel-rdp/util/environment.cpp
vulkan/parallel-rdp/volk/volk.c
)
if(ARES_UNITY_CORES)
ares_add_sources(
CORE #
n64
UNITY #
${_parallel_rdp_sources}
)
else()
ares_add_sources(
CORE #
n64
PRIMARY #
${_parallel_rdp_sources}
)
endif()
ares_add_sources(
CORE #
n64
PRIMARY #
vulkan/parallel-rdp/parallel-rdp/command_ring.hpp
vulkan/parallel-rdp/parallel-rdp/rdp_device.hpp
vulkan/parallel-rdp/parallel-rdp/rdp_dump_write.hpp
vulkan/parallel-rdp/parallel-rdp/rdp_renderer.hpp
vulkan/parallel-rdp/parallel-rdp/video_interface.hpp
vulkan/parallel-rdp/vulkan/buffer.hpp
vulkan/parallel-rdp/vulkan/buffer_pool.hpp
vulkan/parallel-rdp/vulkan/command_buffer.hpp
vulkan/parallel-rdp/vulkan/command_pool.hpp
vulkan/parallel-rdp/vulkan/context.hpp
vulkan/parallel-rdp/vulkan/cookie.hpp
vulkan/parallel-rdp/vulkan/descriptor_set.hpp
vulkan/parallel-rdp/vulkan/device.hpp
vulkan/parallel-rdp/vulkan/event_manager.hpp
vulkan/parallel-rdp/vulkan/fence.hpp
vulkan/parallel-rdp/vulkan/fence_manager.hpp
vulkan/parallel-rdp/vulkan/image.hpp
vulkan/parallel-rdp/vulkan/indirect_layout.hpp
vulkan/parallel-rdp/vulkan/memory_allocator.hpp
vulkan/parallel-rdp/vulkan/pipeline_event.hpp
vulkan/parallel-rdp/vulkan/query_pool.hpp
vulkan/parallel-rdp/vulkan/render_pass.hpp
vulkan/parallel-rdp/vulkan/sampler.hpp
vulkan/parallel-rdp/vulkan/semaphore.hpp
vulkan/parallel-rdp/vulkan/semaphore_manager.hpp
vulkan/parallel-rdp/vulkan/shader.hpp
vulkan/parallel-rdp/vulkan/texture/texture_format.hpp
vulkan/parallel-rdp/util/arena_allocator.hpp
vulkan/parallel-rdp/util/logging.hpp
vulkan/parallel-rdp/util/thread_id.hpp
vulkan/parallel-rdp/util/aligned_alloc.hpp
vulkan/parallel-rdp/util/timer.hpp
vulkan/parallel-rdp/util/timeline_trace_file.hpp
vulkan/parallel-rdp/util/thread_name.hpp
vulkan/parallel-rdp/util/environment.hpp
)
target_include_directories(
ares
PRIVATE
vulkan/parallel-rdp/parallel-rdp
vulkan/parallel-rdp/volk
vulkan/parallel-rdp/vulkan
vulkan/parallel-rdp/vulkan-headers/include
vulkan/parallel-rdp/util
)
target_compile_options(ares PRIVATE $<$<CXX_COMPILER_ID:Clang,AppleClang>:-Wno-newline-eof>)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(ares PUBLIC Threads::Threads)
if(OS_WINDOWS)
target_compile_definitions(ares PRIVATE VK_USE_PLATFORM_WIN32_KHR)
target_link_libraries(ares PRIVATE winmm)
else()
target_link_libraries(ares PRIVATE dl)
endif()
ares_components(sm5k)
ares_add_sources(
CORE #
n64
UNITY #
n64.cpp
PRIMARY #
memory/memory.cpp
system/system.cpp
cartridge/cartridge.cpp
cic/cic.cpp
controller/controller.cpp
dd/dd.cpp
mi/mi.cpp
vi/vi.cpp
ai/ai.cpp
pi/pi.cpp
pif/pif.cpp
ri/ri.cpp
si/si.cpp
rdram/rdram.cpp
cpu/cpu.cpp
rsp/rsp.cpp
rdp/rdp.cpp
aleck64/aleck64.cpp
)
target_enable_feature(ares "N64 Vulkan rendering with paraLLEl-RDP" VULKAN)
target_compile_definitions(ares PUBLIC VULKAN)
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()
ares_add_sources(
CORE #
n64
INCLUDED #
n64.hpp
CMakeLists.txt
)
ares_add_sources(
CORE #
n64
INCLUDED #
ai/ai.hpp
ai/debugger.cpp
ai/io.cpp
ai/serialization.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
aleck64/aleck64.hpp
aleck64/controls.cpp
aleck64/debugger.cpp
aleck64/serialization.cpp
aleck64/io.cpp
aleck64/vdp.cpp
aleck64/game-config/11beat.hpp
aleck64/game-config/doncdoon.hpp
aleck64/game-config/kurufev.hpp
aleck64/game-config/mayjin3.hpp
aleck64/game-config/starsldr.hpp
aleck64/game-config/twrshaft.hpp
aleck64/game-config/vivdolls.hpp
aleck64/game-config/hipai.hpp
aleck64/game-config/hipai2.hpp
aleck64/game-config/srmvs.hpp
aleck64/game-config/mtetrisc.hpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
cartridge/cartridge.hpp
cartridge/debugger.cpp
cartridge/flash.cpp
cartridge/isviewer.cpp
cartridge/joybus.cpp
cartridge/rtc.cpp
cartridge/serialization.cpp
cartridge/slot.cpp
cartridge/slot.hpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
cic/cic.hpp
cic/commands.cpp
cic/io.cpp
cic/serialization.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
controller/controller.hpp
controller/port.cpp
controller/port.hpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
controller/aleck64/aleck64.cpp
controller/aleck64/aleck64.hpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
controller/gamepad/gamepad.cpp
controller/gamepad/gamepad.hpp
controller/gamepad/transfer-pak.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
controller/mouse/mouse.cpp
controller/mouse/mouse.hpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
cpu/algorithms.cpp
cpu/context.cpp
cpu/cpu.hpp
cpu/dcache.cpp
cpu/debugger.cpp
cpu/disassembler.cpp
cpu/exceptions.cpp
cpu/interpreter-cop2.cpp
cpu/interpreter-fpu.cpp
cpu/interpreter-ipu.cpp
cpu/interpreter-scc.cpp
cpu/interpreter.cpp
cpu/memory.cpp
cpu/recompiler.cpp
cpu/serialization.cpp
cpu/tlb.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
dd/controller.cpp
dd/dd.hpp
dd/debugger.cpp
dd/drive.cpp
dd/io.cpp
dd/rtc.cpp
dd/serialization.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
memory/bus.hpp
memory/io.hpp
memory/memory.hpp
memory/lsb/readable.hpp
memory/lsb/writable.hpp
memory/msb/readable.hpp
memory/msb/writable.hpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
mi/debugger.cpp
mi/io.cpp
mi/mi.hpp
mi/serialization.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
pi/bus.hpp
pi/debugger.cpp
pi/dma.cpp
pi/io.cpp
pi/pi.hpp
pi/serialization.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
pif/debugger.cpp
pif/hle.cpp
pif/io.cpp
pif/pif.hpp
pif/serialization.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
rdp/debugger.cpp
rdp/io.cpp
rdp/rdp.hpp
rdp/render.cpp
rdp/serialization.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
rdram/debugger.cpp
rdram/io.cpp
rdram/rdram.hpp
rdram/serialization.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
ri/debugger.cpp
ri/io.cpp
ri/ri.hpp
ri/serialization.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
rsp/debugger.cpp
rsp/decoder.cpp
rsp/disassembler.cpp
rsp/dma.cpp
rsp/interpreter-ipu.cpp
rsp/interpreter-scc.cpp
rsp/interpreter-vpu.cpp
rsp/interpreter.cpp
rsp/io.cpp
rsp/recompiler.cpp
rsp/rsp.hpp
rsp/serialization.cpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
si/debugger.cpp
si/dma.cpp
si/io.cpp
si/serialization.cpp
si/si.hpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
system/serialization.cpp
system/system.hpp
)
ares_add_sources(
CORE #
n64
INCLUDED #
vi/debugger.cpp
vi/io.cpp
vi/serialization.cpp
vi/vi.hpp
)
target_compile_definitions(ares PRIVATE VULKAN)
set(
_parallel_rdp_sources
vulkan/vulkan.cpp
vulkan/vulkan.hpp
vulkan/parallel-rdp/parallel-rdp/command_ring.cpp
vulkan/parallel-rdp/parallel-rdp/rdp_device.cpp
vulkan/parallel-rdp/parallel-rdp/rdp_dump_write.cpp
vulkan/parallel-rdp/parallel-rdp/rdp_renderer.cpp
vulkan/parallel-rdp/parallel-rdp/video_interface.cpp
vulkan/parallel-rdp/vulkan/buffer.cpp
vulkan/parallel-rdp/vulkan/buffer_pool.cpp
vulkan/parallel-rdp/vulkan/command_buffer.cpp
vulkan/parallel-rdp/vulkan/command_pool.cpp
vulkan/parallel-rdp/vulkan/context.cpp
vulkan/parallel-rdp/vulkan/cookie.cpp
vulkan/parallel-rdp/vulkan/descriptor_set.cpp
vulkan/parallel-rdp/vulkan/device.cpp
vulkan/parallel-rdp/vulkan/event_manager.cpp
vulkan/parallel-rdp/vulkan/fence.cpp
vulkan/parallel-rdp/vulkan/fence_manager.cpp
vulkan/parallel-rdp/vulkan/image.cpp
vulkan/parallel-rdp/vulkan/indirect_layout.cpp
vulkan/parallel-rdp/vulkan/memory_allocator.cpp
vulkan/parallel-rdp/vulkan/pipeline_event.cpp
vulkan/parallel-rdp/vulkan/query_pool.cpp
vulkan/parallel-rdp/vulkan/render_pass.cpp
vulkan/parallel-rdp/vulkan/sampler.cpp
vulkan/parallel-rdp/vulkan/semaphore.cpp
vulkan/parallel-rdp/vulkan/semaphore_manager.cpp
vulkan/parallel-rdp/vulkan/shader.cpp
vulkan/parallel-rdp/vulkan/texture/texture_format.cpp
vulkan/parallel-rdp/util/arena_allocator.cpp
vulkan/parallel-rdp/util/logging.cpp
vulkan/parallel-rdp/util/thread_id.cpp
vulkan/parallel-rdp/util/aligned_alloc.cpp
vulkan/parallel-rdp/util/timer.cpp
vulkan/parallel-rdp/util/timeline_trace_file.cpp
vulkan/parallel-rdp/util/thread_name.cpp
vulkan/parallel-rdp/util/environment.cpp
vulkan/parallel-rdp/volk/volk.c
)
if(ARES_UNITY_CORES)
ares_add_sources(
CORE #
n64
UNITY #
${_parallel_rdp_sources}
)
else()
ares_add_sources(
CORE #
n64
PRIMARY #
${_parallel_rdp_sources}
)
endif()
ares_add_sources(
CORE #
n64
PRIMARY #
vulkan/parallel-rdp/parallel-rdp/command_ring.hpp
vulkan/parallel-rdp/parallel-rdp/rdp_device.hpp
vulkan/parallel-rdp/parallel-rdp/rdp_dump_write.hpp
vulkan/parallel-rdp/parallel-rdp/rdp_renderer.hpp
vulkan/parallel-rdp/parallel-rdp/video_interface.hpp
vulkan/parallel-rdp/vulkan/buffer.hpp
vulkan/parallel-rdp/vulkan/buffer_pool.hpp
vulkan/parallel-rdp/vulkan/command_buffer.hpp
vulkan/parallel-rdp/vulkan/command_pool.hpp
vulkan/parallel-rdp/vulkan/context.hpp
vulkan/parallel-rdp/vulkan/cookie.hpp
vulkan/parallel-rdp/vulkan/descriptor_set.hpp
vulkan/parallel-rdp/vulkan/device.hpp
vulkan/parallel-rdp/vulkan/event_manager.hpp
vulkan/parallel-rdp/vulkan/fence.hpp
vulkan/parallel-rdp/vulkan/fence_manager.hpp
vulkan/parallel-rdp/vulkan/image.hpp
vulkan/parallel-rdp/vulkan/indirect_layout.hpp
vulkan/parallel-rdp/vulkan/memory_allocator.hpp
vulkan/parallel-rdp/vulkan/pipeline_event.hpp
vulkan/parallel-rdp/vulkan/query_pool.hpp
vulkan/parallel-rdp/vulkan/render_pass.hpp
vulkan/parallel-rdp/vulkan/sampler.hpp
vulkan/parallel-rdp/vulkan/semaphore.hpp
vulkan/parallel-rdp/vulkan/semaphore_manager.hpp
vulkan/parallel-rdp/vulkan/shader.hpp
vulkan/parallel-rdp/vulkan/texture/texture_format.hpp
vulkan/parallel-rdp/util/arena_allocator.hpp
vulkan/parallel-rdp/util/logging.hpp
vulkan/parallel-rdp/util/thread_id.hpp
vulkan/parallel-rdp/util/aligned_alloc.hpp
vulkan/parallel-rdp/util/timer.hpp
vulkan/parallel-rdp/util/timeline_trace_file.hpp
vulkan/parallel-rdp/util/thread_name.hpp
vulkan/parallel-rdp/util/environment.hpp
)
target_include_directories(
ares
PRIVATE
vulkan/parallel-rdp/parallel-rdp
vulkan/parallel-rdp/volk
vulkan/parallel-rdp/vulkan
vulkan/parallel-rdp/vulkan-headers/include
vulkan/parallel-rdp/util
)
target_compile_options(ares PRIVATE $<$<CXX_COMPILER_ID:Clang,AppleClang>:-Wno-newline-eof>)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(ares PUBLIC Threads::Threads)
if(OS_WINDOWS)
target_compile_definitions(ares PRIVATE VK_USE_PLATFORM_WIN32_KHR)
target_link_libraries(ares PRIVATE winmm)
else()
target_link_libraries(ares PRIVATE dl)
endif()
-99
View File
@@ -1,99 +0,0 @@
ares.components += sm5k
ares.objects += ares-n64-memory
ares.objects += ares-n64-system
ares.objects += ares-n64-cartridge
ares.objects += ares-n64-cic
ares.objects += ares-n64-controller
ares.objects += ares-n64-dd
ares.objects += ares-n64-mi
ares.objects += ares-n64-vi
ares.objects += ares-n64-ai
ares.objects += ares-n64-pi
ares.objects += ares-n64-pif
ares.objects += ares-n64-ri
ares.objects += ares-n64-si
ares.objects += ares-n64-rdram
ares.objects += ares-n64-cpu
ares.objects += ares-n64-rsp
ares.objects += ares-n64-rdp
ares.objects += ares-n64-aleck64
$(object.path)/ares-n64-memory.o: $(ares.path)/n64/memory/memory.cpp
$(object.path)/ares-n64-system.o: $(ares.path)/n64/system/system.cpp
$(object.path)/ares-n64-cartridge.o: $(ares.path)/n64/cartridge/cartridge.cpp
$(object.path)/ares-n64-cic.o: $(ares.path)/n64/cic/cic.cpp
$(object.path)/ares-n64-controller.o: $(ares.path)/n64/controller/controller.cpp
$(object.path)/ares-n64-dd.o: $(ares.path)/n64/dd/dd.cpp
$(object.path)/ares-n64-sp.o: $(ares.path)/n64/sp/sp.cpp
$(object.path)/ares-n64-dp.o: $(ares.path)/n64/dp/dp.cpp
$(object.path)/ares-n64-mi.o: $(ares.path)/n64/mi/mi.cpp
$(object.path)/ares-n64-vi.o: $(ares.path)/n64/vi/vi.cpp
$(object.path)/ares-n64-ai.o: $(ares.path)/n64/ai/ai.cpp
$(object.path)/ares-n64-pi.o: $(ares.path)/n64/pi/pi.cpp
$(object.path)/ares-n64-pif.o: $(ares.path)/n64/pif/pif.cpp
$(object.path)/ares-n64-ri.o: $(ares.path)/n64/ri/ri.cpp
$(object.path)/ares-n64-si.o: $(ares.path)/n64/si/si.cpp
$(object.path)/ares-n64-rdram.o: $(ares.path)/n64/rdram/rdram.cpp
$(object.path)/ares-n64-cpu.o: $(ares.path)/n64/cpu/cpu.cpp
$(object.path)/ares-n64-rsp.o: $(ares.path)/n64/rsp/rsp.cpp
$(object.path)/ares-n64-rdp.o: $(ares.path)/n64/rdp/rdp.cpp
$(object.path)/ares-n64-aleck64.o: $(ares.path)/n64/aleck64/aleck64.cpp
ifeq ($(vulkan),true)
ifeq ($(platform),macos)
molten = $(ares.path)/../thirdparty/MoltenVK/libMoltenVK.dylib
ifeq ($(wildcard $(molten)),)
molten = $(shell brew --prefix molten-vk)/lib/libMoltenVK.dylib
ifeq ($(wildcard $(molten)),)
$(error Compiling Ares N64 Vulkan backend requires MoltenVK. Install it via Homebrew, compile it using thirdparty/MoltenVK/build-moltenvk.sh, or disable with vulkan=false")
endif
endif
ares.dylibs += $(molten)
endif
ares.objects += ares-n64-vulkan
$(object.path)/ares-n64-vulkan.o: $(ares.path)/n64/vulkan/vulkan.cpp
PARALLEL_RDP_IMPLEMENTATION := $(ares.path)/n64/vulkan/parallel-rdp
include $(PARALLEL_RDP_IMPLEMENTATION)/config.mk
ares.objects += $(PARALLEL_RDP_SOURCES_CXX:$(PARALLEL_RDP_IMPLEMENTATION)/%.cpp=ares-n64-parallel-rdp/%)
ares.objects += $(PARALLEL_RDP_SOURCES_C:$(PARALLEL_RDP_IMPLEMENTATION)/%.c=ares-n64-parallel-rdp/%)
flags += $(PARALLEL_RDP_INCLUDE_DIRS) $(PARALLEL_RDP_CXXFLAGS) $(PARALLEL_RDP_CFLAGS)
$(object.path)/ares-n64-parallel-rdp/volk/volk.o: $(PARALLEL_RDP_IMPLEMENTATION)/volk/volk.c
$(object.path)/ares-n64-parallel-rdp/parallel-rdp/video_interface.o: $(PARALLEL_RDP_IMPLEMENTATION)/parallel-rdp/video_interface.cpp
$(object.path)/ares-n64-parallel-rdp/parallel-rdp/command_ring.o: $(PARALLEL_RDP_IMPLEMENTATION)/parallel-rdp/command_ring.cpp
$(object.path)/ares-n64-parallel-rdp/parallel-rdp/rdp_device.o: $(PARALLEL_RDP_IMPLEMENTATION)/parallel-rdp/rdp_device.cpp
$(object.path)/ares-n64-parallel-rdp/parallel-rdp/rdp_dump_write.o: $(PARALLEL_RDP_IMPLEMENTATION)/parallel-rdp/rdp_dump_write.cpp
$(object.path)/ares-n64-parallel-rdp/parallel-rdp/rdp_renderer.o: $(PARALLEL_RDP_IMPLEMENTATION)/parallel-rdp/rdp_renderer.cpp
$(object.path)/ares-n64-parallel-rdp/vulkan/buffer.o: $(PARALLEL_RDP_IMPLEMENTATION)/vulkan/buffer.cpp
$(object.path)/ares-n64-parallel-rdp/vulkan/buffer_pool.o: $(PARALLEL_RDP_IMPLEMENTATION)/vulkan/buffer_pool.cpp
$(object.path)/ares-n64-parallel-rdp/vulkan/command_buffer.o: $(PARALLEL_RDP_IMPLEMENTATION)/vulkan/command_buffer.cpp
$(object.path)/ares-n64-parallel-rdp/vulkan/command_pool.o: $(PARALLEL_RDP_IMPLEMENTATION)/vulkan/command_pool.cpp
$(object.path)/ares-n64-parallel-rdp/vulkan/context.o: $(PARALLEL_RDP_IMPLEMENTATION)/vulkan/context.cpp
$(object.path)/ares-n64-parallel-rdp/vulkan/cookie.o: $(PARALLEL_RDP_IMPLEMENTATION)/vulkan/cookie.cpp
$(object.path)/ares-n64-parallel-rdp/vulkan/descriptor_set.o: $(PARALLEL_RDP_IMPLEMENTATION)/vulkan/descriptor_set.cpp
$(object.path)/ares-n64-parallel-rdp/vulkan/device.o: $(PARALLEL_RDP_IMPLEMENTATION)/vulkan/device.cpp
$(object.path)/ares-n64-parallel-rdp/vulkan/event_manager.o: $(PARALLEL_RDP_IMPLEMENTATION)/vulkan/event_manager.cpp
$(object.path)/ares-n64-parallel-rdp/vulkan/fence.o: $(PARALLEL_RDP_IMPLEMENTATION)/vulkan/fence.cpp
$(object.path)/ares-n64-parallel-rdp/vulkan/fence_manager.o: $(PARALLEL_RDP_IMPLEMENTATION)/vulkan/fence_manager.cpp
$(object.path)/ares-n64-parallel-rdp/vulkan/image.o: $(PARALLEL_RDP_IMPLEMENTATION)/vulkan/image.cpp
$(object.path)/ares-n64-parallel-rdp/vulkan/indirect_layout.o: $(PARALLEL_RDP_IMPLEMENTATION)/vulkan/indirect_layout.cpp
$(object.path)/ares-n64-parallel-rdp/vulkan/memory_allocator.o: $(PARALLEL_RDP_IMPLEMENTATION)/vulkan/memory_allocator.cpp
$(object.path)/ares-n64-parallel-rdp/vulkan/pipeline_event.o: $(PARALLEL_RDP_IMPLEMENTATION)/vulkan/pipeline_event.cpp
$(object.path)/ares-n64-parallel-rdp/vulkan/query_pool.o: $(PARALLEL_RDP_IMPLEMENTATION)/vulkan/query_pool.cpp
$(object.path)/ares-n64-parallel-rdp/vulkan/render_pass.o: $(PARALLEL_RDP_IMPLEMENTATION)/vulkan/render_pass.cpp
$(object.path)/ares-n64-parallel-rdp/vulkan/sampler.o: $(PARALLEL_RDP_IMPLEMENTATION)/vulkan/sampler.cpp
$(object.path)/ares-n64-parallel-rdp/vulkan/semaphore.o: $(PARALLEL_RDP_IMPLEMENTATION)/vulkan/semaphore.cpp
$(object.path)/ares-n64-parallel-rdp/vulkan/semaphore_manager.o: $(PARALLEL_RDP_IMPLEMENTATION)/vulkan/semaphore_manager.cpp
$(object.path)/ares-n64-parallel-rdp/vulkan/shader.o: $(PARALLEL_RDP_IMPLEMENTATION)/vulkan/shader.cpp
$(object.path)/ares-n64-parallel-rdp/vulkan/texture/texture_format.o: $(PARALLEL_RDP_IMPLEMENTATION)/vulkan/texture/texture_format.cpp
$(object.path)/ares-n64-parallel-rdp/util/arena_allocator.o: $(PARALLEL_RDP_IMPLEMENTATION)/util/arena_allocator.cpp
$(object.path)/ares-n64-parallel-rdp/util/logging.o: $(PARALLEL_RDP_IMPLEMENTATION)/util/logging.cpp
$(object.path)/ares-n64-parallel-rdp/util/thread_id.o: $(PARALLEL_RDP_IMPLEMENTATION)/util/thread_id.cpp
$(object.path)/ares-n64-parallel-rdp/util/aligned_alloc.o: $(PARALLEL_RDP_IMPLEMENTATION)/util/aligned_alloc.cpp
$(object.path)/ares-n64-parallel-rdp/util/timer.o: $(PARALLEL_RDP_IMPLEMENTATION)/util/timer.cpp
$(object.path)/ares-n64-parallel-rdp/util/timeline_trace_file.o: $(PARALLEL_RDP_IMPLEMENTATION)/util/timeline_trace_file.cpp
$(object.path)/ares-n64-parallel-rdp/util/environment.o: $(PARALLEL_RDP_IMPLEMENTATION)/util/environment.cpp
$(object.path)/ares-n64-parallel-rdp/util/thread_name.o: $(PARALLEL_RDP_IMPLEMENTATION)/util/thread_name.cpp
else
$(warning Ares n64 core requires Vulkan for RDP emulation, only titles using Software Rendering will function)
endif
+2
View File
@@ -26,5 +26,7 @@ struct Accuracy {
struct PIF {
// Emulate a region-locked console
static constexpr bool RegionLock = false;
// Emulate the PIF's checksum security check
static constexpr bool IPL2Checksum = true;
};
};
+2 -2
View File
@@ -54,7 +54,7 @@ auto Cartridge::joybusComm(n8 send, n8 recv, n8 input[], n8 output[]) -> n2 {
if(input[0] == 0x07 && send >= 2 && recv >= 9) {
if(cartridge.rtc.present) {
rtc.read(input[1], &output[0]);
output[8] = 0x00;
output[8] = rtc.status;
valid = 1;
}
}
@@ -63,7 +63,7 @@ auto Cartridge::joybusComm(n8 send, n8 recv, n8 input[], n8 output[]) -> n2 {
if(input[0] == 0x08 && send >= 10 && recv >= 1) {
if(cartridge.rtc.present) {
rtc.write(input[1], &input[2]);
output[0] = 0x00;
output[0] = rtc.status;
valid = 1;
}
}
+128 -22
View File
@@ -12,6 +12,8 @@ Gamepad::Gamepad(Node::Port parent) {
port->setDisconnect([&] { return disconnect(); });
port->setSupported({"Controller Pak", "Rumble Pak", "Transfer Pak"});
bank = 0;
x = node->append<Node::Input::Axis> ("X-Axis");
y = node->append<Node::Input::Axis> ("Y-Axis");
up = node->append<Node::Input::Button>("Up");
@@ -54,12 +56,56 @@ auto Gamepad::allocate(string name) -> Node::Peripheral {
auto Gamepad::connect() -> void {
if(!slot) return;
if(slot->name() == "Controller Pak") {
bool create = true;
node->setPak(pak = platform->pak(node));
ram.allocate(32_KiB);
system.controllerPakBankCount = system.configuredControllerPakBankCount; //reset controller bank count
ram.allocate(system.controllerPakBankCount * 32_KiB); //allocate N banks * 32KiB, max # of banks allowed is 62
bank = 0;
formatControllerPak();
if(auto fp = pak->read("save.pak")) {
if(fp->attribute("loaded").boolean()) {
//read the bank count
u8 banks;
u32 bank_size;
fp->seek(0x20 + 0x1A);
fp->read(array_span<u8>{&banks, sizeof(banks)});
fp->seek(0);
if (banks < 1) {
banks = 1;
} else if (banks > 62) {
banks = 62;
}
bank_size = 32_KiB * banks;
if (bank_size != ram.size) {
ram.allocate(bank_size);
//update the system controller bank count
system.controllerPakBankCount = banks;
}
ram.load(pak->read("save.pak"));
if (fp->size() != bank_size) {
//reallocate vfs node
pak->remove(fp);
pak->append("save.pak", bank_size);
ram.save(pak->write("save.pak")); //write data back to filesystem
}
create = false;
}
}
if (create) {
//we need to create a controller pak file, so reallocate the vfs file to configured size
if (auto fp = pak->read("save.pak")) {
pak->remove(fp);
pak->append("save.pak", system.controllerPakBankCount * 32_KiB);
ram.save(pak->write("save.pak"));
}
}
}
@@ -132,7 +178,8 @@ auto Gamepad::comm(n8 send, n8 recv, n8 input[], n8 output[]) -> n2 {
u16 address = (input[1] << 8 | input[2] << 0) & ~31;
if(pif.addressCRC(address) == (n5)input[2]) {
for(u32 index : range(recv - 1)) {
if(address <= 0x7FFF) output[index] = ram.read<Byte>(address);
// read into current bank
if(address <= 0x7FFF) output[index] = ram.read<Byte>(bank * 32_KiB + address);
else output[index] = 0;
address++;
}
@@ -173,12 +220,46 @@ auto Gamepad::comm(n8 send, n8 recv, n8 input[], n8 output[]) -> n2 {
if(ram) {
u16 address = (input[1] << 8 | input[2] << 0) & ~31;
if(pif.addressCRC(address) == (n5)input[2]) {
for(u32 index : range(send - 3)) {
if(address <= 0x7FFF) ram.write<Byte>(address, input[3 + index]);
address++;
//check if address is bank switch command
if (address == 0x8000) {
if (send >= 4) {
u8 reqBank = input[3];
if (reqBank < system.controllerPakBankCount) {
bank = reqBank;
}
} else {
if (system.homebrewMode) {
debug(unusual, "Controller Pak bank switch command with no bank specified");
}
bank = 0;
}
if (system.homebrewMode) {
//Verify we have 32 bytes (1 block) input and each value is the same bank
if (send == 35) {
u8 bank = input[3];
for (u32 i = 4; i < 35; i++) {
if (input[i] != bank) {
debug(unusual, "Controller Pak bank switch command with mismatched data");
break;
}
}
} else {
debug(unusual, "Controller Pak bank switch command with invalid data length");
}
}
output[0] = pif.dataCRC({&input[3], send - 3u});
valid = 1;
} else {
for(u32 index : range(send - 3)) {
if(address <= 0x7FFF) ram.write<Byte>(bank * 32_KiB + address, input[3 + index]);
address++;
}
output[0] = pif.dataCRC({&input[3], send - 3u});
valid = 1;
}
output[0] = pif.dataCRC({&input[3], send - 3u});
valid = 1;
}
}
@@ -315,8 +396,24 @@ auto Gamepad::read() -> n32 {
return data;
}
//controller paks contain 32KB of SRAM split into 128 pages of 256 bytes each.
//the first 5 pages are for storing system data, and the remaining 123 for game data.
auto Gamepad::getInodeChecksum(u8 bank) -> u8 {
if (bank < 62) {
u32 checksum = 0;
u32 i = bank == 0 ? 3 + ram.read<Byte>(0x20 + 0x1a) * 2 : 1; //first bank has 3 + bank * 2 system pages, other banks have 127.
for (i; i < 0x100; i++) {
checksum += ram.read<Byte>((1 + bank) * 0x100) + ram.read<Byte>((1 + bank) * 0x100 + 0x01);
}
return checksum;
}
return 0;
}
//controller paks contain 32KB * nBanks of SRAM split into 128 pages of 256 bytes each.
//the first 3 + nBanks * 2 pages of bank 0 are for storing system data, and the remaining 123 for game data.
//the remaining banks page 0 is unused and the remaining 127 are for game data.
auto Gamepad::formatControllerPak() -> void {
ram.fill(0x00);
@@ -325,12 +422,12 @@ auto Gamepad::formatControllerPak() -> void {
n19 fieldB = random();
n27 fieldC = random();
for(u32 area : array<u8[4]>{1,3,4,6}) {
ram.write<Byte>(area * 0x20 + 0x01, fieldA); //unknown
ram.write<Word>(area * 0x20 + 0x04, fieldB); //serial# hi
ram.write<Word>(area * 0x20 + 0x08, fieldC); //serial# lo
ram.write<Half>(area * 0x20 + 0x18, 0x0001); //device ID
ram.write<Byte>(area * 0x20 + 0x1a, 0x01); //banks (0x01 = 32KB)
ram.write<Byte>(area * 0x20 + 0x1b, 0x00); //version#
ram.write<Byte>(area * 0x20 + 0x01, fieldA); //unknown
ram.write<Word>(area * 0x20 + 0x04, fieldB); //serial# hi
ram.write<Word>(area * 0x20 + 0x08, fieldC); //serial# lo
ram.write<Half>(area * 0x20 + 0x18, 0x0001); //device ID
ram.write<Byte>(area * 0x20 + 0x1a, system.controllerPakBankCount); //banks (0x01 = 32KB), (62 = max banks)
ram.write<Byte>(area * 0x20 + 0x1b, 0x00); //version#
u16 checksum = 0;
u16 inverted = 0;
for(u32 half : range(14)) {
@@ -342,16 +439,25 @@ auto Gamepad::formatControllerPak() -> void {
ram.write<Half>(area * 0x20 + 0x1e, inverted);
}
//pages 1+2 (inode table)
for(u32 page : array<u8[2]>{1,2}) {
ram.write<Byte>(0x100 * page + 0x01, 0x71); //unknown
for(u32 slot : range(5,128)) {
ram.write<Byte>(0x100 * page + slot * 2 + 0x01, 0x03); //0x01 = stop, 0x03 = empty
//pages 1 thru nBanks, nBanks+1 thru (nBanks*2) (inode table, inode table copy)
u8 nBanks = ram.read<Byte>(0x20 + 0x1a);
u32 inodeTablePage = 1;
u32 inodeTableCopyPage = 1 + nBanks * 2;
for(u32 bank : range(0,nBanks)) {
u32 firstDataPage = bank == 0 ? (3 + nBanks * 2) : 1; //first bank has 3 + bank * 2 system pages, other banks have 127.
for(u32 page : array<u32[2]>{inodeTablePage + bank, inodeTableCopyPage + bank}) {
for(u32 slot : range(firstDataPage,128)) {
ram.write<Byte>(0x100 * page + slot * 2 + 0x01, 0x03); //0x01 = stop, 0x03 = empty
}
ram.write<Byte>(0x100 * page + 0x01, getInodeChecksum(bank)); //checksum
}
}
//pages 3+4 (note table)
//pages 5-127 (game saves)
//page 1 is pak info and serial
//pages 2-nBanks are for the inode table
//pages at nBanks+1,2*nBanks are for the inode table backup
//pages at 2*nBanks+1, 2*nBanks+2 are for note table
//pages 3 + 2*nBanks are for save data
}
auto Gamepad::serialize(serializer& s) -> void {
Loaded 100 of 4440 files, more files were not shown because too many files have changed in this diff. Show more