mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-18 09:41:33 +02:00
Compare commits
151 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
55e78b03de | ||
|
47dcdc1b4f | ||
|
e13ab011eb | ||
|
892f202945 | ||
|
e575196abc | ||
|
404caeab50 | ||
|
dde9b4c2c7 | ||
|
793f2e5bf4 | ||
|
cc4ab9bc25 | ||
|
2551f20f3a | ||
|
5b29ddbcaa | ||
|
ac4d16c917 | ||
|
01c16dcf4d | ||
|
169c0871c7 | ||
|
ffee61a1b1 | ||
|
526df86ee6 | ||
|
748cf44f35 | ||
|
a4f96f0648 | ||
|
2ca1bab9ed | ||
|
1e6a745f19 | ||
|
90b1350110 | ||
|
357d054c19 | ||
|
d62e3f3362 | ||
|
4ec45a7453 | ||
|
f5d40bd1ee | ||
|
6aa7c944d5 | ||
|
dafd673177 | ||
|
a64c1adaa8 | ||
|
0d1d6f329d | ||
|
7cd897b53b | ||
|
011f470b07 | ||
|
6edad01fb8 | ||
|
3ecea80ecb | ||
|
b7b848eff5 | ||
|
da7350ac5c | ||
|
ba3fca27ad | ||
|
5775155714 | ||
|
f1108408a8 | ||
|
996358da66 | ||
|
c717a0e7bd | ||
|
2884cd87d2 | ||
|
454b90be24 | ||
|
2b9a22e1d8 | ||
|
1c1cfd086b | ||
|
f2978247c1 | ||
|
4f32551430 | ||
|
c61c3cabc6 | ||
|
819d6dbde4 | ||
|
f51bc06739 | ||
|
4f09a3873d | ||
|
55bfe402e7 | ||
|
30d7fa1923 | ||
|
9f86a3be26 | ||
|
6b7e6e01bb | ||
|
53f8de6ac3 | ||
|
cd18cdb1d6 | ||
|
6cb7d89d64 | ||
|
19f3cdfd5e | ||
|
a32b6fae74 | ||
|
03a6e1c7de | ||
|
6b34f134bf | ||
|
2de906ea46 | ||
|
95addddc46 | ||
|
45e9e0f0ea | ||
|
0aea7fd5c5 | ||
|
fb95d5b59f | ||
|
3d646aef73 | ||
|
3fb7ff6bfe | ||
|
d8bc2050be | ||
|
e71da4d8c8 | ||
|
e78aca34b9 | ||
|
0c82cc325e | ||
|
78c76962ec | ||
|
e22167cf82 | ||
|
1698533774 | ||
|
7b66e1c531 | ||
|
c6f92b782c | ||
|
e3f2e634c8 | ||
|
e598e81ab9 | ||
|
d37fb1c12e | ||
|
eaf33cb078 | ||
|
07427e4697 | ||
|
b5301b7ea8 | ||
|
57c53a86b4 | ||
|
f19f31938b | ||
|
4efee7e9f1 | ||
|
3701236ca0 | ||
|
2f684caa7c | ||
|
3a064fc5a3 | ||
|
5a4b667eae | ||
|
62729df2d1 | ||
|
1ef227f482 | ||
|
6e5542aa20 | ||
|
675662e739 | ||
|
409dd371b9 | ||
|
18d2ab6435 | ||
|
1e626e75ef | ||
|
c6d90d3ff1 | ||
|
29caf77751 | ||
|
29b13083d5 | ||
|
3883172a4e | ||
|
fa77fc6a8f | ||
|
56c9a5195e | ||
|
a6ebce428f | ||
|
0788627898 | ||
|
1195c46ac0 | ||
|
90f094b931 | ||
|
2bb1606552 | ||
|
08e5e81609 | ||
|
03aaaba889 | ||
|
556ab4c809 | ||
|
23467b5b1f | ||
|
5ae1bcd973 | ||
|
64e3658bcb | ||
|
ab515b59d4 | ||
|
bb7b2f2e60 | ||
|
639b9db961 | ||
|
f857f35e72 | ||
|
5dc27a9fb3 | ||
|
ce3dba130c | ||
|
f9ca7a4927 | ||
|
db1c37c799 | ||
|
7a98db84ac | ||
|
b73493b492 | ||
|
95831d3675 | ||
|
ab25877af4 | ||
|
5ba538ee39 | ||
|
04b85ade6b | ||
|
0b088b6b55 | ||
|
252f479b22 | ||
|
46dbe00f14 | ||
|
66ad62b79f | ||
|
27b2d11839 | ||
|
96c381f91f | ||
|
5757949023 | ||
|
bad27bb8f3 | ||
|
06ceb7d29e | ||
|
e030428054 | ||
|
24dce7dd92 | ||
|
b577e6d5d0 | ||
|
db988d9588 | ||
|
f6303518d5 | ||
|
9e8913cea0 | ||
|
0e56b27228 | ||
|
fe81130f54 | ||
|
216b472418 | ||
|
bc7456246c | ||
|
a7b30b069c | ||
|
454b39cb78 | ||
|
f65b7a8528 | ||
|
7e88bdde09 |
44
.cirrus.yml
44
.cirrus.yml
@@ -6,7 +6,7 @@ linux-x86_64-binaries_task:
|
||||
- apt-get update && apt-get -y install build-essential libgtk2.0-dev libpulse-dev mesa-common-dev libgtksourceview2.0-dev libcairo2-dev libsdl2-dev libxv-dev libao-dev libopenal-dev libudev-dev zip
|
||||
|
||||
compile_script:
|
||||
- make -C bsnes
|
||||
- make -C bsnes local=false
|
||||
|
||||
package_script:
|
||||
- mkdir bsnes-nightly
|
||||
@@ -14,67 +14,71 @@ linux-x86_64-binaries_task:
|
||||
- mkdir bsnes-nightly/Firmware
|
||||
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes
|
||||
- cp -a bsnes/Database/* bsnes-nightly/Database
|
||||
- cp -a icarus/Database/* bsnes-nightly/Database
|
||||
- cp -a GPLv3.txt bsnes-nightly/
|
||||
- cp -a shaders bsnes-nightly/Shaders
|
||||
- cp -a GPLv3.txt bsnes-nightly
|
||||
- zip -r bsnes-nightly.zip bsnes-nightly
|
||||
|
||||
bsnes-nightly_artifacts:
|
||||
path: "bsnes-nightly/**"
|
||||
path: "bsnes-nightly.zip"
|
||||
|
||||
freebsd-x86_64-binaries_task:
|
||||
freebsd_instance:
|
||||
image: freebsd-12-0-release-amd64
|
||||
|
||||
setup_script:
|
||||
- pkg install --yes gmake gdb gcc8 pkgconf sdl2 openal-soft gtksourceview2 libXv
|
||||
- pkg install --yes gmake gdb gcc8 pkgconf sdl2 openal-soft gtksourceview2 libXv zip
|
||||
|
||||
compile_script:
|
||||
- gmake -C bsnes
|
||||
- gmake -C bsnes local=false
|
||||
|
||||
package_script:
|
||||
- mkdir bsnes-nightly
|
||||
- mkdir bsnes-nightly/Database
|
||||
- mkdir bsnes-nightly/Firmware
|
||||
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes
|
||||
- cp -a bsnes/Database/* bsnes-nightly/Database/
|
||||
- cp -a icarus/Database/* bsnes-nightly/Database/
|
||||
- cp -a GPLv3.txt bsnes-nightly/
|
||||
- cp -a bsnes/Database/* bsnes-nightly/Database
|
||||
- cp -a shaders bsnes-nightly/Shaders
|
||||
- cp -a GPLv3.txt bsnes-nightly
|
||||
- zip -r bsnes-nightly.zip bsnes-nightly
|
||||
|
||||
bsnes-nightly_artifacts:
|
||||
path: "bsnes-nightly/**"
|
||||
path: "bsnes-nightly.zip"
|
||||
|
||||
windows-x86_64-binaries_task:
|
||||
container:
|
||||
image: ubuntu:latest
|
||||
|
||||
setup_script:
|
||||
- apt-get update && apt-get -y install build-essential mingw-w64
|
||||
- apt-get update && apt-get -y install build-essential mingw-w64 zip
|
||||
|
||||
compile_script:
|
||||
- make -C bsnes platform=windows compiler="x86_64-w64-mingw32-g++" windres="x86_64-w64-mingw32-windres"
|
||||
- make -C bsnes local=false platform=windows compiler="x86_64-w64-mingw32-g++" windres="x86_64-w64-mingw32-windres"
|
||||
|
||||
package_script:
|
||||
- mkdir bsnes-nightly
|
||||
- mkdir bsnes-nightly/Database
|
||||
- mkdir bsnes-nightly/Firmware
|
||||
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes.exe
|
||||
- cp -a bsnes/Database/* bsnes-nightly/Database/
|
||||
- cp -a icarus/Database/* bsnes-nightly/Database/
|
||||
- cp -a GPLv3.txt bsnes-nightly/
|
||||
- cp -a bsnes/Database/* bsnes-nightly/Database
|
||||
- cp -a shaders bsnes-nightly/Shaders
|
||||
- cp -a GPLv3.txt bsnes-nightly
|
||||
- zip -r bsnes-nightly.zip bsnes-nightly
|
||||
|
||||
bsnes-nightly_artifacts:
|
||||
path: "bsnes-nightly/**"
|
||||
path: "bsnes-nightly.zip"
|
||||
|
||||
macOS-x86_64-binaries_task:
|
||||
osx_instance:
|
||||
image: mojave-base
|
||||
|
||||
compile_script:
|
||||
- make -C bsnes
|
||||
- make -C bsnes local=false
|
||||
|
||||
package_script:
|
||||
- mkdir bsnes-nightly
|
||||
- cp -a bsnes/out/bsnes.app bsnes-nightly/
|
||||
- cp -a GPLv3.txt bsnes-nightly/
|
||||
- cp -a bsnes/out/bsnes.app bsnes-nightly
|
||||
- cp -a GPLv3.txt bsnes-nightly
|
||||
- zip -r bsnes-nightly.zip bsnes-nightly
|
||||
|
||||
bsnes-nightly_artifacts:
|
||||
path: "bsnes-nightly/**"
|
||||
path: "bsnes-nightly.zip"
|
||||
|
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*.fs linguist-detectable=false
|
||||
*.vs linguist-detectable=false
|
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
patreon: byuu
|
89
CONTRIBUTING.md
Normal file
89
CONTRIBUTING.md
Normal file
@@ -0,0 +1,89 @@
|
||||
Contributing
|
||||
============
|
||||
|
||||
Code contributions are most welcome and highly appreciated!
|
||||
|
||||
But first, please note that although bsnes is licensed under the GPLv3 license,
|
||||
in order to be merged upstream, any code contributions must be provided under
|
||||
the ISC source code license.
|
||||
|
||||
This is *not* a CLA (community license agreement), no legal contract needs to be
|
||||
signed, and you will maintain full and exclusive copyright ownership over any
|
||||
contributed source code.
|
||||
|
||||
There are two reasons for this requirement:
|
||||
|
||||
GPLv4+
|
||||
------
|
||||
|
||||
bsnes is currently licensed under the GPLv3 license only. I do not license bsnes
|
||||
under the GPLv3 or later license, because there is no way of knowing what the
|
||||
GPLv4 and later licenses will change, and if they will be in the best interests
|
||||
of emulator development and video game preservation.
|
||||
|
||||
Although I put a good deal of trust into the FSF, no one is an oracle that can
|
||||
predict the future. Would *you* agree to a license before being able to read it?
|
||||
|
||||
However, the GPLv4 may prove beneficial, and close important holes in the GPLv3
|
||||
license, just as the GPLv3 license closed the GPLv2's TiVoization loophole. And
|
||||
so it is important that bsnes retains the option of relicensing to the GPLv4+ in
|
||||
the future.
|
||||
|
||||
As a point of interest, there have been projects with similar concerns about
|
||||
using a GPLv2 or later clause, that are now permanently stuck on the GPLv2
|
||||
license. There have also been projects that did use a GPLv2 or later clause,
|
||||
only to disagree with the changes introduced in the GPLv3.
|
||||
|
||||
ISC
|
||||
---
|
||||
|
||||
The more important reason for this requirement is that it is my intention to
|
||||
release the entirety of bsnes under the ISC license once official upstream
|
||||
development has ceased.
|
||||
|
||||
The reason I would want to relicense bsnes to the ISC license upon its official
|
||||
discontinuation is because once again, no one is an oracle, and I cannot predict
|
||||
what future issues bsnes permanently remaining under the GPLv3 license may
|
||||
cause.
|
||||
|
||||
For instance, imagine a world where a certain vendor took over the world, and
|
||||
the only way to distribute applications was with their approval, and their store
|
||||
rules forbade GPLv3 software. Or perhaps a world where the GPL was abandoned in
|
||||
favor of the new OSSv1 license. But GPLv3 software was incompatible with the
|
||||
OSSv1 license. Other open source developers would not be able to use bsnes in
|
||||
that scenario.
|
||||
|
||||
It would be very disappointing if all of our work ended up unusable 50+ years
|
||||
into the future because it was permanently bound to the GPLv3 license.
|
||||
|
||||
GPLv3
|
||||
-----
|
||||
|
||||
The reason I use the GPLv3 license currently is because it is a balance between
|
||||
altruism and self-interest. The GPLv3 allows other vendors to sell my own code
|
||||
without sharing revenue with me, and indeed this has already happened. But the
|
||||
GPLv3 also prevents other vendors from improving upon bsnes without sharing
|
||||
their work with everyone else as I have.
|
||||
|
||||
While I am actively developing bsnes, I do not wish to compete against myself.
|
||||
|
||||
As such, I believe the GPLv3 is the best license during active development, and
|
||||
the ISC is the best license once bsnes is officially discontinued.
|
||||
|
||||
Considerations
|
||||
--------------
|
||||
|
||||
This is the part that should concern you as a contributor: I am not requesting
|
||||
contributed source code to be released under the ISC so that I personally may
|
||||
sell GPLv3 commercial license exemptions to your work, but in the future when
|
||||
bsnes is released under the ISC license, that will open the door for anyone to
|
||||
sell the work commercially in a closed source form.
|
||||
|
||||
If this is not acceptable to you, I wholly understand and I welcome you to
|
||||
release your work under the GPLv3 in the form of a bsnes fork. And if your work
|
||||
is not an essential part of the core emulation -- that is to say, it may be
|
||||
optionally disabled -- then I am still willing to work with you in merging such
|
||||
work upstream anyway under the full GPLv3 license, but please reach out to me
|
||||
first before developing under the assumption your work will be merged upstream.
|
||||
|
||||
Thank you very much for reading and hopefully for your understanding.
|
17
LICENSE.txt
17
LICENSE.txt
@@ -1,10 +1,9 @@
|
||||
----------------------------------------------------------------------
|
||||
bsnes - Suite of videogame console emulators
|
||||
icarus - Game library importer for higan
|
||||
bsnes - Super Nintendo emulator
|
||||
|
||||
Copyright © 2004-2019 byuu
|
||||
Copyright © 2004-2019 byuu
|
||||
|
||||
https://byuu.org/
|
||||
https://byuu.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -21,12 +20,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------
|
||||
hiro - User interface toolkit
|
||||
libco - C cooperative threading library
|
||||
nall - C++ template library
|
||||
ruby - Hardware abstraction layer
|
||||
libco - C cooperative threading library
|
||||
nall - C++ template library
|
||||
ruby - Hardware abstraction library
|
||||
hiro - User interface library
|
||||
|
||||
Copyright © 2006-2019 byuu
|
||||
Copyright © 2006-2019 byuu
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for
|
||||
any purpose with or without fee is hereby granted, provided that the
|
||||
|
44
README.md
44
README.md
@@ -1,8 +1,10 @@
|
||||
bsnes
|
||||
=====
|
||||
|
||||
bsnes is a multi-platform Super Nintendo (Super Famicom) emulator that focuses
|
||||
on performance, features, and ease of use.
|
||||

|
||||
|
||||
bsnes is a multi-platform Super Nintendo (Super Famicom) emulator from
|
||||
[byuu](https://byuu.org) that focuses on performance, features, and ease of use.
|
||||
|
||||
bsnes currently enjoys 100% known, bug-free compatibility with the entire SNES
|
||||
library when configured to its most accurate settings, giving it the same
|
||||
@@ -20,10 +22,6 @@ However, bugs will exist, regressions will occur, so proceed at your own risk.
|
||||
If stability is required, please download the latest stable release from the
|
||||
[official website.](https://bsnes.byuu.org)
|
||||
|
||||
Nightly builds are available via Cirrus CI.
|
||||
|
||||
- [Nightly builds](https://cirrus-ci.com/github/byuu/bsnes)
|
||||
|
||||
Unique Features
|
||||
---------------
|
||||
|
||||
@@ -36,12 +34,11 @@ Unique Features
|
||||
- Built-in games database with thousands of game entries
|
||||
- Built-in cheat code database for hundreds of popular games (by mightymo)
|
||||
- Built-in save state manager with screenshot previews and naming capabilities
|
||||
- Support for ASIO low-latency audio
|
||||
- Customizable per-byte game mappings to support any cartridges, including prototype games
|
||||
- 7-zip decompression support
|
||||
- Extensive Satellaview emulation, including BS Memory flash write and wear-leveling emulation
|
||||
- 30-bit color output support (where supported)
|
||||
- Optional higan game folder support (standard game ROM files are also fully supported!)
|
||||
- Advanced mapping system allowing multiple bindings to every emulated input
|
||||
|
||||
Standard Features
|
||||
-----------------
|
||||
@@ -53,6 +50,7 @@ Standard Features
|
||||
- Several built-in software filters, including HQ2x (by MaxSt) and snes_ntsc (by blargg)
|
||||
- Adaptive sync and dynamic rate control for perfect audio/video synchronization
|
||||
- Just-in-time input polling for minimal input latency
|
||||
- Run-ahead support for removing internal game engine input latency
|
||||
- Support for Direct3D exclusive mode video
|
||||
- Support for WASAPI exclusive mode audio
|
||||
- Periodic auto-saving of game saves
|
||||
@@ -60,16 +58,42 @@ Standard Features
|
||||
- Sprite limit disable support
|
||||
- Cubic audio interpolation support
|
||||
- Optional high-level emulation of most SNES coprocessors
|
||||
- SuperFX overclocking of up to 800%
|
||||
- Optional emulation of flaws in older emulators for compatibility with older unofficial software
|
||||
- CPU, SA1, and SuperFX overclocking support
|
||||
- Frame advance support
|
||||
- Screenshot support
|
||||
- Cheat code search support
|
||||
- Movie recording and playback support
|
||||
- Rewind support
|
||||
- HiDPI support
|
||||
- Multi-monitor support
|
||||
- Turbo support for controller inputs
|
||||
|
||||
Links
|
||||
-----
|
||||
|
||||
- [Official website](https://bsnes.byuu.org)
|
||||
- [Official website](https://byuu.org/bsnes)
|
||||
- [Official git repository](https://github.com/byuu/bsnes)
|
||||
- [Developer resources](https://byuu.net)
|
||||
- [Donations](https://patreon.com/byuu)
|
||||
|
||||
Release Builds
|
||||
--------------
|
||||
|
||||
- [Windows binaries](https://byuu.itch.io/bsnes)
|
||||
|
||||
Nightly Builds
|
||||
--------------
|
||||
|
||||
- [Download](https://cirrus-ci.com/github/byuu/bsnes/master)
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
|
||||
Preview
|
||||
-------
|
||||
|
||||

|
||||

|
||||

|
||||
|
@@ -1,5 +1,5 @@
|
||||
database
|
||||
revision: 2018-09-20
|
||||
revision: 2020-01-01
|
||||
|
||||
//BS Memory (JPN)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
database
|
||||
revision: 2018-09-20
|
||||
revision: 2020-01-01
|
||||
|
||||
//Sufami Turbo (JPN)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
database
|
||||
revision: 2018-09-20
|
||||
revision: 2020-01-01
|
||||
|
||||
//Prototypes (JPN)
|
||||
|
||||
@@ -125,7 +125,7 @@ game
|
||||
//Super Famicom (JPN)
|
||||
|
||||
database
|
||||
revision: 2018-09-20
|
||||
revision: 2020-01-01
|
||||
|
||||
game
|
||||
sha256: 5c4e283efc338958b8dd45ebd6daf133a9eb280420a98e2e1df358ae0242c366
|
||||
@@ -1277,7 +1277,7 @@ game
|
||||
size: 0x100
|
||||
content: Boot
|
||||
manufacturer: Nintendo
|
||||
architecture: LR35902
|
||||
architecture: SM83
|
||||
identifier: SGB1
|
||||
|
||||
game
|
||||
@@ -1296,7 +1296,7 @@ game
|
||||
size: 0x100
|
||||
content: Boot
|
||||
manufacturer: Nintendo
|
||||
architecture: LR35902
|
||||
architecture: SM83
|
||||
identifier: SGB2
|
||||
oscillator
|
||||
frequency: 20971520
|
||||
@@ -1705,7 +1705,7 @@ game
|
||||
//Super Nintendo (ESP)
|
||||
|
||||
database
|
||||
revision: 2018-04-14
|
||||
revision: 2018-09-21
|
||||
|
||||
game
|
||||
sha256: bd5e7a6bc08f64d39c54204b82c6c156f144c03e13c890128588c5faa560659c
|
||||
@@ -1767,6 +1767,50 @@ game
|
||||
size: 0x200000
|
||||
content: Program
|
||||
|
||||
game
|
||||
sha256: bd7e98db82d6b52307be1f3e1fd171e1e7204dc1f8810a95ee2cc64757087e4a
|
||||
label: The Lost Vikings
|
||||
name: Lost Vikings, The
|
||||
region: SNSP-LV-ESP
|
||||
revision: SESP-LV-0
|
||||
board: SHVC-1A0N-20
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x100000
|
||||
content: Program
|
||||
|
||||
game
|
||||
sha256: 6eecabd46305ac95d9cf3a17e1392c24a1b68a7a313173ef0c5b5a3a24cf3353
|
||||
label: Lufia
|
||||
name: Lufia
|
||||
region: SNSP-ANIS-ESP
|
||||
revision: SPAL-ANIS-0
|
||||
board: SHVC-1A3M-30
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x300000
|
||||
content: Program
|
||||
memory
|
||||
type: RAM
|
||||
size: 0x2000
|
||||
content: Save
|
||||
|
||||
game
|
||||
sha256: d70bc7916ed5132c3b0053f2adbb5004d78ccb986210c9440fedf642cac68554
|
||||
label: MechWarrior
|
||||
name: MechWarrior
|
||||
region: SNSP-WM-ESP
|
||||
revision: SESP-WM-0
|
||||
board: SHVC-1A1M-10
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x100000
|
||||
content: Program
|
||||
memory
|
||||
type: RAM
|
||||
size: 0x800
|
||||
content: Save
|
||||
|
||||
game
|
||||
sha256: d2233d6310522bbf183b6ca9bbe3e2afaf24de0cc4304bff6d0d547d678aed6f
|
||||
label: Sonic Blast Man
|
||||
@@ -1779,6 +1823,18 @@ game
|
||||
size: 0x100000
|
||||
content: Program
|
||||
|
||||
game
|
||||
sha256: a20d346da18ddabf70dc43f5095c4189c4a646ca8e6d4ed6c68c20e380f50332
|
||||
label: Super Battletank 2
|
||||
name: Super Battletank 2
|
||||
region: SNSP-2X-ESP
|
||||
revision: SESP-2X-0
|
||||
board: SHVC-1A0N-20
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x200000
|
||||
content: Program
|
||||
|
||||
game
|
||||
sha256: 9eaf1c46d8a068c910d66f582e23b1155882ddfa4b9fd0813819fc5c008167e2
|
||||
label: Super James Pond
|
||||
@@ -1803,6 +1859,46 @@ game
|
||||
size: 0x100000
|
||||
content: Program
|
||||
|
||||
game
|
||||
sha256: 7f731f4bb620e682132660da39641dda5762211dca4732f8192dd2411211b822
|
||||
label: Terranigma
|
||||
name: Terranigma
|
||||
region: SNSP-AQTS-ESP
|
||||
revision: SPAL-AQTS-0
|
||||
board: SHVC-1J3M-20
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x400000
|
||||
content: Program
|
||||
memory
|
||||
type: RAM
|
||||
size: 0x2000
|
||||
content: Save
|
||||
|
||||
game
|
||||
sha256: 981128c93f0753dec7af29ec084f13e704cc5d02414be55bb477fc4b2fef5e58
|
||||
label: Tiny Toon Adventures: Buster Busts Loose!
|
||||
name: Tiny Toon Adventures - Buster Busts Loose!
|
||||
region: SNSP-TA-ESP
|
||||
revision: SESP-TA-0
|
||||
board: SHVC-1A0N-20
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x100000
|
||||
content: Program
|
||||
|
||||
game
|
||||
sha256: ce2445ecd0a43f6025dc80857d91dae7c46d33f7821bf98232c2894ca1959da2
|
||||
label: Turn and Burn: No-Fly Zone
|
||||
name: Turn and Burn - No-Fly Zone
|
||||
region: SNSP-ZN-ESP
|
||||
revision: SESP-ZN-0
|
||||
board: SHVC-1A0N-20
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x200000
|
||||
content: Program
|
||||
|
||||
game
|
||||
sha256: 9ed876a632aa699047e9efba8a64ab57abc55086a0aab6b5fa67d87ea4647f3f
|
||||
label: Whirlo
|
||||
@@ -1846,7 +1942,7 @@ game
|
||||
//Super Nintendo (EUR)
|
||||
|
||||
database
|
||||
revision: 2018-05-06
|
||||
revision: 2018-09-21
|
||||
|
||||
game
|
||||
sha256: ec3e81d628a293514e303b44e3b1ac03461ddd1da32764b10b7fab1e507602df
|
||||
@@ -2904,6 +3000,18 @@ game
|
||||
size: 0x100000
|
||||
content: Program
|
||||
|
||||
game
|
||||
sha256: 4ad736a9e1c7f34740afaa7777b8f1a31da4bb4a021e7ae341d1dafd74fa0acc
|
||||
label: True Lies
|
||||
name: True Lies
|
||||
region: SNSP-ATLP-EUR
|
||||
revision: SPAL-ATLP-0
|
||||
board: SHVC-1A0N-30
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x200000
|
||||
content: Program
|
||||
|
||||
game
|
||||
sha256: dbf11d4c77b9aa3416f687201d57d71a23bb8fb0b8fe5e9e8212db3fac036631
|
||||
label: Turbo Toons
|
||||
@@ -2942,7 +3050,7 @@ game
|
||||
|
||||
game
|
||||
sha256: 1217ddf2fe475661a54f50e111864102faf854397ce5aceea4297204ebd6cbb6
|
||||
label: Val d'isére Championship
|
||||
label: Val d'isère Championship
|
||||
name: Val d'isere Championship
|
||||
region: SNSP-8Z-EUR
|
||||
revision: SPAL-8Z-0
|
||||
@@ -3016,7 +3124,7 @@ game
|
||||
//Super Nintendo (FAH)
|
||||
|
||||
database
|
||||
revision: 2018-04-14
|
||||
revision: 2018-09-21
|
||||
|
||||
game
|
||||
sha256: 0aafd04a43ae29266e43920a7f9954d4a49f6fe43a5abffecc9c2fd5ad7d6cea
|
||||
@@ -3110,6 +3218,18 @@ game
|
||||
size: 0x800
|
||||
content: Save
|
||||
|
||||
game
|
||||
sha256: 826a328f401cdf5a9ee87aaa7a2784cbb21813b165a6c7ca3f702fe6ba8c0804
|
||||
label: Eric Cantona: Football Challenge
|
||||
name: Eric Cantona - Football Challenge
|
||||
region: SNSP-EC-FAH
|
||||
revision: SPAL-EC-0
|
||||
board: SHVC-1A0N-20
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x80000
|
||||
content: Program
|
||||
|
||||
game
|
||||
sha256: ce09743d44a54f64862d8c53c11c2c84f2f861ec74c778bd8b05b0a3b07708d6
|
||||
label: FIFA International Soccer
|
||||
@@ -3423,10 +3543,22 @@ game
|
||||
size: 0x180000
|
||||
content: Program
|
||||
|
||||
game
|
||||
sha256: 5a9103b04b9246f63af9018cbbd7934c6b79076dd9b0062887bd16077cd37c81
|
||||
label: Val d'Isère Championship
|
||||
name: Val d'Isere Championship
|
||||
region: SNSP-8V-FAH
|
||||
revision: SPAL-8V-0
|
||||
board: SHVC-1A0N-20
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x100000
|
||||
content: Program
|
||||
|
||||
//Super Nintendo (FRA)
|
||||
|
||||
database
|
||||
revision: 2018-04-14
|
||||
revision: 2018-09-21
|
||||
|
||||
game
|
||||
sha256: 65df600780021f13ced52e7fbc507b7b2e6491b2c5c25fe78d0515dcbe669403
|
||||
@@ -3660,6 +3792,22 @@ game
|
||||
size: 0x180000
|
||||
content: Program
|
||||
|
||||
game
|
||||
sha256: b730adcbb34a19f8fd1c2abe27455cc3256329a9b8a021291e3009ea33004127
|
||||
label: Secret of Mana
|
||||
name: Secret of Mana
|
||||
region: SNSP-K2-FRA
|
||||
revision: SFRA-K2-1
|
||||
board: SHVC-1J3M-11
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x200000
|
||||
content: Program
|
||||
memory
|
||||
type: RAM
|
||||
size: 0x2000
|
||||
content: Save
|
||||
|
||||
game
|
||||
sha256: f73e6da9e979c839c7c22ec487bea6667d3e65e7d8f9fcc97a2bcdeb4487cddf
|
||||
label: SimCity
|
||||
@@ -3676,6 +3824,38 @@ game
|
||||
size: 0x8000
|
||||
content: Save
|
||||
|
||||
game
|
||||
sha256: 1f226553ba05fe738d085a88154469bbc9f9058f7dfc320a327259d84ae5f393
|
||||
label: Soul Blazer
|
||||
name: Soul Blazer
|
||||
region: SNSP-SO-FRA
|
||||
revision: SFRA-SO-0
|
||||
board: SHVC-1J3M-20
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x100000
|
||||
content: Program
|
||||
memory
|
||||
type: RAM
|
||||
size: 0x2000
|
||||
content: Save
|
||||
|
||||
game
|
||||
sha256: 5d0a234a2fcb343d169206d9d7d578507c44f800ead9cc9ccfa0b1d4cb1cc9e5
|
||||
label: Terranigma
|
||||
name: Terranigma
|
||||
region: SNSP-AQTF-FRA
|
||||
revision: SPAL-AQTF-0
|
||||
board: SHVC-1J3M-20
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x400000
|
||||
content: Program
|
||||
memory
|
||||
type: RAM
|
||||
size: 0x2000
|
||||
content: Save
|
||||
|
||||
//Super Nintendo (FRG)
|
||||
|
||||
database
|
||||
@@ -3762,7 +3942,23 @@ game
|
||||
//Super Nintendo (ITA)
|
||||
|
||||
database
|
||||
revision: 2018-04-14
|
||||
revision: 2018-09-21
|
||||
|
||||
game
|
||||
sha256: deab7aad7c168423e43eae14e9e31efa29c7341ab84f936be508911ce508b372
|
||||
label: MechWarrior
|
||||
name: MechWarrior
|
||||
region: SNSP-WM-ITA
|
||||
revision: SITA-WM-0
|
||||
board: SHVC-1A1M-01
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x100000
|
||||
content: Program
|
||||
memory
|
||||
type: RAM
|
||||
size: 0x800
|
||||
content: Save
|
||||
|
||||
game
|
||||
sha256: aafbae4c2a7a5a35c81a183df0470027b4b5690f836592af21c15af6b259328d
|
||||
@@ -3796,7 +3992,7 @@ game
|
||||
//Super Nintendo (NOE)
|
||||
|
||||
database
|
||||
revision: 2018-04-14
|
||||
revision: 2020-01-01
|
||||
|
||||
game
|
||||
sha256: b342d12d71729edebc1911725ea23d58c1a397b27253a5c8cd96cfb58af242a9
|
||||
@@ -4394,6 +4590,18 @@ game
|
||||
size: 0x80000
|
||||
content: Program
|
||||
|
||||
game
|
||||
sha256: 09299d142e485ba2fcdbd9b3a6d1a5acfbc7fc70b06cf22be28479686419a7a9
|
||||
label: Jimmy Connors Pro Tennis Tour
|
||||
name: Jimmy Connors Pro Tennis Tour
|
||||
region: SNSP-JC-NOE
|
||||
revision: SFRG-JC-0
|
||||
board: SHVC-1A0N-20
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x80000
|
||||
content: Program
|
||||
|
||||
game
|
||||
sha256: 74c55ea3c9733bf263628a260df7492fc840d7de1c3fceebb7bcf6d99a8c81d6
|
||||
label: Joe & Mac: Caveman Ninja
|
||||
@@ -4470,6 +4678,18 @@ game
|
||||
size: 0x100000
|
||||
content: Program
|
||||
|
||||
game
|
||||
sha256: 4a7444780a750f97943d974589586d4cf89d8957e396cc5a7ad565cd4c1b70a7
|
||||
label: The Legend of the Mystical Ninja
|
||||
name: Legend of the Mystical Ninja, The
|
||||
region: SNSP-GG-NOE
|
||||
revision: SFRG-GG-0
|
||||
board: SHVC-1A0N-20
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x100000
|
||||
content: Program
|
||||
|
||||
game
|
||||
sha256: 5ec66298ddb579b35cc5d3df5bfeeee05bdf71347565c7c5f5f3869bf4f1e469
|
||||
label: Looney Tunes Basketball
|
||||
@@ -4820,7 +5040,7 @@ game
|
||||
size: 0x100
|
||||
content: Boot
|
||||
manufacturer: Nintendo
|
||||
architecture: LR35902
|
||||
architecture: SM83
|
||||
identifier: SGB1
|
||||
|
||||
game
|
||||
@@ -4975,6 +5195,18 @@ game
|
||||
size: 0x100000
|
||||
content: Program
|
||||
|
||||
game
|
||||
sha256: 94e6fe78bb1a1d89ccfd74ad92e2a489f8e2e257d6dfe62404155741763f962f
|
||||
label: True Lies
|
||||
name: True Lies
|
||||
region: SNSP-ATLD-NOE
|
||||
revision: SPAL-ATLD-0
|
||||
board: SHVC-1A0N-30
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x200000
|
||||
content: Program
|
||||
|
||||
game
|
||||
sha256: a1105819d48c04d680c8292bbfa9abbce05224f1bc231afd66af43b7e0a1fd4e
|
||||
label: Unirally
|
||||
@@ -5054,7 +5286,7 @@ game
|
||||
//Super Nintendo (SCN)
|
||||
|
||||
database
|
||||
revision: 2018-04-14
|
||||
revision: 2018-09-21
|
||||
|
||||
game
|
||||
sha256: beb379ba48f63561c0f939ecd8f623ec06c1b5e06976eef9887e5c62f3df2766
|
||||
@@ -5080,6 +5312,22 @@ game
|
||||
size: 0x300000
|
||||
content: Program
|
||||
|
||||
game
|
||||
sha256: 4fb9eb8fa4d9c3a0b6c24bac5b0a0b0f079f083f5e6dfa937a161c8f4bcde853
|
||||
label: Shadowrun
|
||||
name: Shadowrun
|
||||
region: SNSP-WR-SCN
|
||||
revision: SSWE-WR-0
|
||||
board: SHVC-1A3M-20
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x100000
|
||||
content: Program
|
||||
memory
|
||||
type: RAM
|
||||
size: 0x2000
|
||||
content: Save
|
||||
|
||||
game
|
||||
sha256: e15247495311e91db9431d61777a264d4b42def011291d512b273fc8acd1cbfa
|
||||
label: Soul Blazer
|
||||
@@ -5096,6 +5344,18 @@ game
|
||||
size: 0x2000
|
||||
content: Save
|
||||
|
||||
game
|
||||
sha256: 687c4f9a14cc16605f5e92aa0fe33bf083fe8e39ba781676259fadf932480890
|
||||
label: Tintin i Tibet
|
||||
name: Tintin i Tibet
|
||||
region: SNSP-AT6X-SCN
|
||||
revision: SPAL-AT6X-0
|
||||
board: SHVC-2A0N-20
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x180000
|
||||
content: Program
|
||||
|
||||
game
|
||||
sha256: a6297356fb06f1575b432fae463171f53e3b786fd77b841557547a9117fb52fe
|
||||
label: X-Zone
|
||||
@@ -5761,7 +6021,7 @@ game
|
||||
//Super Nintendo (USA)
|
||||
|
||||
database
|
||||
revision: 2018-09-20
|
||||
revision: 2020-01-01
|
||||
|
||||
game
|
||||
sha256: 2ffe8828480f943056fb1ab5c3c84d48a0bf8cbe3ed7c9960b349b59adb07f3b
|
||||
@@ -6555,8 +6815,8 @@ game
|
||||
sha256: 6fa6b8a8804ff6544bdedf94339a86ba64ce0b6dbf059605abb1cd6f102d3483
|
||||
label: Bill Laimbeer's Combat Basketball
|
||||
name: Bill Laimbeer's Combat Basketball
|
||||
region: SNS-C8-USA
|
||||
revision: SNS-C8-0
|
||||
region: SNS-CB-USA
|
||||
revision: SNS-CB-0
|
||||
board: SHVC-1A3B-12
|
||||
memory
|
||||
type: ROM
|
||||
@@ -13628,7 +13888,7 @@ game
|
||||
size: 0x100
|
||||
content: Boot
|
||||
manufacturer: Nintendo
|
||||
architecture: LR35902
|
||||
architecture: SM83
|
||||
identifier: SGB1
|
||||
|
||||
game
|
||||
@@ -14516,8 +14776,8 @@ game
|
||||
sha256: 3cdebbd8adc4bb6773a7995f542fdac49adefca71cba583255a1c1bf37ac3946
|
||||
label: Tetris & Dr. Mario
|
||||
name: Tetris & Dr. Mario
|
||||
region: SNS-AFTE-USA
|
||||
revision: SNS-AFTE-0
|
||||
region: SNS-ATFE-USA
|
||||
revision: SNS-ATFE-0
|
||||
board: SHVC-1A0N-30
|
||||
memory
|
||||
type: ROM
|
@@ -2,6 +2,7 @@ target := bsnes
|
||||
binary := application
|
||||
build := performance
|
||||
openmp := true
|
||||
local := true
|
||||
flags += -I. -I..
|
||||
|
||||
# in order for this to work, obj/lzma.o must be omitted or bsnes will hang on startup.
|
||||
@@ -11,6 +12,11 @@ ifeq ($(profile),true)
|
||||
options += -pg
|
||||
endif
|
||||
|
||||
# binaries built with this flag are faster, but are not portable to multiple machines.
|
||||
ifeq ($(local),true)
|
||||
flags += -march=native
|
||||
endif
|
||||
|
||||
nall.path := ../nall
|
||||
include $(nall.path)/GNUmakefile
|
||||
|
||||
@@ -30,15 +36,12 @@ else ifeq ($(platform),macos)
|
||||
endif
|
||||
else ifneq ($(filter $(platform),linux bsd),)
|
||||
ifeq ($(binary),application)
|
||||
flags += -march=native
|
||||
options += -Wl,-export-dynamic
|
||||
options += -lX11 -lXext
|
||||
else ifeq ($(binary),library)
|
||||
flags += -fPIC
|
||||
options += -shared
|
||||
endif
|
||||
else
|
||||
$(error "unsupported platform")
|
||||
endif
|
||||
|
||||
objects := libco emulator filter lzma
|
||||
|
@@ -10,45 +10,45 @@ Audio::~Audio() {
|
||||
}
|
||||
|
||||
auto Audio::reset(Interface* interface) -> void {
|
||||
this->interface = interface;
|
||||
streams.reset();
|
||||
channels = 0;
|
||||
_interface = interface;
|
||||
_streams.reset();
|
||||
_channels = 0;
|
||||
}
|
||||
|
||||
auto Audio::setFrequency(double frequency) -> void {
|
||||
this->frequency = frequency;
|
||||
for(auto& stream : streams) {
|
||||
_frequency = frequency;
|
||||
for(auto& stream : _streams) {
|
||||
stream->setFrequency(stream->inputFrequency, frequency);
|
||||
}
|
||||
}
|
||||
|
||||
auto Audio::setVolume(double volume) -> void {
|
||||
this->volume = volume;
|
||||
_volume = volume;
|
||||
}
|
||||
|
||||
auto Audio::setBalance(double balance) -> void {
|
||||
this->balance = balance;
|
||||
_balance = balance;
|
||||
}
|
||||
|
||||
auto Audio::createStream(uint channels, double frequency) -> shared_pointer<Stream> {
|
||||
this->channels = max(this->channels, channels);
|
||||
_channels = max(_channels, channels);
|
||||
shared_pointer<Stream> stream = new Stream;
|
||||
stream->reset(channels, frequency, this->frequency);
|
||||
streams.append(stream);
|
||||
stream->reset(channels, frequency, _frequency);
|
||||
_streams.append(stream);
|
||||
return stream;
|
||||
}
|
||||
|
||||
auto Audio::process() -> void {
|
||||
while(streams) {
|
||||
for(auto& stream : streams) {
|
||||
while(_streams) {
|
||||
for(auto& stream : _streams) {
|
||||
if(!stream->pending()) return;
|
||||
}
|
||||
|
||||
double samples[channels];
|
||||
double samples[_channels];
|
||||
for(auto& sample : samples) sample = 0.0;
|
||||
|
||||
for(auto& stream : streams) {
|
||||
double buffer[channels];
|
||||
for(auto& stream : _streams) {
|
||||
double buffer[_channels];
|
||||
uint length = stream->read(buffer), offset = 0;
|
||||
|
||||
for(auto& sample : samples) {
|
||||
@@ -57,16 +57,16 @@ auto Audio::process() -> void {
|
||||
}
|
||||
}
|
||||
|
||||
for(auto c : range(channels)) {
|
||||
samples[c] = max(-1.0, min(+1.0, samples[c] * volume));
|
||||
for(auto c : range(_channels)) {
|
||||
samples[c] = max(-1.0, min(+1.0, samples[c] * _volume));
|
||||
}
|
||||
|
||||
if(channels == 2) {
|
||||
if(balance < 0.0) samples[1] *= 1.0 + balance;
|
||||
if(balance > 0.0) samples[0] *= 1.0 - balance;
|
||||
if(_channels == 2) {
|
||||
if(_balance < 0.0) samples[1] *= 1.0 + _balance;
|
||||
if(_balance > 0.0) samples[0] *= 1.0 - _balance;
|
||||
}
|
||||
|
||||
platform->audioFrame(samples, channels);
|
||||
platform->audioFrame(samples, _channels);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -17,6 +17,11 @@ struct Audio {
|
||||
~Audio();
|
||||
auto reset(Interface* interface) -> void;
|
||||
|
||||
inline auto channels() const -> uint { return _channels; }
|
||||
inline auto frequency() const -> double { return _frequency; }
|
||||
inline auto volume() const -> double { return _volume; }
|
||||
inline auto balance() const -> double { return _balance; }
|
||||
|
||||
auto setFrequency(double frequency) -> void;
|
||||
auto setVolume(double volume) -> void;
|
||||
auto setBalance(double balance) -> void;
|
||||
@@ -26,14 +31,14 @@ struct Audio {
|
||||
private:
|
||||
auto process() -> void;
|
||||
|
||||
Interface* interface = nullptr;
|
||||
vector<shared_pointer<Stream>> streams;
|
||||
Interface* _interface = nullptr;
|
||||
vector<shared_pointer<Stream>> _streams;
|
||||
|
||||
uint channels = 0;
|
||||
double frequency = 48000.0;
|
||||
uint _channels = 0;
|
||||
double _frequency = 48000.0;
|
||||
|
||||
double volume = 1.0;
|
||||
double balance = 0.0;
|
||||
double _volume = 1.0;
|
||||
double _balance = 0.0;
|
||||
|
||||
friend class Stream;
|
||||
};
|
||||
@@ -50,14 +55,16 @@ struct Filter {
|
||||
|
||||
struct Stream {
|
||||
auto reset(uint channels, double inputFrequency, double outputFrequency) -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
auto frequency() const -> double;
|
||||
auto setFrequency(double inputFrequency, maybe<double> outputFrequency = nothing) -> void;
|
||||
|
||||
auto addDCRemovalFilter() -> void;
|
||||
auto addLowPassFilter(double cutoffFrequency, Filter::Order order, uint passes = 1) -> void;
|
||||
auto addHighPassFilter(double cutoffFrequency, Filter::Order order, uint passes = 1) -> void;
|
||||
|
||||
auto pending() const -> bool;
|
||||
auto pending() const -> uint;
|
||||
auto read(double samples[]) -> uint;
|
||||
auto write(const double samples[]) -> void;
|
||||
|
||||
@@ -66,6 +73,8 @@ struct Stream {
|
||||
write(samples);
|
||||
}
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
struct Channel {
|
||||
vector<Filter> filters;
|
||||
|
@@ -9,6 +9,16 @@ auto Stream::reset(uint channelCount, double inputFrequency, double outputFreque
|
||||
setFrequency(inputFrequency, outputFrequency);
|
||||
}
|
||||
|
||||
auto Stream::reset() -> void {
|
||||
for(auto& channel : channels) {
|
||||
channel.resampler.reset(this->inputFrequency, this->outputFrequency);
|
||||
}
|
||||
}
|
||||
|
||||
auto Stream::frequency() const -> double {
|
||||
return inputFrequency;
|
||||
}
|
||||
|
||||
auto Stream::setFrequency(double inputFrequency, maybe<double> outputFrequency) -> void {
|
||||
this->inputFrequency = inputFrequency;
|
||||
if(outputFrequency) this->outputFrequency = outputFrequency();
|
||||
@@ -77,8 +87,9 @@ auto Stream::addHighPassFilter(double cutoffFrequency, Filter::Order order, uint
|
||||
}
|
||||
}
|
||||
|
||||
auto Stream::pending() const -> bool {
|
||||
return channels && channels[0].resampler.pending();
|
||||
auto Stream::pending() const -> uint {
|
||||
if(!channels) return 0;
|
||||
return channels[0].resampler.pending();
|
||||
}
|
||||
|
||||
auto Stream::read(double samples[]) -> uint {
|
||||
@@ -104,3 +115,11 @@ auto Stream::write(const double samples[]) -> void {
|
||||
|
||||
audio.process();
|
||||
}
|
||||
|
||||
auto Stream::serialize(serializer& s) -> void {
|
||||
for(auto& channel : channels) {
|
||||
channel.resampler.serialize(s);
|
||||
}
|
||||
s.real(inputFrequency);
|
||||
s.real(outputFrequency);
|
||||
}
|
||||
|
@@ -1,91 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
struct BitsCaptureLo{};
|
||||
struct BitsCaptureHi{};
|
||||
|
||||
struct BitsLo {
|
||||
const uint lo;
|
||||
inline BitsLo(uint lo) : lo(lo) {}
|
||||
};
|
||||
|
||||
struct BitsHi {
|
||||
const uint hi;
|
||||
inline BitsHi(uint hi) : hi(hi) {}
|
||||
};
|
||||
|
||||
struct BitsRange {
|
||||
const uint lo, hi;
|
||||
inline BitsRange(uint lo, uint hi) : lo(lo), hi(hi) {}
|
||||
};
|
||||
|
||||
inline auto operator*(BitsCaptureLo, uint lhs) { return BitsLo{lhs}; }
|
||||
inline auto operator*(uint rhs, BitsCaptureHi) { return BitsHi{rhs}; }
|
||||
inline auto operator-(const BitsLo& lhs, const BitsHi& rhs) { return BitsRange{lhs.lo, rhs.hi}; }
|
||||
|
||||
template<typename T> struct Bits {
|
||||
T& value;
|
||||
using type =
|
||||
conditional_t<sizeof(T) <= 1, uint8_t,
|
||||
conditional_t<sizeof(T) <= 2, uint16_t,
|
||||
conditional_t<sizeof(T) <= 4, uint32_t,
|
||||
conditional_t<sizeof(T) <= 8, uint64_t,
|
||||
void>>>>;
|
||||
const uint lo;
|
||||
const type mask;
|
||||
|
||||
inline Bits(T& value, BitsRange range) : value(value), lo(range.lo), mask(~0ull >> 64 - (range.hi - range.lo + 1) << range.lo) {}
|
||||
inline Bits(const Bits& source) { operator=((uint64_t)source); }
|
||||
inline auto operator=(const Bits& source) -> Bits& { return operator=((uint64_t)source); }
|
||||
|
||||
inline operator type() const {
|
||||
return (value & mask) >> lo;
|
||||
}
|
||||
|
||||
template<typename U> inline auto operator=(U source) -> Bits& {
|
||||
value = value & ~mask | source << lo & mask;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U> inline auto operator&=(U source) -> Bits& {
|
||||
value = value & (~mask | source << lo & mask);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U> inline auto operator^=(U source) -> Bits& {
|
||||
value = value ^ source << lo & mask;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U> inline auto operator|=(U source) -> Bits& {
|
||||
value = value | source << lo & mask;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> struct Bits<const T> {
|
||||
T value;
|
||||
using type =
|
||||
conditional_t<sizeof(T) <= 1, uint8_t,
|
||||
conditional_t<sizeof(T) <= 2, uint16_t,
|
||||
conditional_t<sizeof(T) <= 4, uint32_t,
|
||||
conditional_t<sizeof(T) <= 8, uint64_t,
|
||||
void>>>>;
|
||||
const uint lo;
|
||||
const type mask;
|
||||
|
||||
inline Bits(const T& value, BitsRange range) : value(value), lo(range.lo), mask(~0ull >> 64 - (range.hi - range.lo + 1) << range.lo) {}
|
||||
Bits(const Bits& source) = delete;
|
||||
auto operator=(const Bits& source) -> Bits& = delete;
|
||||
|
||||
inline operator type() const {
|
||||
return (value & mask) >> lo;
|
||||
}
|
||||
};
|
||||
|
||||
#define bits(value,range) Bits<decltype(value)>{value,BitsCaptureLo{}*range*BitsCaptureHi{}}
|
||||
#define bit1(value,index) Bits<decltype(value)>{value,BitsRange{(uint)index,(uint)index}}
|
||||
#define bit8(value,index) Bits<decltype(value)>{value,BitsRange{(uint)index*8,(uint)index*8+7}}
|
||||
|
||||
#define cbits(value,range) Bits<const decltype(value)>{value,BitsCaptureLo{}*range*BitsCaptureHi{}}
|
||||
#define cbit1(value,index) Bits<const decltype(value)>{value,BitsRange{(uint)index,(uint)index}}
|
||||
#define cbit8(value,index) Bits<const decltype(value)>{value,BitsRange{(uint)index*8,(uint)index*8+7}}
|
@@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <libco/libco.h>
|
||||
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/adaptive-array.hpp>
|
||||
#include <nall/any.hpp>
|
||||
#include <nall/bit-field.hpp>
|
||||
#include <nall/chrono.hpp>
|
||||
#include <nall/dl.hpp>
|
||||
#include <nall/endian.hpp>
|
||||
@@ -21,9 +22,6 @@
|
||||
#include <nall/hash/sha256.hpp>
|
||||
using namespace nall;
|
||||
|
||||
#include <libco/libco.h>
|
||||
|
||||
#include <emulator/bits.hpp>
|
||||
#include <emulator/types.hpp>
|
||||
#include <emulator/memory/readable.hpp>
|
||||
#include <emulator/memory/writable.hpp>
|
||||
@@ -31,13 +29,13 @@ using namespace nall;
|
||||
|
||||
namespace Emulator {
|
||||
static const string Name = "bsnes";
|
||||
static const string Version = "108";
|
||||
static const string Version = "114";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "https://byuu.org";
|
||||
|
||||
//incremented only when serialization format changes
|
||||
static const string SerializerVersion = "108";
|
||||
static const string SerializerVersion = "112";
|
||||
|
||||
namespace Constants {
|
||||
namespace Colorburst {
|
||||
|
@@ -37,6 +37,7 @@ struct Game {
|
||||
string sha256;
|
||||
string label;
|
||||
string name;
|
||||
string title;
|
||||
string region;
|
||||
string revision;
|
||||
string board;
|
||||
@@ -50,6 +51,7 @@ auto Game::load(string_view text) -> void {
|
||||
sha256 = document["game/sha256"].text();
|
||||
label = document["game/label"].text();
|
||||
name = document["game/name"].text();
|
||||
title = document["game/title"].text();
|
||||
region = document["game/region"].text();
|
||||
revision = document["game/revision"].text();
|
||||
board = document["game/board"].text();
|
||||
|
@@ -61,6 +61,7 @@ struct Interface {
|
||||
virtual auto hashes() -> vector<string> { return {}; }
|
||||
virtual auto manifests() -> vector<string> { return {}; }
|
||||
virtual auto titles() -> vector<string> { return {}; }
|
||||
virtual auto title() -> string { return {}; }
|
||||
virtual auto load() -> bool { return false; }
|
||||
virtual auto save() -> void {}
|
||||
virtual auto unload() -> void {}
|
||||
@@ -80,7 +81,7 @@ struct Interface {
|
||||
virtual auto synchronize(uint64 timestamp = 0) -> void {}
|
||||
|
||||
//state functions
|
||||
virtual auto serialize() -> serializer { return {}; }
|
||||
virtual auto serialize(bool synchronize = true) -> serializer { return {}; }
|
||||
virtual auto unserialize(serializer&) -> bool { return false; }
|
||||
|
||||
//cheat functions
|
||||
@@ -100,6 +101,9 @@ struct Interface {
|
||||
|
||||
virtual auto frameSkip() -> uint { return 0; }
|
||||
virtual auto setFrameSkip(uint frameSkip) -> void {}
|
||||
|
||||
virtual auto runAhead() -> bool { return false; }
|
||||
virtual auto setRunAhead(bool runAhead) -> void {}
|
||||
};
|
||||
|
||||
}
|
||||
|
@@ -24,14 +24,14 @@ struct Readable {
|
||||
memory::fill<T>(self.data, self.mask + 1, fill);
|
||||
}
|
||||
|
||||
inline auto load(vfs::shared::file fp) -> void {
|
||||
inline auto load(shared_pointer<vfs::file> fp) -> void {
|
||||
fp->read(self.data, min(fp->size(), self.size * sizeof(T)));
|
||||
for(uint address = self.size; address <= self.mask; address++) {
|
||||
self.data[address] = self.data[mirror(address, self.size)];
|
||||
}
|
||||
}
|
||||
|
||||
inline auto save(vfs::shared::file fp) -> void {
|
||||
inline auto save(shared_pointer<vfs::file> fp) -> void {
|
||||
fp->write(self.data, self.size * sizeof(T));
|
||||
}
|
||||
|
||||
|
@@ -24,14 +24,14 @@ struct Writable {
|
||||
memory::fill<T>(self.data, self.mask + 1, fill);
|
||||
}
|
||||
|
||||
inline auto load(vfs::shared::file fp) -> void {
|
||||
inline auto load(shared_pointer<vfs::file> fp) -> void {
|
||||
fp->read(self.data, min(fp->size(), self.size * sizeof(T)));
|
||||
for(uint address = self.size; address <= self.mask; address++) {
|
||||
self.data[address] = self.data[mirror(address, self.size)];
|
||||
}
|
||||
}
|
||||
|
||||
inline auto save(vfs::shared::file fp) -> void {
|
||||
inline auto save(shared_pointer<vfs::file> fp) -> void {
|
||||
fp->write(self.data, self.size * sizeof(T));
|
||||
}
|
||||
|
||||
|
@@ -14,7 +14,7 @@ struct Platform {
|
||||
};
|
||||
|
||||
virtual auto path(uint id) -> string { return ""; }
|
||||
virtual auto open(uint id, string name, vfs::file::mode mode, bool required = false) -> vfs::shared::file { return {}; }
|
||||
virtual auto open(uint id, string name, vfs::file::mode mode, bool required = false) -> shared_pointer<vfs::file> { return {}; }
|
||||
virtual auto load(uint id, string name, string type, vector<string> options = {}) -> Load { return {}; }
|
||||
virtual auto videoFrame(const uint16* data, uint pitch, uint width, uint height, uint scale) -> void {}
|
||||
virtual auto audioFrame(const float* samples, uint channels) -> void {}
|
||||
|
@@ -65,10 +65,10 @@ struct Random {
|
||||
if((random() & 1) == 0) hivalue = ~lovalue;
|
||||
|
||||
for(uint32 address : range(size)) {
|
||||
uint8 value = bit1(address,lobit) ? lovalue : hivalue;
|
||||
if(bit1(address,hibit)) value = ~value;
|
||||
if((random() & 511) == 0) bit1(value,random() & 7) ^= 1;
|
||||
if((random() & 2047) == 0) bit1(value,random() & 7) ^= 1;
|
||||
uint8 value = (address & 1ull << lobit) ? lovalue : hivalue;
|
||||
if((address & 1ull << hibit)) value = ~value;
|
||||
if((random() & 511) == 0) value ^= 1 << (random() & 7);
|
||||
if((random() & 2047) == 0) value ^= 1 << (random() & 7);
|
||||
data[address] = value;
|
||||
}
|
||||
}
|
||||
|
@@ -1,98 +0,0 @@
|
||||
#pragma once
|
||||
#define uintmax uint64
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
struct Scheduler {
|
||||
enum class Mode : uint {
|
||||
Run,
|
||||
SynchronizeMaster,
|
||||
SynchronizeSlave,
|
||||
};
|
||||
|
||||
enum class Event : uint {
|
||||
Step,
|
||||
Frame,
|
||||
Synchronize,
|
||||
};
|
||||
|
||||
inline auto synchronizing() const -> bool { return _mode == Mode::SynchronizeSlave; }
|
||||
|
||||
auto reset() -> void {
|
||||
_host = co_active();
|
||||
_threads.reset();
|
||||
}
|
||||
|
||||
auto primary(Thread& thread) -> void {
|
||||
_master = _resume = thread.handle();
|
||||
uintmax clock = 0;
|
||||
for(auto& thread : _threads) {
|
||||
thread->_clock = clock++; //this bias prioritizes threads appended earlier first
|
||||
}
|
||||
}
|
||||
|
||||
auto append(Thread& thread) -> bool {
|
||||
if(_threads.find(&thread)) return false;
|
||||
thread._clock = _threads.size();
|
||||
return _threads.append(&thread), true;
|
||||
}
|
||||
|
||||
auto remove(Thread& thread) -> bool {
|
||||
if(auto offset = _threads.find(&thread)) return _threads.remove(*offset), true;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto enter(Mode mode = Mode::Run) -> Event {
|
||||
_mode = mode;
|
||||
_host = co_active();
|
||||
co_switch(_resume);
|
||||
return _event;
|
||||
}
|
||||
|
||||
inline auto resume(Thread& thread) -> void {
|
||||
if(_mode != Mode::SynchronizeSlave) co_switch(thread.handle());
|
||||
}
|
||||
|
||||
auto exit(Event event) -> void {
|
||||
uintmax minimum = -1;
|
||||
for(auto thread : _threads) {
|
||||
if(thread->_clock < minimum) minimum = thread->_clock;
|
||||
}
|
||||
for(auto thread : _threads) {
|
||||
thread->_clock -= minimum;
|
||||
}
|
||||
|
||||
_event = event;
|
||||
_resume = co_active();
|
||||
co_switch(_host);
|
||||
}
|
||||
|
||||
inline auto synchronize(Thread& thread) -> void {
|
||||
if(thread.handle() == _master) {
|
||||
while(enter(Mode::SynchronizeMaster) != Event::Synchronize);
|
||||
} else {
|
||||
_resume = thread.handle();
|
||||
while(enter(Mode::SynchronizeSlave) != Event::Synchronize);
|
||||
}
|
||||
}
|
||||
|
||||
inline auto synchronize() -> void {
|
||||
if(co_active() == _master) {
|
||||
if(_mode == Mode::SynchronizeMaster) return exit(Event::Synchronize);
|
||||
} else {
|
||||
if(_mode == Mode::SynchronizeSlave) return exit(Event::Synchronize);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
cothread_t _host = nullptr; //program thread (used to exit scheduler)
|
||||
cothread_t _resume = nullptr; //resume thread (used to enter scheduler)
|
||||
cothread_t _master = nullptr; //primary thread (used to synchronize components)
|
||||
Mode _mode = Mode::Run;
|
||||
Event _event = Event::Step;
|
||||
vector<Thread*> _threads;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#undef uintmax
|
@@ -1,64 +0,0 @@
|
||||
#pragma once
|
||||
#define uintmax uint64
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
struct Thread {
|
||||
enum : uintmax { Second = (uintmax)-1 >> 1 };
|
||||
|
||||
virtual ~Thread() {
|
||||
if(_handle) co_delete(_handle);
|
||||
}
|
||||
|
||||
inline auto active() const { return co_active() == _handle; }
|
||||
inline auto handle() const { return _handle; }
|
||||
inline auto frequency() const { return _frequency; }
|
||||
inline auto scalar() const { return _scalar; }
|
||||
inline auto clock() const { return _clock; }
|
||||
|
||||
auto setHandle(cothread_t handle) -> void {
|
||||
_handle = handle;
|
||||
}
|
||||
|
||||
auto setFrequency(double frequency) -> void {
|
||||
_frequency = frequency + 0.5;
|
||||
_scalar = Second / _frequency;
|
||||
}
|
||||
|
||||
auto setScalar(uintmax scalar) -> void {
|
||||
_scalar = scalar;
|
||||
}
|
||||
|
||||
auto setClock(uintmax clock) -> void {
|
||||
_clock = clock;
|
||||
}
|
||||
|
||||
auto create(auto (*entrypoint)() -> void, double frequency) -> void {
|
||||
if(_handle) co_delete(_handle);
|
||||
_handle = co_create(64 * 1024 * sizeof(void*), entrypoint);
|
||||
setFrequency(frequency);
|
||||
setClock(0);
|
||||
}
|
||||
|
||||
inline auto step(uint clocks) -> void {
|
||||
_clock += _scalar * clocks;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
s.integer(_frequency);
|
||||
s.integer(_scalar);
|
||||
s.integer(_clock);
|
||||
}
|
||||
|
||||
protected:
|
||||
cothread_t _handle = nullptr;
|
||||
uintmax _frequency = 0;
|
||||
uintmax _scalar = 0;
|
||||
uintmax _clock = 0;
|
||||
|
||||
friend class Scheduler;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#undef uintmax
|
@@ -8,7 +8,6 @@ using int5 = nall::Integer< 5>;
|
||||
using int6 = nall::Integer< 6>;
|
||||
using int7 = nall::Integer< 7>;
|
||||
using int8 = int8_t;
|
||||
//using int8 = nall::Integer< 8>;
|
||||
using int9 = nall::Integer< 9>;
|
||||
using int10 = nall::Integer<10>;
|
||||
using int11 = nall::Integer<11>;
|
||||
@@ -17,7 +16,6 @@ using int13 = nall::Integer<13>;
|
||||
using int14 = nall::Integer<14>;
|
||||
using int15 = nall::Integer<15>;
|
||||
using int16 = int16_t;
|
||||
//using int16 = nall::Integer<16>;
|
||||
using int17 = nall::Integer<17>;
|
||||
using int18 = nall::Integer<18>;
|
||||
using int19 = nall::Integer<19>;
|
||||
@@ -34,40 +32,8 @@ using int29 = nall::Integer<29>;
|
||||
using int30 = nall::Integer<30>;
|
||||
using int31 = nall::Integer<31>;
|
||||
using int32 = int32_t;
|
||||
//using int32 = nall::Integer<32>;
|
||||
using int33 = nall::Integer<33>;
|
||||
using int34 = nall::Integer<34>;
|
||||
using int35 = nall::Integer<35>;
|
||||
using int36 = nall::Integer<36>;
|
||||
using int37 = nall::Integer<37>;
|
||||
using int38 = nall::Integer<38>;
|
||||
using int39 = nall::Integer<39>;
|
||||
using int40 = nall::Integer<40>;
|
||||
using int41 = nall::Integer<41>;
|
||||
using int42 = nall::Integer<42>;
|
||||
using int43 = nall::Integer<43>;
|
||||
using int44 = nall::Integer<44>;
|
||||
using int45 = nall::Integer<45>;
|
||||
using int46 = nall::Integer<46>;
|
||||
using int47 = nall::Integer<47>;
|
||||
using int48 = nall::Integer<48>;
|
||||
using int49 = nall::Integer<49>;
|
||||
using int50 = nall::Integer<50>;
|
||||
using int51 = nall::Integer<51>;
|
||||
using int52 = nall::Integer<52>;
|
||||
using int53 = nall::Integer<53>;
|
||||
using int54 = nall::Integer<54>;
|
||||
using int55 = nall::Integer<55>;
|
||||
using int56 = nall::Integer<56>;
|
||||
using int57 = nall::Integer<57>;
|
||||
using int58 = nall::Integer<58>;
|
||||
using int59 = nall::Integer<59>;
|
||||
using int60 = nall::Integer<60>;
|
||||
using int61 = nall::Integer<61>;
|
||||
using int62 = nall::Integer<62>;
|
||||
using int63 = nall::Integer<63>;
|
||||
using int48 = nall::Integer<48>; //Cx4
|
||||
using int64 = int64_t;
|
||||
//using int64 = nall::Integer<64>;
|
||||
|
||||
using uint1 = nall::Natural< 1>;
|
||||
using uint2 = nall::Natural< 2>;
|
||||
@@ -77,7 +43,6 @@ using uint5 = nall::Natural< 5>;
|
||||
using uint6 = nall::Natural< 6>;
|
||||
using uint7 = nall::Natural< 7>;
|
||||
using uint8 = uint8_t;
|
||||
//using uint8 = nall::Natural< 8>;
|
||||
using uint9 = nall::Natural< 9>;
|
||||
using uint10 = nall::Natural<10>;
|
||||
using uint11 = nall::Natural<11>;
|
||||
@@ -86,7 +51,6 @@ using uint13 = nall::Natural<13>;
|
||||
using uint14 = nall::Natural<14>;
|
||||
using uint15 = nall::Natural<15>;
|
||||
using uint16 = uint16_t;
|
||||
//using uint16 = nall::Natural<16>;
|
||||
using uint17 = nall::Natural<17>;
|
||||
using uint18 = nall::Natural<18>;
|
||||
using uint19 = nall::Natural<19>;
|
||||
@@ -103,37 +67,6 @@ using uint29 = nall::Natural<29>;
|
||||
using uint30 = nall::Natural<30>;
|
||||
using uint31 = nall::Natural<31>;
|
||||
using uint32 = uint32_t;
|
||||
//using uint32 = nall::Natural<32>;
|
||||
using uint33 = nall::Natural<33>;
|
||||
using uint34 = nall::Natural<34>;
|
||||
using uint35 = nall::Natural<35>;
|
||||
using uint36 = nall::Natural<36>;
|
||||
using uint37 = nall::Natural<37>;
|
||||
using uint38 = nall::Natural<38>;
|
||||
using uint39 = nall::Natural<39>;
|
||||
using uint40 = nall::Natural<40>;
|
||||
using uint41 = nall::Natural<41>;
|
||||
using uint42 = nall::Natural<42>;
|
||||
using uint43 = nall::Natural<43>;
|
||||
using uint44 = nall::Natural<44>;
|
||||
using uint45 = nall::Natural<45>;
|
||||
using uint46 = nall::Natural<46>;
|
||||
using uint47 = nall::Natural<47>;
|
||||
using uint48 = nall::Natural<48>;
|
||||
using uint49 = nall::Natural<49>;
|
||||
using uint50 = nall::Natural<50>;
|
||||
using uint51 = nall::Natural<51>;
|
||||
using uint52 = nall::Natural<52>;
|
||||
using uint53 = nall::Natural<53>;
|
||||
using uint54 = nall::Natural<54>;
|
||||
using uint55 = nall::Natural<55>;
|
||||
using uint56 = nall::Natural<56>;
|
||||
using uint57 = nall::Natural<57>;
|
||||
using uint58 = nall::Natural<58>;
|
||||
using uint59 = nall::Natural<59>;
|
||||
using uint60 = nall::Natural<60>;
|
||||
using uint61 = nall::Natural<61>;
|
||||
using uint62 = nall::Natural<62>;
|
||||
using uint63 = nall::Natural<63>;
|
||||
using uint40 = nall::Natural<40>; //SA1
|
||||
using uint48 = nall::Natural<48>; //Cx4
|
||||
using uint64 = uint64_t;
|
||||
//using uint64 = nall::Natural<64>;
|
||||
|
@@ -48,6 +48,23 @@ bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index)
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint8_t agb_bias_for_channel(GB_gameboy_t *gb, unsigned index)
|
||||
{
|
||||
if (!gb->apu.is_active[index]) return 0;
|
||||
|
||||
switch (index) {
|
||||
case GB_SQUARE_1:
|
||||
return gb->apu.square_channels[GB_SQUARE_1].current_volume;
|
||||
case GB_SQUARE_2:
|
||||
return gb->apu.square_channels[GB_SQUARE_2].current_volume;
|
||||
case GB_WAVE:
|
||||
return 0;
|
||||
case GB_NOISE:
|
||||
return gb->apu.noise_channel.current_volume;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void update_sample(GB_gameboy_t *gb, unsigned index, int8_t value, unsigned cycles_offset)
|
||||
{
|
||||
if (gb->model >= GB_MODEL_AGB) {
|
||||
@@ -66,15 +83,17 @@ static void update_sample(GB_gameboy_t *gb, unsigned index, int8_t value, unsign
|
||||
}
|
||||
|
||||
GB_sample_t output;
|
||||
uint8_t bias = agb_bias_for_channel(gb, index);
|
||||
|
||||
if (gb->io_registers[GB_IO_NR51] & (1 << index)) {
|
||||
output.right = (0xf - value * 2) * right_volume;
|
||||
output.right = (0xf - value * 2 + bias) * right_volume;
|
||||
}
|
||||
else {
|
||||
output.right = 0xf * right_volume;
|
||||
}
|
||||
|
||||
if (gb->io_registers[GB_IO_NR51] & (0x10 << index)) {
|
||||
output.left = (0xf - value * 2) * left_volume;
|
||||
output.left = (0xf - value * 2 + bias) * left_volume;
|
||||
}
|
||||
else {
|
||||
output.left = 0xf * left_volume;
|
||||
@@ -681,6 +700,21 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
||||
case GB_IO_NR14:
|
||||
case GB_IO_NR24: {
|
||||
unsigned index = reg == GB_IO_NR24? GB_SQUARE_2: GB_SQUARE_1;
|
||||
|
||||
/* TODO: When the sample length changes right before being updated, the countdown should change to the
|
||||
old length, but the current sample should not change. Because our write timing isn't accurate to
|
||||
the T-cycle, we hack around it by stepping the sample index backwards. */
|
||||
if ((value & 0x80) == 0 && gb->apu.is_active[index]) {
|
||||
/* On an AGB, as well as on CGB C and earlier (TODO: Tested: 0, B and C), it behaves slightly different on
|
||||
double speed. */
|
||||
if (gb->model == GB_MODEL_CGB_E /* || gb->model == GB_MODEL_CGB_D */ || gb->apu.square_channels[index].sample_countdown & 1) {
|
||||
if (gb->apu.square_channels[index].sample_countdown >> 1 == (gb->apu.square_channels[index].sample_length ^ 0x7FF)) {
|
||||
gb->apu.square_channels[index].current_sample_index--;
|
||||
gb->apu.square_channels[index].current_sample_index &= 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gb->apu.square_channels[index].sample_length &= 0xFF;
|
||||
gb->apu.square_channels[index].sample_length |= (value & 7) << 8;
|
||||
if (index == GB_SQUARE_1) {
|
||||
@@ -970,9 +1004,23 @@ void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate)
|
||||
if (sample_rate) {
|
||||
gb->apu_output.highpass_rate = pow(0.999958, GB_get_clock_rate(gb) / (double)sample_rate);
|
||||
}
|
||||
gb->apu_output.rate_set_in_clocks = false;
|
||||
GB_apu_update_cycles_per_sample(gb);
|
||||
}
|
||||
|
||||
void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample)
|
||||
{
|
||||
|
||||
if (cycles_per_sample == 0) {
|
||||
GB_set_sample_rate(gb, 0);
|
||||
return;
|
||||
}
|
||||
gb->apu_output.cycles_per_sample = cycles_per_sample;
|
||||
gb->apu_output.sample_rate = GB_get_clock_rate(gb) / cycles_per_sample * 2;
|
||||
gb->apu_output.highpass_rate = pow(0.999958, cycles_per_sample);
|
||||
gb->apu_output.rate_set_in_clocks = true;
|
||||
}
|
||||
|
||||
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback)
|
||||
{
|
||||
gb->apu_output.sample_callback = callback;
|
||||
@@ -985,6 +1033,7 @@ void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode)
|
||||
|
||||
void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb)
|
||||
{
|
||||
if (gb->apu_output.rate_set_in_clocks) return;
|
||||
if (gb->apu_output.sample_rate) {
|
||||
gb->apu_output.cycles_per_sample = 2 * GB_get_clock_rate(gb) / (double)gb->apu_output.sample_rate; /* 2 * because we use 8MHz units */
|
||||
}
|
||||
|
@@ -143,9 +143,12 @@ typedef struct {
|
||||
GB_double_sample_t highpass_diff;
|
||||
|
||||
GB_sample_callback_t sample_callback;
|
||||
|
||||
bool rate_set_in_clocks;
|
||||
} GB_apu_output_t;
|
||||
|
||||
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate);
|
||||
void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample); /* Cycles are in 8MHz units */
|
||||
void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode);
|
||||
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback);
|
||||
#ifdef GB_INTERNAL
|
||||
|
@@ -127,13 +127,13 @@ static void display_vblank(GB_gameboy_t *gb)
|
||||
if (GB_is_hle_sgb(gb)) {
|
||||
GB_sgb_render(gb);
|
||||
}
|
||||
|
||||
|
||||
if (gb->turbo) {
|
||||
if (GB_timing_sync_turbo(gb)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!gb->disable_rendering && ((!(gb->io_registers[GB_IO_LCDC] & 0x80) || gb->stopped) || gb->frame_skip_state == GB_FRAMESKIP_LCD_TURNED_ON)) {
|
||||
/* LCD is off, set screen to white or black (if LCD is on in stop mode) */
|
||||
if (gb->sgb) {
|
||||
@@ -162,9 +162,20 @@ static inline uint8_t scale_channel(uint8_t x)
|
||||
|
||||
static inline uint8_t scale_channel_with_curve(uint8_t x)
|
||||
{
|
||||
return (uint8_t[]){0,2,4,7,12,18,25,34,42,52,62,73,85,97,109,121,134,146,158,170,182,193,203,213,221,230,237,243,248,251,253,255,}[x];
|
||||
return (uint8_t[]){0,2,4,7,12,18,25,34,42,52,62,73,85,97,109,121,134,146,158,170,182,193,203,213,221,230,237,243,248,251,253,255}[x];
|
||||
}
|
||||
|
||||
static inline uint8_t scale_channel_with_curve_agb(uint8_t x)
|
||||
{
|
||||
return (uint8_t[]){0,2,5,10,15,20,26,32,38,45,52,60,68,76,84,92,101,110,119,128,138,148,158,168,178,189,199,210,221,232,244,255}[x];
|
||||
}
|
||||
|
||||
static inline uint8_t scale_channel_with_curve_sgb(uint8_t x)
|
||||
{
|
||||
return (uint8_t[]){0,2,5,9,15,20,27,34,42,50,58,67,76,85,94,104,114,123,133,143,153,163,173,182,192,202,211,220,229,238,247,255}[x];
|
||||
}
|
||||
|
||||
|
||||
uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color)
|
||||
{
|
||||
uint8_t r = (color) & 0x1F;
|
||||
@@ -177,13 +188,29 @@ uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color)
|
||||
b = scale_channel(b);
|
||||
}
|
||||
else {
|
||||
r = scale_channel_with_curve(r);
|
||||
g = scale_channel_with_curve(g);
|
||||
b = scale_channel_with_curve(b);
|
||||
if (GB_is_sgb(gb)) {
|
||||
return gb->rgb_encode_callback(gb,
|
||||
scale_channel_with_curve_sgb(r),
|
||||
scale_channel_with_curve_sgb(g),
|
||||
scale_channel_with_curve_sgb(b));
|
||||
}
|
||||
bool agb = gb->model == GB_MODEL_AGB;
|
||||
r = agb? scale_channel_with_curve_agb(r) : scale_channel_with_curve(r);
|
||||
g = agb? scale_channel_with_curve_agb(g) : scale_channel_with_curve(g);
|
||||
b = agb? scale_channel_with_curve_agb(b) : scale_channel_with_curve(b);
|
||||
|
||||
if (gb->color_correction_mode != GB_COLOR_CORRECTION_CORRECT_CURVES) {
|
||||
uint8_t new_g = (g * 3 + b) / 4;
|
||||
uint8_t new_r = r, new_b = b;
|
||||
uint8_t new_r, new_g, new_b;
|
||||
if (agb) {
|
||||
new_r = (r * 7 + g * 1) / 8;
|
||||
new_g = (g * 3 + b * 1) / 4;
|
||||
new_b = (b * 7 + r * 1) / 8;
|
||||
}
|
||||
else {
|
||||
new_g = (g * 3 + b) / 4;
|
||||
new_r = r;
|
||||
new_b = b;
|
||||
}
|
||||
if (gb->color_correction_mode == GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS) {
|
||||
uint8_t old_max = MAX(r, MAX(g, b));
|
||||
uint8_t new_max = MAX(new_r, MAX(new_g, new_b));
|
||||
@@ -200,7 +227,7 @@ uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color)
|
||||
if (new_min != 0xff) {
|
||||
new_r = 0xff - (0xff - new_r) * (0xff - old_min) / (0xff - new_min);
|
||||
new_g = 0xff - (0xff - new_g) * (0xff - old_min) / (0xff - new_min);
|
||||
new_b = 0xff - (0xff - new_b) * (0xff - old_min) / (0xff - new_min);;
|
||||
new_b = 0xff - (0xff - new_b) * (0xff - old_min) / (0xff - new_min);
|
||||
}
|
||||
}
|
||||
r = new_r;
|
||||
@@ -377,7 +404,6 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
|
||||
}
|
||||
|
||||
uint8_t icd_pixel = 0;
|
||||
|
||||
{
|
||||
uint8_t pixel = bg_enabled? fifo_item->pixel : 0;
|
||||
if (pixel && bg_priority) {
|
||||
@@ -394,7 +420,6 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
|
||||
else if (gb->model & GB_MODEL_NO_SFC_BIT) {
|
||||
if (gb->icd_pixel_callback) {
|
||||
icd_pixel = pixel;
|
||||
//gb->icd_pixel_callback(gb, pixel);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -423,7 +448,7 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
|
||||
gb->screen[gb->position_in_line + gb->current_line * WIDTH] = gb->sprite_palettes_rgb[oam_fifo_item->palette * 4 + pixel];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (gb->model & GB_MODEL_NO_SFC_BIT) {
|
||||
if (gb->icd_pixel_callback) {
|
||||
gb->icd_pixel_callback(gb, icd_pixel);
|
||||
@@ -769,10 +794,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
||||
fifo_push_bg_row(&gb->bg_fifo, 0, 0, 0, false, false);
|
||||
/* Todo: find out actual access time of SCX */
|
||||
gb->position_in_line = - (gb->io_registers[GB_IO_SCX] & 7) - 8;
|
||||
gb->current_lcd_line++; // Todo: unverified timing
|
||||
if (gb->current_lcd_line == LINES && GB_is_sgb(gb)) {
|
||||
display_vblank(gb);
|
||||
}
|
||||
|
||||
gb->fetcher_x = ((gb->io_registers[GB_IO_SCX]) / 8) & 0x1f;
|
||||
gb->extra_penalty_for_sprite_at_0 = (gb->io_registers[GB_IO_SCX] & 7);
|
||||
|
||||
@@ -909,6 +931,12 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
||||
}
|
||||
GB_SLEEP(gb, display, 11, LINE_LENGTH - gb->cycles_for_line);
|
||||
gb->mode_for_interrupt = 2;
|
||||
|
||||
// Todo: unverified timing
|
||||
gb->current_lcd_line++;
|
||||
if (gb->current_lcd_line == LINES && GB_is_sgb(gb)) {
|
||||
display_vblank(gb);
|
||||
}
|
||||
|
||||
if (gb->icd_hreset_callback) {
|
||||
gb->icd_hreset_callback(gb);
|
||||
@@ -985,7 +1013,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
||||
gb->window_disabled_while_active = false;
|
||||
gb->current_line = 0;
|
||||
// TODO: not the correct timing
|
||||
gb->current_lcd_line = -1;
|
||||
gb->current_lcd_line = 0;
|
||||
if (gb->icd_vreset_callback) {
|
||||
gb->icd_vreset_callback(gb);
|
||||
}
|
||||
|
@@ -12,11 +12,19 @@
|
||||
#include "random.h"
|
||||
#include "gb.h"
|
||||
|
||||
|
||||
#ifdef DISABLE_REWIND
|
||||
#define GB_rewind_free(...)
|
||||
#define GB_rewind_push(...)
|
||||
#endif
|
||||
|
||||
|
||||
static inline uint32_t state_magic(void)
|
||||
{
|
||||
if (sizeof(bool) == 1) return 'SAME';
|
||||
return 'S4ME';
|
||||
}
|
||||
|
||||
void GB_attributed_logv(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, va_list args)
|
||||
{
|
||||
char *string = NULL;
|
||||
@@ -659,7 +667,7 @@ void GB_disconnect_serial(GB_gameboy_t *gb)
|
||||
|
||||
bool GB_is_inited(GB_gameboy_t *gb)
|
||||
{
|
||||
return gb->magic == 'SAME';
|
||||
return gb->magic == state_magic();
|
||||
}
|
||||
|
||||
bool GB_is_cgb(GB_gameboy_t *gb)
|
||||
@@ -711,7 +719,8 @@ static void reset_ram(GB_gameboy_t *gb)
|
||||
case GB_MODEL_DMG_B:
|
||||
case GB_MODEL_SGB_NTSC: /* Unverified*/
|
||||
case GB_MODEL_SGB_PAL: /* Unverified */
|
||||
case GB_MODEL_SGB_NO_SFC:
|
||||
case GB_MODEL_SGB_NTSC_NO_SFC: /* Unverified */
|
||||
case GB_MODEL_SGB_PAL_NO_SFC: /* Unverified */
|
||||
for (unsigned i = 0; i < gb->ram_size; i++) {
|
||||
gb->ram[i] = GB_random();
|
||||
if (i & 0x100) {
|
||||
@@ -757,7 +766,8 @@ static void reset_ram(GB_gameboy_t *gb)
|
||||
case GB_MODEL_DMG_B:
|
||||
case GB_MODEL_SGB_NTSC: /* Unverified*/
|
||||
case GB_MODEL_SGB_PAL: /* Unverified */
|
||||
case GB_MODEL_SGB_NO_SFC:
|
||||
case GB_MODEL_SGB_NTSC_NO_SFC: /* Unverified */
|
||||
case GB_MODEL_SGB_PAL_NO_SFC: /* Unverified */
|
||||
case GB_MODEL_SGB2:
|
||||
case GB_MODEL_SGB2_NO_SFC:
|
||||
for (unsigned i = 0; i < sizeof(gb->hram); i++) {
|
||||
@@ -782,7 +792,8 @@ static void reset_ram(GB_gameboy_t *gb)
|
||||
case GB_MODEL_DMG_B:
|
||||
case GB_MODEL_SGB_NTSC: /* Unverified */
|
||||
case GB_MODEL_SGB_PAL: /* Unverified */
|
||||
case GB_MODEL_SGB_NO_SFC: /* Unverified */
|
||||
case GB_MODEL_SGB_NTSC_NO_SFC: /* Unverified */
|
||||
case GB_MODEL_SGB_PAL_NO_SFC: /* Unverified */
|
||||
case GB_MODEL_SGB2:
|
||||
case GB_MODEL_SGB2_NO_SFC:
|
||||
for (unsigned i = 0; i < 8; i++) {
|
||||
@@ -810,7 +821,8 @@ static void reset_ram(GB_gameboy_t *gb)
|
||||
case GB_MODEL_DMG_B:
|
||||
case GB_MODEL_SGB_NTSC: /* Unverified*/
|
||||
case GB_MODEL_SGB_PAL: /* Unverified */
|
||||
case GB_MODEL_SGB_NO_SFC: /* Unverified */
|
||||
case GB_MODEL_SGB_NTSC_NO_SFC: /* Unverified */
|
||||
case GB_MODEL_SGB_PAL_NO_SFC: /* Unverified */
|
||||
case GB_MODEL_SGB2:
|
||||
case GB_MODEL_SGB2_NO_SFC: {
|
||||
uint8_t temp;
|
||||
@@ -857,7 +869,7 @@ void GB_reset(GB_gameboy_t *gb)
|
||||
gb->mbc_rom_bank = 1;
|
||||
gb->last_rtc_second = time(NULL);
|
||||
gb->cgb_ram_bank = 1;
|
||||
gb->io_registers[GB_IO_JOYP] = 0xF;
|
||||
gb->io_registers[GB_IO_JOYP] = 0xCF;
|
||||
gb->mbc_ram_size = mbc_ram_size;
|
||||
if (GB_is_cgb(gb)) {
|
||||
gb->ram_size = 0x1000 * 8;
|
||||
@@ -924,7 +936,7 @@ void GB_reset(GB_gameboy_t *gb)
|
||||
gb->nontrivial_jump_state = NULL;
|
||||
}
|
||||
|
||||
gb->magic = (uintptr_t)'SAME';
|
||||
gb->magic = state_magic();
|
||||
}
|
||||
|
||||
void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model)
|
||||
@@ -1016,12 +1028,12 @@ void GB_set_clock_multiplier(GB_gameboy_t *gb, double multiplier)
|
||||
|
||||
uint32_t GB_get_clock_rate(GB_gameboy_t *gb)
|
||||
{
|
||||
if (gb->model == GB_MODEL_SGB_NTSC) {
|
||||
return SGB_NTSC_FREQUENCY * gb->clock_multiplier;
|
||||
}
|
||||
if (gb->model == GB_MODEL_SGB_PAL) {
|
||||
if (gb->model & GB_MODEL_PAL_BIT) {
|
||||
return SGB_PAL_FREQUENCY * gb->clock_multiplier;
|
||||
}
|
||||
if ((gb->model & ~GB_MODEL_NO_SFC_BIT) == GB_MODEL_SGB) {
|
||||
return SGB_NTSC_FREQUENCY * gb->clock_multiplier;
|
||||
}
|
||||
return CPU_FREQUENCY * gb->clock_multiplier;
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,6 @@
|
||||
#ifndef GB_h
|
||||
#define GB_h
|
||||
#define typeof __typeof__
|
||||
#define _XOPEN_SOURCE 500
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
@@ -71,7 +70,9 @@ typedef enum {
|
||||
GB_MODEL_SGB = 0x004,
|
||||
GB_MODEL_SGB_NTSC = GB_MODEL_SGB,
|
||||
GB_MODEL_SGB_PAL = GB_MODEL_SGB | GB_MODEL_PAL_BIT,
|
||||
GB_MODEL_SGB_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT,
|
||||
GB_MODEL_SGB_NTSC_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT,
|
||||
GB_MODEL_SGB_NO_SFC = GB_MODEL_SGB_NTSC_NO_SFC,
|
||||
GB_MODEL_SGB_PAL_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT | GB_MODEL_PAL_BIT,
|
||||
// GB_MODEL_MGB = 0x100,
|
||||
GB_MODEL_SGB2 = 0x101,
|
||||
GB_MODEL_SGB2_NO_SFC = GB_MODEL_SGB2 | GB_MODEL_NO_SFC_BIT,
|
||||
@@ -277,10 +278,6 @@ typedef struct {
|
||||
This struct is not packed, but dumped sections exclusively use types that have the same alignment in both 32 and 64
|
||||
bit platforms. */
|
||||
|
||||
/* We make sure bool is 1 for cross-platform save state compatibility. */
|
||||
/* Todo: We might want to typedef our own bool if this prevents SameBoy from working on specific platforms. */
|
||||
//_Static_assert(sizeof(bool) == 1, "sizeof(bool) != 1");
|
||||
|
||||
#ifdef GB_INTERNAL
|
||||
struct GB_gameboy_s {
|
||||
#else
|
||||
@@ -544,6 +541,7 @@ struct GB_gameboy_internal_s {
|
||||
GB_icd_pixel_callback_t icd_pixel_callback;
|
||||
GB_icd_vreset_callback_t icd_hreset_callback;
|
||||
GB_icd_vreset_callback_t icd_vreset_callback;
|
||||
GB_read_memory_callback_t read_memory_callback;
|
||||
|
||||
/* IR */
|
||||
long cycles_since_ir_change; // In 8MHz units
|
||||
|
@@ -3,7 +3,7 @@
|
||||
|
||||
void GB_update_joyp(GB_gameboy_t *gb)
|
||||
{
|
||||
if (gb->model & GB_MODEL_SGB_NO_SFC) return;
|
||||
if (gb->model & GB_MODEL_NO_SFC_BIT) return;
|
||||
|
||||
uint8_t key_selection = 0;
|
||||
uint8_t previous_state = 0;
|
||||
@@ -12,7 +12,7 @@ void GB_update_joyp(GB_gameboy_t *gb)
|
||||
previous_state = gb->io_registers[GB_IO_JOYP] & 0xF;
|
||||
key_selection = (gb->io_registers[GB_IO_JOYP] >> 4) & 3;
|
||||
gb->io_registers[GB_IO_JOYP] &= 0xF0;
|
||||
uint8_t current_player = gb->sgb? gb->sgb->current_player : 0;
|
||||
uint8_t current_player = gb->sgb? (gb->sgb->current_player & (gb->sgb->player_count - 1) & 3) : 0;
|
||||
switch (key_selection) {
|
||||
case 3:
|
||||
if (gb->sgb && gb->sgb->player_count > 1) {
|
||||
@@ -73,7 +73,7 @@ void GB_icd_set_joyp(GB_gameboy_t *gb, uint8_t value)
|
||||
if (previous_state & ~(gb->io_registers[GB_IO_JOYP] & 0xF)) {
|
||||
gb->io_registers[GB_IO_IF] |= 0x10;
|
||||
}
|
||||
|
||||
gb->io_registers[GB_IO_JOYP] |= 0xC0;
|
||||
}
|
||||
|
||||
void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed)
|
||||
|
@@ -273,7 +273,8 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
|
||||
case GB_MODEL_DMG_B:
|
||||
case GB_MODEL_SGB_NTSC:
|
||||
case GB_MODEL_SGB_PAL:
|
||||
case GB_MODEL_SGB_NO_SFC:
|
||||
case GB_MODEL_SGB_NTSC_NO_SFC:
|
||||
case GB_MODEL_SGB_PAL_NO_SFC:
|
||||
case GB_MODEL_SGB2:
|
||||
case GB_MODEL_SGB2_NO_SFC:
|
||||
;
|
||||
@@ -421,11 +422,9 @@ static GB_read_function_t * const read_map[] =
|
||||
read_ram, read_high_memory, /* EXXX FXXX */
|
||||
};
|
||||
|
||||
static GB_read_memory_callback_t GB_read_memory_callback_v = 0;
|
||||
|
||||
void GB_set_read_memory_callback(GB_gameboy_t *gb, GB_read_memory_callback_t callback)
|
||||
{
|
||||
GB_read_memory_callback_v = callback;
|
||||
gb->read_memory_callback = callback;
|
||||
}
|
||||
|
||||
uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr)
|
||||
@@ -436,9 +435,9 @@ uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr)
|
||||
if (is_addr_in_dma_use(gb, addr)) {
|
||||
addr = gb->dma_current_src;
|
||||
}
|
||||
if (GB_read_memory_callback_v) {
|
||||
if (gb->read_memory_callback) {
|
||||
uint8_t data = read_map[addr >> 12](gb, addr);
|
||||
data = GB_read_memory_callback_v(gb, addr, data);
|
||||
data = gb->read_memory_callback(gb, addr, data);
|
||||
return data;
|
||||
}
|
||||
return read_map[addr >> 12](gb, addr);
|
||||
@@ -598,7 +597,8 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
case GB_MODEL_DMG_B:
|
||||
case GB_MODEL_SGB_NTSC:
|
||||
case GB_MODEL_SGB_PAL:
|
||||
case GB_MODEL_SGB_NO_SFC:
|
||||
case GB_MODEL_SGB_NTSC_NO_SFC:
|
||||
case GB_MODEL_SGB_PAL_NO_SFC:
|
||||
case GB_MODEL_SGB2:
|
||||
case GB_MODEL_SGB2_NO_SFC:
|
||||
case GB_MODEL_CGB_E:
|
||||
@@ -753,9 +753,15 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
return;
|
||||
|
||||
case GB_IO_JOYP:
|
||||
gb->io_registers[GB_IO_JOYP] = value & 0xF0;
|
||||
GB_sgb_write(gb, value);
|
||||
GB_update_joyp(gb);
|
||||
if (gb->joyp_write_callback) {
|
||||
gb->joyp_write_callback(gb, value);
|
||||
GB_update_joyp(gb);
|
||||
}
|
||||
else if ((gb->io_registers[GB_IO_JOYP] & 0x30) != (value & 0x30)) {
|
||||
GB_sgb_write(gb, value);
|
||||
gb->io_registers[GB_IO_JOYP] = value & 0xF0;
|
||||
GB_update_joyp(gb);
|
||||
}
|
||||
return;
|
||||
|
||||
case GB_IO_BIOS:
|
||||
|
@@ -72,6 +72,75 @@ static inline void load_attribute_file(GB_gameboy_t *gb, unsigned file_index)
|
||||
}
|
||||
}
|
||||
|
||||
static const uint16_t built_in_palettes[] =
|
||||
{
|
||||
0x67BF, 0x265B, 0x10B5, 0x2866,
|
||||
0x637B, 0x3AD9, 0x0956, 0x0000,
|
||||
0x7F1F, 0x2A7D, 0x30F3, 0x4CE7,
|
||||
0x57FF, 0x2618, 0x001F, 0x006A,
|
||||
0x5B7F, 0x3F0F, 0x222D, 0x10EB,
|
||||
0x7FBB, 0x2A3C, 0x0015, 0x0900,
|
||||
0x2800, 0x7680, 0x01EF, 0x2FFF,
|
||||
0x73BF, 0x46FF, 0x0110, 0x0066,
|
||||
0x533E, 0x2638, 0x01E5, 0x0000,
|
||||
0x7FFF, 0x2BBF, 0x00DF, 0x2C0A,
|
||||
0x7F1F, 0x463D, 0x74CF, 0x4CA5,
|
||||
0x53FF, 0x03E0, 0x00DF, 0x2800,
|
||||
0x433F, 0x72D2, 0x3045, 0x0822,
|
||||
0x7FFA, 0x2A5F, 0x0014, 0x0003,
|
||||
0x1EED, 0x215C, 0x42FC, 0x0060,
|
||||
0x7FFF, 0x5EF7, 0x39CE, 0x0000,
|
||||
0x4F5F, 0x630E, 0x159F, 0x3126,
|
||||
0x637B, 0x121C, 0x0140, 0x0840,
|
||||
0x66BC, 0x3FFF, 0x7EE0, 0x2C84,
|
||||
0x5FFE, 0x3EBC, 0x0321, 0x0000,
|
||||
0x63FF, 0x36DC, 0x11F6, 0x392A,
|
||||
0x65EF, 0x7DBF, 0x035F, 0x2108,
|
||||
0x2B6C, 0x7FFF, 0x1CD9, 0x0007,
|
||||
0x53FC, 0x1F2F, 0x0E29, 0x0061,
|
||||
0x36BE, 0x7EAF, 0x681A, 0x3C00,
|
||||
0x7BBE, 0x329D, 0x1DE8, 0x0423,
|
||||
0x739F, 0x6A9B, 0x7293, 0x0001,
|
||||
0x5FFF, 0x6732, 0x3DA9, 0x2481,
|
||||
0x577F, 0x3EBC, 0x456F, 0x1880,
|
||||
0x6B57, 0x6E1B, 0x5010, 0x0007,
|
||||
0x0F96, 0x2C97, 0x0045, 0x3200,
|
||||
0x67FF, 0x2F17, 0x2230, 0x1548,
|
||||
};
|
||||
|
||||
static const struct {
|
||||
char name[16];
|
||||
unsigned palette_index;
|
||||
} palette_assignments[] =
|
||||
{
|
||||
{"ZELDA", 5},
|
||||
{"SUPER MARIOLAND", 6},
|
||||
{"MARIOLAND2", 0x14},
|
||||
{"SUPERMARIOLAND3", 2},
|
||||
{"KIRBY DREAM LAND", 0xB},
|
||||
{"HOSHINOKA-BI", 0xB},
|
||||
{"KIRBY'S PINBALL", 3},
|
||||
{"YOSSY NO TAMAGO", 0xC},
|
||||
{"MARIO & YOSHI", 0xC},
|
||||
{"YOSSY NO COOKIE", 4},
|
||||
{"YOSHI'S COOKIE", 4},
|
||||
{"DR.MARIO", 0x12},
|
||||
{"TETRIS", 0x11},
|
||||
{"YAKUMAN", 0x13},
|
||||
{"METROID2", 0x1F},
|
||||
{"KAERUNOTAMENI", 9},
|
||||
{"GOLF", 0x18},
|
||||
{"ALLEY WAY", 0x16},
|
||||
{"BASEBALL", 0xF},
|
||||
{"TENNIS", 0x17},
|
||||
{"F1RACE", 0x1E},
|
||||
{"KID ICARUS", 0xE},
|
||||
{"QIX", 0x19},
|
||||
{"SOLARSTRIKER", 7},
|
||||
{"X", 0x1C},
|
||||
{"GBWARS", 0x15},
|
||||
};
|
||||
|
||||
static void command_ready(GB_gameboy_t *gb)
|
||||
{
|
||||
/* SGB header commands are used to send the contents of the header to the SNES CPU.
|
||||
@@ -81,6 +150,8 @@ static void command_ready(GB_gameboy_t *gb)
|
||||
0xE content bytes. The last command, FB, is padded with zeros, so information past the header is not sent. */
|
||||
|
||||
if ((gb->sgb->command[0] & 0xF1) == 0xF1) {
|
||||
if(gb->boot_rom_finished) return;
|
||||
|
||||
uint8_t checksum = 0;
|
||||
for (unsigned i = 2; i < 0x10; i++) {
|
||||
checksum += gb->sgb->command[i];
|
||||
@@ -90,14 +161,23 @@ static void command_ready(GB_gameboy_t *gb)
|
||||
gb->sgb->disable_commands = true;
|
||||
return;
|
||||
}
|
||||
if (gb->sgb->command[0] == 0xf9) {
|
||||
if (gb->sgb->command[0xc] != 3) { // SGB Flag
|
||||
gb->sgb->disable_commands = true;
|
||||
}
|
||||
unsigned index = (gb->sgb->command[0] >> 1) & 7;
|
||||
if (index > 5) {
|
||||
return;
|
||||
}
|
||||
else if (gb->sgb->command[0] == 0xfb) {
|
||||
if (gb->sgb->command[0x3] != 0x33) { // Old licensee code
|
||||
memcpy(&gb->sgb->received_header[index * 14], &gb->sgb->command[2], 14);
|
||||
if (gb->sgb->command[0] == 0xfb) {
|
||||
if (gb->sgb->received_header[0x42] != 3 || gb->sgb->received_header[0x47] != 0x33) {
|
||||
gb->sgb->disable_commands = true;
|
||||
for (unsigned i = 0; i < sizeof(palette_assignments) / sizeof(palette_assignments[0]); i++) {
|
||||
if (memcmp(palette_assignments[i].name, &gb->sgb->received_header[0x30], sizeof(palette_assignments[i].name)) == 0) {
|
||||
gb->sgb->effective_palettes[0] = built_in_palettes[palette_assignments[i].palette_index * 4 - 4];
|
||||
gb->sgb->effective_palettes[1] = built_in_palettes[palette_assignments[i].palette_index * 4 + 1 - 4];
|
||||
gb->sgb->effective_palettes[2] = built_in_palettes[palette_assignments[i].palette_index * 4 + 2 - 4];
|
||||
gb->sgb->effective_palettes[3] = built_in_palettes[palette_assignments[i].palette_index * 4 + 3 - 4];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -257,8 +337,15 @@ static void command_ready(GB_gameboy_t *gb)
|
||||
// Not supported, but used by almost all SGB games for hot patching, so let's mute the warning for this
|
||||
break;
|
||||
case MLT_REQ:
|
||||
gb->sgb->player_count = (uint8_t[]){1, 2, 1, 4}[gb->sgb->command[1] & 3];
|
||||
gb->sgb->current_player = gb->sgb->player_count - 1;
|
||||
if (gb->sgb->player_count == 1) {
|
||||
gb->sgb->current_player = 0;
|
||||
}
|
||||
gb->sgb->player_count = (gb->sgb->command[1] & 3) + 1; /* Todo: When breaking save state comaptibility,
|
||||
fix this to be 0 based. */
|
||||
if (gb->sgb->player_count == 3) {
|
||||
gb->sgb->current_player++;
|
||||
}
|
||||
gb->sgb->mlt_lock = true;
|
||||
break;
|
||||
case CHR_TRN:
|
||||
gb->sgb->vram_transfer_countdown = 2;
|
||||
@@ -298,30 +385,33 @@ static void command_ready(GB_gameboy_t *gb)
|
||||
}
|
||||
|
||||
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
|
||||
{
|
||||
if (gb->joyp_write_callback) {
|
||||
gb->joyp_write_callback(gb, value);
|
||||
}
|
||||
{
|
||||
if (!GB_is_sgb(gb)) return;
|
||||
if (!GB_is_hle_sgb(gb)) {
|
||||
/* Notify via callback */
|
||||
return;
|
||||
}
|
||||
if (gb->sgb->disable_commands) return;
|
||||
if (gb->sgb->command_write_index >= sizeof(gb->sgb->command) * 8) return;
|
||||
if (gb->sgb->command_write_index >= sizeof(gb->sgb->command) * 8) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t command_size = (gb->sgb->command[0] & 7 ?: 1) * SGB_PACKET_SIZE * 8;
|
||||
if ((gb->sgb->command[0] & 0xF1) == 0xF1) {
|
||||
command_size = SGB_PACKET_SIZE * 8;
|
||||
}
|
||||
|
||||
if ((value & 0x20) == 0 && (gb->io_registers[GB_IO_JOYP] & 0x20) != 0) {
|
||||
gb->sgb->mlt_lock ^= true;
|
||||
}
|
||||
|
||||
switch ((value >> 4) & 3) {
|
||||
case 3:
|
||||
gb->sgb->ready_for_pulse = true;
|
||||
/* TODO: This is the logic used by BGB which *should* work for most/all games, but a proper test ROM is needed */
|
||||
if (gb->sgb->player_count > 1 && (gb->io_registers[GB_IO_JOYP] & 0x30) == 0x10) {
|
||||
if ((gb->sgb->player_count & 1) == 0 && !gb->sgb->mlt_lock) {
|
||||
gb->sgb->current_player++;
|
||||
gb->sgb->current_player &= gb->sgb->player_count - 1;
|
||||
gb->sgb->current_player &= 3;
|
||||
gb->sgb->mlt_lock = true;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -382,22 +472,9 @@ void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint8_t scale_channel(uint8_t x)
|
||||
{
|
||||
return (x << 3) | (x >> 2);
|
||||
}
|
||||
|
||||
static uint32_t convert_rgb15(GB_gameboy_t *gb, uint16_t color)
|
||||
{
|
||||
uint8_t r = (color) & 0x1F;
|
||||
uint8_t g = (color >> 5) & 0x1F;
|
||||
uint8_t b = (color >> 10) & 0x1F;
|
||||
|
||||
r = scale_channel(r);
|
||||
g = scale_channel(g);
|
||||
b = scale_channel(b);
|
||||
|
||||
return gb->rgb_encode_callback(gb, r, g, b);
|
||||
return GB_convert_rgb15(gb, color);
|
||||
}
|
||||
|
||||
static uint32_t convert_rgb15_with_fade(GB_gameboy_t *gb, uint16_t color, uint8_t fade)
|
||||
@@ -410,11 +487,9 @@ static uint32_t convert_rgb15_with_fade(GB_gameboy_t *gb, uint16_t color, uint8_
|
||||
if (g >= 0x20) g = 0;
|
||||
if (b >= 0x20) b = 0;
|
||||
|
||||
r = scale_channel(r);
|
||||
g = scale_channel(g);
|
||||
b = scale_channel(b);
|
||||
color = r | (g << 5) | (b << 10);
|
||||
|
||||
return gb->rgb_encode_callback(gb, r, g, b);
|
||||
return GB_convert_rgb15(gb, color);
|
||||
}
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -679,10 +754,10 @@ void GB_sgb_load_default_data(GB_gameboy_t *gb)
|
||||
/* Re-center */
|
||||
memmove(&gb->sgb->border.map[25 * 32 + 1], &gb->sgb->border.map[25 * 32], (32 * 3 - 1) * sizeof(gb->sgb->border.map[0]));
|
||||
}
|
||||
gb->sgb->effective_palettes[0] = 0x639E;
|
||||
gb->sgb->effective_palettes[1] = 0x263A;
|
||||
gb->sgb->effective_palettes[2] = 0x10D4;
|
||||
gb->sgb->effective_palettes[3] = 0x2866;
|
||||
gb->sgb->effective_palettes[0] = built_in_palettes[0];
|
||||
gb->sgb->effective_palettes[1] = built_in_palettes[1];
|
||||
gb->sgb->effective_palettes[2] = built_in_palettes[2];
|
||||
gb->sgb->effective_palettes[3] = built_in_palettes[3];
|
||||
}
|
||||
|
||||
static double fm_synth(double phase)
|
||||
|
@@ -49,6 +49,12 @@ struct GB_sgb_s {
|
||||
|
||||
/* Intro */
|
||||
int16_t intro_animation;
|
||||
|
||||
/* GB Header */
|
||||
uint8_t received_header[0x54];
|
||||
|
||||
/* Multiplayer (cont) */
|
||||
bool mlt_lock;
|
||||
};
|
||||
|
||||
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value);
|
||||
|
@@ -33,6 +33,7 @@ static const GB_conflict_t cgb_conflict_map[0x80] = {
|
||||
/* Todo: most values not verified, and probably differ between revisions */
|
||||
};
|
||||
|
||||
/* Todo: verify on an MGB */
|
||||
static const GB_conflict_t dmg_conflict_map[0x80] = {
|
||||
[GB_IO_IF] = GB_CONFLICT_WRITE_CPU,
|
||||
[GB_IO_LYC] = GB_CONFLICT_READ_OLD,
|
||||
@@ -40,7 +41,6 @@ static const GB_conflict_t dmg_conflict_map[0x80] = {
|
||||
[GB_IO_SCY] = GB_CONFLICT_READ_NEW,
|
||||
[GB_IO_STAT] = GB_CONFLICT_STAT_DMG,
|
||||
|
||||
/* Todo: these are GB_CONFLICT_READ_NEW on MGB/SGB2 */
|
||||
[GB_IO_BGP] = GB_CONFLICT_PALETTE_DMG,
|
||||
[GB_IO_OBP0] = GB_CONFLICT_PALETTE_DMG,
|
||||
[GB_IO_OBP1] = GB_CONFLICT_PALETTE_DMG,
|
||||
@@ -51,6 +51,24 @@ static const GB_conflict_t dmg_conflict_map[0x80] = {
|
||||
[GB_IO_SCX] = GB_CONFLICT_READ_NEW,
|
||||
};
|
||||
|
||||
/* Todo: Verify on an SGB1 */
|
||||
static const GB_conflict_t sgb_conflict_map[0x80] = {
|
||||
[GB_IO_IF] = GB_CONFLICT_WRITE_CPU,
|
||||
[GB_IO_LYC] = GB_CONFLICT_READ_OLD,
|
||||
[GB_IO_LCDC] = GB_CONFLICT_READ_NEW,
|
||||
[GB_IO_SCY] = GB_CONFLICT_READ_NEW,
|
||||
[GB_IO_STAT] = GB_CONFLICT_STAT_DMG,
|
||||
|
||||
[GB_IO_BGP] = GB_CONFLICT_READ_NEW,
|
||||
[GB_IO_OBP0] = GB_CONFLICT_READ_NEW,
|
||||
[GB_IO_OBP1] = GB_CONFLICT_READ_NEW,
|
||||
|
||||
/* Todo: these were not verified at all */
|
||||
[GB_IO_WY] = GB_CONFLICT_READ_NEW,
|
||||
[GB_IO_WX] = GB_CONFLICT_READ_NEW,
|
||||
[GB_IO_SCX] = GB_CONFLICT_READ_NEW,
|
||||
};
|
||||
|
||||
static uint8_t cycle_read(GB_gameboy_t *gb, uint16_t addr)
|
||||
{
|
||||
if (gb->pending_cycles) {
|
||||
@@ -92,7 +110,17 @@ static void cycle_write(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
assert(gb->pending_cycles);
|
||||
GB_conflict_t conflict = GB_CONFLICT_READ_OLD;
|
||||
if ((addr & 0xFF80) == 0xFF00) {
|
||||
conflict = (GB_is_cgb(gb)? cgb_conflict_map : dmg_conflict_map)[addr & 0x7F];
|
||||
const GB_conflict_t *map = NULL;
|
||||
if (GB_is_cgb(gb)) {
|
||||
map = cgb_conflict_map;
|
||||
}
|
||||
else if (GB_is_sgb(gb)) {
|
||||
map = sgb_conflict_map;
|
||||
}
|
||||
else {
|
||||
map = dmg_conflict_map;
|
||||
}
|
||||
conflict = map[addr & 0x7F];
|
||||
}
|
||||
switch (conflict) {
|
||||
case GB_CONFLICT_READ_OLD:
|
||||
|
5
bsnes/gb/README
Normal file
5
bsnes/gb/README
Normal file
@@ -0,0 +1,5 @@
|
||||
Core: 2019-10-22 master snapshot
|
||||
* has issues with SGB2 audio desynchronization
|
||||
|
||||
Core-new: 2019-11-05 master snapshot
|
||||
* Game Boy inputs are not working
|
@@ -6,6 +6,7 @@ struct SuperFamicom {
|
||||
|
||||
auto manifest() const -> string;
|
||||
auto region() const -> string;
|
||||
auto videoRegion() const -> string;
|
||||
auto revision() const -> string;
|
||||
auto board() const -> string;
|
||||
auto title() const -> string;
|
||||
@@ -139,6 +140,11 @@ auto SuperFamicom::manifest() const -> string {
|
||||
}
|
||||
|
||||
auto SuperFamicom::region() const -> string {
|
||||
//Unlicensed software (homebrew, ROM hacks, etc) often change the standard region code,
|
||||
//and then neglect to change the extended header region code. Thanks to that, we can't
|
||||
//decode and display the full game serial + region code.
|
||||
return videoRegion();
|
||||
|
||||
string region;
|
||||
|
||||
char A = data[headerAddress + 0x02]; //game type
|
||||
@@ -163,7 +169,7 @@ auto SuperFamicom::region() const -> string {
|
||||
if(D == 'P') region = {"SNSP-", code, "-EUR"};
|
||||
if(D == 'S') region = {"SNSP-", code, "-ESP"};
|
||||
if(D == 'U') region = {"SNSP-", code, "-AUS"};
|
||||
if(D == 'W') region = {"SNSP-", code, "-SCN"};
|
||||
if(D == 'X') region = {"SNSP-", code, "-SCN"};
|
||||
}
|
||||
|
||||
if(!region) {
|
||||
@@ -181,11 +187,23 @@ auto SuperFamicom::region() const -> string {
|
||||
if(E == 0x0f) region = {"CAN"};
|
||||
if(E == 0x10) region = {"BRA"};
|
||||
if(E == 0x11) region = {"AUS"};
|
||||
if(E == 0x12) region = {"SCN"};
|
||||
}
|
||||
|
||||
return region ? region : "NTSC";
|
||||
}
|
||||
|
||||
auto SuperFamicom::videoRegion() const -> string {
|
||||
auto region = data[headerAddress + 0x29];
|
||||
if(region == 0x00) return "NTSC"; //JPN
|
||||
if(region == 0x01) return "NTSC"; //USA
|
||||
if(region == 0x0b) return "NTSC"; //ROC
|
||||
if(region == 0x0d) return "NTSC"; //KOR
|
||||
if(region == 0x0f) return "NTSC"; //CAN
|
||||
if(region == 0x10) return "NTSC"; //BRA
|
||||
return "PAL";
|
||||
}
|
||||
|
||||
auto SuperFamicom::revision() const -> string {
|
||||
string revision;
|
||||
|
||||
@@ -212,7 +230,7 @@ auto SuperFamicom::revision() const -> string {
|
||||
if(D == 'P') revision = {"SNSP-", code, "-", F};
|
||||
if(D == 'S') revision = {"SNSP-", code, "-", F};
|
||||
if(D == 'U') revision = {"SNSP-", code, "-", F};
|
||||
if(D == 'W') revision = {"SNSP-", code, "-", F};
|
||||
if(D == 'X') revision = {"SNSP-", code, "-", F};
|
||||
}
|
||||
|
||||
if(!revision) {
|
||||
@@ -248,6 +266,9 @@ auto SuperFamicom::board() const -> string {
|
||||
if(headerAddress == 0x40ffb0) mode = "EXHIROM-";
|
||||
}
|
||||
|
||||
//this game's title ovewrites the map mode with '!' (0x21), but is a LOROM game
|
||||
if(title() == "YUYU NO QUIZ DE GO!GO") mode = "LOROM-";
|
||||
|
||||
if(mode == "LOROM-" && headerAddress == 0x407fb0) mode = "EXLOROM-";
|
||||
|
||||
bool epsonRTC = false;
|
||||
@@ -443,15 +464,17 @@ auto SuperFamicom::firmwareRomSize() const -> uint {
|
||||
}
|
||||
|
||||
auto SuperFamicom::ramSize() const -> uint {
|
||||
auto ramSize = data[headerAddress + 0x28] & 7;
|
||||
if(ramSize) return 1024 << ramSize;
|
||||
auto ramSize = data[headerAddress + 0x28] & 15;
|
||||
if(ramSize > 8) ramSize = 8;
|
||||
if(ramSize > 0) return 1024 << ramSize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto SuperFamicom::expansionRamSize() const -> uint {
|
||||
if(data[headerAddress + 0x2a] == 0x33) {
|
||||
auto ramSize = data[headerAddress + 0x0d] & 7;
|
||||
if(ramSize) return 1024 << ramSize;
|
||||
auto ramSize = data[headerAddress + 0x0d] & 15;
|
||||
if(ramSize > 8) ramSize = 8;
|
||||
if(ramSize > 0) return 1024 << ramSize;
|
||||
}
|
||||
if((data[headerAddress + 0x26] >> 4) == 1) {
|
||||
//GSU: Starfox / Starwing lacks an extended header; but still has expansion RAM
|
@@ -1,11 +1,11 @@
|
||||
auto ARM7TDMI::ADD(uint32 source, uint32 modify, bool carry) -> uint32 {
|
||||
uint32 result = source + modify + carry;
|
||||
if(cpsr().t || bit1(opcode,20)) {
|
||||
if(cpsr().t || (opcode & 1 << 20)) {
|
||||
uint32 overflow = ~(source ^ modify) & (source ^ result);
|
||||
cpsr().v = 1 << 31 & (overflow);
|
||||
cpsr().c = 1 << 31 & (overflow ^ source ^ modify ^ result);
|
||||
cpsr().z = result == 0;
|
||||
cpsr().n = bit1(result,31);
|
||||
cpsr().n = result >> 31;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -19,10 +19,10 @@ auto ARM7TDMI::ASR(uint32 source, uint8 shift) -> uint32 {
|
||||
}
|
||||
|
||||
auto ARM7TDMI::BIT(uint32 result) -> uint32 {
|
||||
if(cpsr().t || bit1(opcode,20)) {
|
||||
if(cpsr().t || (opcode & 1 << 20)) {
|
||||
cpsr().c = carry;
|
||||
cpsr().z = result == 0;
|
||||
cpsr().n = bit1(result,31);
|
||||
cpsr().n = result >> 31;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -49,9 +49,9 @@ auto ARM7TDMI::MUL(uint32 product, uint32 multiplicand, uint32 multiplier) -> ui
|
||||
if(multiplier >> 16 && multiplier >> 16 != 0xffff) idle();
|
||||
if(multiplier >> 24 && multiplier >> 24 != 0xff) idle();
|
||||
product += multiplicand * multiplier;
|
||||
if(cpsr().t || bit1(opcode,20)) {
|
||||
if(cpsr().t || (opcode & 1 << 20)) {
|
||||
cpsr().z = product == 0;
|
||||
cpsr().n = bit1(product,31);
|
||||
cpsr().n = product >> 31;
|
||||
}
|
||||
return product;
|
||||
}
|
||||
|
@@ -148,14 +148,14 @@ struct ARM7TDMI {
|
||||
}
|
||||
|
||||
inline auto operator=(uint32 data) -> PSR& {
|
||||
m = bits(data,0-4);
|
||||
t = bit1(data,5);
|
||||
f = bit1(data,6);
|
||||
i = bit1(data,7);
|
||||
v = bit1(data,28);
|
||||
c = bit1(data,29);
|
||||
z = bit1(data,30);
|
||||
n = bit1(data,31);
|
||||
m = data >> 0 & 31;
|
||||
t = data >> 5 & 1;
|
||||
f = data >> 6 & 1;
|
||||
i = data >> 7 & 1;
|
||||
v = data >> 28 & 1;
|
||||
c = data >> 29 & 1;
|
||||
z = data >> 30 & 1;
|
||||
n = data >> 31 & 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@@ -186,7 +186,7 @@ auto ARM7TDMI::armDisassembleMoveMultiple
|
||||
(uint16 list, uint4 n, uint1 mode, uint1 writeback, uint1 type, uint1 up, uint1 pre) -> string {
|
||||
string registers;
|
||||
for(auto index : range(16)) {
|
||||
if(bit1(list,index)) registers.append(_r[index], ",");
|
||||
if((list & 1 << index)) registers.append(_r[index], ",");
|
||||
}
|
||||
registers.trimRight(",", 1L);
|
||||
return {mode ? "ldm" : "stm", _c,
|
||||
@@ -358,7 +358,7 @@ auto ARM7TDMI::thumbDisassembleMoveMultiple
|
||||
(uint8 list, uint3 n, uint1 mode) -> string {
|
||||
string registers;
|
||||
for(uint m : range(8)) {
|
||||
if(bit1(list,m)) registers.append(_r[m], ",");
|
||||
if((list & 1 << m)) registers.append(_r[m], ",");
|
||||
}
|
||||
registers.trimRight(",", 1L);
|
||||
return {mode ? "ldmia" : "stmia", " ", _r[n], "!,{", registers, "}"};
|
||||
@@ -395,7 +395,7 @@ auto ARM7TDMI::thumbDisassembleStackMultiple
|
||||
(uint8 list, uint1 lrpc, uint1 mode) -> string {
|
||||
string registers;
|
||||
for(uint m : range(8)) {
|
||||
if(bit1(list,m)) registers.append(_r[m], ",");
|
||||
if((list & 1 << m)) registers.append(_r[m], ",");
|
||||
}
|
||||
if(lrpc) registers.append(!mode ? "lr," : "pc,");
|
||||
registers.trimRight(",", 1L);
|
||||
|
@@ -68,8 +68,11 @@ auto ARM7TDMI::armInitialize() -> void {
|
||||
#define pattern(s) \
|
||||
std::integral_constant<uint32_t, bit::test(s)>::value
|
||||
|
||||
#define bit1(value, index) (value >> index & 1)
|
||||
#define bits(value, lo, hi) (value >> lo & (1ull << (hi - lo + 1)) - 1)
|
||||
|
||||
#define arguments \
|
||||
bits(opcode, 0-23), /* displacement */ \
|
||||
bits(opcode, 0,23), /* displacement */ \
|
||||
bit1(opcode,24) /* link */
|
||||
for(uint4 displacementLo : range(16))
|
||||
for(uint4 displacementHi : range(16))
|
||||
@@ -81,7 +84,7 @@ auto ARM7TDMI::armInitialize() -> void {
|
||||
#undef arguments
|
||||
|
||||
#define arguments \
|
||||
bits(opcode, 0- 3) /* m */
|
||||
bits(opcode, 0, 3) /* m */
|
||||
{
|
||||
auto opcode = pattern(".... 0001 0010 ---- ---- ---- 0001 ????");
|
||||
bind(opcode, BranchExchangeRegister);
|
||||
@@ -89,12 +92,12 @@ auto ARM7TDMI::armInitialize() -> void {
|
||||
#undef arguments
|
||||
|
||||
#define arguments \
|
||||
bits(opcode, 0- 7), /* immediate */ \
|
||||
bits(opcode, 8-11), /* shift */ \
|
||||
bits(opcode,12-15), /* d */ \
|
||||
bits(opcode,16-19), /* n */ \
|
||||
bits(opcode, 0, 7), /* immediate */ \
|
||||
bits(opcode, 8,11), /* shift */ \
|
||||
bits(opcode,12,15), /* d */ \
|
||||
bits(opcode,16,19), /* n */ \
|
||||
bit1(opcode,20), /* save */ \
|
||||
bits(opcode,21-24) /* mode */
|
||||
bits(opcode,21,24) /* mode */
|
||||
for(uint4 shiftHi : range(16))
|
||||
for(uint1 save : range(2))
|
||||
for(uint4 mode : range(16)) {
|
||||
@@ -105,13 +108,13 @@ auto ARM7TDMI::armInitialize() -> void {
|
||||
#undef arguments
|
||||
|
||||
#define arguments \
|
||||
bits(opcode, 0- 3), /* m */ \
|
||||
bits(opcode, 5- 6), /* type */ \
|
||||
bits(opcode, 7-11), /* shift */ \
|
||||
bits(opcode,12-15), /* d */ \
|
||||
bits(opcode,16-19), /* n */ \
|
||||
bits(opcode, 0, 3), /* m */ \
|
||||
bits(opcode, 5, 6), /* type */ \
|
||||
bits(opcode, 7,11), /* shift */ \
|
||||
bits(opcode,12,15), /* d */ \
|
||||
bits(opcode,16,19), /* n */ \
|
||||
bit1(opcode,20), /* save */ \
|
||||
bits(opcode,21-24) /* mode */
|
||||
bits(opcode,21,24) /* mode */
|
||||
for(uint2 type : range(4))
|
||||
for(uint1 shiftLo : range(2))
|
||||
for(uint1 save : range(2))
|
||||
@@ -123,13 +126,13 @@ auto ARM7TDMI::armInitialize() -> void {
|
||||
#undef arguments
|
||||
|
||||
#define arguments \
|
||||
bits(opcode, 0- 3), /* m */ \
|
||||
bits(opcode, 5- 6), /* type */ \
|
||||
bits(opcode, 8-11), /* s */ \
|
||||
bits(opcode,12-15), /* d */ \
|
||||
bits(opcode,16-19), /* n */ \
|
||||
bits(opcode, 0, 3), /* m */ \
|
||||
bits(opcode, 5, 6), /* type */ \
|
||||
bits(opcode, 8,11), /* s */ \
|
||||
bits(opcode,12,15), /* d */ \
|
||||
bits(opcode,16,19), /* n */ \
|
||||
bit1(opcode,20), /* save */ \
|
||||
bits(opcode,21-24) /* mode */
|
||||
bits(opcode,21,24) /* mode */
|
||||
for(uint2 type : range(4))
|
||||
for(uint1 save : range(2))
|
||||
for(uint4 mode : range(16)) {
|
||||
@@ -140,10 +143,10 @@ auto ARM7TDMI::armInitialize() -> void {
|
||||
#undef arguments
|
||||
|
||||
#define arguments \
|
||||
bits(opcode, 0- 3) << 0 | bits(opcode, 8-11) << 4, /* immediate */ \
|
||||
bits(opcode, 0, 3) << 0 | bits(opcode, 8,11) << 4, /* immediate */ \
|
||||
bit1(opcode, 5), /* half */ \
|
||||
bits(opcode,12-15), /* d */ \
|
||||
bits(opcode,16-19), /* n */ \
|
||||
bits(opcode,12,15), /* d */ \
|
||||
bits(opcode,16,19), /* n */ \
|
||||
bit1(opcode,21), /* writeback */ \
|
||||
bit1(opcode,23), /* up */ \
|
||||
bit1(opcode,24) /* pre */
|
||||
@@ -157,10 +160,10 @@ auto ARM7TDMI::armInitialize() -> void {
|
||||
#undef arguments
|
||||
|
||||
#define arguments \
|
||||
bits(opcode, 0- 3), /* m */ \
|
||||
bits(opcode, 0, 3), /* m */ \
|
||||
bit1(opcode, 5), /* half */ \
|
||||
bits(opcode,12-15), /* d */ \
|
||||
bits(opcode,16-19), /* n */ \
|
||||
bits(opcode,12,15), /* d */ \
|
||||
bits(opcode,16,19), /* n */ \
|
||||
bit1(opcode,21), /* writeback */ \
|
||||
bit1(opcode,23), /* up */ \
|
||||
bit1(opcode,24) /* pre */
|
||||
@@ -174,9 +177,9 @@ auto ARM7TDMI::armInitialize() -> void {
|
||||
#undef arguments
|
||||
|
||||
#define arguments \
|
||||
bits(opcode, 0- 3), /* m */ \
|
||||
bits(opcode,12-15), /* d */ \
|
||||
bits(opcode,16-19), /* n */ \
|
||||
bits(opcode, 0, 3), /* m */ \
|
||||
bits(opcode,12,15), /* d */ \
|
||||
bits(opcode,16,19), /* n */ \
|
||||
bit1(opcode,22) /* byte */
|
||||
for(uint1 byte : range(2)) {
|
||||
auto opcode = pattern(".... 0001 0?00 ???? ???? ---- 1001 ????") | byte << 22;
|
||||
@@ -185,9 +188,9 @@ auto ARM7TDMI::armInitialize() -> void {
|
||||
#undef arguments
|
||||
|
||||
#define arguments \
|
||||
bits(opcode, 0- 3) << 0 | bits(opcode, 8-11) << 4, /* immediate */ \
|
||||
bits(opcode,12-15), /* d */ \
|
||||
bits(opcode,16-19), /* n */ \
|
||||
bits(opcode, 0, 3) << 0 | bits(opcode, 8,11) << 4, /* immediate */ \
|
||||
bits(opcode,12,15), /* d */ \
|
||||
bits(opcode,16,19), /* n */ \
|
||||
bit1(opcode,20), /* mode */ \
|
||||
bit1(opcode,21), /* writeback */ \
|
||||
bit1(opcode,23), /* up */ \
|
||||
@@ -202,9 +205,9 @@ auto ARM7TDMI::armInitialize() -> void {
|
||||
#undef arguments
|
||||
|
||||
#define arguments \
|
||||
bits(opcode, 0- 3), /* m */ \
|
||||
bits(opcode,12-15), /* d */ \
|
||||
bits(opcode,16-19), /* n */ \
|
||||
bits(opcode, 0, 3), /* m */ \
|
||||
bits(opcode,12,15), /* d */ \
|
||||
bits(opcode,16,19), /* n */ \
|
||||
bit1(opcode,20), /* mode */ \
|
||||
bit1(opcode,21), /* writeback */ \
|
||||
bit1(opcode,23), /* up */ \
|
||||
@@ -219,9 +222,9 @@ auto ARM7TDMI::armInitialize() -> void {
|
||||
#undef arguments
|
||||
|
||||
#define arguments \
|
||||
bits(opcode, 0-11), /* immediate */ \
|
||||
bits(opcode,12-15), /* d */ \
|
||||
bits(opcode,16-19), /* n */ \
|
||||
bits(opcode, 0,11), /* immediate */ \
|
||||
bits(opcode,12,15), /* d */ \
|
||||
bits(opcode,16,19), /* n */ \
|
||||
bit1(opcode,20), /* mode */ \
|
||||
bit1(opcode,21), /* writeback */ \
|
||||
bit1(opcode,22), /* byte */ \
|
||||
@@ -240,8 +243,8 @@ auto ARM7TDMI::armInitialize() -> void {
|
||||
#undef arguments
|
||||
|
||||
#define arguments \
|
||||
bits(opcode, 0-15), /* list */ \
|
||||
bits(opcode,16-19), /* n */ \
|
||||
bits(opcode, 0,15), /* list */ \
|
||||
bits(opcode,16,19), /* n */ \
|
||||
bit1(opcode,20), /* mode */ \
|
||||
bit1(opcode,21), /* writeback */ \
|
||||
bit1(opcode,22), /* type */ \
|
||||
@@ -260,11 +263,11 @@ auto ARM7TDMI::armInitialize() -> void {
|
||||
#undef arguments
|
||||
|
||||
#define arguments \
|
||||
bits(opcode, 0- 3), /* m */ \
|
||||
bits(opcode, 5- 6), /* type */ \
|
||||
bits(opcode, 7-11), /* shift */ \
|
||||
bits(opcode,12-15), /* d */ \
|
||||
bits(opcode,16-19), /* n */ \
|
||||
bits(opcode, 0, 3), /* m */ \
|
||||
bits(opcode, 5, 6), /* type */ \
|
||||
bits(opcode, 7,11), /* shift */ \
|
||||
bits(opcode,12,15), /* d */ \
|
||||
bits(opcode,16,19), /* n */ \
|
||||
bit1(opcode,20), /* mode */ \
|
||||
bit1(opcode,21), /* writeback */ \
|
||||
bit1(opcode,22), /* byte */ \
|
||||
@@ -284,7 +287,7 @@ auto ARM7TDMI::armInitialize() -> void {
|
||||
#undef arguments
|
||||
|
||||
#define arguments \
|
||||
bits(opcode,12-15), /* d */ \
|
||||
bits(opcode,12,15), /* d */ \
|
||||
bit1(opcode,22) /* mode */
|
||||
for(uint1 mode : range(2)) {
|
||||
auto opcode = pattern(".... 0001 0?00 ---- ???? ---- 0000 ----") | mode << 22;
|
||||
@@ -293,9 +296,9 @@ auto ARM7TDMI::armInitialize() -> void {
|
||||
#undef arguments
|
||||
|
||||
#define arguments \
|
||||
bits(opcode, 0- 7), /* immediate */ \
|
||||
bits(opcode, 8-11), /* rotate */ \
|
||||
bits(opcode,16-19), /* field */ \
|
||||
bits(opcode, 0, 7), /* immediate */ \
|
||||
bits(opcode, 8,11), /* rotate */ \
|
||||
bits(opcode,16,19), /* field */ \
|
||||
bit1(opcode,22) /* mode */
|
||||
for(uint4 immediateHi : range(16))
|
||||
for(uint1 mode : range(2)) {
|
||||
@@ -305,8 +308,8 @@ auto ARM7TDMI::armInitialize() -> void {
|
||||
#undef arguments
|
||||
|
||||
#define arguments \
|
||||
bits(opcode, 0- 3), /* m */ \
|
||||
bits(opcode,16-19), /* field */ \
|
||||
bits(opcode, 0, 3), /* m */ \
|
||||
bits(opcode,16,19), /* field */ \
|
||||
bit1(opcode,22) /* mode */
|
||||
for(uint1 mode : range(2)) {
|
||||
auto opcode = pattern(".... 0001 0?10 ???? ---- ---- 0000 ????") | mode << 22;
|
||||
@@ -315,10 +318,10 @@ auto ARM7TDMI::armInitialize() -> void {
|
||||
#undef arguments
|
||||
|
||||
#define arguments \
|
||||
bits(opcode, 0- 3), /* m */ \
|
||||
bits(opcode, 8-11), /* s */ \
|
||||
bits(opcode,12-15), /* n */ \
|
||||
bits(opcode,16-19), /* d */ \
|
||||
bits(opcode, 0, 3), /* m */ \
|
||||
bits(opcode, 8,11), /* s */ \
|
||||
bits(opcode,12,15), /* n */ \
|
||||
bits(opcode,16,19), /* d */ \
|
||||
bit1(opcode,20), /* save */ \
|
||||
bit1(opcode,21) /* accumulate */
|
||||
for(uint1 save : range(2))
|
||||
@@ -329,10 +332,10 @@ auto ARM7TDMI::armInitialize() -> void {
|
||||
#undef arguments
|
||||
|
||||
#define arguments \
|
||||
bits(opcode, 0- 3), /* m */ \
|
||||
bits(opcode, 8-11), /* s */ \
|
||||
bits(opcode,12-15), /* l */ \
|
||||
bits(opcode,16-19), /* h */ \
|
||||
bits(opcode, 0, 3), /* m */ \
|
||||
bits(opcode, 8,11), /* s */ \
|
||||
bits(opcode,12,15), /* l */ \
|
||||
bits(opcode,16,19), /* h */ \
|
||||
bit1(opcode,20), /* save */ \
|
||||
bit1(opcode,21), /* accumulate */ \
|
||||
bit1(opcode,22) /* sign */
|
||||
@@ -345,7 +348,7 @@ auto ARM7TDMI::armInitialize() -> void {
|
||||
#undef arguments
|
||||
|
||||
#define arguments \
|
||||
bits(opcode, 0-23) /* immediate */
|
||||
bits(opcode, 0,23) /* immediate */
|
||||
for(uint4 immediateLo : range(16))
|
||||
for(uint4 immediateHi : range(16)) {
|
||||
auto opcode = pattern(".... 1111 ???? ???? ???? ???? ???? ????") | immediateLo << 4 | immediateHi << 20;
|
||||
@@ -356,7 +359,7 @@ auto ARM7TDMI::armInitialize() -> void {
|
||||
#define arguments
|
||||
for(uint12 id : range(4096)) {
|
||||
if(armInstruction[id]) continue;
|
||||
auto opcode = pattern(".... ???? ???? ---- ---- ---- ???? ----") | bits(id,0-3) << 4 | bits(id,4-11) << 20;
|
||||
auto opcode = pattern(".... ???? ???? ---- ---- ---- ???? ----") | bits(id,0,3) << 4 | bits(id,4,11) << 20;
|
||||
bind(opcode, Undefined);
|
||||
}
|
||||
#undef arguments
|
||||
@@ -386,7 +389,7 @@ auto ARM7TDMI::thumbInitialize() -> void {
|
||||
for(uint4 m : range(16))
|
||||
for(uint2 mode : range(4)) {
|
||||
if(mode == 3) continue;
|
||||
auto opcode = pattern("0100 01?? ???? ????") | bits(d,0-2) << 0 | m << 3 | bit1(d,3) << 7 | mode << 8;
|
||||
auto opcode = pattern("0100 01?? ???? ????") | bits(d,0,2) << 0 | m << 3 | bit1(d,3) << 7 | mode << 8;
|
||||
bind(opcode, ALUExtended, d, m, mode);
|
||||
}
|
||||
|
||||
@@ -533,6 +536,9 @@ auto ARM7TDMI::thumbInitialize() -> void {
|
||||
bind(opcode, Undefined);
|
||||
}
|
||||
|
||||
#undef bit1
|
||||
#undef bits
|
||||
|
||||
#undef bind
|
||||
#undef pattern
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@ auto ARM7TDMI::armALU(uint4 mode, uint4 d, uint4 n, uint32 rm) -> void {
|
||||
case 15: r(d) = BIT(~rm); break; //MVN
|
||||
}
|
||||
|
||||
if(exception() && d == 15 && bit1(opcode,20)) {
|
||||
if(exception() && d == 15 && (opcode & 1 << 20)) {
|
||||
cpsr() = spsr();
|
||||
}
|
||||
}
|
||||
@@ -29,21 +29,21 @@ auto ARM7TDMI::armMoveToStatus(uint4 field, uint1 mode, uint32 data) -> void {
|
||||
if(mode && (cpsr().m == PSR::USR || cpsr().m == PSR::SYS)) return;
|
||||
PSR& psr = mode ? spsr() : cpsr();
|
||||
|
||||
if(field.bit(0)) {
|
||||
if(field & 1) {
|
||||
if(mode || privileged()) {
|
||||
psr.m = bits(data,0-4);
|
||||
psr.t = bit1(data,5);
|
||||
psr.f = bit1(data,6);
|
||||
psr.i = bit1(data,7);
|
||||
psr.m = data >> 0 & 31;
|
||||
psr.t = data >> 5 & 1;
|
||||
psr.f = data >> 6 & 1;
|
||||
psr.i = data >> 7 & 1;
|
||||
if(!mode && psr.t) r(15).data += 2;
|
||||
}
|
||||
}
|
||||
|
||||
if(field.bit(3)) {
|
||||
psr.v = bit1(data,28);
|
||||
psr.c = bit1(data,29);
|
||||
psr.z = bit1(data,30);
|
||||
psr.n = bit1(data,31);
|
||||
if(field & 8) {
|
||||
psr.v = data >> 28 & 1;
|
||||
psr.c = data >> 29 & 1;
|
||||
psr.z = data >> 30 & 1;
|
||||
psr.n = data >> 31 & 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,13 +193,13 @@ auto ARM7TDMI::armInstructionMoveMultiple
|
||||
|
||||
auto cpsrMode = cpsr().m;
|
||||
bool usr = false;
|
||||
if(type && mode == 1 && !bit1(list,15)) usr = true;
|
||||
if(type && mode == 1 && !(list & 0x8000)) usr = true;
|
||||
if(type && mode == 0) usr = true;
|
||||
if(usr) cpsr().m = PSR::USR;
|
||||
|
||||
uint sequential = Nonsequential;
|
||||
for(uint m : range(16)) {
|
||||
if(!bit1(list,m)) continue;
|
||||
if(!(list & 1 << m)) continue;
|
||||
if(mode == 1) r(m) = read(Word | sequential, rn);
|
||||
if(mode == 0) write(Word | sequential, rn, r(m));
|
||||
rn += 4;
|
||||
@@ -210,7 +210,7 @@ auto ARM7TDMI::armInstructionMoveMultiple
|
||||
|
||||
if(mode) {
|
||||
idle();
|
||||
if(type && bit1(list,15) && cpsr().m != PSR::USR && cpsr().m != PSR::SYS) {
|
||||
if(type && (list & 0x8000) && cpsr().m != PSR::USR && cpsr().m != PSR::SYS) {
|
||||
cpsr() = spsr();
|
||||
}
|
||||
} else {
|
||||
@@ -299,7 +299,7 @@ auto ARM7TDMI::armInstructionMultiplyLong
|
||||
|
||||
if(save) {
|
||||
cpsr().z = rd == 0;
|
||||
cpsr().n = bit1(rd,63);
|
||||
cpsr().n = rd >> 63 & 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -127,7 +127,7 @@ auto ARM7TDMI::thumbInstructionMoveMultiple
|
||||
uint32 rn = r(n);
|
||||
|
||||
for(uint m : range(8)) {
|
||||
if(!bit1(list,m)) continue;
|
||||
if(!(list & 1 << m)) continue;
|
||||
switch(mode) {
|
||||
case 0: write(Word | Nonsequential, rn, r(m)); break; //STMIA
|
||||
case 1: r(m) = read(Word | Nonsequential, rn); break; //LDMIA
|
||||
@@ -135,7 +135,7 @@ auto ARM7TDMI::thumbInstructionMoveMultiple
|
||||
rn += 4;
|
||||
}
|
||||
|
||||
if(mode == 0 || !bit1(list,n)) r(n) = rn;
|
||||
if(mode == 0 || !(list & 1 << n)) r(n) = rn;
|
||||
if(mode == 1) idle();
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ auto ARM7TDMI::thumbInstructionStackMultiple
|
||||
|
||||
uint sequential = Nonsequential;
|
||||
for(uint m : range(8)) {
|
||||
if(!bit1(list,m)) continue;
|
||||
if(!(list & 1 << m)) continue;
|
||||
switch(mode) {
|
||||
case 0: write(Word | sequential, sp, r(m)); break; //PUSH
|
||||
case 1: r(m) = read(Word | sequential, sp); break; //POP
|
||||
|
@@ -34,22 +34,22 @@ struct Register {
|
||||
};
|
||||
|
||||
struct SFR {
|
||||
union {
|
||||
uint16_t data = 0;
|
||||
BooleanBitField<uint16_t, 15> irq; //interrupt flag
|
||||
BooleanBitField<uint16_t, 12> b; //with flag
|
||||
BooleanBitField<uint16_t, 11> ih; //immediate higher 8-bit flag
|
||||
BooleanBitField<uint16_t, 10> il; //immediate lower 8-bit flag
|
||||
BooleanBitField<uint16_t, 9> alt2; //alt2 instruction mode
|
||||
BooleanBitField<uint16_t, 8> alt1; //alt1 instruction mode
|
||||
BooleanBitField<uint16_t, 6> r; //ROM r14 read flag
|
||||
BooleanBitField<uint16_t, 5> g; //go flag
|
||||
BooleanBitField<uint16_t, 4> ov; //overflow flag
|
||||
BooleanBitField<uint16_t, 3> s; //sign flag
|
||||
BooleanBitField<uint16_t, 2> cy; //carry flag
|
||||
BooleanBitField<uint16_t, 1> z; //zero flag
|
||||
NaturalBitField<uint16_t, 9, 8> alt; //instruction mode (composite flag)
|
||||
};
|
||||
uint16_t data = 0;
|
||||
|
||||
BitField<16, 1> z {&data}; //zero flag
|
||||
BitField<16, 2> cy {&data}; //carry flag
|
||||
BitField<16, 3> s {&data}; //sign flag
|
||||
BitField<16, 4> ov {&data}; //overflow flag
|
||||
BitField<16, 5> g {&data}; //go flag
|
||||
BitField<16, 6> r {&data}; //ROM r14 flag
|
||||
BitField<16, 8> alt1{&data}; //alt1 instruction mode
|
||||
BitField<16, 9> alt2{&data}; //alt2 instruction mode
|
||||
BitField<16,10> il {&data}; //immediate lower 8-bit flag
|
||||
BitField<16,11> ih {&data}; //immediate upper 8-bit flag
|
||||
BitField<16,12> b {&data}; //with flag
|
||||
BitField<16,15> irq {&data}; //interrupt flag
|
||||
|
||||
BitRange<16,8,9> alt{&data}; //composite instruction mode
|
||||
|
||||
inline operator uint() const { return data & 0x9f7e; }
|
||||
inline auto& operator=(const uint value) { return data = value, *this; }
|
||||
|
@@ -84,8 +84,9 @@ auto HG51B::cache() -> bool {
|
||||
|
||||
io.cache.address[io.cache.page] = address;
|
||||
for(uint offset : range(256)) {
|
||||
step(wait(address)); bit8(programRAM[io.cache.page][offset],0) = read(address++);
|
||||
step(wait(address)); bit8(programRAM[io.cache.page][offset],1) = read(address++);
|
||||
step(wait(address));
|
||||
programRAM[io.cache.page][offset] = read(address++) << 0;
|
||||
programRAM[io.cache.page][offset] |= read(address++) << 8;
|
||||
}
|
||||
return io.cache.enable = 0, true;
|
||||
}
|
||||
|
@@ -203,11 +203,11 @@ auto HG51B::instructionLD(uint15& out, uint8 imm) -> void {
|
||||
}
|
||||
|
||||
auto HG51B::instructionLDL(uint15& out, uint8 imm) -> void {
|
||||
bits(out,0-7) = imm;
|
||||
out = out & 0x7f00 | imm << 0;
|
||||
}
|
||||
|
||||
auto HG51B::instructionLDH(uint15& out, uint7 imm) -> void {
|
||||
bits(out,8-14) = imm;
|
||||
out = out & 0x00ff | (imm & 0x7f) << 8;
|
||||
}
|
||||
|
||||
auto HG51B::instructionMUL(uint7 reg) -> void {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
auto HG51B::readRegister(uint7 address) -> uint24 {
|
||||
switch(address) {
|
||||
case 0x01: return bits(r.mul,24-47);
|
||||
case 0x02: return bits(r.mul, 0-23);
|
||||
case 0x01: return r.mul >> 24 & 0xffffff;
|
||||
case 0x02: return r.mul >> 0 & 0xffffff;
|
||||
case 0x03: return r.mdr;
|
||||
case 0x08: return r.rom;
|
||||
case 0x0c: return r.ram;
|
||||
@@ -64,8 +64,8 @@ auto HG51B::readRegister(uint7 address) -> uint24 {
|
||||
|
||||
auto HG51B::writeRegister(uint7 address, uint24 data) -> void {
|
||||
switch(address) {
|
||||
case 0x01: bits(r.mul,24-47) = data; return;
|
||||
case 0x02: bits(r.mul, 0-23) = data; return;
|
||||
case 0x01: r.mul = r.mul & 0xffffffull | data << 24; return;
|
||||
case 0x02: r.mul = r.mul & ~0xffffffull | data << 0; return;
|
||||
case 0x03: r.mdr = data; return;
|
||||
case 0x08: r.rom = data; return;
|
||||
case 0x0c: r.ram = data; return;
|
||||
|
@@ -7,32 +7,33 @@ auto SPC700::instructionAbsoluteBitModify(uint3 mode) -> void {
|
||||
switch(mode) {
|
||||
case 0: //or addr:bit
|
||||
idle();
|
||||
CF |= bit1(data,bit);
|
||||
CF |= bool(data & 1 << bit);
|
||||
break;
|
||||
case 1: //or !addr:bit
|
||||
idle();
|
||||
CF |= !bit1(data,bit);
|
||||
CF |= !bool(data & 1 << bit);
|
||||
break;
|
||||
case 2: //and addr:bit
|
||||
CF &= bit1(data,bit);
|
||||
CF &= bool(data & 1 << bit);
|
||||
break;
|
||||
case 3: //and !addr:bit
|
||||
CF &= !bit1(data,bit);
|
||||
CF &= !bool(data & 1 << bit);
|
||||
break;
|
||||
case 4: //eor addr:bit
|
||||
idle();
|
||||
CF ^= bit1(data,bit);
|
||||
CF ^= bool(data & 1 << bit);
|
||||
break;
|
||||
case 5: //ld addr:bit
|
||||
CF = bit1(data,bit);
|
||||
CF = bool(data & 1 << bit);
|
||||
break;
|
||||
case 6: //st addr:bit
|
||||
idle();
|
||||
bit1(data,bit) = CF;
|
||||
data &= ~(1 << bit);
|
||||
data |= CF << bit;
|
||||
write(address, data);
|
||||
break;
|
||||
case 7: //not addr:bit
|
||||
bit1(data,bit) ^= 1;
|
||||
data ^= 1 << bit;
|
||||
write(address, data);
|
||||
break;
|
||||
}
|
||||
@@ -41,7 +42,8 @@ auto SPC700::instructionAbsoluteBitModify(uint3 mode) -> void {
|
||||
auto SPC700::instructionAbsoluteBitSet(uint3 bit, bool value) -> void {
|
||||
uint8 address = fetch();
|
||||
uint8 data = load(address);
|
||||
bit1(data,bit) = value;
|
||||
data &= ~(1 << bit);
|
||||
data |= value << bit;
|
||||
store(address, data);
|
||||
}
|
||||
|
||||
@@ -95,7 +97,7 @@ auto SPC700::instructionBranchBit(uint3 bit, bool match) -> void {
|
||||
uint8 data = load(address);
|
||||
idle();
|
||||
uint8 displacement = fetch();
|
||||
if(bit1(data,bit) != match) return;
|
||||
if(bool(data & 1 << bit) != match) return;
|
||||
idle();
|
||||
idle();
|
||||
PC += (int8)displacement;
|
||||
@@ -248,7 +250,7 @@ auto SPC700::instructionDirectDirectCompare(fpb op) -> void {
|
||||
uint8 target = fetch();
|
||||
uint8 lhs = load(target);
|
||||
lhs = alu(lhs, rhs);
|
||||
load(target);
|
||||
idle();
|
||||
}
|
||||
|
||||
auto SPC700::instructionDirectDirectModify(fpb op) -> void {
|
||||
@@ -272,7 +274,7 @@ auto SPC700::instructionDirectImmediateCompare(fpb op) -> void {
|
||||
uint8 address = fetch();
|
||||
uint8 data = load(address);
|
||||
data = alu(data, immediate);
|
||||
load(address);
|
||||
idle();
|
||||
}
|
||||
|
||||
auto SPC700::instructionDirectImmediateModify(fpb op) -> void {
|
||||
@@ -467,7 +469,7 @@ auto SPC700::instructionIndirectXCompareIndirectY(fpb op) -> void {
|
||||
uint8 rhs = load(Y);
|
||||
uint8 lhs = load(X);
|
||||
lhs = alu(lhs, rhs);
|
||||
load(X);
|
||||
idle();
|
||||
}
|
||||
|
||||
auto SPC700::instructionIndirectXWriteIndirectY(fpb op) -> void {
|
||||
|
@@ -134,14 +134,14 @@ struct SPC700 {
|
||||
}
|
||||
|
||||
inline auto& operator=(uint8 data) {
|
||||
c = bit1(data,0);
|
||||
z = bit1(data,1);
|
||||
i = bit1(data,2);
|
||||
h = bit1(data,3);
|
||||
b = bit1(data,4);
|
||||
p = bit1(data,5);
|
||||
v = bit1(data,6);
|
||||
n = bit1(data,7);
|
||||
c = data & 0x01;
|
||||
z = data & 0x02;
|
||||
i = data & 0x04;
|
||||
h = data & 0x08;
|
||||
b = data & 0x10;
|
||||
p = data & 0x20;
|
||||
v = data & 0x40;
|
||||
n = data & 0x80;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
@@ -37,12 +37,12 @@ struct uPD96050 {
|
||||
}
|
||||
|
||||
inline auto operator=(uint16 data) -> Flag& {
|
||||
ov0 = bit1(data,0);
|
||||
ov1 = bit1(data,1);
|
||||
z = bit1(data,2);
|
||||
c = bit1(data,3);
|
||||
s0 = bit1(data,4);
|
||||
s1 = bit1(data,5);
|
||||
ov0 = data >> 0 & 1;
|
||||
ov1 = data >> 1 & 1;
|
||||
z = data >> 2 & 1;
|
||||
c = data >> 3 & 1;
|
||||
s0 = data >> 4 & 1;
|
||||
s1 = data >> 5 & 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -64,17 +64,17 @@ struct uPD96050 {
|
||||
}
|
||||
|
||||
inline auto operator=(uint16 data) -> Status& {
|
||||
p0 = bit1(data, 0);
|
||||
p1 = bit1(data, 1);
|
||||
ei = bit1(data, 7);
|
||||
sic = bit1(data, 8);
|
||||
soc = bit1(data, 9);
|
||||
drc = bit1(data,10);
|
||||
dma = bit1(data,11);
|
||||
drs = bit1(data,12);
|
||||
usf0 = bit1(data,13);
|
||||
usf1 = bit1(data,14);
|
||||
rqm = bit1(data,15);
|
||||
p0 = data >> 0 & 1;
|
||||
p1 = data >> 1 & 1;
|
||||
ei = data >> 7 & 1;
|
||||
sic = data >> 8 & 1;
|
||||
soc = data >> 9 & 1;
|
||||
drc = data >> 10 & 1;
|
||||
dma = data >> 11 & 1;
|
||||
drs = data >> 12 & 1;
|
||||
usf0 = data >> 13 & 1;
|
||||
usf1 = data >> 14 & 1;
|
||||
rqm = data >> 15 & 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@@ -31,10 +31,10 @@ auto WDC65816::disassemble(uint24 address, bool e, bool m, bool x) -> string {
|
||||
return data | readWord(address + 1) << 8;
|
||||
};
|
||||
|
||||
auto opcode = read(address); address(0,15)++;
|
||||
auto operand0 = read(address); address(0,15)++;
|
||||
auto operand1 = read(address); address(0,15)++;
|
||||
auto operand2 = read(address); address(0,15)++;
|
||||
auto opcode = read(address); address.bit(0,15)++;
|
||||
auto operand0 = read(address); address.bit(0,15)++;
|
||||
auto operand1 = read(address); address.bit(0,15)++;
|
||||
auto operand2 = read(address); address.bit(0,15)++;
|
||||
|
||||
uint8 operandByte = operand0 << 0;
|
||||
uint16 operandWord = operand0 << 0 | operand1 << 8;
|
||||
@@ -295,9 +295,9 @@ auto WDC65816::disassemble(uint24 address, bool e, bool m, bool x) -> string {
|
||||
op(0x71, "adc", indirectIndexedY)
|
||||
op(0x72, "adc", indirect)
|
||||
op(0x73, "adc", stackIndirect)
|
||||
op(0x74, "stz", absoluteX)
|
||||
op(0x75, "adc", absoluteX)
|
||||
op(0x76, "ror", absoluteX)
|
||||
op(0x74, "stz", directX)
|
||||
op(0x75, "adc", directX)
|
||||
op(0x76, "ror", directX)
|
||||
op(0x77, "adc", indirectLongY)
|
||||
op(0x78, "sei", implied)
|
||||
op(0x79, "adc", absoluteY)
|
||||
|
@@ -8,7 +8,7 @@ N push(PC.b);
|
||||
IF = 1;
|
||||
DF = 0;
|
||||
PC.l = read(r.vector + 0);
|
||||
PC.h = read(r.vector + 1);
|
||||
L PC.h = read(r.vector + 1);
|
||||
PC.b = 0x00;
|
||||
idleJump();
|
||||
}
|
||||
|
@@ -45,7 +45,7 @@ auto WDC65816::instructionLongRead8(alu8 op, r16 I) -> void {
|
||||
V.l = fetch();
|
||||
V.h = fetch();
|
||||
V.b = fetch();
|
||||
L W.l = read(V.d + I.w + 0);
|
||||
L W.l = readLong(V.d + I.w + 0);
|
||||
alu(W.l);
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@ auto WDC65816::instructionLongRead16(alu16 op, r16 I) -> void {
|
||||
V.l = fetch();
|
||||
V.h = fetch();
|
||||
V.b = fetch();
|
||||
W.l = read(V.d + I.w + 0);
|
||||
L W.h = read(V.d + I.w + 1);
|
||||
W.l = readLong(V.d + I.w + 0);
|
||||
L W.h = readLong(V.d + I.w + 1);
|
||||
alu(W.w);
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ auto WDC65816::instructionIndirectLongRead8(alu8 op, r16 I) -> void {
|
||||
V.l = readDirectN(U.l + 0);
|
||||
V.h = readDirectN(U.l + 1);
|
||||
V.b = readDirectN(U.l + 2);
|
||||
L W.l = read(V.d + I.w + 0);
|
||||
L W.l = readLong(V.d + I.w + 0);
|
||||
alu(W.l);
|
||||
}
|
||||
|
||||
@@ -167,8 +167,8 @@ auto WDC65816::instructionIndirectLongRead16(alu16 op, r16 I) -> void {
|
||||
V.l = readDirectN(U.l + 0);
|
||||
V.h = readDirectN(U.l + 1);
|
||||
V.b = readDirectN(U.l + 2);
|
||||
W.l = read(V.d + I.w + 0);
|
||||
L W.h = read(V.d + I.w + 1);
|
||||
W.l = readLong(V.d + I.w + 0);
|
||||
L W.h = readLong(V.d + I.w + 1);
|
||||
alu(W.w);
|
||||
}
|
||||
|
||||
|
@@ -30,15 +30,15 @@ auto WDC65816::instructionLongWrite8(r16 I) -> void {
|
||||
V.l = fetch();
|
||||
V.h = fetch();
|
||||
V.b = fetch();
|
||||
L write(V.d + I.w + 0, A.l);
|
||||
L writeLong(V.d + I.w + 0, A.l);
|
||||
}
|
||||
|
||||
auto WDC65816::instructionLongWrite16(r16 I) -> void {
|
||||
V.l = fetch();
|
||||
V.h = fetch();
|
||||
V.b = fetch();
|
||||
write(V.d + I.w + 0, A.l);
|
||||
L write(V.d + I.w + 1, A.h);
|
||||
writeLong(V.d + I.w + 0, A.l);
|
||||
L writeLong(V.d + I.w + 1, A.h);
|
||||
}
|
||||
|
||||
auto WDC65816::instructionDirectWrite8(r16 F) -> void {
|
||||
@@ -130,7 +130,7 @@ auto WDC65816::instructionIndirectLongWrite8(r16 I) -> void {
|
||||
V.l = readDirectN(U.l + 0);
|
||||
V.h = readDirectN(U.l + 1);
|
||||
V.b = readDirectN(U.l + 2);
|
||||
L write(V.d + I.w + 0, A.l);
|
||||
L writeLong(V.d + I.w + 0, A.l);
|
||||
}
|
||||
|
||||
auto WDC65816::instructionIndirectLongWrite16(r16 I) -> void {
|
||||
@@ -139,8 +139,8 @@ auto WDC65816::instructionIndirectLongWrite16(r16 I) -> void {
|
||||
V.l = readDirectN(U.l + 0);
|
||||
V.h = readDirectN(U.l + 1);
|
||||
V.b = readDirectN(U.l + 2);
|
||||
write(V.d + I.w + 0, A.l);
|
||||
L write(V.d + I.w + 1, A.h);
|
||||
writeLong(V.d + I.w + 0, A.l);
|
||||
L writeLong(V.d + I.w + 1, A.h);
|
||||
}
|
||||
|
||||
auto WDC65816::instructionStackWrite8() -> void {
|
||||
|
@@ -50,31 +50,39 @@ auto WDC65816::pushN(uint8 data) -> void {
|
||||
}
|
||||
|
||||
auto WDC65816::readDirect(uint address) -> uint8 {
|
||||
if(EF && !D.l) return read(D.w | uint8(address));
|
||||
return read(uint16(D.w + address));
|
||||
if(EF && !D.l) return read(D.w | address & 0xff);
|
||||
return read(D.w + address & 0xffff);
|
||||
}
|
||||
|
||||
auto WDC65816::writeDirect(uint address, uint8 data) -> void {
|
||||
if(EF && !D.l) return write(D.w | uint8(address), data);
|
||||
write(uint16(D.w + address), data);
|
||||
if(EF && !D.l) return write(D.w | address & 0xff, data);
|
||||
write(D.w + address & 0xffff, data);
|
||||
}
|
||||
|
||||
auto WDC65816::readDirectN(uint address) -> uint8 {
|
||||
return read(uint16(D.w + address));
|
||||
return read(D.w + address & 0xffff);
|
||||
}
|
||||
|
||||
auto WDC65816::readBank(uint address) -> uint8 {
|
||||
return read((B << 16) + address);
|
||||
return read((B << 16) + address & 0xffffff);
|
||||
}
|
||||
|
||||
auto WDC65816::writeBank(uint address, uint8 data) -> void {
|
||||
write((B << 16) + address, data);
|
||||
write((B << 16) + address & 0xffffff, data);
|
||||
}
|
||||
|
||||
auto WDC65816::readLong(uint address) -> uint8 {
|
||||
return read(address & 0xffffff);
|
||||
}
|
||||
|
||||
auto WDC65816::writeLong(uint address, uint8 data) -> void {
|
||||
write(address & 0xffffff, data);
|
||||
}
|
||||
|
||||
auto WDC65816::readStack(uint address) -> uint8 {
|
||||
return read(uint16(S.w + address));
|
||||
return read(S.w + address & 0xffff);
|
||||
}
|
||||
|
||||
auto WDC65816::writeStack(uint address, uint8 data) -> void {
|
||||
write(uint16(S.w + address), data);
|
||||
write(S.w + address & 0xffff, data);
|
||||
}
|
||||
|
@@ -10,8 +10,8 @@ struct WDC65816 {
|
||||
virtual auto idle() -> void = 0;
|
||||
virtual auto idleBranch() -> void {}
|
||||
virtual auto idleJump() -> void {}
|
||||
virtual auto read(uint24 addr) -> uint8 = 0;
|
||||
virtual auto write(uint24 addr, uint8 data) -> void = 0;
|
||||
virtual auto read(uint addr) -> uint8 = 0;
|
||||
virtual auto write(uint addr, uint8 data) -> void = 0;
|
||||
virtual auto lastCycle() -> void = 0;
|
||||
virtual auto interruptPending() const -> bool = 0;
|
||||
virtual auto interrupt() -> void;
|
||||
@@ -19,6 +19,9 @@ struct WDC65816 {
|
||||
|
||||
virtual auto readDisassembler(uint addr) -> uint8 { return 0; }
|
||||
|
||||
inline auto irq() const -> bool { return r.irq; }
|
||||
inline auto irq(bool line) -> void { r.irq = line; }
|
||||
|
||||
using r8 = uint8;
|
||||
|
||||
union r16 {
|
||||
@@ -44,22 +47,24 @@ struct WDC65816 {
|
||||
auto power() -> void;
|
||||
|
||||
//memory.cpp
|
||||
inline auto idleIRQ() -> void;
|
||||
inline auto idle2() -> void;
|
||||
inline auto idle4(uint16 x, uint16 y) -> void;
|
||||
inline auto idle6(uint16 address) -> void;
|
||||
inline auto fetch() -> uint8;
|
||||
inline auto pull() -> uint8;
|
||||
auto push(uint8 data) -> void;
|
||||
inline auto pullN() -> uint8;
|
||||
inline auto pushN(uint8 data) -> void;
|
||||
inline auto readDirect(uint address) -> uint8;
|
||||
inline auto writeDirect(uint address, uint8 data) -> void;
|
||||
inline auto readDirectN(uint address) -> uint8;
|
||||
inline auto readBank(uint address) -> uint8;
|
||||
inline auto writeBank(uint address, uint8 data) -> void;
|
||||
inline auto readStack(uint address) -> uint8;
|
||||
inline auto writeStack(uint address, uint8 data) -> void;
|
||||
alwaysinline auto idleIRQ() -> void;
|
||||
alwaysinline auto idle2() -> void;
|
||||
alwaysinline auto idle4(uint16 x, uint16 y) -> void;
|
||||
alwaysinline auto idle6(uint16 address) -> void;
|
||||
alwaysinline auto fetch() -> uint8;
|
||||
alwaysinline auto pull() -> uint8;
|
||||
auto push(uint8 data) -> void;
|
||||
alwaysinline auto pullN() -> uint8;
|
||||
alwaysinline auto pushN(uint8 data) -> void;
|
||||
alwaysinline auto readDirect(uint address) -> uint8;
|
||||
alwaysinline auto writeDirect(uint address, uint8 data) -> void;
|
||||
alwaysinline auto readDirectN(uint address) -> uint8;
|
||||
alwaysinline auto readBank(uint address) -> uint8;
|
||||
alwaysinline auto writeBank(uint address, uint8 data) -> void;
|
||||
alwaysinline auto readLong(uint address) -> uint8;
|
||||
alwaysinline auto writeLong(uint address, uint8 data) -> void;
|
||||
alwaysinline auto readStack(uint address) -> uint8;
|
||||
alwaysinline auto writeStack(uint address, uint8 data) -> void;
|
||||
|
||||
//algorithms.cpp
|
||||
using alu8 = auto (WDC65816::*)( uint8) -> uint8;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
processors += wdc65816 spc700 arm7tdmi gsu hg51b upd96050
|
||||
processors += wdc65816 spc700 arm7tdmi
|
||||
|
||||
objects += sfc-interface sfc-system sfc-controller
|
||||
objects += sfc-cartridge sfc-memory
|
||||
|
@@ -37,6 +37,17 @@ auto Cartridge::titles() const -> vector<string> {
|
||||
return titles;
|
||||
}
|
||||
|
||||
auto Cartridge::title() const -> string {
|
||||
if(slotGameBoy.label) return slotGameBoy.label;
|
||||
if(has.MCC && slotBSMemory.label) return slotBSMemory.label;
|
||||
if(slotBSMemory.label) return {game.label, " + ", slotBSMemory.label};
|
||||
if(slotSufamiTurboA.label && slotSufamiTurboB.label) return {slotSufamiTurboA.label, " + ", slotSufamiTurboB.label};
|
||||
if(slotSufamiTurboA.label) return slotSufamiTurboA.label;
|
||||
if(slotSufamiTurboB.label) return slotSufamiTurboB.label;
|
||||
if(has.Cx4 || has.DSP1 || has.DSP2 || has.DSP4 || has.ST0010) return {"[HLE] ", game.label};
|
||||
return game.label;
|
||||
}
|
||||
|
||||
auto Cartridge::load() -> bool {
|
||||
information = {};
|
||||
has = {};
|
||||
@@ -54,6 +65,7 @@ auto Cartridge::load() -> bool {
|
||||
if(auto fp = platform->open(ID::SuperFamicom, "manifest.bml", File::Read, File::Required)) {
|
||||
game.load(fp->reads());
|
||||
} else return false;
|
||||
|
||||
loadCartridge(game.document);
|
||||
|
||||
//Game Boy
|
||||
|
@@ -1,10 +1,12 @@
|
||||
struct Cartridge {
|
||||
auto pathID() const -> uint { return information.pathID; }
|
||||
auto region() const -> string { return information.region; }
|
||||
auto headerTitle() const -> string { return game.title; }
|
||||
|
||||
auto hashes() const -> vector<string>;
|
||||
auto manifests() const -> vector<string>;
|
||||
auto titles() const -> vector<string>;
|
||||
auto title() const -> string;
|
||||
|
||||
auto load() -> bool;
|
||||
auto save() -> void;
|
||||
|
@@ -141,7 +141,7 @@ auto Cartridge::loadMap(Markup::Node map, T& memory) -> uint {
|
||||
auto Cartridge::loadMap(
|
||||
Markup::Node map,
|
||||
const function<uint8 (uint, uint8)>& reader,
|
||||
const function<void (uint, uint8)>& writer
|
||||
const function<void (uint, uint8)>& writer
|
||||
) -> uint {
|
||||
auto addr = map["address"].text();
|
||||
auto size = map["size"].natural();
|
||||
@@ -428,7 +428,7 @@ auto Cartridge::loadHitachiDSP(Markup::Node node, uint roms) -> void {
|
||||
}
|
||||
}
|
||||
|
||||
if(configuration.hacks.coprocessors.hle) {
|
||||
if(configuration.hacks.coprocessor.preferHLE) {
|
||||
has.Cx4 = true;
|
||||
for(auto map : node.find("map")) {
|
||||
loadMap(map, {&Cx4::read, &cx4}, {&Cx4::write, &cx4});
|
||||
@@ -503,7 +503,7 @@ auto Cartridge::loaduPD7725(Markup::Node node) -> void {
|
||||
}
|
||||
}
|
||||
|
||||
if(failed || configuration.hacks.coprocessors.hle) {
|
||||
if(failed || configuration.hacks.coprocessor.preferHLE) {
|
||||
auto manifest = BML::serialize(game.document);
|
||||
if(manifest.find("identifier: DSP1")) { //also matches DSP1B
|
||||
has.DSP1 = true;
|
||||
@@ -530,7 +530,7 @@ auto Cartridge::loaduPD7725(Markup::Node node) -> void {
|
||||
|
||||
if(failed) {
|
||||
//throw an error to the user
|
||||
platform->open(ID::SuperFamicom, "<DSP1-4>", File::Read, File::Required);
|
||||
platform->open(ID::SuperFamicom, "DSP3", File::Read, File::Required);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -579,11 +579,11 @@ auto Cartridge::loaduPD96050(Markup::Node node) -> void {
|
||||
if(auto file = game.memory(memory)) {
|
||||
if(auto fp = platform->open(ID::SuperFamicom, file->name(), File::Read)) {
|
||||
for(auto n : range(2048)) necdsp.dataROM[n] = fp->readl(2);
|
||||
} else failed = false;
|
||||
} else failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(failed || configuration.hacks.coprocessors.hle) {
|
||||
if(failed || configuration.hacks.coprocessor.preferHLE) {
|
||||
auto manifest = BML::serialize(game.document);
|
||||
if(manifest.find("identifier: ST010")) {
|
||||
has.ST0010 = true;
|
||||
@@ -598,7 +598,7 @@ auto Cartridge::loaduPD96050(Markup::Node node) -> void {
|
||||
|
||||
if(failed) {
|
||||
//throw an error to the user
|
||||
platform->open(ID::SuperFamicom, "<ST010-011>", File::Read, File::Required);
|
||||
platform->open(ID::SuperFamicom, "ST011", File::Read, File::Required);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -6,9 +6,16 @@ namespace SuperFamicom {
|
||||
#include "serialization.cpp"
|
||||
ArmDSP armdsp;
|
||||
|
||||
auto ArmDSP::synchronizeCPU() -> void {
|
||||
if(clock >= 0) scheduler.resume(cpu.thread);
|
||||
}
|
||||
|
||||
auto ArmDSP::Enter() -> void {
|
||||
armdsp.boot();
|
||||
while(true) scheduler.synchronize(), armdsp.main();
|
||||
while(true) {
|
||||
scheduler.synchronize();
|
||||
armdsp.main();
|
||||
}
|
||||
}
|
||||
|
||||
auto ArmDSP::boot() -> void {
|
||||
@@ -32,8 +39,8 @@ auto ArmDSP::main() -> void {
|
||||
|
||||
auto ArmDSP::step(uint clocks) -> void {
|
||||
if(bridge.timer && --bridge.timer == 0);
|
||||
Thread::step(clocks);
|
||||
synchronize(cpu);
|
||||
clock += clocks * (uint64_t)cpu.frequency;
|
||||
synchronizeCPU();
|
||||
}
|
||||
|
||||
//MMIO: 00-3f,80-bf:3800-38ff
|
||||
@@ -41,7 +48,7 @@ auto ArmDSP::step(uint clocks) -> void {
|
||||
//a0 ignored
|
||||
|
||||
auto ArmDSP::read(uint addr, uint8) -> uint8 {
|
||||
cpu.synchronize(*this);
|
||||
cpu.synchronizeCoprocessors();
|
||||
|
||||
uint8 data = 0x00;
|
||||
addr &= 0xff06;
|
||||
@@ -65,7 +72,7 @@ auto ArmDSP::read(uint addr, uint8) -> uint8 {
|
||||
}
|
||||
|
||||
auto ArmDSP::write(uint addr, uint8 data) -> void {
|
||||
cpu.synchronize(*this);
|
||||
cpu.synchronizeCoprocessors();
|
||||
|
||||
addr &= 0xff06;
|
||||
|
||||
|
@@ -7,6 +7,7 @@
|
||||
struct ArmDSP : Processor::ARM7TDMI, Thread {
|
||||
#include "registers.hpp"
|
||||
|
||||
auto synchronizeCPU() -> void;
|
||||
static auto Enter() -> void;
|
||||
auto boot() -> void;
|
||||
auto main() -> void;
|
||||
|
@@ -83,9 +83,9 @@ auto ArmDSP::set(uint mode, uint32 addr, uint32 word) -> void {
|
||||
|
||||
if(addr == 0x4000'0010) bridge.signal = true;
|
||||
|
||||
if(addr == 0x4000'0020) bit8(bridge.timerlatch,0) = word;
|
||||
if(addr == 0x4000'0024) bit8(bridge.timerlatch,1) = word;
|
||||
if(addr == 0x4000'0028) bit8(bridge.timerlatch,2) = word;
|
||||
if(addr == 0x4000'0020) bridge.timerlatch = bridge.timerlatch & 0xffff00 | word << 0;
|
||||
if(addr == 0x4000'0024) bridge.timerlatch = bridge.timerlatch & 0xff00ff | word << 8;
|
||||
if(addr == 0x4000'0028) bridge.timerlatch = bridge.timerlatch & 0x00ffff | word << 16;
|
||||
|
||||
if(addr == 0x4000'002c) bridge.timer = bridge.timerlatch;
|
||||
}
|
||||
|
@@ -7,8 +7,15 @@ namespace SuperFamicom {
|
||||
#include "serialization.cpp"
|
||||
EpsonRTC epsonrtc;
|
||||
|
||||
auto EpsonRTC::synchronizeCPU() -> void {
|
||||
if(clock >= 0) scheduler.resume(cpu.thread);
|
||||
}
|
||||
|
||||
auto EpsonRTC::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), epsonrtc.main();
|
||||
while(true) {
|
||||
scheduler.synchronize();
|
||||
epsonrtc.main();
|
||||
}
|
||||
}
|
||||
|
||||
auto EpsonRTC::main() -> void {
|
||||
@@ -27,7 +34,11 @@ auto EpsonRTC::main() -> void {
|
||||
}
|
||||
|
||||
step(1);
|
||||
synchronize(cpu);
|
||||
synchronizeCPU();
|
||||
}
|
||||
|
||||
auto EpsonRTC::step(uint clocks) -> void {
|
||||
clock += clocks * (uint64_t)cpu.frequency;
|
||||
}
|
||||
|
||||
auto EpsonRTC::initialize() -> void {
|
||||
@@ -127,7 +138,7 @@ auto EpsonRTC::synchronize(uint64 timestamp) -> void {
|
||||
}
|
||||
|
||||
auto EpsonRTC::read(uint addr, uint8 data) -> uint8 {
|
||||
cpu.synchronize(*this);
|
||||
cpu.synchronizeCoprocessors();
|
||||
addr &= 3;
|
||||
|
||||
if(addr == 0) {
|
||||
@@ -152,7 +163,7 @@ auto EpsonRTC::read(uint addr, uint8 data) -> uint8 {
|
||||
}
|
||||
|
||||
auto EpsonRTC::write(uint addr, uint8 data) -> void {
|
||||
cpu.synchronize(*this);
|
||||
cpu.synchronizeCoprocessors();
|
||||
addr &= 3, data &= 15;
|
||||
|
||||
if(addr == 0) {
|
||||
|
@@ -1,10 +1,10 @@
|
||||
//Epson RTC-4513 Real-Time Clock
|
||||
|
||||
struct EpsonRTC : Thread {
|
||||
using Thread::synchronize;
|
||||
|
||||
auto synchronizeCPU() -> void;
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
|
||||
auto initialize() -> void;
|
||||
auto power() -> void;
|
||||
|
@@ -5,8 +5,15 @@ namespace SuperFamicom {
|
||||
#include "serialization.cpp"
|
||||
Event event;
|
||||
|
||||
auto Event::synchronizeCPU() -> void {
|
||||
if(clock >= 0) scheduler.resume(cpu.thread);
|
||||
}
|
||||
|
||||
auto Event::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), event.main();
|
||||
while(true) {
|
||||
scheduler.synchronize();
|
||||
event.main();
|
||||
}
|
||||
}
|
||||
|
||||
auto Event::main() -> void {
|
||||
@@ -26,7 +33,11 @@ auto Event::main() -> void {
|
||||
}
|
||||
|
||||
step(1);
|
||||
synchronize(cpu);
|
||||
synchronizeCPU();
|
||||
}
|
||||
|
||||
auto Event::step(uint clocks) -> void {
|
||||
clock += clocks * (uint64_t)cpu.frequency;
|
||||
}
|
||||
|
||||
auto Event::unload() -> void {
|
||||
@@ -40,7 +51,7 @@ auto Event::power() -> void {
|
||||
create(Event::Enter, 1);
|
||||
|
||||
//DIP switches 0-3 control the time: 3 minutes + 0-15 extra minutes
|
||||
timer = (3 + bits(dip.value,0-3)) * 60; //in seconds
|
||||
timer = (3 + (dip.value & 15)) * 60; //in seconds
|
||||
//DIP switches 4-5 serve an unknown purpose
|
||||
//DIP switches 6-7 are not connected
|
||||
|
||||
|
@@ -15,8 +15,10 @@
|
||||
|
||||
struct Event : Thread {
|
||||
//event.cpp
|
||||
auto synchronizeCPU() -> void;
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
auto unload() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#include <sfc/sfc.hpp>
|
||||
#include <processor/hg51b/hg51b.cpp>
|
||||
|
||||
namespace SuperFamicom {
|
||||
|
||||
@@ -7,19 +8,26 @@ namespace SuperFamicom {
|
||||
#include "data-rom.cpp"
|
||||
HitachiDSP hitachidsp;
|
||||
|
||||
auto HitachiDSP::synchronizeCPU() -> void {
|
||||
if(clock >= 0) scheduler.resume(cpu.thread);
|
||||
}
|
||||
|
||||
auto HitachiDSP::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), hitachidsp.main();
|
||||
while(true) {
|
||||
scheduler.synchronize();
|
||||
hitachidsp.main();
|
||||
}
|
||||
}
|
||||
|
||||
auto HitachiDSP::step(uint clocks) -> void {
|
||||
HG51B::step(clocks);
|
||||
Thread::step(clocks);
|
||||
synchronize(cpu);
|
||||
clock += clocks * (uint64_t)cpu.frequency;
|
||||
synchronizeCPU();
|
||||
}
|
||||
|
||||
auto HitachiDSP::halt() -> void {
|
||||
HG51B::halt();
|
||||
if(io.irq == 0) r.i = 1, cpu.r.irq = 1;
|
||||
if(io.irq == 0) cpu.irq(r.i = 1);
|
||||
}
|
||||
|
||||
auto HitachiDSP::unload() -> void {
|
||||
|
@@ -3,6 +3,7 @@ struct HitachiDSP : Processor::HG51B, Thread {
|
||||
WritableMemory ram;
|
||||
|
||||
//hitachidsp.cpp
|
||||
auto synchronizeCPU() -> void;
|
||||
static auto Enter() -> void;
|
||||
auto step(uint clocks) -> void override;
|
||||
auto halt() -> void override;
|
||||
|
@@ -134,21 +134,21 @@ auto HitachiDSP::readIO(uint address, uint8 data) -> uint8 {
|
||||
|
||||
//IO
|
||||
switch(address) {
|
||||
case 0x7f40: return bit8(io.dma.source,0);
|
||||
case 0x7f41: return bit8(io.dma.source,1);
|
||||
case 0x7f42: return bit8(io.dma.source,2);
|
||||
case 0x7f43: return bit8(io.dma.length,0);
|
||||
case 0x7f44: return bit8(io.dma.length,1);
|
||||
case 0x7f45: return bit8(io.dma.target,0);
|
||||
case 0x7f46: return bit8(io.dma.target,1);
|
||||
case 0x7f47: return bit8(io.dma.target,2);
|
||||
case 0x7f40: return io.dma.source >> 0;
|
||||
case 0x7f41: return io.dma.source >> 8;
|
||||
case 0x7f42: return io.dma.source >> 16;
|
||||
case 0x7f43: return io.dma.length >> 0;
|
||||
case 0x7f44: return io.dma.length >> 8;
|
||||
case 0x7f45: return io.dma.target >> 0;
|
||||
case 0x7f46: return io.dma.target >> 8;
|
||||
case 0x7f47: return io.dma.target >> 16;
|
||||
case 0x7f48: return io.cache.page;
|
||||
case 0x7f49: return bit8(io.cache.base,0);
|
||||
case 0x7f4a: return bit8(io.cache.base,1);
|
||||
case 0x7f4b: return bit8(io.cache.base,2);
|
||||
case 0x7f49: return io.cache.base >> 0;
|
||||
case 0x7f4a: return io.cache.base >> 8;
|
||||
case 0x7f4b: return io.cache.base >> 16;
|
||||
case 0x7f4c: return io.cache.lock[0] << 0 | io.cache.lock[1] << 1;
|
||||
case 0x7f4d: return bit8(io.cache.pb,0);
|
||||
case 0x7f4e: return bit8(io.cache.pb,1);
|
||||
case 0x7f4d: return io.cache.pb >> 0;
|
||||
case 0x7f4e: return io.cache.pb >> 8;
|
||||
case 0x7f4f: return io.cache.pc;
|
||||
case 0x7f50: return io.wait.ram << 0 | io.wait.rom << 4;
|
||||
case 0x7f51: return io.irq;
|
||||
@@ -166,7 +166,11 @@ auto HitachiDSP::readIO(uint address, uint8 data) -> uint8 {
|
||||
//registers
|
||||
if((address >= 0x7f80 && address <= 0x7faf) || (address >= 0x7fc0 && address <= 0x7fef)) {
|
||||
address &= 0x3f;
|
||||
return r.gpr[address / 3].byte(address % 3);
|
||||
switch(address % 3) {
|
||||
case 0: return r.gpr[address / 3] >> 0;
|
||||
case 1: return r.gpr[address / 3] >> 8;
|
||||
case 2: return r.gpr[address / 3] >> 16;
|
||||
}
|
||||
}
|
||||
|
||||
return 0x00;
|
||||
@@ -177,16 +181,16 @@ auto HitachiDSP::writeIO(uint address, uint8 data) -> void {
|
||||
|
||||
//IO
|
||||
switch(address) {
|
||||
case 0x7f40: bit8(io.dma.source,0) = data; return;
|
||||
case 0x7f41: bit8(io.dma.source,1) = data; return;
|
||||
case 0x7f42: bit8(io.dma.source,2) = data; return;
|
||||
case 0x7f40: io.dma.source = io.dma.source & 0xffff00 | data << 0; return;
|
||||
case 0x7f41: io.dma.source = io.dma.source & 0xff00ff | data << 8; return;
|
||||
case 0x7f42: io.dma.source = io.dma.source & 0x00ffff | data << 16; return;
|
||||
|
||||
case 0x7f43: bit8(io.dma.length,0) = data; return;
|
||||
case 0x7f44: bit8(io.dma.length,1) = data; return;
|
||||
case 0x7f43: io.dma.length = io.dma.length & 0xff00 | data << 0; return;
|
||||
case 0x7f44: io.dma.length = io.dma.length & 0x00ff | data << 8; return;
|
||||
|
||||
case 0x7f45: bit8(io.dma.target,0) = data; return;
|
||||
case 0x7f46: bit8(io.dma.target,1) = data; return;
|
||||
case 0x7f47: bit8(io.dma.target,2) = data;
|
||||
case 0x7f45: io.dma.target = io.dma.target & 0xffff00 | data << 0; return;
|
||||
case 0x7f46: io.dma.target = io.dma.target & 0xff00ff | data << 8; return;
|
||||
case 0x7f47: io.dma.target = io.dma.target & 0x00ffff | data << 16;
|
||||
if(io.halt) io.dma.enable = 1;
|
||||
return;
|
||||
|
||||
@@ -195,17 +199,17 @@ auto HitachiDSP::writeIO(uint address, uint8 data) -> void {
|
||||
if(io.halt) io.cache.enable = 1;
|
||||
return;
|
||||
|
||||
case 0x7f49: bit8(io.cache.base,0) = data; return;
|
||||
case 0x7f4a: bit8(io.cache.base,1) = data; return;
|
||||
case 0x7f4b: bit8(io.cache.base,2) = data; return;
|
||||
case 0x7f49: io.cache.base = io.cache.base & 0xffff00 | data << 0; return;
|
||||
case 0x7f4a: io.cache.base = io.cache.base & 0xff00ff | data << 8; return;
|
||||
case 0x7f4b: io.cache.base = io.cache.base & 0x00ffff | data << 16; return;
|
||||
|
||||
case 0x7f4c:
|
||||
io.cache.lock[0] = bit1(data,0);
|
||||
io.cache.lock[1] = bit1(data,1);
|
||||
io.cache.lock[0] = bool(data & 1);
|
||||
io.cache.lock[1] = bool(data & 2);
|
||||
return;
|
||||
|
||||
case 0x7f4d: bit8(io.cache.pb,0) = data; return;
|
||||
case 0x7f4e: bit8(io.cache.pb,1) = data; return;
|
||||
case 0x7f4d: io.cache.pb = io.cache.pb & 0xff00 | data << 0; return;
|
||||
case 0x7f4e: io.cache.pb = io.cache.pb & 0x00ff | data << 8; return;
|
||||
|
||||
case 0x7f4f:
|
||||
io.cache.pc = data;
|
||||
@@ -217,13 +221,13 @@ auto HitachiDSP::writeIO(uint address, uint8 data) -> void {
|
||||
return;
|
||||
|
||||
case 0x7f50:
|
||||
io.wait.ram = bits(data,0-2);
|
||||
io.wait.rom = bits(data,4-6);
|
||||
io.wait.ram = data >> 0 & 7;
|
||||
io.wait.rom = data >> 4 & 7;
|
||||
return;
|
||||
|
||||
case 0x7f51:
|
||||
io.irq = data & 1;
|
||||
if(io.irq == 1) r.i = 0, cpu.r.irq = 0;
|
||||
if(io.irq == 1) cpu.irq(r.i = 0);
|
||||
return;
|
||||
|
||||
case 0x7f52:
|
||||
@@ -259,6 +263,11 @@ auto HitachiDSP::writeIO(uint address, uint8 data) -> void {
|
||||
//registers
|
||||
if((address >= 0x7f80 && address <= 0x7faf) || (address >= 0x7fc0 && address <= 0x7fef)) {
|
||||
address &= 0x3f;
|
||||
bit8(r.gpr[address / 3],address % 3) = data;
|
||||
switch(address % 3) {
|
||||
case 0: r.gpr[address / 3] = r.gpr[address / 3] & 0xffff00 | data << 0; break;
|
||||
case 1: r.gpr[address / 3] = r.gpr[address / 3] & 0xff00ff | data << 8; break;
|
||||
case 2: r.gpr[address / 3] = r.gpr[address / 3] & 0x00ffff | data << 16; break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@@ -46,6 +46,10 @@ namespace SameBoy {
|
||||
}
|
||||
}
|
||||
|
||||
auto ICD::synchronizeCPU() -> void {
|
||||
if(clock >= 0) scheduler.resume(cpu.thread);
|
||||
}
|
||||
|
||||
auto ICD::Enter() -> void {
|
||||
while(true) {
|
||||
scheduler.synchronize();
|
||||
@@ -58,26 +62,34 @@ auto ICD::main() -> void {
|
||||
auto clocks = GB_run(&sameboy);
|
||||
step(clocks >> 1);
|
||||
} else { //DMG halted
|
||||
stream->sample(float(0.0), float(0.0));
|
||||
apuWrite(0.0, 0.0);
|
||||
step(128);
|
||||
}
|
||||
synchronize(cpu);
|
||||
synchronizeCPU();
|
||||
}
|
||||
|
||||
auto ICD::step(uint clocks) -> void {
|
||||
clock += clocks * (uint64_t)cpu.frequency;
|
||||
}
|
||||
|
||||
//SGB1 uses the CPU oscillator (~2.4% faster than a real Game Boy)
|
||||
//SGB2 uses a dedicated oscillator (same speed as a real Game Boy)
|
||||
auto ICD::clockFrequency() const -> uint {
|
||||
return Frequency ? Frequency : system.cpuFrequency();
|
||||
}
|
||||
|
||||
auto ICD::load() -> bool {
|
||||
information = {};
|
||||
|
||||
//todo: connect to SFC random enable setting
|
||||
//GB_random_set_enabled(false);
|
||||
GB_random_set_enabled(configuration.hacks.entropy != "None");
|
||||
if(Frequency == 0) {
|
||||
GB_init(&sameboy, GB_MODEL_SGB_NO_SFC);
|
||||
GB_load_boot_rom_from_buffer(&sameboy, (const unsigned char*)&SGB1BootROM[0], 256);
|
||||
GB_set_sample_rate(&sameboy, uint(system.cpuFrequency() / 5.0 / 128.0));
|
||||
} else {
|
||||
GB_init(&sameboy, GB_MODEL_SGB2_NO_SFC);
|
||||
GB_load_boot_rom_from_buffer(&sameboy, (const unsigned char*)&SGB2BootROM[0], 256);
|
||||
GB_set_sample_rate(&sameboy, uint(Frequency / 5.0 / 128.0));
|
||||
}
|
||||
GB_set_sample_rate_by_clocks(&sameboy, 256);
|
||||
GB_set_highpass_filter_mode(&sameboy, GB_HIGHPASS_ACCURATE);
|
||||
GB_set_icd_hreset_callback(&sameboy, &SameBoy::hreset);
|
||||
GB_set_icd_vreset_callback(&sameboy, &SameBoy::vreset);
|
||||
@@ -130,18 +142,15 @@ auto ICD::unload() -> void {
|
||||
}
|
||||
|
||||
auto ICD::power(bool reset) -> void {
|
||||
//SGB1 uses CPU oscillator; SGB2 uses dedicated oscillator
|
||||
create(ICD::Enter, (Frequency ? Frequency : system.cpuFrequency()) / 5.0);
|
||||
if(!reset) {
|
||||
stream = Emulator::audio.createStream(2, uint((Frequency ? Frequency : system.cpuFrequency()) / 5.0 / 128.0));
|
||||
}
|
||||
auto frequency = clockFrequency() / 5;
|
||||
create(ICD::Enter, frequency);
|
||||
if(!reset) stream = Emulator::audio.createStream(2, frequency / 128);
|
||||
|
||||
for(auto& packet : this->packet) packet = {};
|
||||
packetSize = 0;
|
||||
|
||||
joypID = 3;
|
||||
joyp14Lock = 0;
|
||||
joyp15Lock = 0;
|
||||
joypID = 0;
|
||||
joypLock = 1;
|
||||
pulseLock = 1;
|
||||
strobeLock = 0;
|
||||
packetLock = 0;
|
||||
|
@@ -4,8 +4,11 @@ struct ICD : Emulator::Platform, Thread {
|
||||
|
||||
inline auto pathID() const -> uint { return information.pathID; }
|
||||
|
||||
auto synchronizeCPU() -> void;
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
auto clockFrequency() const -> uint;
|
||||
|
||||
auto load() -> bool;
|
||||
auto save() -> void;
|
||||
@@ -42,8 +45,7 @@ private:
|
||||
uint7 packetSize;
|
||||
|
||||
uint2 joypID;
|
||||
uint1 joyp14Lock;
|
||||
uint1 joyp15Lock;
|
||||
uint1 joypLock;
|
||||
uint1 pulseLock;
|
||||
uint1 strobeLock;
|
||||
uint1 packetLock;
|
||||
|
@@ -21,15 +21,14 @@ auto ICD::ppuWrite(uint2 color) -> void {
|
||||
|
||||
auto ICD::apuWrite(float left, float right) -> void {
|
||||
float samples[] = {left, right};
|
||||
stream->write(samples);
|
||||
if(!system.runAhead) stream->write(samples);
|
||||
}
|
||||
|
||||
auto ICD::joypWrite(bool p14, bool p15) -> void {
|
||||
//joypad handling
|
||||
if(p14 == 1 && p15 == 1) {
|
||||
if(joyp14Lock == 0 && joyp15Lock == 0) {
|
||||
joyp14Lock = 1;
|
||||
joyp15Lock = 1;
|
||||
if(joypLock == 0) {
|
||||
joypLock = 1;
|
||||
joypID++;
|
||||
if(mltReq == 0) joypID &= 0; //1-player mode
|
||||
if(mltReq == 1) joypID &= 1; //2-player mode
|
||||
@@ -46,35 +45,35 @@ auto ICD::joypWrite(bool p14, bool p15) -> void {
|
||||
|
||||
uint4 input = 0xf;
|
||||
if(p14 == 1 && p15 == 1) input = 0xf - joypID;
|
||||
if(p14 == 0) input &= bits(joypad,0-3); //d-pad
|
||||
if(p15 == 0) input &= bits(joypad,4-7); //buttons
|
||||
if(p14 == 0) input &= (joypad >> 0 & 15); //d-pad
|
||||
if(p15 == 0) input &= (joypad >> 4 & 15); //buttons
|
||||
|
||||
GB_icd_set_joyp(&sameboy, input);
|
||||
|
||||
if(p14 == 0 && p15 == 1) joyp14Lock = 0;
|
||||
if(p14 == 1 && p15 == 0) joyp15Lock = 0;
|
||||
if(p14 == 0 && p15 == 1);
|
||||
if(p14 == 1 && p15 == 0) joypLock ^= 1;
|
||||
|
||||
//packet handling
|
||||
if(p14 == 0 && p15 == 0) { //pulse
|
||||
pulseLock = false;
|
||||
pulseLock = 0;
|
||||
packetOffset = 0;
|
||||
bitOffset = 0;
|
||||
strobeLock = true;
|
||||
packetLock = false;
|
||||
strobeLock = 1;
|
||||
packetLock = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if(pulseLock) return;
|
||||
if(pulseLock == 1) return;
|
||||
|
||||
if(p14 == 1 && p15 == 1) {
|
||||
strobeLock = false;
|
||||
strobeLock = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if(strobeLock) {
|
||||
if(strobeLock == 1) {
|
||||
if(p14 == 1 || p15 == 1) { //malformed packet
|
||||
packetLock = false;
|
||||
pulseLock = true;
|
||||
packetLock = 0;
|
||||
pulseLock = 1;
|
||||
bitOffset = 0;
|
||||
packetOffset = 0;
|
||||
} else {
|
||||
@@ -85,18 +84,21 @@ auto ICD::joypWrite(bool p14, bool p15) -> void {
|
||||
//p14:0, p15:1 = 0
|
||||
//p14:1, p15:0 = 1
|
||||
bool bit = p15 == 0;
|
||||
strobeLock = true;
|
||||
strobeLock = 1;
|
||||
|
||||
if(packetLock) {
|
||||
if(packetLock == 1) {
|
||||
if(p14 == 0 && p15 == 1) {
|
||||
if((joypPacket[0] >> 3) == 0x11) {
|
||||
mltReq = joypPacket[1] & 3;
|
||||
joypID = 3; //required: the next time P14==1 && P15==1; increment and start from ID=0 (Joypad 1)
|
||||
if(mltReq == 0) joypID &= 0; //1-player mode
|
||||
if(mltReq == 1) joypID &= 1; //2-player mode
|
||||
if(mltReq == 2) joypID &= 3; //4-player mode (unverified; but the most likely behavior)
|
||||
if(mltReq == 3) joypID &= 3; //4-player mode
|
||||
}
|
||||
|
||||
if(packetSize < 64) packet[packetSize++] = joypPacket;
|
||||
packetLock = false;
|
||||
pulseLock = true;
|
||||
packetLock = 0;
|
||||
pulseLock = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -107,5 +109,5 @@ auto ICD::joypWrite(bool p14, bool p15) -> void {
|
||||
joypPacket[packetOffset] = bitData;
|
||||
if(++packetOffset) return;
|
||||
|
||||
packetLock = true;
|
||||
packetLock = 1;
|
||||
}
|
||||
|
@@ -55,13 +55,14 @@ auto ICD::writeIO(uint addr, uint8 data) -> void {
|
||||
if((r6003 & 0x80) == 0x00 && (data & 0x80) == 0x80) {
|
||||
power(true); //soft reset
|
||||
}
|
||||
auto frequency = system.cpuFrequency();
|
||||
auto frequency = clockFrequency();
|
||||
switch(data & 3) {
|
||||
case 0: setFrequency(frequency / 4); break; //fast (glitchy, even on real hardware)
|
||||
case 1: setFrequency(frequency / 5); break; //normal
|
||||
case 2: setFrequency(frequency / 7); break; //slow
|
||||
case 3: setFrequency(frequency / 9); break; //very slow
|
||||
case 0: this->frequency = frequency / 4; break; //fast (glitchy, even on real hardware)
|
||||
case 1: this->frequency = frequency / 5; break; //normal
|
||||
case 2: this->frequency = frequency / 7; break; //slow
|
||||
case 3: this->frequency = frequency / 9; break; //very slow
|
||||
}
|
||||
stream->setFrequency(this->frequency / 128);
|
||||
r6003 = data;
|
||||
return;
|
||||
}
|
||||
|
@@ -16,8 +16,7 @@ auto ICD::serialize(serializer& s) -> void {
|
||||
s.integer(packetSize);
|
||||
|
||||
s.integer(joypID);
|
||||
s.integer(joyp14Lock);
|
||||
s.integer(joyp15Lock);
|
||||
s.integer(joypLock);
|
||||
s.integer(pulseLock);
|
||||
s.integer(strobeLock);
|
||||
s.integer(packetLock);
|
||||
|
@@ -40,8 +40,8 @@ auto MCC::read(uint address, uint8 data) -> uint8 {
|
||||
case 2: return r.mapping << 7;
|
||||
case 3: return r.psramEnableLo << 7;
|
||||
case 4: return r.psramEnableHi << 7;
|
||||
case 5: return r.psramMapping.bit(0) << 7;
|
||||
case 6: return r.psramMapping.bit(1) << 7;
|
||||
case 5: return (r.psramMapping >> 0 & 1) << 7;
|
||||
case 6: return (r.psramMapping >> 1 & 1) << 7;
|
||||
case 7: return r.romEnableLo << 7;
|
||||
case 8: return r.romEnableHi << 7;
|
||||
case 9: return r.exEnableLo << 7;
|
||||
@@ -60,20 +60,20 @@ auto MCC::read(uint address, uint8 data) -> uint8 {
|
||||
auto MCC::write(uint address, uint8 data) -> void {
|
||||
if((address & 0xf0f000) == 0x005000) { //$00-0f:5000-5fff
|
||||
switch(address >> 16 & 15) {
|
||||
case 1: irq.enable = bit1(data,7); break;
|
||||
case 2: w.mapping = bit1(data,7); break;
|
||||
case 3: w.psramEnableLo = bit1(data,7); break;
|
||||
case 4: w.psramEnableHi = bit1(data,7); break;
|
||||
case 5: bit1(w.psramMapping,0) = bit1(data,7); break;
|
||||
case 6: bit1(w.psramMapping,1) = bit1(data,7); break;
|
||||
case 7: w.romEnableLo = bit1(data,7); break;
|
||||
case 8: w.romEnableHi = bit1(data,7); break;
|
||||
case 9: w.exEnableLo = bit1(data,7); break;
|
||||
case 10: w.exEnableHi = bit1(data,7); break;
|
||||
case 11: w.exMapping = bit1(data,7); break;
|
||||
case 12: w.internallyWritable = bit1(data,7); break;
|
||||
case 13: w.externallyWritable = bit1(data,7); break;
|
||||
case 14: if(bit1(data,7)) commit(); break;
|
||||
case 1: irq.enable = data >> 7; break;
|
||||
case 2: w.mapping = data >> 7; break;
|
||||
case 3: w.psramEnableLo = data >> 7; break;
|
||||
case 4: w.psramEnableHi = data >> 7; break;
|
||||
case 5: w.psramMapping = w.psramMapping & 2 | data >> 7 << 0; break;
|
||||
case 6: w.psramMapping = w.psramMapping & 1 | data >> 7 << 1; break;
|
||||
case 7: w.romEnableLo = data >> 7; break;
|
||||
case 8: w.romEnableHi = data >> 7; break;
|
||||
case 9: w.exEnableLo = data >> 7; break;
|
||||
case 10: w.exEnableHi = data >> 7; break;
|
||||
case 11: w.exMapping = data >> 7; break;
|
||||
case 12: w.internallyWritable = data >> 7; break;
|
||||
case 13: w.externallyWritable = data >> 7; break;
|
||||
case 14: if(data >> 7) commit(); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -6,8 +6,16 @@ MSU1 msu1;
|
||||
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto MSU1::synchronizeCPU() -> void {
|
||||
if(clock >= 0) scheduler.resume(cpu.thread);
|
||||
}
|
||||
|
||||
|
||||
auto MSU1::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), msu1.main();
|
||||
while(true) {
|
||||
scheduler.synchronize();
|
||||
msu1.main();
|
||||
}
|
||||
}
|
||||
|
||||
auto MSU1::main() -> void {
|
||||
@@ -34,9 +42,13 @@ auto MSU1::main() -> void {
|
||||
}
|
||||
}
|
||||
|
||||
stream->sample(float(left), float(right));
|
||||
if(!system.runAhead) stream->sample(float(left), float(right));
|
||||
step(1);
|
||||
synchronize(cpu);
|
||||
synchronizeCPU();
|
||||
}
|
||||
|
||||
auto MSU1::step(uint clocks) -> void {
|
||||
clock += clocks * (uint64_t)cpu.frequency;
|
||||
}
|
||||
|
||||
auto MSU1::unload() -> void {
|
||||
@@ -46,7 +58,7 @@ auto MSU1::unload() -> void {
|
||||
|
||||
auto MSU1::power() -> void {
|
||||
create(MSU1::Enter, 44100);
|
||||
stream = Emulator::audio.createStream(2, frequency());
|
||||
stream = Emulator::audio.createStream(2, frequency);
|
||||
|
||||
io.dataSeekOffset = 0;
|
||||
io.dataReadOffset = 0;
|
||||
@@ -98,9 +110,9 @@ auto MSU1::audioOpen() -> void {
|
||||
}
|
||||
|
||||
auto MSU1::readIO(uint addr, uint8) -> uint8 {
|
||||
cpu.synchronize(*this);
|
||||
cpu.synchronizeCoprocessors();
|
||||
|
||||
switch(0x2000 | (addr & 7)) {
|
||||
switch(0x2000 | addr & 7) {
|
||||
case 0x2000:
|
||||
return (
|
||||
Revision << 0
|
||||
@@ -128,18 +140,18 @@ auto MSU1::readIO(uint addr, uint8) -> uint8 {
|
||||
}
|
||||
|
||||
auto MSU1::writeIO(uint addr, uint8 data) -> void {
|
||||
cpu.synchronize(*this);
|
||||
cpu.synchronizeCoprocessors();
|
||||
|
||||
switch(0x2000 | (addr & 7)) {
|
||||
case 0x2000: bit8(io.dataSeekOffset,0) = data; break;
|
||||
case 0x2001: bit8(io.dataSeekOffset,1) = data; break;
|
||||
case 0x2002: bit8(io.dataSeekOffset,2) = data; break;
|
||||
case 0x2003: bit8(io.dataSeekOffset,3) = data;
|
||||
switch(0x2000 | addr & 7) {
|
||||
case 0x2000: io.dataSeekOffset = io.dataSeekOffset & 0xffffff00 | data << 0; break;
|
||||
case 0x2001: io.dataSeekOffset = io.dataSeekOffset & 0xffff00ff | data << 8; break;
|
||||
case 0x2002: io.dataSeekOffset = io.dataSeekOffset & 0xff00ffff | data << 16; break;
|
||||
case 0x2003: io.dataSeekOffset = io.dataSeekOffset & 0x00ffffff | data << 24;
|
||||
io.dataReadOffset = io.dataSeekOffset;
|
||||
if(dataFile) dataFile->seek(io.dataReadOffset);
|
||||
break;
|
||||
case 0x2004: bit8(io.audioTrack,0) = data; break;
|
||||
case 0x2005: bit8(io.audioTrack,1) = data;
|
||||
case 0x2004: io.audioTrack = io.audioTrack & 0xff00 | data << 0; break;
|
||||
case 0x2005: io.audioTrack = io.audioTrack & 0x00ff | data << 8;
|
||||
io.audioPlay = false;
|
||||
io.audioRepeat = false;
|
||||
io.audioPlayOffset = 8;
|
||||
@@ -156,9 +168,9 @@ auto MSU1::writeIO(uint addr, uint8 data) -> void {
|
||||
case 0x2007:
|
||||
if(io.audioBusy) break;
|
||||
if(io.audioError) break;
|
||||
io.audioPlay = bit1(data,0);
|
||||
io.audioRepeat = bit1(data,1);
|
||||
boolean audioResume = bit1(data,2);
|
||||
io.audioPlay = bool(data & 1);
|
||||
io.audioRepeat = bool(data & 2);
|
||||
boolean audioResume = bool(data & 4);
|
||||
if(!io.audioPlay && audioResume) {
|
||||
io.audioResumeTrack = io.audioTrack;
|
||||
io.audioResumeOffset = io.audioPlayOffset;
|
||||
|
@@ -1,8 +1,10 @@
|
||||
struct MSU1 : Thread {
|
||||
shared_pointer<Emulator::Stream> stream;
|
||||
|
||||
auto synchronizeCPU() -> void;
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
auto unload() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
@@ -15,8 +17,8 @@ struct MSU1 : Thread {
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
vfs::shared::file dataFile;
|
||||
vfs::shared::file audioFile;
|
||||
shared_pointer<vfs::file> dataFile;
|
||||
shared_pointer<vfs::file> audioFile;
|
||||
|
||||
enum Flag : uint {
|
||||
Revision = 0x02, //max: 0x07
|
||||
|
@@ -1,22 +1,34 @@
|
||||
#include <sfc/sfc.hpp>
|
||||
#include <processor/upd96050/upd96050.cpp>
|
||||
|
||||
namespace SuperFamicom {
|
||||
|
||||
#include "serialization.cpp"
|
||||
NECDSP necdsp;
|
||||
|
||||
auto NECDSP::synchronizeCPU() -> void {
|
||||
if(clock >= 0) scheduler.resume(cpu.thread);
|
||||
}
|
||||
|
||||
auto NECDSP::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), necdsp.main();
|
||||
while(true) {
|
||||
scheduler.synchronize();
|
||||
necdsp.main();
|
||||
}
|
||||
}
|
||||
|
||||
auto NECDSP::main() -> void {
|
||||
exec();
|
||||
step(1);
|
||||
synchronize(cpu);
|
||||
synchronizeCPU();
|
||||
}
|
||||
|
||||
auto NECDSP::step(uint clocks) -> void {
|
||||
clock += clocks * (uint64_t)cpu.frequency;
|
||||
}
|
||||
|
||||
auto NECDSP::read(uint addr, uint8) -> uint8 {
|
||||
cpu.synchronize(*this);
|
||||
cpu.synchronizeCoprocessors();
|
||||
if(addr & 1) {
|
||||
return uPD96050::readSR();
|
||||
} else {
|
||||
@@ -25,7 +37,7 @@ auto NECDSP::read(uint addr, uint8) -> uint8 {
|
||||
}
|
||||
|
||||
auto NECDSP::write(uint addr, uint8 data) -> void {
|
||||
cpu.synchronize(*this);
|
||||
cpu.synchronizeCoprocessors();
|
||||
if(addr & 1) {
|
||||
return uPD96050::writeSR(data);
|
||||
} else {
|
||||
@@ -34,12 +46,12 @@ auto NECDSP::write(uint addr, uint8 data) -> void {
|
||||
}
|
||||
|
||||
auto NECDSP::readRAM(uint addr, uint8) -> uint8 {
|
||||
cpu.synchronize(*this);
|
||||
cpu.synchronizeCoprocessors();
|
||||
return uPD96050::readDP(addr);
|
||||
}
|
||||
|
||||
auto NECDSP::writeRAM(uint addr, uint8 data) -> void {
|
||||
cpu.synchronize(*this);
|
||||
cpu.synchronizeCoprocessors();
|
||||
return uPD96050::writeDP(addr, data);
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,8 @@
|
||||
struct NECDSP : Processor::uPD96050, Thread {
|
||||
auto synchronizeCPU() -> void;
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
|
||||
auto read(uint addr, uint8 data) -> uint8;
|
||||
auto write(uint addr, uint8 data) -> void;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
auto SA1::BWRAM::conflict() const -> bool {
|
||||
if(configuration.hacks.coprocessors.delayedSync) return false;
|
||||
if(configuration.hacks.coprocessor.delayedSync) return false;
|
||||
|
||||
if((cpu.r.mar & 0x40e000) == 0x006000) return true; //00-3f,80-bf:6000-7fff
|
||||
if((cpu.r.mar & 0xf00000) == 0x400000) return true; //40-4f:0000-ffff
|
||||
@@ -22,7 +22,7 @@ auto SA1::BWRAM::write(uint address, uint8 data) -> void {
|
||||
//00-3f,80-bf:6000-7fff size=0x2000 => 00:0000-1fff
|
||||
//40-4f:0000-ffff => untranslated
|
||||
auto SA1::BWRAM::readCPU(uint address, uint8 data) -> uint8 {
|
||||
cpu.synchronize(sa1);
|
||||
cpu.synchronizeCoprocessors();
|
||||
|
||||
if(address < 0x2000) { //$00-3f,80-bf:6000-7fff
|
||||
address = sa1.mmio.sbm * 0x2000 + (address & 0x1fff);
|
||||
@@ -33,7 +33,7 @@ auto SA1::BWRAM::readCPU(uint address, uint8 data) -> uint8 {
|
||||
}
|
||||
|
||||
auto SA1::BWRAM::writeCPU(uint address, uint8 data) -> void {
|
||||
cpu.synchronize(sa1);
|
||||
cpu.synchronizeCoprocessors();
|
||||
|
||||
if(address < 0x2000) { //$00-3f,80-bf:6000-7fff
|
||||
address = sa1.mmio.sbm * 0x2000 + (address & 0x1fff);
|
||||
@@ -80,18 +80,18 @@ auto SA1::BWRAM::readBitmap(uint20 address, uint8 data) -> uint8 {
|
||||
uint shift = address & 1;
|
||||
address >>= 1;
|
||||
switch(shift) {
|
||||
case 0: return cbits(read(address),0-3);
|
||||
case 1: return cbits(read(address),4-7);
|
||||
case 0: return read(address) >> 0 & 15;
|
||||
case 1: return read(address) >> 4 & 15;
|
||||
}
|
||||
} else {
|
||||
//2bpp
|
||||
uint shift = address & 3;
|
||||
address >>= 2;
|
||||
switch(shift) {
|
||||
case 0: return cbits(read(address),0-1);
|
||||
case 1: return cbits(read(address),2-3);
|
||||
case 2: return cbits(read(address),4-5);
|
||||
case 3: return cbits(read(address),6-7);
|
||||
case 0: return read(address) >> 0 & 3;
|
||||
case 1: return read(address) >> 2 & 3;
|
||||
case 2: return read(address) >> 4 & 3;
|
||||
case 3: return read(address) >> 6 & 3;
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
|
@@ -51,7 +51,7 @@ auto SA1::dmaCC1() -> void {
|
||||
mmio.chdma_irqfl = true;
|
||||
if(mmio.chdma_irqen) {
|
||||
mmio.chdma_irqcl = 0;
|
||||
cpu.r.irq = 1;
|
||||
cpu.irq(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
auto SA1::readIOCPU(uint address, uint8 data) -> uint8 {
|
||||
cpu.synchronize(sa1);
|
||||
cpu.synchronizeCoprocessors();
|
||||
|
||||
switch(0x2200 | address & 0x1ff) {
|
||||
|
||||
@@ -25,7 +25,7 @@ auto SA1::readIOCPU(uint address, uint8 data) -> uint8 {
|
||||
}
|
||||
|
||||
auto SA1::readIOSA1(uint address, uint8) -> uint8 {
|
||||
synchronize(cpu);
|
||||
synchronizeCPU();
|
||||
|
||||
switch(0x2200 | address & 0x1ff) {
|
||||
|
||||
@@ -101,7 +101,7 @@ auto SA1::readIOSA1(uint address, uint8) -> uint8 {
|
||||
}
|
||||
|
||||
auto SA1::writeIOCPU(uint address, uint8 data) -> void {
|
||||
cpu.synchronize(sa1);
|
||||
cpu.synchronizeCoprocessors();
|
||||
|
||||
switch(0x2200 | address & 0x1ff) {
|
||||
|
||||
@@ -136,14 +136,14 @@ auto SA1::writeIOCPU(uint address, uint8 data) -> void {
|
||||
if(!mmio.cpu_irqen && (data & 0x80)) {
|
||||
if(mmio.cpu_irqfl) {
|
||||
mmio.cpu_irqcl = 0;
|
||||
cpu.r.irq = 1;
|
||||
cpu.irq(1);
|
||||
}
|
||||
}
|
||||
|
||||
if(!mmio.chdma_irqen && (data & 0x20)) {
|
||||
if(mmio.chdma_irqfl) {
|
||||
mmio.chdma_irqcl = 0;
|
||||
cpu.r.irq = 1;
|
||||
cpu.irq(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ auto SA1::writeIOCPU(uint address, uint8 data) -> void {
|
||||
if(mmio.cpu_irqcl ) mmio.cpu_irqfl = false;
|
||||
if(mmio.chdma_irqcl) mmio.chdma_irqfl = false;
|
||||
|
||||
if(!mmio.cpu_irqfl && !mmio.chdma_irqfl) cpu.r.irq = 0;
|
||||
if(!mmio.cpu_irqfl && !mmio.chdma_irqfl) cpu.irq(0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -236,7 +236,7 @@ auto SA1::writeIOCPU(uint address, uint8 data) -> void {
|
||||
}
|
||||
|
||||
auto SA1::writeIOSA1(uint address, uint8 data) -> void {
|
||||
synchronize(cpu);
|
||||
synchronizeCPU();
|
||||
|
||||
switch(0x2200 | address & 0x1ff) {
|
||||
|
||||
@@ -251,7 +251,7 @@ auto SA1::writeIOSA1(uint address, uint8 data) -> void {
|
||||
mmio.cpu_irqfl = true;
|
||||
if(mmio.cpu_irqen) {
|
||||
mmio.cpu_irqcl = 0;
|
||||
cpu.r.irq = 1;
|
||||
cpu.irq(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,19 +403,19 @@ auto SA1::writeIOSA1(uint address, uint8 data) -> void {
|
||||
|
||||
//(MAL) multiplicand / dividend low
|
||||
case 0x2251: {
|
||||
bit8(mmio.ma,0) = data;
|
||||
mmio.ma = mmio.ma & ~0x00ff | data << 0;
|
||||
return;
|
||||
}
|
||||
|
||||
//(MAH) multiplicand / dividend high
|
||||
case 0x2252: {
|
||||
bit8(mmio.ma,1) = data;
|
||||
mmio.ma = mmio.ma & ~0xff00 | data << 8;
|
||||
return;
|
||||
}
|
||||
|
||||
//(MBL) multiplier / divisor low
|
||||
case 0x2253: {
|
||||
bit8(mmio.mb,0) = data;
|
||||
mmio.mb = mmio.mb & ~0x00ff | data << 0;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -423,7 +423,7 @@ auto SA1::writeIOSA1(uint address, uint8 data) -> void {
|
||||
//multiplication / cumulative sum only resets MB
|
||||
//division resets both MA and MB
|
||||
case 0x2254: {
|
||||
bit8(mmio.mb,1) = data;
|
||||
mmio.mb = mmio.mb & ~0xff00 | data << 8;
|
||||
|
||||
if(mmio.acm == 0) {
|
||||
if(mmio.md == 0) {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
auto SA1::IRAM::conflict() const -> bool {
|
||||
if(configuration.hacks.coprocessors.delayedSync) return false;
|
||||
if(configuration.hacks.coprocessor.delayedSync) return false;
|
||||
|
||||
if((cpu.r.mar & 0x40f800) == 0x003000) return cpu.refresh() == 0; //00-3f,80-bf:3000-37ff
|
||||
return false;
|
||||
@@ -18,12 +18,12 @@ auto SA1::IRAM::write(uint address, uint8 data) -> void {
|
||||
}
|
||||
|
||||
auto SA1::IRAM::readCPU(uint address, uint8 data) -> uint8 {
|
||||
cpu.synchronize(sa1);
|
||||
cpu.synchronizeCoprocessors();
|
||||
return read(address, data);
|
||||
}
|
||||
|
||||
auto SA1::IRAM::writeCPU(uint address, uint8 data) -> void {
|
||||
cpu.synchronize(sa1);
|
||||
cpu.synchronizeCoprocessors();
|
||||
return write(address, data);
|
||||
}
|
||||
|
||||
|
@@ -18,7 +18,7 @@ auto SA1::idleBranch() -> void {
|
||||
if(r.pc.d & 1) idleJump();
|
||||
}
|
||||
|
||||
auto SA1::read(uint24 address) -> uint8 {
|
||||
auto SA1::read(uint address) -> uint8 {
|
||||
r.mar = address;
|
||||
uint8 data = r.mdr;
|
||||
|
||||
@@ -62,7 +62,7 @@ auto SA1::read(uint24 address) -> uint8 {
|
||||
return data;
|
||||
}
|
||||
|
||||
auto SA1::write(uint24 address, uint8 data) -> void {
|
||||
auto SA1::write(uint address, uint8 data) -> void {
|
||||
r.mar = address;
|
||||
r.mdr = data;
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
auto SA1::ROM::conflict() const -> bool {
|
||||
if(configuration.hacks.coprocessors.delayedSync) return false;
|
||||
if(configuration.hacks.coprocessor.delayedSync) return false;
|
||||
|
||||
if((cpu.r.mar & 0x408000) == 0x008000) return true; //00-3f,80-bf:8000-ffff
|
||||
if((cpu.r.mar & 0xc00000) == 0xc00000) return true; //c0-ff:0000-ffff
|
||||
|
@@ -11,8 +11,15 @@ namespace SuperFamicom {
|
||||
#include "serialization.cpp"
|
||||
SA1 sa1;
|
||||
|
||||
auto SA1::synchronizeCPU() -> void {
|
||||
if(clock >= 0) scheduler.resume(cpu.thread);
|
||||
}
|
||||
|
||||
auto SA1::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), sa1.main();
|
||||
while(true) {
|
||||
scheduler.synchronize();
|
||||
sa1.main();
|
||||
}
|
||||
}
|
||||
|
||||
auto SA1::main() -> void {
|
||||
@@ -78,13 +85,9 @@ auto SA1::interruptPending() const -> bool {
|
||||
return status.interruptPending;
|
||||
}
|
||||
|
||||
auto SA1::synchronizing() const -> bool {
|
||||
return scheduler.synchronizing();
|
||||
}
|
||||
|
||||
auto SA1::step() -> void {
|
||||
Thread::step(2);
|
||||
synchronize(cpu);
|
||||
clock += (uint64_t)cpu.frequency << 1;
|
||||
synchronizeCPU();
|
||||
|
||||
//adjust counters:
|
||||
//note that internally, status counters are in clocks;
|
||||
@@ -127,8 +130,10 @@ auto SA1::unload() -> void {
|
||||
}
|
||||
|
||||
auto SA1::power() -> void {
|
||||
double overclock = max(1.0, min(4.0, configuration.hacks.sa1.overclock / 100.0));
|
||||
|
||||
WDC65816::power();
|
||||
create(SA1::Enter, system.cpuFrequency());
|
||||
create(SA1::Enter, system.cpuFrequency() * overclock);
|
||||
|
||||
bwram.dma = false;
|
||||
for(uint address : range(iram.size())) {
|
||||
|
@@ -1,7 +1,10 @@
|
||||
//Super Accelerator (SA-1)
|
||||
|
||||
struct SA1 : Processor::WDC65816, Thread {
|
||||
inline auto synchronizing() const -> bool override { return scheduler.synchronizing(); }
|
||||
|
||||
//sa1.cpp
|
||||
auto synchronizeCPU() -> void;
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step() -> void;
|
||||
@@ -10,7 +13,6 @@ struct SA1 : Processor::WDC65816, Thread {
|
||||
alwaysinline auto triggerIRQ() -> void;
|
||||
alwaysinline auto lastCycle() -> void override;
|
||||
alwaysinline auto interruptPending() const -> bool override;
|
||||
auto synchronizing() const -> bool override;
|
||||
|
||||
auto unload() -> void;
|
||||
auto power() -> void;
|
||||
@@ -36,8 +38,8 @@ struct SA1 : Processor::WDC65816, Thread {
|
||||
alwaysinline auto idle() -> void override;
|
||||
alwaysinline auto idleJump() -> void override;
|
||||
alwaysinline auto idleBranch() -> void override;
|
||||
alwaysinline auto read(uint24 address) -> uint8 override;
|
||||
alwaysinline auto write(uint24 address, uint8 data) -> void override;
|
||||
alwaysinline auto read(uint address) -> uint8 override;
|
||||
alwaysinline auto write(uint address, uint8 data) -> void override;
|
||||
auto readVBR(uint address, uint8 data = 0) -> uint8;
|
||||
auto readDisassembler(uint address) -> uint8 override;
|
||||
|
||||
|
@@ -3,6 +3,7 @@ struct Decompressor {
|
||||
IM(SDD1::Decompressor& self) : self(self) {}
|
||||
auto init(uint offset) -> void;
|
||||
auto getCodeWord(uint8 codeLength) -> uint8;
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
Decompressor& self;
|
||||
@@ -13,6 +14,7 @@ struct Decompressor {
|
||||
struct GCD { //golomb-code decoder
|
||||
GCD(SDD1::Decompressor& self) : self(self) {}
|
||||
auto getRunCount(uint8 codeNumber, uint8& mpsCount, bool& lpsIndex) -> void;
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
Decompressor& self;
|
||||
@@ -23,6 +25,7 @@ struct Decompressor {
|
||||
BG(SDD1::Decompressor& self, uint8 codeNumber) : self(self), codeNumber(codeNumber) {}
|
||||
auto init() -> void;
|
||||
auto getBit(bool& endOfRun) -> uint8;
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
Decompressor& self;
|
||||
@@ -35,6 +38,7 @@ struct Decompressor {
|
||||
PEM(SDD1::Decompressor& self) : self(self) {}
|
||||
auto init() -> void;
|
||||
auto getBit(uint8 context) -> uint8;
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
Decompressor& self;
|
||||
@@ -54,6 +58,7 @@ struct Decompressor {
|
||||
CM(SDD1::Decompressor& self) : self(self) {}
|
||||
auto init(uint offset) -> void;
|
||||
auto getBit() -> uint8;
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
Decompressor& self;
|
||||
@@ -68,6 +73,7 @@ struct Decompressor {
|
||||
OL(SDD1::Decompressor& self) : self(self) {}
|
||||
auto init(uint offset) -> void;
|
||||
auto decompress() -> uint8;
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
Decompressor& self;
|
||||
@@ -78,6 +84,7 @@ struct Decompressor {
|
||||
Decompressor();
|
||||
auto init(uint offset) -> void;
|
||||
auto read() -> uint8;
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
IM im;
|
||||
GCD gcd;
|
||||
|
@@ -66,11 +66,11 @@ auto SDD1::dmaRead(uint addr, uint8 data) -> uint8 {
|
||||
auto SDD1::dmaWrite(uint addr, uint8 data) -> void {
|
||||
uint channel = addr >> 4 & 7;
|
||||
switch(addr & 15) {
|
||||
case 2: bit8(dma[channel].addr,0) = data; break;
|
||||
case 3: bit8(dma[channel].addr,1) = data; break;
|
||||
case 4: bit8(dma[channel].addr,2) = data; break;
|
||||
case 5: bit8(dma[channel].size,0) = data; break;
|
||||
case 6: bit8(dma[channel].size,1) = data; break;
|
||||
case 2: dma[channel].addr = dma[channel].addr & 0xffff00 | data << 0; break;
|
||||
case 3: dma[channel].addr = dma[channel].addr & 0xff00ff | data << 8; break;
|
||||
case 4: dma[channel].addr = dma[channel].addr & 0x00ffff | data << 16; break;
|
||||
case 5: dma[channel].size = dma[channel].size & 0xff00 | data << 0; break;
|
||||
case 6: dma[channel].size = dma[channel].size & 0x00ff | data << 8; break;
|
||||
}
|
||||
return cpu.writeDMA(addr, data);
|
||||
}
|
||||
@@ -100,7 +100,7 @@ auto SDD1::mcuRead(uint addr, uint8 data) -> uint8 {
|
||||
if(r4800 & r4801) {
|
||||
//at least one channel has S-DD1 decompression enabled ...
|
||||
for(auto n : range(8)) {
|
||||
if(bit1(r4800,n) && bit1(r4801,n)) {
|
||||
if((r4800 & 1 << n) && (r4801 & 1 << n)) {
|
||||
//S-DD1 always uses fixed transfer mode, so address will not change during transfer
|
||||
if(addr == dma[n].addr) {
|
||||
if(!dmaReady) {
|
||||
@@ -113,7 +113,7 @@ auto SDD1::mcuRead(uint addr, uint8 data) -> uint8 {
|
||||
data = decompressor.read();
|
||||
if(--dma[n].size == 0) {
|
||||
dmaReady = false;
|
||||
bit1(r4801,n) = 0;
|
||||
r4801 &= ~(1 << n);
|
||||
}
|
||||
|
||||
return data;
|
||||
|
@@ -11,4 +11,56 @@ auto SDD1::serialize(serializer& s) -> void {
|
||||
s.integer(dma[n].size);
|
||||
}
|
||||
s.integer(dmaReady);
|
||||
|
||||
decompressor.serialize(s);
|
||||
}
|
||||
|
||||
auto SDD1::Decompressor::serialize(serializer& s) -> void {
|
||||
im.serialize(s);
|
||||
gcd.serialize(s);
|
||||
bg0.serialize(s);
|
||||
bg1.serialize(s);
|
||||
bg2.serialize(s);
|
||||
bg3.serialize(s);
|
||||
bg4.serialize(s);
|
||||
bg5.serialize(s);
|
||||
bg6.serialize(s);
|
||||
bg7.serialize(s);
|
||||
cm.serialize(s);
|
||||
ol.serialize(s);
|
||||
}
|
||||
|
||||
auto SDD1::Decompressor::IM::serialize(serializer& s) -> void {
|
||||
s.integer(offset);
|
||||
s.integer(bitCount);
|
||||
}
|
||||
|
||||
auto SDD1::Decompressor::GCD::serialize(serializer& s) -> void {
|
||||
}
|
||||
|
||||
auto SDD1::Decompressor::BG::serialize(serializer& s) -> void {
|
||||
s.integer(mpsCount);
|
||||
s.integer(lpsIndex);
|
||||
}
|
||||
|
||||
auto SDD1::Decompressor::PEM::serialize(serializer& s) -> void {
|
||||
for(auto& info : contextInfo) {
|
||||
s.integer(info.status);
|
||||
s.integer(info.mps);
|
||||
}
|
||||
}
|
||||
|
||||
auto SDD1::Decompressor::CM::serialize(serializer& s) -> void {
|
||||
s.integer(bitplanesInfo);
|
||||
s.integer(contextBitsInfo);
|
||||
s.integer(bitNumber);
|
||||
s.integer(currentBitplane);
|
||||
s.array(previousBitplaneBits);
|
||||
}
|
||||
|
||||
auto SDD1::Decompressor::OL::serialize(serializer& s) -> void {
|
||||
s.integer(bitplanesInfo);
|
||||
s.integer(r0);
|
||||
s.integer(r1);
|
||||
s.integer(r2);
|
||||
}
|
||||
|
@@ -7,15 +7,26 @@ namespace SuperFamicom {
|
||||
#include "serialization.cpp"
|
||||
SharpRTC sharprtc;
|
||||
|
||||
auto SharpRTC::synchronizeCPU() -> void {
|
||||
if(clock >= 0) scheduler.resume(cpu.thread);
|
||||
}
|
||||
|
||||
auto SharpRTC::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), sharprtc.main();
|
||||
while(true) {
|
||||
scheduler.synchronize();
|
||||
sharprtc.main();
|
||||
}
|
||||
}
|
||||
|
||||
auto SharpRTC::main() -> void {
|
||||
tickSecond();
|
||||
|
||||
step(1);
|
||||
synchronize(cpu);
|
||||
synchronizeCPU();
|
||||
}
|
||||
|
||||
auto SharpRTC::step(uint clocks) -> void {
|
||||
clock += clocks * (uint64_t)cpu.frequency;
|
||||
}
|
||||
|
||||
auto SharpRTC::initialize() -> void {
|
||||
|
@@ -1,8 +1,8 @@
|
||||
struct SharpRTC : Thread {
|
||||
using Thread::synchronize;
|
||||
|
||||
auto synchronizeCPU() -> void;
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
|
||||
auto initialize() -> void;
|
||||
auto power() -> void;
|
||||
|
@@ -16,8 +16,15 @@ SPC7110::~SPC7110() {
|
||||
delete decompressor;
|
||||
}
|
||||
|
||||
auto SPC7110::synchronizeCPU() -> void {
|
||||
if(clock >= 0) scheduler.resume(cpu.thread);
|
||||
}
|
||||
|
||||
auto SPC7110::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), spc7110.main();
|
||||
while(true) {
|
||||
scheduler.synchronize();
|
||||
spc7110.main();
|
||||
}
|
||||
}
|
||||
|
||||
auto SPC7110::main() -> void {
|
||||
@@ -27,9 +34,13 @@ auto SPC7110::main() -> void {
|
||||
addClocks(1);
|
||||
}
|
||||
|
||||
auto SPC7110::step(uint clocks) -> void {
|
||||
clock += clocks * (uint64_t)cpu.frequency;
|
||||
}
|
||||
|
||||
auto SPC7110::addClocks(uint clocks) -> void {
|
||||
step(clocks);
|
||||
synchronize(cpu);
|
||||
synchronizeCPU();
|
||||
}
|
||||
|
||||
auto SPC7110::unload() -> void {
|
||||
@@ -96,7 +107,7 @@ auto SPC7110::power() -> void {
|
||||
}
|
||||
|
||||
auto SPC7110::read(uint addr, uint8 data) -> uint8 {
|
||||
cpu.synchronize(*this);
|
||||
cpu.synchronizeCoprocessors();
|
||||
if((addr & 0xff0000) == 0x500000) addr = 0x4800; //$50:0000-ffff == $4800
|
||||
if((addr & 0xff0000) == 0x580000) addr = 0x4808; //$58:0000-ffff == $4808
|
||||
addr = 0x4800 | (addr & 0x3f); //$00-3f,80-bf:4800-483f
|
||||
@@ -180,7 +191,7 @@ auto SPC7110::read(uint addr, uint8 data) -> uint8 {
|
||||
}
|
||||
|
||||
auto SPC7110::write(uint addr, uint8 data) -> void {
|
||||
cpu.synchronize(*this);
|
||||
cpu.synchronizeCoprocessors();
|
||||
if((addr & 0xff0000) == 0x500000) addr = 0x4800; //$50:0000-ffff == $4800
|
||||
if((addr & 0xff0000) == 0x580000) addr = 0x4808; //$58:0000-ffff == $4808
|
||||
addr = 0x4800 | (addr & 0x3f); //$00-3f,80-bf:4800-483f
|
||||
|
@@ -4,8 +4,10 @@ struct SPC7110 : Thread {
|
||||
SPC7110();
|
||||
~SPC7110();
|
||||
|
||||
auto synchronizeCPU() -> void;
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
auto unload() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
auto SuperFX::stop() -> void {
|
||||
cpu.r.irq = 1;
|
||||
cpu.irq(1);
|
||||
}
|
||||
|
||||
auto SuperFX::color(uint8 source) -> uint8 {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user