Compare commits
161 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
7786206a4f | ||
|
fbc1571889 | ||
|
53843934c0 | ||
|
37b610da53 | ||
|
2d9ce59e99 | ||
|
559a6585ef | ||
|
25145f59cc | ||
|
17fc6d8d51 | ||
|
6871e0e32a | ||
|
bb1dd8c609 | ||
|
c9f7c6c4be | ||
|
95d0020297 | ||
|
41148b1024 | ||
|
dbee893408 | ||
|
cb86cd116c | ||
|
1a889ae232 | ||
|
79be6f2355 | ||
|
cac3858f65 | ||
|
1fd6d983da | ||
|
aaf094e7c4 | ||
|
3159285eaa | ||
|
90da691717 | ||
|
598076e400 | ||
|
075f540ec4 | ||
|
41eccf6ec4 | ||
|
4c4e79aa0e | ||
|
0b44399c0a | ||
|
23dd28952b | ||
|
03b06257d3 | ||
|
336d20123f | ||
|
c58169945c | ||
|
c2d0ed4ca8 | ||
|
3d34517f3e | ||
|
a3e0f6da25 | ||
|
bd814f0358 | ||
|
f9adb4d2c6 | ||
|
9a6ae6dacb | ||
|
1e4affe5f9 | ||
|
93a6a1ce7e | ||
|
3b4e8b6d75 | ||
|
b2b51d544f | ||
|
5da4532771 | ||
|
552d385031 | ||
|
0595e9e866 | ||
|
23da4e4e91 | ||
|
41e127a07c | ||
|
5d135b556d | ||
|
2335bb0df8 | ||
|
212da0a966 | ||
|
5deba5cbc1 | ||
|
716c95f279 | ||
|
876b4be1d2 | ||
|
22bd4b9277 | ||
|
f1a4576ac4 | ||
|
0aedb3430c | ||
|
35ff15f83e | ||
|
65a3e6c676 | ||
|
393c2395bb | ||
|
6090c63958 | ||
|
0c55796060 | ||
|
372e9ef42b | ||
|
40a5fbe605 | ||
|
ec960c5172 | ||
|
b14c6bf155 | ||
|
5b97fa2415 | ||
|
f70a20bc42 | ||
|
470e27323d | ||
|
5a8c814e25 | ||
|
91bb781b73 | ||
|
15b67922b3 | ||
|
173a5d67bc | ||
|
ec9729a9e1 | ||
|
77ac5f9e88 | ||
|
8c337d4ac6 | ||
|
73354923eb | ||
|
3aa90590ca | ||
|
52d0cd8dfb | ||
|
c67fb2c726 | ||
|
5d29700fa1 | ||
|
5e7fdbe2c0 | ||
|
51e3fcd3fa | ||
|
18852bcbe2 | ||
|
bcc2627793 | ||
|
685cec6583 | ||
|
6882bd98cf | ||
|
6c8e3c885d | ||
|
8f5bc80f01 | ||
|
2b8df2e70e | ||
|
ec4ab1dc11 | ||
|
5961ea9c03 | ||
|
3353efd3a1 | ||
|
a73a94f331 | ||
|
7ee1534093 | ||
|
87e2154ea1 | ||
|
d8bd1fca1f | ||
|
7acbf5c3dd | ||
|
ea11c6d098 | ||
|
f5b96e9e9e | ||
|
6078cdacbb | ||
|
c2648faeab | ||
|
2e14bd1c81 | ||
|
cd5dde0f62 | ||
|
e5f19e49d4 | ||
|
c6ed8bb4b1 | ||
|
c24eb6e592 | ||
|
d537eaa0fd | ||
|
8bbbc5e737 | ||
|
210306e661 | ||
|
6847058210 | ||
|
b7dca2f317 | ||
|
b69909be8d | ||
|
8617711ea2 | ||
|
540d960e30 | ||
|
8023d9cbe8 | ||
|
0ea17abfea | ||
|
8f61c267c5 | ||
|
eaa2c1f6c0 | ||
|
985610c167 | ||
|
72b824cf1a | ||
|
2dd35f984d | ||
|
e216912ca3 | ||
|
a4a3d611a6 | ||
|
5c55cc2c94 | ||
|
c49d3b2006 | ||
|
61091167b8 | ||
|
610d42d573 | ||
|
f8a6cc2cbd | ||
|
3a175ad2b0 | ||
|
5e330da4e8 | ||
|
c38a771f22 | ||
|
3d8be92550 | ||
|
38fbcd5277 | ||
|
0cb3529547 | ||
|
2f81b5a3e7 | ||
|
aef8d5e962 | ||
|
a5af5eab3c | ||
|
41efdba45a | ||
|
b55783c322 | ||
|
3ec08cebbe | ||
|
8d7d452534 | ||
|
56cb9c01a5 | ||
|
e9d2d56df9 | ||
|
6d487925d0 | ||
|
7aad868adb | ||
|
dd06dd0fed | ||
|
3c26736d4b | ||
|
f53cb33eb9 | ||
|
426de198b7 | ||
|
9e06857e4d | ||
|
e28aa32324 | ||
|
a9571ff5b8 | ||
|
3d21e9afe0 | ||
|
1cb37fc974 | ||
|
f92fc276af | ||
|
18afd41a80 | ||
|
3507c522f4 | ||
|
7a4bfca106 | ||
|
0db886f91d | ||
|
904d11a3f7 | ||
|
214b921388 | ||
|
b8b5aef165 |
5
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
higan/profile/WonderSwan.sys/internal.ram
|
higan/systems/WonderSwan.sys/internal.ram
|
||||||
higan/profile/WonderSwan Color.sys/internal.ram
|
higan/systems/WonderSwan Color.sys/internal.ram
|
||||||
|
higan/systems/Super Famicom.sys/configuration.bml
|
||||||
docs_build/
|
docs_build/
|
||||||
|
@@ -1,39 +1,71 @@
|
|||||||
# NOTE: This file is not part of the official higan source, it's been added
|
# NOTE: This file is not part of the official higan source, it's been added
|
||||||
# to help build WIP binaries with minimal fuss.
|
# to help build WIP binaries with minimal fuss.
|
||||||
|
|
||||||
image: debian:stable
|
image: ubuntu:latest
|
||||||
|
|
||||||
linux-x86_64-binaries:
|
higan-linux-x86_64-binaries:
|
||||||
script:
|
script:
|
||||||
- apt-get update && apt-get -y install build-essential libgtk2.0-dev libpulse-dev mesa-common-dev libgtksourceview2.0-dev libcairo2-dev libsdl1.2-dev libxv-dev libao-dev libopenal-dev libudev-dev
|
- 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 mkdocs
|
||||||
- make -C icarus compiler=g++
|
- make -C genius
|
||||||
- make -C higan compiler=g++
|
- make -C icarus
|
||||||
|
- make -C higan target=higan
|
||||||
|
- LC_ALL=C.UTF-8 mkdocs build
|
||||||
- mkdir higan-nightly
|
- mkdir higan-nightly
|
||||||
|
- cp -a genius/out/genius higan-nightly/genius
|
||||||
- cp -a icarus/out/icarus higan-nightly/icarus
|
- cp -a icarus/out/icarus higan-nightly/icarus
|
||||||
- cp -a icarus/Database higan-nightly/
|
- cp -a icarus/Database higan-nightly/
|
||||||
|
- cp -a icarus/Firmware higan-nightly/
|
||||||
- cp -a higan/out/higan higan-nightly/higan
|
- cp -a higan/out/higan higan-nightly/higan
|
||||||
- cp -a higan/systems/* higan-nightly/
|
- cp -a higan/systems/ higan-nightly/
|
||||||
- cp -a shaders "higan-nightly/Video Shaders"
|
- cp -a shaders higan-nightly/
|
||||||
|
- cp -a docs_build higan-nightly/docs
|
||||||
|
- cp -a GPLv3.txt higan-nightly/
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- higan-nightly/*
|
- higan-nightly/*
|
||||||
|
|
||||||
windows-x86_64-binaries:
|
bsnes-linux-x86_64-binaries:
|
||||||
# This is a normal Windows cross-compile process, except that
|
|
||||||
# nall::chrono tries to use clock_gettime on Windows even
|
|
||||||
# though it's a POSIX function, and for some weird reason mingw has
|
|
||||||
# clock_gettime() in the pthread library.
|
|
||||||
script:
|
script:
|
||||||
- apt-get update && apt-get -y install build-essential mingw-w64
|
- 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
|
||||||
- sed -i -e 's/-lole32/& -static -lpthread/' nall/GNUmakefile
|
- make -C higan target=bsnes
|
||||||
- make -C icarus platform=windows compiler="x86_64-w64-mingw32-g++ -static-libgcc -static-libstdc++" windres="x86_64-w64-mingw32-windres"
|
- mkdir bsnes-nightly
|
||||||
- make -C higan platform=windows compiler="x86_64-w64-mingw32-g++ -static-libgcc -static-libstdc++" windres="x86_64-w64-mingw32-windres"
|
- cp -a higan/out/bsnes bsnes-nightly/bsnes
|
||||||
|
- cp -a shaders bsnes-nightly/
|
||||||
|
- cp -a GPLv3.txt bsnes-nightly/
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- bsnes-nightly/*
|
||||||
|
|
||||||
|
higan-windows-x86_64-binaries:
|
||||||
|
script:
|
||||||
|
- apt-get update && apt-get -y install build-essential mingw-w64 mkdocs
|
||||||
|
# genius does not currently build on Windows due to lack of a combo edit control in hiro
|
||||||
|
#- make -C genius platform=windows compiler="x86_64-w64-mingw32-g++" windres="x86_64-w64-mingw32-windres"
|
||||||
|
- make -C icarus platform=windows compiler="x86_64-w64-mingw32-g++" windres="x86_64-w64-mingw32-windres"
|
||||||
|
- make -C higan target=higan platform=windows compiler="x86_64-w64-mingw32-g++" windres="x86_64-w64-mingw32-windres"
|
||||||
|
- LC_ALL=C.UTF-8 mkdocs build
|
||||||
- mkdir higan-nightly
|
- mkdir higan-nightly
|
||||||
|
#- cp -a genius/out/genius higan-nightly/genius.exe
|
||||||
- cp -a icarus/out/icarus higan-nightly/icarus.exe
|
- cp -a icarus/out/icarus higan-nightly/icarus.exe
|
||||||
- cp -a icarus/Database higan-nightly/
|
- cp -a icarus/Database higan-nightly/
|
||||||
|
- cp -a icarus/Firmware higan-nightly/
|
||||||
- cp -a higan/out/higan higan-nightly/higan.exe
|
- cp -a higan/out/higan higan-nightly/higan.exe
|
||||||
- cp -a higan/systems/* higan-nightly/
|
- cp -a higan/systems/ higan-nightly/
|
||||||
- cp -a shaders "higan-nightly/Video Shaders"
|
- cp -a shaders higan-nightly/
|
||||||
|
- cp -a docs_build higan-nightly/docs
|
||||||
|
- cp -a GPLv3.txt higan-nightly/
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- higan-nightly/*
|
- higan-nightly/*
|
||||||
|
|
||||||
|
bsnes-windows-x86_64-binaries:
|
||||||
|
script:
|
||||||
|
- apt-get update && apt-get -y install build-essential mingw-w64
|
||||||
|
- make -C higan target=bsnes platform=windows compiler="x86_64-w64-mingw32-g++" windres="x86_64-w64-mingw32-windres"
|
||||||
|
- mkdir bsnes-nightly
|
||||||
|
- cp -a higan/out/bsnes bsnes-nightly/bsnes.exe
|
||||||
|
- cp -a shaders bsnes-nightly/
|
||||||
|
- cp -a GPLv3.txt bsnes-nightly/
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- bsnes-nightly/*
|
||||||
|
@@ -2,8 +2,8 @@ Contributing to higan
|
|||||||
=====================
|
=====================
|
||||||
|
|
||||||
If you would like to propose a change to higan,
|
If you would like to propose a change to higan,
|
||||||
you should create an account on the [official forums][f],
|
you should create an account on the [unofficial forums][f],
|
||||||
go to the "Projects" forum and the "higan" sub-forum,
|
go to the "Projects" forum,
|
||||||
and post your idea in a new topic there.
|
and post your idea in a new topic there.
|
||||||
|
|
||||||
[f]: https://board.byuu.org/
|
[f]: https://helmet.kafuka.org/bboard/
|
||||||
|
674
GPLv3.txt
Normal file
@@ -0,0 +1,674 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
44
LICENSE.txt
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
----------------------------------------------------------------------
|
||||||
|
higan - Suite of videogame console emulators
|
||||||
|
icarus - Game library importer for higan
|
||||||
|
|
||||||
|
Copyright © 2004-2017 byuu
|
||||||
|
|
||||||
|
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
|
||||||
|
the Free Software Foundation, specifically version 3 of the License
|
||||||
|
and no other version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
hiro - User interface toolkit
|
||||||
|
libco - C cooperative threading library
|
||||||
|
nall - C++ template library
|
||||||
|
ruby - Hardware abstraction layer
|
||||||
|
|
||||||
|
Copyright © 2006-2017 byuu
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for
|
||||||
|
any purpose with or without fee is hereby granted, provided that the
|
||||||
|
above copyright notice and this permission notice appear in all
|
||||||
|
copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
|
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
|
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
|
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
||||||
|
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
----------------------------------------------------------------------
|
@@ -27,11 +27,11 @@ Official higan resources
|
|||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
- [Official homepage](https://byuu.org/emulation/higan/)
|
- [Official homepage](https://byuu.org/emulation/higan/)
|
||||||
- [Official forum](https://board.byuu.org/viewforum.php?f=4)
|
|
||||||
|
|
||||||
Unofficial higan resources
|
Unofficial higan resources
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
|
- [Unofficial forum](https://helmet.kafuka.org/bboard/)
|
||||||
- Documentation for
|
- Documentation for
|
||||||
[the current stable version][stadocs]
|
[the current stable version][stadocs]
|
||||||
- [Source code repository](https://gitlab.com/higan/higan/)
|
- [Source code repository](https://gitlab.com/higan/higan/)
|
||||||
@@ -43,6 +43,6 @@ Unofficial higan resources
|
|||||||
[the latest WIP version][wipdocs]
|
[the latest WIP version][wipdocs]
|
||||||
|
|
||||||
|
|
||||||
[wipwin]: https://gitlab.com/higan/higan/-/jobs/artifacts/master/download?job=windows-x86_64-binaries
|
[wipwin]: https://gitlab.com/higan/higan/-/jobs/artifacts/master/download?job=higan-windows-x86_64-binaries
|
||||||
[stadocs]: https://higan.readthedocs.io/
|
[stadocs]: https://higan.readthedocs.io/
|
||||||
[wipdocs]: https://higan.readthedocs.io/en/latest/
|
[wipdocs]: https://higan.readthedocs.io/en/latest/
|
||||||
|
297
README.txt
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
|
||||||
|
higan - "Now you're playing with fire!"
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
higan is a multi-system emulator that began development on October 14th, 2004.
|
||||||
|
It currently plays games for the following systems:
|
||||||
|
|
||||||
|
* Nintendo Famicom (NES), Super Famicom (SNES)
|
||||||
|
* Nintendo Game Boy, Game Boy Color + Game Boy Advance
|
||||||
|
* Sega Master System, Game Gear + Mega Drive (Genesis)
|
||||||
|
* NEC PC Engine (TurboGrafx) + SuperGrafx
|
||||||
|
* Bandai WonderSwan + WonderSwan Color
|
||||||
|
|
||||||
|
|
||||||
|
Supported Systems
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
* FreeBSD 10+
|
||||||
|
* Windows 7, 8, 10
|
||||||
|
* Linux 3.2+
|
||||||
|
* OS X 10.7 Lion or above
|
||||||
|
|
||||||
|
You'll need a fast CPU, with clock speed being vastly more important.
|
||||||
|
Dual-core is perfectly adequate, since higan doesn't make significant use
|
||||||
|
of more than one core.
|
||||||
|
|
||||||
|
For Intel, you're looking at a 3.5 GHz Haswell architecture or better,
|
||||||
|
whilst AMD users will want a similarly specced Ryzen build.
|
||||||
|
|
||||||
|
|
||||||
|
Controller Setup
|
||||||
|
----------------
|
||||||
|
|
||||||
|
First, you'll want to configure your controllers. Choose Settings -> Input and
|
||||||
|
pick the system you'd like to configure. If you have two players, or a special
|
||||||
|
game (eg. Mario Paint), you can pick the controller port and device here.
|
||||||
|
|
||||||
|
To assign inputs, double click the name and press the stick or button on your
|
||||||
|
controller. You can have multiple assignments, for example both keyboard and
|
||||||
|
joypad. The Erase button clears the assignments for one input; Reset clears
|
||||||
|
them all in one go.
|
||||||
|
|
||||||
|
(Normally you only need to do this once. But because of how USB works,
|
||||||
|
it's impossible to tell identical controllers apart. This means if you move
|
||||||
|
one to another port, it counts as a new device, and you'll have to reassign
|
||||||
|
the buttons or move it back.)
|
||||||
|
|
||||||
|
|
||||||
|
Loading Games
|
||||||
|
-------------
|
||||||
|
|
||||||
|
After this you can go to Library -> Load ROM File and select a game. higan
|
||||||
|
adds it to your library, and it should start immediately. (Game Boy Advance
|
||||||
|
titles need one more step; please see the FAQ below.)
|
||||||
|
|
||||||
|
To add games en masse, you can use Library -> Import ROM Files. This opens
|
||||||
|
icarus, where you can choose a folder of ROMs, then select the ones you want
|
||||||
|
to import.
|
||||||
|
|
||||||
|
In both cases, if you choose a system under the Library submenus, all games
|
||||||
|
added will show up in a file browser under the Emulation folder in your user
|
||||||
|
profile. The path can be changed under Settings -> Advanced if desired.
|
||||||
|
|
||||||
|
|
||||||
|
Controller Ports
|
||||||
|
----------------
|
||||||
|
|
||||||
|
If you're emulating a console, you need to plug the controllers in, since
|
||||||
|
there's no connection by default.
|
||||||
|
|
||||||
|
Usually this means selecting eg. Super Famicom -> Controller Port 1 -> Gamepad,
|
||||||
|
for example. However, some games require other peripherals like the SNES Mouse.
|
||||||
|
|
||||||
|
|
||||||
|
Troubleshooting & FAQ
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Q: What's the Library?
|
||||||
|
|
||||||
|
A: higan loads folders containing all the files needed to run the game.
|
||||||
|
Odds are you have PC games and music albums organised the same way.
|
||||||
|
This does mean that to play the games, you have to import them first.
|
||||||
|
|
||||||
|
If you're familiar with iTunes or Steam, you already know how this works!
|
||||||
|
|
||||||
|
|
||||||
|
Q: Importing vs. loading? What's the difference?
|
||||||
|
|
||||||
|
A: The "Library -> Load ROM File" menu is a shortcut. It adds the game to your
|
||||||
|
library, then opens it without the manual import process.
|
||||||
|
|
||||||
|
However, if you have lots of games to add at once, you'll want
|
||||||
|
"Import ROM Files" instead.
|
||||||
|
|
||||||
|
|
||||||
|
Q: Why's higan say I'm missing a file ("Game Boy Advance.sys/bios.rom")?
|
||||||
|
|
||||||
|
A: This is the ROM for the startup screen you see when you switch on the
|
||||||
|
Game Boy Advance. Games require it to run, but like other ROMs, it's
|
||||||
|
copyrighted and therefore not provided with higan.
|
||||||
|
|
||||||
|
Having acquired a copy, you'll have to drop it in the requested folder,
|
||||||
|
then rename it to bios.rom.
|
||||||
|
|
||||||
|
|
||||||
|
Q: Where are the games imported? Where did all my save files go?
|
||||||
|
|
||||||
|
A: Check the path under Settings -> Advanced. On Windows it'll probably be
|
||||||
|
something like C:\Users\<name>\Emulation, organised by system. The saves
|
||||||
|
are typically named save.ram.
|
||||||
|
|
||||||
|
|
||||||
|
Q: Where can I find the settings?
|
||||||
|
|
||||||
|
A: There's a few possible locations for settings.bml.
|
||||||
|
|
||||||
|
1) In the same folder as the higan executable.
|
||||||
|
|
||||||
|
2) In the older location if you previously installed higan:
|
||||||
|
|
||||||
|
C:\Users\<name>\AppData\Roaming\higan (Windows)
|
||||||
|
/home/<name>/.config/higan (BSD, Linux)
|
||||||
|
|
||||||
|
3) In the new location (created if the others aren't found):
|
||||||
|
|
||||||
|
C:\Users\<name>\AppData\Local\higan (Windows)
|
||||||
|
/home/<name>/.local/share/higan (BSD, Linux)
|
||||||
|
/Users/<name>/Library/Application Support/higan (Mac)
|
||||||
|
|
||||||
|
higan checks these in order, so you can make a portable install if you like.
|
||||||
|
|
||||||
|
(macOS normally hides the Library folder. To open it, switch to Finder,
|
||||||
|
hold the Option key and select Go -> Library from the menu.)
|
||||||
|
|
||||||
|
|
||||||
|
Q: I set up my gamepads, but they don't work!
|
||||||
|
|
||||||
|
A: Try configuring the ports found in the system menu (eg.
|
||||||
|
Super Famicom -> Controller Port 1 -> Gamepad). Like a real console,
|
||||||
|
fresh higan installs come without any controllers plugged in.
|
||||||
|
|
||||||
|
|
||||||
|
Q: I upgraded higan, why do I get a black screen? What's "Ignore Manifests?"
|
||||||
|
|
||||||
|
A: higan looks at a file called "manifest.bml" to get the information needed
|
||||||
|
to run each game. However, the format has changed over time, making older
|
||||||
|
manifests incompatible with newer higan releases.
|
||||||
|
|
||||||
|
If you tick "Settings -> Advanced -> Ignore Manifests," you might find this
|
||||||
|
resolves the problem. This can be useful for developers and testers.
|
||||||
|
However, it breaks a few titles that require manifests to work!
|
||||||
|
|
||||||
|
Should you find yourself in this situation, consider removing manifest.bml.
|
||||||
|
|
||||||
|
(By default, no manifests are created; higan looks at the files in the
|
||||||
|
game folder, and with the help of a database, tries to regenerate the
|
||||||
|
correct one each time you load the game.)
|
||||||
|
|
||||||
|
|
||||||
|
Q: I have "Ignore Manifests" ticked, but the game won't run?
|
||||||
|
|
||||||
|
A: A few games have especially quirky setups that require manifests for
|
||||||
|
the time being, so you'll need to untick this option:
|
||||||
|
|
||||||
|
* Far East of Eden: Tengai Makyou Zero (English translation only)
|
||||||
|
* Campus Challenge '92
|
||||||
|
* PowerFest '94
|
||||||
|
|
||||||
|
|
||||||
|
Q: Why's the audio lag, stutter, distort, or sound robotic?
|
||||||
|
|
||||||
|
A: If you have an Atom, certain Celeron models, or an older AMD processor
|
||||||
|
(or even an especially old Intel such as a Core 2 Duo)... then these aren't
|
||||||
|
fast enough, sorry. :(
|
||||||
|
|
||||||
|
Try going into the Settings -> Advanced menu, then pick a different audio
|
||||||
|
driver and restart higan. WASAPI can be fussy on some devices.
|
||||||
|
|
||||||
|
Select Settings -> Audio and experiment with the latency. Larger values
|
||||||
|
should be more reliable, with the downside of laggier game controls.
|
||||||
|
|
||||||
|
Occasionally software that hooks into the system or other apps, for example
|
||||||
|
mouse settings panels, can cause lag and other problems.
|
||||||
|
|
||||||
|
Because higan is CPU-intensive and single-threaded, it can interact badly
|
||||||
|
with video capture which is yet another burden on the system. If you're
|
||||||
|
trying to stream or broadcast, and you have Windows 7, consider disabling
|
||||||
|
DWM. Also, look up how to configure hardware encoding (eg. QuickSync).
|
||||||
|
|
||||||
|
|
||||||
|
Q: Can I get smoother video?
|
||||||
|
|
||||||
|
A: Try Settings -> Video -> Exclusive mode, then switch to fullscreen. This
|
||||||
|
currently requires the Direct3D video driver under Settings -> Advanced
|
||||||
|
in order to work.
|
||||||
|
|
||||||
|
(Exclusive fullscreen is pretty experimental at the moment.
|
||||||
|
There are cases where it fails badly, so save your work!)
|
||||||
|
|
||||||
|
Exclusive mode will normally yield what's known as "tearing." If this
|
||||||
|
bothers you, there's an alternative... albeit one with serious gotchas,
|
||||||
|
which is why it's hidden away.
|
||||||
|
|
||||||
|
Close higan, then open up settings.bml and look for the following:
|
||||||
|
|
||||||
|
Video
|
||||||
|
Driver:Direct3D
|
||||||
|
Synchronize:false
|
||||||
|
...
|
||||||
|
|
||||||
|
Change false to true, save the file, then start higan and untick
|
||||||
|
Settings -> Synchronize Audio.
|
||||||
|
|
||||||
|
Keep in mind that this setting can and will reduce sound quality, as GPUs
|
||||||
|
and sound cards in modern PCs generally are not synchronised with each
|
||||||
|
other. The second big consideration is that your refresh rate needs to
|
||||||
|
match the game.
|
||||||
|
|
||||||
|
PAL and NTSC titles run at 50 Hz and 60 Hz, respectively. This applies to
|
||||||
|
all console systems. Of the handhelds: Game Boy, Game Boy Color and
|
||||||
|
Game Boy Advance run at 60 Hz, while WonderSwan runs at 75 Hz.
|
||||||
|
|
||||||
|
This means you'll need a monitor that supports these frequencies, set to
|
||||||
|
the appropriate display mode. Not all of them do. If your refresh rate
|
||||||
|
doesn't match, games will run at the wrong speed.
|
||||||
|
|
||||||
|
|
||||||
|
Online Resources
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Official homepage:
|
||||||
|
|
||||||
|
https://byuu.org/emulation/higan
|
||||||
|
|
||||||
|
Unofficial forum:
|
||||||
|
|
||||||
|
https://helmet.kafuka.org/bboard/
|
||||||
|
|
||||||
|
Unoffical source code repository + documentation:
|
||||||
|
|
||||||
|
https://gitlab.com/higan/higan
|
||||||
|
https://higan.readthedocs.io
|
||||||
|
|
||||||
|
Info on game folders and firmware:
|
||||||
|
|
||||||
|
https://byuu.org/emulation/higan/game-paks
|
||||||
|
https://byuu.org/emulation/higan/firmware
|
||||||
|
|
||||||
|
Donations:
|
||||||
|
|
||||||
|
https://patreon.com/byuu
|
||||||
|
|
||||||
|
Commercial use:
|
||||||
|
|
||||||
|
https://byuu.org/emulation/higan/licensing
|
||||||
|
|
||||||
|
|
||||||
|
Credits
|
||||||
|
-------
|
||||||
|
|
||||||
|
Original author:
|
||||||
|
|
||||||
|
byuu
|
||||||
|
|
||||||
|
We'd like to acknowledge many invaluable contributions made to higan
|
||||||
|
by the following individuals:
|
||||||
|
|
||||||
|
Andreas Naive Hendricks266 Overload
|
||||||
|
Ange Albertini hex_usr p4plus2
|
||||||
|
anomie jchadwick quequotion
|
||||||
|
AWJ Jonas Quinn RedDwarf
|
||||||
|
Bisqwit kode54 Richard Bannister
|
||||||
|
blargg krom Ryphecha
|
||||||
|
Łukasz Krawczyk Lioncash segher
|
||||||
|
Cydrak Lord Nightmare Sintendo
|
||||||
|
Danish lowkey SuperMikeMan
|
||||||
|
DMV27 MerryMage tetsuo55
|
||||||
|
Dr. Decapitator Matthew Callis TmEE
|
||||||
|
endrift mightymo TRAC
|
||||||
|
Fatbag Nach wareya
|
||||||
|
FitzRoy ncbncb zones
|
||||||
|
gekkio neviksti
|
||||||
|
GIGO OV2
|
||||||
|
|
||||||
|
It's been a long, wild ride... apologies to anyone we've missed!
|
||||||
|
|
||||||
|
For more information, please see:
|
||||||
|
|
||||||
|
https://board.byuu.org/viewtopic.php?f=4&t=1631&p=41575#p41575
|
||||||
|
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
higan is provided under the GNU General Public License, version 3.
|
||||||
|
However, certain libraries may be used under the more permissive ISC license.
|
||||||
|
Please see LICENSE.txt for details.
|
@@ -8,5 +8,5 @@ checklink \
|
|||||||
--summary \
|
--summary \
|
||||||
--broken \
|
--broken \
|
||||||
--location=http://127.0.0.1:8000/ \
|
--location=http://127.0.0.1:8000/ \
|
||||||
--exclude 'github.com|board.byuu.org' \
|
--exclude 'github.com|board.byuu.org|helmet.kafuka.org' \
|
||||||
http://127.0.0.1:8000/
|
http://127.0.0.1:8000/
|
||||||
|
@@ -8,7 +8,7 @@ higan would create a game folder named `dkc3.sfc`,
|
|||||||
and inside it store the game data as `program.rom`
|
and inside it store the game data as `program.rom`
|
||||||
and the save data as `save.ram`:
|
and the save data as `save.ram`:
|
||||||
|
|
||||||
```text
|
```python
|
||||||
+- Super Famicom
|
+- Super Famicom
|
||||||
|
|
|
|
||||||
+- dkc3.sfc
|
+- dkc3.sfc
|
||||||
@@ -27,7 +27,7 @@ For example,
|
|||||||
if another emulator loaded the game `dkc3.sfc`
|
if another emulator loaded the game `dkc3.sfc`
|
||||||
it might store the save data in `dkc3.srm`:
|
it might store the save data in `dkc3.srm`:
|
||||||
|
|
||||||
```text
|
```python
|
||||||
+- Super Famicom
|
+- Super Famicom
|
||||||
|
|
|
|
||||||
+- dkc3.sfc
|
+- dkc3.sfc
|
||||||
@@ -53,7 +53,7 @@ higan can use a larger number of files per game.
|
|||||||
For example,
|
For example,
|
||||||
higan's low-level emulation of Super Famicom co-processors
|
higan's low-level emulation of Super Famicom co-processors
|
||||||
often requires [separate firmware files][firmware].
|
often requires [separate firmware files][firmware].
|
||||||
higan's [MSU-1 feature][msu1]
|
higan's [MSU1 feature][msu1]
|
||||||
supports up to 99 audio tracks per game,
|
supports up to 99 audio tracks per game,
|
||||||
and higan supports up to 133 save-states per game.
|
and higan supports up to 133 save-states per game.
|
||||||
Thus,
|
Thus,
|
||||||
@@ -70,7 +70,7 @@ like save-states and the cheat database
|
|||||||
to be kept separate from the game's actual data,
|
to be kept separate from the game's actual data,
|
||||||
by putting it in a sub-folder.
|
by putting it in a sub-folder.
|
||||||
|
|
||||||
[msu1]: ../guides/import.md#msu-1-games
|
[msu1]: ../guides/import.md#msu1-games
|
||||||
[firmware]: ../guides/import.md#games-with-co-processor-firmware
|
[firmware]: ../guides/import.md#games-with-co-processor-firmware
|
||||||
|
|
||||||
For a more detailed motivation for game folders,
|
For a more detailed motivation for game folders,
|
||||||
@@ -128,19 +128,27 @@ to all emulators that support them:
|
|||||||
will create this file.
|
will create this file.
|
||||||
- `*.data.rom`, `*.program.rom`:
|
- `*.data.rom`, `*.program.rom`:
|
||||||
Files named like this are usually [co-processor firmware][firmware].
|
Files named like this are usually [co-processor firmware][firmware].
|
||||||
- `msu1.rom`:
|
|
||||||
Holds streamable data for [the MSU-1][msu1].
|
Files used by higan's [MSU1 extension][msu1]
|
||||||
|
are in the `msu1` sub-folder:
|
||||||
|
|
||||||
|
- `data.rom`:
|
||||||
|
Holds data that the MSU1 can stream.
|
||||||
- `track-*.pcm`:
|
- `track-*.pcm`:
|
||||||
Holds streamable audio for [the MSU-1][msu1].
|
Holds audio that the MSU1 can stream.
|
||||||
|
|
||||||
Files that are only useful to higan specifically
|
Files that are only useful to higan specifically
|
||||||
are placed in a `higan` sub-folder:
|
are placed in a `higan` sub-folder:
|
||||||
|
|
||||||
- `cheats.bml`:
|
- `cheats.bml`:
|
||||||
All information present in
|
All information present in
|
||||||
[the Cheat Editor](../interface/higan-tools.md#the-cheat-editor)
|
the [Cheat Editor](../interface/higan-tools.md#cheat-editor)
|
||||||
is stored here.
|
is stored here.
|
||||||
|
- `notes.txt`:
|
||||||
|
Everything entered in the [Game Notes] is stored here.
|
||||||
- `states/quick/slot-*.bst`:
|
- `states/quick/slot-*.bst`:
|
||||||
All [Quick States](save-states.md#quick-states) are stored here.
|
All [Quick States](save-states.md#quick-states) are stored here.
|
||||||
- `states/managed/slot-*.bst`:
|
- `states/managed/slot-*.bst`:
|
||||||
All [Manager States](save-states.md#manager-states) are stored here.
|
All [Manager States](save-states.md#manager-states) are stored here.
|
||||||
|
|
||||||
|
[Game Notes]: ../interface/higan-tools.md#game-notes
|
||||||
|
@@ -4,8 +4,8 @@ is the folder where all the
|
|||||||
When [icarus](../interface/icarus.md) imports a game,
|
When [icarus](../interface/icarus.md) imports a game,
|
||||||
it creates or updates
|
it creates or updates
|
||||||
the corresponding game folder in the game library.
|
the corresponding game folder in the game library.
|
||||||
When you use the console sub-menu items
|
When you use the items in
|
||||||
in [higan's Library menu](../interface/higan.md#the-library-menu),
|
[higan's Systems menu](../interface/higan.md#the-systems-menu),
|
||||||
higan shows you the games for that console
|
higan shows you the games for that console
|
||||||
that are already in the library.
|
that are already in the library.
|
||||||
|
|
||||||
|
@@ -9,7 +9,7 @@ and to the console itself.
|
|||||||
If you load a game into higan,
|
If you load a game into higan,
|
||||||
you can look at the game's manifest
|
you can look at the game's manifest
|
||||||
by opening [the Tools menu](../interface/higan.md#the-tools-menu)
|
by opening [the Tools menu](../interface/higan.md#the-tools-menu)
|
||||||
and choosing [Manifest Viewer](../interface/higan-tools.md#the-manifest-viewer).
|
and choosing [Manifest Viewer](../interface/higan-tools.md#manifest-viewer).
|
||||||
|
|
||||||
Why manifests?
|
Why manifests?
|
||||||
--------------
|
--------------
|
||||||
@@ -17,7 +17,7 @@ Why manifests?
|
|||||||
For most consoles,
|
For most consoles,
|
||||||
a manifest isn't strictly necessary:
|
a manifest isn't strictly necessary:
|
||||||
the raw game data provides enough clues
|
the raw game data provides enough clues
|
||||||
for emulators to guess the circuit board configuration,
|
for emulators to guess the correct circuit board configuration,
|
||||||
or at least
|
or at least
|
||||||
to guess a *reasonable* configuration.
|
to guess a *reasonable* configuration.
|
||||||
However,
|
However,
|
||||||
@@ -100,6 +100,13 @@ heuristics will always be needed as a fallback,
|
|||||||
but at least if the heuristics are wrong
|
but at least if the heuristics are wrong
|
||||||
they can be overridden.
|
they can be overridden.
|
||||||
|
|
||||||
|
If you are a homebrew author
|
||||||
|
or have dumped a previously-unknown cartridge,
|
||||||
|
and want to write a manifest yourself,
|
||||||
|
you should read the [official manifest specification][manifest].
|
||||||
|
|
||||||
|
[manifest]: https://doc.byuu.org/higan/manifests/
|
||||||
|
|
||||||
Ignoring manifests
|
Ignoring manifests
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
@@ -119,7 +119,7 @@ Manager states
|
|||||||
--------------
|
--------------
|
||||||
|
|
||||||
higan's
|
higan's
|
||||||
[State Manager](../interface/higan-tools.md#the-state-manager)
|
[State Manager](../interface/higan-tools.md#state-manager)
|
||||||
allows you to create over a hundred save states,
|
allows you to create over a hundred save states,
|
||||||
and add a helpful description to each one.
|
and add a helpful description to each one.
|
||||||
|
|
||||||
|
63
docs/credits.md
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
higan's original author:
|
||||||
|
|
||||||
|
- byuu
|
||||||
|
|
||||||
|
We'd like to acknowledge
|
||||||
|
many invaluable contributions made to higan
|
||||||
|
by the following individuals:
|
||||||
|
|
||||||
|
- Andreas Naive
|
||||||
|
- Ange Albertini
|
||||||
|
- anomie
|
||||||
|
- AWJ
|
||||||
|
- Bisqwit
|
||||||
|
- blargg
|
||||||
|
- Łukasz Krawczyk
|
||||||
|
- Cydrak
|
||||||
|
- Danish
|
||||||
|
- DMV27
|
||||||
|
- Dr. Decapitator
|
||||||
|
- endrift
|
||||||
|
- Fatbag
|
||||||
|
- FitzRoy
|
||||||
|
- gekkio
|
||||||
|
- GIGO
|
||||||
|
- Hendricks266
|
||||||
|
- hex_usr
|
||||||
|
- ikari_01
|
||||||
|
- jchadwick
|
||||||
|
- Jonas Quinn
|
||||||
|
- kode54
|
||||||
|
- krom
|
||||||
|
- Lioncash
|
||||||
|
- Lord Nightmare
|
||||||
|
- lowkey
|
||||||
|
- MerryMage
|
||||||
|
- Matthew Callis
|
||||||
|
- mightymo
|
||||||
|
- Nach
|
||||||
|
- ncbncb
|
||||||
|
- neviksti
|
||||||
|
- OV2
|
||||||
|
- Overload
|
||||||
|
- p4plus2
|
||||||
|
- quequotion
|
||||||
|
- RedDwarf
|
||||||
|
- Richard Bannister
|
||||||
|
- Ryphecha
|
||||||
|
- segher
|
||||||
|
- Sintendo
|
||||||
|
- SuperMikeMan
|
||||||
|
- tetsuo55
|
||||||
|
- TmEE
|
||||||
|
- TRAC
|
||||||
|
- wareya
|
||||||
|
- zones
|
||||||
|
|
||||||
|
It's been a long, wild ride...
|
||||||
|
apologies to anyone we've missed!
|
||||||
|
|
||||||
|
For more information,
|
||||||
|
see the [credits thread](
|
||||||
|
https://helmet.kafuka.org/byuubackup2/viewtopic.php@f=4&t=1631.html)
|
||||||
|
on the archive of the official forums.
|
@@ -5,17 +5,17 @@ playing audio,
|
|||||||
and accepting input from game controllers.
|
and accepting input from game controllers.
|
||||||
Or rather,
|
Or rather,
|
||||||
there are many standards,
|
there are many standards,
|
||||||
and different ones work best
|
and different ones work better
|
||||||
on different computers.
|
on different computers.
|
||||||
Therefore,
|
Therefore,
|
||||||
higan comes with "drivers"
|
higan comes with "drivers"
|
||||||
for video, audio and input,
|
for video, audio and input,
|
||||||
so you can find the one that works best for you.
|
so you can find the one that works best for your computer.
|
||||||
To see what drivers you're currently using,
|
To see what drivers you're currently using,
|
||||||
or to choose different ones,
|
or to choose different ones,
|
||||||
go to
|
go to the [Advanced tab] of the Settings window.
|
||||||
[the Advanced tab](../interface/higan-settings.md#advanced)
|
|
||||||
of the Settings window.
|
[Advanced tab]: ../interface/higan-settings.md#advanced
|
||||||
|
|
||||||
Here are the most notable drivers
|
Here are the most notable drivers
|
||||||
for each platform
|
for each platform
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
Before it can load a game,
|
Before it can load a game,
|
||||||
higan requires that all the game's data
|
higan requires that all the game's data
|
||||||
be stored correctly in
|
be stored correctly in the [Game Library].
|
||||||
[the Game Library](../concepts/game-library.md).
|
|
||||||
For [regular games](#regular-games)
|
For [regular games](#regular-games)
|
||||||
this is simple,
|
this is simple,
|
||||||
but some games require special treatment,
|
but some games require special treatment,
|
||||||
@@ -11,7 +10,7 @@ unusual hardware.
|
|||||||
Regular games
|
Regular games
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
higan's importing tool, icarus, can import games
|
higan's importing tool, [icarus], can import games
|
||||||
in the most commonly-used formats
|
in the most commonly-used formats
|
||||||
for each supported console,
|
for each supported console,
|
||||||
and also those same formats inside `.zip` files
|
and also those same formats inside `.zip` files
|
||||||
@@ -20,26 +19,20 @@ More advanced compression formats
|
|||||||
like RAR or 7-zip are not supported.
|
like RAR or 7-zip are not supported.
|
||||||
|
|
||||||
To import a game,
|
To import a game,
|
||||||
open [the Library menu](../interface/higan.md#the-library-menu),
|
open the [Systems menu],
|
||||||
choose "Load ROM File ..."
|
choose "Load ROM File ..."
|
||||||
to open [a filesystem browser](../interface/common.md#the-filesystem-browser),
|
to open a [filesystem browser],
|
||||||
choose the ROM file of the game you want to play,
|
choose the ROM file of the game you want to play,
|
||||||
and it will be imported into the library and start playing.
|
and it will be imported into the library
|
||||||
|
and (if possible) start playing.
|
||||||
|
|
||||||
**Note:**
|
**Note:**
|
||||||
If you want to import many games,
|
If you want to import many games,
|
||||||
run icarus directly,
|
run icarus directly.
|
||||||
or choose "Import ROM Files ..."
|
See the [icarus] documentation for details.
|
||||||
from the Library menu
|
|
||||||
(which just runs icarus anyway).
|
|
||||||
See [the icarus documentation](../interface/icarus.md) for details.
|
|
||||||
|
|
||||||
To play a game for a particular console from your library,
|
To play a game for a particular console from your library,
|
||||||
open the Library menu,
|
open the [Systems menu],
|
||||||
pick the console manufacturer sub-menu
|
|
||||||
(Nintendo for the Super Famicom,
|
|
||||||
Bandai for the WonderSwan,
|
|
||||||
etc.)
|
|
||||||
then choose the appropriate console menu item.
|
then choose the appropriate console menu item.
|
||||||
A filesystem browser will appear
|
A filesystem browser will appear
|
||||||
listing all the games in the library
|
listing all the games in the library
|
||||||
@@ -75,13 +68,24 @@ higan requires a copy of the co-processor firmware
|
|||||||
as well as the actual game data.
|
as well as the actual game data.
|
||||||
Unfortunately,
|
Unfortunately,
|
||||||
like games themselves,
|
like games themselves,
|
||||||
co-processor firmware cannot legally be distributed,
|
most co-processor firmware cannot legally be distributed,
|
||||||
so you'll need to obtain
|
so you'll need to
|
||||||
copies of the relevant firmware data
|
obtain copies of the relevant firmware data
|
||||||
yourself.
|
for yourself.
|
||||||
|
|
||||||
To import a game that requires co-processor firmware,
|
To import a game that requires co-processor firmware,
|
||||||
you must first combine the game data and the firmware into a single file.
|
the easiest approach is to drop the firmware files into
|
||||||
|
icarus' `Firmware` directory
|
||||||
|
before importing the game.
|
||||||
|
The directory should be beside the icarus executable,
|
||||||
|
or it can be `%LOCALAPPDATA%\icarus\Firmware` (on Windows)
|
||||||
|
or `~/.local/share/icarus/Firmware/` (on Linux).
|
||||||
|
|
||||||
|
If the easy approach doesn't work for a particular game,
|
||||||
|
it may be because icarus has incorrectly guessed
|
||||||
|
which firmware that game needs.
|
||||||
|
To ensure icarus uses specific firmware with a specific game,
|
||||||
|
you must combine the game data and the firmware into a single file.
|
||||||
For example,
|
For example,
|
||||||
let's say you want to import *Super Bases Loaded 2* for the Super Famicom,
|
let's say you want to import *Super Bases Loaded 2* for the Super Famicom,
|
||||||
which is stored in the file `sbl2.sfc`
|
which is stored in the file `sbl2.sfc`
|
||||||
@@ -105,7 +109,7 @@ cat dsp1.program.rom dsp1.data.rom >> sbl2.sfc
|
|||||||
|
|
||||||
**Note:**
|
**Note:**
|
||||||
For co-processor chips with multiple firmware files,
|
For co-processor chips with multiple firmware files,
|
||||||
you must put the "program" file before the "data" file.
|
always put the "program" file before the "data" file.
|
||||||
|
|
||||||
Wikipedia [lists which Super Famicom games use which co-processors][wpec],
|
Wikipedia [lists which Super Famicom games use which co-processors][wpec],
|
||||||
although not all co-processors require separate firmware.
|
although not all co-processors require separate firmware.
|
||||||
@@ -127,13 +131,13 @@ here's the firmware files you'll need:
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">CX4</th>
|
<th scope="row">CX4<br><sup>See Note 1</sup></th>
|
||||||
<td><code>cx4.data.rom</code></td>
|
<td><code>cx4.data.rom</code></td>
|
||||||
<td>3072</td>
|
<td>3072</td>
|
||||||
<td><code>ae8d4d1961b93421ff00b3caa1d0f0ce7783e749772a3369c36b3dbf0d37ef18</code></td>
|
<td><code>ae8d4d1961b93421ff00b3caa1d0f0ce7783e749772a3369c36b3dbf0d37ef18</code></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" rowspan=2>DSP1/1A<br><sup>See Note 1</sup></th>
|
<th scope="row" rowspan=2>DSP1/1A<br><sup>See Note 2</sup></th>
|
||||||
<td><code>dsp1.data.rom</code></td>
|
<td><code>dsp1.data.rom</code></td>
|
||||||
<td>2048</td>
|
<td>2048</td>
|
||||||
<td><code>0b5da6533e55852ee8fc397977ec5576c5b9f1fb2e05656d8f87123a121b076e</code></td>
|
<td><code>0b5da6533e55852ee8fc397977ec5576c5b9f1fb2e05656d8f87123a121b076e</code></td>
|
||||||
@@ -144,7 +148,7 @@ here's the firmware files you'll need:
|
|||||||
<td><code>269584b347a22953a2989494c850a7c1c027f4ca5add517a60e0c7d8833d0fac</code></td>
|
<td><code>269584b347a22953a2989494c850a7c1c027f4ca5add517a60e0c7d8833d0fac</code></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" rowspan=2>DSP1B<br><sup>See Note 2</sup></th>
|
<th scope="row" rowspan=2>DSP1B<br><sup>See Note 3</sup></th>
|
||||||
<td><code>dsp1b.data.rom</code></td>
|
<td><code>dsp1b.data.rom</code></td>
|
||||||
<td>2048</td>
|
<td>2048</td>
|
||||||
<td><code>8546cbac530830446bb8a277f6b139d4ad64d650bdbac7e4e150e2f095665049</code></td>
|
<td><code>8546cbac530830446bb8a277f6b139d4ad64d650bdbac7e4e150e2f095665049</code></td>
|
||||||
@@ -213,53 +217,54 @@ here's the firmware files you'll need:
|
|||||||
<th scope="row" rowspan="2">ST018</th>
|
<th scope="row" rowspan="2">ST018</th>
|
||||||
<td><code>st018.data.rom</code></td>
|
<td><code>st018.data.rom</code></td>
|
||||||
<td>32768</td>
|
<td>32768</td>
|
||||||
<td><code>b5377d1bebe8adc507a024b6e2b9b8fdf4877e451da84fbad05dff4e4a70311e</code></td>
|
<td><code>c67032238b7182696cb80cf41b61bacda91adb2120b5370bea20c9dbf5cc79b8</code></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>st018.program.rom</code></td>
|
<td><code>st018.program.rom</code></td>
|
||||||
<td>131072</td>
|
<td>131072</td>
|
||||||
<td><code>d90a5cda380e81cb9ba11a9da7539b173c49b31bedc7a3ac9c3c8b3f97e89e14</code></td>
|
<td><code>6cceff3c6945bb2672040066d218efcd2f31492f3f5c28916c8e53435c2c887e</code></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
**Note 1:**
|
**Note 1:**
|
||||||
|
The CX4 firmware is shipped with higan,
|
||||||
|
because it just contains mathematical tables
|
||||||
|
and not a copyrightable program.
|
||||||
|
|
||||||
|
**Note 2:**
|
||||||
The DSP1 and DSP1A are physically different,
|
The DSP1 and DSP1A are physically different,
|
||||||
but the firmware inside is identical.
|
but the firmware inside is identical.
|
||||||
|
|
||||||
**Note 2:**
|
**Note 3:**
|
||||||
The DSP1B is very similar to the DSP1A,
|
The DSP1B is very similar to the DSP1A,
|
||||||
but fixes some bugs.
|
but fixes some bugs.
|
||||||
Note that icarus' heuristics cannot distinguish between
|
Note that icarus' heuristics cannot distinguish between
|
||||||
a game that uses the DSP1
|
a game that uses the DSP1
|
||||||
and one that uses the DSP1B,
|
and one that uses the DSP1B,
|
||||||
so if it cannot find your game in its manifest database,
|
so if it cannot find your game in its database,
|
||||||
it will assume it uses DSP1B.
|
it will assume it uses DSP1B.
|
||||||
Many games work just as well with either variant,
|
Many games work just as well with either variant,
|
||||||
but *Pilotwings* requires the DSP1 firmware,
|
but *Pilotwings* requires the DSP1 firmware,
|
||||||
while *Ballz 3D* requires the DSP1B.
|
while *Ballz 3D* requires the DSP1B.
|
||||||
|
|
||||||
If you try to import a game
|
If you try to import a game with icarus,
|
||||||
using the "Import ROM Files ..." option
|
but it cannot find the required firmware files,
|
||||||
in [the Library menu](../interface/higan.md#the-library-menu)
|
|
||||||
(or using icarus directly)
|
|
||||||
but it does not include the correct firmware data,
|
|
||||||
a window will appear saying
|
a window will appear saying
|
||||||
"Import completed, but with 1 errors. View log?"
|
"Import completed, but with 1 errors. View log?"
|
||||||
(or however many games were lacking the correct firmware).
|
(or however many games were lacking firmware).
|
||||||
If you press "Yes",
|
If you press "Yes",
|
||||||
a new window will appear listing the games that couldn't be imported,
|
a new window will appear listing the games that couldn't be imported,
|
||||||
and what problem was detected:
|
and what problem was detected:
|
||||||
|
|
||||||
> [sbl2.sfc] ROM image is missing DSP1 firmware data
|
> [sbl2.sfc] ROM image is missing data: dsp1.program.rom; dsp1.data.rom
|
||||||
|
|
||||||
If you try to import a game
|
If you try to import a game
|
||||||
using the "Load ROM File ..." option
|
using the "Load ROM File ..." option in the [Systems menu]
|
||||||
in [the Library menu](../interface/higan.md#the-library-menu)
|
|
||||||
but it does not include the correct firmware data,
|
but it does not include the correct firmware data,
|
||||||
nothing will happen,
|
nothing will happen,
|
||||||
and higan will just sit there
|
and higan will just sit there
|
||||||
with "No cartridge loaded" in
|
with "Unloaded" in
|
||||||
[the status bar](../interface/higan.md#the-status-bar).
|
[the status bar](../interface/higan.md#the-status-bar).
|
||||||
|
|
||||||
Once a game with co-processor firmware is imported,
|
Once a game with co-processor firmware is imported,
|
||||||
@@ -268,7 +273,7 @@ you can play it just like any [regular game](#regular-games).
|
|||||||
Satellaview games
|
Satellaview games
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
The [Satellaview][wpbsx]
|
The [Satellaview]
|
||||||
was a satellite modem peripheral
|
was a satellite modem peripheral
|
||||||
released for the Super Famicom in Japan.
|
released for the Super Famicom in Japan.
|
||||||
As well as the actual modem
|
As well as the actual modem
|
||||||
@@ -282,7 +287,7 @@ This control cartridge was called
|
|||||||
which in English is
|
which in English is
|
||||||
*BS-X The Story of The Town Whose Name Was Stolen*.
|
*BS-X The Story of The Town Whose Name Was Stolen*.
|
||||||
|
|
||||||
[wpbsx]: https://en.wikipedia.org/wiki/Satellaview
|
[Satellaview]: https://en.wikipedia.org/wiki/Satellaview
|
||||||
|
|
||||||
The control cartridge had a slot that accepted
|
The control cartridge had a slot that accepted
|
||||||
re-writable "memory paks",
|
re-writable "memory paks",
|
||||||
@@ -301,8 +306,16 @@ containing extra content for specific games.
|
|||||||
Importing a game that has a slot for a memory pak
|
Importing a game that has a slot for a memory pak
|
||||||
is just like [importing a regular game](#regular-games).
|
is just like [importing a regular game](#regular-games).
|
||||||
|
|
||||||
Importing a memory pak is like importing a regular game,
|
To import a memory pak,
|
||||||
but the name of the memory pak file *must* end in `.bs`
|
you should use [icarus].
|
||||||
|
You can use the "Load ROM File ..." menu item
|
||||||
|
in the [Systems menu],
|
||||||
|
but higan cannot actually load a memory pak directly,
|
||||||
|
so once you choose a file to load
|
||||||
|
it looks like nothing has happened.
|
||||||
|
|
||||||
|
When importing a memory pak,
|
||||||
|
the name of the memory pak file *must* end in `.bs`
|
||||||
(if it's in a `.zip` file,
|
(if it's in a `.zip` file,
|
||||||
that's OK,
|
that's OK,
|
||||||
but the name *inside* the `.zip` file
|
but the name *inside* the `.zip` file
|
||||||
@@ -317,8 +330,7 @@ Rename the file and it should work beautifully.
|
|||||||
Playing a game that has a slot for a memory pak
|
Playing a game that has a slot for a memory pak
|
||||||
is just like playing a regular game,
|
is just like playing a regular game,
|
||||||
but after you have selected which game you want to play
|
but after you have selected which game you want to play
|
||||||
higan will open another
|
higan will open another [filesystem browser]
|
||||||
[filesystem browser](../interface/common.md#the-filesystem-browser)
|
|
||||||
to let you pick which previously-imported memory pak
|
to let you pick which previously-imported memory pak
|
||||||
you want to insert into the game.
|
you want to insert into the game.
|
||||||
If you press "Cancel" at this point,
|
If you press "Cancel" at this point,
|
||||||
@@ -352,7 +364,7 @@ see [the BS-X Project](https://bsxproj.superfamicom.org/).
|
|||||||
Sufami Turbo games
|
Sufami Turbo games
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
The [Sufami Turbo][wpst]
|
The [Sufami Turbo]
|
||||||
was a special cartridge released
|
was a special cartridge released
|
||||||
for the Super Famicom in Japan.
|
for the Super Famicom in Japan.
|
||||||
The Sufami Turbo on its own does nothing,
|
The Sufami Turbo on its own does nothing,
|
||||||
@@ -365,8 +377,16 @@ from a game in slot B.
|
|||||||
Importing the Sufami Turbo cartridge
|
Importing the Sufami Turbo cartridge
|
||||||
is just like [importing a regular game](#regular-games).
|
is just like [importing a regular game](#regular-games).
|
||||||
|
|
||||||
Importing a mini-cartridge is like importing a regular game,
|
To import a mini-cartridge,
|
||||||
but the name of the mini-cartridge file *must* end in `.st`
|
you should use [icarus].
|
||||||
|
You can use the "Load ROM File ..." menu item
|
||||||
|
in the [Systems menu],
|
||||||
|
but higan cannot actually load a mini-cartridge directly,
|
||||||
|
so once you choose a file to load
|
||||||
|
it looks like nothing has happened.
|
||||||
|
|
||||||
|
When importing a mini-cartridge,
|
||||||
|
the name of the file *must* end in `.st`
|
||||||
(if it's in a `.zip` file,
|
(if it's in a `.zip` file,
|
||||||
that's OK,
|
that's OK,
|
||||||
but the name *inside* the `.zip` file
|
but the name *inside* the `.zip` file
|
||||||
@@ -380,8 +400,7 @@ Rename the file and it should work beautifully.
|
|||||||
|
|
||||||
To play a Sufami Turbo game,
|
To play a Sufami Turbo game,
|
||||||
load the Sufami Turbo cartridge like any other game.
|
load the Sufami Turbo cartridge like any other game.
|
||||||
higan will open another
|
higan will open another [filesystem browser]
|
||||||
[filesystem browser](../interface/common.md#the-filesystem-browser)
|
|
||||||
to let you pick which previously-imported mini-cartridge
|
to let you pick which previously-imported mini-cartridge
|
||||||
you want to insert into slot A.
|
you want to insert into slot A.
|
||||||
If you press "Cancel" at this point,
|
If you press "Cancel" at this point,
|
||||||
@@ -397,7 +416,12 @@ to let you choose a mini-cartridge for slot B.
|
|||||||
If you press "Cancel" at this point,
|
If you press "Cancel" at this point,
|
||||||
the Sufami Turbo cartridge will boot without anything in slot B.
|
the Sufami Turbo cartridge will boot without anything in slot B.
|
||||||
|
|
||||||
[wpst]: https://en.wikipedia.org/wiki/Sufami_Turbo
|
If you play Sufami Turbo games regularly,
|
||||||
|
you may want to add the Sufami Turbo base cartridge
|
||||||
|
to the [Systems menu]
|
||||||
|
so you don't have to tell higan where it is every time.
|
||||||
|
|
||||||
|
[Sufami Turbo]: https://en.wikipedia.org/wiki/Sufami_Turbo
|
||||||
|
|
||||||
Super Game Boy games
|
Super Game Boy games
|
||||||
--------------------
|
--------------------
|
||||||
@@ -407,17 +431,29 @@ released for the Super Famicom
|
|||||||
(and all its regional variants around the world)
|
(and all its regional variants around the world)
|
||||||
that allowed Game Boy games to be played
|
that allowed Game Boy games to be played
|
||||||
via the Super Famicom's controllers and video output.
|
via the Super Famicom's controllers and video output.
|
||||||
The Super Game Boy 2 was released in Japan,
|
The Super Game Boy does not emulate the Game Boy hardware,
|
||||||
and had some minor extra features
|
it physically includes all the Game Boy components
|
||||||
beyond the original Super Game Boy,
|
so compatibility with Game Boy games is high.
|
||||||
but importing and playing games
|
However, the Super Game Boy drives the Game Boy hardware
|
||||||
works the same way in higan.
|
from the Super Famicom's timing signals, which means
|
||||||
|
games play 2.4% faster than on a real Game Boy.
|
||||||
|
|
||||||
The Super Game Boy cartridge includes
|
The Super Game Boy 2 was a Japan-only release
|
||||||
the complete hardware of an original
|
that fixed the timing problem of the original Super Game Boy,
|
||||||
(black-and-white)
|
and included a different set of default borders.
|
||||||
Game Boy,
|
higan emulates the Super Game Boy 2 completely,
|
||||||
so it needs a boot ROM:
|
including the timing change.
|
||||||
|
|
||||||
|
Because the Super Game Boy cartridge includes
|
||||||
|
the original Game Boy hardware,
|
||||||
|
it needs a boot ROM.
|
||||||
|
icarus includes these files
|
||||||
|
and can reliably decide when to use them,
|
||||||
|
so importing either Super Game Boy cartridge
|
||||||
|
is just like [importing a regular game](#regular-games).
|
||||||
|
|
||||||
|
In case you need to check the Super Game Boy boot roms,
|
||||||
|
here are their details:
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
@@ -444,22 +480,19 @@ so it needs a boot ROM:
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
To import the SGB base cartridge,
|
|
||||||
you must first combine the base cartridge data
|
|
||||||
and the boot ROM into a single file,
|
|
||||||
just like
|
|
||||||
[games with co-processor firmware](#games-with-co-processor-firmware).
|
|
||||||
Then you may import it like [a regular game](#regular-games).
|
|
||||||
|
|
||||||
To play a Game Boy game in Super Game Boy mode,
|
To play a Game Boy game in Super Game Boy mode,
|
||||||
load the Super Game Boy cartridge like any other game.
|
load the Super Game Boy cartridge like any other game.
|
||||||
higan will open another
|
higan will open another [filesystem browser]
|
||||||
[filesystem browser](../interface/common.md#the-filesystem-browser)
|
|
||||||
to let you pick which previously-imported Game Boy game
|
to let you pick which previously-imported Game Boy game
|
||||||
you want to insert into the Super Game Boy.
|
you want to insert into the Super Game Boy.
|
||||||
If you press "Cancel" at this point,
|
If you press "Cancel" at this point,
|
||||||
higan will crash, so don't do that.
|
higan will crash, so don't do that.
|
||||||
|
|
||||||
|
If you regularly play Game Boy games
|
||||||
|
through the Super Game Boy,
|
||||||
|
you may want to add it to the [Systems menu]
|
||||||
|
so you don't have to tell higan where it is every time.
|
||||||
|
|
||||||
**Note:**
|
**Note:**
|
||||||
Only games for the original, black-and-white Game Boy
|
Only games for the original, black-and-white Game Boy
|
||||||
can be used with the Super Game Boy.
|
can be used with the Super Game Boy.
|
||||||
@@ -471,14 +504,14 @@ for details.
|
|||||||
|
|
||||||
[blackcarts]: ../notes.md#playing-game-boy-color-games-in-game-boy-mode
|
[blackcarts]: ../notes.md#playing-game-boy-color-games-in-game-boy-mode
|
||||||
|
|
||||||
MSU-1 games
|
MSU1 games
|
||||||
-----------
|
----------
|
||||||
|
|
||||||
The MSU-1 is a fictional expansion chip
|
The MSU1 is a fictional expansion chip
|
||||||
invented by higan's author byuu,
|
invented by higan's author byuu,
|
||||||
designed to allow the Super Famicom
|
designed to allow the Super Famicom
|
||||||
to stream data and audio.
|
to stream data and audio.
|
||||||
Although the MSU-1 is not specific
|
Although the MSU1 is not specific
|
||||||
to any particular storage medium,
|
to any particular storage medium,
|
||||||
it gives the Super Famicom similar capabilities
|
it gives the Super Famicom similar capabilities
|
||||||
to CD-based add-ons
|
to CD-based add-ons
|
||||||
@@ -486,32 +519,32 @@ like the Mega Drive's Mega CD
|
|||||||
and the PC Engine's CD-ROM²,
|
and the PC Engine's CD-ROM²,
|
||||||
such as CD-quality music and full-motion video.
|
such as CD-quality music and full-motion video.
|
||||||
|
|
||||||
Although the MSU-1 was invented for higan,
|
Although the MSU1 was invented for higan,
|
||||||
it is now supported by other Super Famicom emulators too.
|
it is now supported by other Super Famicom emulators too.
|
||||||
The [SD2SNES][sd2snes] programmable cartridge
|
The [SD2SNES] programmable cartridge
|
||||||
even allows you to play MSU-1 games on a real console.
|
even allows you to play MSU1 games on a real console.
|
||||||
There are a number of homebrew games
|
There are a number of homebrew games
|
||||||
that make use of the MSU-1,
|
that make use of the MSU1,
|
||||||
and also mods for commercial Super Famicom games
|
and also mods for commercial Super Famicom games
|
||||||
that add higher-quality music and sometimes video.
|
that add higher-quality music and sometimes video.
|
||||||
|
|
||||||
One thing to be aware of
|
One thing to be aware of
|
||||||
when importing an MSU-1 game
|
when importing an MSU1 game
|
||||||
is that early firmware versions of the SD2SNES
|
is that early firmware versions of the SD2SNES
|
||||||
had a bug that caused MSU-1 music to play too quietly.
|
had a bug that caused MSU1 music to play too quietly.
|
||||||
Skipping over [the full details][msu1vol],
|
Skipping over [the full details][msu1vol],
|
||||||
the short version is this:
|
the short version is this:
|
||||||
|
|
||||||
- If offered the choice between "boosted" or non-boosted audio,
|
- If offered the choice between "boosted" or non-boosted audio,
|
||||||
you want the non-boosted version.
|
you want the non-boosted version.
|
||||||
- If an MSU-1 mod for a commercial game offers
|
- If an MSU1 mod for a commercial game offers
|
||||||
"emulator" and "hardware" versions of the patch file,
|
"emulator" and "hardware" versions of the patch file,
|
||||||
it means the audio tracks are already boosted.
|
it means the audio tracks are already boosted.
|
||||||
- Some
|
- Some
|
||||||
[third](https://www.zeldix.net/t1265-#18320)
|
[third](https://www.zeldix.net/t1265-#18320)
|
||||||
[parties](https://www.zeldix.net/t1339-#19818)
|
[parties](https://www.zeldix.net/t1339-#19818)
|
||||||
have created replacement, non-boosted audio tracks
|
have created replacement, non-boosted audio tracks
|
||||||
for the most popular MSU-1 mods.
|
for the most popular MSU1 mods.
|
||||||
If the mod you want to play has a replacement pack,
|
If the mod you want to play has a replacement pack,
|
||||||
use it with the "hardware" version of the patch.
|
use it with the "hardware" version of the patch.
|
||||||
- Even without access to non-boosted audio tracks,
|
- Even without access to non-boosted audio tracks,
|
||||||
@@ -522,11 +555,11 @@ the short version is this:
|
|||||||
distorting and clipping,
|
distorting and clipping,
|
||||||
in which case try the "emulator" patch.
|
in which case try the "emulator" patch.
|
||||||
|
|
||||||
To import an MSU-1 game:
|
To import an MSU1 game:
|
||||||
|
|
||||||
1. If you have a single, large file
|
1. If you have a single, large file
|
||||||
with the `.msu1` extension,
|
with the `.msu1` extension,
|
||||||
that is a pack for use with [Mercurial Magic][mermag],
|
that is a pack for use with [Mercurial Magic],
|
||||||
which can automatically set up a game folder
|
which can automatically set up a game folder
|
||||||
in the correct format.
|
in the correct format.
|
||||||
Go read Mercurial Magic's documentation
|
Go read Mercurial Magic's documentation
|
||||||
@@ -534,29 +567,31 @@ To import an MSU-1 game:
|
|||||||
2. Otherwise,
|
2. Otherwise,
|
||||||
import the Super Famicom ROM with icarus,
|
import the Super Famicom ROM with icarus,
|
||||||
[like a regular game](#regular-games).
|
[like a regular game](#regular-games).
|
||||||
- If this is a homebrew game with MSU-1 support,
|
- If this is a homebrew game with MSU1 support,
|
||||||
there will probably be an ordinary ROM
|
there will probably be an ordinary ROM
|
||||||
whose name ends in `.sfc`,
|
whose name ends in `.sfc`,
|
||||||
which is the file you want to import.
|
which is the file you want to import.
|
||||||
- If this is a commercial game modded for MSU-1 support,
|
- If this is a commercial game modded for MSU1 support,
|
||||||
there will probably be a patch file
|
there will probably be a patch file
|
||||||
whose name ends in `.ips` or `.bps`.
|
whose name ends in `.ips` or `.bps`.
|
||||||
Get a copy of the correct version of the commercial game,
|
Get a copy of the correct version of the commercial game,
|
||||||
apply the patch with a tool like [Flips][flips],
|
apply the patch with a tool like [Flips],
|
||||||
then import the patched file.
|
then import the patched file.
|
||||||
- If there's "hardware" and "emulator" versions of the patch,
|
- If there's "hardware" and "emulator" versions of the patch,
|
||||||
see "One thing to be aware of..." above.
|
see "One thing to be aware of..." above.
|
||||||
3. Find the game folder in
|
3. Find the game folder in the [game library]
|
||||||
[the game library](../concepts/game-library.md)
|
|
||||||
that icarus created when it imported the game.
|
that icarus created when it imported the game.
|
||||||
4. Copy the MSU-1 data file into the game folder.
|
4. Inside the game folder,
|
||||||
- This should be named `msu1.rom`
|
create a new folder named `msu1`.
|
||||||
|
5. Copy the MSU1 data file into the new `msu1` folder.
|
||||||
|
- This should be named `data.rom`
|
||||||
- If there's no file by that name,
|
- If there's no file by that name,
|
||||||
look for a file with a `.msu` extension
|
look for a file named `msu1.rom`,
|
||||||
|
or a file with a `.msu` extension,
|
||||||
and rename it to `msu1.rom`.
|
and rename it to `msu1.rom`.
|
||||||
- If there's no file ending in `.msu` either,
|
- If there's no file ending in `.msu` either,
|
||||||
create an empty file named `msu1.rom`.
|
create an empty file named `msu1.rom`.
|
||||||
5. Copy the audio tracks into the game folder.
|
6. Copy the audio tracks into the game folder.
|
||||||
- If you have to choose between two sets of audio files,
|
- If you have to choose between two sets of audio files,
|
||||||
see "One thing to be aware of..." above.
|
see "One thing to be aware of..." above.
|
||||||
- These should be named
|
- These should be named
|
||||||
@@ -576,20 +611,20 @@ To import an MSU-1 game:
|
|||||||
this game probably just doesn't use the audio-playback feature.
|
this game probably just doesn't use the audio-playback feature.
|
||||||
|
|
||||||
Once the game folder is set up,
|
Once the game folder is set up,
|
||||||
playing an MSU-1 game is just like
|
playing an MSU1 game is just like
|
||||||
[a regular game](#regular-games).
|
[a regular game](#regular-games).
|
||||||
|
|
||||||
[sd2snes]: https://sd2snes.de/
|
[SD2SNES]: https://sd2snes.de/
|
||||||
[flips]: http://www.romhacking.net/utilities/1040/
|
[Flips]: http://www.romhacking.net/utilities/1040/
|
||||||
[msu1vol]: http://blog.qwertymodo.com/2017/07/the-msu-1-volume-fiasco-explained.html
|
[msu1vol]: http://blog.qwertymodo.com/2017/07/the-msu-1-volume-fiasco-explained.html
|
||||||
[mermag]: https://github.com/hex-usr/Mercurial-Magic/
|
[Mercurial Magic]: https://github.com/qwertymodo/Mercurial-Magic
|
||||||
|
|
||||||
Patched games
|
Patched games
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
The console emulation community
|
The console emulation community
|
||||||
has a long and vibrant history of game modding,
|
has a long and vibrant history of game modding,
|
||||||
or [ROM hacking][rhdn],
|
or [ROM hacking],
|
||||||
including fan-translations,
|
including fan-translations,
|
||||||
new levels for existing games,
|
new levels for existing games,
|
||||||
and more.
|
and more.
|
||||||
@@ -598,7 +633,7 @@ would be copyright infringement,
|
|||||||
the changes are typically distributed as "patches",
|
the changes are typically distributed as "patches",
|
||||||
a file containing a list of modifications to make,
|
a file containing a list of modifications to make,
|
||||||
that can be automatically applied by a "patcher" tool
|
that can be automatically applied by a "patcher" tool
|
||||||
like [Flips][flips].
|
like [Flips].
|
||||||
|
|
||||||
higan does not support soft-patching,
|
higan does not support soft-patching,
|
||||||
so if you want to play a patched game in higan,
|
so if you want to play a patched game in higan,
|
||||||
@@ -608,7 +643,7 @@ creating a new, patched copy of the game.
|
|||||||
Then you can import and play the patched game just like
|
Then you can import and play the patched game just like
|
||||||
[a regular game](#regular-games).
|
[a regular game](#regular-games).
|
||||||
|
|
||||||
[rhdn]: http://www.romhacking.net/
|
[ROM hacking]: http://www.romhacking.net/
|
||||||
|
|
||||||
Game Boy Advance games
|
Game Boy Advance games
|
||||||
----------------------
|
----------------------
|
||||||
@@ -627,7 +662,7 @@ GBA games can be imported and played just like
|
|||||||
|
|
||||||
Note that some GBA games
|
Note that some GBA games
|
||||||
have trouble with
|
have trouble with
|
||||||
[in-game saves](../notes#in-game-saves-and-the-game-boy-advance).
|
[in-game saves](../notes.md#in-game-saves-and-the-game-boy-advance).
|
||||||
|
|
||||||
PowerFest '94
|
PowerFest '94
|
||||||
-------------
|
-------------
|
||||||
@@ -645,79 +680,17 @@ switch between them after a specific time,
|
|||||||
extract a score,
|
extract a score,
|
||||||
and display the combined total at the end.
|
and display the combined total at the end.
|
||||||
|
|
||||||
icarus cannot automatically import
|
Previous versions of higan
|
||||||
dumps of the PowerFest '94 ROMs,
|
could emulate the PowerFest '94 cartridge,
|
||||||
but if you have the files,
|
but changes to higan's manifest system in v107
|
||||||
you can import them manually.
|
prevent PowerFest '94 from working in that version.
|
||||||
|
Support will likely be re-added in a future version,
|
||||||
|
but in the mean time you can use higan v106
|
||||||
|
and follow [that version's import instructions][pf94v106].
|
||||||
|
|
||||||
You will need the following files:
|
[pf94v106]: https://higan.readthedocs.io/en/v106/guides/import/#powerfest-94
|
||||||
|
|
||||||
<table>
|
[filesystem browser]: ../interface/common.md#the-filesystem-browser
|
||||||
<thead>
|
[Game Library]: ../concepts/game-library.md
|
||||||
<tr>
|
[icarus]: ../interface/icarus.md
|
||||||
<th>Part</th>
|
[Systems menu]: ../interface/higan.md#the-systems-menu
|
||||||
<th>Filename</th>
|
|
||||||
<th>Size (bytes)</th>
|
|
||||||
<th>SHA256</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">Scoring</th>
|
|
||||||
<td><code>program.rom</code></td>
|
|
||||||
<td>262144</td>
|
|
||||||
<td><code>2fc9dca305ce3fb2f1a476567de500d50c174fbfbabd32b1b91c3ea6a731b4a1</code></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">Super Mario Bros. - The Lost Levels</th>
|
|
||||||
<td><code>slot-1.rom</code></td>
|
|
||||||
<td>524288</td>
|
|
||||||
<td><code>7fd86113c5f95f794d65807bb75ab91c93c914670c27fc813ffa2ca20a48705e</code></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">Super Mario Kart</th>
|
|
||||||
<td><code>slot-2.rom</code></td>
|
|
||||||
<td>524288</td>
|
|
||||||
<td><code>19eb77affbf8dd068f5d79a3cf80a2084fd73237cd1ae4e47192b4422449e64a</code></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">Ken Griffey Jr. Presents Major League Baseball</th>
|
|
||||||
<td><code>slot-3.rom</code></td>
|
|
||||||
<td>1048576</td>
|
|
||||||
<td><code>d47bc9f9a6289c4f2e7f6bf74095f6ed36b1043a761e3e729ac9af2fc39ae062</code></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
You will also need
|
|
||||||
the usual `dsp1.program.rom` and `dsp1.data.rom`
|
|
||||||
[co-processor firmware](#games-with-co-processor-firmware) files.
|
|
||||||
|
|
||||||
**Note:** the versions of
|
|
||||||
*Super Mario Kart*
|
|
||||||
and *Ken Griffey Jr...*
|
|
||||||
in *PowerFest '94*
|
|
||||||
are not the same as the stand-alone versions of those games.
|
|
||||||
|
|
||||||
To "import" *PowerFest '94*,
|
|
||||||
collect all the files mentioned above, then:
|
|
||||||
|
|
||||||
1. Inside [the game library](../concepts/game-library.md),
|
|
||||||
create the `Super Famicom` folder
|
|
||||||
(if it does not already exist).
|
|
||||||
2. Inside the `Super Famicom` folder,
|
|
||||||
create a `PowerFest '94.sfc` folder
|
|
||||||
(the `.sfc` extension is important,
|
|
||||||
but you can choose a different base name if you want).
|
|
||||||
3. Copy the various ROM files into the `PowerFest '94.sfc` folder.
|
|
||||||
|
|
||||||
To play *PowerFest '94*,
|
|
||||||
open the Library menu,
|
|
||||||
pick the Nintendo sub-menu,
|
|
||||||
then choose the Super Famicom sub-menu item
|
|
||||||
to open a filesystem browser listing
|
|
||||||
all the Super Famicom games in the library.
|
|
||||||
Select *PowerFest '94* from the list
|
|
||||||
and click the Open button,
|
|
||||||
or just double-click the game,
|
|
||||||
and it will begin playing.
|
|
||||||
|
@@ -2,17 +2,16 @@ Most of the consoles higan emulates
|
|||||||
were designed for low resolution NTSC televisions,
|
were designed for low resolution NTSC televisions,
|
||||||
and their video output is chunky and blocky
|
and their video output is chunky and blocky
|
||||||
by today's standards.
|
by today's standards.
|
||||||
Video shaders customise how a console's video output
|
Video shaders customise how the emulated console's video output
|
||||||
is drawn to the computer screen,
|
is drawn to the computer screen,
|
||||||
and can clean up and smooth out the original video,
|
and can clean up and smooth out the original video,
|
||||||
reproduce the scanlines and blurring of the original display,
|
reproduce the scanlines and blurring of the original display,
|
||||||
or any other visual effect.
|
or any other visual effect.
|
||||||
|
|
||||||
The available video shaders are listed in
|
The available video shaders are listed in
|
||||||
the "Video Shaders" sub-menu of
|
the "Shader" sub-menu of the [Settings menu].
|
||||||
[the Settings menu](../interface/higan.md#the-settings-menu).
|
|
||||||
Which shaders are available depends on
|
Which shaders are available depends on
|
||||||
the [video driver](drivers.md#video) higan is configured to use.
|
the [video driver] higan is configured to use.
|
||||||
Most drivers only support these shaders:
|
Most drivers only support these shaders:
|
||||||
|
|
||||||
- **None**
|
- **None**
|
||||||
@@ -20,7 +19,8 @@ Most drivers only support these shaders:
|
|||||||
the colour of the single nearest console pixel,
|
the colour of the single nearest console pixel,
|
||||||
sometimes called "nearest neighbour" scaling.
|
sometimes called "nearest neighbour" scaling.
|
||||||
This produces unnaturally crisp and blocky images.
|
This produces unnaturally crisp and blocky images.
|
||||||
- If you use [aspect correction or non-integral scaling][ac],
|
- If you enable Scale, Stretch, or Aspect Correction modes
|
||||||
|
in the Output sub-menu of the [Settings menu],
|
||||||
neighbouring console pixels may be drawn
|
neighbouring console pixels may be drawn
|
||||||
with a different number of computer pixels due to rounding errors,
|
with a different number of computer pixels due to rounding errors,
|
||||||
causing a distracting rippling effect.
|
causing a distracting rippling effect.
|
||||||
@@ -31,8 +31,6 @@ Most drivers only support these shaders:
|
|||||||
sometimes called "bilinear" scaling.
|
sometimes called "bilinear" scaling.
|
||||||
This produces unnaturally blurry images.
|
This produces unnaturally blurry images.
|
||||||
|
|
||||||
[ac]: ../interface/higan-settings.md#video
|
|
||||||
|
|
||||||
In addition to those,
|
In addition to those,
|
||||||
the OpenGL driver also supports custom shaders.
|
the OpenGL driver also supports custom shaders.
|
||||||
|
|
||||||
@@ -52,12 +50,13 @@ Where to get custom shaders
|
|||||||
|
|
||||||
- higan includes some simple example shaders.
|
- higan includes some simple example shaders.
|
||||||
If your copy of higan did not come with shaders,
|
If your copy of higan did not come with shaders,
|
||||||
you can get them from
|
you can get them from the [unofficial higan repository].
|
||||||
[the unofficial higan repository](https://gitlab.com/higan/higan/tree/master/shaders).
|
- [quark-shaders] contains many high-quality shaders for use with higan.
|
||||||
- [quark-shaders](https://github.com/hizzlekizzle/quark-shaders)
|
|
||||||
contains many high-quality shaders for use with higan.
|
|
||||||
- You can write your own.
|
- You can write your own.
|
||||||
|
|
||||||
|
[unofficial higan repository]: https://gitlab.com/higan/higan/tree/master/shaders
|
||||||
|
[quark-shaders]: https://github.com/hizzlekizzle/quark-shaders
|
||||||
|
|
||||||
How to install custom shaders
|
How to install custom shaders
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
@@ -68,11 +67,11 @@ it should contain a file named `manifest.bml`,
|
|||||||
and probably some `*.fs` or `*.vs` files.
|
and probably some `*.fs` or `*.vs` files.
|
||||||
|
|
||||||
Place the shader folder inside
|
Place the shader folder inside
|
||||||
the `Video Shaders` folder
|
the `shaders` folder
|
||||||
of your higan installation.
|
of your higan installation.
|
||||||
If you don't have a `Video Shaders` folder,
|
If you don't have a `shaders` folder,
|
||||||
create it beside the `*.sys` folders
|
create it beside the `systems` folder
|
||||||
like `Game Boy Advance.sys` and `Super Famicom.sys`.
|
and `settings.bml`.
|
||||||
|
|
||||||
- On Windows,
|
- On Windows,
|
||||||
this is probably the folder containing `higan.exe`
|
this is probably the folder containing `higan.exe`
|
||||||
@@ -80,10 +79,9 @@ like `Game Boy Advance.sys` and `Super Famicom.sys`.
|
|||||||
this is probably `~/.local/share/higan`
|
this is probably `~/.local/share/higan`
|
||||||
|
|
||||||
Launch higan,
|
Launch higan,
|
||||||
open the Settings menu,
|
open the [Settings menu],
|
||||||
and choose "Advanced ..."
|
and choose "Advanced ..." to open
|
||||||
to open [the Advanced tab](../interface/higan-settings.md#advanced)
|
the [Advanced tab] of the Settings window.
|
||||||
of the Settings window.
|
|
||||||
Under "Driver Selection",
|
Under "Driver Selection",
|
||||||
make sure "Video" is set to "OpenGL".
|
make sure "Video" is set to "OpenGL".
|
||||||
If it wasn't already set that way,
|
If it wasn't already set that way,
|
||||||
@@ -91,7 +89,7 @@ you'll need to restart higan
|
|||||||
for the change to take effect.
|
for the change to take effect.
|
||||||
|
|
||||||
Open the Settings menu again,
|
Open the Settings menu again,
|
||||||
choose the "Video Shader" sub-menu,
|
choose the "Shader" sub-menu,
|
||||||
and now the shaders you installed
|
and now the shaders you installed
|
||||||
should be listed at the bottom of the menu.
|
should be listed at the bottom of the menu.
|
||||||
|
|
||||||
@@ -213,3 +211,7 @@ The PC Engine does not support an interlaced mode,
|
|||||||
but its horizontal resolution is much more flexible
|
but its horizontal resolution is much more flexible
|
||||||
than the Super Famicom or Mega Drive,
|
than the Super Famicom or Mega Drive,
|
||||||
and so it has the same problems with shaders as those consoles.
|
and so it has the same problems with shaders as those consoles.
|
||||||
|
|
||||||
|
[Settings menu]: ../interface/higan.md#the-settings-menu
|
||||||
|
[video driver]: drivers.md#video
|
||||||
|
[Advanced tab]: ../interface/higan-settings.md#advanced
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
higan, the multi-system emulator
|
higan, the multi-system emulator
|
||||||
================================
|
================================
|
||||||
|
|
||||||
higan emulates a number of classic video-game consoles of the 1980s and 1990s,
|
higan emulates a number of 2D video-game consoles,
|
||||||
allowing you to play classic games on a modern general-purpose computer.
|
allowing you to play classic games on a modern general-purpose computer.
|
||||||
|
|
||||||
To get started with higan right away,
|
To get started with higan right away,
|
||||||
@@ -11,7 +11,7 @@ see the [Quick Start](qs.md) section of the documentation.
|
|||||||
About higan
|
About higan
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
As of v104,
|
As of v107,
|
||||||
higan has top-tier support for the following consoles:
|
higan has top-tier support for the following consoles:
|
||||||
|
|
||||||
- Nintendo Super Famicom/Super Nintendo Entertainment System,
|
- Nintendo Super Famicom/Super Nintendo Entertainment System,
|
||||||
@@ -33,6 +33,7 @@ It also includes some level of support for these consoles:
|
|||||||
- NEC SuperGrafx
|
- NEC SuperGrafx
|
||||||
- Bandai WonderSwan
|
- Bandai WonderSwan
|
||||||
- Bandai WonderSwan Color
|
- Bandai WonderSwan Color
|
||||||
|
- Pocket Challenge v2
|
||||||
|
|
||||||
**Note:** Some consoles were released under different names
|
**Note:** Some consoles were released under different names
|
||||||
in different geographic regions.
|
in different geographic regions.
|
||||||
@@ -63,17 +64,17 @@ by the time you read this,
|
|||||||
and it may contain errors or omissions.
|
and it may contain errors or omissions.
|
||||||
If you find something that's wrong,
|
If you find something that's wrong,
|
||||||
or you have a suggestion,
|
or you have a suggestion,
|
||||||
post a message on the official higan forum.
|
post a message on the unofficial forum.
|
||||||
|
|
||||||
Official higan resources
|
Official higan resources
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
- [Official homepage](https://byuu.org/emulation/higan/)
|
- [Official homepage](https://byuu.org/emulation/higan/)
|
||||||
- [Official forum](https://board.byuu.org/viewforum.php?f=4)
|
|
||||||
|
|
||||||
Unofficial higan resources
|
Unofficial higan resources
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
|
- [Unofficial forum](https://helmet.kafuka.org/bboard/)
|
||||||
- [Source code repository](https://gitlab.com/higan/higan/)
|
- [Source code repository](https://gitlab.com/higan/higan/)
|
||||||
archives official higan releases
|
archives official higan releases
|
||||||
and WIP snapshots
|
and WIP snapshots
|
||||||
@@ -84,12 +85,12 @@ Unofficial higan resources
|
|||||||
or smarter algorithms for scaling up to modern PC resolutions.
|
or smarter algorithms for scaling up to modern PC resolutions.
|
||||||
See [Using video shaders][shaders] below for details.
|
See [Using video shaders][shaders] below for details.
|
||||||
- [Mercurial Magic](https://github.com/hex-usr/Mercurial-Magic/)
|
- [Mercurial Magic](https://github.com/hex-usr/Mercurial-Magic/)
|
||||||
is a tool for converting MSU-1 games and mods into a format
|
is a tool for converting MSU1 games and mods into a format
|
||||||
higan can use.
|
higan can use.
|
||||||
See [Importing MSU-1 games][msu1] for details.
|
See [Importing MSU1 games][msu1] for details.
|
||||||
|
|
||||||
[shaders]: guides/shaders.md
|
[shaders]: guides/shaders.md
|
||||||
[msu1]: guides/import.md#msu-1-games
|
[msu1]: guides/import.md#msu1-games
|
||||||
|
|
||||||
There are also other projects
|
There are also other projects
|
||||||
based on current or older versions of higan,
|
based on current or older versions of higan,
|
||||||
@@ -112,7 +113,7 @@ that you might want to check out.
|
|||||||
is a fork of bsnes v094
|
is a fork of bsnes v094
|
||||||
adapted to work as a
|
adapted to work as a
|
||||||
[libretro](https://www.libretro.com/) emulation core.
|
[libretro](https://www.libretro.com/) emulation core.
|
||||||
- [nSide](https://github.com/hex-usr/nSide)
|
- [nSide](https://gitlab.com/hex-usr/nSide)
|
||||||
is a fork of higan that greatly enhances
|
is a fork of higan that greatly enhances
|
||||||
its NES emulation support,
|
its NES emulation support,
|
||||||
and adds minor features to the other cores too.
|
and adds minor features to the other cores too.
|
||||||
|
@@ -23,7 +23,7 @@ If you have a real GBA and a flash-cart,
|
|||||||
the Internet contains many tools
|
the Internet contains many tools
|
||||||
that will extract the BIOS image so it can be copied
|
that will extract the BIOS image so it can be copied
|
||||||
to your desktop computer.
|
to your desktop computer.
|
||||||
The correct GBA BIOS file is exactly 16384 bytes long,
|
The correct GBA BIOS file is exactly 16,384 bytes long,
|
||||||
and has the SHA256 hash
|
and has the SHA256 hash
|
||||||
`fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570`.
|
`fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570`.
|
||||||
|
|
||||||
|
@@ -12,7 +12,7 @@ using the Git source-code management tool,
|
|||||||
or by clicking the download button on the right-hand side of the web-page
|
or by clicking the download button on the right-hand side of the web-page
|
||||||
and choosing an archive format.
|
and choosing an archive format.
|
||||||
|
|
||||||
You will also need GCC 4.9 or higher,
|
You will also need GCC 7 or higher,
|
||||||
including the C and C++ compiler,
|
including the C and C++ compiler,
|
||||||
GNU Make,
|
GNU Make,
|
||||||
and development files
|
and development files
|
||||||
@@ -24,7 +24,7 @@ for the following libraries:
|
|||||||
- Mesa
|
- Mesa
|
||||||
- gtksourceview 2.x
|
- gtksourceview 2.x
|
||||||
- Cairo
|
- Cairo
|
||||||
- SDL 1.2
|
- SDL 2.0
|
||||||
- libXv
|
- libXv
|
||||||
- libAO
|
- libAO
|
||||||
- OpenAL
|
- OpenAL
|
||||||
@@ -35,7 +35,7 @@ On a Debian-derived Linux distribution
|
|||||||
you can install everything you need with a command like:
|
you can install everything you need with a command like:
|
||||||
|
|
||||||
sudo apt-get install build-essential libgtk2.0-dev libpulse-dev \
|
sudo apt-get install build-essential libgtk2.0-dev libpulse-dev \
|
||||||
mesa-common-dev libgtksourceview2.0-dev libcairo2-dev libsdl1.2-dev \
|
mesa-common-dev libgtksourceview2.0-dev libcairo2-dev libsdl2-dev \
|
||||||
libxv-dev libao-dev libopenal-dev libudev-dev
|
libxv-dev libao-dev libopenal-dev libudev-dev
|
||||||
|
|
||||||
Once you have all the dependencies installed,
|
Once you have all the dependencies installed,
|
||||||
@@ -53,9 +53,9 @@ being installed system-wide.
|
|||||||
3. Type `cd ~/higan-src`
|
3. Type `cd ~/higan-src`
|
||||||
(or wherever you put the higan source)
|
(or wherever you put the higan source)
|
||||||
and press Enter
|
and press Enter
|
||||||
4. Type `make -C higan compiler=g++` and press Enter
|
4. Type `make -C higan target=higan` and press Enter
|
||||||
to build the main higan executable
|
to build the main higan executable
|
||||||
5. Type `make -C icarus compiler=g++` and press Enter
|
5. Type `make -C icarus` and press Enter
|
||||||
to build the icarus import tool
|
to build the icarus import tool
|
||||||
|
|
||||||
Installing a compiled build on Linux
|
Installing a compiled build on Linux
|
||||||
@@ -68,7 +68,7 @@ as described in the previous section:
|
|||||||
2. Type `cd ~/higan-src`
|
2. Type `cd ~/higan-src`
|
||||||
(or wherever you put the higan source)
|
(or wherever you put the higan source)
|
||||||
and press Enter
|
and press Enter
|
||||||
3. Type `make -C higan install` and press Enter
|
3. Type `make -C higan target=higan install` and press Enter
|
||||||
to install higan and its supporting files
|
to install higan and its supporting files
|
||||||
4. Type `make -C icarus install` and press Enter
|
4. Type `make -C icarus install` and press Enter
|
||||||
to install icarus and its game database
|
to install icarus and its game database
|
||||||
@@ -123,7 +123,7 @@ as installed by the above instructions:
|
|||||||
2. Type `cd ~/higan-src`
|
2. Type `cd ~/higan-src`
|
||||||
(or wherever you put the higan source)
|
(or wherever you put the higan source)
|
||||||
and press Enter
|
and press Enter
|
||||||
3. Type `make -C higan uninstall` and press Enter
|
3. Type `make -C higan target=higan uninstall` and press Enter
|
||||||
4. Type `make -C icarus uninstall` and press Enter
|
4. Type `make -C icarus uninstall` and press Enter
|
||||||
|
|
||||||
To remove higan's configuration,
|
To remove higan's configuration,
|
||||||
|
@@ -50,38 +50,58 @@ using the Git source-code management tool,
|
|||||||
or by clicking the download button on the right-hand side of the web-page
|
or by clicking the download button on the right-hand side of the web-page
|
||||||
and choosing an archive format.
|
and choosing an archive format.
|
||||||
|
|
||||||
You will need a C++ compiler to compile higan.
|
You will need a C++ compiler that supports C++17 to compile higan.
|
||||||
We recommend installing [TDM64-GCC][tdm],
|
We recommend installing the latest version of [MinGW-W64].
|
||||||
preferably the latest version
|
|
||||||
but anything newer than 4.9 should be fine.
|
|
||||||
higan does not support building with clang++
|
higan does not support building with clang++
|
||||||
(Clang is still not quite there yet for Windows)
|
(Clang is still not quite there yet for Windows)
|
||||||
nor Microsoft Visual C++
|
nor Microsoft Visual C++
|
||||||
(last we checked, it didn't support all the C++ features higan uses).
|
(last we checked, it didn't support all the C++ features higan uses).
|
||||||
|
|
||||||
**Note:** Make sure you get TDM64-GCC,
|
[MinGW-W64]: https://mingw-w64.org/
|
||||||
not TDM-GCC.
|
|
||||||
When compiled in x86 (32-bit) mode,
|
MinGW-W64 is available in a number of variants,
|
||||||
|
and the installer should ask you which you want.
|
||||||
|
|
||||||
|
- **Version:**
|
||||||
|
Version 8.1.0 is known to work,
|
||||||
|
later versions may work too.
|
||||||
|
- **Architecture:**
|
||||||
|
You *must* choose "x86_64", not "i686".
|
||||||
|
When built with an i686 compiler,
|
||||||
higan may crash at startup
|
higan may crash at startup
|
||||||
because gcc targeting x86 does not support
|
because gcc targeting x86 does not support
|
||||||
Windows' structured exception handling (SEH).
|
Windows' structured exception handling (SEH).
|
||||||
Also,
|
Also,
|
||||||
historically in x86 mode
|
historically in x86 mode
|
||||||
gcc has miscompiled a part of the NES emulation core.
|
gcc has miscompiled a part of the NES emulation core.
|
||||||
See the higan forum
|
See the archive of the official forum
|
||||||
[for](https://board.byuu.org/viewtopic.php?p=41977#p41977)
|
[for](https://helmet.kafuka.org/byuubackup2/viewtopic.php@f=4&t=1636&start=20.html#p41977)
|
||||||
[details](https://board.byuu.org/viewtopic.php?p=42253#p42253).
|
[details](https://helmet.kafuka.org/byuubackup2/viewtopic.php@f=4&t=1636&start=30.html#p42253).
|
||||||
|
- **Threads:**
|
||||||
|
Both options should work,
|
||||||
|
but higan is developed with the "posix" model.
|
||||||
|
- **Exception:**
|
||||||
|
You *must* choose "seh",
|
||||||
|
or higan may crash at startup.
|
||||||
|
If "seh" is not an option,
|
||||||
|
make sure "Architecture" is set to "x86_64".
|
||||||
|
- **Build Revision:**
|
||||||
|
Choose the largest number, whatever it is.
|
||||||
|
|
||||||
Once you've installed the compiler,
|
When the compiler is installed,
|
||||||
open a command-prompt window,
|
it adds a "Run terminal" shortcut to the Start menu
|
||||||
|
which opens a command-prompt
|
||||||
|
with all the compiler tools available.
|
||||||
|
|
||||||
|
To verify that the compiler is installed correctly,
|
||||||
|
launch the "Run Terminal" shortcut,
|
||||||
type `g++ --version`
|
type `g++ --version`
|
||||||
then press Enter
|
then press Enter.
|
||||||
to check it's installed correctly.
|
|
||||||
You should see a message like
|
You should see a message like
|
||||||
|
|
||||||
```text
|
```text
|
||||||
g++ 1.2.3 20010101
|
g++ (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0
|
||||||
Copyright (C) 2001 Free Software Foundation, Inc.
|
Copyright (C) 2018 Free Software Foundation, Inc.
|
||||||
This is free software; see the source for copying conditions. There is NO
|
This is free software; see the source for copying conditions. There is NO
|
||||||
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
```
|
```
|
||||||
@@ -91,11 +111,11 @@ and the corresponding dates.
|
|||||||
If you see an error message like
|
If you see an error message like
|
||||||
"'g++' is not recognized as an internal or external command,
|
"'g++' is not recognized as an internal or external command,
|
||||||
operable program or batch file",
|
operable program or batch file",
|
||||||
you may need to add the compiler's "bin" folder
|
make sure you're using the "Run terminal" shortcut,
|
||||||
to your computer's `%PATH%`.
|
or otherwise have MinGW-W64's "bin" directory in your `%PATH%`.
|
||||||
See the compiler's documentation for help with that.
|
See the compiler's documentation for help with that.
|
||||||
|
|
||||||
Once mingw is installed and available from the command prompt:
|
Once the compiler is installed:
|
||||||
|
|
||||||
1. Put the higan source code in some convenient location,
|
1. Put the higan source code in some convenient location,
|
||||||
like `C:\higan-src`
|
like `C:\higan-src`
|
||||||
@@ -103,13 +123,11 @@ Once mingw is installed and available from the command prompt:
|
|||||||
3. Type `cd C:\higan-src`
|
3. Type `cd C:\higan-src`
|
||||||
(or wherever you put the higan source)
|
(or wherever you put the higan source)
|
||||||
and press Enter
|
and press Enter
|
||||||
4. Type `mingw32-make -C icarus compiler=g++` and press Enter
|
4. Type `mingw32-make -C icarus` and press Enter
|
||||||
to build the icarus import tool
|
to build the icarus import tool
|
||||||
5. Type `mingw32-make -C higan compiler=g++` and press Enter
|
5. Type `mingw32-make -C higan target=higan` and press Enter
|
||||||
to build the main higan executable
|
to build the main higan executable
|
||||||
|
|
||||||
[tdm]: http://tdm-gcc.tdragon.net/download
|
|
||||||
|
|
||||||
Installing a compiled build on Windows
|
Installing a compiled build on Windows
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
||||||
@@ -120,24 +138,20 @@ Installing a compiled build on Windows
|
|||||||
into the new folder
|
into the new folder
|
||||||
3. Copy `C:\higan-src\icarus\Database` and its contents
|
3. Copy `C:\higan-src\icarus\Database` and its contents
|
||||||
into the new folder
|
into the new folder
|
||||||
4. Copy `C:\higan-src\higan\out\higan.exe`
|
4. Copy `C:\higan-src\icarus\Firmware` and its contents
|
||||||
into the new folder
|
into the new folder
|
||||||
5. Copy all the `*.sys` folders
|
5. Copy `C:\higan-src\higan\out\higan.exe`
|
||||||
in `C:\higan-src\higan\systems`
|
|
||||||
into the new folder
|
into the new folder
|
||||||
6. If the higan source includes a `shaders` folder,
|
6. Copy `C:\higan-src\higan\systems`
|
||||||
make another new folder named `Video Shaders`
|
into the new folder
|
||||||
inside the new folder,
|
7. If the higan source includes a `shaders` folder,
|
||||||
and copy all the `*.shader` folders
|
copy it into the new folder too.
|
||||||
from `C:\higan-src\shaders\`
|
|
||||||
into the `Video Shaders` folder.
|
|
||||||
|
|
||||||
The new folder should now contain
|
The new folder should now contain
|
||||||
`icarus.exe`,
|
`icarus.exe`,
|
||||||
`higan.exe`,
|
`higan.exe`,
|
||||||
a folder named `Database`,
|
and folders named `Database`, `Firmware`, `systems`,
|
||||||
and half a dozen folders named after the systems higan emulates
|
and possibly `shaders`.
|
||||||
with `.sys` at the end.
|
|
||||||
This is what you would get by downloading an official build,
|
This is what you would get by downloading an official build,
|
||||||
as described under
|
as described under
|
||||||
[Installing an official release on Windows][instwin]
|
[Installing an official release on Windows][instwin]
|
||||||
|
1
docs/interface/down.png
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../hiro/resource/icon/go/down.png
|
@@ -57,7 +57,8 @@ higan will prompt for one.
|
|||||||
`SUBGAME2` is ignored.
|
`SUBGAME2` is ignored.
|
||||||
|
|
||||||
When `GAME` refers to
|
When `GAME` refers to
|
||||||
the [Super Game Boy](../guides/import.md#super-game-boy-games),
|
the [Super Game Boy](../guides/import.md#super-game-boy-games)
|
||||||
|
or Super Game Boy 2,
|
||||||
`SUBGAME1` should be
|
`SUBGAME1` should be
|
||||||
the path to a game folder or ROM file
|
the path to a game folder or ROM file
|
||||||
representing a Game Boy game to insert into the slot.
|
representing a Game Boy game to insert into the slot.
|
||||||
|
@@ -6,7 +6,79 @@ and contains less-frequently-modified settings.
|
|||||||
Most of these can be safely ignored,
|
Most of these can be safely ignored,
|
||||||
or set once and never changed again.
|
or set once and never changed again.
|
||||||
|
|
||||||
This window has a tab for each main category of options:
|
This window has a tab for each main category of options.
|
||||||
|
|
||||||
|
Systems
|
||||||
|
=======
|
||||||
|
|
||||||
|
This tab configures the contents of
|
||||||
|
[the Systems menu](higan.md#the-systems-menu),
|
||||||
|
so you can make it easier to load the games you care about
|
||||||
|
and hide things that get in the way.
|
||||||
|
|
||||||
|
Each item in the list represents
|
||||||
|
a single item in the Systems menu.
|
||||||
|
If the box at the left is ticked,
|
||||||
|
that item will be included in the menu,
|
||||||
|
otherwise it will be hidden—but higan will remember its configuration
|
||||||
|
in case you want to show it again.
|
||||||
|
|
||||||
|
At the bottom left are
|
||||||
|
 and  buttons.
|
||||||
|
These move the selected item
|
||||||
|
upward or downward in the list.
|
||||||
|
|
||||||
|
The **Append** button in the lower right
|
||||||
|
adds a new item to the end of the list.
|
||||||
|
It opens the [System Properties](#system-properties) dialog,
|
||||||
|
so you can enter the details of the new item.
|
||||||
|
If you don't want the new item to be at the end,
|
||||||
|
you can use the up and down buttons
|
||||||
|
in the lower left
|
||||||
|
to move it to its intended location.
|
||||||
|
|
||||||
|
The **Modify** button in the lower right
|
||||||
|
opens the [System Properties](#system-properties) dialog
|
||||||
|
for the selected item,
|
||||||
|
so you can make changes.
|
||||||
|
|
||||||
|
The **Remove** button in the lower right
|
||||||
|
removes the selected item from the list entirely.
|
||||||
|
Unlike hiding the item,
|
||||||
|
this forgets whatever configuration the item had.
|
||||||
|
|
||||||
|
System Properties
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
This dialog appears when clicking "Append" or "Modify"
|
||||||
|
in the [Systems](#systems) tab.
|
||||||
|
It allows you to configure a new ("Append") or existing ("Modify") entry
|
||||||
|
in the [Systems menu](higan.md#the-systems-menu).
|
||||||
|
|
||||||
|
- **System** controls which console will be emulated
|
||||||
|
when this menu-item is chosen.
|
||||||
|
- **Load** controls what game will be loaded
|
||||||
|
into the emulated console
|
||||||
|
when this menu-item is chosen.
|
||||||
|
- If left blank,
|
||||||
|
higan will open [a filesystem browser](common.md#the-filesystem-browser)
|
||||||
|
allowing you to pick a previously-imported game from
|
||||||
|
the [game library](../concepts/game-library.md).
|
||||||
|
- If you choose a particular game,
|
||||||
|
higan will immediately load it
|
||||||
|
when the menu-item is chosen.
|
||||||
|
If the game requires additional data
|
||||||
|
(for example, the Super Game Boy requires a Game Boy cartridge)
|
||||||
|
higan will prompt for it.
|
||||||
|
- **Alias** controls the name of this item,
|
||||||
|
as displayed in the Systems menu.
|
||||||
|
- **Append** (present in "Append" mode) closes the dialog
|
||||||
|
and adds a new item with this configuration
|
||||||
|
to the list.
|
||||||
|
- **Modify** (present in "Modify" mode) closes the dialog
|
||||||
|
and updates the configuration of
|
||||||
|
the item being modified.
|
||||||
|
- **Cancel** closes the dialog without making any changes.
|
||||||
|
|
||||||
Video
|
Video
|
||||||
=====
|
=====
|
||||||
@@ -20,9 +92,9 @@ settings adjust the colour and brightness
|
|||||||
of the emulated console's video output:
|
of the emulated console's video output:
|
||||||
|
|
||||||
- **Saturation** adjusts the vibrancy of colours displayed,
|
- **Saturation** adjusts the vibrancy of colours displayed,
|
||||||
where 0% makes things pure grey,
|
where 0% makes things black-and-white,
|
||||||
100% is normal,
|
100% is normal,
|
||||||
and 200% is garishly brightly coloured.
|
and 200% is garishly exaggerated colour.
|
||||||
- **Gamma** adjusts how bright mid-range colours are
|
- **Gamma** adjusts how bright mid-range colours are
|
||||||
compared to the brightest colours,
|
compared to the brightest colours,
|
||||||
where 100% is normal,
|
where 100% is normal,
|
||||||
@@ -30,20 +102,23 @@ of the emulated console's video output:
|
|||||||
This is in addition to
|
This is in addition to
|
||||||
any adjustment applied by
|
any adjustment applied by
|
||||||
the "Colors" option
|
the "Colors" option
|
||||||
in the "Video Emulation" sub-menu
|
in the "Emulation" sub-menu
|
||||||
of the [Settings menu](higan.md#the-settings-menu).
|
of the [Settings menu](higan.md#the-settings-menu).
|
||||||
- **Luminance** adjusts the overall brightness,
|
- **Luminance** adjusts the overall brightness,
|
||||||
where 100% is normal,
|
where 100% is normal,
|
||||||
and 0% is totally black.
|
and 0% is totally black.
|
||||||
|
|
||||||
**Overscan Mask**
|
**Overscan Area**
|
||||||
removes parts of
|
controls what parts of the video output are hidden
|
||||||
the video output that would have been hidden
|
when "Show Overscan Area" is disabled
|
||||||
by the bezel around the edge of
|
in the "Output" sub-menu of
|
||||||
a standard-definition television screen.
|
the [Settings menu](higan.md#the-settings-menu).
|
||||||
Some games (particularly on the Famicom)
|
On a standard-definition television,
|
||||||
displayed random glitchy output in this area,
|
the outermost edges of the emulated console's video output
|
||||||
which can be distracting.
|
would have been hidden by the bezel,
|
||||||
|
so some games (particularly on the Famicom)
|
||||||
|
allowed random glitchy output to appear there,
|
||||||
|
assuming it wouldn't be visible.
|
||||||
|
|
||||||
- **Horizontal**
|
- **Horizontal**
|
||||||
removes pixels from the left and right of the video output.
|
removes pixels from the left and right of the video output.
|
||||||
@@ -58,41 +133,7 @@ whether the Super Famicom is in
|
|||||||
lo-res (256px) or hi-res (512px)
|
lo-res (256px) or hi-res (512px)
|
||||||
mode.
|
mode.
|
||||||
|
|
||||||
**Windowed Mode**
|
**Fullscreen**
|
||||||
settings apply when higan is running
|
|
||||||
in a normal window.
|
|
||||||
|
|
||||||
- **Aspect Correction**
|
|
||||||
stretches the image to match the aspect ratio
|
|
||||||
produced by the original console hardware,
|
|
||||||
but can cause a "ripple" effect
|
|
||||||
during horizontal scrolling
|
|
||||||
due to rounding errors.
|
|
||||||
[Video shaders](../guides/shaders.md)
|
|
||||||
can reduce this effect.
|
|
||||||
- **Integral Scaling**
|
|
||||||
makes higan draw the emulated video output
|
|
||||||
at a whole-number multiple of the original size,
|
|
||||||
rather than completely filling the available space.
|
|
||||||
This means that every game pixel
|
|
||||||
uses the same number of computer pixels,
|
|
||||||
and avoids graphics looking chunky and uneven.
|
|
||||||
Note that Aspect Correction
|
|
||||||
is applied after integral scaling,
|
|
||||||
so some unevenness may be visible
|
|
||||||
even with this option enabled.
|
|
||||||
- **Adaptive Sizing**
|
|
||||||
automatically resizes the higan window
|
|
||||||
to fit snugly around the emulated video output
|
|
||||||
whenever it changes size
|
|
||||||
(because the user loaded a game for a different console,
|
|
||||||
chose a different option from
|
|
||||||
the [Video Scale sub-menu](higan.md#the-settings-menu),
|
|
||||||
toggled Aspect Correction, etc.)
|
|
||||||
When disabled,
|
|
||||||
higan generally respects manual resizing.
|
|
||||||
|
|
||||||
**Fullscreen Mode**
|
|
||||||
settings apply
|
settings apply
|
||||||
when higan is running fullscreen,
|
when higan is running fullscreen,
|
||||||
because it was started with the `--fullscreen`
|
because it was started with the `--fullscreen`
|
||||||
@@ -100,10 +141,6 @@ because it was started with the `--fullscreen`
|
|||||||
or because the user pressed
|
or because the user pressed
|
||||||
the Toggle Fullscreen [hotkey](higan-settings.md#hotkeys).
|
the Toggle Fullscreen [hotkey](higan-settings.md#hotkeys).
|
||||||
|
|
||||||
- **Aspect Correction**
|
|
||||||
behaves the same way as in Windowed mode above.
|
|
||||||
- **Integral Scaling**
|
|
||||||
behaves the same way as in Windowed mode above.
|
|
||||||
- **Exclusive Mode**
|
- **Exclusive Mode**
|
||||||
requests exclusive access
|
requests exclusive access
|
||||||
to the computer's video output
|
to the computer's video output
|
||||||
@@ -113,7 +150,7 @@ the Toggle Fullscreen [hotkey](higan-settings.md#hotkeys).
|
|||||||
from drawing anything,
|
from drawing anything,
|
||||||
and may also temporarily disable any kind of compositing,
|
and may also temporarily disable any kind of compositing,
|
||||||
reducing video latency.
|
reducing video latency.
|
||||||
As of v104,
|
As of v107,
|
||||||
only the Direct3D video driver is capable of exclusive mode;
|
only the Direct3D video driver is capable of exclusive mode;
|
||||||
with other drivers this option does nothing.
|
with other drivers this option does nothing.
|
||||||
|
|
||||||
@@ -164,52 +201,52 @@ before it is sent to your computer's speakers.
|
|||||||
where 0% means only the left speaker produces sound,
|
where 0% means only the left speaker produces sound,
|
||||||
50% means both speakers produce sound equally,
|
50% means both speakers produce sound equally,
|
||||||
and 100% means only the right speaker produces sound.
|
and 100% means only the right speaker produces sound.
|
||||||
- **Reverb** adds a slight reverberation effect
|
|
||||||
to the emulated console's audio output,
|
|
||||||
as though you were playing the game in a tunnel or small room.
|
|
||||||
|
|
||||||
Input
|
Input
|
||||||
=====
|
=====
|
||||||
|
|
||||||
This tab controls which PC inputs
|
This tab controls
|
||||||
are mapped to which emulated controllers.
|
how higan handles input for the emulated consoles.
|
||||||
|
|
||||||
|
**When focus is lost**
|
||||||
|
controls what happens when a game is loaded,
|
||||||
|
but higan is not the current foreground window.
|
||||||
|
|
||||||
|
- **Pause Emulation** automatically pauses emulation.
|
||||||
|
- **Block Input** allows emulation to keep running,
|
||||||
|
but higan will ignore all configured button presses.
|
||||||
|
If you're using the keyboard to emulate a controller,
|
||||||
|
this prevents typing in other applications
|
||||||
|
from messing with higan,
|
||||||
|
but music will keep playing.
|
||||||
|
- **Allow Input** allows emulation to continue as normal.
|
||||||
|
This allows somebody to play higan with a controller
|
||||||
|
in one window,
|
||||||
|
while somebody else types into another application
|
||||||
|
in another window.
|
||||||
|
|
||||||
|
The rest of this tab configures
|
||||||
|
the mapping from PC inputs to emulated controllers.
|
||||||
The exact PC inputs that can be mapped
|
The exact PC inputs that can be mapped
|
||||||
depend on [the input driver](../guides/drivers.md#input).
|
depend on [the input driver](../guides/drivers.md#input).
|
||||||
|
|
||||||
General input settings:
|
To choose which of the possible controllers to configure:
|
||||||
|
|
||||||
- **Pause Emulation** automatically pauses emulation
|
- The first drop-down list controls
|
||||||
when the main higan window
|
which console's ports appear in the second list.
|
||||||
is not the current foreground window.
|
- The second drop-down list controls
|
||||||
- **Allow Input** can be ticked
|
which port's compatible controllers appear in the third list.
|
||||||
when "Pause Emulation" is *not* ticked,
|
- The third drop-down list controls
|
||||||
and allows configured inputs to keep affecting higan
|
which controller's inputs are shown
|
||||||
even when higan is running in the background.
|
in the mapping list below.
|
||||||
This is particularly relevant if
|
|
||||||
you configure your PC keyboard to control higan:
|
|
||||||
if you tick this box,
|
|
||||||
and switch to a different application
|
|
||||||
leaving higan running in the background,
|
|
||||||
typing in that other application may affect
|
|
||||||
the emulated game running in higan
|
|
||||||
even though you can't see it!
|
|
||||||
|
|
||||||
Choosing which of the possible controllers to configure:
|
|
||||||
|
|
||||||
- The console selector chooses which console's inputs
|
|
||||||
to display in the mapping list below.
|
|
||||||
- The port selector chooses which port of the selected console
|
|
||||||
to display in the mapping list below.
|
|
||||||
- The controller selector chooses which controller
|
|
||||||
associated with the given console and port
|
|
||||||
to display in the mapping list below.
|
|
||||||
Note that some consoles only allow particular controllers
|
Note that some consoles only allow particular controllers
|
||||||
to be used in a particular port.
|
to be used in a particular port.
|
||||||
For example,
|
For example,
|
||||||
the Super Scope controller for the Super Famicom
|
the Super Scope controller for the Super Famicom
|
||||||
only works in Controller Port 2.
|
only works in Controller Port 2.
|
||||||
|
|
||||||
Configuring the selected controller:
|
To configure the selected controller:
|
||||||
|
|
||||||
- The mapping list includes
|
- The mapping list includes
|
||||||
every button and axis on the selected controller,
|
every button and axis on the selected controller,
|
||||||
@@ -260,6 +297,13 @@ then click one of the
|
|||||||
or "Mouse Y-axis"
|
or "Mouse Y-axis"
|
||||||
buttons in the bottom-left of the window.
|
buttons in the bottom-left of the window.
|
||||||
|
|
||||||
|
**Note:**
|
||||||
|
To use an controller axis mapped to a mouse axis,
|
||||||
|
higan will need to be in fullscreen mode,
|
||||||
|
or you'll need to press
|
||||||
|
the key mapped to "Toggle Mouse Capture"
|
||||||
|
on the [Hotkeys tab](#hotkeys).
|
||||||
|
|
||||||
If you start mapping a button or axis,
|
If you start mapping a button or axis,
|
||||||
but decide you don't want to,
|
but decide you don't want to,
|
||||||
you can press Escape
|
you can press Escape
|
||||||
@@ -267,10 +311,25 @@ to exit the "Press a key or button to map..." mode
|
|||||||
without actually mapping anything.
|
without actually mapping anything.
|
||||||
|
|
||||||
**Note:**
|
**Note:**
|
||||||
Consoles in the Game Boy family include
|
The Game Boy and Game Boy Color consoles
|
||||||
a Rumble "input" which is really more of an output.
|
have a "Cartridge" port with controllers
|
||||||
|
that are not really controllers:
|
||||||
|
|
||||||
|
- The "MBC5" controller is automatically used for
|
||||||
|
games whose cartridge includes the MBC5 memory-mapper
|
||||||
|
and a rumble motor,
|
||||||
|
like *Pokémon Pinball*.
|
||||||
See [Rumble Compatibility for Game Boy (Color)][gbcrumble]
|
See [Rumble Compatibility for Game Boy (Color)][gbcrumble]
|
||||||
and [Rumble Compatibility for Game Boy Advance][gbarumble]
|
for details.
|
||||||
|
- The "MBC7" controller is automatically used for
|
||||||
|
games whose cartridge includes the MBC7 memory-mapper
|
||||||
|
and an accelerometer,
|
||||||
|
like *Kirby Tilt 'n' Tumble*.
|
||||||
|
|
||||||
|
**Note:**
|
||||||
|
The Game Boy Advance console includes
|
||||||
|
a Rumble "input" which is really more of an output.
|
||||||
|
See [Rumble Compatibility for Game Boy Advance][gbarumble]
|
||||||
for details.
|
for details.
|
||||||
|
|
||||||
[gbcrumble]: ../notes.md#rumble-compatibility-for-game-boy-color
|
[gbcrumble]: ../notes.md#rumble-compatibility-for-game-boy-color
|
||||||
@@ -288,12 +347,12 @@ Hotkeys
|
|||||||
|
|
||||||
This tab is like "Inputs" above,
|
This tab is like "Inputs" above,
|
||||||
except it contains controls for higan itself
|
except it contains controls for higan itself
|
||||||
instead of the emulated console.
|
instead of for the emulated console.
|
||||||
|
|
||||||
- **Toggle Fullscreen** puts higan into fullscreen mode,
|
- **Toggle Fullscreen** puts higan into fullscreen mode,
|
||||||
where the menu and status bar are hidden,
|
where the menu and status bar are hidden,
|
||||||
and the emulated console's video output
|
and the emulated console's video output
|
||||||
is enlarged to cover the entire screen.
|
can cover the entire screen.
|
||||||
Toggling fullscreen also automatically captures the mouse.
|
Toggling fullscreen also automatically captures the mouse.
|
||||||
- **Toggle Mouse Capture** hides the usual mouse-cursor,
|
- **Toggle Mouse Capture** hides the usual mouse-cursor,
|
||||||
and captures the mouse so it cannot leave the higan window.
|
and captures the mouse so it cannot leave the higan window.
|
||||||
@@ -308,13 +367,22 @@ instead of the emulated console.
|
|||||||
- **Increment Quick State** selects the next [Quick State][qstates] slot.
|
- **Increment Quick State** selects the next [Quick State][qstates] slot.
|
||||||
The status bar will briefly display the new current slot number.
|
The status bar will briefly display the new current slot number.
|
||||||
- **Pause Emulation** pauses the emulated console
|
- **Pause Emulation** pauses the emulated console
|
||||||
until the Pause Emulation hotkey is pressed a second time.
|
until the Pause Emulation hotkey is pressed a second time,
|
||||||
|
or "Pause Emulation" is chosen from
|
||||||
|
[the Tools menu](higan.md#the-tools-menu)..
|
||||||
- **Fast Forward** disables audio and video synchronisation
|
- **Fast Forward** disables audio and video synchronisation
|
||||||
for as long as it's held down,
|
for as long as it's held down,
|
||||||
so emulation proceeds as quickly as possible.
|
so emulation proceeds as quickly as possible.
|
||||||
If your PC struggles to hit "real time"
|
If your PC struggles to hit "real time"
|
||||||
(60fps for most emulated consoles),
|
(60fps for most emulated consoles),
|
||||||
this likely won't have any effect.
|
this likely won't have any effect.
|
||||||
|
- **Soft Reset** restarts the emulated console's CPU
|
||||||
|
while leaving the console's memory untouched,
|
||||||
|
just like the "Soft Reset" menu item
|
||||||
|
in [the console menu](higan.md#the-console-menu).
|
||||||
|
This hotkey does nothing
|
||||||
|
when the "Soft Reset" item
|
||||||
|
does not appear in the console menu.
|
||||||
- **Power Cycle** turns the emulated console off and back on
|
- **Power Cycle** turns the emulated console off and back on
|
||||||
(a "hard reset"),
|
(a "hard reset"),
|
||||||
just like the "Power Cycle" menu item
|
just like the "Power Cycle" menu item
|
||||||
@@ -359,8 +427,7 @@ for help choosing which drivers you should use.
|
|||||||
configures how higan interacts
|
configures how higan interacts
|
||||||
with the [Game Library](../concepts/game-library.md).
|
with the [Game Library](../concepts/game-library.md).
|
||||||
|
|
||||||
- **Location** selects where higan
|
- **Location** tells higan where to look for games to load.
|
||||||
looks for games to load.
|
|
||||||
See [Moving the Game Library](../concepts/game-library.md#moving-the-game-library)
|
See [Moving the Game Library](../concepts/game-library.md#moving-the-game-library)
|
||||||
for more information.
|
for more information.
|
||||||
- **Ignore Manifests** makes higan ignore
|
- **Ignore Manifests** makes higan ignore
|
||||||
@@ -371,3 +438,16 @@ with the [Game Library](../concepts/game-library.md).
|
|||||||
to guess a manifest on the fly.
|
to guess a manifest on the fly.
|
||||||
See [Ignoring manifests](../concepts/manifests.md#ignoring-manifests)
|
See [Ignoring manifests](../concepts/manifests.md#ignoring-manifests)
|
||||||
for details.
|
for details.
|
||||||
|
|
||||||
|
**Other**
|
||||||
|
|
||||||
|
- **Auto-Save Memory Periodically** makes higan write
|
||||||
|
[in-game saves](../concepts/save-states.md#save-states-versus-in-game-saves)
|
||||||
|
to disk during gameplay,
|
||||||
|
instead of only when higan exits.
|
||||||
|
This may cause stuttering,
|
||||||
|
but means that you haven't lost everything
|
||||||
|
if higan crashes,
|
||||||
|
or your computer loses power.
|
||||||
|
- Note that this does not include
|
||||||
|
[game notes](higan-tools.md#game-notes)
|
||||||
|
@@ -3,10 +3,10 @@ appears when you choose
|
|||||||
one of the items at the bottom of
|
one of the items at the bottom of
|
||||||
[the Tools menu](higan.md#the-tools-menu).
|
[the Tools menu](higan.md#the-tools-menu).
|
||||||
|
|
||||||
The window has a tab for each tool:
|
The window has a tab for each tool.
|
||||||
|
|
||||||
The Cheat Editor
|
Cheat Editor
|
||||||
----------------
|
============
|
||||||
|
|
||||||
For some consoles,
|
For some consoles,
|
||||||
higan supports applying temporary changes to the code of a running game.
|
higan supports applying temporary changes to the code of a running game.
|
||||||
@@ -89,8 +89,8 @@ in Super Mario World,
|
|||||||
you can lock the time to 999 with these codes:
|
you can lock the time to 999 with these codes:
|
||||||
`7e0f31=09+7e0f32=09+7e0f33=09`.
|
`7e0f31=09+7e0f32=09+7e0f33=09`.
|
||||||
|
|
||||||
The State Manager
|
State Manager
|
||||||
-----------------
|
=============
|
||||||
|
|
||||||
The State Manager allows you to create,
|
The State Manager allows you to create,
|
||||||
load,
|
load,
|
||||||
@@ -125,8 +125,8 @@ and click "Erase" in the bottom-right corner.
|
|||||||
To clear all the slots at once,
|
To clear all the slots at once,
|
||||||
click "Reset" in the bottom-right corner.
|
click "Reset" in the bottom-right corner.
|
||||||
|
|
||||||
The Manifest Viewer
|
Manifest Viewer
|
||||||
-------------------
|
===============
|
||||||
|
|
||||||
As described in
|
As described in
|
||||||
[Game Manifests](../concepts/manifests.md),
|
[Game Manifests](../concepts/manifests.md),
|
||||||
@@ -135,3 +135,14 @@ describe how the various parts of a game cartridge
|
|||||||
are wired up together.
|
are wired up together.
|
||||||
The Manifest Viewer lets you examine
|
The Manifest Viewer lets you examine
|
||||||
the configuration higan is using for the loaded game.
|
the configuration higan is using for the loaded game.
|
||||||
|
|
||||||
|
Game Notes
|
||||||
|
==========
|
||||||
|
|
||||||
|
The Game Notes tab
|
||||||
|
is a place where you can write whatever you want
|
||||||
|
about the running game.
|
||||||
|
This information is automatically stored inside
|
||||||
|
the [game folder](../concepts/game-folders.md)
|
||||||
|
and loaded back into this tab
|
||||||
|
every time the game is loaded.
|
||||||
|
@@ -5,28 +5,32 @@ a status-bar across the bottom,
|
|||||||
and a large area in the middle that shows
|
and a large area in the middle that shows
|
||||||
the running game's video output.
|
the running game's video output.
|
||||||
|
|
||||||
The Library menu
|
The Systems menu
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
Manufacturer sub-menus
|
This menu lists the systems higan emulates.
|
||||||
allow you to play
|
Choosing any system from this menu allows you to play
|
||||||
games you've already imported
|
games for that system that you've already imported
|
||||||
into higan's
|
into higan's [game library](../concepts/game-library.md).
|
||||||
[game library](../concepts/game-library.md).
|
|
||||||
See [Importing and playing games](../guides/import.md).
|
See [Importing and playing games](../guides/import.md).
|
||||||
|
|
||||||
|
You can customise this menu
|
||||||
|
in [higan's Systems settings](higan-settings.md#systems)
|
||||||
|
to hide systems you don't care about,
|
||||||
|
or add a specific cartridge for any supported system.
|
||||||
|
This makes it more convenient
|
||||||
|
to play games that involve mini-cartridges:
|
||||||
|
for example, you can
|
||||||
|
add the Sufami Turbo to the list
|
||||||
|
and load *SD Ultra Battle*
|
||||||
|
in two clicks instead of three.
|
||||||
|
|
||||||
**Load ROM File ...**
|
**Load ROM File ...**
|
||||||
opens a [filesystem browser](common.md#the-filesystem-browser)
|
opens a [filesystem browser](common.md#the-filesystem-browser)
|
||||||
allowing you to choose a single ROM file.
|
allowing you to choose a single ROM file.
|
||||||
It will be imported and immediately start playing.
|
It will be imported and immediately start playing.
|
||||||
See [Importing and playing games](../guides/import.md).
|
See [Importing and playing games](../guides/import.md).
|
||||||
|
|
||||||
**Import ROM Files ...**
|
|
||||||
launches the icarus importing tool,
|
|
||||||
allowing you to bulk-import many ROM files at once.
|
|
||||||
See [the icarus documentation](icarus.md).
|
|
||||||
|
|
||||||
|
|
||||||
The console menu
|
The console menu
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
@@ -35,8 +39,7 @@ The console menu does not appear
|
|||||||
until a game is loaded.
|
until a game is loaded.
|
||||||
Also,
|
Also,
|
||||||
it's not named "console",
|
it's not named "console",
|
||||||
it's named for the kind of console
|
it's named for the console that runs the loaded game.
|
||||||
the loaded game runs on.
|
|
||||||
For example,
|
For example,
|
||||||
when playing a Game Boy game,
|
when playing a Game Boy game,
|
||||||
you will have a "Game Boy" menu.
|
you will have a "Game Boy" menu.
|
||||||
@@ -46,7 +49,6 @@ to the particular console being emulated.
|
|||||||
All consoles will have some of the following items,
|
All consoles will have some of the following items,
|
||||||
but few consoles have all of them.
|
but few consoles have all of them.
|
||||||
|
|
||||||
|
|
||||||
**Controller Port 1**
|
**Controller Port 1**
|
||||||
allows you
|
allows you
|
||||||
to connect different emulated controllers
|
to connect different emulated controllers
|
||||||
@@ -77,6 +79,20 @@ even though the Famicom did not support alternate controllers,
|
|||||||
because the Famicom emulation core also emulates the NES,
|
because the Famicom emulation core also emulates the NES,
|
||||||
which did.
|
which did.
|
||||||
|
|
||||||
|
**Controller**
|
||||||
|
is like "Controller Port 1"
|
||||||
|
for consoles that only have one controller port.
|
||||||
|
|
||||||
|
**Hardware**
|
||||||
|
appears for consoles with buttons on the main unit,
|
||||||
|
like the Game Boy,
|
||||||
|
or Master System.
|
||||||
|
It only allows the built-in controls to be used.
|
||||||
|
|
||||||
|
**Cartridge**
|
||||||
|
appears for the Game Boy and Game Boy Colour.
|
||||||
|
The options inside it do nothing.
|
||||||
|
|
||||||
**Expansion Port**
|
**Expansion Port**
|
||||||
allows you
|
allows you
|
||||||
to connect different emulated devices
|
to connect different emulated devices
|
||||||
@@ -91,6 +107,30 @@ This option allows the same program
|
|||||||
to control the emulated SNES,
|
to control the emulated SNES,
|
||||||
for development or testing.
|
for development or testing.
|
||||||
|
|
||||||
|
**Extension Port**
|
||||||
|
is the name the Sega Mega Drive used for its expansion port.
|
||||||
|
|
||||||
|
**Soft Reset**
|
||||||
|
restarts the emulated console's CPU
|
||||||
|
while leaving the console's memory untouched,
|
||||||
|
like pressing the "reset" button
|
||||||
|
on a physical console.
|
||||||
|
|
||||||
|
This menu item does not appear
|
||||||
|
for consoles that did not have a "reset" button,
|
||||||
|
like hand-helds.
|
||||||
|
|
||||||
|
It also does not appear for the Sega Master System,
|
||||||
|
since that console's reset button is wired up like a controller
|
||||||
|
rather than directly attached to the CPU.
|
||||||
|
To reset the Master System,
|
||||||
|
bind a keyboard or joypad button
|
||||||
|
to the "Reset" function
|
||||||
|
on the "Controls" controller
|
||||||
|
in the "Hardware" port
|
||||||
|
of the Sega Master System
|
||||||
|
in [higan's Input settings](higan-settings.md#input).
|
||||||
|
|
||||||
**Power Cycle**
|
**Power Cycle**
|
||||||
restarts the loaded game
|
restarts the loaded game
|
||||||
as though the emulated console were switched off and on again.
|
as though the emulated console were switched off and on again.
|
||||||
@@ -99,7 +139,7 @@ as though the emulated console were switched off and on again.
|
|||||||
stops the current game,
|
stops the current game,
|
||||||
as though the emulated console were switched off.
|
as though the emulated console were switched off.
|
||||||
You can load a new game
|
You can load a new game
|
||||||
from [the Library menu](#the-library-menu).
|
from [the Systems menu](#the-systems-menu).
|
||||||
|
|
||||||
[21fx]: https://github.com/defparam/21FX
|
[21fx]: https://github.com/defparam/21FX
|
||||||
|
|
||||||
@@ -109,13 +149,83 @@ The Settings menu
|
|||||||
The Settings menu allows you to configure things
|
The Settings menu allows you to configure things
|
||||||
that aren't specific to any particular console.
|
that aren't specific to any particular console.
|
||||||
|
|
||||||
|
**Size**
|
||||||
**Video Scale** determines the size
|
determines the size
|
||||||
of the emulated console's video output
|
of the emulated console's video output
|
||||||
when higan is running in windowed mode
|
when higan is running in windowed mode
|
||||||
(as opposed to fullscreen).
|
(as opposed to fullscreen).
|
||||||
|
The menu-items that indicate particular sizes
|
||||||
|
are only approximate, since
|
||||||
|
aspect correction can be applied,
|
||||||
|
different consoles have different native image sizes,
|
||||||
|
and some consoles can change the size of their output image dynamically.
|
||||||
|
|
||||||
**Video Emulation** applies various effects
|
- **1x (240p)**
|
||||||
|
resizes the higan window
|
||||||
|
so that each pixel of the emulated console's video output
|
||||||
|
is drawn as a single pixel on the computer screen.
|
||||||
|
- **2x (480p)**
|
||||||
|
resizes the higan window
|
||||||
|
so that each pixel of the emulated console's video output
|
||||||
|
is drawn as a 2×2 block of pixels on the computer screen.
|
||||||
|
- **3x (720p)**
|
||||||
|
resizes the higan window
|
||||||
|
so that each pixel of the emulated console's video output
|
||||||
|
is drawn as a 3×3 block of pixels on the computer screen.
|
||||||
|
- **Shrink Window To Size**
|
||||||
|
resizes the higan window to fit the emulated console's video output
|
||||||
|
at its current scale,
|
||||||
|
so there's no black padding between the image and the window border
|
||||||
|
(some padding may remain
|
||||||
|
if "Show Overscan Area" is enabled
|
||||||
|
in the Output menu).
|
||||||
|
- **Center Window**
|
||||||
|
moves the higan window to the centre of the computer screen.
|
||||||
|
|
||||||
|
**Output**
|
||||||
|
controls how higan draws the emulated console's video output
|
||||||
|
into the space available,
|
||||||
|
in both windowed and fullscreen modes.
|
||||||
|
|
||||||
|
- **Center**
|
||||||
|
draws the emulated video
|
||||||
|
at the largest integer multiple of the native size that will fit,
|
||||||
|
centered in the space available.
|
||||||
|
This gives the most crisp output,
|
||||||
|
but often has black borders.
|
||||||
|
- **Scale**
|
||||||
|
draws the emulated video
|
||||||
|
at the largest size that will fit,
|
||||||
|
and which preserves the image's aspect ratio.
|
||||||
|
This strikes a balance between
|
||||||
|
displaying the video output as it was intended,
|
||||||
|
and eliminating black borders.
|
||||||
|
- **Stretch**
|
||||||
|
draws the emulated video
|
||||||
|
to cover the entire available output area,
|
||||||
|
even if that distorts the image.
|
||||||
|
This completely eliminates black borders,
|
||||||
|
but can look very weird.
|
||||||
|
- **Adaptive Sizing**
|
||||||
|
allows higan to resize its window
|
||||||
|
when the emulated console changes the resolution
|
||||||
|
of its video output.
|
||||||
|
This can avoid black borders,
|
||||||
|
but the window resizing itself might be even more distracting.
|
||||||
|
- **Aspect Correction**
|
||||||
|
horizontally stretches the emulated video output
|
||||||
|
to match the aspect ratio produced by the original console.
|
||||||
|
It can make the output look more "lumpy",
|
||||||
|
but is a more accurate representation
|
||||||
|
of the original console's output.
|
||||||
|
- **Show Overscan Area**
|
||||||
|
controls whether the area defined by
|
||||||
|
the "Overscan Area" sliders in
|
||||||
|
the [Video settings](higan-settings.md#video)
|
||||||
|
is clipped from the emulated video output
|
||||||
|
or shown.
|
||||||
|
|
||||||
|
**Emulation** applies various effects
|
||||||
to the emulated console's video output
|
to the emulated console's video output
|
||||||
to reproduce some behaviours
|
to reproduce some behaviours
|
||||||
that aren't technically part of the console itself:
|
that aren't technically part of the console itself:
|
||||||
@@ -141,7 +251,7 @@ that aren't technically part of the console itself:
|
|||||||
the dim, washed out colours of the original Game Boy Advance,
|
the dim, washed out colours of the original Game Boy Advance,
|
||||||
and the pea-green display of the original Game Boy.
|
and the pea-green display of the original Game Boy.
|
||||||
|
|
||||||
**Video Shader** controls
|
**Shader** controls
|
||||||
how the low-resolution video output of the emulated console
|
how the low-resolution video output of the emulated console
|
||||||
is scaled up to suit modern high-resolution displays.
|
is scaled up to suit modern high-resolution displays.
|
||||||
[Using video shaders](../guides/shaders.md)
|
[Using video shaders](../guides/shaders.md)
|
||||||
@@ -166,6 +276,9 @@ at the bottom of the window.
|
|||||||
This option has no effect in fullscreen mode.
|
This option has no effect in fullscreen mode.
|
||||||
See [The status bar](#the-status-bar) for more information.
|
See [The status bar](#the-status-bar) for more information.
|
||||||
|
|
||||||
|
**Systems ...**
|
||||||
|
opens [higan's Systems settings](higan-settings.md#systems).
|
||||||
|
|
||||||
**Video ...**
|
**Video ...**
|
||||||
opens [higan's Video settings](higan-settings.md#video).
|
opens [higan's Video settings](higan-settings.md#video).
|
||||||
|
|
||||||
@@ -199,16 +312,26 @@ restores the emulated console to
|
|||||||
a state previously saved to one of the quick state slots.
|
a state previously saved to one of the quick state slots.
|
||||||
See [Save States](../concepts/save-states.md) for more information.
|
See [Save States](../concepts/save-states.md) for more information.
|
||||||
|
|
||||||
**Cheat Editor**
|
**Pause Emulation**
|
||||||
opens [the Cheat Editor tab](higan-tools.md#the-cheat-editor)
|
pauses the emulated console
|
||||||
|
until this menu-item is selected again.
|
||||||
|
This can also be triggered by
|
||||||
|
the [pause hotkey](higan-settings.md#hotkeys).
|
||||||
|
|
||||||
|
**Cheat Editor ...**
|
||||||
|
opens the [Cheat Editor tab](higan-tools.md#cheat-editor)
|
||||||
of the Tools window.
|
of the Tools window.
|
||||||
|
|
||||||
**State Manager**
|
**State Manager ...**
|
||||||
opens [the State Manager tab](higan-tools.md#the-state-manager)
|
opens the [State Manager tab](higan-tools.md#state-manager)
|
||||||
of the Tools window.
|
of the Tools window.
|
||||||
|
|
||||||
**Manifest Viewer**
|
**Manifest Viewer ...**
|
||||||
opens [the Manifest Viewer tab](higan-tools.md#the-manifest-viewer)
|
opens the [Manifest Viewer tab](higan-tools.md#manifest-viewer)
|
||||||
|
of the Tools window.
|
||||||
|
|
||||||
|
**Game Notes ...**
|
||||||
|
opens [the Game Notes tab](higan-tools.md#game-notes)
|
||||||
of the Tools window.
|
of the Tools window.
|
||||||
|
|
||||||
The Help menu
|
The Help menu
|
||||||
@@ -237,7 +360,7 @@ at the bottom of the main higan window,
|
|||||||
while "Show Status Bar" is ticked in [the Settings menu](#the-settings-menu).
|
while "Show Status Bar" is ticked in [the Settings menu](#the-settings-menu).
|
||||||
|
|
||||||
Before any game is loaded,
|
Before any game is loaded,
|
||||||
the status bar displays "No cartridge loaded".
|
the status bar displays "Unloaded".
|
||||||
|
|
||||||
When a game is loaded and running,
|
When a game is loaded and running,
|
||||||
the status bar displays the current emulation speed
|
the status bar displays the current emulation speed
|
||||||
@@ -255,11 +378,13 @@ or you may have pressed the "turbo" [hotkey](higan-settings.md#hotkeys).
|
|||||||
|
|
||||||
The status bar displays "Paused"
|
The status bar displays "Paused"
|
||||||
if you have pressed the "pause" [hotkey](higan-settings.md#hotkeys),
|
if you have pressed the "pause" [hotkey](higan-settings.md#hotkeys),
|
||||||
|
selected "Pause Emulation" from [the Tools menu](#the-tools-menu),
|
||||||
or if "When focus is lost: Pause Emulation" is ticked
|
or if "When focus is lost: Pause Emulation" is ticked
|
||||||
in [higan's Input settings](higan-settings.md#input)
|
in [higan's Input settings](higan-settings.md#input)
|
||||||
and the main higan window is not the foreground window.
|
and the main higan window is not the foreground window.
|
||||||
To resume emulation,
|
To resume emulation,
|
||||||
make sure the main higan window is in the foreground,
|
make sure the main higan window is in the foreground,
|
||||||
|
select "Pause Emulation" from the Tools menu again,
|
||||||
and/or press the "pause" hotkey.
|
and/or press the "pause" hotkey.
|
||||||
|
|
||||||
The status bar briefly displays "Selected quick state slot X"
|
The status bar briefly displays "Selected quick state slot X"
|
||||||
@@ -279,7 +404,7 @@ sub-menu that has not had a save-state saved to it,
|
|||||||
or when you press the "Load Quick State" hotkey
|
or when you press the "Load Quick State" hotkey
|
||||||
while the current Quick State slot has not had a save-state saved to it,
|
while the current Quick State slot has not had a save-state saved to it,
|
||||||
|
|
||||||
The status bar briefly displays "Power cycled"
|
The status bar briefly displays "System has been power cycled"
|
||||||
when you choose "Power Cycle" from [the console menu](#the-console-menu),
|
when you choose "Power Cycle" from [the console menu](#the-console-menu),
|
||||||
or press the "Power Cycle" hotkey.
|
or press the "Power Cycle" hotkey.
|
||||||
|
|
||||||
|
@@ -1,10 +1,7 @@
|
|||||||
When launching icarus,
|
icarus is a separate tool
|
||||||
directly or by picking "Import ROM Files ..."
|
bundled with higan
|
||||||
from higan's [Library menu](higan.md#the-library-menu),
|
that allows you to bulk-import ROM files
|
||||||
the main icarus window appears.
|
into higan's [game library].
|
||||||
This window allows you to bulk-import ROM files
|
|
||||||
into [higan's game library][gamelib],
|
|
||||||
and also to access icarus' settings.
|
|
||||||
|
|
||||||
Bulk importing ROM files
|
Bulk importing ROM files
|
||||||
------------------------
|
------------------------
|
||||||
@@ -18,7 +15,7 @@ with customisations:
|
|||||||
consoles higan emulates,
|
consoles higan emulates,
|
||||||
plus `.zip` files since ROM dumps are often compressed.
|
plus `.zip` files since ROM dumps are often compressed.
|
||||||
- Each matching file has a check-box next to it.
|
- Each matching file has a check-box next to it.
|
||||||
- You can tick the check-box next to every file at once
|
- You can tick the check-box next to every listed file at once
|
||||||
by pressing "Select All" in the bottom-left.
|
by pressing "Select All" in the bottom-left.
|
||||||
- You can un-tick all the check-boxes
|
- You can un-tick all the check-boxes
|
||||||
by pressing "Unselect All" in the bottom-left.
|
by pressing "Unselect All" in the bottom-left.
|
||||||
@@ -27,7 +24,7 @@ Pressing "Import ..." in the bottom-right
|
|||||||
will close the filesystem browser
|
will close the filesystem browser
|
||||||
then try to import all the files
|
then try to import all the files
|
||||||
whose check-boxes are ticked
|
whose check-boxes are ticked
|
||||||
into [the Game Library][gamelib].
|
into the [game library].
|
||||||
icarus displays a progress dialog during the import process,
|
icarus displays a progress dialog during the import process,
|
||||||
and a result window if any errors occurred.
|
and a result window if any errors occurred.
|
||||||
|
|
||||||
@@ -47,12 +44,9 @@ The icarus Settings dialog contains the following settings:
|
|||||||
where icarus puts the games it imports.
|
where icarus puts the games it imports.
|
||||||
See [Moving the Game Library][movgamelib]
|
See [Moving the Game Library][movgamelib]
|
||||||
for details.
|
for details.
|
||||||
- **Create Manifests** causes icarus
|
- **Create Manifests** causes icarus to
|
||||||
to include
|
include a [manifest] file
|
||||||
[a manifest file](../concepts/manifests.md)
|
inside the [game folder] for each imported game.
|
||||||
inside
|
|
||||||
[the game folder](../concepts/game-folders.md)
|
|
||||||
for each imported game.
|
|
||||||
See [Ignoring manifests](../concepts/manifests.md#ignoring-manifests)
|
See [Ignoring manifests](../concepts/manifests.md#ignoring-manifests)
|
||||||
for details.
|
for details.
|
||||||
- **Use Database** causes icarus to use manifest information
|
- **Use Database** causes icarus to use manifest information
|
||||||
@@ -65,5 +59,42 @@ The icarus Settings dialog contains the following settings:
|
|||||||
higan uses icarus to generate a manifest when a game is loaded,
|
higan uses icarus to generate a manifest when a game is loaded,
|
||||||
not just at import-time.
|
not just at import-time.
|
||||||
|
|
||||||
[gamelib]: ../concepts/game-library.md
|
Command line
|
||||||
|
------------
|
||||||
|
|
||||||
|
icarus can be launched in any of the following ways:
|
||||||
|
|
||||||
|
> icarus
|
||||||
|
>
|
||||||
|
> icarus \-\-import *FILE*
|
||||||
|
>
|
||||||
|
> icarus \-\-manifest *GAME*
|
||||||
|
|
||||||
|
When run without arguments,
|
||||||
|
icarus runs interactively
|
||||||
|
as described under [Bulk importing ROM files](#bulk-importing-rom-files) above.
|
||||||
|
|
||||||
|
When run with the `--import` flag,
|
||||||
|
`FILE` should be the path to a ROM file
|
||||||
|
for one of the consoles higan supports,
|
||||||
|
or a `.zip` file containing such a ROM file.
|
||||||
|
icarus will import it into the [game library]
|
||||||
|
just as it would if running interactively,
|
||||||
|
and the full path to the ressulting game folder
|
||||||
|
is printed to icarus' standard output.
|
||||||
|
|
||||||
|
If the game cannot be imported correctly
|
||||||
|
due to missing firmware,
|
||||||
|
icarus prints no output.
|
||||||
|
|
||||||
|
When run with the `--manifest` flag,
|
||||||
|
`GAME` should be the path to a [game folder],
|
||||||
|
such as a game previously imported into the [game library].
|
||||||
|
icarus will examine the game,
|
||||||
|
come up with a [manifest] describing the game's memory layout,
|
||||||
|
and print it to standard output.
|
||||||
|
|
||||||
|
[game library]: ../concepts/game-library.md
|
||||||
[movgamelib]: ../concepts/game-library.md#moving-the-game-library
|
[movgamelib]: ../concepts/game-library.md#moving-the-game-library
|
||||||
|
[game folder]: ../concepts/game-folders.md
|
||||||
|
[manifest]: ../concepts/manifests.md
|
||||||
|
@@ -29,8 +29,8 @@ you may be comparing it
|
|||||||
to a Mega Drive calibrated to a different scale
|
to a Mega Drive calibrated to a different scale
|
||||||
(or to an emulator tweaked to match such a Mega Drive).
|
(or to an emulator tweaked to match such a Mega Drive).
|
||||||
|
|
||||||
[vol]: https://board.byuu.org/viewtopic.php?p=42482#p42482
|
[vol]: https://helmet.kafuka.org/byuubackup2/viewtopic.php@f=4&t=1235&start=140.html#p42482
|
||||||
[va6]: https://board.byuu.org/viewtopic.php?p=42195#p42195
|
[va6]: https://helmet.kafuka.org/byuubackup2/viewtopic.php@f=4&t=1235&start=130.html#p42195
|
||||||
|
|
||||||
Playing Game Boy Color games in Game Boy mode
|
Playing Game Boy Color games in Game Boy mode
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
@@ -128,19 +128,23 @@ Rumble compatibility for Game Boy (Color)
|
|||||||
|
|
||||||
The Game Boy and Game Boy Color did not natively support
|
The Game Boy and Game Boy Color did not natively support
|
||||||
any kind of rumble or force-feedback system,
|
any kind of rumble or force-feedback system,
|
||||||
but some game cartridges (such as Pokémon Pinball)
|
but some game cartridges (such as *Pokémon Pinball*)
|
||||||
included a rumble motor within the cartridge itself.
|
included a rumble motor within the cartridge itself.
|
||||||
|
Such cartridges generally used the "MBC5" memory mapper chip.
|
||||||
|
|
||||||
Because higan does not currently support
|
To experience the rumble effect in higan,
|
||||||
game-specific controller features,
|
you'll need to configure the MBC5 "controller"
|
||||||
to experience the rumble effect in higan
|
connected to the "cartridge" port:
|
||||||
you'll need to configure the console:
|
|
||||||
|
|
||||||
- Open
|
- Open
|
||||||
[higan's Input settings](interface/higan-settings.md#input)
|
[higan's Input settings](interface/higan-settings.md#input)
|
||||||
- In the list of consoles,
|
- In the list of consoles,
|
||||||
select Game Boy, or Game Boy Color
|
select Game Boy, or Game Boy Color
|
||||||
depending on which console you want to use to play the game
|
depending on which console you want to use to play the game
|
||||||
|
- In the list of ports,
|
||||||
|
select "Cartridge"
|
||||||
|
- In the list of controllers,
|
||||||
|
select "MBC5"
|
||||||
- In the list of inputs,
|
- In the list of inputs,
|
||||||
double-click "Rumble"
|
double-click "Rumble"
|
||||||
or select it and press Enter
|
or select it and press Enter
|
||||||
|
@@ -51,7 +51,7 @@ Load a game
|
|||||||
-----------
|
-----------
|
||||||
|
|
||||||
From
|
From
|
||||||
[the Library menu](interface/higan.md#the-library-menu),
|
[the Systems menu](interface/higan.md#the-systems-menu),
|
||||||
choose "Load ROM File ..."
|
choose "Load ROM File ..."
|
||||||
to open [a filesystem browser](interface/common.md#the-filesystem-browser),
|
to open [a filesystem browser](interface/common.md#the-filesystem-browser),
|
||||||
and choose the game you want to play.
|
and choose the game you want to play.
|
||||||
@@ -63,7 +63,7 @@ In the future,
|
|||||||
if you want to play this game again,
|
if you want to play this game again,
|
||||||
you can choose "Load ROM File ..." as you did before,
|
you can choose "Load ROM File ..." as you did before,
|
||||||
or you can choose the appropriate console name
|
or you can choose the appropriate console name
|
||||||
from the Library menu,
|
from the Systems menu,
|
||||||
which will list all the games for that console
|
which will list all the games for that console
|
||||||
in the Game Library.
|
in the Game Library.
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ start out with nothing plugged in,
|
|||||||
so if the game you're playing needs a gamepad connected,
|
so if the game you're playing needs a gamepad connected,
|
||||||
you'll have to connect it!
|
you'll have to connect it!
|
||||||
|
|
||||||
This doesn't apply to handheld consoles
|
This doesn't apply to hand-held consoles
|
||||||
like the Game Boy and WonderSwan,
|
like the Game Boy and WonderSwan,
|
||||||
since the "controller" is always connected.
|
since the "controller" is always connected.
|
||||||
This *does* apply to the Famicom,
|
This *does* apply to the Famicom,
|
||||||
|
@@ -3,8 +3,13 @@ Release checklist
|
|||||||
|
|
||||||
1. Commit the new release
|
1. Commit the new release
|
||||||
2. Tag the commit
|
2. Tag the commit
|
||||||
3. `git push`
|
3. `git push --tags origin master` to push the commit and tag at the
|
||||||
4. `git push --tags`
|
same time.
|
||||||
5. Go to [the docs admin][rtd] and enable builds for the new tag.
|
4. Go to [the docs admin][rtd] and verify that it's building the new
|
||||||
|
version as 'stable' and under its tag name.
|
||||||
|
5. Check out the `libretro` branch.
|
||||||
|
6. Merge changes from master.
|
||||||
|
7. Copy `target-bsnes/resource/resource.?pp` to the `target-libretro` folder.
|
||||||
|
7. `git push` to make the new changes available.
|
||||||
|
|
||||||
[rtd]: https://readthedocs.org/dashboard/higan/versions/
|
[rtd]: https://readthedocs.org/projects/higan/builds/
|
||||||
|
BIN
firmware/cx4.data.rom
Normal file
BIN
firmware/sgb1.boot.rom
Normal file
BIN
firmware/sgb2.boot.rom
Normal file
59
genius/GNUmakefile
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
name := genius
|
||||||
|
build := stable
|
||||||
|
flags += -I..
|
||||||
|
|
||||||
|
nall.path := ../nall
|
||||||
|
include $(nall.path)/GNUmakefile
|
||||||
|
|
||||||
|
hiro.path := ../hiro
|
||||||
|
hiro.resource := data/$(name).rc
|
||||||
|
include $(hiro.path)/GNUmakefile
|
||||||
|
|
||||||
|
objects := obj/genius.o
|
||||||
|
|
||||||
|
obj/genius.o: genius.cpp
|
||||||
|
|
||||||
|
all: $(hiro.objects) $(objects)
|
||||||
|
$(info Linking out/$(name) ...)
|
||||||
|
+@$(compiler) -o out/$(name) $(hiro.objects) $(objects) $(hiro.options) $(options)
|
||||||
|
ifeq ($(platform),macos)
|
||||||
|
rm -rf out/$(name).app
|
||||||
|
mkdir -p out/$(name).app/Contents/MacOS/
|
||||||
|
mkdir -p out/$(name).app/Contents/Resources/
|
||||||
|
mv out/$(name) out/$(name).app/Contents/MacOS/$(name)
|
||||||
|
cp data/$(name).plist out/$(name).app/Contents/Info.plist
|
||||||
|
sips -s format icns data/$(name).png --out out/$(name).app/Contents/Resources/$(name).icns
|
||||||
|
endif
|
||||||
|
|
||||||
|
verbose: hiro.verbose nall.verbose all;
|
||||||
|
|
||||||
|
clean:
|
||||||
|
ifeq ($(platform),macos)
|
||||||
|
rm -rf out/$(name).app
|
||||||
|
endif
|
||||||
|
$(call delete,obj/*)
|
||||||
|
$(call delete,out/*)
|
||||||
|
|
||||||
|
install: all
|
||||||
|
ifeq ($(platform),macos)
|
||||||
|
cp -R out/$(name).app /Applications/$(name).app
|
||||||
|
else ifneq ($(filter $(platform),linux bsd),)
|
||||||
|
mkdir -p $(prefix)/bin/
|
||||||
|
mkdir -p $(prefix)/share/applications/
|
||||||
|
mkdir -p $(prefix)/share/icons/
|
||||||
|
mkdir -p $(prefix)/share/$(name)/
|
||||||
|
cp out/$(name) $(prefix)/bin/$(name)
|
||||||
|
cp data/$(name).desktop $(prefix)/share/applications/$(name).desktop
|
||||||
|
cp data/$(name).png $(prefix)/share/icons/$(name).png
|
||||||
|
endif
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
ifeq ($(platform),macos)
|
||||||
|
rm -rf /Applications/$(name).app
|
||||||
|
else ifneq ($(filter $(platform),linux bsd),)
|
||||||
|
rm -f $(prefix)/bin/$(name)
|
||||||
|
rm -f $(prefix)/share/applications/$(name).desktop
|
||||||
|
rm -f $(prefix)/share/icons/$(name).png
|
||||||
|
endif
|
||||||
|
|
||||||
|
-include obj/*.d
|
14
genius/data/genius.Manifest
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||||
|
<assemblyIdentity type="win32" name="genius" version="1.0.0.0" processorArchitecture="*"/>
|
||||||
|
<dependency>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
|
||||||
|
</dependentAssembly>
|
||||||
|
</dependency>
|
||||||
|
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||||
|
<dpiAware>false</dpiAware>
|
||||||
|
</asmv3:windowsSettings>
|
||||||
|
</asmv3:application>
|
||||||
|
</assembly>
|
8
genius/data/genius.desktop
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Name=genius
|
||||||
|
Comment=Emulator
|
||||||
|
Exec=genius
|
||||||
|
Icon=genius
|
||||||
|
Terminal=false
|
||||||
|
Type=Application
|
||||||
|
Categories=Game;Emulator;
|
BIN
genius/data/genius.ico
Normal file
After Width: | Height: | Size: 33 KiB |
18
genius/data/genius.plist
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>org.byuu.genius</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>genius</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>genius</string>
|
||||||
|
<key>CFBundleIconFile</key>
|
||||||
|
<string>genius.icns</string>
|
||||||
|
<key>NSHighResolutionCapable</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
BIN
genius/data/genius.png
Normal file
After Width: | Height: | Size: 14 KiB |
2
genius/data/genius.rc
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
1 24 "genius.Manifest"
|
||||||
|
2 ICON DISCARDABLE "genius.ico"
|
80
genius/data/genius.svg
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="256mm"
|
||||||
|
height="256mm"
|
||||||
|
viewBox="0 0 256 256"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
|
||||||
|
sodipodi:docname="icarus.svg">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="0.5"
|
||||||
|
inkscape:cx="62.34093"
|
||||||
|
inkscape:cy="560"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1028"
|
||||||
|
inkscape:window-x="-8"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-41)">
|
||||||
|
<circle
|
||||||
|
id="path10"
|
||||||
|
cx="128.0"
|
||||||
|
cy="169.0"
|
||||||
|
r="120.0"
|
||||||
|
style="stroke-width:0.25;fill:#b8b8ff;fill-opacity:1" />
|
||||||
|
<g
|
||||||
|
aria-label="氷"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:260.07336426px;line-height:1.25;font-family:KaiTi;-inkscape-font-specification:KaiTi;letter-spacing:0px;word-spacing:0px;fill:#4050e0;fill-opacity:1;stroke:#4050e0;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="text818">
|
||||||
|
<path
|
||||||
|
style="fill:#4050e0;fill-opacity:1;stroke:#4050e0;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 130.80961,146.24049 q 5.07956,5.07956 14.22276,15.23868 14.22277,-13.20685 25.39779,-27.42962 12.19094,-15.23867 11.17503,-24.38187 0,-10.15912 10.15912,-4.06365 10.15911,6.09547 14.22276,12.19094 4.06364,5.07956 -2.03182,7.11138 -6.09547,1.01591 -22.35006,14.22276 -15.23867,12.19094 -32.50917,26.4137 13.20685,11.17503 23.36597,20.31823 11.17502,9.14321 24.38187,18.28641 14.22277,8.1273 27.42962,14.22276 14.22276,5.07956 21.33414,7.11139 7.11138,2.03182 -3.04773,5.07955 -9.14321,2.03183 -23.36597,3.04774 -14.22276,0 -21.33414,-2.03182 -6.09547,-3.04774 -11.17503,-8.1273 -4.06365,-5.07956 -24.38188,-27.42961 -19.30232,-23.36597 -31.49326,-39.62055 1.01591,41.65237 2.03182,64.00243 2.03183,22.35005 0,34.54099 -2.03182,12.19094 -8.12729,19.30232 -5.07956,7.11138 -8.12729,4.06365 -2.03182,-2.03183 -7.11138,-11.17503 -5.07956,-8.12729 -16.254587,-15.23867 -11.175027,-8.1273 1.015912,-5.07956 12.190935,2.03182 16.254585,2.03182 4.06365,-1.01591 6.09547,-7.11138 2.03182,-7.11138 2.03182,-46.73193 0,-40.63646 -1.01591,-77.20928 -1.01591,-37.58873 -6.09547,-45.716022 -5.07956,-9.143205 4.06365,-7.111382 10.15911,1.015912 16.25458,5.079558 7.11138,3.047735 4.06365,9.143205 -3.04774,5.079557 -4.06365,15.238673 -1.01591,10.159118 -1.01591,51.811488 z"
|
||||||
|
id="path822" />
|
||||||
|
<path
|
||||||
|
style="fill:#4050e0;fill-opacity:1;stroke:#4050e0;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 88.141325,149.28823 q 5.079558,1.01591 14.222765,7.11138 9.1432,6.09547 4.06364,10.15911 -5.07955,4.06365 -12.190935,18.28641 -6.09547,14.22276 -14.222763,25.39779 -8.127292,10.15912 -19.30232,19.30232 -11.175027,8.12729 -21.334143,12.19094 -9.143204,3.04774 -16.254585,5.07956 -6.095469,1.01591 5.079558,-6.09547 11.175027,-7.11138 21.334143,-17.2705 11.175027,-10.15911 18.286408,-21.33414 8.127293,-12.19094 11.175028,-20.31823 3.047735,-9.14321 3.047735,-14.22276 1.015911,-5.07956 -3.047735,-5.07956 -4.063646,0 -15.238674,4.06364 -10.159116,4.06365 -15.238674,6.09547 -4.063646,2.03183 -13.20685,-3.04773 -8.127293,-6.09547 2.031823,-6.09547 11.175027,-1.01591 24.381878,-5.07956 14.222762,-5.07956 17.270497,-7.11138 4.063646,-3.04773 9.143204,-2.03182 z"
|
||||||
|
id="path820" />
|
||||||
|
<path
|
||||||
|
style="fill:#4050e0;fill-opacity:1;stroke:#4050e0;stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 63.759447,101.54038 q 9.143204,1.01591 18.286409,5.07956 9.143204,4.06365 11.175027,11.17503 2.031823,7.11138 -1.015912,11.17503 -3.047734,4.06364 -15.238673,-4.06365 -11.175028,-9.1432 -16.254586,-16.25459 -5.079557,-8.12729 3.047735,-7.11138 z"
|
||||||
|
id="path815" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4.9 KiB |
629
genius/genius.cpp
Normal file
@@ -0,0 +1,629 @@
|
|||||||
|
#include <nall/nall.hpp>
|
||||||
|
using namespace nall;
|
||||||
|
|
||||||
|
#include <hiro/hiro.hpp>
|
||||||
|
using namespace hiro;
|
||||||
|
|
||||||
|
#include "genius.hpp"
|
||||||
|
unique_pointer<ListWindow> listWindow;
|
||||||
|
unique_pointer<GameWindow> gameWindow;
|
||||||
|
unique_pointer<MemoryWindow> memoryWindow;
|
||||||
|
unique_pointer<OscillatorWindow> oscillatorWindow;
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
ListWindow::ListWindow() {
|
||||||
|
listWindow = this;
|
||||||
|
|
||||||
|
fileMenu.setText("File");
|
||||||
|
newAction.setText("New").onActivate([&] { newDatabase(); });
|
||||||
|
openAction.setText("Open ...").onActivate([&] {
|
||||||
|
if(auto location = BrowserDialog().setParent(*this).setFilters({"*.bml"}).openFile()) {
|
||||||
|
loadDatabase(location);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
saveAction.setText("Save").onActivate([&] {
|
||||||
|
if(!location) return saveAsAction.doActivate();
|
||||||
|
saveDatabase(location);
|
||||||
|
});
|
||||||
|
saveAsAction.setText("Save As ...").onActivate([&] {
|
||||||
|
if(auto location = BrowserDialog().setParent(*this).setFilters({"*.bml"}).saveFile()) {
|
||||||
|
saveDatabase(location);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
quitAction.setText("Quit").onActivate([&] { quit(); });
|
||||||
|
|
||||||
|
helpMenu.setText("Help");
|
||||||
|
aboutAction.setText("About ...").onActivate([&] {
|
||||||
|
MessageDialog().setParent(*this).setTitle("About").setText({
|
||||||
|
"genius\n",
|
||||||
|
"Author: byuu\n",
|
||||||
|
"Website: https://byuu.org/"
|
||||||
|
}).information();
|
||||||
|
});
|
||||||
|
|
||||||
|
layout.setPadding(5);
|
||||||
|
gameList.setHeadered();
|
||||||
|
gameList.onActivate([&] { modifyButton.doActivate(); });
|
||||||
|
gameList.onChange([&] { updateWindow(); });
|
||||||
|
appendButton.setText("Append").onActivate([&] {
|
||||||
|
setEnabled(false);
|
||||||
|
gameWindow->show();
|
||||||
|
});
|
||||||
|
modifyButton.setText("Modify").onActivate([&] {
|
||||||
|
if(auto item = gameList.selected()) {
|
||||||
|
setEnabled(false);
|
||||||
|
gameWindow->show(games[item.offset()]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
removeButton.setText("Remove").onActivate([&] { removeGame(); });
|
||||||
|
|
||||||
|
onClose([&] { quit(); });
|
||||||
|
|
||||||
|
setSize({820, 600});
|
||||||
|
reloadList();
|
||||||
|
updateWindow();
|
||||||
|
setCentered();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ListWindow::quit() -> void {
|
||||||
|
if(!modified || MessageDialog().setParent(*this).setText({
|
||||||
|
"Are you sure you want to quit without saving your changes?"
|
||||||
|
}).question() == "Yes") {
|
||||||
|
Application::quit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ListWindow::reloadList() -> void {
|
||||||
|
gameList.reset();
|
||||||
|
gameList.append(TableViewColumn().setText("Name").setExpandable());
|
||||||
|
gameList.append(TableViewColumn().setText("Region"));
|
||||||
|
gameList.append(TableViewColumn().setText("Revision"));
|
||||||
|
gameList.append(TableViewColumn().setText("Board"));
|
||||||
|
for(auto& game : games) {
|
||||||
|
TableViewItem item{&gameList};
|
||||||
|
item.append(TableViewCell().setText(game.name));
|
||||||
|
item.append(TableViewCell().setText(game.region));
|
||||||
|
item.append(TableViewCell().setText(game.revision));
|
||||||
|
item.append(TableViewCell().setText(game.board));
|
||||||
|
}
|
||||||
|
Application::processEvents();
|
||||||
|
gameList.resizeColumns();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ListWindow::updateWindow() -> void {
|
||||||
|
modifyButton.setEnabled((bool)gameList.selected());
|
||||||
|
removeButton.setEnabled((bool)gameList.selected());
|
||||||
|
string name = Location::base(location);
|
||||||
|
if(!name) name = "(Untitled)";
|
||||||
|
setTitle({modified ? "*" : "", name, " [", games.size(), "] - genius"});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ListWindow::newDatabase() -> void {
|
||||||
|
games.reset();
|
||||||
|
modified = false;
|
||||||
|
location = "";
|
||||||
|
reloadList();
|
||||||
|
updateWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ListWindow::loadDatabase(string location) -> void {
|
||||||
|
auto document = BML::unserialize(string::read(location));
|
||||||
|
|
||||||
|
games.reset();
|
||||||
|
for(auto node : document.find("game")) {
|
||||||
|
Game game;
|
||||||
|
game.sha256 = node["sha256"].text();
|
||||||
|
game.label = node["label"].text();
|
||||||
|
game.name = node["name"].text();
|
||||||
|
game.region = node["region"].text();
|
||||||
|
game.revision = node["revision"].text();
|
||||||
|
game.board = node["board"].text();
|
||||||
|
for(auto object : node["board"]) {
|
||||||
|
Component component;
|
||||||
|
if(object.name() == "memory") {
|
||||||
|
component.type = Component::Type::Memory;
|
||||||
|
component.memory.type = object["type"].text();
|
||||||
|
component.memory.size = object["size"].text();
|
||||||
|
component.memory.content = object["content"].text();
|
||||||
|
component.memory.manufacturer = object["manufacturer"].text();
|
||||||
|
component.memory.architecture = object["architecture"].text();
|
||||||
|
component.memory.identifier = object["identifier"].text();
|
||||||
|
component.memory.Volatile = (bool)object["volatile"];
|
||||||
|
}
|
||||||
|
if(object.name() == "oscillator") {
|
||||||
|
component.type = Component::Type::Oscillator;
|
||||||
|
component.oscillator.frequency = object["frequency"].text();
|
||||||
|
}
|
||||||
|
game.components.append(component);
|
||||||
|
}
|
||||||
|
game.note = node["note"].text();
|
||||||
|
games.append(game);
|
||||||
|
}
|
||||||
|
|
||||||
|
modified = false;
|
||||||
|
this->location = location;
|
||||||
|
reloadList();
|
||||||
|
updateWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ListWindow::saveDatabase(string location) -> void {
|
||||||
|
auto fp = file::open(location, file::mode::write);
|
||||||
|
if(!fp) return MessageDialog().setParent(*this).setText({
|
||||||
|
"Error: failed to write file.\n\n",
|
||||||
|
"Name: ", location
|
||||||
|
}).error(), void();
|
||||||
|
|
||||||
|
auto copy = games;
|
||||||
|
copy.sort([](auto x, auto y) {
|
||||||
|
return string::icompare(
|
||||||
|
{x.name, "\n", x.region, "\n", x.revision},
|
||||||
|
{y.name, "\n", y.region, "\n", y.revision}
|
||||||
|
) < 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
fp.print("database\n");
|
||||||
|
fp.print(" revision: ", chrono::local::date(), "\n\n");
|
||||||
|
|
||||||
|
for(auto& game : copy) {
|
||||||
|
fp.print("game\n");
|
||||||
|
fp.print(" sha256: ", game.sha256, "\n");
|
||||||
|
if(game.label)
|
||||||
|
fp.print(" label: ", game.label, "\n");
|
||||||
|
fp.print(" name: ", game.name, "\n");
|
||||||
|
fp.print(" region: ", game.region, "\n");
|
||||||
|
fp.print(" revision: ", game.revision, "\n");
|
||||||
|
if(game.board)
|
||||||
|
fp.print(" board: ", game.board, "\n");
|
||||||
|
else if(game.components)
|
||||||
|
fp.print(" board\n");
|
||||||
|
for(auto& component : game.components) {
|
||||||
|
if(component.type == Component::Type::Memory) {
|
||||||
|
fp.print(" memory\n");
|
||||||
|
fp.print(" type: ", component.memory.type, "\n");
|
||||||
|
fp.print(" size: ", component.memory.size, "\n");
|
||||||
|
fp.print(" content: ", component.memory.content, "\n");
|
||||||
|
if(component.memory.manufacturer)
|
||||||
|
fp.print(" manufacturer: ", component.memory.manufacturer, "\n");
|
||||||
|
if(component.memory.architecture)
|
||||||
|
fp.print(" architecture: ", component.memory.architecture, "\n");
|
||||||
|
if(component.memory.identifier)
|
||||||
|
fp.print(" identifier: ", component.memory.identifier, "\n");
|
||||||
|
if(component.memory.Volatile)
|
||||||
|
fp.print(" volatile\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(component.type == Component::Type::Oscillator) {
|
||||||
|
fp.print(" oscillator\n");
|
||||||
|
fp.print(" frequency: ", component.oscillator.frequency, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(game.note)
|
||||||
|
fp.print(" note: ", game.note, "\n");
|
||||||
|
fp.print("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
modified = false;
|
||||||
|
this->location = location;
|
||||||
|
updateWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ListWindow::appendGame(Game game) -> void {
|
||||||
|
modified = true;
|
||||||
|
auto offset = games.size();
|
||||||
|
games.append(game);
|
||||||
|
reloadList();
|
||||||
|
gameList.item(offset).setSelected().setFocused();
|
||||||
|
updateWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ListWindow::modifyGame(Game game) -> void {
|
||||||
|
if(auto item = gameList.selected()) {
|
||||||
|
modified = true;
|
||||||
|
auto offset = item.offset();
|
||||||
|
games[offset] = game;
|
||||||
|
reloadList();
|
||||||
|
gameList.item(offset).setSelected().setFocused();
|
||||||
|
updateWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ListWindow::removeGame() -> void {
|
||||||
|
if(auto item = gameList.selected()) {
|
||||||
|
if(MessageDialog().setParent(*this).setText({
|
||||||
|
"Are you sure you want to permanently remove this game?\n\n",
|
||||||
|
"Name: ", item.cell(0).text()
|
||||||
|
}).question() == "Yes") {
|
||||||
|
modified = true;
|
||||||
|
games.remove(item.offset());
|
||||||
|
reloadList();
|
||||||
|
updateWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
GameWindow::GameWindow() {
|
||||||
|
gameWindow = this;
|
||||||
|
|
||||||
|
layout.setPadding(5);
|
||||||
|
hashLabel.setText("SHA256:").setAlignment(1.0);
|
||||||
|
hashEdit.setFont(Font().setFamily(Font::Mono)).onChange([&] { modified = true, updateWindow(); });
|
||||||
|
regionLabel.setText("Region:").setAlignment(1.0);
|
||||||
|
regionEdit.setFont(Font().setFamily(Font::Mono)).onChange([&] { modified = true, updateWindow(); });
|
||||||
|
revisionLabel.setText("Revision:");
|
||||||
|
revisionEdit.setFont(Font().setFamily(Font::Mono)).onChange([&] { modified = true, updateWindow(); });
|
||||||
|
boardLabel.setText("Board:");
|
||||||
|
boardEdit.setFont(Font().setFamily(Font::Mono)).onChange([&] { modified = true, updateWindow(); });
|
||||||
|
nameLabel.setText("Name:").setAlignment(1.0);
|
||||||
|
nameEdit.onChange([&] { modified = true, updateWindow(); });
|
||||||
|
labelLabel.setText("Label:").setAlignment(1.0);
|
||||||
|
labelEdit.onChange([&] { modified = true, updateWindow(); });
|
||||||
|
noteLabel.setText("Note:").setAlignment(1.0);
|
||||||
|
noteEdit.onChange([&] { modified = true, updateWindow(); });
|
||||||
|
componentLabel.setText("Tree:").setAlignment({1.0, 0.0});
|
||||||
|
componentTree.onActivate([&] { modifyComponentButton.doActivate(); });
|
||||||
|
componentTree.onChange([&] { updateWindow(); });
|
||||||
|
appendMemoryButton.setText("Memory").onActivate([&] {
|
||||||
|
setEnabled(false);
|
||||||
|
memoryWindow->show();
|
||||||
|
});
|
||||||
|
appendOscillatorButton.setText("Oscillator").onActivate([&] {
|
||||||
|
setEnabled(false);
|
||||||
|
oscillatorWindow->show();
|
||||||
|
});
|
||||||
|
modifyComponentButton.setText("Modify").onActivate([&] {
|
||||||
|
if(auto item = componentTree.selected()) {
|
||||||
|
setEnabled(false);
|
||||||
|
auto path = item.path().split("/");
|
||||||
|
auto offset = path(0).natural();
|
||||||
|
Component component = game.components[offset];
|
||||||
|
if(component.type == Component::Type::Memory) {
|
||||||
|
memoryWindow->show(component.memory);
|
||||||
|
}
|
||||||
|
if(component.type == Component::Type::Oscillator) {
|
||||||
|
oscillatorWindow->show(component.oscillator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
removeComponentButton.setText("Remove").onActivate([&] { removeComponent(); });
|
||||||
|
acceptButton.setText("Accept").onActivate([&] { accept(); });
|
||||||
|
cancelButton.setText("Cancel").onActivate([&] { cancel(); });
|
||||||
|
|
||||||
|
onClose([&] { cancel(); });
|
||||||
|
|
||||||
|
setSize({640, 480});
|
||||||
|
setDismissable();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GameWindow::show(Game game) -> void {
|
||||||
|
this->game = game;
|
||||||
|
modified = false;
|
||||||
|
create = !game.sha256;
|
||||||
|
|
||||||
|
hashEdit.setText(game.sha256);
|
||||||
|
regionEdit.setText(game.region);
|
||||||
|
revisionEdit.setText(game.revision);
|
||||||
|
boardEdit.setText(game.board);
|
||||||
|
nameEdit.setText(game.name);
|
||||||
|
labelEdit.setText(game.label);
|
||||||
|
noteEdit.setText(game.note);
|
||||||
|
acceptButton.setText(create ? "Create" : "Apply");
|
||||||
|
|
||||||
|
reloadList();
|
||||||
|
updateWindow();
|
||||||
|
setCentered(*listWindow);
|
||||||
|
setVisible();
|
||||||
|
|
||||||
|
if(create) {
|
||||||
|
hashEdit.setFocused();
|
||||||
|
} else {
|
||||||
|
cancelButton.setFocused();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GameWindow::accept() -> void {
|
||||||
|
game.sha256 = hashEdit.text().strip();
|
||||||
|
game.region = regionEdit.text().strip();
|
||||||
|
game.revision = revisionEdit.text().strip();
|
||||||
|
game.board = boardEdit.text().strip();
|
||||||
|
game.name = nameEdit.text().strip();
|
||||||
|
game.label = labelEdit.text().strip();
|
||||||
|
game.note = noteEdit.text().strip();
|
||||||
|
|
||||||
|
if(create) {
|
||||||
|
listWindow->appendGame(game);
|
||||||
|
} else {
|
||||||
|
listWindow->modifyGame(game);
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryWindow->setVisible(false);
|
||||||
|
setVisible(false);
|
||||||
|
listWindow->setEnabled();
|
||||||
|
listWindow->setFocused();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GameWindow::cancel() -> void {
|
||||||
|
if(!modified || MessageDialog().setParent(*this).setText({
|
||||||
|
"Are you sure you want to discard your changes to this game?"
|
||||||
|
}).question() == "Yes") {
|
||||||
|
memoryWindow->setVisible(false);
|
||||||
|
setVisible(false);
|
||||||
|
listWindow->setEnabled();
|
||||||
|
listWindow->setFocused();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GameWindow::reloadList() -> void {
|
||||||
|
componentTree.reset();
|
||||||
|
uint counter = 1;
|
||||||
|
for(auto& component : game.components) {
|
||||||
|
TreeViewItem item;
|
||||||
|
|
||||||
|
string index = {"[", counter++, "] "};
|
||||||
|
if(component.type == Component::Type::Memory) {
|
||||||
|
item.setText({index, "Memory"});
|
||||||
|
item.append(TreeViewItem().setText({"Type: ", component.memory.type}));
|
||||||
|
item.append(TreeViewItem().setText({"Size: ", component.memory.size}));
|
||||||
|
item.append(TreeViewItem().setText({"Content: ", component.memory.content}));
|
||||||
|
if(component.memory.manufacturer)
|
||||||
|
item.append(TreeViewItem().setText({"Manufacturer: ", component.memory.manufacturer}));
|
||||||
|
if(component.memory.architecture)
|
||||||
|
item.append(TreeViewItem().setText({"Architecture: ", component.memory.architecture}));
|
||||||
|
if(component.memory.identifier)
|
||||||
|
item.append(TreeViewItem().setText({"Identifier: ", component.memory.identifier}));
|
||||||
|
if(component.memory.Volatile)
|
||||||
|
item.append(TreeViewItem().setText({"Volatile"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(component.type == Component::Type::Oscillator) {
|
||||||
|
item.setText({index, "Oscillator"});
|
||||||
|
item.append(TreeViewItem().setText({"Frequency: ", component.oscillator.frequency}));
|
||||||
|
}
|
||||||
|
|
||||||
|
componentTree.append(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
Application::processEvents();
|
||||||
|
for(auto& item : componentTree.items()) item.setExpanded();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GameWindow::updateWindow() -> void {
|
||||||
|
bool valid = true;
|
||||||
|
bool hashValid = hashEdit.text().strip().size() == 64;
|
||||||
|
hashEdit.setEditable(!hashValid).setBackgroundColor(
|
||||||
|
!create || hashValid ? Color{192, 255, 192}
|
||||||
|
: (valid = false, Color{255, 224, 224}));
|
||||||
|
regionEdit.setBackgroundColor(regionEdit.text().strip() ? Color{} : (valid = false, Color{255, 224, 224}));
|
||||||
|
revisionEdit.setBackgroundColor(revisionEdit.text().strip() ? Color{} : (valid = false, Color{255, 224, 224}));
|
||||||
|
boardEdit.setBackgroundColor(boardEdit.text().strip() ? Color{} : (Color{255, 255, 240}));
|
||||||
|
nameEdit.setBackgroundColor(nameEdit.text().strip() ? Color{} : (valid = false, Color{255, 224, 224}));
|
||||||
|
labelEdit.setBackgroundColor(labelEdit.text().strip() ? Color{} : (Color{255, 255, 240}));
|
||||||
|
noteEdit.setBackgroundColor(noteEdit.text().strip() ? Color{} : (Color{255, 255, 240}));
|
||||||
|
modifyComponentButton.setEnabled((bool)componentTree.selected());
|
||||||
|
removeComponentButton.setEnabled((bool)componentTree.selected());
|
||||||
|
acceptButton.setEnabled(valid);
|
||||||
|
setTitle({modified ? "*" : "", create ? "Add New Game" : "Modify Game Details"});
|
||||||
|
if(create && hashValid && hashEdit.focused()) regionEdit.setFocused();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GameWindow::appendComponent(Component component) -> void {
|
||||||
|
modified = true;
|
||||||
|
auto offset = game.components.size();
|
||||||
|
game.components.append(component);
|
||||||
|
reloadList();
|
||||||
|
componentTree.item(offset).setSelected().setFocused();
|
||||||
|
updateWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GameWindow::modifyComponent(Component component) -> void {
|
||||||
|
if(auto item = componentTree.selected()) {
|
||||||
|
modified = true;
|
||||||
|
auto path = item.path().split("/");
|
||||||
|
auto offset = path(0).natural();
|
||||||
|
game.components[offset] = component;
|
||||||
|
reloadList();
|
||||||
|
componentTree.item(offset).setSelected().setFocused();
|
||||||
|
updateWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GameWindow::removeComponent() -> void {
|
||||||
|
if(auto item = componentTree.selected()) {
|
||||||
|
if(MessageDialog().setParent(*this).setText({
|
||||||
|
"Are you sure you want to permanently remove this component?"
|
||||||
|
}).question() == "Yes") {
|
||||||
|
modified = true;
|
||||||
|
auto path = item.path().split("/");
|
||||||
|
auto offset = path(0).natural();
|
||||||
|
game.components.remove(offset);
|
||||||
|
reloadList();
|
||||||
|
updateWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
MemoryWindow::MemoryWindow() {
|
||||||
|
memoryWindow = this;
|
||||||
|
|
||||||
|
layout.setPadding(5);
|
||||||
|
typeLabel.setText("Type:").setAlignment(1.0);
|
||||||
|
typeEdit.append(ComboEditItem().setText("ROM"));
|
||||||
|
typeEdit.append(ComboEditItem().setText("EEPROM"));
|
||||||
|
typeEdit.append(ComboEditItem().setText("Flash"));
|
||||||
|
typeEdit.append(ComboEditItem().setText("RAM"));
|
||||||
|
typeEdit.append(ComboEditItem().setText("RTC"));
|
||||||
|
typeEdit.onChange([&] { modified = true, updateWindow(); });
|
||||||
|
sizeLabel.setText("Size:").setAlignment(1.0);
|
||||||
|
sizeEdit.onChange([&] { modified = true, updateWindow(); });
|
||||||
|
contentLabel.setText("Content:").setAlignment(1.0);
|
||||||
|
contentEdit.append(ComboEditItem().setText("Program"));
|
||||||
|
contentEdit.append(ComboEditItem().setText("Data"));
|
||||||
|
contentEdit.append(ComboEditItem().setText("Character"));
|
||||||
|
contentEdit.append(ComboEditItem().setText("Save"));
|
||||||
|
contentEdit.append(ComboEditItem().setText("Time"));
|
||||||
|
contentEdit.onChange([&] { modified = true, updateWindow(); });
|
||||||
|
manufacturerLabel.setText("Manufacturer:").setAlignment(1.0);
|
||||||
|
manufacturerEdit.onChange([&] { modified = true, updateWindow(); });
|
||||||
|
architectureLabel.setText("Architecture:").setAlignment(1.0);
|
||||||
|
architectureEdit.onChange([&] { modified = true, updateWindow(); });
|
||||||
|
identifierLabel.setText("Identifier:").setAlignment(1.0);
|
||||||
|
identifierEdit.onChange([&] { modified = true, updateWindow(); });
|
||||||
|
volatileOption.setText("Volatile").onToggle([&] { modified = true, updateWindow(); });
|
||||||
|
acceptButton.setText("Accept").onActivate([&] { accept(); });
|
||||||
|
cancelButton.setText("Cancel").onActivate([&] { cancel(); });
|
||||||
|
|
||||||
|
onClose([&] { cancel(); });
|
||||||
|
|
||||||
|
setSize({320, layout.minimumSize().height()});
|
||||||
|
setDismissable();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto MemoryWindow::show(Memory memory) -> void {
|
||||||
|
this->memory = memory;
|
||||||
|
modified = false;
|
||||||
|
create = !memory.type;
|
||||||
|
|
||||||
|
typeEdit.setText(memory.type);
|
||||||
|
sizeEdit.setText(memory.size);
|
||||||
|
contentEdit.setText(memory.content);
|
||||||
|
manufacturerEdit.setText(memory.manufacturer);
|
||||||
|
architectureEdit.setText(memory.architecture);
|
||||||
|
identifierEdit.setText(memory.identifier);
|
||||||
|
volatileOption.setChecked(memory.Volatile);
|
||||||
|
|
||||||
|
updateWindow();
|
||||||
|
setCentered(*gameWindow);
|
||||||
|
setVisible();
|
||||||
|
|
||||||
|
typeEdit.setFocused();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto MemoryWindow::accept() -> void {
|
||||||
|
memory.type = typeEdit.text().strip();
|
||||||
|
memory.size = sizeEdit.text().strip();
|
||||||
|
memory.content = contentEdit.text().strip();
|
||||||
|
memory.manufacturer = manufacturerEdit.text().strip();
|
||||||
|
memory.architecture = architectureEdit.text().strip();
|
||||||
|
memory.identifier = identifierEdit.text().strip();
|
||||||
|
memory.Volatile = volatileOption.checked() && (memory.type == "RAM" || memory.type == "RTC");
|
||||||
|
|
||||||
|
Component component{Component::Type::Memory};
|
||||||
|
component.memory = memory;
|
||||||
|
if(create) {
|
||||||
|
gameWindow->appendComponent(component);
|
||||||
|
} else {
|
||||||
|
gameWindow->modifyComponent(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
setVisible(false);
|
||||||
|
gameWindow->setEnabled();
|
||||||
|
gameWindow->setFocused();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto MemoryWindow::cancel() -> void {
|
||||||
|
if(!modified || MessageDialog().setParent(*this).setText({
|
||||||
|
"Are you sure you want to discard your changes to this memory?"
|
||||||
|
}).question() == "Yes") {
|
||||||
|
setVisible(false);
|
||||||
|
gameWindow->setEnabled();
|
||||||
|
gameWindow->setFocused();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto MemoryWindow::updateWindow() -> void {
|
||||||
|
bool valid = true;
|
||||||
|
typeEdit.setBackgroundColor(typeEdit.text().strip() ? Color{} : (valid = false, Color{255, 224, 224}));
|
||||||
|
sizeEdit.setBackgroundColor(sizeEdit.text().strip() ? Color{} : (valid = false, Color{255, 224, 224}));
|
||||||
|
contentEdit.setBackgroundColor(contentEdit.text().strip() ? Color{} : (valid = false, Color{255, 224, 224}));
|
||||||
|
manufacturerEdit.setBackgroundColor(manufacturerEdit.text().strip() ? Color{} : (Color{255, 255, 240}));
|
||||||
|
architectureEdit.setBackgroundColor(architectureEdit.text().strip() ? Color{} : (Color{255, 255, 240}));
|
||||||
|
identifierEdit.setBackgroundColor(identifierEdit.text().strip() ? Color{} : (Color{255, 255, 240}));
|
||||||
|
volatileOption.setEnabled(typeEdit.text().strip() == "RAM" || typeEdit.text().strip() == "RTC");
|
||||||
|
acceptButton.setEnabled(valid);
|
||||||
|
setTitle({modified ? "*" : "", create ? "Add New Memory" : "Modify Memory Details"});
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
OscillatorWindow::OscillatorWindow() {
|
||||||
|
oscillatorWindow = this;
|
||||||
|
|
||||||
|
layout.setPadding(5);
|
||||||
|
frequencyLabel.setText("Frequency:").setAlignment(1.0);
|
||||||
|
frequencyEdit.onChange([&] { modified = true, updateWindow(); });
|
||||||
|
acceptButton.setText("Accept").onActivate([&] { accept(); });
|
||||||
|
cancelButton.setText("Cancel").onActivate([&] { cancel(); });
|
||||||
|
|
||||||
|
onClose([&] { cancel(); });
|
||||||
|
|
||||||
|
setSize({320, layout.minimumSize().height()});
|
||||||
|
setDismissable();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto OscillatorWindow::show(Oscillator oscillator) -> void {
|
||||||
|
this->oscillator = oscillator;
|
||||||
|
modified = false;
|
||||||
|
create = !oscillator.frequency;
|
||||||
|
|
||||||
|
frequencyEdit.setText(oscillator.frequency);
|
||||||
|
|
||||||
|
updateWindow();
|
||||||
|
setCentered(*gameWindow);
|
||||||
|
setVisible();
|
||||||
|
|
||||||
|
frequencyEdit.setFocused();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto OscillatorWindow::accept() -> void {
|
||||||
|
oscillator.frequency = frequencyEdit.text().strip();
|
||||||
|
|
||||||
|
Component component{Component::Type::Oscillator};
|
||||||
|
component.oscillator = oscillator;
|
||||||
|
if(create) {
|
||||||
|
gameWindow->appendComponent(component);
|
||||||
|
} else {
|
||||||
|
gameWindow->modifyComponent(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
setVisible(false);
|
||||||
|
gameWindow->setEnabled();
|
||||||
|
gameWindow->setFocused();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto OscillatorWindow::cancel() -> void {
|
||||||
|
if(!modified || MessageDialog().setParent(*this).setText({
|
||||||
|
"Are you sure you want to discard your changes to this property?"
|
||||||
|
}).question() == "Yes") {
|
||||||
|
setVisible(false);
|
||||||
|
gameWindow->setEnabled();
|
||||||
|
gameWindow->setFocused();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto OscillatorWindow::updateWindow() -> void {
|
||||||
|
bool valid = true;
|
||||||
|
frequencyEdit.setBackgroundColor(frequencyEdit.text().strip() ? Color{} : (valid = false, Color{255, 224, 224}));
|
||||||
|
acceptButton.setEnabled(valid);
|
||||||
|
setTitle({modified ? "*" : "", create ? "Add New Property" : "Modify Property Details"});
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
auto hiro::initialize() -> void {
|
||||||
|
Application::setName("genius");
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <nall/main.hpp>
|
||||||
|
auto nall::main(Arguments) -> void {
|
||||||
|
new ListWindow;
|
||||||
|
new GameWindow;
|
||||||
|
new MemoryWindow;
|
||||||
|
new OscillatorWindow;
|
||||||
|
|
||||||
|
listWindow->setVisible();
|
||||||
|
Application::run();
|
||||||
|
}
|
178
genius/genius.hpp
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
struct Memory {
|
||||||
|
string type;
|
||||||
|
string size;
|
||||||
|
string content;
|
||||||
|
string manufacturer;
|
||||||
|
string architecture;
|
||||||
|
string identifier;
|
||||||
|
boolean Volatile;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Oscillator {
|
||||||
|
string frequency;
|
||||||
|
};
|
||||||
|
|
||||||
|
//variant meta-class
|
||||||
|
struct Component {
|
||||||
|
enum class Type : uint {
|
||||||
|
Memory,
|
||||||
|
Oscillator,
|
||||||
|
} type;
|
||||||
|
Memory memory;
|
||||||
|
Oscillator oscillator;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Game {
|
||||||
|
string sha256;
|
||||||
|
string region;
|
||||||
|
string revision;
|
||||||
|
string board;
|
||||||
|
string name;
|
||||||
|
string label;
|
||||||
|
string note;
|
||||||
|
vector<Component> components;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ListWindow : Window {
|
||||||
|
ListWindow();
|
||||||
|
auto quit() -> void;
|
||||||
|
auto reloadList() -> void;
|
||||||
|
auto updateWindow() -> void;
|
||||||
|
auto newDatabase() -> void;
|
||||||
|
auto loadDatabase(string) -> void;
|
||||||
|
auto saveDatabase(string) -> void;
|
||||||
|
auto appendGame(Game) -> void;
|
||||||
|
auto modifyGame(Game) -> void;
|
||||||
|
auto removeGame() -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool modified = false;
|
||||||
|
vector<Game> games;
|
||||||
|
string location;
|
||||||
|
|
||||||
|
MenuBar menuBar{this};
|
||||||
|
Menu fileMenu{&menuBar};
|
||||||
|
MenuItem newAction{&fileMenu};
|
||||||
|
MenuItem openAction{&fileMenu};
|
||||||
|
MenuItem saveAction{&fileMenu};
|
||||||
|
MenuItem saveAsAction{&fileMenu};
|
||||||
|
MenuSeparator quitSeparator{&fileMenu};
|
||||||
|
MenuItem quitAction{&fileMenu};
|
||||||
|
Menu helpMenu{&menuBar};
|
||||||
|
MenuItem aboutAction{&helpMenu};
|
||||||
|
|
||||||
|
HorizontalLayout layout{this};
|
||||||
|
TableView gameList{&layout, Size{~0, ~0}};
|
||||||
|
VerticalLayout controlLayout{&layout, Size{80, ~0}};
|
||||||
|
Button appendButton{&controlLayout, Size{~0, 0}};
|
||||||
|
Button modifyButton{&controlLayout, Size{~0, 0}};
|
||||||
|
Button removeButton{&controlLayout, Size{~0, 0}};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GameWindow : Window {
|
||||||
|
GameWindow();
|
||||||
|
auto show(Game = {}) -> void;
|
||||||
|
auto accept() -> void;
|
||||||
|
auto cancel() -> void;
|
||||||
|
auto reloadList() -> void;
|
||||||
|
auto updateWindow() -> void;
|
||||||
|
auto appendComponent(Component) -> void;
|
||||||
|
auto modifyComponent(Component) -> void;
|
||||||
|
auto removeComponent() -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool modified = false;
|
||||||
|
bool create = true;
|
||||||
|
Game game;
|
||||||
|
|
||||||
|
VerticalLayout layout{this};
|
||||||
|
HorizontalLayout hashLayout{&layout, Size{~0, 0}};
|
||||||
|
Label hashLabel{&hashLayout, Size{50, 0}};
|
||||||
|
LineEdit hashEdit{&hashLayout, Size{~0, 0}};
|
||||||
|
HorizontalLayout infoLayout{&layout, Size{~0, 0}};
|
||||||
|
Label regionLabel{&infoLayout, Size{50, 0}};
|
||||||
|
LineEdit regionEdit{&infoLayout, Size{~0, 0}};
|
||||||
|
Label revisionLabel{&infoLayout, Size{0, 0}};
|
||||||
|
LineEdit revisionEdit{&infoLayout, Size{~0, 0}};
|
||||||
|
Label boardLabel{&infoLayout, Size{0, 0}};
|
||||||
|
LineEdit boardEdit{&infoLayout, Size{~0, 0}, 0};
|
||||||
|
HorizontalLayout nameLayout{&layout, Size{~0, 0}};
|
||||||
|
Label nameLabel{&nameLayout, Size{50, 0}};
|
||||||
|
LineEdit nameEdit{&nameLayout, Size{~0, 0}};
|
||||||
|
HorizontalLayout labelLayout{&layout, Size{~0, 0}};
|
||||||
|
Label labelLabel{&labelLayout, Size{50, 0}};
|
||||||
|
LineEdit labelEdit{&labelLayout, Size{~0, 0}};
|
||||||
|
HorizontalLayout noteLayout{&layout, Size{~0, 0}};
|
||||||
|
Label noteLabel{¬eLayout, Size{50, 0}};
|
||||||
|
LineEdit noteEdit{¬eLayout, Size{~0, 0}};
|
||||||
|
HorizontalLayout lowerLayout{&layout, Size{~0, ~0}};
|
||||||
|
Label componentLabel{&lowerLayout, Size{50, ~0}};
|
||||||
|
TreeView componentTree{&lowerLayout, Size{~0, ~0}};
|
||||||
|
VerticalLayout controlLayout{&lowerLayout, Size{0, ~0}};
|
||||||
|
Button appendMemoryButton{&controlLayout, Size{80, 0}};
|
||||||
|
Button appendOscillatorButton{&controlLayout, Size{80, 0}};
|
||||||
|
Button modifyComponentButton{&controlLayout, Size{80, 0}};
|
||||||
|
Button removeComponentButton{&controlLayout, Size{80, 0}};
|
||||||
|
Widget controlSpacer{&controlLayout, Size{0, ~0}};
|
||||||
|
Button acceptButton{&controlLayout, Size{80, 0}};
|
||||||
|
Button cancelButton{&controlLayout, Size{80, 0}};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MemoryWindow : Window {
|
||||||
|
MemoryWindow();
|
||||||
|
auto show(Memory = {}) -> void;
|
||||||
|
auto accept() -> void;
|
||||||
|
auto cancel() -> void;
|
||||||
|
auto updateWindow() -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool modified = false;
|
||||||
|
bool create = true;
|
||||||
|
Memory memory;
|
||||||
|
|
||||||
|
VerticalLayout layout{this};
|
||||||
|
HorizontalLayout infoLayout{&layout, Size{~0, 0}};
|
||||||
|
Label typeLabel{&infoLayout, Size{80, 0}};
|
||||||
|
ComboEdit typeEdit{&infoLayout, Size{~0, 0}};
|
||||||
|
Label sizeLabel{&infoLayout, Size{0, 0}};
|
||||||
|
LineEdit sizeEdit{&infoLayout, Size{~0, 0}};
|
||||||
|
HorizontalLayout contentLayout{&layout, Size{~0, 0}};
|
||||||
|
Label contentLabel{&contentLayout, Size{80, 0}};
|
||||||
|
ComboEdit contentEdit{&contentLayout, Size{~0, 0}};
|
||||||
|
HorizontalLayout manufacturerLayout{&layout, Size{~0, 0}};
|
||||||
|
Label manufacturerLabel{&manufacturerLayout, Size{80, 0}};
|
||||||
|
LineEdit manufacturerEdit{&manufacturerLayout, Size{~0, 0}};
|
||||||
|
HorizontalLayout architectureLayout{&layout, Size{~0, 0}};
|
||||||
|
Label architectureLabel{&architectureLayout, Size{80, 0}};
|
||||||
|
LineEdit architectureEdit{&architectureLayout, Size{~0, 0}};
|
||||||
|
HorizontalLayout identifierLayout{&layout, Size{~0, 0}};
|
||||||
|
Label identifierLabel{&identifierLayout, Size{80, 0}};
|
||||||
|
LineEdit identifierEdit{&identifierLayout, Size{~0, 0}};
|
||||||
|
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
||||||
|
Widget controlSpacer{&controlLayout, Size{~0, 0}};
|
||||||
|
CheckLabel volatileOption{&controlLayout, Size{0, 0}};
|
||||||
|
Button acceptButton{&controlLayout, Size{80, 0}};
|
||||||
|
Button cancelButton{&controlLayout, Size{80, 0}};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OscillatorWindow : Window {
|
||||||
|
OscillatorWindow();
|
||||||
|
auto show(Oscillator = {}) -> void;
|
||||||
|
auto accept() -> void;
|
||||||
|
auto cancel() -> void;
|
||||||
|
auto updateWindow() -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool modified = false;
|
||||||
|
bool create = true;
|
||||||
|
Oscillator oscillator;
|
||||||
|
|
||||||
|
VerticalLayout layout{this};
|
||||||
|
HorizontalLayout frequencyLayout{&layout, Size{~0, 0}};
|
||||||
|
Label frequencyLabel{&frequencyLayout, Size{60, 0}};
|
||||||
|
LineEdit frequencyEdit{&frequencyLayout, Size{~0, 0}};
|
||||||
|
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
||||||
|
Widget controlSpacer{&controlLayout, Size{~0, 0}};
|
||||||
|
Button acceptButton{&controlLayout, Size{80, 0}};
|
||||||
|
Button cancelButton{&controlLayout, Size{80, 0}};
|
||||||
|
};
|
2
genius/obj/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*.o
|
||||||
|
*.d
|
1
genius/out/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
genius
|
@@ -1,16 +1,15 @@
|
|||||||
build := optimize
|
target := bsnes
|
||||||
include ../nall/GNUmakefile
|
|
||||||
|
|
||||||
binary := application
|
binary := application
|
||||||
target := tomoko
|
build := performance
|
||||||
objects := libco emulator audio video resource
|
openmp := true
|
||||||
|
|
||||||
flags += -I. -I..
|
flags += -I. -I..
|
||||||
|
|
||||||
|
nall.path := ../nall
|
||||||
|
include $(nall.path)/GNUmakefile
|
||||||
|
|
||||||
ifeq ($(platform),windows)
|
ifeq ($(platform),windows)
|
||||||
link += $(if $(call streq,$(console),true),-mconsole,-mwindows)
|
|
||||||
ifeq ($(binary),application)
|
ifeq ($(binary),application)
|
||||||
link += -mthreads -lpthread -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32
|
link += -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32
|
||||||
link += -Wl,-enable-auto-import
|
link += -Wl,-enable-auto-import
|
||||||
link += -Wl,-enable-runtime-pseudo-reloc
|
link += -Wl,-enable-runtime-pseudo-reloc
|
||||||
else ifeq ($(binary),library)
|
else ifeq ($(binary),library)
|
||||||
@@ -23,8 +22,6 @@ else ifeq ($(platform),macos)
|
|||||||
link += -dynamiclib
|
link += -dynamiclib
|
||||||
endif
|
endif
|
||||||
else ifneq ($(filter $(platform),linux bsd),)
|
else ifneq ($(filter $(platform),linux bsd),)
|
||||||
flags += -fopenmp
|
|
||||||
link += -fopenmp
|
|
||||||
ifeq ($(binary),application)
|
ifeq ($(binary),application)
|
||||||
flags += -march=native
|
flags += -march=native
|
||||||
link += -Wl,-export-dynamic
|
link += -Wl,-export-dynamic
|
||||||
@@ -37,29 +34,66 @@ else
|
|||||||
$(error "unsupported platform")
|
$(error "unsupported platform")
|
||||||
endif
|
endif
|
||||||
|
|
||||||
compile = \
|
objects := libco emulator
|
||||||
$(strip \
|
|
||||||
$(if $(filter %.c,$<), \
|
|
||||||
$(compiler) $(cflags) $(flags) $1 -c $< -o $@, \
|
|
||||||
$(if $(filter %.cpp,$<), \
|
|
||||||
$(compiler) $(cppflags) $(flags) $1 -c $< -o $@ \
|
|
||||||
) \
|
|
||||||
) \
|
|
||||||
)
|
|
||||||
|
|
||||||
%.o: $<; $(call compile)
|
obj/libco.o: ../libco/libco.c
|
||||||
|
obj/emulator.o: emulator/emulator.cpp
|
||||||
|
|
||||||
all: build;
|
ifeq ($(target),higan)
|
||||||
|
cores := fc sfc ms md pce msx gb gba ws ngp
|
||||||
|
endif
|
||||||
|
|
||||||
obj/libco.o: ../libco/libco.c $(call rwildcard,../libco)
|
ifeq ($(target),bsnes)
|
||||||
obj/emulator.o: emulator/emulator.cpp $(call rwildcard,emulator)
|
cores := sfc gb
|
||||||
obj/audio.o: audio/audio.cpp $(call rwildcard,audio)
|
endif
|
||||||
obj/video.o: video/video.cpp $(call rwildcard,video)
|
|
||||||
obj/resource.o: resource/resource.cpp $(call rwildcard,resource)
|
|
||||||
|
|
||||||
|
ifneq ($(filter $(cores),fc),)
|
||||||
|
include fc/GNUmakefile
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(filter $(cores),sfc),)
|
||||||
|
include sfc/GNUmakefile
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(filter $(cores),ms),)
|
||||||
|
include ms/GNUmakefile
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(filter $(cores),md),)
|
||||||
|
include md/GNUmakefile
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(filter $(cores),pce),)
|
||||||
|
include pce/GNUmakefile
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(filter $(cores),msx),)
|
||||||
|
include msx/GNUmakefile
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(filter $(cores),gb),)
|
||||||
|
include gb/GNUmakefile
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(filter $(cores),gba),)
|
||||||
|
include gba/GNUmakefile
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(filter $(cores),ws),)
|
||||||
|
include ws/GNUmakefile
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(filter $(cores),ngp),)
|
||||||
|
include ngp/GNUmakefile
|
||||||
|
endif
|
||||||
|
|
||||||
|
include processor/GNUmakefile
|
||||||
|
|
||||||
|
flags += $(foreach c,$(call strupper,$(cores)),-DCORE_$c)
|
||||||
ui := target-$(target)
|
ui := target-$(target)
|
||||||
include $(ui)/GNUmakefile
|
include $(ui)/GNUmakefile
|
||||||
|
-include obj/*.d
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
-@$(call delete,out/*)
|
$(call delete,obj/*)
|
||||||
-@$(call delete,obj/*)
|
$(call delete,out/*)
|
||||||
|
@@ -1,70 +0,0 @@
|
|||||||
auto Stream::reset(uint channels_, double inputFrequency, double outputFrequency) -> void {
|
|
||||||
this->inputFrequency = inputFrequency;
|
|
||||||
this->outputFrequency = outputFrequency;
|
|
||||||
|
|
||||||
channels.reset();
|
|
||||||
channels.resize(channels_);
|
|
||||||
|
|
||||||
for(auto& channel : channels) {
|
|
||||||
channel.filters.reset();
|
|
||||||
channel.resampler.reset(inputFrequency, outputFrequency);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Stream::setFrequency(double inputFrequency, maybe<double> outputFrequency) -> void {
|
|
||||||
this->inputFrequency = inputFrequency;
|
|
||||||
if(outputFrequency) this->outputFrequency = outputFrequency();
|
|
||||||
|
|
||||||
for(auto& channel : channels) {
|
|
||||||
channel.resampler.reset(this->inputFrequency, this->outputFrequency);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Stream::addFilter(Filter::Order order, Filter::Type type, double cutoffFrequency, uint passes) -> void {
|
|
||||||
for(auto& channel : channels) {
|
|
||||||
for(auto pass : range(passes)) {
|
|
||||||
Filter filter{order};
|
|
||||||
|
|
||||||
if(order == Filter::Order::First) {
|
|
||||||
DSP::IIR::OnePole::Type _type;
|
|
||||||
if(type == Filter::Type::LowPass) _type = DSP::IIR::OnePole::Type::LowPass;
|
|
||||||
if(type == Filter::Type::HighPass) _type = DSP::IIR::OnePole::Type::HighPass;
|
|
||||||
filter.onePole.reset(_type, cutoffFrequency, inputFrequency);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(order == Filter::Order::Second) {
|
|
||||||
DSP::IIR::Biquad::Type _type;
|
|
||||||
if(type == Filter::Type::LowPass) _type = DSP::IIR::Biquad::Type::LowPass;
|
|
||||||
if(type == Filter::Type::HighPass) _type = DSP::IIR::Biquad::Type::HighPass;
|
|
||||||
double q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
|
|
||||||
filter.biquad.reset(_type, cutoffFrequency, inputFrequency, q);
|
|
||||||
}
|
|
||||||
|
|
||||||
channel.filters.append(filter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Stream::pending() const -> bool {
|
|
||||||
return channels && channels[0].resampler.pending();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Stream::read(double samples[]) -> uint {
|
|
||||||
for(auto c : range(channels)) samples[c] = channels[c].resampler.read();
|
|
||||||
return channels.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Stream::write(const double samples[]) -> void {
|
|
||||||
for(auto c : range(channels)) {
|
|
||||||
double sample = samples[c] + 1e-25; //constant offset used to suppress denormals
|
|
||||||
for(auto& filter : channels[c].filters) {
|
|
||||||
switch(filter.order) {
|
|
||||||
case Filter::Order::First: sample = filter.onePole.process(sample); break;
|
|
||||||
case Filter::Order::Second: sample = filter.biquad.process(sample); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
channels[c].resampler.write(sample);
|
|
||||||
}
|
|
||||||
|
|
||||||
audio.process();
|
|
||||||
}
|
|
@@ -1,34 +1,16 @@
|
|||||||
#include <emulator/emulator.hpp>
|
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
|
|
||||||
#include "stream.cpp"
|
#include "stream.cpp"
|
||||||
Audio audio;
|
Audio audio;
|
||||||
|
|
||||||
auto Audio::reset(maybe<uint> channels_, maybe<double> frequency_) -> void {
|
Audio::~Audio() {
|
||||||
interface = nullptr;
|
reset(nullptr);
|
||||||
|
|
||||||
if(channels_) channels = channels_();
|
|
||||||
if(frequency_) frequency = frequency_();
|
|
||||||
|
|
||||||
streams.reset();
|
|
||||||
reverb.reset();
|
|
||||||
|
|
||||||
reverb.resize(channels);
|
|
||||||
for(auto c : range(channels)) {
|
|
||||||
reverb[c].resize(7);
|
|
||||||
reverb[c][0].resize(1229);
|
|
||||||
reverb[c][1].resize(1559);
|
|
||||||
reverb[c][2].resize(1907);
|
|
||||||
reverb[c][3].resize(4057);
|
|
||||||
reverb[c][4].resize(8117);
|
|
||||||
reverb[c][5].resize(8311);
|
|
||||||
reverb[c][6].resize(9931);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Audio::setInterface(Interface* interface) -> void {
|
auto Audio::reset(Interface* interface) -> void {
|
||||||
this->interface = interface;
|
this->interface = interface;
|
||||||
|
streams.reset();
|
||||||
|
channels = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Audio::setFrequency(double frequency) -> void {
|
auto Audio::setFrequency(double frequency) -> void {
|
||||||
@@ -46,11 +28,8 @@ auto Audio::setBalance(double balance) -> void {
|
|||||||
this->balance = balance;
|
this->balance = balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Audio::setReverb(bool enabled) -> void {
|
|
||||||
this->reverbEnable = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Audio::createStream(uint channels, double frequency) -> shared_pointer<Stream> {
|
auto Audio::createStream(uint channels, double frequency) -> shared_pointer<Stream> {
|
||||||
|
this->channels = max(this->channels, channels);
|
||||||
shared_pointer<Stream> stream = new Stream;
|
shared_pointer<Stream> stream = new Stream;
|
||||||
stream->reset(channels, frequency, this->frequency);
|
stream->reset(channels, frequency, this->frequency);
|
||||||
streams.append(stream);
|
streams.append(stream);
|
||||||
@@ -58,7 +37,7 @@ auto Audio::createStream(uint channels, double frequency) -> shared_pointer<Stre
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto Audio::process() -> void {
|
auto Audio::process() -> void {
|
||||||
while(true) {
|
while(streams) {
|
||||||
for(auto& stream : streams) {
|
for(auto& stream : streams) {
|
||||||
if(!stream->pending()) return;
|
if(!stream->pending()) return;
|
||||||
}
|
}
|
||||||
@@ -67,7 +46,7 @@ auto Audio::process() -> void {
|
|||||||
for(auto& sample : samples) sample = 0.0;
|
for(auto& sample : samples) sample = 0.0;
|
||||||
|
|
||||||
for(auto& stream : streams) {
|
for(auto& stream : streams) {
|
||||||
double buffer[16];
|
double buffer[channels];
|
||||||
uint length = stream->read(buffer), offset = 0;
|
uint length = stream->read(buffer), offset = 0;
|
||||||
|
|
||||||
for(auto& sample : samples) {
|
for(auto& sample : samples) {
|
||||||
@@ -78,13 +57,6 @@ auto Audio::process() -> void {
|
|||||||
|
|
||||||
for(auto c : range(channels)) {
|
for(auto c : range(channels)) {
|
||||||
samples[c] = max(-1.0, min(+1.0, samples[c] * volume));
|
samples[c] = max(-1.0, min(+1.0, samples[c] * volume));
|
||||||
|
|
||||||
if(reverbEnable) {
|
|
||||||
samples[c] *= 0.125;
|
|
||||||
for(auto n : range(7)) samples[c] += 0.125 * reverb[c][n].last();
|
|
||||||
for(auto n : range(7)) reverb[c][n].write(samples[c]);
|
|
||||||
samples[c] *= 8.000;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(channels == 2) {
|
if(channels == 2) {
|
||||||
@@ -92,7 +64,7 @@ auto Audio::process() -> void {
|
|||||||
if(balance > 0.0) samples[0] *= 1.0 - balance;
|
if(balance > 0.0) samples[0] *= 1.0 - balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
platform->audioSample(samples, channels);
|
platform->audioFrame(samples, channels);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <nall/dsp/iir/dc-removal.hpp>
|
||||||
#include <nall/dsp/iir/one-pole.hpp>
|
#include <nall/dsp/iir/one-pole.hpp>
|
||||||
#include <nall/dsp/iir/biquad.hpp>
|
#include <nall/dsp/iir/biquad.hpp>
|
||||||
#include <nall/dsp/resampler/cubic.hpp>
|
#include <nall/dsp/resampler/cubic.hpp>
|
||||||
@@ -12,13 +13,12 @@ struct Filter;
|
|||||||
struct Stream;
|
struct Stream;
|
||||||
|
|
||||||
struct Audio {
|
struct Audio {
|
||||||
auto reset(maybe<uint> channels = nothing, maybe<double> frequency = nothing) -> void;
|
~Audio();
|
||||||
auto setInterface(Interface* interface) -> void;
|
auto reset(Interface* interface) -> void;
|
||||||
|
|
||||||
auto setFrequency(double frequency) -> void;
|
auto setFrequency(double frequency) -> void;
|
||||||
auto setVolume(double volume) -> void;
|
auto setVolume(double volume) -> void;
|
||||||
auto setBalance(double balance) -> void;
|
auto setBalance(double balance) -> void;
|
||||||
auto setReverb(bool enabled) -> void;
|
|
||||||
|
|
||||||
auto createStream(uint channels, double frequency) -> shared_pointer<Stream>;
|
auto createStream(uint channels, double frequency) -> shared_pointer<Stream>;
|
||||||
|
|
||||||
@@ -29,24 +29,22 @@ private:
|
|||||||
vector<shared_pointer<Stream>> streams;
|
vector<shared_pointer<Stream>> streams;
|
||||||
|
|
||||||
uint channels = 0;
|
uint channels = 0;
|
||||||
double frequency = 0.0;
|
double frequency = 48000.0;
|
||||||
|
|
||||||
double volume = 1.0;
|
double volume = 1.0;
|
||||||
double balance = 0.0;
|
double balance = 0.0;
|
||||||
|
|
||||||
bool reverbEnable = false;
|
|
||||||
vector<vector<queue<double>>> reverb;
|
|
||||||
|
|
||||||
friend class Stream;
|
friend class Stream;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Filter {
|
struct Filter {
|
||||||
enum class Order : uint { First, Second };
|
enum class Mode : uint { DCRemoval, OnePole, Biquad } mode;
|
||||||
enum class Type : uint { LowPass, HighPass };
|
enum class Type : uint { None, LowPass, HighPass } type;
|
||||||
|
enum class Order : uint { None, First, Second } order;
|
||||||
|
|
||||||
Order order;
|
DSP::IIR::DCRemoval dcRemoval;
|
||||||
DSP::IIR::OnePole onePole; //first-order
|
DSP::IIR::OnePole onePole;
|
||||||
DSP::IIR::Biquad biquad; //second-order
|
DSP::IIR::Biquad biquad;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Stream {
|
struct Stream {
|
||||||
@@ -54,7 +52,9 @@ struct Stream {
|
|||||||
|
|
||||||
auto setFrequency(double inputFrequency, maybe<double> outputFrequency = nothing) -> void;
|
auto setFrequency(double inputFrequency, maybe<double> outputFrequency = nothing) -> void;
|
||||||
|
|
||||||
auto addFilter(Filter::Order order, Filter::Type type, double cutoffFrequency, uint passes = 1) -> 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 -> bool;
|
||||||
auto read(double samples[]) -> uint;
|
auto read(double samples[]) -> uint;
|
||||||
@@ -68,6 +68,7 @@ struct Stream {
|
|||||||
private:
|
private:
|
||||||
struct Channel {
|
struct Channel {
|
||||||
vector<Filter> filters;
|
vector<Filter> filters;
|
||||||
|
vector<DSP::IIR::Biquad> nyquist;
|
||||||
DSP::Resampler::Cubic resampler;
|
DSP::Resampler::Cubic resampler;
|
||||||
};
|
};
|
||||||
vector<Channel> channels;
|
vector<Channel> channels;
|
106
higan/emulator/audio/stream.cpp
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
auto Stream::reset(uint channelCount, double inputFrequency, double outputFrequency) -> void {
|
||||||
|
channels.reset();
|
||||||
|
channels.resize(channelCount);
|
||||||
|
|
||||||
|
for(auto& channel : channels) {
|
||||||
|
channel.filters.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
setFrequency(inputFrequency, outputFrequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Stream::setFrequency(double inputFrequency, maybe<double> outputFrequency) -> void {
|
||||||
|
this->inputFrequency = inputFrequency;
|
||||||
|
if(outputFrequency) this->outputFrequency = outputFrequency();
|
||||||
|
|
||||||
|
for(auto& channel : channels) {
|
||||||
|
channel.nyquist.reset();
|
||||||
|
channel.resampler.reset(this->inputFrequency, this->outputFrequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this->inputFrequency >= this->outputFrequency * 2) {
|
||||||
|
//add a low-pass filter to prevent aliasing during resampling
|
||||||
|
double cutoffFrequency = min(25000.0, this->outputFrequency / 2.0 - 2000.0);
|
||||||
|
for(auto& channel : channels) {
|
||||||
|
uint passes = 3;
|
||||||
|
for(uint pass : range(passes)) {
|
||||||
|
DSP::IIR::Biquad filter;
|
||||||
|
double q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
|
||||||
|
filter.reset(DSP::IIR::Biquad::Type::LowPass, cutoffFrequency, this->inputFrequency, q);
|
||||||
|
channel.nyquist.append(filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Stream::addDCRemovalFilter() -> void {
|
||||||
|
return; //todo: test to ensure this is desirable before enabling
|
||||||
|
for(auto& channel : channels) {
|
||||||
|
Filter filter{Filter::Mode::DCRemoval, Filter::Type::None, Filter::Order::None};
|
||||||
|
channel.filters.append(filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Stream::addLowPassFilter(double cutoffFrequency, Filter::Order order, uint passes) -> void {
|
||||||
|
for(auto& channel : channels) {
|
||||||
|
for(uint pass : range(passes)) {
|
||||||
|
if(order == Filter::Order::First) {
|
||||||
|
Filter filter{Filter::Mode::OnePole, Filter::Type::LowPass, Filter::Order::First};
|
||||||
|
filter.onePole.reset(DSP::IIR::OnePole::Type::LowPass, cutoffFrequency, inputFrequency);
|
||||||
|
channel.filters.append(filter);
|
||||||
|
}
|
||||||
|
if(order == Filter::Order::Second) {
|
||||||
|
Filter filter{Filter::Mode::Biquad, Filter::Type::LowPass, Filter::Order::Second};
|
||||||
|
double q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
|
||||||
|
filter.biquad.reset(DSP::IIR::Biquad::Type::LowPass, cutoffFrequency, inputFrequency, q);
|
||||||
|
channel.filters.append(filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Stream::addHighPassFilter(double cutoffFrequency, Filter::Order order, uint passes) -> void {
|
||||||
|
for(auto& channel : channels) {
|
||||||
|
for(uint pass : range(passes)) {
|
||||||
|
if(order == Filter::Order::First) {
|
||||||
|
Filter filter{Filter::Mode::OnePole, Filter::Type::HighPass, Filter::Order::First};
|
||||||
|
filter.onePole.reset(DSP::IIR::OnePole::Type::HighPass, cutoffFrequency, inputFrequency);
|
||||||
|
channel.filters.append(filter);
|
||||||
|
}
|
||||||
|
if(order == Filter::Order::Second) {
|
||||||
|
Filter filter{Filter::Mode::Biquad, Filter::Type::HighPass, Filter::Order::Second};
|
||||||
|
double q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
|
||||||
|
filter.biquad.reset(DSP::IIR::Biquad::Type::HighPass, cutoffFrequency, inputFrequency, q);
|
||||||
|
channel.filters.append(filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Stream::pending() const -> bool {
|
||||||
|
return channels && channels[0].resampler.pending();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Stream::read(double samples[]) -> uint {
|
||||||
|
for(uint c : range(channels.size())) samples[c] = channels[c].resampler.read();
|
||||||
|
return channels.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Stream::write(const double samples[]) -> void {
|
||||||
|
for(auto c : range(channels.size())) {
|
||||||
|
double sample = samples[c] + 1e-25; //constant offset used to suppress denormals
|
||||||
|
for(auto& filter : channels[c].filters) {
|
||||||
|
switch(filter.mode) {
|
||||||
|
case Filter::Mode::DCRemoval: sample = filter.dcRemoval.process(sample); break;
|
||||||
|
case Filter::Mode::OnePole: sample = filter.onePole.process(sample); break;
|
||||||
|
case Filter::Mode::Biquad: sample = filter.biquad.process(sample); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(auto& filter : channels[c].nyquist) {
|
||||||
|
sample = filter.process(sample);
|
||||||
|
}
|
||||||
|
channels[c].resampler.write(sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
audio.process();
|
||||||
|
}
|
@@ -17,11 +17,11 @@ struct Cheat {
|
|||||||
codes.reset();
|
codes.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto append(uint addr, uint data, maybe<uint> comp = nothing) -> void {
|
auto append(uint addr, uint data, maybe<uint> comp = {}) -> void {
|
||||||
codes.append({addr, data, comp});
|
codes.append({addr, data, comp});
|
||||||
}
|
}
|
||||||
|
|
||||||
auto assign(const string_vector& list) -> void {
|
auto assign(const vector<string>& list) -> void {
|
||||||
reset();
|
reset();
|
||||||
for(auto& entry : list) {
|
for(auto& entry : list) {
|
||||||
for(auto code : entry.split("+")) {
|
for(auto code : entry.split("+")) {
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
#include <emulator/emulator.hpp>
|
#include <emulator/emulator.hpp>
|
||||||
|
|
||||||
|
#include <emulator/audio/audio.cpp>
|
||||||
|
#include <emulator/video/video.cpp>
|
||||||
|
#include <emulator/resource/resource.cpp>
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
|
|
||||||
Platform* platform = nullptr;
|
Platform* platform = nullptr;
|
||||||
|
@@ -1,24 +1,43 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <nall/nall.hpp>
|
#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>
|
||||||
|
#include <nall/image.hpp>
|
||||||
|
#include <nall/literals.hpp>
|
||||||
|
#include <nall/random.hpp>
|
||||||
|
#include <nall/serializer.hpp>
|
||||||
|
#include <nall/shared-pointer.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
#include <nall/traits.hpp>
|
||||||
|
#include <nall/unique-pointer.hpp>
|
||||||
|
#include <nall/vector.hpp>
|
||||||
#include <nall/vfs.hpp>
|
#include <nall/vfs.hpp>
|
||||||
|
#include <nall/hash/crc32.hpp>
|
||||||
|
#include <nall/hash/sha256.hpp>
|
||||||
using namespace nall;
|
using namespace nall;
|
||||||
|
|
||||||
#include "types.hpp"
|
|
||||||
#include <libco/libco.h>
|
#include <libco/libco.h>
|
||||||
#include <audio/audio.hpp>
|
#include <emulator/types.hpp>
|
||||||
#include <video/video.hpp>
|
#include <emulator/memory/readable.hpp>
|
||||||
#include <resource/resource.hpp>
|
#include <emulator/memory/writable.hpp>
|
||||||
|
#include <emulator/audio/audio.hpp>
|
||||||
|
#include <emulator/video/video.hpp>
|
||||||
|
#include <emulator/resource/resource.hpp>
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
static const string Name = "higan";
|
||||||
static const string Version = "105";
|
static const string Version = "107";
|
||||||
static const string Author = "byuu";
|
static const string Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "https://byuu.org/";
|
static const string Website = "https://byuu.org/";
|
||||||
|
|
||||||
//incremented only when serialization format changes
|
//incremented only when serialization format changes
|
||||||
static const string SerializerVersion = "104";
|
static const string SerializerVersion = "107";
|
||||||
|
|
||||||
namespace Constants {
|
namespace Constants {
|
||||||
namespace Colorburst {
|
namespace Colorburst {
|
||||||
@@ -38,3 +57,4 @@ namespace Emulator {
|
|||||||
|
|
||||||
#include "platform.hpp"
|
#include "platform.hpp"
|
||||||
#include "interface.hpp"
|
#include "interface.hpp"
|
||||||
|
#include "game.hpp"
|
||||||
|
110
higan/emulator/game.hpp
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Emulator {
|
||||||
|
|
||||||
|
struct Game {
|
||||||
|
struct Memory;
|
||||||
|
struct Oscillator;
|
||||||
|
|
||||||
|
inline auto load(string_view) -> void;
|
||||||
|
inline auto memory(Markup::Node) -> maybe<Memory>;
|
||||||
|
inline auto oscillator(natural = 0) -> maybe<Oscillator>;
|
||||||
|
|
||||||
|
struct Memory {
|
||||||
|
Memory() = default;
|
||||||
|
inline Memory(Markup::Node);
|
||||||
|
explicit operator bool() const { return (bool)type; }
|
||||||
|
inline auto name() const -> string;
|
||||||
|
|
||||||
|
string type;
|
||||||
|
natural size;
|
||||||
|
string content;
|
||||||
|
string manufacturer;
|
||||||
|
string architecture;
|
||||||
|
string identifier;
|
||||||
|
boolean nonVolatile;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Oscillator {
|
||||||
|
Oscillator() = default;
|
||||||
|
inline Oscillator(Markup::Node);
|
||||||
|
explicit operator bool() const { return frequency; }
|
||||||
|
|
||||||
|
natural frequency;
|
||||||
|
};
|
||||||
|
|
||||||
|
Markup::Node document;
|
||||||
|
string sha256;
|
||||||
|
string label;
|
||||||
|
string name;
|
||||||
|
string region;
|
||||||
|
string revision;
|
||||||
|
string board;
|
||||||
|
vector<Memory> memoryList;
|
||||||
|
vector<Oscillator> oscillatorList;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto Game::load(string_view text) -> void {
|
||||||
|
document = BML::unserialize(text);
|
||||||
|
|
||||||
|
sha256 = document["game/sha256"].text();
|
||||||
|
label = document["game/label"].text();
|
||||||
|
name = document["game/name"].text();
|
||||||
|
region = document["game/region"].text();
|
||||||
|
revision = document["game/revision"].text();
|
||||||
|
board = document["game/board"].text();
|
||||||
|
|
||||||
|
for(auto node : document.find("game/board/memory")) {
|
||||||
|
memoryList.append(Memory{node});
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto node : document.find("game/board/oscillator")) {
|
||||||
|
oscillatorList.append(Oscillator{node});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Game::memory(Markup::Node node) -> maybe<Memory> {
|
||||||
|
if(!node) return nothing;
|
||||||
|
for(auto& memory : memoryList) {
|
||||||
|
auto type = node["type"].text();
|
||||||
|
auto size = node["size"].natural();
|
||||||
|
auto content = node["content"].text();
|
||||||
|
auto manufacturer = node["manufacturer"].text();
|
||||||
|
auto architecture = node["architecture"].text();
|
||||||
|
auto identifier = node["identifier"].text();
|
||||||
|
if(type && type != memory.type) continue;
|
||||||
|
if(size && size != memory.size) continue;
|
||||||
|
if(content && content != memory.content) continue;
|
||||||
|
if(manufacturer && manufacturer != memory.manufacturer) continue;
|
||||||
|
if(architecture && architecture != memory.architecture) continue;
|
||||||
|
if(identifier && identifier != memory.identifier) continue;
|
||||||
|
return memory;
|
||||||
|
}
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Game::oscillator(natural index) -> maybe<Oscillator> {
|
||||||
|
if(index < oscillatorList.size()) return oscillatorList[index];
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::Memory::Memory(Markup::Node node) {
|
||||||
|
type = node["type"].text();
|
||||||
|
size = node["size"].natural();
|
||||||
|
content = node["content"].text();
|
||||||
|
manufacturer = node["manufacturer"].text();
|
||||||
|
architecture = node["architecture"].text();
|
||||||
|
identifier = node["identifier"].text();
|
||||||
|
nonVolatile = !(bool)node["volatile"];
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Game::Memory::name() const -> string {
|
||||||
|
if(architecture) return string{architecture, ".", content, ".", type}.downcase();
|
||||||
|
return string{content, ".", type}.downcase();
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::Oscillator::Oscillator(Markup::Node node) {
|
||||||
|
frequency = node["frequency"].natural();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -6,79 +6,96 @@ struct Interface {
|
|||||||
struct Information {
|
struct Information {
|
||||||
string manufacturer;
|
string manufacturer;
|
||||||
string name;
|
string name;
|
||||||
bool overscan;
|
string extension;
|
||||||
} information;
|
bool resettable = false;
|
||||||
|
|
||||||
struct Medium {
|
|
||||||
uint id;
|
|
||||||
string name;
|
|
||||||
string type; //extension
|
|
||||||
};
|
|
||||||
vector<Medium> media;
|
|
||||||
|
|
||||||
struct Device {
|
|
||||||
uint id;
|
|
||||||
string name;
|
|
||||||
struct Input {
|
|
||||||
uint type; //0 = digital, 1 = analog (relative), 2 = rumble
|
|
||||||
string name;
|
|
||||||
};
|
|
||||||
vector<Input> inputs;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Port {
|
struct Display {
|
||||||
uint id;
|
struct Type { enum : uint {
|
||||||
|
CRT,
|
||||||
|
LCD,
|
||||||
|
};};
|
||||||
|
uint id = 0;
|
||||||
string name;
|
string name;
|
||||||
vector<Device> devices;
|
uint type = 0;
|
||||||
};
|
uint colors = 0;
|
||||||
vector<Port> ports;
|
|
||||||
|
|
||||||
//information
|
|
||||||
virtual auto manifest() -> string = 0;
|
|
||||||
virtual auto title() -> string = 0;
|
|
||||||
|
|
||||||
struct VideoInformation {
|
|
||||||
uint width = 0;
|
uint width = 0;
|
||||||
uint height = 0;
|
uint height = 0;
|
||||||
uint internalWidth = 0;
|
uint internalWidth = 0;
|
||||||
uint internalHeight = 0;
|
uint internalHeight = 0;
|
||||||
double aspectCorrection = 0;
|
double aspectCorrection = 0;
|
||||||
double refreshRate = 0;
|
|
||||||
};
|
};
|
||||||
virtual auto videoInformation() -> VideoInformation = 0;
|
|
||||||
virtual auto videoColors() -> uint32 = 0;
|
|
||||||
virtual auto videoColor(uint32 color) -> uint64 = 0;
|
|
||||||
|
|
||||||
//media interface
|
struct Port {
|
||||||
|
uint id;
|
||||||
|
string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Device {
|
||||||
|
uint id;
|
||||||
|
string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Input {
|
||||||
|
struct Type { enum : uint {
|
||||||
|
Hat,
|
||||||
|
Button,
|
||||||
|
Trigger,
|
||||||
|
Control,
|
||||||
|
Axis,
|
||||||
|
Rumble,
|
||||||
|
};};
|
||||||
|
|
||||||
|
uint type;
|
||||||
|
string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
//information
|
||||||
|
virtual auto information() -> Information { return {}; }
|
||||||
|
|
||||||
|
virtual auto display() -> Display { return {}; }
|
||||||
|
virtual auto color(uint32 color) -> uint64 { return 0; }
|
||||||
|
|
||||||
|
//game interface
|
||||||
virtual auto loaded() -> bool { return false; }
|
virtual auto loaded() -> bool { return false; }
|
||||||
virtual auto sha256() -> string { return ""; }
|
virtual auto hashes() -> vector<string> { return {}; }
|
||||||
virtual auto load(uint id) -> bool { return false; }
|
virtual auto manifests() -> vector<string> { return {}; }
|
||||||
|
virtual auto titles() -> vector<string> { return {}; }
|
||||||
|
virtual auto load() -> bool { return false; }
|
||||||
virtual auto save() -> void {}
|
virtual auto save() -> void {}
|
||||||
virtual auto unload() -> void {}
|
virtual auto unload() -> void {}
|
||||||
|
|
||||||
//system interface
|
//system interface
|
||||||
|
virtual auto ports() -> vector<Port> { return {}; }
|
||||||
|
virtual auto devices(uint port) -> vector<Device> { return {}; }
|
||||||
|
virtual auto inputs(uint device) -> vector<Input> { return {}; }
|
||||||
|
virtual auto connected(uint port) -> uint { return 0; }
|
||||||
virtual auto connect(uint port, uint device) -> void {}
|
virtual auto connect(uint port, uint device) -> void {}
|
||||||
virtual auto power() -> void {}
|
virtual auto power() -> void {}
|
||||||
|
virtual auto reset() -> void {}
|
||||||
virtual auto run() -> void {}
|
virtual auto run() -> void {}
|
||||||
|
|
||||||
//time functions
|
//time functions
|
||||||
virtual auto rtc() -> bool { return false; }
|
virtual auto rtc() -> bool { return false; }
|
||||||
virtual auto rtcSynchronize() -> void {}
|
virtual auto synchronize(uint64 timestamp = 0) -> void {}
|
||||||
|
|
||||||
//state functions
|
//state functions
|
||||||
virtual auto serialize() -> serializer = 0;
|
virtual auto serialize() -> serializer { return {}; }
|
||||||
virtual auto unserialize(serializer&) -> bool = 0;
|
virtual auto unserialize(serializer&) -> bool { return false; }
|
||||||
|
|
||||||
//cheat functions
|
//cheat functions
|
||||||
virtual auto cheatSet(const string_vector& = {}) -> void {}
|
virtual auto cheats(const vector<string>& = {}) -> void {}
|
||||||
|
|
||||||
|
//configuration
|
||||||
|
virtual auto configuration() -> string { return {}; }
|
||||||
|
virtual auto configuration(string name) -> string { return {}; }
|
||||||
|
virtual auto configure(string configuration = "") -> bool { return false; }
|
||||||
|
virtual auto configure(string name, string value) -> bool { return false; }
|
||||||
|
|
||||||
//settings
|
//settings
|
||||||
virtual auto cap(const string& name) -> bool { return false; }
|
virtual auto cap(const string& name) -> bool { return false; }
|
||||||
virtual auto get(const string& name) -> any { return {}; }
|
virtual auto get(const string& name) -> any { return {}; }
|
||||||
virtual auto set(const string& name, const any& value) -> bool { return false; }
|
virtual auto set(const string& name, const any& value) -> bool { return false; }
|
||||||
|
|
||||||
//shared functions
|
|
||||||
auto videoColor(uint16 r, uint16 g, uint16 b) -> uint32;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
30
higan/emulator/memory/memory.hpp
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Emulator::Memory {
|
||||||
|
|
||||||
|
inline auto mirror(uint address, uint size) -> uint {
|
||||||
|
if(size == 0) return 0;
|
||||||
|
uint base = 0;
|
||||||
|
uint mask = 1 << 31;
|
||||||
|
while(address >= size) {
|
||||||
|
while(!(address & mask)) mask >>= 1;
|
||||||
|
address -= mask;
|
||||||
|
if(size > mask) {
|
||||||
|
size -= mask;
|
||||||
|
base += mask;
|
||||||
|
}
|
||||||
|
mask >>= 1;
|
||||||
|
}
|
||||||
|
return base + address;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto reduce(uint address, uint mask) -> uint {
|
||||||
|
while(mask) {
|
||||||
|
uint bits = (mask & -mask) - 1;
|
||||||
|
address = address >> 1 & ~bits | address & bits;
|
||||||
|
mask = (mask & mask - 1) >> 1;
|
||||||
|
}
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
63
higan/emulator/memory/readable.hpp
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <emulator/memory/memory.hpp>
|
||||||
|
|
||||||
|
namespace Emulator::Memory {
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct Readable {
|
||||||
|
~Readable() { reset(); }
|
||||||
|
|
||||||
|
inline auto reset() -> void {
|
||||||
|
delete[] self.data;
|
||||||
|
self.data = nullptr;
|
||||||
|
self.size = 0;
|
||||||
|
self.mask = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto allocate(uint size, T fill = ~0ull) -> void {
|
||||||
|
if(!size) return reset();
|
||||||
|
delete[] self.data;
|
||||||
|
self.size = size;
|
||||||
|
self.mask = bit::round(self.size) - 1;
|
||||||
|
self.data = new T[self.mask + 1];
|
||||||
|
memory::fill<T>(self.data, self.mask + 1, fill);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto load(vfs::shared::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 {
|
||||||
|
fp->write(self.data, self.size * sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator bool() const { return (bool)self.data; }
|
||||||
|
inline auto data() const -> const T* { return self.data; }
|
||||||
|
inline auto size() const -> uint { return self.size; }
|
||||||
|
inline auto mask() const -> uint { return self.mask; }
|
||||||
|
|
||||||
|
inline auto operator[](uint address) const -> T { return self.data[address & self.mask]; }
|
||||||
|
inline auto read(uint address) const -> T { return self.data[address & self.mask]; }
|
||||||
|
inline auto write(uint address, T data) const -> void {}
|
||||||
|
|
||||||
|
auto serialize(serializer& s) -> void {
|
||||||
|
const uint size = self.size;
|
||||||
|
s.integer(self.size);
|
||||||
|
s.integer(self.mask);
|
||||||
|
if(self.size != size) allocate(self.size);
|
||||||
|
s.array(self.data, self.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct {
|
||||||
|
T* data = nullptr;
|
||||||
|
uint size = 0;
|
||||||
|
uint mask = 0;
|
||||||
|
} self;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
65
higan/emulator/memory/writable.hpp
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <emulator/memory/memory.hpp>
|
||||||
|
|
||||||
|
namespace Emulator::Memory {
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct Writable {
|
||||||
|
~Writable() { reset(); }
|
||||||
|
|
||||||
|
inline auto reset() -> void {
|
||||||
|
delete[] self.data;
|
||||||
|
self.data = nullptr;
|
||||||
|
self.size = 0;
|
||||||
|
self.mask = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto allocate(uint size, T fill = ~0ull) -> void {
|
||||||
|
if(!size) return reset();
|
||||||
|
delete[] self.data;
|
||||||
|
self.size = size;
|
||||||
|
self.mask = bit::round(self.size) - 1;
|
||||||
|
self.data = new T[self.mask + 1];
|
||||||
|
memory::fill<T>(self.data, self.mask + 1, fill);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto load(vfs::shared::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 {
|
||||||
|
fp->write(self.data, self.size * sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator bool() const { return (bool)self.data; }
|
||||||
|
inline auto data() -> T* { return self.data; }
|
||||||
|
inline auto data() const -> const T* { return self.data; }
|
||||||
|
inline auto size() const -> uint { return self.size; }
|
||||||
|
inline auto mask() const -> uint { return self.mask; }
|
||||||
|
|
||||||
|
inline auto operator[](uint address) -> T& { return self.data[address & self.mask]; }
|
||||||
|
inline auto operator[](uint address) const -> T { return self.data[address & self.mask]; }
|
||||||
|
inline auto read(uint address) const -> T { return self.data[address & self.mask]; }
|
||||||
|
inline auto write(uint address, T data) -> void { self.data[address & self.mask] = data; }
|
||||||
|
|
||||||
|
auto serialize(serializer& s) -> void {
|
||||||
|
const uint size = self.size;
|
||||||
|
s.integer(self.size);
|
||||||
|
s.integer(self.mask);
|
||||||
|
if(self.size != size) allocate(self.size);
|
||||||
|
s.array(self.data, self.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct {
|
||||||
|
T* data = nullptr;
|
||||||
|
uint size = 0;
|
||||||
|
uint mask = 0;
|
||||||
|
} self;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@@ -4,27 +4,24 @@ namespace Emulator {
|
|||||||
|
|
||||||
struct Platform {
|
struct Platform {
|
||||||
struct Load {
|
struct Load {
|
||||||
Load() : _pathID(nothing) {}
|
Load() = default;
|
||||||
Load(uint pathID, string option = "") : _pathID(pathID), _option(option) {}
|
Load(uint pathID, string option = "") : valid(true), pathID(pathID), option(option) {}
|
||||||
|
explicit operator bool() const { return valid; }
|
||||||
|
|
||||||
explicit operator bool() const { return (bool)_pathID; }
|
bool valid = false;
|
||||||
auto pathID() const -> uint { return _pathID(); }
|
uint pathID = 0;
|
||||||
auto option() const -> string { return _option; }
|
string option;
|
||||||
|
|
||||||
private:
|
|
||||||
maybe<uint> _pathID;
|
|
||||||
string _option;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual auto path(uint id) -> string { return ""; }
|
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) -> vfs::shared::file { return {}; }
|
||||||
virtual auto load(uint id, string name, string type, string_vector options = {}) -> Load { return {}; }
|
virtual auto load(uint id, string name, string type, vector<string> options = {}) -> Load { return {}; }
|
||||||
virtual auto videoRefresh(const uint32* data, uint pitch, uint width, uint height) -> void {}
|
virtual auto videoFrame(const uint32* data, uint pitch, uint width, uint height) -> void {}
|
||||||
virtual auto audioSample(const double* samples, uint channels) -> void {}
|
virtual auto audioFrame(const double* samples, uint channels) -> void {}
|
||||||
virtual auto inputPoll(uint port, uint device, uint input) -> int16 { return 0; }
|
virtual auto inputPoll(uint port, uint device, uint input) -> int16 { return 0; }
|
||||||
virtual auto inputRumble(uint port, uint device, uint input, bool enable) -> void {}
|
virtual auto inputRumble(uint port, uint device, uint input, bool enable) -> void {}
|
||||||
virtual auto dipSettings(Markup::Node node) -> uint { return 0; }
|
virtual auto dipSettings(Markup::Node node) -> uint { return 0; }
|
||||||
virtual auto notify(string text) -> void { print(text, "\n"); }
|
virtual auto notify(string text) -> void {}
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Platform* platform;
|
extern Platform* platform;
|
||||||
|
@@ -1,6 +1,4 @@
|
|||||||
namespace name=Resource
|
namespace name=Resource
|
||||||
namespace name=Logo
|
|
||||||
binary name=higan file=logo/higan.png
|
|
||||||
namespace name=Sprite
|
namespace name=Sprite
|
||||||
binary name=CrosshairRed file=sprite/crosshair-red.png
|
binary name=CrosshairRed file=sprite/crosshair-red.png
|
||||||
binary name=CrosshairGreen file=sprite/crosshair-green.png
|
binary name=CrosshairGreen file=sprite/crosshair-green.png
|
45
higan/emulator/resource/resource.cpp
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#include "resource.hpp"
|
||||||
|
|
||||||
|
namespace Resource {
|
||||||
|
namespace Sprite {
|
||||||
|
const unsigned char CrosshairRed[342] = {
|
||||||
|
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
|
||||||
|
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,14,196,0,0,14,
|
||||||
|
196,1,149,43,14,27,0,0,0,248,73,68,65,84,88,133,205,87,65,14,196,32,8,132,102,255,255,101,246,176,177,139,
|
||||||
|
148,81,80,27,229,212,70,102,6,212,0,50,229,77,26,107,156,37,139,2,228,241,209,39,11,113,71,156,68,139,106,128,
|
||||||
|
56,255,198,175,203,223,114,16,79,68,253,138,90,99,141,113,112,80,231,131,196,11,83,52,19,43,196,53,135,147,7,38,
|
||||||
|
150,104,244,212,32,86,235,228,236,20,6,200,207,191,117,215,70,12,242,94,139,133,166,236,173,236,67,252,111,139,67,157,
|
||||||
|
237,71,48,27,192,244,142,93,228,23,148,144,184,228,131,96,254,3,164,4,176,213,108,37,52,5,208,53,47,227,81,28,
|
||||||
|
49,153,102,163,88,96,149,68,150,193,21,223,59,128,68,43,69,13,103,4,199,246,8,34,151,240,209,249,38,112,251,47,
|
||||||
|
97,177,209,74,152,246,95,93,9,211,51,160,181,99,142,128,104,115,55,124,59,136,115,7,146,237,51,33,2,71,166,226,
|
||||||
|
94,23,13,77,214,104,44,103,174,163,143,86,189,244,187,224,232,151,81,21,132,39,210,33,91,246,54,132,193,44,226,219,
|
||||||
|
107,95,57,136,120,253,172,254,16,23,0,0,0,0,73,69,78,68,174,66,96,130,
|
||||||
|
};
|
||||||
|
const unsigned char CrosshairGreen[329] = {
|
||||||
|
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
|
||||||
|
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,14,196,0,0,14,
|
||||||
|
196,1,149,43,14,27,0,0,0,235,73,68,65,84,88,133,213,87,65,18,195,32,8,196,78,31,230,211,253,153,61,180,
|
||||||
|
52,18,145,1,193,97,178,39,141,44,139,24,69,11,216,209,133,177,98,117,166,37,92,162,77,176,170,118,223,26,163,78,
|
||||||
|
68,71,145,198,244,169,157,57,35,84,248,43,222,255,109,154,254,113,140,114,102,222,18,239,165,120,251,181,42,0,232,103,
|
||||||
|
114,217,85,226,163,27,124,232,163,87,142,115,153,82,137,71,98,233,247,21,44,228,194,169,217,171,252,159,22,95,234,164,
|
||||||
|
47,129,55,128,144,140,237,166,63,132,151,190,4,247,147,16,103,35,157,90,220,140,119,121,80,224,94,108,0,164,227,119,
|
||||||
|
182,221,229,13,182,82,193,225,176,42,56,59,188,105,9,52,5,3,109,58,243,205,202,203,255,9,17,251,91,202,169,227,
|
||||||
|
205,128,235,198,19,17,64,40,82,171,225,233,32,158,113,33,65,164,222,9,105,16,50,81,55,238,88,210,212,119,1,0,
|
||||||
|
238,241,241,126,143,125,62,216,173,151,209,35,222,134,235,96,98,252,229,226,3,112,72,179,236,202,138,114,18,0,0,0,
|
||||||
|
0,73,69,78,68,174,66,96,130,
|
||||||
|
};
|
||||||
|
const unsigned char CrosshairBlue[332] = {
|
||||||
|
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
|
||||||
|
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,14,196,0,0,14,
|
||||||
|
196,1,149,43,14,27,0,0,0,238,73,68,65,84,88,133,213,87,91,18,195,32,8,196,78,15,232,81,189,161,253,9,
|
||||||
|
25,52,98,121,57,76,246,43,137,44,11,24,69,11,232,209,55,99,69,235,76,74,184,69,107,229,245,91,27,220,137,124,
|
||||||
|
75,140,58,21,165,34,181,246,199,251,100,167,174,200,32,124,137,119,124,134,177,252,116,108,224,44,120,44,190,156,56,102,
|
||||||
|
163,204,228,182,107,173,80,31,93,225,67,30,189,112,124,85,41,145,120,36,88,191,159,96,33,23,78,101,47,242,127,90,
|
||||||
|
156,213,73,159,2,111,0,33,21,179,150,63,132,151,62,5,243,78,136,217,236,118,173,85,198,86,30,20,152,154,13,192,
|
||||||
|
118,251,125,216,90,121,212,118,215,112,86,224,26,142,133,247,152,2,73,195,64,155,190,248,166,229,229,255,132,8,243,146,
|
||||||
|
242,234,120,43,224,58,241,68,4,16,138,212,110,120,58,136,119,28,72,16,169,103,194,33,136,63,68,209,184,103,74,83,
|
||||||
|
239,5,0,215,26,167,231,123,124,103,130,53,221,140,94,113,55,100,131,9,242,151,139,31,79,50,234,237,105,206,30,22,
|
||||||
|
0,0,0,0,73,69,78,68,174,66,96,130,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
7
higan/emulator/resource/resource.hpp
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Resource {
|
||||||
|
namespace Sprite {
|
||||||
|
extern const unsigned char CrosshairRed[342];
|
||||||
|
extern const unsigned char CrosshairGreen[329];
|
||||||
|
extern const unsigned char CrosshairBlue[332];
|
||||||
|
}
|
||||||
|
}
|
Before Width: | Height: | Size: 332 B After Width: | Height: | Size: 332 B |
Before Width: | Height: | Size: 329 B After Width: | Height: | Size: 329 B |
Before Width: | Height: | Size: 342 B After Width: | Height: | Size: 342 B |
@@ -15,6 +15,10 @@ struct Thread {
|
|||||||
inline auto scalar() const { return _scalar; }
|
inline auto scalar() const { return _scalar; }
|
||||||
inline auto clock() const { return _clock; }
|
inline auto clock() const { return _clock; }
|
||||||
|
|
||||||
|
auto setHandle(cothread_t handle) -> void {
|
||||||
|
_handle = handle;
|
||||||
|
}
|
||||||
|
|
||||||
auto setFrequency(double frequency) -> void {
|
auto setFrequency(double frequency) -> void {
|
||||||
_frequency = frequency + 0.5;
|
_frequency = frequency + 0.5;
|
||||||
_scalar = Second / _frequency;
|
_scalar = Second / _frequency;
|
||||||
|
@@ -1,16 +1,15 @@
|
|||||||
#include <emulator/emulator.hpp>
|
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
|
|
||||||
#include "sprite.cpp"
|
#include "sprite.cpp"
|
||||||
Video video;
|
Video video;
|
||||||
|
|
||||||
Video::~Video() {
|
Video::~Video() {
|
||||||
reset();
|
reset(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Video::reset() -> void {
|
auto Video::reset(Interface* interface) -> void {
|
||||||
interface = nullptr;
|
this->interface = interface;
|
||||||
|
|
||||||
sprites.reset();
|
sprites.reset();
|
||||||
delete buffer;
|
delete buffer;
|
||||||
buffer = nullptr;
|
buffer = nullptr;
|
||||||
@@ -25,18 +24,14 @@ auto Video::reset() -> void {
|
|||||||
effects.rotateLeft = false;
|
effects.rotateLeft = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Video::setInterface(Interface* interface) -> void {
|
|
||||||
this->interface = interface;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Video::setPalette() -> void {
|
auto Video::setPalette() -> void {
|
||||||
if(!interface) return;
|
if(!interface) return;
|
||||||
|
|
||||||
delete palette;
|
delete palette;
|
||||||
colors = interface->videoColors();
|
colors = interface->display().colors;
|
||||||
palette = new uint32[colors];
|
palette = new uint32[colors];
|
||||||
for(auto index : range(colors)) {
|
for(auto index : range(colors)) {
|
||||||
uint64 color = interface->videoColor(index);
|
uint64 color = interface->color(index);
|
||||||
uint16 b = color.bits( 0,15);
|
uint16 b = color.bits( 0,15);
|
||||||
uint16 g = color.bits(16,31);
|
uint16 g = color.bits(16,31);
|
||||||
uint16 r = color.bits(32,47);
|
uint16 r = color.bits(32,47);
|
||||||
@@ -63,10 +58,16 @@ auto Video::setPalette() -> void {
|
|||||||
b = uclamp<16>(b * luminance);
|
b = uclamp<16>(b * luminance);
|
||||||
}
|
}
|
||||||
|
|
||||||
//convert color from 16-bits/channel to 8-bits/channel; force alpha to 1.0
|
switch(depth) {
|
||||||
palette[index] = a.byte(1) << 24 | r.byte(1) << 16 | g.byte(1) << 8 | b.byte(1) << 0;
|
case 24: palette[index] = r >> 8 << 16 | g >> 8 << 8 | b >> 8 << 0; break;
|
||||||
|
case 30: palette[index] = r >> 6 << 20 | g >> 6 << 10 | b >> 6 << 0; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Video::setDepth(uint depth) -> void {
|
||||||
|
this->depth = depth;
|
||||||
|
}
|
||||||
|
|
||||||
auto Video::setSaturation(double saturation) -> void {
|
auto Video::setSaturation(double saturation) -> void {
|
||||||
this->saturation = saturation;
|
this->saturation = saturation;
|
||||||
@@ -101,7 +102,7 @@ auto Video::createSprite(uint width, uint height) -> shared_pointer<Sprite> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto Video::removeSprite(shared_pointer<Sprite> sprite) -> bool {
|
auto Video::removeSprite(shared_pointer<Sprite> sprite) -> bool {
|
||||||
for(uint n : range(sprites)) {
|
for(uint n : range(sprites.size())) {
|
||||||
if(sprite == sprites[n]) {
|
if(sprite == sprites[n]) {
|
||||||
sprites.remove(n);
|
sprites.remove(n);
|
||||||
return true;
|
return true;
|
||||||
@@ -133,21 +134,23 @@ auto Video::refresh(uint32* input, uint pitch, uint width, uint height) -> void
|
|||||||
*target++ = color;
|
*target++ = color;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
uint32 mask = depth == 30 ? 0x40100401 : 0x01010101;
|
||||||
for(uint x : range(width)) {
|
for(uint x : range(width)) {
|
||||||
auto a = *target;
|
auto a = *target;
|
||||||
auto b = palette[*source++];
|
auto b = palette[*source++];
|
||||||
*target++ = (a + b - ((a ^ b) & 0x01010101)) >> 1;
|
*target++ = (a + b - ((a ^ b) & mask)) >> 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(effects.colorBleed) {
|
if(effects.colorBleed) {
|
||||||
|
uint32 mask = depth == 30 ? 0x40100401 : 0x01010101;
|
||||||
for(uint y : range(height)) {
|
for(uint y : range(height)) {
|
||||||
auto target = output + y * width;
|
auto target = output + y * width;
|
||||||
for(uint x : range(width)) {
|
for(uint x : range(width)) {
|
||||||
auto a = target[x];
|
auto a = target[x];
|
||||||
auto b = target[x + (x != width - 1)];
|
auto b = target[x + (x != width - 1)];
|
||||||
target[x] = (a + b - ((a ^ b) & 0x01010101)) >> 1;
|
target[x] = (a + b - ((a ^ b) & mask)) >> 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,6 +170,7 @@ auto Video::refresh(uint32* input, uint pitch, uint width, uint height) -> void
|
|||||||
for(auto& sprite : sprites) {
|
for(auto& sprite : sprites) {
|
||||||
if(!sprite->visible) continue;
|
if(!sprite->visible) continue;
|
||||||
|
|
||||||
|
uint32 opaqueAlpha = depth == 30 ? 0xc0000000 : 0xff000000;
|
||||||
for(int y : range(sprite->height)) {
|
for(int y : range(sprite->height)) {
|
||||||
for(int x : range(sprite->width)) {
|
for(int x : range(sprite->width)) {
|
||||||
int pixelY = sprite->y + y;
|
int pixelY = sprite->y + y;
|
||||||
@@ -176,12 +180,12 @@ auto Video::refresh(uint32* input, uint pitch, uint width, uint height) -> void
|
|||||||
if(pixelX < 0 || pixelX >= width) continue;
|
if(pixelX < 0 || pixelX >= width) continue;
|
||||||
|
|
||||||
auto pixel = sprite->pixels[y * sprite->width + x];
|
auto pixel = sprite->pixels[y * sprite->width + x];
|
||||||
if(pixel) output[pixelY * width + pixelX] = 0xff000000 | pixel;
|
if(pixel) output[pixelY * width + pixelX] = opaqueAlpha | pixel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
platform->videoRefresh(output, width * sizeof(uint32), width, height);
|
platform->videoFrame(output, width * sizeof(uint32), width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -14,11 +14,10 @@ struct Video {
|
|||||||
};
|
};
|
||||||
|
|
||||||
~Video();
|
~Video();
|
||||||
|
auto reset(Interface* interface) -> void;
|
||||||
auto reset() -> void;
|
|
||||||
auto setInterface(Interface* interface) -> void;
|
|
||||||
|
|
||||||
auto setPalette() -> void;
|
auto setPalette() -> void;
|
||||||
|
auto setDepth(uint depth) -> void;
|
||||||
auto setSaturation(double saturation) -> void;
|
auto setSaturation(double saturation) -> void;
|
||||||
auto setGamma(double gamma) -> void;
|
auto setGamma(double gamma) -> void;
|
||||||
auto setLuminance(double luminance) -> void;
|
auto setLuminance(double luminance) -> void;
|
||||||
@@ -42,6 +41,7 @@ private:
|
|||||||
uint height = 0;
|
uint height = 0;
|
||||||
uint colors = 0;
|
uint colors = 0;
|
||||||
|
|
||||||
|
uint depth = 24;
|
||||||
double saturation = 1.0;
|
double saturation = 1.0;
|
||||||
double gamma = 1.0;
|
double gamma = 1.0;
|
||||||
double luminance = 1.0;
|
double luminance = 1.0;
|
@@ -3,11 +3,11 @@ processors += mos6502
|
|||||||
objects += fc-interface fc-system fc-controller
|
objects += fc-interface fc-system fc-controller
|
||||||
objects += fc-memory fc-cartridge fc-cpu fc-apu fc-ppu
|
objects += fc-memory fc-cartridge fc-cpu fc-apu fc-ppu
|
||||||
|
|
||||||
obj/fc-interface.o: fc/interface/interface.cpp $(call rwildcard,fc/interface/)
|
obj/fc-interface.o: fc/interface/interface.cpp
|
||||||
obj/fc-system.o: fc/system/system.cpp $(call rwildcard,fc/system/)
|
obj/fc-system.o: fc/system/system.cpp
|
||||||
obj/fc-controller.o: fc/controller/controller.cpp $(call rwildcard,fc/controller/)
|
obj/fc-controller.o: fc/controller/controller.cpp
|
||||||
obj/fc-memory.o: fc/memory/memory.cpp $(call rwildcard,fc/memory/)
|
obj/fc-memory.o: fc/memory/memory.cpp
|
||||||
obj/fc-cartridge.o: fc/cartridge/cartridge.cpp $(call rwildcard,fc/cartridge/)
|
obj/fc-cartridge.o: fc/cartridge/cartridge.cpp
|
||||||
obj/fc-cpu.o: fc/cpu/cpu.cpp $(call rwildcard,fc/cpu/)
|
obj/fc-cpu.o: fc/cpu/cpu.cpp
|
||||||
obj/fc-apu.o: fc/apu/apu.cpp $(call rwildcard,fc/apu/)
|
obj/fc-apu.o: fc/apu/apu.cpp
|
||||||
obj/fc-ppu.o: fc/ppu/ppu.cpp $(call rwildcard,fc/ppu/)
|
obj/fc-ppu.o: fc/ppu/ppu.cpp
|
||||||
|
@@ -71,13 +71,13 @@ auto APU::setSample(int16 sample) -> void {
|
|||||||
cartridgeSample = sample;
|
cartridgeSample = sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto APU::power() -> void {
|
auto APU::power(bool reset) -> void {
|
||||||
create(APU::Enter, system.frequency());
|
create(APU::Enter, system.frequency());
|
||||||
stream = Emulator::audio.createStream(1, frequency() / rate());
|
stream = Emulator::audio.createStream(1, frequency() / rate());
|
||||||
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 90.0);
|
stream->addHighPassFilter( 90.0, Emulator::Filter::Order::First);
|
||||||
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 440.0);
|
stream->addHighPassFilter( 440.0, Emulator::Filter::Order::First);
|
||||||
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::LowPass, 14000.0);
|
stream->addLowPassFilter (14000.0, Emulator::Filter::Order::First);
|
||||||
stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3);
|
stream->addDCRemovalFilter();
|
||||||
|
|
||||||
pulse[0].power();
|
pulse[0].power();
|
||||||
pulse[1].power();
|
pulse[1].power();
|
||||||
|
@@ -12,7 +12,7 @@ struct APU : Thread {
|
|||||||
auto setIRQ() -> void;
|
auto setIRQ() -> void;
|
||||||
auto setSample(int16 sample) -> void;
|
auto setSample(int16 sample) -> void;
|
||||||
|
|
||||||
auto power() -> void;
|
auto power(bool reset) -> void;
|
||||||
|
|
||||||
auto readIO(uint16 addr) -> uint8;
|
auto readIO(uint16 addr) -> uint8;
|
||||||
auto writeIO(uint16 addr, uint8 data) -> void;
|
auto writeIO(uint16 addr, uint8 data) -> void;
|
||||||
|
@@ -22,6 +22,7 @@ struct BandaiFCG : Board {
|
|||||||
case 2: return 0x0000 | (addr & 0x03ff);
|
case 2: return 0x0000 | (addr & 0x03ff);
|
||||||
case 3: return 0x0400 | (addr & 0x03ff);
|
case 3: return 0x0400 | (addr & 0x03ff);
|
||||||
}
|
}
|
||||||
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto readPRG(uint addr) -> uint8 {
|
auto readPRG(uint addr) -> uint8 {
|
||||||
|
@@ -21,66 +21,60 @@
|
|||||||
|
|
||||||
Board::Board(Markup::Node& document) {
|
Board::Board(Markup::Node& document) {
|
||||||
cartridge.board = this;
|
cartridge.board = this;
|
||||||
auto board = document["board"];
|
information.type = document["game/board"].text();
|
||||||
|
|
||||||
information.type = board["id"].text();
|
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=ROM,content=Program)"]}) {
|
||||||
information.battery = (bool)board["prg/ram/name"];
|
if(prgrom.size = memory.size) prgrom.data = new uint8_t[prgrom.size]();
|
||||||
|
if(auto fp = platform->open(cartridge.pathID(), memory.name(), File::Read, File::Required)) {
|
||||||
auto prom = board["prg/rom"];
|
|
||||||
auto pram = board["prg/ram"];
|
|
||||||
auto crom = board["chr/rom"];
|
|
||||||
auto cram = board["chr/ram"];
|
|
||||||
|
|
||||||
prgrom.size = prom["size"].natural();
|
|
||||||
prgram.size = pram["size"].natural();
|
|
||||||
chrrom.size = crom["size"].natural();
|
|
||||||
chrram.size = cram["size"].natural();
|
|
||||||
|
|
||||||
if(prgrom.size) prgrom.data = new uint8_t[prgrom.size]();
|
|
||||||
if(prgram.size) prgram.data = new uint8_t[prgram.size]();
|
|
||||||
if(chrrom.size) chrrom.data = new uint8_t[chrrom.size]();
|
|
||||||
if(chrram.size) chrram.data = new uint8_t[chrram.size]();
|
|
||||||
|
|
||||||
if(prgrom.name = prom["name"].text()) {
|
|
||||||
if(auto fp = platform->open(cartridge.pathID(), prgrom.name, File::Read, File::Required)) {
|
|
||||||
fp->read(prgrom.data, min(prgrom.size, fp->size()));
|
fp->read(prgrom.data, min(prgrom.size, fp->size()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(prgram.name = pram["name"].text()) {
|
|
||||||
if(auto fp = platform->open(cartridge.pathID(), prgram.name, File::Read)) {
|
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=RAM,content=Save)"]}) {
|
||||||
|
if(prgram.size = memory.size) prgram.data = new uint8_t[prgram.size](), prgram.writable = true;
|
||||||
|
if(memory.nonVolatile) {
|
||||||
|
if(auto fp = platform->open(cartridge.pathID(), memory.name(), File::Read)) {
|
||||||
fp->read(prgram.data, min(prgram.size, fp->size()));
|
fp->read(prgram.data, min(prgram.size, fp->size()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(chrrom.name = crom["name"].text()) {
|
}
|
||||||
if(auto fp = platform->open(cartridge.pathID(), chrrom.name, File::Read, File::Required)) {
|
|
||||||
|
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=ROM,content=Character)"]}) {
|
||||||
|
if(chrrom.size = memory.size) chrrom.data = new uint8_t[chrrom.size]();
|
||||||
|
if(auto fp = platform->open(cartridge.pathID(), memory.name(), File::Read, File::Required)) {
|
||||||
fp->read(chrrom.data, min(chrrom.size, fp->size()));
|
fp->read(chrrom.data, min(chrrom.size, fp->size()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(chrram.name = cram["name"].text()) {
|
|
||||||
if(auto fp = platform->open(cartridge.pathID(), chrram.name, File::Read)) {
|
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=RAM,content=Character)"]}) {
|
||||||
|
if(chrram.size = memory.size) chrram.data = new uint8_t[chrram.size](), chrram.writable = true;
|
||||||
|
if(memory.nonVolatile) {
|
||||||
|
if(auto fp = platform->open(cartridge.pathID(), memory.name(), File::Read)) {
|
||||||
fp->read(chrram.data, min(chrram.size, fp->size()));
|
fp->read(chrram.data, min(chrram.size, fp->size()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
prgram.writable = true;
|
|
||||||
chrram.writable = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Board::save() -> void {
|
auto Board::save() -> void {
|
||||||
auto document = BML::unserialize(cartridge.manifest());
|
auto document = BML::unserialize(cartridge.manifest());
|
||||||
|
|
||||||
if(auto name = document["board/prg/ram/name"].text()) {
|
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=RAM,content=Save)"]}) {
|
||||||
if(auto fp = platform->open(cartridge.pathID(), name, File::Write)) {
|
if(memory.nonVolatile) {
|
||||||
|
if(auto fp = platform->open(cartridge.pathID(), memory.name(), File::Write)) {
|
||||||
fp->write(prgram.data, prgram.size);
|
fp->write(prgram.data, prgram.size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(auto name = document["board/chr/ram/name"].text()) {
|
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=RAM,content=Character)"]}) {
|
||||||
if(auto fp = platform->open(cartridge.pathID(), name, File::Write)) {
|
if(memory.nonVolatile) {
|
||||||
|
if(auto fp = platform->open(cartridge.pathID(), memory.name(), File::Write)) {
|
||||||
fp->write(chrram.data, chrram.size);
|
fp->write(chrram.data, chrram.size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto Board::Memory::read(uint addr) const -> uint8 {
|
auto Board::Memory::read(uint addr) const -> uint8 {
|
||||||
return data[mirror(addr, size)];
|
return data[mirror(addr, size)];
|
||||||
@@ -138,9 +132,9 @@ auto Board::serialize(serializer& s) -> void {
|
|||||||
|
|
||||||
auto Board::load(string manifest) -> Board* {
|
auto Board::load(string manifest) -> Board* {
|
||||||
auto document = BML::unserialize(manifest);
|
auto document = BML::unserialize(manifest);
|
||||||
cartridge.information.title = document["information/title"].text();
|
cartridge.information.title = document["game/label"].text();
|
||||||
|
|
||||||
string type = document["board/id"].text();
|
string type = document["game/board"].text();
|
||||||
|
|
||||||
if(type == "BANDAI-FCG" ) return new BandaiFCG(document);
|
if(type == "BANDAI-FCG" ) return new BandaiFCG(document);
|
||||||
|
|
||||||
|
@@ -39,7 +39,6 @@ struct Board {
|
|||||||
|
|
||||||
struct Information {
|
struct Information {
|
||||||
string type;
|
string type;
|
||||||
bool battery;
|
|
||||||
} information;
|
} information;
|
||||||
|
|
||||||
Memory prgrom;
|
Memory prgrom;
|
||||||
|
@@ -151,6 +151,7 @@ struct Sunsoft5B : Board {
|
|||||||
case 2: return 0x0000 | (addr & 0x03ff); //first
|
case 2: return 0x0000 | (addr & 0x03ff); //first
|
||||||
case 3: return 0x0400 | (addr & 0x03ff); //second
|
case 3: return 0x0400 | (addr & 0x03ff); //second
|
||||||
}
|
}
|
||||||
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto readCHR(uint addr) -> uint8 {
|
auto readCHR(uint addr) -> uint8 {
|
||||||
|
@@ -17,8 +17,8 @@ auto Cartridge::main() -> void {
|
|||||||
|
|
||||||
auto Cartridge::load() -> bool {
|
auto Cartridge::load() -> bool {
|
||||||
if(auto loaded = platform->load(ID::Famicom, "Famicom", "fc", {"NTSC-J", "NTSC-U", "PAL"})) {
|
if(auto loaded = platform->load(ID::Famicom, "Famicom", "fc", {"NTSC-J", "NTSC-U", "PAL"})) {
|
||||||
information.pathID = loaded.pathID();
|
information.pathID = loaded.pathID;
|
||||||
information.region = loaded.option();
|
information.region = loaded.option;
|
||||||
} else return false;
|
} else return false;
|
||||||
|
|
||||||
if(auto fp = platform->open(pathID(), "manifest.bml", File::Read, File::Required)) {
|
if(auto fp = platform->open(pathID(), "manifest.bml", File::Read, File::Required)) {
|
||||||
|
@@ -10,7 +10,7 @@ struct Cartridge : Thread {
|
|||||||
|
|
||||||
auto pathID() const -> uint { return information.pathID; }
|
auto pathID() const -> uint { return information.pathID; }
|
||||||
auto region() const -> string { return information.region; }
|
auto region() const -> string { return information.region; }
|
||||||
auto sha256() const -> string { return information.sha256; }
|
auto hash() const -> string { return information.sha256; }
|
||||||
auto manifest() const -> string { return information.manifest; }
|
auto manifest() const -> string { return information.manifest; }
|
||||||
auto title() const -> string { return information.title; }
|
auto title() const -> string { return information.title; }
|
||||||
|
|
||||||
|
@@ -34,6 +34,7 @@ struct MMC1 : Chip {
|
|||||||
case 2: return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
case 2: return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
||||||
case 3: return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
case 3: return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||||
}
|
}
|
||||||
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto writeIO(uint addr, uint8 data) -> void {
|
auto writeIO(uint addr, uint8 data) -> void {
|
||||||
|
@@ -35,6 +35,7 @@ struct MMC3 : Chip {
|
|||||||
case 3:
|
case 3:
|
||||||
return (0x3f << 13) | (addr & 0x1fff);
|
return (0x3f << 13) | (addr & 0x1fff);
|
||||||
}
|
}
|
||||||
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto addrCHR(uint addr) const -> uint {
|
auto addrCHR(uint addr) const -> uint {
|
||||||
@@ -53,11 +54,13 @@ struct MMC3 : Chip {
|
|||||||
if(addr <= 0x17ff) return (chrBank[0] << 10) | (addr & 0x07ff);
|
if(addr <= 0x17ff) return (chrBank[0] << 10) | (addr & 0x07ff);
|
||||||
if(addr <= 0x1fff) return (chrBank[1] << 10) | (addr & 0x07ff);
|
if(addr <= 0x1fff) return (chrBank[1] << 10) | (addr & 0x07ff);
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto addrCIRAM(uint addr) const -> uint {
|
auto addrCIRAM(uint addr) const -> uint {
|
||||||
if(mirror == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
if(mirror == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
||||||
if(mirror == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
if(mirror == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||||
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto readRAM(uint addr) -> uint8 {
|
auto readRAM(uint addr) -> uint8 {
|
||||||
|
@@ -83,6 +83,8 @@ struct MMC5 : Chip {
|
|||||||
case 0x5205: return (multiplier * multiplicand) >> 0;
|
case 0x5205: return (multiplier * multiplicand) >> 0;
|
||||||
case 0x5206: return (multiplier * multiplicand) >> 8;
|
case 0x5206: return (multiplier * multiplicand) >> 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto writePRG(uint addr, uint8 data) -> void {
|
auto writePRG(uint addr, uint8 data) -> void {
|
||||||
@@ -215,6 +217,8 @@ struct MMC5 : Chip {
|
|||||||
auto bank = chrSpriteBank[(addr / 0x0400)];
|
auto bank = chrSpriteBank[(addr / 0x0400)];
|
||||||
return (bank * 0x0400) + (addr & 0x03ff);
|
return (bank * 0x0400) + (addr & 0x03ff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto chrBGAddr(uint addr) -> uint {
|
auto chrBGAddr(uint addr) -> uint {
|
||||||
@@ -239,6 +243,8 @@ struct MMC5 : Chip {
|
|||||||
auto bank = chrBGBank[(addr / 0x0400)];
|
auto bank = chrBGBank[(addr / 0x0400)];
|
||||||
return (bank * 0x0400) + (addr & 0x03ff);
|
return (bank * 0x0400) + (addr & 0x03ff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto chrVSAddr(uint addr) -> uint {
|
auto chrVSAddr(uint addr) -> uint {
|
||||||
@@ -274,6 +280,7 @@ struct MMC5 : Chip {
|
|||||||
case 2: return exramMode < 2 ? exram[addr & 0x03ff] : (uint8)0x00;
|
case 2: return exramMode < 2 ? exram[addr & 0x03ff] : (uint8)0x00;
|
||||||
case 3: return (hcounter & 2) == 0 ? fillmodeTile : fillmodeColor;
|
case 3: return (hcounter & 2) == 0 ? fillmodeTile : fillmodeColor;
|
||||||
}
|
}
|
||||||
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto readCHR(uint addr) -> uint8 {
|
auto readCHR(uint addr) -> uint8 {
|
||||||
|
@@ -35,6 +35,7 @@ struct MMC6 : Chip {
|
|||||||
case 3:
|
case 3:
|
||||||
return (0x3f << 13) | (addr & 0x1fff);
|
return (0x3f << 13) | (addr & 0x1fff);
|
||||||
}
|
}
|
||||||
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto addrCHR(uint addr) const -> uint {
|
auto addrCHR(uint addr) const -> uint {
|
||||||
@@ -53,11 +54,13 @@ struct MMC6 : Chip {
|
|||||||
if(addr <= 0x17ff) return (chrBank[0] << 10) | (addr & 0x07ff);
|
if(addr <= 0x17ff) return (chrBank[0] << 10) | (addr & 0x07ff);
|
||||||
if(addr <= 0x1fff) return (chrBank[1] << 10) | (addr & 0x07ff);
|
if(addr <= 0x1fff) return (chrBank[1] << 10) | (addr & 0x07ff);
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto addrCIRAM(uint addr) const -> uint {
|
auto addrCIRAM(uint addr) const -> uint {
|
||||||
if(mirror == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
if(mirror == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
||||||
if(mirror == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
if(mirror == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||||
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto readRAM(uint addr) -> uint8 {
|
auto readRAM(uint addr) -> uint8 {
|
||||||
|
@@ -115,6 +115,7 @@ struct VRC6 : Chip {
|
|||||||
if((addr & 0xc000) == 0x8000) return (prgBank[0] << 14) | (addr & 0x3fff);
|
if((addr & 0xc000) == 0x8000) return (prgBank[0] << 14) | (addr & 0x3fff);
|
||||||
if((addr & 0xe000) == 0xc000) return (prgBank[1] << 13) | (addr & 0x1fff);
|
if((addr & 0xe000) == 0xc000) return (prgBank[1] << 13) | (addr & 0x1fff);
|
||||||
if((addr & 0xe000) == 0xe000) return ( 0xff << 13) | (addr & 0x1fff);
|
if((addr & 0xe000) == 0xe000) return ( 0xff << 13) | (addr & 0x1fff);
|
||||||
|
return 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto addrCHR(uint addr) const -> uint {
|
auto addrCHR(uint addr) const -> uint {
|
||||||
@@ -129,6 +130,7 @@ struct VRC6 : Chip {
|
|||||||
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
|
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
|
||||||
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
|
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
|
||||||
}
|
}
|
||||||
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto readRAM(uint addr) -> uint8 {
|
auto readRAM(uint addr) -> uint8 {
|
||||||
|
@@ -96,6 +96,7 @@ struct VRC7 : Chip {
|
|||||||
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
|
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
|
||||||
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
|
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
|
||||||
}
|
}
|
||||||
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto power() -> void {
|
auto power() -> void {
|
||||||
|
@@ -24,22 +24,21 @@ auto CPU::step(uint clocks) -> void {
|
|||||||
for(auto peripheral : peripherals) synchronize(*peripheral);
|
for(auto peripheral : peripherals) synchronize(*peripheral);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::power() -> void {
|
auto CPU::power(bool reset) -> void {
|
||||||
MOS6502::BCD = 0;
|
MOS6502::BCD = 0;
|
||||||
MOS6502::power();
|
MOS6502::power();
|
||||||
create(CPU::Enter, system.frequency());
|
create(CPU::Enter, system.frequency());
|
||||||
|
|
||||||
for(auto addr : range(0x0800)) ram[addr] = 0xff;
|
if(!reset) for(auto& data : ram) data = 0xff;
|
||||||
ram[0x0008] = 0xf7;
|
ram[0x008] = 0xf7; //todo: what is this about?
|
||||||
ram[0x0009] = 0xef;
|
ram[0x009] = 0xef;
|
||||||
ram[0x000a] = 0xdf;
|
ram[0x00a] = 0xdf;
|
||||||
ram[0x000f] = 0xbf;
|
ram[0x00f] = 0xbf;
|
||||||
|
|
||||||
r.pc.byte(0) = bus.read(0xfffc);
|
r.pc.byte(0) = bus.read(0xfffc);
|
||||||
r.pc.byte(1) = bus.read(0xfffd);
|
r.pc.byte(1) = bus.read(0xfffd);
|
||||||
|
|
||||||
memory::fill(&io, sizeof(IO));
|
io = {};
|
||||||
io.rdyLine = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -6,7 +6,7 @@ struct CPU : Processor::MOS6502, Thread {
|
|||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
auto step(uint clocks) -> void;
|
auto step(uint clocks) -> void;
|
||||||
|
|
||||||
auto power() -> void;
|
auto power(bool reset) -> void;
|
||||||
|
|
||||||
//memory.cpp
|
//memory.cpp
|
||||||
auto readRAM(uint11 addr) -> uint8;
|
auto readRAM(uint11 addr) -> uint8;
|
||||||
@@ -37,20 +37,20 @@ struct CPU : Processor::MOS6502, Thread {
|
|||||||
//protected:
|
//protected:
|
||||||
vector<Thread*> peripherals;
|
vector<Thread*> peripherals;
|
||||||
|
|
||||||
uint8 ram[0x0800];
|
uint8 ram[0x800];
|
||||||
|
|
||||||
struct IO {
|
struct IO {
|
||||||
bool interruptPending;
|
bool interruptPending = 0;
|
||||||
bool nmiPending;
|
bool nmiPending = 0;
|
||||||
bool nmiLine;
|
bool nmiLine = 0;
|
||||||
bool irqLine;
|
bool irqLine = 0;
|
||||||
bool apuLine;
|
bool apuLine = 0;
|
||||||
|
|
||||||
bool rdyLine;
|
bool rdyLine = 1;
|
||||||
bool rdyAddrValid;
|
bool rdyAddrValid = 0;
|
||||||
uint16 rdyAddrValue;
|
uint16 rdyAddrValue;
|
||||||
|
|
||||||
bool oamdmaPending;
|
bool oamdmaPending = 0;
|
||||||
uint8 oamdmaPage;
|
uint8 oamdmaPage;
|
||||||
} io;
|
} io;
|
||||||
};
|
};
|
||||||
|
@@ -4,69 +4,35 @@ namespace Famicom {
|
|||||||
|
|
||||||
Settings settings;
|
Settings settings;
|
||||||
|
|
||||||
Interface::Interface() {
|
auto Interface::information() -> Information {
|
||||||
|
Information information;
|
||||||
information.manufacturer = "Nintendo";
|
information.manufacturer = "Nintendo";
|
||||||
information.name = "Famicom";
|
information.name = "Famicom";
|
||||||
information.overscan = true;
|
information.extension = "fc";
|
||||||
|
information.resettable = true;
|
||||||
media.append({ID::Famicom, "Famicom", "fc"});
|
return information;
|
||||||
|
|
||||||
Port controllerPort1{ID::Port::Controller1, "Controller Port 1"};
|
|
||||||
Port controllerPort2{ID::Port::Controller2, "Controller Port 2"};
|
|
||||||
|
|
||||||
{ Device device{ID::Device::None, "None"};
|
|
||||||
controllerPort1.devices.append(device);
|
|
||||||
controllerPort2.devices.append(device);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{ Device device{ID::Device::Gamepad, "Gamepad"};
|
auto Interface::display() -> Display {
|
||||||
device.inputs.append({0, "Up" });
|
Display display;
|
||||||
device.inputs.append({0, "Down" });
|
display.type = Display::Type::CRT;
|
||||||
device.inputs.append({0, "Left" });
|
display.colors = 1 << 9;
|
||||||
device.inputs.append({0, "Right" });
|
display.width = 256;
|
||||||
device.inputs.append({0, "B" });
|
display.height = 240;
|
||||||
device.inputs.append({0, "A" });
|
display.internalWidth = 256;
|
||||||
device.inputs.append({0, "Select"});
|
display.internalHeight = 240;
|
||||||
device.inputs.append({0, "Start" });
|
display.aspectCorrection = 8.0 / 7.0;
|
||||||
controllerPort1.devices.append(device);
|
return display;
|
||||||
controllerPort2.devices.append(device);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ports.append(move(controllerPort1));
|
auto Interface::color(uint32 n) -> uint64 {
|
||||||
ports.append(move(controllerPort2));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Interface::manifest() -> string {
|
|
||||||
return cartridge.manifest();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Interface::title() -> string {
|
|
||||||
return cartridge.title();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Interface::videoInformation() -> VideoInformation {
|
|
||||||
VideoInformation vi;
|
|
||||||
vi.width = 256;
|
|
||||||
vi.height = 240;
|
|
||||||
vi.internalWidth = 256;
|
|
||||||
vi.internalHeight = 240;
|
|
||||||
vi.aspectCorrection = 8.0 / 7.0;
|
|
||||||
vi.refreshRate = system.frequency() / (ppu.vlines() * ppu.rate() * 341.0);
|
|
||||||
return vi;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Interface::videoColors() -> uint32 {
|
|
||||||
return 1 << 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Interface::videoColor(uint32 n) -> uint64 {
|
|
||||||
double saturation = 2.0;
|
double saturation = 2.0;
|
||||||
double hue = 0.0;
|
double hue = 0.0;
|
||||||
double contrast = 1.0;
|
double contrast = 1.0;
|
||||||
double brightness = 1.0;
|
double brightness = 1.0;
|
||||||
double gamma = settings.colorEmulation ? 1.8 : 2.2;
|
double gamma = settings.colorEmulation ? 1.8 : 2.2;
|
||||||
|
|
||||||
int color = (n & 0x0f), level = color < 0xe ? (n >> 4) & 3 : 1;
|
int color = (n & 0x0f), level = color < 0xe ? int(n >> 4 & 3) : 1;
|
||||||
|
|
||||||
static const double black = 0.518, white = 1.962, attenuation = 0.746;
|
static const double black = 0.518, white = 1.962, attenuation = 0.746;
|
||||||
static const double levels[8] = {
|
static const double levels[8] = {
|
||||||
@@ -114,11 +80,19 @@ auto Interface::loaded() -> bool {
|
|||||||
return system.loaded();
|
return system.loaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::sha256() -> string {
|
auto Interface::hashes() -> vector<string> {
|
||||||
return cartridge.sha256();
|
return {cartridge.hash()};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::load(uint id) -> bool {
|
auto Interface::manifests() -> vector<string> {
|
||||||
|
return {cartridge.manifest()};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Interface::titles() -> vector<string> {
|
||||||
|
return {cartridge.title()};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Interface::load() -> bool {
|
||||||
return system.load(this);
|
return system.load(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,13 +105,68 @@ auto Interface::unload() -> void {
|
|||||||
system.unload();
|
system.unload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Interface::ports() -> vector<Port> { return {
|
||||||
|
{ID::Port::Controller1, "Controller Port 1"},
|
||||||
|
{ID::Port::Controller2, "Controller Port 2"},
|
||||||
|
{ID::Port::Expansion, "Expansion Port" }};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Interface::devices(uint port) -> vector<Device> {
|
||||||
|
if(port == ID::Port::Controller1) return {
|
||||||
|
{ID::Device::None, "None" },
|
||||||
|
{ID::Device::Gamepad, "Gamepad"}
|
||||||
|
};
|
||||||
|
|
||||||
|
if(port == ID::Port::Controller2) return {
|
||||||
|
{ID::Device::None, "None" },
|
||||||
|
{ID::Device::Gamepad, "Gamepad"}
|
||||||
|
};
|
||||||
|
|
||||||
|
if(port == ID::Port::Expansion) return {
|
||||||
|
{ID::Device::None, "None"}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Interface::inputs(uint device) -> vector<Input> {
|
||||||
|
using Type = Input::Type;
|
||||||
|
|
||||||
|
if(device == ID::Device::None) return {
|
||||||
|
};
|
||||||
|
|
||||||
|
if(device == ID::Device::Gamepad) return {
|
||||||
|
{Type::Hat, "Up" },
|
||||||
|
{Type::Hat, "Down" },
|
||||||
|
{Type::Hat, "Left" },
|
||||||
|
{Type::Hat, "Right" },
|
||||||
|
{Type::Button, "B" },
|
||||||
|
{Type::Button, "A" },
|
||||||
|
{Type::Control, "Select"},
|
||||||
|
{Type::Control, "Start" }
|
||||||
|
};
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Interface::connected(uint port) -> uint {
|
||||||
|
if(port == ID::Port::Controller1) return settings.controllerPort1;
|
||||||
|
if(port == ID::Port::Controller2) return settings.controllerPort2;
|
||||||
|
if(port == ID::Port::Expansion) return settings.expansionPort;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
auto Interface::connect(uint port, uint device) -> void {
|
auto Interface::connect(uint port, uint device) -> void {
|
||||||
if(port == ID::Port::Controller1) controllerPort1.connect(settings.controllerPort1 = device);
|
if(port == ID::Port::Controller1) controllerPort1.connect(settings.controllerPort1 = device);
|
||||||
if(port == ID::Port::Controller2) controllerPort2.connect(settings.controllerPort2 = device);
|
if(port == ID::Port::Controller2) controllerPort2.connect(settings.controllerPort2 = device);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::power() -> void {
|
auto Interface::power() -> void {
|
||||||
system.power();
|
system.power(/* reset = */ false);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Interface::reset() -> void {
|
||||||
|
system.power(/* reset = */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::run() -> void {
|
auto Interface::run() -> void {
|
||||||
@@ -153,7 +182,7 @@ auto Interface::unserialize(serializer& s) -> bool {
|
|||||||
return system.unserialize(s);
|
return system.unserialize(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::cheatSet(const string_vector& list) -> void {
|
auto Interface::cheats(const vector<string>& list) -> void {
|
||||||
cheat.assign(list);
|
cheat.assign(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +201,7 @@ auto Interface::get(const string& name) -> any {
|
|||||||
auto Interface::set(const string& name, const any& value) -> bool {
|
auto Interface::set(const string& name, const any& value) -> bool {
|
||||||
if(name == "Color Emulation" && value.is<bool>()) {
|
if(name == "Color Emulation" && value.is<bool>()) {
|
||||||
settings.colorEmulation = value.get<bool>();
|
settings.colorEmulation = value.get<bool>();
|
||||||
system.configureVideoPalette();
|
Emulator::video.setPalette();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(name == "Scanline Emulation" && value.is<bool>()) return settings.scanlineEmulation = value.get<bool>(), true;
|
if(name == "Scanline Emulation" && value.is<bool>()) return settings.scanlineEmulation = value.get<bool>(), true;
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
#if defined(CORE_FC)
|
||||||
|
|
||||||
namespace Famicom {
|
namespace Famicom {
|
||||||
|
|
||||||
struct ID {
|
struct ID {
|
||||||
@@ -19,31 +21,33 @@ struct ID {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct Interface : Emulator::Interface {
|
struct Interface : Emulator::Interface {
|
||||||
using Emulator::Interface::load;
|
auto information() -> Information override;
|
||||||
|
|
||||||
Interface();
|
auto display() -> Display override;
|
||||||
|
auto color(uint32 color) -> uint64 override;
|
||||||
auto manifest() -> string override;
|
|
||||||
auto title() -> string override;
|
|
||||||
|
|
||||||
auto videoInformation() -> VideoInformation override;
|
|
||||||
auto videoColors() -> uint32 override;
|
|
||||||
auto videoColor(uint32 color) -> uint64 override;
|
|
||||||
|
|
||||||
auto loaded() -> bool override;
|
auto loaded() -> bool override;
|
||||||
auto sha256() -> string override;
|
auto hashes() -> vector<string> override;
|
||||||
auto load(uint id) -> bool override;
|
auto manifests() -> vector<string> override;
|
||||||
|
auto titles() -> vector<string> override;
|
||||||
|
auto load() -> bool override;
|
||||||
auto save() -> void override;
|
auto save() -> void override;
|
||||||
auto unload() -> void override;
|
auto unload() -> void override;
|
||||||
|
|
||||||
|
auto ports() -> vector<Port> override;
|
||||||
|
auto devices(uint port) -> vector<Device> override;
|
||||||
|
auto inputs(uint device) -> vector<Input> override;
|
||||||
|
|
||||||
|
auto connected(uint port) -> uint override;
|
||||||
auto connect(uint port, uint device) -> void override;
|
auto connect(uint port, uint device) -> void override;
|
||||||
auto power() -> void override;
|
auto power() -> void override;
|
||||||
|
auto reset() -> void override;
|
||||||
auto run() -> void override;
|
auto run() -> void override;
|
||||||
|
|
||||||
auto serialize() -> serializer override;
|
auto serialize() -> serializer override;
|
||||||
auto unserialize(serializer&) -> bool override;
|
auto unserialize(serializer&) -> bool override;
|
||||||
|
|
||||||
auto cheatSet(const string_vector&) -> void override;
|
auto cheats(const vector<string>&) -> void override;
|
||||||
|
|
||||||
auto cap(const string& name) -> bool override;
|
auto cap(const string& name) -> bool override;
|
||||||
auto get(const string& name) -> any override;
|
auto get(const string& name) -> any override;
|
||||||
@@ -54,11 +58,13 @@ struct Settings {
|
|||||||
bool colorEmulation = true;
|
bool colorEmulation = true;
|
||||||
bool scanlineEmulation = true;
|
bool scanlineEmulation = true;
|
||||||
|
|
||||||
uint controllerPort1 = 0;
|
uint controllerPort1 = ID::Device::Gamepad;
|
||||||
uint controllerPort2 = 0;
|
uint controllerPort2 = ID::Device::Gamepad;
|
||||||
uint expansionPort = 0;
|
uint expansionPort = ID::Device::None;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Settings settings;
|
extern Settings settings;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
@@ -54,18 +54,19 @@ auto PPU::refresh() -> void {
|
|||||||
Emulator::video.refresh(buffer, 256 * sizeof(uint32), 256, 240);
|
Emulator::video.refresh(buffer, 256 * sizeof(uint32), 256, 240);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::power() -> void {
|
auto PPU::power(bool reset) -> void {
|
||||||
create(PPU::Enter, system.frequency());
|
create(PPU::Enter, system.frequency());
|
||||||
|
|
||||||
memory::fill(&io, sizeof(IO));
|
io = {};
|
||||||
memory::fill(&latch, sizeof(Latches));
|
latch = {};
|
||||||
io.vramIncrement = 1;
|
|
||||||
|
|
||||||
for(auto& n : ciram ) n = 0;
|
if(!reset) {
|
||||||
for(auto& n : cgram ) n = 0;
|
for(auto& data : ciram ) data = 0;
|
||||||
for(auto& n : oam ) n = 0;
|
for(auto& data : cgram ) data = 0;
|
||||||
|
for(auto& data : oam ) data = 0;
|
||||||
|
}
|
||||||
|
|
||||||
for(auto& n : buffer) n = 0;
|
for(auto& data : buffer) data = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -11,7 +11,7 @@ struct PPU : Thread {
|
|||||||
auto frame() -> void;
|
auto frame() -> void;
|
||||||
auto refresh() -> void;
|
auto refresh() -> void;
|
||||||
|
|
||||||
auto power() -> void;
|
auto power(bool reset) -> void;
|
||||||
|
|
||||||
//memory.cpp
|
//memory.cpp
|
||||||
auto readCIRAM(uint11 addr) -> uint8;
|
auto readCIRAM(uint11 addr) -> uint8;
|
||||||
@@ -39,13 +39,14 @@ struct PPU : Thread {
|
|||||||
uint8 mdr;
|
uint8 mdr;
|
||||||
|
|
||||||
uint1 field;
|
uint1 field;
|
||||||
uint lx;
|
uint lx = 0;
|
||||||
uint ly;
|
uint ly = 0;
|
||||||
|
|
||||||
uint8 busData;
|
uint8 busData;
|
||||||
|
|
||||||
union {
|
union Union {
|
||||||
uint value;
|
auto& operator=(const Union& u) { value = u.value; return *this; }
|
||||||
|
uint value = 0;
|
||||||
NaturalBitField<uint, 0, 4> tileX;
|
NaturalBitField<uint, 0, 4> tileX;
|
||||||
NaturalBitField<uint, 5, 9> tileY;
|
NaturalBitField<uint, 5, 9> tileY;
|
||||||
NaturalBitField<uint,10,11> nametable;
|
NaturalBitField<uint,10,11> nametable;
|
||||||
@@ -59,28 +60,28 @@ struct PPU : Thread {
|
|||||||
NaturalBitField<uint,16,18> fineX;
|
NaturalBitField<uint,16,18> fineX;
|
||||||
} v, t;
|
} v, t;
|
||||||
|
|
||||||
bool nmiHold;
|
bool nmiHold = 0;
|
||||||
bool nmiFlag;
|
bool nmiFlag = 0;
|
||||||
|
|
||||||
//$2000
|
//$2000
|
||||||
uint vramIncrement;
|
uint vramIncrement = 1;
|
||||||
uint spriteAddress;
|
uint spriteAddress = 0;
|
||||||
uint bgAddress;
|
uint bgAddress = 0;
|
||||||
uint spriteHeight;
|
uint spriteHeight = 0;
|
||||||
bool masterSelect;
|
bool masterSelect = 0;
|
||||||
bool nmiEnable;
|
bool nmiEnable = 0;
|
||||||
|
|
||||||
//$2001
|
//$2001
|
||||||
bool grayscale;
|
bool grayscale = 0;
|
||||||
bool bgEdgeEnable;
|
bool bgEdgeEnable = 0;
|
||||||
bool spriteEdgeEnable;
|
bool spriteEdgeEnable = 0;
|
||||||
bool bgEnable;
|
bool bgEnable = 0;
|
||||||
bool spriteEnable;
|
bool spriteEnable = 0;
|
||||||
uint3 emphasis;
|
uint3 emphasis;
|
||||||
|
|
||||||
//$2002
|
//$2002
|
||||||
bool spriteOverflow;
|
bool spriteOverflow = 0;
|
||||||
bool spriteZeroHit;
|
bool spriteZeroHit = 0;
|
||||||
|
|
||||||
//$2003
|
//$2003
|
||||||
uint8 oamAddress;
|
uint8 oamAddress;
|
||||||
@@ -106,8 +107,8 @@ struct PPU : Thread {
|
|||||||
uint16 tiledataLo;
|
uint16 tiledataLo;
|
||||||
uint16 tiledataHi;
|
uint16 tiledataHi;
|
||||||
|
|
||||||
uint oamIterator;
|
uint oamIterator = 0;
|
||||||
uint oamCounter;
|
uint oamCounter = 0;
|
||||||
|
|
||||||
OAM oam[8]; //primary
|
OAM oam[8]; //primary
|
||||||
OAM soam[8]; //secondary
|
OAM soam[8]; //secondary
|
||||||
|
@@ -3,14 +3,11 @@ auto System::serialize() -> serializer {
|
|||||||
|
|
||||||
uint signature = 0x31545342;
|
uint signature = 0x31545342;
|
||||||
char version[16] = {0};
|
char version[16] = {0};
|
||||||
char hash[64] = {0};
|
|
||||||
char description[512] = {0};
|
char description[512] = {0};
|
||||||
memory::copy(&version, (const char*)Emulator::SerializerVersion, Emulator::SerializerVersion.size());
|
memory::copy(&version, (const char*)Emulator::SerializerVersion, Emulator::SerializerVersion.size());
|
||||||
memory::copy(&hash, (const char*)cartridge.sha256(), 64);
|
|
||||||
|
|
||||||
s.integer(signature);
|
s.integer(signature);
|
||||||
s.array(version);
|
s.array(version);
|
||||||
s.array(hash);
|
|
||||||
s.array(description);
|
s.array(description);
|
||||||
|
|
||||||
serializeAll(s);
|
serializeAll(s);
|
||||||
@@ -20,18 +17,16 @@ auto System::serialize() -> serializer {
|
|||||||
auto System::unserialize(serializer& s) -> bool {
|
auto System::unserialize(serializer& s) -> bool {
|
||||||
uint signature;
|
uint signature;
|
||||||
char version[16];
|
char version[16];
|
||||||
char hash[64];
|
|
||||||
char description[512];
|
char description[512];
|
||||||
|
|
||||||
s.integer(signature);
|
s.integer(signature);
|
||||||
s.array(version);
|
s.array(version);
|
||||||
s.array(hash);
|
|
||||||
s.array(description);
|
s.array(description);
|
||||||
|
|
||||||
if(signature != 0x31545342) return false;
|
if(signature != 0x31545342) return false;
|
||||||
if(string{version} != Emulator::SerializerVersion) return false;
|
if(string{version} != Emulator::SerializerVersion) return false;
|
||||||
|
|
||||||
power();
|
power(/* reset = */ false);
|
||||||
serializeAll(s);
|
serializeAll(s);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -54,12 +49,10 @@ auto System::serializeInit() -> void {
|
|||||||
|
|
||||||
uint signature = 0;
|
uint signature = 0;
|
||||||
char version[16];
|
char version[16];
|
||||||
char hash[64];
|
|
||||||
char description[512];
|
char description[512];
|
||||||
|
|
||||||
s.integer(signature);
|
s.integer(signature);
|
||||||
s.array(version);
|
s.array(version);
|
||||||
s.array(hash);
|
|
||||||
s.array(description);
|
s.array(description);
|
||||||
|
|
||||||
serializeAll(s);
|
serializeAll(s);
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace Famicom {
|
namespace Famicom {
|
||||||
|
|
||||||
#include "video.cpp"
|
|
||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
System system;
|
System system;
|
||||||
Scheduler scheduler;
|
Scheduler scheduler;
|
||||||
@@ -62,20 +61,17 @@ auto System::unload() -> void {
|
|||||||
information.loaded = false;
|
information.loaded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto System::power() -> void {
|
auto System::power(bool reset) -> void {
|
||||||
Emulator::video.reset();
|
Emulator::video.reset(interface);
|
||||||
Emulator::video.setInterface(interface);
|
Emulator::video.setPalette();
|
||||||
configureVideoPalette();
|
|
||||||
configureVideoEffects();
|
|
||||||
|
|
||||||
Emulator::audio.reset();
|
Emulator::audio.reset(interface);
|
||||||
Emulator::audio.setInterface(interface);
|
|
||||||
|
|
||||||
scheduler.reset();
|
scheduler.reset();
|
||||||
cartridge.power();
|
cartridge.power();
|
||||||
cpu.power();
|
cpu.power(reset);
|
||||||
apu.power();
|
apu.power(reset);
|
||||||
ppu.power();
|
ppu.power(reset);
|
||||||
scheduler.primary(cpu);
|
scheduler.primary(cpu);
|
||||||
|
|
||||||
controllerPort1.power(ID::Port::Controller1);
|
controllerPort1.power(ID::Port::Controller1);
|
||||||
|
@@ -11,15 +11,11 @@ struct System {
|
|||||||
auto load(Emulator::Interface*) -> bool;
|
auto load(Emulator::Interface*) -> bool;
|
||||||
auto save() -> void;
|
auto save() -> void;
|
||||||
auto unload() -> void;
|
auto unload() -> void;
|
||||||
auto power() -> void;
|
auto power(bool reset) -> void;
|
||||||
|
|
||||||
auto init() -> void;
|
auto init() -> void;
|
||||||
auto term() -> void;
|
auto term() -> void;
|
||||||
|
|
||||||
//video.cpp
|
|
||||||
auto configureVideoPalette() -> void;
|
|
||||||
auto configureVideoEffects() -> void;
|
|
||||||
|
|
||||||
//serialization.cpp
|
//serialization.cpp
|
||||||
auto serialize() -> serializer;
|
auto serialize() -> serializer;
|
||||||
auto unserialize(serializer&) -> bool;
|
auto unserialize(serializer&) -> bool;
|
||||||
|
@@ -1,6 +0,0 @@
|
|||||||
auto System::configureVideoPalette() -> void {
|
|
||||||
Emulator::video.setPalette();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto System::configureVideoEffects() -> void {
|
|
||||||
}
|
|
@@ -1,13 +1,13 @@
|
|||||||
processors += lr35902
|
processors += sm83
|
||||||
|
|
||||||
objects += gb-interface gb-system
|
objects += gb-interface gb-system
|
||||||
objects += gb-memory gb-cartridge
|
objects += gb-memory gb-cartridge
|
||||||
objects += gb-cpu gb-ppu gb-apu
|
objects += gb-cpu gb-ppu gb-apu
|
||||||
|
|
||||||
obj/gb-interface.o: gb/interface/interface.cpp $(call rwildcard,gb/interface/)
|
obj/gb-interface.o: gb/interface/interface.cpp
|
||||||
obj/gb-system.o: gb/system/system.cpp $(call rwildcard,gb/system/)
|
obj/gb-system.o: gb/system/system.cpp
|
||||||
obj/gb-cartridge.o: gb/cartridge/cartridge.cpp $(call rwildcard,gb/cartridge/)
|
obj/gb-cartridge.o: gb/cartridge/cartridge.cpp
|
||||||
obj/gb-memory.o: gb/memory/memory.cpp $(call rwildcard,gb/memory/)
|
obj/gb-memory.o: gb/memory/memory.cpp
|
||||||
obj/gb-cpu.o: gb/cpu/cpu.cpp $(call rwildcard,gb/cpu/)
|
obj/gb-cpu.o: gb/cpu/cpu.cpp
|
||||||
obj/gb-ppu.o: gb/ppu/ppu.cpp $(call rwildcard,gb/ppu/)
|
obj/gb-ppu.o: gb/ppu/ppu.cpp
|
||||||
obj/gb-apu.o: gb/apu/apu.cpp $(call rwildcard,gb/apu/)
|
obj/gb-apu.o: gb/apu/apu.cpp
|
||||||
|
@@ -55,8 +55,8 @@ auto APU::power() -> void {
|
|||||||
create(Enter, 2 * 1024 * 1024);
|
create(Enter, 2 * 1024 * 1024);
|
||||||
if(!Model::SuperGameBoy()) {
|
if(!Model::SuperGameBoy()) {
|
||||||
stream = Emulator::audio.createStream(2, frequency());
|
stream = Emulator::audio.createStream(2, frequency());
|
||||||
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 20.0);
|
stream->addHighPassFilter(20.0, Emulator::Filter::Order::First);
|
||||||
stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3);
|
stream->addDCRemovalFilter();
|
||||||
}
|
}
|
||||||
for(uint n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
|
for(uint n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
|
||||||
|
|
||||||
@@ -68,8 +68,8 @@ auto APU::power() -> void {
|
|||||||
phase = 0;
|
phase = 0;
|
||||||
cycle = 0;
|
cycle = 0;
|
||||||
|
|
||||||
LinearFeedbackShiftRegisterGenerator r;
|
PRNG::PCG prng;
|
||||||
for(auto& n : wave.pattern) n = r();
|
for(auto& n : wave.pattern) n = prng.random();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto APU::readIO(uint16 addr) -> uint8 {
|
auto APU::readIO(uint16 addr) -> uint8 {
|
||||||
|
@@ -17,24 +17,43 @@ Cartridge cartridge;
|
|||||||
#include "tama/tama.cpp"
|
#include "tama/tama.cpp"
|
||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
|
|
||||||
|
auto Cartridge::Enter() -> void {
|
||||||
|
while(true) scheduler.synchronize(), cartridge.main();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::main() -> void {
|
||||||
|
mapper->main();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::step(uint clocks) -> void {
|
||||||
|
Thread::step(clocks);
|
||||||
|
synchronize(cpu);
|
||||||
|
}
|
||||||
|
|
||||||
auto Cartridge::load() -> bool {
|
auto Cartridge::load() -> bool {
|
||||||
information = {};
|
information = {};
|
||||||
|
rom = {};
|
||||||
|
ram = {};
|
||||||
|
rtc = {};
|
||||||
|
mapper = &mbc0;
|
||||||
|
accelerometer = false;
|
||||||
|
rumble = false;
|
||||||
|
|
||||||
if(Model::GameBoy()) {
|
if(Model::GameBoy()) {
|
||||||
if(auto loaded = platform->load(ID::GameBoy, "Game Boy", "gb")) {
|
if(auto loaded = platform->load(ID::GameBoy, "Game Boy", "gb")) {
|
||||||
information.pathID = loaded.pathID();
|
information.pathID = loaded.pathID;
|
||||||
} else return false;
|
} else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Model::GameBoyColor()) {
|
if(Model::GameBoyColor()) {
|
||||||
if(auto loaded = platform->load(ID::GameBoyColor, "Game Boy Color", "gbc")) {
|
if(auto loaded = platform->load(ID::GameBoyColor, "Game Boy Color", "gbc")) {
|
||||||
information.pathID = loaded.pathID();
|
information.pathID = loaded.pathID;
|
||||||
} else return false;
|
} else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Model::SuperGameBoy()) {
|
if(Model::SuperGameBoy()) {
|
||||||
if(auto loaded = platform->load(ID::SuperGameBoy, "Game Boy", "gb")) {
|
if(auto loaded = platform->load(ID::SuperGameBoy, "Game Boy", "gb")) {
|
||||||
information.pathID = loaded.pathID();
|
information.pathID = loaded.pathID;
|
||||||
} else return false;
|
} else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,10 +62,9 @@ auto Cartridge::load() -> bool {
|
|||||||
} else return false;
|
} else return false;
|
||||||
|
|
||||||
auto document = BML::unserialize(information.manifest);
|
auto document = BML::unserialize(information.manifest);
|
||||||
auto board = document["board"];
|
information.title = document["game/label"].text();
|
||||||
information.title = document["information/title"].text();
|
|
||||||
|
|
||||||
auto mapperID = document["board/mapper"].text();
|
auto mapperID = document["game/board"].text();
|
||||||
if(mapperID == "MBC0" ) mapper = &mbc0;
|
if(mapperID == "MBC0" ) mapper = &mbc0;
|
||||||
if(mapperID == "MBC1" ) mapper = &mbc1;
|
if(mapperID == "MBC1" ) mapper = &mbc1;
|
||||||
if(mapperID == "MBC1M") mapper = &mbc1m;
|
if(mapperID == "MBC1M") mapper = &mbc1m;
|
||||||
@@ -59,55 +77,65 @@ auto Cartridge::load() -> bool {
|
|||||||
if(mapperID == "HuC1" ) mapper = &huc1;
|
if(mapperID == "HuC1" ) mapper = &huc1;
|
||||||
if(mapperID == "HuC3" ) mapper = &huc3;
|
if(mapperID == "HuC3" ) mapper = &huc3;
|
||||||
if(mapperID == "TAMA" ) mapper = &tama;
|
if(mapperID == "TAMA" ) mapper = &tama;
|
||||||
if(!mapper) mapper = &mbc0;
|
|
||||||
|
|
||||||
accelerometer = (bool)document["board/accelerometer"];
|
accelerometer = (bool)document["game/board/accelerometer"];
|
||||||
rumble = (bool)document["board/rumble"];
|
rumble = (bool)document["game/board/rumble"];
|
||||||
|
|
||||||
rom.size = max(0x4000, document["board/rom/size"].natural());
|
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=ROM,content=Program)"]}) {
|
||||||
rom.data = (uint8*)memory::allocate(rom.size, 0xff);
|
rom.size = max(0x4000, (uint)memory.size);
|
||||||
if(auto name = document["board/rom/name"].text()) {
|
rom.data = memory::allocate<uint8>(rom.size, 0xff);
|
||||||
if(auto fp = platform->open(pathID(), name, File::Read, File::Required)) {
|
if(auto fp = platform->open(pathID(), memory.name(), File::Read, File::Required)) {
|
||||||
fp->read(rom.data, min(rom.size, fp->size()));
|
fp->read(rom.data, min(rom.size, fp->size()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ram.size = document["board/ram/size"].natural();
|
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=RAM,content=Save)"]}) {
|
||||||
ram.data = (uint8*)memory::allocate(ram.size, 0xff);
|
ram.size = memory.size;
|
||||||
if(auto name = document["board/ram/name"].text()) {
|
ram.data = memory::allocate<uint8>(ram.size, 0xff);
|
||||||
if(auto fp = platform->open(pathID(), name, File::Read, File::Optional)) {
|
if(memory.nonVolatile) {
|
||||||
|
if(auto fp = platform->open(pathID(), memory.name(), File::Read, File::Optional)) {
|
||||||
fp->read(ram.data, min(ram.size, fp->size()));
|
fp->read(ram.data, min(ram.size, fp->size()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rtc.size = document["board/rtc/size"].natural();
|
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=RTC,content=Time)"]}) {
|
||||||
rtc.data = (uint8*)memory::allocate(rtc.size, 0xff);
|
rtc.size = memory.size;
|
||||||
if(auto name = document["board/rtc/name"].text()) {
|
rtc.data = memory::allocate<uint8>(rtc.size, 0xff);
|
||||||
if(auto fp = platform->open(pathID(), name, File::Read, File::Optional)) {
|
if(memory.nonVolatile) {
|
||||||
|
if(auto fp = platform->open(pathID(), memory.name(), File::Read, File::Optional)) {
|
||||||
fp->read(rtc.data, min(rtc.size, fp->size()));
|
fp->read(rtc.data, min(rtc.size, fp->size()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
information.sha256 = Hash::SHA256(rom.data, rom.size).digest();
|
information.sha256 = Hash::SHA256({rom.data, rom.size}).digest();
|
||||||
|
mapper->load(document);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Cartridge::save() -> void {
|
auto Cartridge::save() -> void {
|
||||||
auto document = BML::unserialize(information.manifest);
|
auto document = BML::unserialize(information.manifest);
|
||||||
|
|
||||||
if(auto name = document["board/ram/name"].text()) {
|
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=RAM,content=Save)"]}) {
|
||||||
if(auto fp = platform->open(pathID(), name, File::Write)) {
|
if(memory.nonVolatile) {
|
||||||
|
if(auto fp = platform->open(pathID(), memory.name(), File::Write)) {
|
||||||
fp->write(ram.data, ram.size);
|
fp->write(ram.data, ram.size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(auto name = document["board/rtc/name"].text()) {
|
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=RTC,content=Time)"]}) {
|
||||||
if(auto fp = platform->open(pathID(), name, File::Write)) {
|
if(memory.nonVolatile) {
|
||||||
|
if(auto fp = platform->open(pathID(), memory.name(), File::Write)) {
|
||||||
fp->write(rtc.data, rtc.size);
|
fp->write(rtc.data, rtc.size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mapper->save(document);
|
||||||
|
}
|
||||||
|
|
||||||
auto Cartridge::unload() -> void {
|
auto Cartridge::unload() -> void {
|
||||||
delete[] rom.data;
|
delete[] rom.data;
|
||||||
delete[] ram.data;
|
delete[] ram.data;
|
||||||
@@ -142,6 +170,8 @@ auto Cartridge::writeIO(uint16 addr, uint8 data) -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto Cartridge::power() -> void {
|
auto Cartridge::power() -> void {
|
||||||
|
create(Enter, 4 * 1024 * 1024);
|
||||||
|
|
||||||
for(uint n = 0x0000; n <= 0x7fff; n++) bus.mmio[n] = this;
|
for(uint n = 0x0000; n <= 0x7fff; n++) bus.mmio[n] = this;
|
||||||
for(uint n = 0xa000; n <= 0xbfff; n++) bus.mmio[n] = this;
|
for(uint n = 0xa000; n <= 0xbfff; n++) bus.mmio[n] = this;
|
||||||
bus.mmio[0xff50] = this;
|
bus.mmio[0xff50] = this;
|
||||||
@@ -167,4 +197,10 @@ auto Cartridge::Memory::write(uint address, uint8 byte) -> void {
|
|||||||
data[address] = byte;
|
data[address] = byte;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
auto Cartridge::Mapper::main() -> void {
|
||||||
|
cartridge.step(cartridge.frequency());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
struct Cartridge : MMIO {
|
struct Cartridge : Thread, MMIO {
|
||||||
auto pathID() const -> uint { return information.pathID; }
|
auto pathID() const -> uint { return information.pathID; }
|
||||||
auto sha256() const -> string { return information.sha256; }
|
auto hash() const -> string { return information.sha256; }
|
||||||
auto manifest() const -> string { return information.manifest; }
|
auto manifest() const -> string { return information.manifest; }
|
||||||
auto title() const -> string { return information.title; }
|
auto title() const -> string { return information.title; }
|
||||||
|
|
||||||
|
static auto Enter() -> void;
|
||||||
auto load() -> bool;
|
auto load() -> bool;
|
||||||
auto save() -> void;
|
auto save() -> void;
|
||||||
auto unload() -> void;
|
auto unload() -> void;
|
||||||
@@ -11,6 +12,8 @@ struct Cartridge : MMIO {
|
|||||||
auto readIO(uint16 address) -> uint8;
|
auto readIO(uint16 address) -> uint8;
|
||||||
auto writeIO(uint16 address, uint8 data) -> void;
|
auto writeIO(uint16 address, uint8 data) -> void;
|
||||||
|
|
||||||
|
auto main() -> void;
|
||||||
|
auto step(uint clocks) -> void;
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
auto second() -> void;
|
auto second() -> void;
|
||||||
|
|
||||||
@@ -35,6 +38,9 @@ struct Cartridge : MMIO {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
struct Mapper {
|
struct Mapper {
|
||||||
|
virtual auto load(Markup::Node document) -> void {}
|
||||||
|
virtual auto save(Markup::Node document) -> void {}
|
||||||
|
virtual auto main() -> void;
|
||||||
virtual auto second() -> void {}
|
virtual auto second() -> void {}
|
||||||
virtual auto read(uint16 address) -> uint8 = 0;
|
virtual auto read(uint16 address) -> uint8 = 0;
|
||||||
virtual auto write(uint16 address, uint8 data) -> void = 0;
|
virtual auto write(uint16 address, uint8 data) -> void = 0;
|
||||||
|
@@ -32,7 +32,7 @@ auto Cartridge::MBC5::write(uint16 address, uint8 data) -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if((address & 0xe000) == 0x4000) { //$4000-5fff
|
if((address & 0xe000) == 0x4000) { //$4000-5fff
|
||||||
if(cartridge.rumble) platform->inputRumble(ID::Port::Hardware, ID::Device::Controls, 10, data.bit(3));
|
if(cartridge.rumble) platform->inputRumble(ID::Port::Cartridge, ID::Device::MBC5, 0, data.bit(3));
|
||||||
io.ram.bank = data.bits(0,3);
|
io.ram.bank = data.bits(0,3);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
239
higan/gb/cartridge/mbc7/eeprom.cpp
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
//Microchip 93LCx6
|
||||||
|
// 93LC46 => 1024 cells => 128 x 8-bit or 64 x 16-bit
|
||||||
|
// 93LC56 => 2048 cells => 256 x 8-bit or 128 x 16-bit
|
||||||
|
// 93LC66 => 4096 cells => 512 x 8-bit or 256 x 16-bit
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::load(Markup::Node document) -> void {
|
||||||
|
for(auto& byte : data) byte = 0xff;
|
||||||
|
size = 512; //EEPROM size is in bytes
|
||||||
|
width = 16; //16-bit configuration
|
||||||
|
|
||||||
|
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=EEPROM,content=Save)"]}) {
|
||||||
|
if(memory.size == 128) size = 128;
|
||||||
|
if(memory.size == 256) size = 256;
|
||||||
|
if(memory.size == 512) size = 512;
|
||||||
|
|
||||||
|
if(auto fp = platform->open(cartridge.pathID(), memory.name(), File::Read, File::Optional)) {
|
||||||
|
fp->read(data, min(fp->size(), sizeof(data)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//note: the 93LC56 alone has an extra dummy address bit
|
||||||
|
if(size == 128) input.addressLength = width == 16 ? 6 : 7; //93LC46
|
||||||
|
if(size == 256) input.addressLength = width == 16 ? 8 : 9; //93LC56
|
||||||
|
if(size == 512) input.addressLength = width == 16 ? 8 : 9; //93LC66
|
||||||
|
input.dataLength = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::save(Markup::Node document) -> void {
|
||||||
|
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=EEPROM,content=Save)"]}) {
|
||||||
|
if(auto fp = platform->open(cartridge.pathID(), memory.name(), File::Write)) {
|
||||||
|
fp->write(data, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::main() -> void {
|
||||||
|
//step by approximately one millisecond
|
||||||
|
cartridge.step(cartridge.frequency() / 1000);
|
||||||
|
|
||||||
|
//set during programming commands
|
||||||
|
if(busy) busy--;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::power() -> void {
|
||||||
|
select = 0;
|
||||||
|
clock = 0;
|
||||||
|
writable = 0;
|
||||||
|
busy = 0;
|
||||||
|
|
||||||
|
input.flush();
|
||||||
|
output.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::readIO() -> uint8 {
|
||||||
|
uint8 data = 0b00'1111'00;
|
||||||
|
data.bit(7) = select;
|
||||||
|
data.bit(6) = clock;
|
||||||
|
data.bit(1) = input.edge();
|
||||||
|
if(!select) {
|
||||||
|
data.bit(0) = 1; //high-z when the chip is idle (not selected)
|
||||||
|
} else if(busy) {
|
||||||
|
data.bit(0) = 0; //low when a programming command is in progress
|
||||||
|
} else if(output.count) {
|
||||||
|
data.bit(0) = output.edge(); //shift register data during read commands
|
||||||
|
} else {
|
||||||
|
data.bit(0) = 1; //high-z during all other commands
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::writeIO(uint8 data) -> void {
|
||||||
|
//chip enters idle state on falling CS edge
|
||||||
|
if(select && !data.bit(7)) return power();
|
||||||
|
|
||||||
|
//chip leaves idle state on rising CS edge
|
||||||
|
if(!(select = data.bit(7))) return;
|
||||||
|
|
||||||
|
//input shift register clocks on rising edge
|
||||||
|
if(!clock.raise(data.bit(6))) return;
|
||||||
|
|
||||||
|
//read mode
|
||||||
|
if(output.count && !data.bit(1)) {
|
||||||
|
if(input.start() && *input.start() == 1) {
|
||||||
|
if(input.opcode() && *input.opcode() == 0b10) {
|
||||||
|
output.read();
|
||||||
|
if(output.count == 0) {
|
||||||
|
//sequential read mode
|
||||||
|
input.increment();
|
||||||
|
read();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
output.flush();
|
||||||
|
|
||||||
|
input.write(data.bit(1));
|
||||||
|
|
||||||
|
//wait for start
|
||||||
|
if(!input.start()) return;
|
||||||
|
uint start = *input.start();
|
||||||
|
|
||||||
|
//start bit must be set
|
||||||
|
if(start != 1) return input.flush();
|
||||||
|
|
||||||
|
//wait for opcode
|
||||||
|
if(!input.opcode()) return;
|
||||||
|
uint opcode = *input.opcode();
|
||||||
|
|
||||||
|
//wait for address
|
||||||
|
if(!input.address()) return;
|
||||||
|
|
||||||
|
if(opcode == 0b00) {
|
||||||
|
auto mode = *input.mode();
|
||||||
|
if(mode == 0b00) return writeDisable();
|
||||||
|
if(mode == 0b01) return writeAll();
|
||||||
|
if(mode == 0b10) return eraseAll();
|
||||||
|
if(mode == 0b11) return writeEnable();
|
||||||
|
}
|
||||||
|
if(opcode == 0b01) return write();
|
||||||
|
if(opcode == 0b10) return read();
|
||||||
|
if(opcode == 0b11) return erase();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::read() -> void {
|
||||||
|
uint address = *input.address() << (width == 16) & size - 1;
|
||||||
|
output.flush();
|
||||||
|
for(uint4 index : range(width)) {
|
||||||
|
output.write(data[address + !index.bit(3)].bit(index.bits(0,2)));
|
||||||
|
}
|
||||||
|
output.write(0); //reads have an extra dummy data bit
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::write() -> void {
|
||||||
|
if(!input.data()) return; //wait for data
|
||||||
|
if(!writable) return input.flush();
|
||||||
|
uint address = *input.address() << (width == 16) & size - 1;
|
||||||
|
for(uint4 index : range(width)) {
|
||||||
|
data[address + !index.bit(3)].bit(index.bits(0,2)) = input.read();
|
||||||
|
}
|
||||||
|
busy = 4; //milliseconds
|
||||||
|
return input.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::erase() -> void {
|
||||||
|
if(!writable) return input.flush();
|
||||||
|
uint address = *input.address() << (width == 16) & size - 1;
|
||||||
|
for(uint index : range(width)) {
|
||||||
|
data[address + index / 8].bit(index % 8) = 1;
|
||||||
|
}
|
||||||
|
busy = 4; //milliseconds
|
||||||
|
return input.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::writeAll() -> void {
|
||||||
|
if(!input.data()) return; //wait for data
|
||||||
|
if(!writable) return input.flush();
|
||||||
|
auto word = *input.data();
|
||||||
|
for(uint address = 0; address < 512;) {
|
||||||
|
data[address++] = word.byte(width == 16);
|
||||||
|
data[address++] = word.byte(0);
|
||||||
|
}
|
||||||
|
busy = 16; //milliseconds
|
||||||
|
return input.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::eraseAll() -> void {
|
||||||
|
if(!writable) return input.flush();
|
||||||
|
for(auto& byte : data) byte = 0xff;
|
||||||
|
busy = 8; //milliseconds
|
||||||
|
return input.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::writeEnable() -> void {
|
||||||
|
writable = true;
|
||||||
|
return input.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::writeDisable() -> void {
|
||||||
|
writable = false;
|
||||||
|
return input.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::ShiftRegister::flush() -> void {
|
||||||
|
value = 0;
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::ShiftRegister::edge() -> uint1 {
|
||||||
|
return value.bit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::ShiftRegister::read() -> uint1 {
|
||||||
|
uint1 bit = value.bit(0);
|
||||||
|
value >>= 1;
|
||||||
|
count--;
|
||||||
|
return bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::ShiftRegister::write(uint1 bit) -> void {
|
||||||
|
value <<= 1;
|
||||||
|
value.bit(0) = bit;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::InputShiftRegister::start() -> maybe<uint1> {
|
||||||
|
if(count < 1) return {};
|
||||||
|
return {value >> count - 1 & 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::InputShiftRegister::opcode() -> maybe<uint2> {
|
||||||
|
if(count < 1 + 2) return {};
|
||||||
|
return {value >> count - 3 & 3};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::InputShiftRegister::mode() -> maybe<uint2> {
|
||||||
|
if(count < 1 + 2 + addressLength) return {};
|
||||||
|
return {value >> count - 5 & 3};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::InputShiftRegister::address() -> maybe<uint9> {
|
||||||
|
if(count < 1 + 2 + addressLength) return {};
|
||||||
|
return {value >> count - (3 + addressLength) & (1 << addressLength) - 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::InputShiftRegister::data() -> maybe<uint16> {
|
||||||
|
if(count < 1 + 2 + addressLength + dataLength) return {};
|
||||||
|
return {value >> count - (3 + addressLength + dataLength) & (1 << dataLength) - 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::MBC7::EEPROM::InputShiftRegister::increment() -> void {
|
||||||
|
value.bits(0, addressLength - 1)++;
|
||||||
|
}
|