Compare commits

...

No commits in common. "master" and "gambatte364-patchseries" have entirely different histories.

3539 changed files with 354864 additions and 242069 deletions

1
.gitattributes vendored
View file

@ -1 +0,0 @@
buildaux/version.cpp export-subst

23
.gitignore vendored
View file

@ -1,23 +0,0 @@
*.o
*.a
*.lib
*.obj
*.exe
*.dep
*.ldflags
*.files
docs
rom
lsnes
*.util
/core
/bsnes
/gambatte
src/fonts/font.cpp
src/core/version.cpp
src/cmdhelp/*.cpp
include/cmdhelp/*.hpp
src/emulation/make-ports
src/cmdhelp/mkstubs
src/cmdhelp/mkstubsi
buildaux/mkdeps

6
.gitmodules vendored
View file

@ -1,6 +0,0 @@
[submodule "bsnes"]
path = bsnes
url = .
[submodule "gambatte"]
path = gambatte
url = .

2190
CHANGELOG

File diff suppressed because it is too large Load diff

339
COPYING Normal file
View file

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) 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
this service 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 make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. 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.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
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
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the 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 a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE 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.
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
convey 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 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision 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, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This 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.

View file

@ -1,340 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) 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
this service 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 make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. 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.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
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
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the 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 a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE 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.
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
convey 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 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision 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, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This 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 Library General
Public License instead of this License.

View file

@ -1,674 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://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 <http://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
<http://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
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

1716
Doxyfile

File diff suppressed because it is too large Load diff

106
Makefile
View file

@ -1,106 +1,10 @@
OPTIONS=options.build
include $(OPTIONS)
all: libgambatte/__all_files__
ifndef LUA
LUA=lua
endif
libgambatte/__all_files__: forcelook
$(MAKE) -C libgambatte
#Compilers.
REALCC = $(CROSS_PREFIX)$(CC)
REALLD = $(CROSS_PREFIX)$(LD)
REALRANLIB = $(CROSS_PREFIX)$(RANLIB)
#Flags.
HOSTCCFLAGS = -std=gnu++0x
CFLAGS += -std=gnu++0x -pthread $(USER_CFLAGS)
ifdef BOOST_NEEDS_MT
BOOST_LIB_POSTFIX=-mt
endif
ifdef HOST_BOOST_NEEDS_MT
HOST_BOOST_POSTFIX=-mt
endif
LDFLAGS = -pthread -lboost_iostreams$(BOOST_LIB_POSTFIX) -lboost_filesystem$(BOOST_LIB_POSTFIX) -lboost_system$(BOOST_LIB_POSTFIX) -lz $(USER_LDFLAGS)
HOSTHELPER_LDFLAGS =
ifeq ($(THREADS), NATIVE)
CFLAGS += -DNATIVE_THREADS
else
ifeq ($(THREADS), BOOST)
CFLAGS += -DBOOST_THREADS
LDFLAGS += -lboost_thread$(BOOST_LIB_POSTFIX)
else
$(error "Bad value for THREADS (expected NATIVE or BOOST)")
endif
endif
ifeq ($(REGEX), BOOST)
CFLAGS += -DUSE_BOOST_REGEX
LDFLAGS += -lboost_regex$(BOOST_LIB_POSTFIX)
HOSTHELPER_LDFLAGS += -lboost_regex$(HOST_BOOST_POSTFIX)
endif
HOSTHELPER_LDFLAGS += -lboost_system$(HOST_BOOST_POSTFIX)
ifdef NEED_LIBICONV
LDFLAGS += -liconv
endif
ifdef USE_LIBGCRYPT
CFLAGS += -DUSE_LIBGCRYPT_SHA256
LDFLAGS += -lgcrypt -lgpg-error
endif
ifdef USE_LIBLZMA
CFLAGS += -DLIBLZMA_AVAILABLE
LDFLAGS += -llzma
endif
ifeq ($(ARCHITECTURE), I386)
CFLAGS += -DARCH_IS_I386
else
endif
export
all: src/__all_files__
CFLAGS += $(shell $(CROSS_PREFIX)pkg-config $(LUA) --cflags)
LDFLAGS += $(shell $(CROSS_PREFIX)pkg-config $(LUA) --libs)
CFLAGS += $(shell $(CROSS_PREFIX)curl-config --cflags)
LDFLAGS += $(shell $(CROSS_PREFIX)curl-config --libs)
compiler=$(subst ++,cc,$(REALCC))
gambatte_compiler=$(REALCC)
bsnes/out/libsnes.$(ARCHIVE_SUFFIX): forcelook
$(MAKE) -C bsnes $(BSNES_PROFILE_STRING) $(BSNES_TARGET_STRING)
$(REALRANLIB) bsnes/out/libsnes.$(ARCHIVE_SUFFIX)
src/__all_files__: src/core/version.cpp buildaux/mkdeps$(DOT_EXECUTABLE_SUFFIX) buildaux/txt2cstr$(DOT_EXECUTABLE_SUFFIX) forcelook
$(MAKE) -C src precheck
$(MAKE) -C src
cp src/lsnes$(DOT_EXECUTABLE_SUFFIX) .
buildaux/txt2cstr$(DOT_EXECUTABLE_SUFFIX): buildaux/txt2cstr.cpp
$(HOSTCC) $(HOSTCCFLAGS) -o $@ $<
buildaux/version$(DOT_EXECUTABLE_SUFFIX): buildaux/version.cpp VERSION
$(HOSTCC) $(HOSTCCFLAGS) -o $@ $<
buildaux/mkdeps$(DOT_EXECUTABLE_SUFFIX): buildaux/mkdeps.cpp VERSION
$(HOSTCC) $(HOSTCCFLAGS) -o $@ $< -lboost_filesystem$(HOST_BOOST_POSTFIX) -lboost_system$(HOST_BOOST_POSTFIX)
src/core/version.cpp: buildaux/version$(DOT_EXECUTABLE_SUFFIX) forcelook
buildaux/version$(DOT_EXECUTABLE_SUFFIX) >$@
platclean:
$(MAKE) -C src platclean
clean:
$(MAKE) -C src clean
rm -f buildaux/version$(DOT_EXECUTABLE_SUFFIX)
rm -f buildaux/mkdeps$(DOT_EXECUTABLE_SUFFIX)
rm -f buildaux/txt2cstr$(DOT_EXECUTABLE_SUFFIX)
clean: forcelook
$(MAKE) -C libgambatte clean
forcelook:
@true

129
README Normal file
View file

@ -0,0 +1,129 @@
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Copyright (C) 2007 by Sindre Aamås
sinamas@users.sourceforge.net
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
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 version 2 for more details.
You should have received a copy of the GNU General Public License
version 2 along with this program; if not, write to the
Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
About
--------------------------------------------------------------------------------
Gambatte is an accuracy-focused, open-source, cross-platform
Game Boy Color emulator written in C++. It is based on a few thousand
corner-case hardware tests, as well as previous documentation and reverse
engineering efforts.
The core emulation code is contained in a separate library backend
(libgambatte) written in platform-independent C++. There is currently a Qt GUI
frontend (gambatte_qt), and a simple command line SDL frontend (gambatte_sdl).
The Qt frontend has been ported to Windows, Mac OS X, and Linux/BSD/Unix-like
OSes with audio/video engines utilizing native APIs on these platforms.
The SDL frontend should be usable on all platforms with a working SDL port. It
should also be quite trivial to create new (simple) frontends (note that the
library API should in no way be considered stable).
Usage
--------------------------------------------------------------------------------
You will have to supply Gambatte with a ROM image file of the GB/GBC
program/game you would like to run/play, either as a command line argument, or
through the File->Open... menu in gambatte_qt.
gambatte_sdl keyboard commands:
TAB - fast-forward
Ctrl-f - toggle full screen
Ctrl-r - reset
F5 - save state
F6 - previous state slot
F7 - next state slot
F8 - load state
0 to 9 - select state slot 0 to 9
Default key mapping:
Up: Up
Down: Down
Left: Left
Right: Right
A: D
B: C
Start: Return
Select: Shift
Compiling
--------------------------------------------------------------------------------
Building Gambatte from source code can be done by executing the
build_<qt/sdl>.sh scripts for the qt/sdl frontends respectively, or by issueing
the correct build command (either 'scons' or 'qmake && make') in the top-level
subdirectories (libgambatte will have to be built first). The clean.sh script
can be executed to remove all generated files after a compile (including
binaries).
Requirements for building libgambatte:
- A decent C++ compiler (like g++ in the GNU Compiler Collection).
- SCons.
- optionally zlib
Requirements for building gambatte_sdl:
- A decent C++ compiler (like g++ in the GNU Compiler Collection).
- SDL headers and library.
- SCons.
(- libgambatte.)
Requirements for building gambatte_qt:
- A decent C++ compiler (like g++ in the GNU Compiler Collection).
- Qt4 (Core, GUI, OpenGL) headers and library.
- Qmake and make (GNU Make should work).
- Platform-specific requirements:
* MS Windows:
- Win32 API headers and libraries.
- DirectSound and DirectDraw7 headers and libraries.
- Direct3D9 headers
* Linux/BSD/UNIX-like OSes:
- POSIX/UNIX headers (unistd.h, fcntl.h, sys/ioctl.h, sys/time.h, sys/shm.h).
- Open Sound System header (sys/soundcard.h).
- X11 Xlib, XVideo, XRandR and XShm headers and libraries.
- Alsa headers and library (Linux only).
* Max OS X:
- Recent Mac OS X SDK (Panther Xcode/SDK should work)
(- libgambatte.)
Installing after a compile simply amounts to copying the generated binary
(either gambatte_qt/bin/gambatte_qt<.exe> or gambatte_sdl/gambatte_sdl<.exe>)
to wherever you'd like to keep it.
Thanks
--------------------------------------------------------------------------------
Derek Liauw Kie Fa (Kreed)
Gilles Vollant
Jeff Frohwein
Jonathan Gevaryahu (Lord Nightmare)
kOOPa
Marat Fayzullin
Martin Korth (nocash)
Maxim Stepin (MaxSt)
Nach
Pan of Anthrox
Pascal Felber
Paul Robson
SDL
Shay Green (blargg)
The OpenGL Extension Wrangler Library
--------------------------------------------------------------------------------
Game Boy and Game Boy Color are registered trademarks of
Nintendo of America Inc.
Gambatte is not affiliated with or endorsed by any of the companies mentioned.

3
TODO
View file

@ -1,3 +0,0 @@
- Memory Tracking: Track dynamic state.
- Fix paths and filenames containing non-ASCII on Win32.
- Win64 build.

View file

@ -1 +0,0 @@
2-β24

1
bsnes

@ -1 +0,0 @@
Subproject commit 4cfbbeadc3abe3e3911f7f59ce57b715edc76563

View file

@ -1,82 +0,0 @@
From 831e9614a7babbacf59935960fbaa6cfc8d49c08 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Wed, 9 Nov 2011 00:30:36 +0200
Subject: [PATCH 01/10] Make libsnes compile
Changes between v083 and v084 had broken libsnes. Fix it so it at least
compiles.
---
ui-libsnes/libsnes.cpp | 37 +++++++++++++++++++++++++++++++++++--
1 file changed, 35 insertions(+), 2 deletions(-)
diff --git a/ui-libsnes/libsnes.cpp b/ui-libsnes/libsnes.cpp
index fbb4482..5f5ded6 100755
--- a/ui-libsnes/libsnes.cpp
+++ b/ui-libsnes/libsnes.cpp
@@ -1,5 +1,6 @@
#include "libsnes.hpp"
#include <snes/snes.hpp>
+#include <gameboy/gameboy.hpp>
#include <nall/snes/cartridge.hpp>
#include <nall/gameboy/cartridge.hpp>
@@ -46,6 +47,38 @@ struct Interface : public SNES::Interface {
print(text, "\n");
}
+ void setCheats(const lstring &list = lstring{}) {
+ if(SNES::cartridge.mode() == SNES::Cartridge::Mode::SuperGameBoy) {
+ GameBoy::cheat.reset();
+ for(auto &code : list) {
+ lstring codelist;
+ codelist.split("+", code);
+ for(auto &part : codelist) {
+ unsigned addr, data, comp;
+ if(GameBoy::Cheat::decode(part, addr, data, comp)) {
+ GameBoy::cheat.append({ addr, data, comp });
+ }
+ }
+ }
+ GameBoy::cheat.synchronize();
+ return;
+ }
+
+ SNES::cheat.reset();
+ for(auto &code : list) {
+ lstring codelist;
+ codelist.split("+", code);
+ for(auto &part : codelist) {
+ unsigned addr, data;
+ if(SNES::Cheat::decode(part, addr, data)) {
+ SNES::cheat.append({ addr, data });
+ }
+ }
+ }
+ SNES::cheat.synchronize();
+ }
+
+
string path(SNES::Cartridge::Slot slot, const string &hint) {
return { basename, hint };
}
@@ -115,7 +148,7 @@ void snes_set_cartridge_basename(const char *basename) {
}
void snes_init(void) {
- interface.initialize(&interface);
+ SNES::system.init();
SNES::input.connect(SNES::Controller::Port1, SNES::Input::Device::Joypad);
SNES::input.connect(SNES::Controller::Port2, SNES::Input::Device::Joypad);
}
@@ -244,7 +277,7 @@ bool snes_load_cartridge_super_game_boy(
uint8_t *data = new uint8_t[dmg_size];
memcpy(data, dmg_data, dmg_size);
string xmldmg = (dmg_xml && *dmg_xml) ? string(dmg_xml) : GameBoyCartridge(data, dmg_size).markup;
- GameBoy::cartridge.load(xmldmg, data, dmg_size);
+ GameBoy::cartridge.load(GameBoy::System::Revision::SuperGameBoy, xmldmg, data, dmg_size);
delete[] data;
}
SNES::cartridge.load(SNES::Cartridge::Mode::SuperGameBoy, xmlrom);
--
1.8.4.4

View file

@ -1,26 +0,0 @@
From df7851648e41ae6b6efd1a54cdcd32ac55a90131 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Wed, 9 Nov 2011 00:31:59 +0200
Subject: [PATCH 02/10] Fix bsnes version number in libsnes to be v084, not
v083
---
ui-libsnes/libsnes.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ui-libsnes/libsnes.cpp b/ui-libsnes/libsnes.cpp
index 5f5ded6..6b4ef12 100755
--- a/ui-libsnes/libsnes.cpp
+++ b/ui-libsnes/libsnes.cpp
@@ -112,7 +112,7 @@ struct Interface : public SNES::Interface {
static Interface interface;
const char* snes_library_id(void) {
- return "bsnes v083";
+ return "bsnes v084";
}
unsigned snes_library_revision_major(void) {
--
1.8.4.4

View file

@ -1,84 +0,0 @@
From b481e3d161d924cc5a4449329393c8d9f23b27ec Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Wed, 9 Nov 2011 00:37:44 +0200
Subject: [PATCH 03/10] Don't use time() in emulating chips
Instead of using time() in chip emulation, create new interface method
currentTime(), defaulting to time(0). This way frontend can cleanly
override the current time bsnes is using.
---
snes/chip/bsx/satellaview/satellaview.cpp | 2 +-
snes/chip/spc7110/spc7110.cpp | 2 +-
snes/chip/srtc/srtc.cpp | 2 +-
snes/interface/interface.cpp | 5 +++++
snes/interface/interface.hpp | 1 +
5 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/snes/chip/bsx/satellaview/satellaview.cpp b/snes/chip/bsx/satellaview/satellaview.cpp
index 7bfd13f..b2565ea 100755
--- a/snes/chip/bsx/satellaview/satellaview.cpp
+++ b/snes/chip/bsx/satellaview/satellaview.cpp
@@ -39,7 +39,7 @@ uint8 BSXSatellaview::mmio_read(unsigned addr) {
if(counter == 0) {
time_t rawtime;
- time(&rawtime);
+ rawtime = SNES::interface->currentTime();
tm *t = localtime(&rawtime);
regs.r2192_hour = t->tm_hour;
diff --git a/snes/chip/spc7110/spc7110.cpp b/snes/chip/spc7110/spc7110.cpp
index 835ddbb..f5463e5 100755
--- a/snes/chip/spc7110/spc7110.cpp
+++ b/snes/chip/spc7110/spc7110.cpp
@@ -102,7 +102,7 @@ void SPC7110::set_data_adjust(unsigned addr) { r4814 = addr; r4815 = addr >> 8;
void SPC7110::update_time(int offset) {
time_t rtc_time = (rtc[16] << 0) | (rtc[17] << 8) | (rtc[18] << 16) | (rtc[19] << 24);
- time_t current_time = time(0) - offset;
+ time_t current_time = SNES::interface->currentTime() - offset;
//sizeof(time_t) is platform-dependent; though rtc[] needs to be platform-agnostic.
//yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by
diff --git a/snes/chip/srtc/srtc.cpp b/snes/chip/srtc/srtc.cpp
index 1dd8f86..b11b756 100755
--- a/snes/chip/srtc/srtc.cpp
+++ b/snes/chip/srtc/srtc.cpp
@@ -32,7 +32,7 @@ void SRTC::reset() {
void SRTC::update_time() {
time_t rtc_time = (rtc[16] << 0) | (rtc[17] << 8) | (rtc[18] << 16) | (rtc[19] << 24);
- time_t current_time = time(0);
+ time_t current_time = SNES::interface->currentTime();
//sizeof(time_t) is platform-dependent; though rtc[] needs to be platform-agnostic.
//yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by
diff --git a/snes/interface/interface.cpp b/snes/interface/interface.cpp
index a0e3a81..b3017c9 100755
--- a/snes/interface/interface.cpp
+++ b/snes/interface/interface.cpp
@@ -18,4 +18,9 @@ void Interface::message(const string &text) {
print(text, "\n");
}
+time_t Interface::currentTime()
+{
+ return time(0);
+}
+
}
diff --git a/snes/interface/interface.hpp b/snes/interface/interface.hpp
index f1a48c0..df975e8 100755
--- a/snes/interface/interface.hpp
+++ b/snes/interface/interface.hpp
@@ -5,6 +5,7 @@ struct Interface {
virtual string path(Cartridge::Slot slot, const string &hint) = 0;
virtual void message(const string &text);
+ virtual time_t currentTime();
};
extern Interface *interface;
--
1.8.4.4

View file

@ -1,345 +0,0 @@
From af7fdd9f73a3eb5e9266c59bfb4dd676679b2f7d Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Wed, 9 Nov 2011 01:52:08 +0200
Subject: [PATCH 04/10] Save controller state when savestating
When savestating, save the controller state and restore it upon loadstate.
Prevents libsnes from mixing up buttons.
---
snes/controller/controller.cpp | 8 +++++++
snes/controller/controller.hpp | 2 ++
snes/controller/gamepad/gamepad.cpp | 13 +++++++++++
snes/controller/gamepad/gamepad.hpp | 2 +-
snes/controller/justifier/justifier.cpp | 36 +++++++++++++++++++++++++++++++
snes/controller/justifier/justifier.hpp | 1 +
snes/controller/mouse/mouse.cpp | 13 +++++++++++
snes/controller/mouse/mouse.hpp | 2 +-
snes/controller/multitap/multitap.cpp | 16 ++++++++++++++
snes/controller/multitap/multitap.hpp | 2 +-
snes/controller/superscope/superscope.cpp | 31 ++++++++++++++++++++++++++
snes/controller/superscope/superscope.hpp | 1 +
snes/input/input.cpp | 15 +++++++++++++
snes/input/input.hpp | 1 +
snes/system/serialization.cpp | 1 +
15 files changed, 141 insertions(+), 3 deletions(-)
diff --git a/snes/controller/controller.cpp b/snes/controller/controller.cpp
index 9091b21..f254bed 100755
--- a/snes/controller/controller.cpp
+++ b/snes/controller/controller.cpp
@@ -46,8 +46,16 @@ void Controller::iobit(bool data) {
}
}
+void Controller::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save a zero block.
+ unsigned char blockzeroes[SaveSize] = {0};
+ s.array(blockzeroes, SaveSize);
+}
+
Controller::Controller(bool port) : port(port) {
if(!thread) create(Controller::Enter, 1);
}
+
}
diff --git a/snes/controller/controller.hpp b/snes/controller/controller.hpp
index 7332712..827b2eb 100755
--- a/snes/controller/controller.hpp
+++ b/snes/controller/controller.hpp
@@ -13,12 +13,14 @@
struct Controller : Processor {
enum : bool { Port1 = 0, Port2 = 1 };
+ enum { SaveSize = 16 };
const bool port;
static void Enter();
virtual void enter();
void step(unsigned clocks);
void synchronize_cpu();
+ virtual void serialize(serializer& s);
bool iobit();
void iobit(bool data);
diff --git a/snes/controller/gamepad/gamepad.cpp b/snes/controller/gamepad/gamepad.cpp
index 594020d..4fa1c99 100755
--- a/snes/controller/gamepad/gamepad.cpp
+++ b/snes/controller/gamepad/gamepad.cpp
@@ -13,6 +13,19 @@ void Gamepad::latch(bool data) {
counter = 0;
}
+void Gamepad::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save block.
+ unsigned char block[Controller::SaveSize] = {0};
+ block[0] = latched ? 1 : 0;
+ block[1] = counter;
+ s.array(block, Controller::SaveSize);
+ if(s.mode() == nall::serializer::Load) {
+ latched = (block[0] != 0);
+ counter = block[1];
+ }
+}
+
Gamepad::Gamepad(bool port) : Controller(port) {
latched = 0;
counter = 0;
diff --git a/snes/controller/gamepad/gamepad.hpp b/snes/controller/gamepad/gamepad.hpp
index c5ca69c..a2392d1 100755
--- a/snes/controller/gamepad/gamepad.hpp
+++ b/snes/controller/gamepad/gamepad.hpp
@@ -2,7 +2,7 @@ struct Gamepad : Controller {
uint2 data();
void latch(bool data);
Gamepad(bool port);
-
+ void serialize(serializer& s);
private:
bool latched;
unsigned counter;
diff --git a/snes/controller/justifier/justifier.cpp b/snes/controller/justifier/justifier.cpp
index 8b2d3ee..4b8eca8 100755
--- a/snes/controller/justifier/justifier.cpp
+++ b/snes/controller/justifier/justifier.cpp
@@ -103,6 +103,42 @@ void Justifier::latch(bool data) {
if(latched == 0) active = !active; //toggle between both controllers, even when unchained
}
+void Justifier::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save block.
+ unsigned char block[Controller::SaveSize] = {0};
+ block[0] = latched ? 1 : 0;
+ block[1] = counter;
+ block[2] = active ? 1 : 0;
+ block[3] = trigger1 ? 1 : 0;
+ block[4] = trigger2 ? 1 : 0;
+ block[5] = start1 ? 1 : 0;
+ block[6] = start2 ? 1 : 0;
+ block[7] = (unsigned short)x1 >> 8;
+ block[8] = (unsigned short)x1;
+ block[9] = (unsigned short)x2 >> 8;
+ block[10] = (unsigned short)x2;
+ block[11] = (unsigned short)y1 >> 8;
+ block[12] = (unsigned short)y1;
+ block[13] = (unsigned short)y2 >> 8;
+ block[14] = (unsigned short)y2;
+ s.array(block, Controller::SaveSize);
+ if(s.mode() == nall::serializer::Load) {
+ latched = (block[0] != 0);
+ counter = block[1];
+ active = (block[2] != 0);
+ trigger1 = (block[3] != 0);
+ trigger2 = (block[4] != 0);
+ start1 = (block[5] != 0);
+ start2 = (block[6] != 0);
+ x1 = (short)(((unsigned short)block[7] << 8) | (unsigned short)block[8]);
+ x2 = (short)(((unsigned short)block[9] << 8) | (unsigned short)block[10]);
+ y1 = (short)(((unsigned short)block[11] << 8) | (unsigned short)block[12]);
+ y2 = (short)(((unsigned short)block[13] << 8) | (unsigned short)block[14]);
+ }
+}
+
+
Justifier::Justifier(bool port, bool chained) : Controller(port), chained(chained) {
create(Controller::Enter, 21477272);
latched = 0;
diff --git a/snes/controller/justifier/justifier.hpp b/snes/controller/justifier/justifier.hpp
index 8259147..96e09dc 100755
--- a/snes/controller/justifier/justifier.hpp
+++ b/snes/controller/justifier/justifier.hpp
@@ -2,6 +2,7 @@ struct Justifier : Controller {
void enter();
uint2 data();
void latch(bool data);
+ void serialize(serializer& s);
Justifier(bool port, bool chained);
//private:
diff --git a/snes/controller/mouse/mouse.cpp b/snes/controller/mouse/mouse.cpp
index c9f5d16..6b26fae 100755
--- a/snes/controller/mouse/mouse.cpp
+++ b/snes/controller/mouse/mouse.cpp
@@ -61,6 +61,19 @@ void Mouse::latch(bool data) {
counter = 0;
}
+void Mouse::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save block.
+ unsigned char block[Controller::SaveSize] = {0};
+ block[0] = latched ? 1 : 0;
+ block[1] = counter;
+ s.array(block, Controller::SaveSize);
+ if(s.mode() == nall::serializer::Load) {
+ latched = (block[0] != 0);
+ counter = block[1];
+ }
+}
+
Mouse::Mouse(bool port) : Controller(port) {
latched = 0;
counter = 0;
diff --git a/snes/controller/mouse/mouse.hpp b/snes/controller/mouse/mouse.hpp
index 95e24b6..b66ea51 100755
--- a/snes/controller/mouse/mouse.hpp
+++ b/snes/controller/mouse/mouse.hpp
@@ -2,7 +2,7 @@ struct Mouse : Controller {
uint2 data();
void latch(bool data);
Mouse(bool port);
-
+ void serialize(serializer& s);
private:
bool latched;
unsigned counter;
diff --git a/snes/controller/multitap/multitap.cpp b/snes/controller/multitap/multitap.cpp
index 3a6eb72..146c41d 100755
--- a/snes/controller/multitap/multitap.cpp
+++ b/snes/controller/multitap/multitap.cpp
@@ -30,6 +30,22 @@ void Multitap::latch(bool data) {
counter2 = 0;
}
+void Multitap::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save block.
+ unsigned char block[Controller::SaveSize] = {0};
+ block[0] = latched ? 1 : 0;
+ block[1] = counter1;
+ block[2] = counter2;
+ s.array(block, Controller::SaveSize);
+ if(s.mode() == nall::serializer::Load) {
+ latched = (block[0] != 0);
+ counter1 = block[1];
+ counter2 = block[2];
+ }
+}
+
+
Multitap::Multitap(bool port) : Controller(port) {
latched = 0;
counter1 = 0;
diff --git a/snes/controller/multitap/multitap.hpp b/snes/controller/multitap/multitap.hpp
index 0540af7..e6324ac 100755
--- a/snes/controller/multitap/multitap.hpp
+++ b/snes/controller/multitap/multitap.hpp
@@ -2,7 +2,7 @@ struct Multitap : Controller {
uint2 data();
void latch(bool data);
Multitap(bool port);
-
+ void serialize(serializer& s);
private:
bool latched;
unsigned counter1;
diff --git a/snes/controller/superscope/superscope.cpp b/snes/controller/superscope/superscope.cpp
index e97a2ff..bb260b9 100755
--- a/snes/controller/superscope/superscope.cpp
+++ b/snes/controller/superscope/superscope.cpp
@@ -104,6 +104,37 @@ void SuperScope::latch(bool data) {
counter = 0;
}
+void SuperScope::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save block.
+ unsigned char block[Controller::SaveSize] = {0};
+ block[0] = latched ? 1 : 0;
+ block[1] = counter;
+ block[2] = trigger ? 1 : 0;
+ block[3] = cursor ? 1 : 0;
+ block[4] = turbo ? 1 : 0;
+ block[5] = pause ? 1 : 0;
+ block[6] = offscreen ? 1 : 0;
+ block[7] = (unsigned short)x >> 8;
+ block[8] = (unsigned short)x;
+ block[9] = (unsigned short)y >> 8;
+ block[10] = (unsigned short)y;
+
+ s.array(block, Controller::SaveSize);
+ if(s.mode() == nall::serializer::Load) {
+ latched = (block[0] != 0);
+ counter = block[1];
+ trigger = (block[2] != 0);
+ cursor = (block[3] != 0);
+ turbo = (block[4] != 0);
+ pause = (block[5] != 0);
+ offscreen = (block[6] != 0);
+ x = (short)(((unsigned short)block[7] << 8) | (unsigned short)block[8]);
+ y = (short)(((unsigned short)block[9] << 8) | (unsigned short)block[10]);
+ }
+}
+
+
SuperScope::SuperScope(bool port) : Controller(port) {
create(Controller::Enter, 21477272);
latched = 0;
diff --git a/snes/controller/superscope/superscope.hpp b/snes/controller/superscope/superscope.hpp
index a7a90b7..93509d7 100755
--- a/snes/controller/superscope/superscope.hpp
+++ b/snes/controller/superscope/superscope.hpp
@@ -2,6 +2,7 @@ struct SuperScope : Controller {
void enter();
uint2 data();
void latch(bool data);
+ void serialize(serializer& s);
SuperScope(bool port);
//private:
diff --git a/snes/input/input.cpp b/snes/input/input.cpp
index 9050310..7030495 100755
--- a/snes/input/input.cpp
+++ b/snes/input/input.cpp
@@ -26,6 +26,21 @@ void Input::connect(bool port, Input::Device id) {
}
}
+void Input::serialize(serializer &s)
+{
+ int p1, p2;
+ p1 = (int)config.controller_port1;
+ p2 = (int)config.controller_port2;
+ s.integer(p1);
+ s.integer(p2);
+ if(s.mode() == nall::serializer::Load) {
+ connect(Controller::Port1, (Device)p1);
+ connect(Controller::Port2, (Device)p2);
+ }
+ port1->serialize(s);
+ port2->serialize(s);
+}
+
Input::Input() : port1(nullptr), port2(nullptr) {
connect(Controller::Port1, Input::Device::Joypad);
connect(Controller::Port2, Input::Device::Joypad);
diff --git a/snes/input/input.hpp b/snes/input/input.hpp
index 13ef46e..6832e82 100755
--- a/snes/input/input.hpp
+++ b/snes/input/input.hpp
@@ -31,6 +31,7 @@ struct Input {
Controller *port1;
Controller *port2;
+ void serialize(serializer &s);
void connect(bool port, Input::Device id);
Input();
~Input();
diff --git a/snes/system/serialization.cpp b/snes/system/serialization.cpp
index f7d6f3b..08e7051 100755
--- a/snes/system/serialization.cpp
+++ b/snes/system/serialization.cpp
@@ -56,6 +56,7 @@ void System::serialize_all(serializer &s) {
smp.serialize(s);
ppu.serialize(s);
dsp.serialize(s);
+ input.serialize(s);
if(cartridge.mode() == Cartridge::Mode::SufamiTurbo) sufamiturbo.serialize(s);
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) icd2.serialize(s);
--
1.8.4.4

View file

@ -1,25 +0,0 @@
From aa1352516e38ff64f304d8831b357841c4795e43 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Fri, 11 Nov 2011 03:05:48 +0200
Subject: [PATCH 05/10] Fix unserialization of 64-bit signed integers
---
nall/serializer.hpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nall/serializer.hpp b/nall/serializer.hpp
index ff2337a..e6bc8fa 100755
--- a/nall/serializer.hpp
+++ b/nall/serializer.hpp
@@ -58,7 +58,7 @@ namespace nall {
for(unsigned n = 0; n < size; n++) idata[isize++] = value >> (n << 3);
} else if(imode == Load) {
value = 0;
- for(unsigned n = 0; n < size; n++) value |= idata[isize++] << (n << 3);
+ for(unsigned n = 0; n < size; n++) value |= (unsigned long long)idata[isize++] << (n << 3);
} else if(imode == Size) {
isize += size;
}
--
1.8.4.4

View file

@ -1,53 +0,0 @@
From 794d83cb28a93d9ae1e613598d3c7cf09090d6a3 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Fri, 11 Nov 2011 19:49:46 +0200
Subject: [PATCH 06/10] Allow frontend to control random number seed
---
snes/interface/interface.cpp | 5 +++++
snes/interface/interface.hpp | 1 +
snes/system/system.cpp | 2 +-
3 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/snes/interface/interface.cpp b/snes/interface/interface.cpp
index b3017c9..0a21a13 100755
--- a/snes/interface/interface.cpp
+++ b/snes/interface/interface.cpp
@@ -23,4 +23,9 @@ time_t Interface::currentTime()
return time(0);
}
+time_t Interface::randomSeed()
+{
+ return time(0);
+}
+
}
diff --git a/snes/interface/interface.hpp b/snes/interface/interface.hpp
index df975e8..30ee7fd 100755
--- a/snes/interface/interface.hpp
+++ b/snes/interface/interface.hpp
@@ -6,6 +6,7 @@ struct Interface {
virtual string path(Cartridge::Slot slot, const string &hint) = 0;
virtual void message(const string &text);
virtual time_t currentTime();
+ virtual time_t randomSeed();
};
extern Interface *interface;
diff --git a/snes/system/system.cpp b/snes/system/system.cpp
index 6881810..8583595 100755
--- a/snes/system/system.cpp
+++ b/snes/system/system.cpp
@@ -147,7 +147,7 @@ void System::unload() {
}
void System::power() {
- random.seed((unsigned)time(0));
+ random.seed((unsigned)interface->randomSeed());
region = config.region;
expansion = config.expansion_port;
--
1.8.4.4

View file

@ -1,63 +0,0 @@
From 4f6981592e29038ad9f818399c0d5a48750cf28a Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Wed, 7 Mar 2012 16:57:18 +0200
Subject: [PATCH 07/10] Fix mouse polling
Don't poll for mouse motion excessive number of times (no need to poll it for
each bit!)
---
snes/controller/mouse/mouse.cpp | 14 ++++++++++++--
snes/controller/mouse/mouse.hpp | 2 ++
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/snes/controller/mouse/mouse.cpp b/snes/controller/mouse/mouse.cpp
index 6b26fae..1a066b9 100755
--- a/snes/controller/mouse/mouse.cpp
+++ b/snes/controller/mouse/mouse.cpp
@@ -3,9 +3,13 @@
uint2 Mouse::data() {
if(counter >= 32) return 1;
- int position_x = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::X); //-n = left, 0 = center, +n = right
- int position_y = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Y); //-n = up, 0 = center, +n = down
+ if(counter == 0) {
+ _position_x = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::X); //-n = left, 0 = center, +n = right
+ _position_y = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Y); //-n = up, 0 = center, +n = down
+ }
+ int position_x = _position_x;
+ int position_y = _position_y;
bool direction_x = position_x < 0; //0 = right, 1 = left
bool direction_y = position_y < 0; //0 = down, 1 = up
@@ -67,10 +71,16 @@ void Mouse::serialize(serializer& s) {
unsigned char block[Controller::SaveSize] = {0};
block[0] = latched ? 1 : 0;
block[1] = counter;
+ block[2] = (unsigned short)_position_x >> 8;
+ block[3] = (unsigned short)_position_x;
+ block[4] = (unsigned short)_position_y >> 8;
+ block[5] = (unsigned short)_position_y;
s.array(block, Controller::SaveSize);
if(s.mode() == nall::serializer::Load) {
latched = (block[0] != 0);
counter = block[1];
+ _position_x = (short)(((unsigned short)block[2] << 8) | (unsigned short)block[3]);
+ _position_y = (short)(((unsigned short)block[4] << 8) | (unsigned short)block[5]);
}
}
diff --git a/snes/controller/mouse/mouse.hpp b/snes/controller/mouse/mouse.hpp
index b66ea51..b07c8ab 100755
--- a/snes/controller/mouse/mouse.hpp
+++ b/snes/controller/mouse/mouse.hpp
@@ -6,4 +6,6 @@ struct Mouse : Controller {
private:
bool latched;
unsigned counter;
+ int _position_x;
+ int _position_y;
};
--
1.8.4.4

View file

@ -1,69 +0,0 @@
From 7b09063fbcaf50c56b476a744f9f3d9634777740 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Mon, 24 Sep 2012 21:46:09 +0300
Subject: [PATCH 08/10] Add needed support for detecting true polls as opposed
to just autopolling
---
snes/cpu/cpu.hpp | 1 +
snes/cpu/mmio/mmio.cpp | 18 ++++++++++--------
2 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/snes/cpu/cpu.hpp b/snes/cpu/cpu.hpp
index 67a56d1..976a4a6 100755
--- a/snes/cpu/cpu.hpp
+++ b/snes/cpu/cpu.hpp
@@ -25,6 +25,7 @@ struct CPU : public Processor, public CPUcore, public PPUcounter {
CPU();
~CPU();
+ bool controller_flag;
private:
#include "dma/dma.hpp"
#include "memory/memory.hpp"
diff --git a/snes/cpu/mmio/mmio.cpp b/snes/cpu/mmio/mmio.cpp
index 8b6aaa6..c5ee930 100755
--- a/snes/cpu/mmio/mmio.cpp
+++ b/snes/cpu/mmio/mmio.cpp
@@ -42,6 +42,7 @@ void CPU::mmio_w4016(uint8 data) {
//1-0 = Joypad serial data
uint8 CPU::mmio_r4016() {
uint8 r = regs.mdr & 0xfc;
+ controller_flag = true;
r |= input.port1->data();
return r;
}
@@ -52,6 +53,7 @@ uint8 CPU::mmio_r4016() {
//1-0 = Joypad serial data
uint8 CPU::mmio_r4017() {
uint8 r = (regs.mdr & 0xe0) | 0x1c;
+ controller_flag = true;
r |= input.port2->data();
return r;
}
@@ -204,14 +206,14 @@ uint8 CPU::mmio_r4217() {
return status.rdmpy >> 8;
}
-uint8 CPU::mmio_r4218() { return status.joy1 >> 0; } //JOY1L
-uint8 CPU::mmio_r4219() { return status.joy1 >> 8; } //JOY1H
-uint8 CPU::mmio_r421a() { return status.joy2 >> 0; } //JOY2L
-uint8 CPU::mmio_r421b() { return status.joy2 >> 8; } //JOY2H
-uint8 CPU::mmio_r421c() { return status.joy3 >> 0; } //JOY3L
-uint8 CPU::mmio_r421d() { return status.joy3 >> 8; } //JOY3H
-uint8 CPU::mmio_r421e() { return status.joy4 >> 0; } //JOY4L
-uint8 CPU::mmio_r421f() { return status.joy4 >> 8; } //JOY4H
+uint8 CPU::mmio_r4218() { controller_flag = true; return status.joy1 >> 0; } //JOY1L
+uint8 CPU::mmio_r4219() { controller_flag = true; return status.joy1 >> 8; } //JOY1H
+uint8 CPU::mmio_r421a() { controller_flag = true; return status.joy2 >> 0; } //JOY2L
+uint8 CPU::mmio_r421b() { controller_flag = true; return status.joy2 >> 8; } //JOY2H
+uint8 CPU::mmio_r421c() { controller_flag = true; return status.joy3 >> 0; } //JOY3L
+uint8 CPU::mmio_r421d() { controller_flag = true; return status.joy3 >> 8; } //JOY3H
+uint8 CPU::mmio_r421e() { controller_flag = true; return status.joy4 >> 0; } //JOY4L
+uint8 CPU::mmio_r421f() { controller_flag = true; return status.joy4 >> 8; } //JOY4H
//DMAPx
uint8 CPU::mmio_r43x0(uint8 i) {
--
1.8.4.4

View file

@ -1,65 +0,0 @@
From 62f8a07104b57b75071318098145d99012dbc908 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Sun, 27 Oct 2013 10:52:45 +0200
Subject: [PATCH 09/10] Support notifying latches
---
snes/cpu/mmio/mmio.cpp | 1 +
snes/cpu/timing/joypad.cpp | 1 +
snes/interface/interface.cpp | 5 +++++
snes/interface/interface.hpp | 1 +
4 files changed, 8 insertions(+)
diff --git a/snes/cpu/mmio/mmio.cpp b/snes/cpu/mmio/mmio.cpp
index c5ee930..b7afff0 100755
--- a/snes/cpu/mmio/mmio.cpp
+++ b/snes/cpu/mmio/mmio.cpp
@@ -33,6 +33,7 @@ void CPU::mmio_w2183(uint8 data) {
//strobing $4016.d0 affects both controller port latches.
//$4017 bit 0 writes are ignored.
void CPU::mmio_w4016(uint8 data) {
+ if(data&1) interface->notifyLatched();
input.port1->latch(data & 1);
input.port2->latch(data & 1);
}
diff --git a/snes/cpu/timing/joypad.cpp b/snes/cpu/timing/joypad.cpp
index 6e15346..c69b708 100755
--- a/snes/cpu/timing/joypad.cpp
+++ b/snes/cpu/timing/joypad.cpp
@@ -7,6 +7,7 @@ void CPU::step_auto_joypad_poll() {
if(status.auto_joypad_active && status.auto_joypad_poll) {
if(status.auto_joypad_counter == 0) {
+ interface->notifyLatched();
input.port1->latch(1);
input.port2->latch(1);
input.port1->latch(0);
diff --git a/snes/interface/interface.cpp b/snes/interface/interface.cpp
index 0a21a13..6685556 100755
--- a/snes/interface/interface.cpp
+++ b/snes/interface/interface.cpp
@@ -28,4 +28,9 @@ time_t Interface::randomSeed()
return time(0);
}
+void Interface::notifyLatched()
+{
+ //Nothing.
+}
+
}
diff --git a/snes/interface/interface.hpp b/snes/interface/interface.hpp
index 30ee7fd..203f7b0 100755
--- a/snes/interface/interface.hpp
+++ b/snes/interface/interface.hpp
@@ -7,6 +7,7 @@ struct Interface {
virtual void message(const string &text);
virtual time_t currentTime();
virtual time_t randomSeed();
+ virtual void notifyLatched();
};
extern Interface *interface;
--
1.8.4.4

View file

@ -1,22 +0,0 @@
From 242efcc9cf10fa58c8e06f154c41db21e6aa2688 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Sat, 30 Nov 2013 10:26:59 +0200
Subject: [PATCH 10/10] Add support for auto-detecting bsnes version
---
bsnes.mk | 3 +++
1 file changed, 3 insertions(+)
create mode 100644 bsnes.mk
diff --git a/bsnes.mk b/bsnes.mk
new file mode 100644
index 0000000..11300e2
--- /dev/null
+++ b/bsnes.mk
@@ -0,0 +1,3 @@
+BSNES_SUPPORTS_DEBUGGER=yes
+LIBSNES_DIR=ui-libsnes
+BSNES_VERSION=084
--
1.8.4.4

View file

@ -1,82 +0,0 @@
From b19b3b2d1d7a522af695f4482abb28e52804326b Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Wed, 9 Nov 2011 00:30:36 +0200
Subject: [PATCH 01/27] Make libsnes compile
Changes between v083 and v084 had broken libsnes. Fix it so it at least
compiles.
---
ui-libsnes/libsnes.cpp | 37 +++++++++++++++++++++++++++++++++++--
1 file changed, 35 insertions(+), 2 deletions(-)
diff --git a/ui-libsnes/libsnes.cpp b/ui-libsnes/libsnes.cpp
index fbb4482c..5f5ded69 100755
--- a/ui-libsnes/libsnes.cpp
+++ b/ui-libsnes/libsnes.cpp
@@ -1,5 +1,6 @@
#include "libsnes.hpp"
#include <snes/snes.hpp>
+#include <gameboy/gameboy.hpp>
#include <nall/snes/cartridge.hpp>
#include <nall/gameboy/cartridge.hpp>
@@ -46,6 +47,38 @@ struct Interface : public SNES::Interface {
print(text, "\n");
}
+ void setCheats(const lstring &list = lstring{}) {
+ if(SNES::cartridge.mode() == SNES::Cartridge::Mode::SuperGameBoy) {
+ GameBoy::cheat.reset();
+ for(auto &code : list) {
+ lstring codelist;
+ codelist.split("+", code);
+ for(auto &part : codelist) {
+ unsigned addr, data, comp;
+ if(GameBoy::Cheat::decode(part, addr, data, comp)) {
+ GameBoy::cheat.append({ addr, data, comp });
+ }
+ }
+ }
+ GameBoy::cheat.synchronize();
+ return;
+ }
+
+ SNES::cheat.reset();
+ for(auto &code : list) {
+ lstring codelist;
+ codelist.split("+", code);
+ for(auto &part : codelist) {
+ unsigned addr, data;
+ if(SNES::Cheat::decode(part, addr, data)) {
+ SNES::cheat.append({ addr, data });
+ }
+ }
+ }
+ SNES::cheat.synchronize();
+ }
+
+
string path(SNES::Cartridge::Slot slot, const string &hint) {
return { basename, hint };
}
@@ -115,7 +148,7 @@ void snes_set_cartridge_basename(const char *basename) {
}
void snes_init(void) {
- interface.initialize(&interface);
+ SNES::system.init();
SNES::input.connect(SNES::Controller::Port1, SNES::Input::Device::Joypad);
SNES::input.connect(SNES::Controller::Port2, SNES::Input::Device::Joypad);
}
@@ -244,7 +277,7 @@ bool snes_load_cartridge_super_game_boy(
uint8_t *data = new uint8_t[dmg_size];
memcpy(data, dmg_data, dmg_size);
string xmldmg = (dmg_xml && *dmg_xml) ? string(dmg_xml) : GameBoyCartridge(data, dmg_size).markup;
- GameBoy::cartridge.load(xmldmg, data, dmg_size);
+ GameBoy::cartridge.load(GameBoy::System::Revision::SuperGameBoy, xmldmg, data, dmg_size);
delete[] data;
}
SNES::cartridge.load(SNES::Cartridge::Mode::SuperGameBoy, xmlrom);
--
2.15.0.rc1

View file

@ -1,26 +0,0 @@
From bb2fed04fbfe62a89e4bcfe90f44b4738f7c7c1a Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Wed, 9 Nov 2011 00:31:59 +0200
Subject: [PATCH 02/27] Fix bsnes version number in libsnes to be v085, not
v083
---
ui-libsnes/libsnes.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ui-libsnes/libsnes.cpp b/ui-libsnes/libsnes.cpp
index 5f5ded69..0e63075e 100755
--- a/ui-libsnes/libsnes.cpp
+++ b/ui-libsnes/libsnes.cpp
@@ -112,7 +112,7 @@ struct Interface : public SNES::Interface {
static Interface interface;
const char* snes_library_id(void) {
- return "bsnes v083";
+ return "bsnes v085";
}
unsigned snes_library_revision_major(void) {
--
2.15.0.rc1

View file

@ -1,84 +0,0 @@
From 7379b4570e5755a5a1da25181ba4f5d1ca461a98 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Wed, 9 Nov 2011 00:37:44 +0200
Subject: [PATCH 03/27] Don't use time() in emulating chips
Instead of using time() in chip emulation, create new interface method
currentTime(), defaulting to time(0). This way frontend can cleanly
override the current time bsnes is using.
---
snes/chip/bsx/satellaview/satellaview.cpp | 2 +-
snes/chip/spc7110/spc7110.cpp | 2 +-
snes/chip/srtc/srtc.cpp | 2 +-
snes/interface/interface.cpp | 5 +++++
snes/interface/interface.hpp | 1 +
5 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/snes/chip/bsx/satellaview/satellaview.cpp b/snes/chip/bsx/satellaview/satellaview.cpp
index 386fb628..3c980195 100755
--- a/snes/chip/bsx/satellaview/satellaview.cpp
+++ b/snes/chip/bsx/satellaview/satellaview.cpp
@@ -38,7 +38,7 @@ uint8 BSXSatellaview::mmio_read(unsigned addr) {
if(counter == 0) {
time_t rawtime;
- time(&rawtime);
+ rawtime = SNES::interface->currentTime();
tm *t = localtime(&rawtime);
regs.r2192_hour = t->tm_hour;
diff --git a/snes/chip/spc7110/spc7110.cpp b/snes/chip/spc7110/spc7110.cpp
index d2dc640b..74a817a6 100755
--- a/snes/chip/spc7110/spc7110.cpp
+++ b/snes/chip/spc7110/spc7110.cpp
@@ -101,7 +101,7 @@ void SPC7110::set_data_adjust(unsigned addr) { r4814 = addr; r4815 = addr >> 8;
void SPC7110::update_time(int offset) {
time_t rtc_time = (rtc[16] << 0) | (rtc[17] << 8) | (rtc[18] << 16) | (rtc[19] << 24);
- time_t current_time = time(0) - offset;
+ time_t current_time = SNES::interface->currentTime() - offset;
//sizeof(time_t) is platform-dependent; though rtc[] needs to be platform-agnostic.
//yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by
diff --git a/snes/chip/srtc/srtc.cpp b/snes/chip/srtc/srtc.cpp
index 1b2fd2aa..78fc4c1f 100755
--- a/snes/chip/srtc/srtc.cpp
+++ b/snes/chip/srtc/srtc.cpp
@@ -31,7 +31,7 @@ void SRTC::reset() {
void SRTC::update_time() {
time_t rtc_time = (rtc[16] << 0) | (rtc[17] << 8) | (rtc[18] << 16) | (rtc[19] << 24);
- time_t current_time = time(0);
+ time_t current_time = SNES::interface->currentTime();
//sizeof(time_t) is platform-dependent; though rtc[] needs to be platform-agnostic.
//yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by
diff --git a/snes/interface/interface.cpp b/snes/interface/interface.cpp
index a0e3a81b..b3017c90 100755
--- a/snes/interface/interface.cpp
+++ b/snes/interface/interface.cpp
@@ -18,4 +18,9 @@ void Interface::message(const string &text) {
print(text, "\n");
}
+time_t Interface::currentTime()
+{
+ return time(0);
+}
+
}
diff --git a/snes/interface/interface.hpp b/snes/interface/interface.hpp
index f1a48c0f..df975e83 100755
--- a/snes/interface/interface.hpp
+++ b/snes/interface/interface.hpp
@@ -5,6 +5,7 @@ struct Interface {
virtual string path(Cartridge::Slot slot, const string &hint) = 0;
virtual void message(const string &text);
+ virtual time_t currentTime();
};
extern Interface *interface;
--
2.15.0.rc1

View file

@ -1,346 +0,0 @@
From efe1b5884c316ce070953edd87c6c9aeffffaa94 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Wed, 9 Nov 2011 01:52:08 +0200
Subject: [PATCH 04/27] Save controller state when savestating
When savestating, save the controller state and restore it upon loadstate.
Prevents libsnes from mixing up buttons.
---
snes/controller/controller.cpp | 8 +++++++
snes/controller/controller.hpp | 2 ++
snes/controller/gamepad/gamepad.cpp | 13 +++++++++++
snes/controller/gamepad/gamepad.hpp | 2 +-
snes/controller/justifier/justifier.cpp | 36 +++++++++++++++++++++++++++++++
snes/controller/justifier/justifier.hpp | 1 +
snes/controller/mouse/mouse.cpp | 13 +++++++++++
snes/controller/mouse/mouse.hpp | 2 +-
snes/controller/multitap/multitap.cpp | 16 ++++++++++++++
snes/controller/multitap/multitap.hpp | 2 +-
snes/controller/superscope/superscope.cpp | 31 ++++++++++++++++++++++++++
snes/controller/superscope/superscope.hpp | 1 +
snes/system/input.cpp | 16 ++++++++++++++
snes/system/input.hpp | 1 +
snes/system/serialization.cpp | 1 +
15 files changed, 142 insertions(+), 3 deletions(-)
diff --git a/snes/controller/controller.cpp b/snes/controller/controller.cpp
index 9091b21b..f254bedb 100755
--- a/snes/controller/controller.cpp
+++ b/snes/controller/controller.cpp
@@ -46,8 +46,16 @@ void Controller::iobit(bool data) {
}
}
+void Controller::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save a zero block.
+ unsigned char blockzeroes[SaveSize] = {0};
+ s.array(blockzeroes, SaveSize);
+}
+
Controller::Controller(bool port) : port(port) {
if(!thread) create(Controller::Enter, 1);
}
+
}
diff --git a/snes/controller/controller.hpp b/snes/controller/controller.hpp
index 73327129..827b2eb4 100755
--- a/snes/controller/controller.hpp
+++ b/snes/controller/controller.hpp
@@ -13,12 +13,14 @@
struct Controller : Processor {
enum : bool { Port1 = 0, Port2 = 1 };
+ enum { SaveSize = 16 };
const bool port;
static void Enter();
virtual void enter();
void step(unsigned clocks);
void synchronize_cpu();
+ virtual void serialize(serializer& s);
bool iobit();
void iobit(bool data);
diff --git a/snes/controller/gamepad/gamepad.cpp b/snes/controller/gamepad/gamepad.cpp
index 594020d2..4fa1c99e 100755
--- a/snes/controller/gamepad/gamepad.cpp
+++ b/snes/controller/gamepad/gamepad.cpp
@@ -13,6 +13,19 @@ void Gamepad::latch(bool data) {
counter = 0;
}
+void Gamepad::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save block.
+ unsigned char block[Controller::SaveSize] = {0};
+ block[0] = latched ? 1 : 0;
+ block[1] = counter;
+ s.array(block, Controller::SaveSize);
+ if(s.mode() == nall::serializer::Load) {
+ latched = (block[0] != 0);
+ counter = block[1];
+ }
+}
+
Gamepad::Gamepad(bool port) : Controller(port) {
latched = 0;
counter = 0;
diff --git a/snes/controller/gamepad/gamepad.hpp b/snes/controller/gamepad/gamepad.hpp
index c5ca69ca..a2392d1e 100755
--- a/snes/controller/gamepad/gamepad.hpp
+++ b/snes/controller/gamepad/gamepad.hpp
@@ -2,7 +2,7 @@ struct Gamepad : Controller {
uint2 data();
void latch(bool data);
Gamepad(bool port);
-
+ void serialize(serializer& s);
private:
bool latched;
unsigned counter;
diff --git a/snes/controller/justifier/justifier.cpp b/snes/controller/justifier/justifier.cpp
index 62079166..ad13a9bd 100755
--- a/snes/controller/justifier/justifier.cpp
+++ b/snes/controller/justifier/justifier.cpp
@@ -100,6 +100,42 @@ void Justifier::latch(bool data) {
if(latched == 0) active = !active; //toggle between both controllers, even when unchained
}
+void Justifier::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save block.
+ unsigned char block[Controller::SaveSize] = {0};
+ block[0] = latched ? 1 : 0;
+ block[1] = counter;
+ block[2] = active ? 1 : 0;
+ block[3] = player1.trigger ? 1 : 0;
+ block[4] = player2.trigger ? 1 : 0;
+ block[5] = player1.start ? 1 : 0;
+ block[6] = player2.start ? 1 : 0;
+ block[7] = (unsigned short)player1.x >> 8;
+ block[8] = (unsigned short)player1.x;
+ block[9] = (unsigned short)player2.x >> 8;
+ block[10] = (unsigned short)player2.x;
+ block[11] = (unsigned short)player1.y >> 8;
+ block[12] = (unsigned short)player1.y;
+ block[13] = (unsigned short)player2.y >> 8;
+ block[14] = (unsigned short)player2.y;
+ s.array(block, Controller::SaveSize);
+ if(s.mode() == nall::serializer::Load) {
+ latched = (block[0] != 0);
+ counter = block[1];
+ active = (block[2] != 0);
+ player1.trigger = (block[3] != 0);
+ player2.trigger = (block[4] != 0);
+ player1.start = (block[5] != 0);
+ player2.start = (block[6] != 0);
+ player1.x = (short)(((unsigned short)block[7] << 8) | (unsigned short)block[8]);
+ player2.x = (short)(((unsigned short)block[9] << 8) | (unsigned short)block[10]);
+ player1.y = (short)(((unsigned short)block[11] << 8) | (unsigned short)block[12]);
+ player2.y = (short)(((unsigned short)block[13] << 8) | (unsigned short)block[14]);
+ }
+}
+
+
Justifier::Justifier(bool port, bool chained) : Controller(port), chained(chained) {
create(Controller::Enter, 21477272);
latched = 0;
diff --git a/snes/controller/justifier/justifier.hpp b/snes/controller/justifier/justifier.hpp
index f927acf6..6b7bba07 100755
--- a/snes/controller/justifier/justifier.hpp
+++ b/snes/controller/justifier/justifier.hpp
@@ -2,6 +2,7 @@ struct Justifier : Controller {
void enter();
uint2 data();
void latch(bool data);
+ void serialize(serializer& s);
Justifier(bool port, bool chained);
//private:
diff --git a/snes/controller/mouse/mouse.cpp b/snes/controller/mouse/mouse.cpp
index c9f5d16b..6b26fae5 100755
--- a/snes/controller/mouse/mouse.cpp
+++ b/snes/controller/mouse/mouse.cpp
@@ -61,6 +61,19 @@ void Mouse::latch(bool data) {
counter = 0;
}
+void Mouse::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save block.
+ unsigned char block[Controller::SaveSize] = {0};
+ block[0] = latched ? 1 : 0;
+ block[1] = counter;
+ s.array(block, Controller::SaveSize);
+ if(s.mode() == nall::serializer::Load) {
+ latched = (block[0] != 0);
+ counter = block[1];
+ }
+}
+
Mouse::Mouse(bool port) : Controller(port) {
latched = 0;
counter = 0;
diff --git a/snes/controller/mouse/mouse.hpp b/snes/controller/mouse/mouse.hpp
index 95e24b65..b66ea513 100755
--- a/snes/controller/mouse/mouse.hpp
+++ b/snes/controller/mouse/mouse.hpp
@@ -2,7 +2,7 @@ struct Mouse : Controller {
uint2 data();
void latch(bool data);
Mouse(bool port);
-
+ void serialize(serializer& s);
private:
bool latched;
unsigned counter;
diff --git a/snes/controller/multitap/multitap.cpp b/snes/controller/multitap/multitap.cpp
index 3a6eb720..146c41d4 100755
--- a/snes/controller/multitap/multitap.cpp
+++ b/snes/controller/multitap/multitap.cpp
@@ -30,6 +30,22 @@ void Multitap::latch(bool data) {
counter2 = 0;
}
+void Multitap::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save block.
+ unsigned char block[Controller::SaveSize] = {0};
+ block[0] = latched ? 1 : 0;
+ block[1] = counter1;
+ block[2] = counter2;
+ s.array(block, Controller::SaveSize);
+ if(s.mode() == nall::serializer::Load) {
+ latched = (block[0] != 0);
+ counter1 = block[1];
+ counter2 = block[2];
+ }
+}
+
+
Multitap::Multitap(bool port) : Controller(port) {
latched = 0;
counter1 = 0;
diff --git a/snes/controller/multitap/multitap.hpp b/snes/controller/multitap/multitap.hpp
index 0540af71..e6324ac5 100755
--- a/snes/controller/multitap/multitap.hpp
+++ b/snes/controller/multitap/multitap.hpp
@@ -2,7 +2,7 @@ struct Multitap : Controller {
uint2 data();
void latch(bool data);
Multitap(bool port);
-
+ void serialize(serializer& s);
private:
bool latched;
unsigned counter1;
diff --git a/snes/controller/superscope/superscope.cpp b/snes/controller/superscope/superscope.cpp
index 12068f05..1a1dfbff 100755
--- a/snes/controller/superscope/superscope.cpp
+++ b/snes/controller/superscope/superscope.cpp
@@ -100,6 +100,37 @@ void SuperScope::latch(bool data) {
counter = 0;
}
+void SuperScope::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save block.
+ unsigned char block[Controller::SaveSize] = {0};
+ block[0] = latched ? 1 : 0;
+ block[1] = counter;
+ block[2] = trigger ? 1 : 0;
+ block[3] = cursor ? 1 : 0;
+ block[4] = turbo ? 1 : 0;
+ block[5] = pause ? 1 : 0;
+ block[6] = offscreen ? 1 : 0;
+ block[7] = (unsigned short)x >> 8;
+ block[8] = (unsigned short)x;
+ block[9] = (unsigned short)y >> 8;
+ block[10] = (unsigned short)y;
+
+ s.array(block, Controller::SaveSize);
+ if(s.mode() == nall::serializer::Load) {
+ latched = (block[0] != 0);
+ counter = block[1];
+ trigger = (block[2] != 0);
+ cursor = (block[3] != 0);
+ turbo = (block[4] != 0);
+ pause = (block[5] != 0);
+ offscreen = (block[6] != 0);
+ x = (short)(((unsigned short)block[7] << 8) | (unsigned short)block[8]);
+ y = (short)(((unsigned short)block[9] << 8) | (unsigned short)block[10]);
+ }
+}
+
+
SuperScope::SuperScope(bool port) : Controller(port) {
create(Controller::Enter, 21477272);
latched = 0;
diff --git a/snes/controller/superscope/superscope.hpp b/snes/controller/superscope/superscope.hpp
index a7a90b71..93509d79 100755
--- a/snes/controller/superscope/superscope.hpp
+++ b/snes/controller/superscope/superscope.hpp
@@ -2,6 +2,7 @@ struct SuperScope : Controller {
void enter();
uint2 data();
void latch(bool data);
+ void serialize(serializer& s);
SuperScope(bool port);
//private:
diff --git a/snes/system/input.cpp b/snes/system/input.cpp
index 90503106..ec5559dc 100755
--- a/snes/system/input.cpp
+++ b/snes/system/input.cpp
@@ -26,6 +26,22 @@ void Input::connect(bool port, Input::Device id) {
}
}
+void Input::serialize(serializer &s)
+{
+ int p1, p2;
+ p1 = (int)config.controller_port1;
+ p2 = (int)config.controller_port2;
+ s.integer(p1);
+ s.integer(p2);
+ if(s.mode() == nall::serializer::Load) {
+ connect(Controller::Port1, (Device)p1);
+ connect(Controller::Port2, (Device)p2);
+ }
+ port1->serialize(s);
+ port2->serialize(s);
+}
+
+
Input::Input() : port1(nullptr), port2(nullptr) {
connect(Controller::Port1, Input::Device::Joypad);
connect(Controller::Port2, Input::Device::Joypad);
diff --git a/snes/system/input.hpp b/snes/system/input.hpp
index 13ef46e1..6832e823 100755
--- a/snes/system/input.hpp
+++ b/snes/system/input.hpp
@@ -31,6 +31,7 @@ struct Input {
Controller *port1;
Controller *port2;
+ void serialize(serializer &s);
void connect(bool port, Input::Device id);
Input();
~Input();
diff --git a/snes/system/serialization.cpp b/snes/system/serialization.cpp
index f7d6f3b1..08e70510 100755
--- a/snes/system/serialization.cpp
+++ b/snes/system/serialization.cpp
@@ -56,6 +56,7 @@ void System::serialize_all(serializer &s) {
smp.serialize(s);
ppu.serialize(s);
dsp.serialize(s);
+ input.serialize(s);
if(cartridge.mode() == Cartridge::Mode::SufamiTurbo) sufamiturbo.serialize(s);
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) icd2.serialize(s);
--
2.15.0.rc1

View file

@ -1,25 +0,0 @@
From cdf2f46490f128308eb7f399d03530936ebeda0a Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Fri, 11 Nov 2011 03:05:48 +0200
Subject: [PATCH 05/27] Fix unserialization of 64-bit signed integers
---
nall/serializer.hpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nall/serializer.hpp b/nall/serializer.hpp
index ff2337ab..e6bc8fad 100755
--- a/nall/serializer.hpp
+++ b/nall/serializer.hpp
@@ -58,7 +58,7 @@ namespace nall {
for(unsigned n = 0; n < size; n++) idata[isize++] = value >> (n << 3);
} else if(imode == Load) {
value = 0;
- for(unsigned n = 0; n < size; n++) value |= idata[isize++] << (n << 3);
+ for(unsigned n = 0; n < size; n++) value |= (unsigned long long)idata[isize++] << (n << 3);
} else if(imode == Size) {
isize += size;
}
--
2.15.0.rc1

View file

@ -1,53 +0,0 @@
From 4dc46334ec175e26277632fee4aea80768749af9 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Fri, 11 Nov 2011 19:49:46 +0200
Subject: [PATCH 06/27] Allow frontend to control random number seed
---
snes/interface/interface.cpp | 5 +++++
snes/interface/interface.hpp | 1 +
snes/system/system.cpp | 2 +-
3 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/snes/interface/interface.cpp b/snes/interface/interface.cpp
index b3017c90..0a21a132 100755
--- a/snes/interface/interface.cpp
+++ b/snes/interface/interface.cpp
@@ -23,4 +23,9 @@ time_t Interface::currentTime()
return time(0);
}
+time_t Interface::randomSeed()
+{
+ return time(0);
+}
+
}
diff --git a/snes/interface/interface.hpp b/snes/interface/interface.hpp
index df975e83..30ee7fde 100755
--- a/snes/interface/interface.hpp
+++ b/snes/interface/interface.hpp
@@ -6,6 +6,7 @@ struct Interface {
virtual string path(Cartridge::Slot slot, const string &hint) = 0;
virtual void message(const string &text);
virtual time_t currentTime();
+ virtual time_t randomSeed();
};
extern Interface *interface;
diff --git a/snes/system/system.cpp b/snes/system/system.cpp
index c19a7c51..dbd912d8 100755
--- a/snes/system/system.cpp
+++ b/snes/system/system.cpp
@@ -146,7 +146,7 @@ void System::unload() {
}
void System::power() {
- random.seed((unsigned)time(0));
+ random.seed((unsigned)interface->randomSeed());
region = config.region;
expansion = config.expansion_port;
--
2.15.0.rc1

View file

@ -1,63 +0,0 @@
From eeaf6dc52d39ca9c150ff61864c11297d200d968 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Wed, 7 Mar 2012 16:57:18 +0200
Subject: [PATCH 07/27] Fix mouse polling
Don't poll for mouse motion excessive number of times (no need to poll it for
each bit!)
---
snes/controller/mouse/mouse.cpp | 14 ++++++++++++--
snes/controller/mouse/mouse.hpp | 2 ++
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/snes/controller/mouse/mouse.cpp b/snes/controller/mouse/mouse.cpp
index 6b26fae5..1a066b98 100755
--- a/snes/controller/mouse/mouse.cpp
+++ b/snes/controller/mouse/mouse.cpp
@@ -3,9 +3,13 @@
uint2 Mouse::data() {
if(counter >= 32) return 1;
- int position_x = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::X); //-n = left, 0 = center, +n = right
- int position_y = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Y); //-n = up, 0 = center, +n = down
+ if(counter == 0) {
+ _position_x = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::X); //-n = left, 0 = center, +n = right
+ _position_y = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Y); //-n = up, 0 = center, +n = down
+ }
+ int position_x = _position_x;
+ int position_y = _position_y;
bool direction_x = position_x < 0; //0 = right, 1 = left
bool direction_y = position_y < 0; //0 = down, 1 = up
@@ -67,10 +71,16 @@ void Mouse::serialize(serializer& s) {
unsigned char block[Controller::SaveSize] = {0};
block[0] = latched ? 1 : 0;
block[1] = counter;
+ block[2] = (unsigned short)_position_x >> 8;
+ block[3] = (unsigned short)_position_x;
+ block[4] = (unsigned short)_position_y >> 8;
+ block[5] = (unsigned short)_position_y;
s.array(block, Controller::SaveSize);
if(s.mode() == nall::serializer::Load) {
latched = (block[0] != 0);
counter = block[1];
+ _position_x = (short)(((unsigned short)block[2] << 8) | (unsigned short)block[3]);
+ _position_y = (short)(((unsigned short)block[4] << 8) | (unsigned short)block[5]);
}
}
diff --git a/snes/controller/mouse/mouse.hpp b/snes/controller/mouse/mouse.hpp
index b66ea513..b07c8ab7 100755
--- a/snes/controller/mouse/mouse.hpp
+++ b/snes/controller/mouse/mouse.hpp
@@ -6,4 +6,6 @@ struct Mouse : Controller {
private:
bool latched;
unsigned counter;
+ int _position_x;
+ int _position_y;
};
--
2.15.0.rc1

View file

@ -1,142 +0,0 @@
From 7018377c93553071fc404db872b2746d40ac3bce Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Sat, 1 Sep 2012 11:23:34 +0300
Subject: [PATCH 08/27] Fix uninitialized variables
These uninitialized variables cause a lot of desyncs in Shadowrun.
---
snes/alt/dsp/dsp.cpp | 2 ++
snes/alt/ppu-compatibility/ppu.cpp | 11 +++++++++++
snes/cpu/core/core.cpp | 8 ++++++++
snes/cpu/core/core.hpp | 2 ++
snes/cpu/cpu.cpp | 1 +
snes/smp/core/core.cpp | 11 +++++++++++
snes/smp/core/core.hpp | 2 ++
snes/smp/smp.cpp | 1 +
8 files changed, 38 insertions(+)
diff --git a/snes/alt/dsp/dsp.cpp b/snes/alt/dsp/dsp.cpp
index d0c9e077..c6809f73 100755
--- a/snes/alt/dsp/dsp.cpp
+++ b/snes/alt/dsp/dsp.cpp
@@ -40,6 +40,8 @@ void DSP::write(uint8 addr, uint8 data) {
}
void DSP::power() {
+ clock = 0;
+ memset(samplebuffer, 0, sizeof(samplebuffer));
spc_dsp.init(smp.apuram);
spc_dsp.reset();
spc_dsp.set_output(samplebuffer, 8192);
diff --git a/snes/alt/ppu-compatibility/ppu.cpp b/snes/alt/ppu-compatibility/ppu.cpp
index 1a3835b3..a21e5e31 100755
--- a/snes/alt/ppu-compatibility/ppu.cpp
+++ b/snes/alt/ppu-compatibility/ppu.cpp
@@ -345,6 +345,17 @@ void PPU::power() {
regs.time_over = false;
regs.range_over = false;
+ //All kinds of shit...
+ line = 0;
+ memset(pixel_cache, 0, sizeof(pixel_cache));
+ memset(window, 0, sizeof(window));
+ memset(bg_info, 0, sizeof(bg_info));
+ active_sprite = 0;
+ memset(oam_itemlist, 0, sizeof(oam_itemlist));
+ memset(oam_tilelist, 0, sizeof(oam_tilelist));
+ memset(oam_line_pal, 0, sizeof(oam_line_pal));
+ memset(oam_line_pri, 0, sizeof(oam_line_pri));
+
reset();
}
diff --git a/snes/cpu/core/core.cpp b/snes/cpu/core/core.cpp
index 427176b0..a5b809b9 100755
--- a/snes/cpu/core/core.cpp
+++ b/snes/cpu/core/core.cpp
@@ -86,4 +86,12 @@ CPUcore::CPUcore() {
initialize_opcode_table();
}
+void CPUcore::powercycle()
+{
+ aa.d = 0;
+ rd.d = 0;
+ sp = 0;
+ dp = 0;
+}
+
}
diff --git a/snes/cpu/core/core.hpp b/snes/cpu/core/core.hpp
index 964bd128..7a685a8d 100755
--- a/snes/cpu/core/core.hpp
+++ b/snes/cpu/core/core.hpp
@@ -7,6 +7,8 @@ struct CPUcore {
reg24_t aa, rd;
uint8_t sp, dp;
+ void powercycle();
+
virtual void op_io() = 0;
virtual uint8_t op_read(uint32_t addr) = 0;
virtual void op_write(uint32_t addr, uint8_t data) = 0;
diff --git a/snes/cpu/cpu.cpp b/snes/cpu/cpu.cpp
index f6ae9754..2d7d3432 100755
--- a/snes/cpu/cpu.cpp
+++ b/snes/cpu/cpu.cpp
@@ -125,6 +125,7 @@ void CPU::power() {
mmio_power();
dma_power();
timing_power();
+ CPUcore::powercycle();
}
void CPU::reset() {
diff --git a/snes/smp/core/core.cpp b/snes/smp/core/core.cpp
index 9c94d00a..2fc29be1 100755
--- a/snes/smp/core/core.cpp
+++ b/snes/smp/core/core.cpp
@@ -269,4 +269,15 @@ void SMPcore::op_step() {
}
}
+void SMPcore::powercycle()
+{
+ opcode = 0;
+ dp.w = 0;
+ sp.w = 0;
+ rd.w = 0;
+ wr.w = 0;
+ bit.w = 0;
+ ya.w = 0;
+}
+
}
diff --git a/snes/smp/core/core.hpp b/snes/smp/core/core.hpp
index 6adf6f6b..1489fcef 100755
--- a/snes/smp/core/core.hpp
+++ b/snes/smp/core/core.hpp
@@ -11,6 +11,8 @@ struct SMPcore {
word_t dp, sp, rd, wr, bit, ya;
uint8 opcode;
+ void powercycle();
+
void core_serialize(serializer&);
string disassemble_opcode(uint16 addr);
diff --git a/snes/smp/smp.cpp b/snes/smp/smp.cpp
index 90806245..d4ccf425 100755
--- a/snes/smp/smp.cpp
+++ b/snes/smp/smp.cpp
@@ -53,6 +53,7 @@ void SMP::power() {
timer0.target = 0;
timer1.target = 0;
timer2.target = 0;
+ SMPcore::powercycle();
}
void SMP::reset() {
--
2.15.0.rc1

View file

@ -1,69 +0,0 @@
From 6e0364c9a86caa71623a188a720b2d68b304b89b Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Mon, 24 Sep 2012 21:46:09 +0300
Subject: [PATCH 09/27] Add needed support for detecting true polls as opposed
to just autopolling
---
snes/cpu/cpu.hpp | 1 +
snes/cpu/mmio/mmio.cpp | 18 ++++++++++--------
2 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/snes/cpu/cpu.hpp b/snes/cpu/cpu.hpp
index 3da865e2..49445773 100755
--- a/snes/cpu/cpu.hpp
+++ b/snes/cpu/cpu.hpp
@@ -25,6 +25,7 @@ struct CPU : public Processor, public CPUcore, public PPUcounter {
CPU();
~CPU();
+ bool controller_flag;
private:
#include "dma/dma.hpp"
#include "memory/memory.hpp"
diff --git a/snes/cpu/mmio/mmio.cpp b/snes/cpu/mmio/mmio.cpp
index 8b6aaa6a..c5ee930f 100755
--- a/snes/cpu/mmio/mmio.cpp
+++ b/snes/cpu/mmio/mmio.cpp
@@ -42,6 +42,7 @@ void CPU::mmio_w4016(uint8 data) {
//1-0 = Joypad serial data
uint8 CPU::mmio_r4016() {
uint8 r = regs.mdr & 0xfc;
+ controller_flag = true;
r |= input.port1->data();
return r;
}
@@ -52,6 +53,7 @@ uint8 CPU::mmio_r4016() {
//1-0 = Joypad serial data
uint8 CPU::mmio_r4017() {
uint8 r = (regs.mdr & 0xe0) | 0x1c;
+ controller_flag = true;
r |= input.port2->data();
return r;
}
@@ -204,14 +206,14 @@ uint8 CPU::mmio_r4217() {
return status.rdmpy >> 8;
}
-uint8 CPU::mmio_r4218() { return status.joy1 >> 0; } //JOY1L
-uint8 CPU::mmio_r4219() { return status.joy1 >> 8; } //JOY1H
-uint8 CPU::mmio_r421a() { return status.joy2 >> 0; } //JOY2L
-uint8 CPU::mmio_r421b() { return status.joy2 >> 8; } //JOY2H
-uint8 CPU::mmio_r421c() { return status.joy3 >> 0; } //JOY3L
-uint8 CPU::mmio_r421d() { return status.joy3 >> 8; } //JOY3H
-uint8 CPU::mmio_r421e() { return status.joy4 >> 0; } //JOY4L
-uint8 CPU::mmio_r421f() { return status.joy4 >> 8; } //JOY4H
+uint8 CPU::mmio_r4218() { controller_flag = true; return status.joy1 >> 0; } //JOY1L
+uint8 CPU::mmio_r4219() { controller_flag = true; return status.joy1 >> 8; } //JOY1H
+uint8 CPU::mmio_r421a() { controller_flag = true; return status.joy2 >> 0; } //JOY2L
+uint8 CPU::mmio_r421b() { controller_flag = true; return status.joy2 >> 8; } //JOY2H
+uint8 CPU::mmio_r421c() { controller_flag = true; return status.joy3 >> 0; } //JOY3L
+uint8 CPU::mmio_r421d() { controller_flag = true; return status.joy3 >> 8; } //JOY3H
+uint8 CPU::mmio_r421e() { controller_flag = true; return status.joy4 >> 0; } //JOY4L
+uint8 CPU::mmio_r421f() { controller_flag = true; return status.joy4 >> 8; } //JOY4H
//DMAPx
uint8 CPU::mmio_r43x0(uint8 i) {
--
2.15.0.rc1

View file

@ -1,26 +0,0 @@
From e397bd46f17d6ea00c8c96d5a8e0c5f5b4a6f642 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Sun, 14 Oct 2012 23:31:36 +0300
Subject: [PATCH 10/27] Fix compiling on GCC 4.7
---
nall/string.hpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nall/string.hpp b/nall/string.hpp
index 1b255ce2..07a64dfc 100755
--- a/nall/string.hpp
+++ b/nall/string.hpp
@@ -25,8 +25,8 @@
#include <nall/string/base.hpp>
#include <nall/string/bml.hpp>
#include <nall/string/bsv.hpp>
-#include <nall/string/core.hpp>
#include <nall/string/cast.hpp>
+#include <nall/string/core.hpp>
#include <nall/string/compare.hpp>
#include <nall/string/convert.hpp>
#include <nall/string/cstring.hpp>
--
2.15.0.rc1

View file

@ -1,65 +0,0 @@
From e047aa8eb9883f60e4141effba8128a4a555d8be Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Sun, 27 Oct 2013 10:52:45 +0200
Subject: [PATCH 11/27] Support notifying latches
---
snes/cpu/mmio/mmio.cpp | 1 +
snes/cpu/timing/joypad.cpp | 1 +
snes/interface/interface.cpp | 5 +++++
snes/interface/interface.hpp | 1 +
4 files changed, 8 insertions(+)
diff --git a/snes/cpu/mmio/mmio.cpp b/snes/cpu/mmio/mmio.cpp
index c5ee930f..b7afff00 100755
--- a/snes/cpu/mmio/mmio.cpp
+++ b/snes/cpu/mmio/mmio.cpp
@@ -33,6 +33,7 @@ void CPU::mmio_w2183(uint8 data) {
//strobing $4016.d0 affects both controller port latches.
//$4017 bit 0 writes are ignored.
void CPU::mmio_w4016(uint8 data) {
+ if(data&1) interface->notifyLatched();
input.port1->latch(data & 1);
input.port2->latch(data & 1);
}
diff --git a/snes/cpu/timing/joypad.cpp b/snes/cpu/timing/joypad.cpp
index 179df27d..6a98de00 100755
--- a/snes/cpu/timing/joypad.cpp
+++ b/snes/cpu/timing/joypad.cpp
@@ -9,6 +9,7 @@ void CPU::step_auto_joypad_poll() {
if(status.auto_joypad_active && status.auto_joypad_latch) {
if(status.auto_joypad_counter == 0) {
+ interface->notifyLatched();
input.port1->latch(1);
input.port2->latch(1);
input.port1->latch(0);
diff --git a/snes/interface/interface.cpp b/snes/interface/interface.cpp
index 0a21a132..6685556c 100755
--- a/snes/interface/interface.cpp
+++ b/snes/interface/interface.cpp
@@ -28,4 +28,9 @@ time_t Interface::randomSeed()
return time(0);
}
+void Interface::notifyLatched()
+{
+ //Nothing.
+}
+
}
diff --git a/snes/interface/interface.hpp b/snes/interface/interface.hpp
index 30ee7fde..203f7b0c 100755
--- a/snes/interface/interface.hpp
+++ b/snes/interface/interface.hpp
@@ -7,6 +7,7 @@ struct Interface {
virtual void message(const string &text);
virtual time_t currentTime();
virtual time_t randomSeed();
+ virtual void notifyLatched();
};
extern Interface *interface;
--
2.15.0.rc1

View file

@ -1,799 +0,0 @@
From a5b380757b086e3a00b47fe14e2a63c74683e8da Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Thu, 28 Nov 2013 22:36:29 +0200
Subject: [PATCH 12/27] Support unlimited number of breakpoints
---
snes/alt/cpu/cpu.cpp | 22 +++++++-------
snes/alt/ppu-compatibility/ppu.cpp | 4 +--
snes/alt/ppu-parallel/ppu.cpp | 4 +--
snes/alt/ppu-performance/ppu.cpp | 4 +--
snes/cartridge/cartridge.hpp | 17 +++++++++++
snes/cartridge/markup.cpp | 11 +++++++
snes/cheat/cheat.cpp | 11 ++++---
snes/cheat/cheat.hpp | 1 +
snes/chip/bsx/satellaview/satellaview.cpp | 4 +--
snes/chip/hitachidsp/hitachidsp.cpp | 2 +-
snes/chip/hitachidsp/memory.cpp | 2 +-
snes/chip/nss/nss.cpp | 4 +--
snes/chip/sa1/memory/memory.cpp | 2 +-
snes/chip/sa1/memory/memory.hpp | 2 +-
snes/chip/sa1/sa1.cpp | 2 +-
snes/chip/sdd1/sdd1.cpp | 4 +--
snes/cpu/core/core.hpp | 2 +-
snes/cpu/core/disassembler/disassembler.cpp | 2 +-
snes/cpu/core/memory.hpp | 2 +-
snes/cpu/cpu.cpp | 26 ++++++++--------
snes/cpu/debugger/debugger.cpp | 4 +--
snes/cpu/debugger/debugger.hpp | 2 +-
snes/cpu/dma/dma.cpp | 4 +--
snes/cpu/memory/memory.cpp | 4 +--
snes/cpu/memory/memory.hpp | 2 +-
snes/cpu/mmio/mmio.cpp | 2 +-
snes/debugger/debugger.cpp | 2 +-
snes/memory/memory-inline.hpp | 21 +++++++++++--
snes/memory/memory.cpp | 47 ++++++++++++++++++++++++++---
snes/memory/memory.hpp | 13 +++++++-
snes/ppu/ppu.cpp | 4 +--
snes/smp/core/core.hpp | 2 +-
snes/snes.hpp | 1 +
33 files changed, 166 insertions(+), 70 deletions(-)
diff --git a/snes/alt/cpu/cpu.cpp b/snes/alt/cpu/cpu.cpp
index 814908d0..dcbb92d3 100755
--- a/snes/alt/cpu/cpu.cpp
+++ b/snes/alt/cpu/cpu.cpp
@@ -89,24 +89,24 @@ void CPU::enable() {
function<uint8 (unsigned)> read = { &CPU::mmio_read, (CPU*)&cpu };
function<void (unsigned, uint8)> write = { &CPU::mmio_write, (CPU*)&cpu };
- bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2140, 0x2183, read, write);
- bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2140, 0x2183, read, write);
+ bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2140, 0x2183, 0, read, write);
+ bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2140, 0x2183, 0, read, write);
- bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4016, 0x4017, read, write);
- bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4016, 0x4017, read, write);
+ bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4016, 0x4017, 0, read, write);
+ bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4016, 0x4017, 0, read, write);
- bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4200, 0x421f, read, write);
- bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4200, 0x421f, read, write);
+ bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4200, 0x421f, 0, read, write);
+ bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4200, 0x421f, 0, read, write);
- bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4300, 0x437f, read, write);
- bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4300, 0x437f, read, write);
+ bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4300, 0x437f, 0, read, write);
+ bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4300, 0x437f, 0, read, write);
read = [](unsigned addr) { return cpu.wram[addr]; };
write = [](unsigned addr, uint8 data) { cpu.wram[addr] = data; };
- bus.map(Bus::MapMode::Linear, 0x00, 0x3f, 0x0000, 0x1fff, read, write, 0x000000, 0x002000);
- bus.map(Bus::MapMode::Linear, 0x80, 0xbf, 0x0000, 0x1fff, read, write, 0x000000, 0x002000);
- bus.map(Bus::MapMode::Linear, 0x7e, 0x7f, 0x0000, 0xffff, read, write);
+ bus.map(Bus::MapMode::Linear, 0x00, 0x3f, 0x0000, 0x1fff, 3, read, write, 0x000000, 0x002000);
+ bus.map(Bus::MapMode::Linear, 0x80, 0xbf, 0x0000, 0x1fff, 3, read, write, 0x000000, 0x002000);
+ bus.map(Bus::MapMode::Linear, 0x7e, 0x7f, 0x0000, 0xffff, 3, read, write);
}
void CPU::power() {
diff --git a/snes/alt/ppu-compatibility/ppu.cpp b/snes/alt/ppu-compatibility/ppu.cpp
index a21e5e31..122b1430 100755
--- a/snes/alt/ppu-compatibility/ppu.cpp
+++ b/snes/alt/ppu-compatibility/ppu.cpp
@@ -126,8 +126,8 @@ void PPU::enable() {
function<uint8 (unsigned)> read = { &PPU::mmio_read, (PPU*)&ppu };
function<void (unsigned, uint8)> write = { &PPU::mmio_write, (PPU*)&ppu };
- bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2100, 0x213f, read, write);
- bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2100, 0x213f, read, write);
+ bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2100, 0x213f, 0, read, write);
+ bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2100, 0x213f, 0, read, write);
}
void PPU::power() {
diff --git a/snes/alt/ppu-parallel/ppu.cpp b/snes/alt/ppu-parallel/ppu.cpp
index 1c3dcb70..8dd118b2 100755
--- a/snes/alt/ppu-parallel/ppu.cpp
+++ b/snes/alt/ppu-parallel/ppu.cpp
@@ -36,8 +36,8 @@ void PPU::frame() {
}
void PPU::enable() {
- bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2100, 0x213f, { &PPU::mmio_read, this }, { &PPU::mmio_write, this });
- bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2100, 0x213f, { &PPU::mmio_read, this }, { &PPU::mmio_write, this });
+ bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2100, 0x213f, 0, { &PPU::mmio_read, this }, { &PPU::mmio_write, this });
+ bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2100, 0x213f, 0, { &PPU::mmio_read, this }, { &PPU::mmio_write, this });
}
void PPU::power() {
diff --git a/snes/alt/ppu-performance/ppu.cpp b/snes/alt/ppu-performance/ppu.cpp
index 7c231bc0..4b2b2948 100755
--- a/snes/alt/ppu-performance/ppu.cpp
+++ b/snes/alt/ppu-performance/ppu.cpp
@@ -90,8 +90,8 @@ void PPU::enable() {
function<uint8 (unsigned)> read = { &PPU::mmio_read, (PPU*)&ppu };
function<void (unsigned, uint8)> write = { &PPU::mmio_write, (PPU*)&ppu };
- bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2100, 0x213f, read, write);
- bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2100, 0x213f, read, write);
+ bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2100, 0x213f, 0, read, write);
+ bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2100, 0x213f, 0, read, write);
}
void PPU::power() {
diff --git a/snes/cartridge/cartridge.hpp b/snes/cartridge/cartridge.hpp
index 37555bc0..82e73c4c 100755
--- a/snes/cartridge/cartridge.hpp
+++ b/snes/cartridge/cartridge.hpp
@@ -12,6 +12,22 @@ struct Cartridge : property<Cartridge> {
PAL,
};
+ enum class MemoryClass : unsigned {
+ MISC = 0,
+ ROM = 1,
+ SRAM = 2,
+ WRAM = 3,
+ SUPERFXROM = 4,
+ SUPERFXRAM = 5,
+ SA1IRAM = 6,
+ SA1BWRAM = 7,
+ SUFAMITURBO_ROMA = 8,
+ SUFAMITURBO_ROMB = 9,
+ SUFAMITURBO_RAMA = 10,
+ SUFAMITURBO_RAMB = 11,
+ BSXFLASH = 12,
+ };
+
enum class Slot : unsigned {
Base,
Bsx,
@@ -68,6 +84,7 @@ struct Cartridge : property<Cartridge> {
unsigned addrhi;
unsigned offset;
unsigned size;
+ MemoryClass clazz;
Mapping();
Mapping(const function<uint8 (unsigned)>&, const function<void (unsigned, uint8)>&);
diff --git a/snes/cartridge/markup.cpp b/snes/cartridge/markup.cpp
index e639fe52..2dd0d646 100755
--- a/snes/cartridge/markup.cpp
+++ b/snes/cartridge/markup.cpp
@@ -74,6 +74,7 @@ void Cartridge::parse_markup_rom(XML::Node &root) {
for(auto &node : root) {
if(node.name != "map") continue;
Mapping m(rom);
+ m.clazz = MemoryClass::ROM;
parse_markup_map(m, node);
if(m.size == 0) m.size = rom.size();
mapping.append(m);
@@ -85,6 +86,7 @@ void Cartridge::parse_markup_ram(XML::Node &root) {
ram_size = parse_markup_integer(root["size"].data);
for(auto &node : root) {
Mapping m(ram);
+ m.clazz = MemoryClass::SRAM;
parse_markup_map(m, node);
if(m.size == 0) m.size = ram_size;
mapping.append(m);
@@ -133,6 +135,7 @@ void Cartridge::parse_markup_superfx(XML::Node &root) {
for(auto &leaf : node) {
if(leaf.name != "map") continue;
Mapping m(superfx.rom);
+ //m.clazz = MemoryClass::SUPERFXROM; -- Aliases ROM.
parse_markup_map(m, leaf);
mapping.append(m);
}
@@ -145,6 +148,7 @@ void Cartridge::parse_markup_superfx(XML::Node &root) {
}
if(leaf.name != "map") continue;
Mapping m(superfx.ram);
+ //m.clazz = MemoryClass::SUPERFXRAM; -- Aliases SRAM.
parse_markup_map(m, leaf);
if(m.size == 0) m.size = ram_size;
mapping.append(m);
@@ -188,6 +192,7 @@ void Cartridge::parse_markup_sa1(XML::Node &root) {
for(auto &node : iram) {
if(node.name != "map") continue;
Mapping m(sa1.cpuiram);
+ m.clazz = MemoryClass::SA1IRAM;
parse_markup_map(m, node);
if(m.size == 0) m.size = 2048;
mapping.append(m);
@@ -197,6 +202,7 @@ void Cartridge::parse_markup_sa1(XML::Node &root) {
for(auto &node : bwram) {
if(node.name != "map") continue;
Mapping m(sa1.cpubwram);
+ //m.clazz = MemoryClass::SA1BWRAM; -- Aliases SRAM
parse_markup_map(m, node);
if(m.size == 0) m.size = ram_size;
mapping.append(m);
@@ -341,6 +347,7 @@ void Cartridge::parse_markup_bsx(XML::Node &root) {
for(auto &node : root["slot"]) {
if(node.name != "map") continue;
Mapping m(bsxflash.memory);
+ m.clazz = MemoryClass::BSXFLASH;
parse_markup_map(m, node);
mapping.append(m);
}
@@ -373,6 +380,7 @@ void Cartridge::parse_markup_sufamiturbo(XML::Node &root) {
if(leaf.name != "map") continue;
Memory &memory = slotid == 0 ? sufamiturbo.slotA.rom : sufamiturbo.slotB.rom;
Mapping m(memory);
+ m.clazz = slotid ? MemoryClass::SUFAMITURBO_ROMB : MemoryClass::SUFAMITURBO_ROMA;
parse_markup_map(m, leaf);
if(m.size == 0) m.size = memory.size();
if(m.size) mapping.append(m);
@@ -384,6 +392,7 @@ void Cartridge::parse_markup_sufamiturbo(XML::Node &root) {
if(leaf.name != "map") continue;
Memory &memory = slotid == 0 ? sufamiturbo.slotA.ram : sufamiturbo.slotB.ram;
Mapping m(memory);
+ m.clazz = slotid ? MemoryClass::SUFAMITURBO_RAMB : MemoryClass::SUFAMITURBO_RAMA;
parse_markup_map(m, leaf);
if(m.size == 0) m.size = ram_size;
if(m.size) mapping.append(m);
@@ -536,11 +545,13 @@ void Cartridge::parse_markup_link(XML::Node &root) {
}
Cartridge::Mapping::Mapping() {
+ clazz = MemoryClass::MISC;
mode = Bus::MapMode::Direct;
banklo = bankhi = addrlo = addrhi = offset = size = 0;
}
Cartridge::Mapping::Mapping(Memory &memory) {
+ clazz = MemoryClass::MISC;
read = { &Memory::read, &memory };
write = { &Memory::write, &memory };
mode = Bus::MapMode::Direct;
diff --git a/snes/cheat/cheat.cpp b/snes/cheat/cheat.cpp
index 46c42d1c..3a269cc5 100755
--- a/snes/cheat/cheat.cpp
+++ b/snes/cheat/cheat.cpp
@@ -21,9 +21,9 @@ void Cheat::synchronize() {
for(unsigned i = 0; i < size(); i++) {
const CheatCode &code = operator[](i);
- unsigned addr = mirror(code.addr);
+ unsigned addr = code.nomirror ? code.addr : mirror(code.addr);
override[addr] = true;
- if((addr & 0xffe000) == 0x7e0000) {
+ if(!code.nomirror && (addr & 0xffe000) == 0x7e0000) {
//mirror $7e:0000-1fff to $00-3f|80-bf:0000-1fff
unsigned mirroraddr;
for(unsigned x = 0; x <= 0x3f; x++) {
@@ -40,11 +40,14 @@ void Cheat::synchronize() {
}
uint8 Cheat::read(unsigned addr) const {
- addr = mirror(addr);
+ unsigned raddr = mirror(addr);
for(unsigned i = 0; i < size(); i++) {
const CheatCode &code = operator[](i);
- if(addr == mirror(code.addr)) {
+ if(!code.nomirror && addr == mirror(code.addr)) {
+ return code.data;
+ }
+ if(code.nomirror && raddr == code.addr) {
return code.data;
}
}
diff --git a/snes/cheat/cheat.hpp b/snes/cheat/cheat.hpp
index 306b99b1..b4d2a42e 100755
--- a/snes/cheat/cheat.hpp
+++ b/snes/cheat/cheat.hpp
@@ -1,6 +1,7 @@
struct CheatCode {
unsigned addr;
unsigned data;
+ bool nomirror;
};
struct Cheat : public linear_vector<CheatCode> {
diff --git a/snes/chip/bsx/satellaview/satellaview.cpp b/snes/chip/bsx/satellaview/satellaview.cpp
index 3c980195..25af8e56 100755
--- a/snes/chip/bsx/satellaview/satellaview.cpp
+++ b/snes/chip/bsx/satellaview/satellaview.cpp
@@ -6,8 +6,8 @@ void BSXSatellaview::init() {
}
void BSXSatellaview::load() {
- bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2188, 0x219f, { &BSXSatellaview::mmio_read, &bsxsatellaview }, { &BSXSatellaview::mmio_write, &bsxsatellaview });
- bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2188, 0x219f, { &BSXSatellaview::mmio_read, &bsxsatellaview }, { &BSXSatellaview::mmio_write, &bsxsatellaview });
+ bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2188, 0x219f, 0, { &BSXSatellaview::mmio_read, &bsxsatellaview }, { &BSXSatellaview::mmio_write, &bsxsatellaview });
+ bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2188, 0x219f, 0, { &BSXSatellaview::mmio_read, &bsxsatellaview }, { &BSXSatellaview::mmio_write, &bsxsatellaview });
}
void BSXSatellaview::unload() {
diff --git a/snes/chip/hitachidsp/hitachidsp.cpp b/snes/chip/hitachidsp/hitachidsp.cpp
index 1042267e..3e5c5bdb 100755
--- a/snes/chip/hitachidsp/hitachidsp.cpp
+++ b/snes/chip/hitachidsp/hitachidsp.cpp
@@ -23,7 +23,7 @@ void HitachiDSP::enter() {
break;
case State::DMA:
for(unsigned n = 0; n < regs.dma_length; n++) {
- bus.write(regs.dma_target + n, bus.read(regs.dma_source + n));
+ bus.write(regs.dma_target + n, bus.read(regs.dma_source + n, false));
step(2);
}
state = State::Idle;
diff --git a/snes/chip/hitachidsp/memory.cpp b/snes/chip/hitachidsp/memory.cpp
index 3c9c3af1..36868e88 100755
--- a/snes/chip/hitachidsp/memory.cpp
+++ b/snes/chip/hitachidsp/memory.cpp
@@ -1,7 +1,7 @@
#ifdef HITACHIDSP_CPP
uint8 HitachiDSP::bus_read(unsigned addr) {
- if((addr & 0x408000) == 0x008000) return bus.read(addr);
+ if((addr & 0x408000) == 0x008000) return bus.read(addr, false);
return 0x00;
}
diff --git a/snes/chip/nss/nss.cpp b/snes/chip/nss/nss.cpp
index 964973d0..5946af3b 100755
--- a/snes/chip/nss/nss.cpp
+++ b/snes/chip/nss/nss.cpp
@@ -10,8 +10,8 @@ void NSS::init() {
void NSS::load() {
dip = 0x0000;
- bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4100, 0x4101, { &NSS::read, this }, { &NSS::write, this });
- bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4100, 0x4101, { &NSS::read, this }, { &NSS::write, this });
+ bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4100, 0x4101, 0, { &NSS::read, this }, { &NSS::write, this });
+ bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4100, 0x4101, 0, { &NSS::read, this }, { &NSS::write, this });
}
void NSS::unload() {
diff --git a/snes/chip/sa1/memory/memory.cpp b/snes/chip/sa1/memory/memory.cpp
index d13ac929..9bb4ff20 100755
--- a/snes/chip/sa1/memory/memory.cpp
+++ b/snes/chip/sa1/memory/memory.cpp
@@ -107,7 +107,7 @@ void SA1::op_io() {
tick();
}
-uint8 SA1::op_read(unsigned addr) {
+uint8 SA1::op_read(unsigned addr, bool exec) {
tick();
if(((addr & 0x40e000) == 0x006000) || ((addr & 0xd00000) == 0x400000)) tick();
return bus_read(addr);
diff --git a/snes/chip/sa1/memory/memory.hpp b/snes/chip/sa1/memory/memory.hpp
index ffb9e9f6..ab8e1edd 100755
--- a/snes/chip/sa1/memory/memory.hpp
+++ b/snes/chip/sa1/memory/memory.hpp
@@ -3,7 +3,7 @@ void bus_write(unsigned addr, uint8 data);
uint8 vbr_read(unsigned addr);
alwaysinline void op_io();
-alwaysinline uint8 op_read(unsigned addr);
+alwaysinline uint8 op_read(unsigned addr, bool exec);
alwaysinline void op_write(unsigned addr, uint8 data);
uint8 mmc_read(unsigned addr);
diff --git a/snes/chip/sa1/sa1.cpp b/snes/chip/sa1/sa1.cpp
index 71c6310a..30e00809 100755
--- a/snes/chip/sa1/sa1.cpp
+++ b/snes/chip/sa1/sa1.cpp
@@ -37,7 +37,7 @@ void SA1::enter() {
}
void SA1::op_irq() {
- op_read(regs.pc.d);
+ op_read(regs.pc.d, false);
op_io();
if(!regs.e) op_writestack(regs.pc.b);
op_writestack(regs.pc.h);
diff --git a/snes/chip/sdd1/sdd1.cpp b/snes/chip/sdd1/sdd1.cpp
index c9b8b1c4..5d6cc55f 100755
--- a/snes/chip/sdd1/sdd1.cpp
+++ b/snes/chip/sdd1/sdd1.cpp
@@ -14,8 +14,8 @@ void SDD1::init() {
void SDD1::load() {
//hook S-CPU DMA MMIO registers to gather information for struct dma[];
//buffer address and transfer size information for use in SDD1::mcu_read()
- bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4300, 0x437f, { &SDD1::mmio_read, &sdd1 }, { &SDD1::mmio_write, &sdd1 });
- bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4300, 0x437f, { &SDD1::mmio_read, &sdd1 }, { &SDD1::mmio_write, &sdd1 });
+ bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4300, 0x437f, 0, { &SDD1::mmio_read, &sdd1 }, { &SDD1::mmio_write, &sdd1 });
+ bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4300, 0x437f, 0, { &SDD1::mmio_read, &sdd1 }, { &SDD1::mmio_write, &sdd1 });
}
void SDD1::unload() {
diff --git a/snes/cpu/core/core.hpp b/snes/cpu/core/core.hpp
index 7a685a8d..9d77f3c5 100755
--- a/snes/cpu/core/core.hpp
+++ b/snes/cpu/core/core.hpp
@@ -10,7 +10,7 @@ struct CPUcore {
void powercycle();
virtual void op_io() = 0;
- virtual uint8_t op_read(uint32_t addr) = 0;
+ virtual uint8_t op_read(uint32_t addr, bool exec = false) = 0;
virtual void op_write(uint32_t addr, uint8_t data) = 0;
virtual void last_cycle() = 0;
virtual bool interrupt_pending() = 0;
diff --git a/snes/cpu/core/disassembler/disassembler.cpp b/snes/cpu/core/disassembler/disassembler.cpp
index 030b3ab5..ab8dde24 100755
--- a/snes/cpu/core/disassembler/disassembler.cpp
+++ b/snes/cpu/core/disassembler/disassembler.cpp
@@ -6,7 +6,7 @@ uint8 CPUcore::dreadb(uint32 addr) {
//do not read MMIO registers within debugger
return 0x00;
}
- return bus.read(addr);
+ return bus.read(addr, false);
}
uint16 CPUcore::dreadw(uint32 addr) {
diff --git a/snes/cpu/core/memory.hpp b/snes/cpu/core/memory.hpp
index 49926578..132501c1 100755
--- a/snes/cpu/core/memory.hpp
+++ b/snes/cpu/core/memory.hpp
@@ -1,5 +1,5 @@
alwaysinline uint8_t op_readpc() {
- return op_read((regs.pc.b << 16) + regs.pc.w++);
+ return op_read((regs.pc.b << 16) + regs.pc.w++, true);
}
alwaysinline uint8_t op_readstack() {
diff --git a/snes/cpu/cpu.cpp b/snes/cpu/cpu.cpp
index 2d7d3432..39da6b16 100755
--- a/snes/cpu/cpu.cpp
+++ b/snes/cpu/cpu.cpp
@@ -78,8 +78,8 @@ void CPU::enter() {
} else if(status.reset_pending) {
status.reset_pending = false;
add_clocks(186);
- regs.pc.l = bus.read(0xfffc);
- regs.pc.h = bus.read(0xfffd);
+ regs.pc.l = bus.read(0xfffc, false);
+ regs.pc.h = bus.read(0xfffd, false);
}
}
@@ -95,24 +95,24 @@ void CPU::enable() {
function<uint8 (unsigned)> read = { &CPU::mmio_read, (CPU*)&cpu };
function<void (unsigned, uint8)> write = { &CPU::mmio_write, (CPU*)&cpu };
- bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2140, 0x2183, read, write);
- bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2140, 0x2183, read, write);
+ bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2140, 0x2183, 0, read, write);
+ bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2140, 0x2183, 0, read, write);
- bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4016, 0x4017, read, write);
- bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4016, 0x4017, read, write);
+ bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4016, 0x4017, 0, read, write);
+ bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4016, 0x4017, 0, read, write);
- bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4200, 0x421f, read, write);
- bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4200, 0x421f, read, write);
+ bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4200, 0x421f, 0, read, write);
+ bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4200, 0x421f, 0, read, write);
- bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4300, 0x437f, read, write);
- bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4300, 0x437f, read, write);
+ bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4300, 0x437f, 0, read, write);
+ bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4300, 0x437f, 0, read, write);
read = [](unsigned addr) { return cpu.wram[addr]; };
write = [](unsigned addr, uint8 data) { cpu.wram[addr] = data; };
- bus.map(Bus::MapMode::Linear, 0x00, 0x3f, 0x0000, 0x1fff, read, write, 0x000000, 0x002000);
- bus.map(Bus::MapMode::Linear, 0x80, 0xbf, 0x0000, 0x1fff, read, write, 0x000000, 0x002000);
- bus.map(Bus::MapMode::Linear, 0x7e, 0x7f, 0x0000, 0xffff, read, write);
+ bus.map(Bus::MapMode::Linear, 0x00, 0x3f, 0x0000, 0x1fff, 3, read, write, 0x000000, 0x002000);
+ bus.map(Bus::MapMode::Linear, 0x80, 0xbf, 0x0000, 0x1fff, 3, read, write, 0x000000, 0x002000);
+ bus.map(Bus::MapMode::Linear, 0x7e, 0x7f, 0x0000, 0xffff, 3, read, write);
}
void CPU::power() {
diff --git a/snes/cpu/debugger/debugger.cpp b/snes/cpu/debugger/debugger.cpp
index a33518ed..8301bdb6 100755
--- a/snes/cpu/debugger/debugger.cpp
+++ b/snes/cpu/debugger/debugger.cpp
@@ -19,8 +19,8 @@ void CPUDebugger::op_step() {
synchronize_smp();
}
-uint8 CPUDebugger::op_read(uint32 addr) {
- uint8 data = CPU::op_read(addr);
+uint8 CPUDebugger::op_read(uint32 addr, bool exec) {
+ uint8 data = CPU::op_read(addr, exec);
usage[addr] |= UsageRead;
debugger.breakpoint_test(Debugger::Breakpoint::Source::CPUBus, Debugger::Breakpoint::Mode::Read, addr, data);
return data;
diff --git a/snes/cpu/debugger/debugger.hpp b/snes/cpu/debugger/debugger.hpp
index 579f6f03..c3d66db5 100755
--- a/snes/cpu/debugger/debugger.hpp
+++ b/snes/cpu/debugger/debugger.hpp
@@ -16,7 +16,7 @@ public:
bool opcode_edge; //true right before an opcode execues, used to skip over opcodes
void op_step();
- uint8 op_read(uint32 addr);
+ uint8 op_read(uint32 addr, bool exec = false);
void op_write(uint32 addr, uint8 data);
CPUDebugger();
diff --git a/snes/cpu/dma/dma.cpp b/snes/cpu/dma/dma.cpp
index e8cdb3ec..0a00bfea 100755
--- a/snes/cpu/dma/dma.cpp
+++ b/snes/cpu/dma/dma.cpp
@@ -26,7 +26,7 @@ bool CPU::dma_addr_valid(uint32 abus) {
uint8 CPU::dma_read(uint32 abus) {
if(dma_addr_valid(abus) == false) return 0x00;
- return bus.read(abus);
+ return bus.read(abus, false);
}
//simulate two-stage pipeline for DMA transfers; example:
@@ -49,7 +49,7 @@ void CPU::dma_transfer(bool direction, uint8 bbus, uint32 abus) {
dma_write(dma_transfer_valid(bbus, abus), 0x2100 | bbus, regs.mdr);
} else {
dma_add_clocks(4);
- regs.mdr = dma_transfer_valid(bbus, abus) ? bus.read(0x2100 | bbus) : 0x00;
+ regs.mdr = dma_transfer_valid(bbus, abus) ? bus.read(0x2100 | bbus, false) : 0x00;
dma_add_clocks(4);
dma_write(dma_addr_valid(abus), abus, regs.mdr);
}
diff --git a/snes/cpu/memory/memory.cpp b/snes/cpu/memory/memory.cpp
index c2c8f1fa..31f82c31 100755
--- a/snes/cpu/memory/memory.cpp
+++ b/snes/cpu/memory/memory.cpp
@@ -10,11 +10,11 @@ void CPU::op_io() {
alu_edge();
}
-uint8 CPU::op_read(uint32 addr) {
+uint8 CPU::op_read(uint32 addr, bool exec) {
status.clock_count = speed(addr);
dma_edge();
add_clocks(status.clock_count - 4);
- regs.mdr = bus.read(addr);
+ regs.mdr = bus.read(addr, exec);
add_clocks(4);
alu_edge();
return regs.mdr;
diff --git a/snes/cpu/memory/memory.hpp b/snes/cpu/memory/memory.hpp
index d33861d4..fd64ba8b 100755
--- a/snes/cpu/memory/memory.hpp
+++ b/snes/cpu/memory/memory.hpp
@@ -1,4 +1,4 @@
void op_io();
-debugvirtual uint8 op_read(uint32 addr);
+debugvirtual uint8 op_read(uint32 addr, bool exec);
debugvirtual void op_write(uint32 addr, uint8 data);
alwaysinline unsigned speed(unsigned addr) const;
diff --git a/snes/cpu/mmio/mmio.cpp b/snes/cpu/mmio/mmio.cpp
index b7afff00..30048c19 100755
--- a/snes/cpu/mmio/mmio.cpp
+++ b/snes/cpu/mmio/mmio.cpp
@@ -5,7 +5,7 @@ bool CPU::joylatch() { return status.joypad_strobe_latch; }
//WMDATA
uint8 CPU::mmio_r2180() {
- return bus.read(0x7e0000 | status.wram_addr++);
+ return bus.read(0x7e0000 | status.wram_addr++, false);
}
//WMDATA
diff --git a/snes/debugger/debugger.cpp b/snes/debugger/debugger.cpp
index b1312339..e8d0f5af 100755
--- a/snes/debugger/debugger.cpp
+++ b/snes/debugger/debugger.cpp
@@ -33,7 +33,7 @@ uint8 Debugger::read(Debugger::MemorySource source, unsigned addr) {
case MemorySource::CPUBus: {
//do not read from memory-mapped registers that could affect program behavior
if(((addr - 0x2000) & 0x40c000) == 0x000000) break; //$00-3f:2000-5fff MMIO
- return bus.read(addr & 0xffffff);
+ return bus.read(addr & 0xffffff, false);
} break;
case MemorySource::APUBus: {
diff --git a/snes/memory/memory-inline.hpp b/snes/memory/memory-inline.hpp
index 70503bea..45f150c9 100755
--- a/snes/memory/memory-inline.hpp
+++ b/snes/memory/memory-inline.hpp
@@ -51,11 +51,26 @@ MappedRAM::MappedRAM() : data_(0), size_(0), write_protect_(false) {}
//Bus
-uint8 Bus::read(unsigned addr) {
- if(cheat.override[addr]) return cheat.read(addr);
- return reader[lookup[addr]](target[addr]);
+uint8 Bus::read(unsigned addr, bool exec) {
+ uint8 emask = exec ? 0x24 : 0x09;
+ uint8 val;
+ if(__builtin_expect(cheat.override[addr], 0))
+ val = cheat.read(addr);
+ else
+ val = reader[lookup[addr]](target[addr]);
+ if(__builtin_expect((u_debugflags | debugflags[addr]) & emask, 0)) {
+ unsigned daddr = target[addr];
+ uint8 mclass = classmap[addr];
+ debug_read(mclass, daddr, addr, val, exec);
+ }
+ return val;
}
void Bus::write(unsigned addr, uint8 data) {
+ if(__builtin_expect((u_debugflags | debugflags[addr]) & 0x12, 0)) {
+ unsigned daddr = target[addr];
+ uint8 mclass = classmap[addr];
+ debug_write(mclass, daddr, addr, data);
+ }
return writer[lookup[addr]](target[addr], data);
}
diff --git a/snes/memory/memory.cpp b/snes/memory/memory.cpp
index ede9cbd0..a9a484a0 100755
--- a/snes/memory/memory.cpp
+++ b/snes/memory/memory.cpp
@@ -27,6 +27,7 @@ void Bus::map(
MapMode mode,
unsigned bank_lo, unsigned bank_hi,
unsigned addr_lo, unsigned addr_hi,
+ unsigned mclass,
const function<uint8 (unsigned)> &rd,
const function<void (unsigned, uint8)> &wr,
unsigned base, unsigned length
@@ -48,6 +49,7 @@ void Bus::map(
if(mode == MapMode::Shadow) destaddr = mirror(base + destaddr, length);
lookup[(bank << 16) | addr] = id;
target[(bank << 16) | addr] = destaddr;
+ if(mclass) classmap[(bank << 16) | addr] = mclass;
}
}
}
@@ -57,23 +59,58 @@ void Bus::map_reset() {
function<void (unsigned, uint8)> writer = [](unsigned, uint8) {};
idcount = 0;
- map(MapMode::Direct, 0x00, 0xff, 0x0000, 0xffff, reader, writer);
+ map(MapMode::Direct, 0x00, 0xff, 0x0000, 0xffff, 0xFF, reader, writer);
}
void Bus::map_xml() {
for(auto &m : cartridge.mapping) {
- map(m.mode, m.banklo, m.bankhi, m.addrlo, m.addrhi, m.read, m.write, m.offset, m.size);
+ map(m.mode, m.banklo, m.bankhi, m.addrlo, m.addrhi, (unsigned)m.clazz, m.read, m.write, m.offset, m.size);
}
}
+unsigned Bus::enumerateMirrors(uint8 clazz, uint32 offset, unsigned start)
+{
+ unsigned i;
+ for(i = start; i < 0x1000000; i++)
+ if((classmap[i] == clazz && target[i] == offset) || (i == offset && clazz == 255))
+ return i;
+ return i;
+}
+
+void Bus::clearDebugFlags()
+{
+ u_debugflags = 0;
+ memset(debugflags, 0, 0x1000000);
+}
+
+void Bus::debugFlags(uint8 setf, uint8 clrf)
+{
+ u_debugflags = (u_debugflags | setf) & ~clrf;
+}
+
+void Bus::debugFlags(uint8 setf, uint8 clrf, uint8 clazz, uint32 offset)
+{
+ if(clazz == 255) {
+ setf <<= 3;
+ clrf <<= 3;
+ debugflags[offset] = (debugflags[offset] | setf) & ~clrf;
+ } else
+ for(unsigned i = 0; i < 0x1000000; i++)
+ if(classmap[i] == clazz && target[i] == offset)
+ debugflags[i] = (debugflags[i] | setf) & ~clrf;
+}
+
Bus::Bus() {
- lookup = new uint8 [16 * 1024 * 1024];
- target = new uint32[16 * 1024 * 1024];
+ u_debugflags = 0;
+ lookup = new uint8 [112 * 1024 * 1024];
+ target = (uint32*)(lookup + 0x3000000);
+ classmap = lookup + 0x1000000;
+ debugflags = lookup + 0x2000000;
+ memset(debugflags, 0, 0x1000000);
}
Bus::~Bus() {
delete[] lookup;
- delete[] target;
}
}
diff --git a/snes/memory/memory.hpp b/snes/memory/memory.hpp
index 634e0717..c20e14db 100755
--- a/snes/memory/memory.hpp
+++ b/snes/memory/memory.hpp
@@ -44,10 +44,13 @@ private:
struct Bus {
unsigned mirror(unsigned addr, unsigned size);
- alwaysinline uint8 read(unsigned addr);
+ alwaysinline uint8 read(unsigned addr, bool exec);
alwaysinline void write(unsigned addr, uint8 data);
uint8 *lookup;
+ uint8 *classmap;
+ uint8 *debugflags;
+ uint8 u_debugflags;
uint32 *target;
unsigned idcount;
@@ -59,6 +62,7 @@ struct Bus {
MapMode mode,
unsigned bank_lo, unsigned bank_hi,
unsigned addr_lo, unsigned addr_hi,
+ unsigned mclass,
const function<uint8 (unsigned)> &read,
const function<void (unsigned, uint8)> &write,
unsigned base = 0, unsigned length = 0
@@ -67,6 +71,13 @@ struct Bus {
void map_reset();
void map_xml();
+ void clearDebugFlags();
+ void debugFlags(uint8 setf, uint8 clrf);
+ void debugFlags(uint8 setf, uint8 clrf, uint8 clazz, uint32 offset);
+ unsigned enumerateMirrors(uint8 clazz, uint32 offset, unsigned start);
+ function<void (uint8, unsigned, unsigned, uint8, bool)> debug_read;
+ function<void (uint8, unsigned, unsigned, uint8)> debug_write;
+
Bus();
~Bus();
};
diff --git a/snes/ppu/ppu.cpp b/snes/ppu/ppu.cpp
index 8545175f..13e231cf 100755
--- a/snes/ppu/ppu.cpp
+++ b/snes/ppu/ppu.cpp
@@ -87,8 +87,8 @@ void PPU::enable() {
function<uint8 (unsigned)> read = { &PPU::mmio_read, (PPU*)&ppu };
function<void (unsigned, uint8)> write = { &PPU::mmio_write, (PPU*)&ppu };
- bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2100, 0x213f, read, write);
- bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2100, 0x213f, read, write);
+ bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2100, 0x213f, 0, read, write);
+ bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2100, 0x213f, 0, read, write);
}
void PPU::power() {
diff --git a/snes/smp/core/core.hpp b/snes/smp/core/core.hpp
index 1489fcef..13d69364 100755
--- a/snes/smp/core/core.hpp
+++ b/snes/smp/core/core.hpp
@@ -2,7 +2,7 @@ struct SMPcore {
virtual void op_io() = 0;
virtual uint8 op_read(uint16 addr) = 0;
virtual void op_write(uint16 addr, uint8 data) = 0;
- void op_step();
+ virtual void op_step();
#include "registers.hpp"
#include "memory.hpp"
diff --git a/snes/snes.hpp b/snes/snes.hpp
index dffeeee3..37ed1feb 100755
--- a/snes/snes.hpp
+++ b/snes/snes.hpp
@@ -1,5 +1,6 @@
#ifndef SNES_HPP
#define SNES_HPP
+#define BSNES_SUPPORTS_ADV_BREAKPOINTS
namespace SNES {
namespace Info {
--
2.15.0.rc1

View file

@ -1,22 +0,0 @@
From 8bc6bb381e680616dcc843c99889799aedd43163 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Sat, 30 Nov 2013 10:27:37 +0200
Subject: [PATCH 13/27] Support auto-detecting bsnes version
---
bsnes.mk | 3 +++
1 file changed, 3 insertions(+)
create mode 100644 bsnes.mk
diff --git a/bsnes.mk b/bsnes.mk
new file mode 100644
index 00000000..20f22f61
--- /dev/null
+++ b/bsnes.mk
@@ -0,0 +1,3 @@
+BSNES_SUPPORTS_DEBUGGER=yes
+LIBSNES_DIR=ui-libsnes
+BSNES_VERSION=085
--
2.15.0.rc1

View file

@ -1,138 +0,0 @@
From 40c456dadd79cb2c94379fda8b41a4d0ba051ad1 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Sat, 7 Dec 2013 23:32:44 +0200
Subject: [PATCH 14/27] Support alternate (more accurate) poll timings
---
snes/config/config.cpp | 1 +
snes/config/config.hpp | 1 +
snes/cpu/timing/joypad.cpp | 40 ++++++++++++++++++++++++++++++++++++++++
snes/cpu/timing/timing.cpp | 16 ++++++++++++----
snes/cpu/timing/timing.hpp | 1 +
snes/snes.hpp | 1 +
6 files changed, 56 insertions(+), 4 deletions(-)
diff --git a/snes/config/config.cpp b/snes/config/config.cpp
index 701af94c..206daae0 100755
--- a/snes/config/config.cpp
+++ b/snes/config/config.cpp
@@ -13,6 +13,7 @@ Configuration::Configuration() {
cpu.ntsc_frequency = 21477272; //315 / 88 * 6000000
cpu.pal_frequency = 21281370;
cpu.wram_init_value = 0x55;
+ cpu.alt_poll_timings = false;
smp.ntsc_frequency = 24607104; //32040.5 * 768
smp.pal_frequency = 24607104;
diff --git a/snes/config/config.hpp b/snes/config/config.hpp
index 1f4d037c..dabde597 100755
--- a/snes/config/config.hpp
+++ b/snes/config/config.hpp
@@ -10,6 +10,7 @@ struct Configuration {
unsigned ntsc_frequency;
unsigned pal_frequency;
unsigned wram_init_value;
+ bool alt_poll_timings;
} cpu;
struct SMP {
diff --git a/snes/cpu/timing/joypad.cpp b/snes/cpu/timing/joypad.cpp
index 6a98de00..ae8e94f8 100755
--- a/snes/cpu/timing/joypad.cpp
+++ b/snes/cpu/timing/joypad.cpp
@@ -29,4 +29,44 @@ void CPU::step_auto_joypad_poll() {
}
}
+//called every 128 clocks; see CPU::add_clocks()
+void CPU::step_auto_joypad_poll_NEW(bool polarity) {
+ if(status.auto_joypad_counter > 0 && status.auto_joypad_counter <= 34) {
+ if(!status.auto_joypad_latch) {
+ //FIXME: Is this right, busy flag goes on even if not enabled???
+ if(status.auto_joypad_counter == 1)
+ status.auto_joypad_active = true;
+ if(status.auto_joypad_counter == 34)
+ status.auto_joypad_active = false;
+ } else {
+ if(status.auto_joypad_counter == 1) {
+ status.auto_joypad_active = true;
+ input.port1->latch(1);
+ input.port2->latch(1);
+ }
+ if(status.auto_joypad_counter == 3) {
+ input.port1->latch(0);
+ input.port2->latch(0);
+ }
+ if((status.auto_joypad_counter & 1) != 0 && status.auto_joypad_counter != 1) {
+ uint2 port0 = input.port1->data();
+ uint2 port1 = input.port2->data();
+
+ status.joy1 = (status.joy1 << 1) | (bool)(port0 & 1);
+ status.joy2 = (status.joy2 << 1) | (bool)(port1 & 1);
+ status.joy3 = (status.joy3 << 1) | (bool)(port0 & 2);
+ status.joy4 = (status.joy4 << 1) | (bool)(port1 & 2);
+ }
+ if(status.auto_joypad_counter == 34)
+ status.auto_joypad_active = false;
+ }
+ status.auto_joypad_counter++;
+ }
+ if(vcounter() >= (ppu.overscan() == false ? 225 : 240) && status.auto_joypad_counter == 0 && !polarity) {
+ status.auto_joypad_latch = status.auto_joypad_poll;
+ status.auto_joypad_counter = 1;
+ }
+}
+
+
#endif
diff --git a/snes/cpu/timing/timing.cpp b/snes/cpu/timing/timing.cpp
index f1378f0c..d7cf24f3 100755
--- a/snes/cpu/timing/timing.cpp
+++ b/snes/cpu/timing/timing.cpp
@@ -17,10 +17,18 @@ void CPU::add_clocks(unsigned clocks) {
step(clocks);
- status.auto_joypad_clock += clocks;
- if(status.auto_joypad_clock >= 256) {
- status.auto_joypad_clock -= 256;
- step_auto_joypad_poll();
+ if(config.cpu.alt_poll_timings) {
+ bool opolarity = (status.auto_joypad_clock & 128);
+ status.auto_joypad_clock = (status.auto_joypad_clock + clocks) & 0xFF;
+ bool npolarity = (status.auto_joypad_clock & 128);
+ if(opolarity != npolarity)
+ step_auto_joypad_poll_NEW(opolarity);
+ } else {
+ status.auto_joypad_clock += clocks;
+ if(status.auto_joypad_clock >= 256) {
+ status.auto_joypad_clock -= 256;
+ step_auto_joypad_poll();
+ }
}
if(status.dram_refreshed == false && hcounter() >= status.dram_refresh_position) {
diff --git a/snes/cpu/timing/timing.hpp b/snes/cpu/timing/timing.hpp
index 6c225dab..bf15a727 100755
--- a/snes/cpu/timing/timing.hpp
+++ b/snes/cpu/timing/timing.hpp
@@ -22,3 +22,4 @@ alwaysinline bool irq_test();
//joypad.cpp
void step_auto_joypad_poll();
+void step_auto_joypad_poll_NEW(bool polarity);
diff --git a/snes/snes.hpp b/snes/snes.hpp
index 37ed1feb..4e3ba64c 100755
--- a/snes/snes.hpp
+++ b/snes/snes.hpp
@@ -1,6 +1,7 @@
#ifndef SNES_HPP
#define SNES_HPP
#define BSNES_SUPPORTS_ADV_BREAKPOINTS
+#define BSNES_SUPPORTS_ALT_TIMINGS
namespace SNES {
namespace Info {
--
2.15.0.rc1

View file

@ -1,103 +0,0 @@
From 863bde899b53ae31e854096ac5258208c848a293 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Thu, 6 Mar 2014 21:07:54 +0200
Subject: [PATCH 15/27] Fix mouse speed support
---
snes/config/config.cpp | 1 +
snes/config/config.hpp | 3 +++
snes/controller/mouse/mouse.cpp | 11 +++++++++--
snes/controller/mouse/mouse.hpp | 1 +
4 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/snes/config/config.cpp b/snes/config/config.cpp
index 206daae0..19831370 100755
--- a/snes/config/config.cpp
+++ b/snes/config/config.cpp
@@ -8,6 +8,7 @@ Configuration::Configuration() {
expansion_port = System::ExpansionPortDevice::BSX;
region = System::Region::Autodetect;
random = true;
+ mouse_speed_fix = false;
cpu.version = 2;
cpu.ntsc_frequency = 21477272; //315 / 88 * 6000000
diff --git a/snes/config/config.hpp b/snes/config/config.hpp
index dabde597..68fe0bde 100755
--- a/snes/config/config.hpp
+++ b/snes/config/config.hpp
@@ -1,9 +1,12 @@
+#define BSNES_SUPPORTS_MOUSE_SPEED_FIX
+
struct Configuration {
Input::Device controller_port1;
Input::Device controller_port2;
System::ExpansionPortDevice expansion_port;
System::Region region;
bool random;
+ bool mouse_speed_fix;
struct CPU {
unsigned version;
diff --git a/snes/controller/mouse/mouse.cpp b/snes/controller/mouse/mouse.cpp
index 1a066b98..caa7a358 100755
--- a/snes/controller/mouse/mouse.cpp
+++ b/snes/controller/mouse/mouse.cpp
@@ -1,6 +1,10 @@
#ifdef CONTROLLER_CPP
uint2 Mouse::data() {
+ if(config.mouse_speed_fix && latched) {
+ speed = (speed + 1) % 3;
+ return 0;
+ }
if(counter >= 32) return 1;
if(counter == 0) {
@@ -31,8 +35,8 @@ uint2 Mouse::data() {
case 8: return interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Right);
case 9: return interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Left);
- case 10: return 0; //speed (0 = slow, 1 = normal, 2 = fast, 3 = unused)
- case 11: return 0; // ||
+ case 10: return speed >> 1; //speed (0 = slow, 1 = normal, 2 = fast, 3 = unused)
+ case 11: return speed & 1; // ||
case 12: return 0; //signature
case 13: return 0; // ||
@@ -75,10 +79,12 @@ void Mouse::serialize(serializer& s) {
block[3] = (unsigned short)_position_x;
block[4] = (unsigned short)_position_y >> 8;
block[5] = (unsigned short)_position_y;
+ block[6] = speed;
s.array(block, Controller::SaveSize);
if(s.mode() == nall::serializer::Load) {
latched = (block[0] != 0);
counter = block[1];
+ speed = block[6];
_position_x = (short)(((unsigned short)block[2] << 8) | (unsigned short)block[3]);
_position_y = (short)(((unsigned short)block[4] << 8) | (unsigned short)block[5]);
}
@@ -87,6 +93,7 @@ void Mouse::serialize(serializer& s) {
Mouse::Mouse(bool port) : Controller(port) {
latched = 0;
counter = 0;
+ speed = 0;
}
#endif
diff --git a/snes/controller/mouse/mouse.hpp b/snes/controller/mouse/mouse.hpp
index b07c8ab7..13a9313e 100755
--- a/snes/controller/mouse/mouse.hpp
+++ b/snes/controller/mouse/mouse.hpp
@@ -6,6 +6,7 @@ struct Mouse : Controller {
private:
bool latched;
unsigned counter;
+ unsigned speed;
int _position_x;
int _position_y;
};
--
2.15.0.rc1

View file

@ -1,25 +0,0 @@
From 60267d1f22fd2ff3197c6c829640f66304c89283 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Sun, 16 Mar 2014 16:40:55 +0200
Subject: [PATCH 16/27] Fix tracelog of controller registers
---
snes/cpu/core/disassembler/disassembler.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/snes/cpu/core/disassembler/disassembler.cpp b/snes/cpu/core/disassembler/disassembler.cpp
index ab8dde24..624a80ce 100755
--- a/snes/cpu/core/disassembler/disassembler.cpp
+++ b/snes/cpu/core/disassembler/disassembler.cpp
@@ -1,6 +1,8 @@
#ifdef CPUCORE_CPP
uint8 CPUcore::dreadb(uint32 addr) {
+ if((addr & 0x40fff8) == 0x4218)
+ return bus.read(addr, false); //Controller registers are safe to read.
if((addr & 0x40ffff) >= 0x2000 && (addr & 0x40ffff) <= 0x5fff) {
//$[00-3f|80-bf]:[2000-5fff]
//do not read MMIO registers within debugger
--
2.15.0.rc1

View file

@ -1,105 +0,0 @@
From de71f12eb59a41899a5c77d797e144e6f0919777 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Mon, 17 Mar 2014 14:22:58 +0200
Subject: [PATCH 17/27] Fix performance problem with non-bus breakpoints
---
snes/memory/memory.cpp | 35 ++++++++++++++++++++++++++---------
snes/memory/memory.hpp | 1 +
snes/snes.hpp | 1 +
3 files changed, 28 insertions(+), 9 deletions(-)
diff --git a/snes/memory/memory.cpp b/snes/memory/memory.cpp
index a9a484a0..d22e3137 100755
--- a/snes/memory/memory.cpp
+++ b/snes/memory/memory.cpp
@@ -43,6 +43,7 @@ void Bus::map(
unsigned offset = 0;
for(unsigned bank = bank_lo; bank <= bank_hi; bank++) {
+ region_start.insert((bank << 16) | addr_lo);
for(unsigned addr = addr_lo; addr <= addr_hi; addr++) {
unsigned destaddr = (bank << 16) | addr;
if(mode == MapMode::Linear) destaddr = mirror(base + offset++, length);
@@ -60,6 +61,7 @@ void Bus::map_reset() {
idcount = 0;
map(MapMode::Direct, 0x00, 0xff, 0x0000, 0xffff, 0xFF, reader, writer);
+ region_start.clear();
}
void Bus::map_xml() {
@@ -70,11 +72,21 @@ void Bus::map_xml() {
unsigned Bus::enumerateMirrors(uint8 clazz, uint32 offset, unsigned start)
{
- unsigned i;
- for(i = start; i < 0x1000000; i++)
- if((classmap[i] == clazz && target[i] == offset) || (i == offset && clazz == 255))
- return i;
- return i;
+ if(clazz == 255) {
+ if(start > offset)
+ return 0x1000000;
+ else
+ return start;
+ }
+ //Given region can not contain the same address twice.
+ for(std::set<uint32>::iterator i = region_start.lower_bound(start); i != region_start.end(); i++) {
+ if(classmap[*i] != clazz) continue;
+ if(target[*i] > offset) continue;
+ uint32 wouldbe = offset - target[*i] + *i;
+ if(wouldbe > 0xFFFFFF) continue;
+ if(classmap[wouldbe] == clazz && target[wouldbe] == offset) return wouldbe;
+ }
+ return 0x1000000;
}
void Bus::clearDebugFlags()
@@ -94,10 +106,15 @@ void Bus::debugFlags(uint8 setf, uint8 clrf, uint8 clazz, uint32 offset)
setf <<= 3;
clrf <<= 3;
debugflags[offset] = (debugflags[offset] | setf) & ~clrf;
- } else
- for(unsigned i = 0; i < 0x1000000; i++)
- if(classmap[i] == clazz && target[i] == offset)
- debugflags[i] = (debugflags[i] | setf) & ~clrf;
+ } else {
+ uint32 i = 0;
+ while(true) {
+ i = enumerateMirrors(clazz, offset, i);
+ if(i >= 0x1000000) break;
+ debugflags[i] = (debugflags[i] | setf) & ~clrf;
+ i++;
+ }
+ }
}
Bus::Bus() {
diff --git a/snes/memory/memory.hpp b/snes/memory/memory.hpp
index c20e14db..ee0c0a9e 100755
--- a/snes/memory/memory.hpp
+++ b/snes/memory/memory.hpp
@@ -52,6 +52,7 @@ struct Bus {
uint8 *debugflags;
uint8 u_debugflags;
uint32 *target;
+ std::set<uint32> region_start;
unsigned idcount;
function<uint8 (unsigned)> reader[256];
diff --git a/snes/snes.hpp b/snes/snes.hpp
index 4e3ba64c..9589db9b 100755
--- a/snes/snes.hpp
+++ b/snes/snes.hpp
@@ -38,6 +38,7 @@ namespace SNES {
#include <nall/varint.hpp>
#include <nall/vector.hpp>
#include <nall/gameboy/cartridge.hpp>
+#include <set>
using namespace nall;
#include <gameboy/gameboy.hpp>
--
2.15.0.rc1

View file

@ -1,537 +0,0 @@
From 6bd069191d29ad70d38c82d59dd72cd0996fc45c Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Mon, 31 Mar 2014 20:17:46 +0300
Subject: [PATCH 18/27] Support VRAM, OAM, CGRAM and APURAM breakpoints
---
snes/alt/ppu-compatibility/memory/memory.cpp | 44 +++++++++++++++++++++--
snes/alt/ppu-compatibility/ppu.hpp | 6 ++++
snes/cartridge/cartridge.hpp | 4 +++
snes/smp/core/core.hpp | 2 +-
snes/smp/core/memory.hpp | 6 ++--
snes/smp/core/opcodes.cpp | 36 +++++++++----------
snes/smp/debugger/debugger.cpp | 4 +--
snes/smp/debugger/debugger.hpp | 2 +-
snes/smp/memory/memory.cpp | 54 +++++++++++++++++++---------
snes/smp/memory/memory.hpp | 4 +--
snes/smp/smp.hpp | 4 +++
snes/snes.hpp | 1 +
12 files changed, 122 insertions(+), 45 deletions(-)
diff --git a/snes/alt/ppu-compatibility/memory/memory.cpp b/snes/alt/ppu-compatibility/memory/memory.cpp
index 3f120d84..e47cf201 100755
--- a/snes/alt/ppu-compatibility/memory/memory.cpp
+++ b/snes/alt/ppu-compatibility/memory/memory.cpp
@@ -47,20 +47,31 @@ uint8 PPU::vram_mmio_read(uint16 addr) {
data = vram[addr];
}
}
-
+ if(__builtin_expect(vram_debugflags[addr] & 0x1, 0)) {
+ debug_read(13, addr, data);
+ }
return data;
}
void PPU::vram_mmio_write(uint16 addr, uint8 data) {
if(regs.display_disabled == true) {
+ if(__builtin_expect(vram_debugflags[addr] & 0x2, 0)) {
+ debug_write(13, addr, data);
+ }
vram[addr] = data;
} else {
uint16 v = cpu.vcounter();
uint16 h = cpu.hcounter();
if(v == 0) {
if(h <= 4) {
+ if(__builtin_expect(vram_debugflags[addr] & 0x2, 0)) {
+ debug_write(13, addr, data);
+ }
vram[addr] = data;
} else if(h == 6) {
+ if(__builtin_expect(vram_debugflags[addr] & 0x2, 0)) {
+ debug_write(13, addr, cpu.regs.mdr);
+ }
vram[addr] = cpu.regs.mdr;
} else {
//no write
@@ -71,9 +82,15 @@ void PPU::vram_mmio_write(uint16 addr, uint8 data) {
if(h <= 4) {
//no write
} else {
+ if(__builtin_expect(vram_debugflags[addr] & 0x2, 0)) {
+ debug_write(13, addr, data);
+ }
vram[addr] = data;
}
} else {
+ if(__builtin_expect(vram_debugflags[addr] & 0x2, 0)) {
+ debug_write(13, addr, data);
+ }
vram[addr] = data;
}
}
@@ -93,7 +110,9 @@ uint8 PPU::oam_mmio_read(uint16 addr) {
data = oam[addr];
}
}
-
+ if(__builtin_expect(oam_debugflags[addr] & 0x1, 0)) {
+ debug_read(14, addr, data);
+ }
return data;
}
@@ -104,13 +123,22 @@ void PPU::oam_mmio_write(uint16 addr, uint8 data) {
sprite_list_valid = false;
if(regs.display_disabled == true) {
+ if(__builtin_expect(oam_debugflags[addr] & 0x2, 0)) {
+ debug_write(14, addr, data);
+ }
oam[addr] = data;
update_sprite_list(addr, data);
} else {
if(cpu.vcounter() < (!overscan() ? 225 : 240)) {
+ if(__builtin_expect(oam_debugflags[regs.ioamaddr] & 0x2, 0)) {
+ debug_write(14, regs.ioamaddr, data);
+ }
oam[regs.ioamaddr] = data;
update_sprite_list(regs.ioamaddr, data);
} else {
+ if(__builtin_expect(oam_debugflags[addr] & 0x2, 0)) {
+ debug_write(14, addr, data);
+ }
oam[addr] = data;
update_sprite_list(addr, data);
}
@@ -134,6 +162,9 @@ uint8 PPU::cgram_mmio_read(uint16 addr) {
}
if(addr & 1) data &= 0x7f;
+ if(__builtin_expect(cgram_debugflags[addr] & 0x1, 0)) {
+ debug_read(15, addr, data);
+ }
return data;
}
@@ -142,13 +173,22 @@ void PPU::cgram_mmio_write(uint16 addr, uint8 data) {
if(addr & 1) data &= 0x7f;
if(1 || regs.display_disabled == true) {
+ if(__builtin_expect(cgram_debugflags[addr] & 0x2, 0)) {
+ debug_write(15, addr, data);
+ }
cgram[addr] = data;
} else {
uint16 v = cpu.vcounter();
uint16 h = cpu.hcounter();
if(v < (!overscan() ? 225 : 240) && h >= 128 && h < 1096) {
+ if(__builtin_expect(cgram_debugflags[regs.icgramaddr] & 0x2, 0)) {
+ debug_write(15, regs.icgramaddr, data & 0x7f);
+ }
cgram[regs.icgramaddr] = data & 0x7f;
} else {
+ if(__builtin_expect(cgram_debugflags[addr] & 0x2, 0)) {
+ debug_write(15, addr, data);
+ }
cgram[addr] = data;
}
}
diff --git a/snes/alt/ppu-compatibility/ppu.hpp b/snes/alt/ppu-compatibility/ppu.hpp
index cccaabba..4adac4c4 100755
--- a/snes/alt/ppu-compatibility/ppu.hpp
+++ b/snes/alt/ppu-compatibility/ppu.hpp
@@ -3,6 +3,12 @@ public:
uint8 vram[128 * 1024];
uint8 oam[544];
uint8 cgram[512];
+ //4 is read, 2 is write.
+ uint8 vram_debugflags[128 * 1024];
+ uint8 oam_debugflags[544];
+ uint8 cgram_debugflags[512];
+ function<void (uint8, unsigned, uint8)> debug_read;
+ function<void (uint8, unsigned, uint8)> debug_write;
enum : bool { Threaded = true };
alwaysinline void step(unsigned clocks);
diff --git a/snes/cartridge/cartridge.hpp b/snes/cartridge/cartridge.hpp
index 82e73c4c..2358c088 100755
--- a/snes/cartridge/cartridge.hpp
+++ b/snes/cartridge/cartridge.hpp
@@ -26,6 +26,10 @@ struct Cartridge : property<Cartridge> {
SUFAMITURBO_RAMA = 10,
SUFAMITURBO_RAMB = 11,
BSXFLASH = 12,
+ VRAM = 13,
+ OAM = 14,
+ CGRAM = 15,
+ APURAM = 16,
};
enum class Slot : unsigned {
diff --git a/snes/smp/core/core.hpp b/snes/smp/core/core.hpp
index 13d69364..03f9ac66 100755
--- a/snes/smp/core/core.hpp
+++ b/snes/smp/core/core.hpp
@@ -1,6 +1,6 @@
struct SMPcore {
virtual void op_io() = 0;
- virtual uint8 op_read(uint16 addr) = 0;
+ virtual uint8 op_read(uint16 addr, bool exec) = 0;
virtual void op_write(uint16 addr, uint8 data) = 0;
virtual void op_step();
diff --git a/snes/smp/core/memory.hpp b/snes/smp/core/memory.hpp
index c4b6d99f..c297962f 100755
--- a/snes/smp/core/memory.hpp
+++ b/snes/smp/core/memory.hpp
@@ -1,9 +1,9 @@
alwaysinline uint8 op_readpc() {
- return op_read(regs.pc++);
+ return op_read(regs.pc++, true);
}
alwaysinline uint8 op_readsp() {
- return op_read(0x0100 | ++regs.s);
+ return op_read(0x0100 | ++regs.s, false);
}
alwaysinline void op_writesp(uint8 data) {
@@ -11,7 +11,7 @@ alwaysinline void op_writesp(uint8 data) {
}
alwaysinline uint8 op_readdp(uint8 addr) {
- return op_read((regs.p.p << 8) + addr);
+ return op_read((regs.p.p << 8) + addr, false);
}
alwaysinline void op_writedp(uint8 addr, uint8 data) {
diff --git a/snes/smp/core/opcodes.cpp b/snes/smp/core/opcodes.cpp
index 95b9844f..43db081d 100755
--- a/snes/smp/core/opcodes.cpp
+++ b/snes/smp/core/opcodes.cpp
@@ -11,7 +11,7 @@ template<uint8 (SMPcore::*op)(uint8)>
void SMPcore::op_adjust_addr() {
dp.l = op_readpc();
dp.h = op_readpc();
- rd = op_read(dp);
+ rd = op_read(dp, false);
rd = call(rd);
op_write(dp, rd);
}
@@ -78,7 +78,7 @@ template<uint8 (SMPcore::*op)(uint8, uint8)>
void SMPcore::op_read_addr(uint8 &r) {
dp.l = op_readpc();
dp.h = op_readpc();
- rd = op_read(dp);
+ rd = op_read(dp, false);
r = call(r, rd);
}
@@ -87,7 +87,7 @@ void SMPcore::op_read_addri(uint8 &r) {
dp.l = op_readpc();
dp.h = op_readpc();
op_io();
- rd = op_read(dp + r);
+ rd = op_read(dp + r, false);
regs.a = call(regs.a, rd);
}
@@ -127,7 +127,7 @@ void SMPcore::op_read_idpx() {
op_io();
sp.l = op_readdp(dp++);
sp.h = op_readdp(dp++);
- rd = op_read(sp);
+ rd = op_read(sp, false);
regs.a = call(regs.a, rd);
}
@@ -137,7 +137,7 @@ void SMPcore::op_read_idpy() {
op_io();
sp.l = op_readdp(dp++);
sp.h = op_readdp(dp++);
- rd = op_read(sp + regs.y);
+ rd = op_read(sp + regs.y, false);
regs.a = call(regs.a, rd);
}
@@ -153,7 +153,7 @@ void SMPcore::op_set_addr_bit() {
dp.h = op_readpc();
bit = dp >> 13;
dp &= 0x1fff;
- rd = op_read(dp);
+ rd = op_read(dp, false);
switch(opcode >> 5) {
case 0: //orc addr:bit
case 1: //orc !addr:bit
@@ -198,10 +198,10 @@ void SMPcore::op_set_flag(bool &flag, bool data) {
void SMPcore::op_test_addr(bool set) {
dp.l = op_readpc();
dp.h = op_readpc();
- rd = op_read(dp);
+ rd = op_read(dp, false);
regs.p.n = (regs.a - rd) & 0x80;
regs.p.z = (regs.a - rd) == 0;
- op_read(dp);
+ op_read(dp, false);
op_write(dp, set ? rd | regs.a : rd & ~regs.a);
}
@@ -216,7 +216,7 @@ void SMPcore::op_transfer(uint8 &from, uint8 &to) {
void SMPcore::op_write_addr(uint8 &r) {
dp.l = op_readpc();
dp.h = op_readpc();
- op_read(dp);
+ op_read(dp, false);
op_write(dp, r);
}
@@ -225,7 +225,7 @@ void SMPcore::op_write_addri(uint8 &i) {
dp.h = op_readpc();
op_io();
dp += i;
- op_read(dp);
+ op_read(dp, false);
op_write(dp, regs.a);
}
@@ -317,8 +317,8 @@ void SMPcore::op_bne_ydec() {
}
void SMPcore::op_brk() {
- rd.l = op_read(0xffde);
- rd.h = op_read(0xffdf);
+ rd.l = op_read(0xffde, false);
+ rd.h = op_read(0xffdf, false);
op_io();
op_io();
op_writesp(regs.pc.h);
@@ -411,8 +411,8 @@ void SMPcore::op_jmp_iaddrx() {
dp.h = op_readpc();
op_io();
dp += regs.x;
- rd.l = op_read(dp++);
- rd.h = op_read(dp++);
+ rd.l = op_read(dp++, false);
+ rd.h = op_read(dp++, false);
regs.pc = rd;
}
@@ -438,8 +438,8 @@ void SMPcore::op_jsr_addr() {
void SMPcore::op_jst() {
dp = 0xffde - ((opcode >> 4) << 1);
- rd.l = op_read(dp++);
- rd.h = op_read(dp++);
+ rd.l = op_read(dp++, false);
+ rd.h = op_read(dp++, false);
op_io();
op_io();
op_io();
@@ -505,7 +505,7 @@ void SMPcore::op_sta_idpx() {
op_io();
dp.l = op_readdp(sp++);
dp.h = op_readdp(sp++);
- op_read(dp);
+ op_read(dp, false);
op_write(dp, regs.a);
}
@@ -515,7 +515,7 @@ void SMPcore::op_sta_idpy() {
dp.h = op_readdp(sp++);
op_io();
dp += regs.y;
- op_read(dp);
+ op_read(dp, false);
op_write(dp, regs.a);
}
diff --git a/snes/smp/debugger/debugger.cpp b/snes/smp/debugger/debugger.cpp
index 9546c118..894fdac9 100755
--- a/snes/smp/debugger/debugger.cpp
+++ b/snes/smp/debugger/debugger.cpp
@@ -18,8 +18,8 @@ void SMPDebugger::op_step() {
synchronize_cpu();
}
-uint8 SMPDebugger::op_read(uint16 addr) {
- uint8 data = SMP::op_read(addr);
+uint8 SMPDebugger::op_read(uint16 addr, bool exec) {
+ uint8 data = SMP::op_read(addr, exec);
usage[addr] |= UsageRead;
debugger.breakpoint_test(Debugger::Breakpoint::Source::APURAM, Debugger::Breakpoint::Mode::Read, addr, data);
return data;
diff --git a/snes/smp/debugger/debugger.hpp b/snes/smp/debugger/debugger.hpp
index d5d28e53..26bc7af9 100755
--- a/snes/smp/debugger/debugger.hpp
+++ b/snes/smp/debugger/debugger.hpp
@@ -14,7 +14,7 @@ public:
bool opcode_edge;
void op_step();
- uint8 op_read(uint16 addr);
+ uint8 op_read(uint16 addr, bool exec);
void op_write(uint16 addr, uint8 data);
SMPDebugger();
diff --git a/snes/smp/memory/memory.cpp b/snes/smp/memory/memory.cpp
index 391324c4..58c11915 100755
--- a/snes/smp/memory/memory.cpp
+++ b/snes/smp/memory/memory.cpp
@@ -19,61 +19,83 @@ void SMP::port_write(uint2 port, uint8 data) {
apuram[0xf4 + port] = data;
}
-alwaysinline uint8 SMP::op_busread(uint16 addr) {
+alwaysinline uint8 SMP::op_busread(uint16 addr, bool exec) {
unsigned result;
+ uint8 data;
switch(addr) {
case 0xf0: //TEST -- write-only register
- return 0x00;
+ data = 0x00;
+ break;
case 0xf1: //CONTROL -- write-only register
- return 0x00;
+ data = 0x00;
+ break;
case 0xf2: //DSPADDR
- return status.dsp_addr;
+ data = status.dsp_addr;
+ break;
case 0xf3: //DSPDATA
//0x80-0xff are read-only mirrors of 0x00-0x7f
- return dsp.read(status.dsp_addr & 0x7f);
+ data = dsp.read(status.dsp_addr & 0x7f);
+ break;
case 0xf4: //CPUIO0
case 0xf5: //CPUIO1
case 0xf6: //CPUIO2
case 0xf7: //CPUIO3
synchronize_cpu();
- return cpu.port_read(addr);
+ data = cpu.port_read(addr);
+ break;
case 0xf8: //RAM0
- return status.ram00f8;
+ data = status.ram00f8;
+ break;
case 0xf9: //RAM1
- return status.ram00f9;
+ data = status.ram00f9;
+ break;
case 0xfa: //T0TARGET
case 0xfb: //T1TARGET
case 0xfc: //T2TARGET -- write-only registers
- return 0x00;
+ data = 0x00;
+ break;
case 0xfd: //T0OUT -- 4-bit counter value
result = timer0.stage3_ticks;
timer0.stage3_ticks = 0;
- return result;
+ data = result;
+ break;
case 0xfe: //T1OUT -- 4-bit counter value
result = timer1.stage3_ticks;
timer1.stage3_ticks = 0;
- return result;
+ data = result;
+ break;
case 0xff: //T2OUT -- 4-bit counter value
result = timer2.stage3_ticks;
timer2.stage3_ticks = 0;
- return result;
+ data = result;
+ break;
+ default:
+ data = ram_read(addr);
+ break;
}
-
- return ram_read(addr);
+ uint8 flag = exec ? 0x04 : 0x01;
+ if(__builtin_expect(debugflags[addr] & flag, 0)) {
+ debug_read(16, addr, data, exec);
+ }
+ return data;
}
alwaysinline void SMP::op_buswrite(uint16 addr, uint8 data) {
+ if(__builtin_expect(debugflags[addr] & 0x2, 0)) {
+ debug_write(16, addr, data);
+ }
+
switch(addr) {
case 0xf0: //TEST
if(regs.p.p) break; //writes only valid when P flag is clear
@@ -180,9 +202,9 @@ void SMP::op_io() {
cycle_edge();
}
-uint8 SMP::op_read(uint16 addr) {
+uint8 SMP::op_read(uint16 addr, bool exec) {
add_clocks(12);
- uint8 r = op_busread(addr);
+ uint8 r = op_busread(addr, exec);
add_clocks(12);
cycle_edge();
return r;
diff --git a/snes/smp/memory/memory.hpp b/snes/smp/memory/memory.hpp
index 1a07445d..faa28daa 100755
--- a/snes/smp/memory/memory.hpp
+++ b/snes/smp/memory/memory.hpp
@@ -1,9 +1,9 @@
uint8 ram_read(uint16 addr);
void ram_write(uint16 addr, uint8 data);
-uint8 op_busread(uint16 addr);
+uint8 op_busread(uint16 addr, bool exec);
void op_buswrite(uint16 addr, uint8 data);
void op_io();
-debugvirtual uint8 op_read(uint16 addr);
+debugvirtual uint8 op_read(uint16 addr, bool exec);
debugvirtual void op_write(uint16 addr, uint8 data);
diff --git a/snes/smp/smp.hpp b/snes/smp/smp.hpp
index 6b387cba..6b6ae837 100755
--- a/snes/smp/smp.hpp
+++ b/snes/smp/smp.hpp
@@ -1,6 +1,10 @@
struct SMP : public Processor, public SMPcore {
static const uint8 iplrom[64];
uint8 apuram[64 * 1024];
+ uint8 debugflags[64 * 1024];
+
+ function<void (uint8, unsigned, uint8, bool)> debug_read;
+ function<void (uint8, unsigned, uint8)> debug_write;
enum : bool { Threaded = true };
alwaysinline void step(unsigned clocks);
diff --git a/snes/snes.hpp b/snes/snes.hpp
index 9589db9b..27632bff 100755
--- a/snes/snes.hpp
+++ b/snes/snes.hpp
@@ -1,6 +1,7 @@
#ifndef SNES_HPP
#define SNES_HPP
#define BSNES_SUPPORTS_ADV_BREAKPOINTS
+#define BSNES_SUPPORTS_ADV_BREAKPOINTS_PPU
#define BSNES_SUPPORTS_ALT_TIMINGS
namespace SNES {
--
2.15.0.rc1

View file

@ -1,53 +0,0 @@
From f1106d3dffd27dab526a703aa434512495fbacea Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Mon, 14 Apr 2014 21:21:36 +0300
Subject: [PATCH 19/27] SA1 trace hook support
---
snes/chip/sa1/sa1.cpp | 2 ++
snes/chip/sa1/sa1.hpp | 3 +++
snes/snes.hpp | 1 +
3 files changed, 6 insertions(+)
diff --git a/snes/chip/sa1/sa1.cpp b/snes/chip/sa1/sa1.cpp
index 30e00809..fdec362c 100755
--- a/snes/chip/sa1/sa1.cpp
+++ b/snes/chip/sa1/sa1.cpp
@@ -32,6 +32,8 @@ void SA1::enter() {
continue;
}
+ if(__builtin_expect(trace_enabled ? 1 : 0, 0))
+ step_event();
(this->*opcode_table[op_readpc()])();
}
}
diff --git a/snes/chip/sa1/sa1.hpp b/snes/chip/sa1/sa1.hpp
index 732b2a85..efd36376 100755
--- a/snes/chip/sa1/sa1.hpp
+++ b/snes/chip/sa1/sa1.hpp
@@ -15,6 +15,9 @@ public:
uint16 hcounter;
} status;
+ bool trace_enabled;
+ nall::function<void()> step_event;
+
static void Enter();
void enter();
void tick();
diff --git a/snes/snes.hpp b/snes/snes.hpp
index 27632bff..3bdca7e5 100755
--- a/snes/snes.hpp
+++ b/snes/snes.hpp
@@ -3,6 +3,7 @@
#define BSNES_SUPPORTS_ADV_BREAKPOINTS
#define BSNES_SUPPORTS_ADV_BREAKPOINTS_PPU
#define BSNES_SUPPORTS_ALT_TIMINGS
+#define BSNES_SUPPORTS_TRACE_SA1
namespace SNES {
namespace Info {
--
2.15.0.rc1

View file

@ -1,78 +0,0 @@
From cf662a12578778cb50c25d5275ce58deabd7eabe Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Wed, 30 Apr 2014 00:18:58 +0300
Subject: [PATCH 20/27] Fixes to SA1 open bus emulation
---
snes/chip/sa1/memory/memory.cpp | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/snes/chip/sa1/memory/memory.cpp b/snes/chip/sa1/memory/memory.cpp
index 9bb4ff20..614dfb0c 100755
--- a/snes/chip/sa1/memory/memory.cpp
+++ b/snes/chip/sa1/memory/memory.cpp
@@ -36,6 +36,7 @@ uint8 SA1::bus_read(unsigned addr) {
synchronize_cpu();
return bitmap_read(addr & 0x0fffff);
}
+ return regs.mdr;
}
void SA1::bus_write(unsigned addr, uint8 data) {
@@ -73,29 +74,31 @@ void SA1::bus_write(unsigned addr, uint8 data) {
//to avoid syncing the S-CPU and SA-1*; as both chips are able to access
//these ports.
uint8 SA1::vbr_read(unsigned addr) {
+ //Let's share the bus state with main SA1 bus (is this correct?)
if((addr & 0x408000) == 0x008000) { //$00-3f|80-bf:8000-ffff
- return mmc_read(addr);
+ return regs.mdr = mmc_read(addr);
}
if((addr & 0xc00000) == 0xc00000) { //$c0-ff:0000-ffff
- return mmc_read(addr);
+ return regs.mdr = mmc_read(addr);
}
if((addr & 0x40e000) == 0x006000) { //$00-3f|80-bf:6000-7fff
- return cartridge.ram.read(addr & (cartridge.ram.size() - 1));
+ return regs.mdr = cartridge.ram.read(addr & (cartridge.ram.size() - 1));
}
if((addr & 0xf00000) == 0x400000) { //$40-4f:0000-ffff
- return cartridge.ram.read(addr & (cartridge.ram.size() - 1));
+ return regs.mdr = cartridge.ram.read(addr & (cartridge.ram.size() - 1));
}
if((addr & 0x40f800) == 0x000000) { //$00-3f|80-bf:0000-07ff
- return iram.read(addr & 2047);
+ return regs.mdr = iram.read(addr & 2047);
}
if((addr & 0x40f800) == 0x003000) { //$00-3f|80-bf:3000-37ff
- return iram.read(addr & 0x2047);
+ return regs.mdr = iram.read(addr & 0x2047);
}
+ return regs.mdr;
}
//ROM, I-RAM and MMIO registers are accessed at ~10.74MHz (2 clock ticks)
@@ -110,13 +113,13 @@ void SA1::op_io() {
uint8 SA1::op_read(unsigned addr, bool exec) {
tick();
if(((addr & 0x40e000) == 0x006000) || ((addr & 0xd00000) == 0x400000)) tick();
- return bus_read(addr);
+ return regs.mdr = bus_read(addr);
}
void SA1::op_write(unsigned addr, uint8 data) {
tick();
if(((addr & 0x40e000) == 0x006000) || ((addr & 0xd00000) == 0x400000)) tick();
- bus_write(addr, data);
+ bus_write(addr, regs.mdr = data);
}
uint8 SA1::mmc_read(unsigned addr) {
--
2.15.0.rc1

View file

@ -1,25 +0,0 @@
From 63fc77b07d517c2f9a0fd6ca3fa94f30fb0f5ec2 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Sun, 15 Jun 2014 22:01:26 +0300
Subject: [PATCH 21/27] Call notify latch function on alternate timings mode
too
---
snes/cpu/timing/joypad.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/snes/cpu/timing/joypad.cpp b/snes/cpu/timing/joypad.cpp
index ae8e94f8..3fd4d23e 100755
--- a/snes/cpu/timing/joypad.cpp
+++ b/snes/cpu/timing/joypad.cpp
@@ -41,6 +41,7 @@ void CPU::step_auto_joypad_poll_NEW(bool polarity) {
} else {
if(status.auto_joypad_counter == 1) {
status.auto_joypad_active = true;
+ interface->notifyLatched();
input.port1->latch(1);
input.port2->latch(1);
}
--
2.15.0.rc1

View file

@ -1,288 +0,0 @@
From 5bc96b8aeea26729ef4399c2d8d5e562894616e1 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Tue, 20 Jan 2015 10:04:58 +0200
Subject: [PATCH 22/27] Support DMA tracing
---
snes/alt/ppu-compatibility/mmio/mmio.cpp | 18 +++++++
snes/alt/ppu-compatibility/ppu.cpp | 1 +
snes/alt/ppu-compatibility/ppu.hpp | 4 ++
snes/cpu/cpu.cpp | 1 +
snes/cpu/cpu.hpp | 1 +
snes/cpu/dma/dma.cpp | 84 ++++++++++++++++++++++++++++++++
snes/cpu/dma/dma.hpp | 5 ++
snes/ppu/mmio/mmio.cpp | 18 +++++++
snes/ppu/ppu.cpp | 1 +
snes/ppu/ppu.hpp | 3 ++
snes/snes.hpp | 1 +
11 files changed, 137 insertions(+)
diff --git a/snes/alt/ppu-compatibility/mmio/mmio.cpp b/snes/alt/ppu-compatibility/mmio/mmio.cpp
index aedb67c1..0a269cc0 100755
--- a/snes/alt/ppu-compatibility/mmio/mmio.cpp
+++ b/snes/alt/ppu-compatibility/mmio/mmio.cpp
@@ -1,5 +1,23 @@
#ifdef PPU_CPP
+size_t PPU::get_dma_oam_subaddr(char* buf)
+{
+ return sprintf(buf, "[%03x]", regs.oam_addr);
+}
+
+size_t PPU::get_dma_cgram_subaddr(char* buf)
+{
+ return sprintf(buf, "[%02x%c]", regs.cgram_addr >> 1, (regs.cgram_addr & 1) ?
+ 'H' : 'L');
+}
+
+size_t PPU::get_dma_vram_subaddr(char* buf)
+{
+ return sprintf(buf, "[%04x map%d inc %d on %s]", regs.vram_addr << 1,
+ regs.vram_mapping, 2 * regs.vram_incsize, regs.vram_incmode ? "high" :
+ "low");
+}
+
//INIDISP
void PPU::mmio_w2100(uint8 value) {
if(regs.display_disabled == true && cpu.vcounter() == (!overscan() ? 225 : 240)) {
diff --git a/snes/alt/ppu-compatibility/ppu.cpp b/snes/alt/ppu-compatibility/ppu.cpp
index 122b1430..ac886edc 100755
--- a/snes/alt/ppu-compatibility/ppu.cpp
+++ b/snes/alt/ppu-compatibility/ppu.cpp
@@ -1,4 +1,5 @@
#include <snes/snes.hpp>
+#include <cstdio>
#define PPU_CPP
namespace SNES {
diff --git a/snes/alt/ppu-compatibility/ppu.hpp b/snes/alt/ppu-compatibility/ppu.hpp
index 4adac4c4..b0eabf7c 100755
--- a/snes/alt/ppu-compatibility/ppu.hpp
+++ b/snes/alt/ppu-compatibility/ppu.hpp
@@ -14,6 +14,10 @@ public:
alwaysinline void step(unsigned clocks);
alwaysinline void synchronize_cpu();
+ size_t get_dma_oam_subaddr(char* buf);
+ size_t get_dma_cgram_subaddr(char* buf);
+ size_t get_dma_vram_subaddr(char* buf);
+
#include "memory/memory.hpp"
#include "mmio/mmio.hpp"
#include "render/render.hpp"
diff --git a/snes/cpu/cpu.cpp b/snes/cpu/cpu.cpp
index 39da6b16..ce112afa 100755
--- a/snes/cpu/cpu.cpp
+++ b/snes/cpu/cpu.cpp
@@ -1,4 +1,5 @@
#include <snes/snes.hpp>
+#include <cstdio>
#define CPU_CPP
namespace SNES {
diff --git a/snes/cpu/cpu.hpp b/snes/cpu/cpu.hpp
index 49445773..fd665b1f 100755
--- a/snes/cpu/cpu.hpp
+++ b/snes/cpu/cpu.hpp
@@ -26,6 +26,7 @@ struct CPU : public Processor, public CPUcore, public PPUcounter {
~CPU();
bool controller_flag;
+ function<void(const char*)> dma_trace_fn;
private:
#include "dma/dma.hpp"
#include "memory/memory.hpp"
diff --git a/snes/cpu/dma/dma.cpp b/snes/cpu/dma/dma.cpp
index 0a00bfea..8f7be263 100755
--- a/snes/cpu/dma/dma.cpp
+++ b/snes/cpu/dma/dma.cpp
@@ -144,6 +144,7 @@ void CPU::dma_run() {
for(unsigned i = 0; i < 8; i++) {
if(channel[i].dma_enabled == false) continue;
+ dma_trace_start(i);
unsigned index = 0;
do {
@@ -155,6 +156,7 @@ void CPU::dma_run() {
dma_write(false);
dma_edge();
+ dma_trace_end(i);
channel[i].dma_enabled = false;
}
@@ -202,6 +204,7 @@ void CPU::hdma_run() {
channel[i].dma_enabled = false; //HDMA run during DMA will stop DMA mid-transfer
if(channel[i].hdma_do_transfer) {
+ dma_trace_hdma(i);
static const unsigned transfer_length[8] = { 1, 2, 2, 4, 4, 4, 2, 4 };
unsigned length = transfer_length[channel[i].transfer_mode];
for(unsigned index = 0; index < length; index++) {
@@ -286,4 +289,85 @@ void CPU::dma_reset() {
pipe.data = 0;
}
+size_t CPU::dma_trace_subaddr(char* buf, uint8 b_addr)
+{
+ if(b_addr == 0x04 || b_addr == 0x38) {
+ return ppu.get_dma_oam_subaddr(buf);
+ }
+ if(b_addr == 0x22 || b_addr == 0x3B) {
+ return ppu.get_dma_cgram_subaddr(buf);
+ }
+ if(b_addr == 0x18 || b_addr == 0x19 || b_addr == 0x39 || b_addr == 0x3A) {
+ return ppu.get_dma_vram_subaddr(buf);
+ }
+ if(b_addr == 0x80) {
+ return sprintf(buf, "[%06x]", 0x7e0000 | status.wram_addr);
+ }
+ return 0;
+}
+
+void CPU::dma_trace_start(unsigned i)
+{
+ if(!dma_trace_fn) return;
+ char buf[512];
+ size_t ptr = 0;
+ unsigned bytes = channel[i].transfer_size;
+ if(!bytes) bytes = 0x10000;
+ ptr += sprintf(buf + ptr, "-- DMA%i %d(%x) bytes ", i, bytes, bytes);
+ if(channel[i].direction) {
+ //B->A
+ ptr += sprintf(buf + ptr, "%02x", channel[i].dest_addr);
+ ptr += dma_trace_subaddr(buf + ptr, channel[i].dest_addr);
+ ptr += sprintf(buf + ptr, "-> %02x%04x", channel[i].source_bank,
+ channel[i].source_addr);
+ } else {
+ //A->B
+ ptr += sprintf(buf + ptr, "%02x%04x -> %02x", channel[i].source_bank,
+ channel[i].source_addr, channel[i].dest_addr);
+ ptr += dma_trace_subaddr(buf + ptr, channel[i].dest_addr);
+ }
+ if(channel[i].fixed_transfer)
+ ptr += sprintf(buf + ptr, " fixed");
+ else if(channel[i].reverse_transfer)
+ ptr += sprintf(buf + ptr, " decrement");
+ else
+ ptr += sprintf(buf + ptr, " incrment");
+ ptr += sprintf(buf + ptr, " mode%d --", channel[i].transfer_mode);
+ dma_trace_fn(buf);
+}
+
+void CPU::dma_trace_end(unsigned i)
+{
+ if(!dma_trace_fn) return;
+ if(!channel[i].transfer_size) return; //No message for complete DMA.
+ char buf[512];
+ size_t ptr = 0;
+ sprintf(buf, "-- DMA%i aborted with %d(0x%x) bytes remaining --", i,
+ (int)channel[i].transfer_size, (unsigned)channel[i].transfer_size);
+ dma_trace_fn(buf);
+}
+
+void CPU::dma_trace_hdma(unsigned i)
+{
+ if(!dma_trace_fn) return;
+ char buf[512];
+ size_t ptr = 0;
+ unsigned addr = channel[i].indirect ?
+ (channel[i].indirect_bank << 16) | (channel[i].indirect_addr) :
+ (channel[i].source_bank << 16) | (channel[i].hdma_addr);
+ ptr += sprintf(buf + ptr, "-- HDMA%i %06x -> %02x", i, addr,
+ channel[i].dest_addr);
+ ptr += dma_trace_subaddr(buf + ptr, channel[i].dest_addr);
+ if(channel[i].indirect)
+ ptr += sprintf(buf + ptr, " indirect");
+ if(channel[i].fixed_transfer)
+ ptr += sprintf(buf + ptr, " fixed");
+ else if(channel[i].reverse_transfer)
+ ptr += sprintf(buf + ptr, " decrement");
+ else
+ ptr += sprintf(buf + ptr, " incrment");
+ ptr += sprintf(buf + ptr, " mode%d --", channel[i].transfer_mode);
+ dma_trace_fn(buf);
+}
+
#endif
diff --git a/snes/cpu/dma/dma.hpp b/snes/cpu/dma/dma.hpp
index 33755bde..8740bb3a 100755
--- a/snes/cpu/dma/dma.hpp
+++ b/snes/cpu/dma/dma.hpp
@@ -77,3 +77,8 @@ void hdma_init();
void dma_power();
void dma_reset();
+
+size_t dma_trace_subaddr(char* buf, uint8 b_addr);
+void dma_trace_start(unsigned i);
+void dma_trace_end(unsigned i);
+void dma_trace_hdma(unsigned i);
diff --git a/snes/ppu/mmio/mmio.cpp b/snes/ppu/mmio/mmio.cpp
index 302f74f8..4a4fb9ce 100755
--- a/snes/ppu/mmio/mmio.cpp
+++ b/snes/ppu/mmio/mmio.cpp
@@ -1,5 +1,23 @@
#ifdef PPU_CPP
+size_t PPU::get_dma_oam_subaddr(char* buf)
+{
+ return sprintf(buf, "[%03x]", regs.oam_addr);
+}
+
+size_t PPU::get_dma_cgram_subaddr(char* buf)
+{
+ return sprintf(buf, "[%02x%c]", regs.cgram_addr >> 1, (regs.cgram_addr & 1) ?
+ 'H' : 'L');
+}
+
+size_t PPU::get_dma_vram_subaddr(char* buf)
+{
+ return sprintf(buf, "[%04x map%d inc %d on %s]", regs.vram_addr << 1,
+ regs.vram_mapping, 2 * regs.vram_incsize, regs.vram_incmode ? "high" :
+ "low");
+}
+
bool PPU::interlace() const {
return display.interlace;
}
diff --git a/snes/ppu/ppu.cpp b/snes/ppu/ppu.cpp
index 13e231cf..58742098 100755
--- a/snes/ppu/ppu.cpp
+++ b/snes/ppu/ppu.cpp
@@ -1,4 +1,5 @@
#include <snes/snes.hpp>
+#include <cstdio>
#define PPU_CPP
namespace SNES {
diff --git a/snes/ppu/ppu.hpp b/snes/ppu/ppu.hpp
index fdba113c..0addb775 100755
--- a/snes/ppu/ppu.hpp
+++ b/snes/ppu/ppu.hpp
@@ -21,6 +21,9 @@ struct PPU : public Processor, public PPUcounter {
PPU();
~PPU();
+ size_t get_dma_oam_subaddr(char* buf);
+ size_t get_dma_cgram_subaddr(char* buf);
+ size_t get_dma_vram_subaddr(char* buf);
private:
uint32 *surface;
uint32 *output;
diff --git a/snes/snes.hpp b/snes/snes.hpp
index 3bdca7e5..7c48ebb3 100755
--- a/snes/snes.hpp
+++ b/snes/snes.hpp
@@ -4,6 +4,7 @@
#define BSNES_SUPPORTS_ADV_BREAKPOINTS_PPU
#define BSNES_SUPPORTS_ALT_TIMINGS
#define BSNES_SUPPORTS_TRACE_SA1
+#define BSNES_SUPPORTS_DMA_TRACE
namespace SNES {
namespace Info {
--
2.15.0.rc1

View file

@ -1,86 +0,0 @@
From 9682df9e33c366dfe047a99c8bcefc2c8ab29620 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Sat, 24 Jan 2015 16:46:18 +0200
Subject: [PATCH 23/27] Add autopoller and IRQ/NMI tracing
---
snes/cpu/cpu.cpp | 3 +++
snes/cpu/timing/joypad.cpp | 16 ++++++++++++++--
2 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/snes/cpu/cpu.cpp b/snes/cpu/cpu.cpp
index ce112afa..e11fc882 100755
--- a/snes/cpu/cpu.cpp
+++ b/snes/cpu/cpu.cpp
@@ -69,14 +69,17 @@ void CPU::enter() {
if(status.interrupt_pending) {
status.interrupt_pending = false;
if(status.nmi_pending) {
+ if(dma_trace_fn) dma_trace_fn("-- NMI occured --");
status.nmi_pending = false;
regs.vector = (regs.e == false ? 0xffea : 0xfffa);
op_irq();
} else if(status.irq_pending) {
+ if(dma_trace_fn) dma_trace_fn("-- IRQ occured --");
status.irq_pending = false;
regs.vector = (regs.e == false ? 0xffee : 0xfffe);
op_irq();
} else if(status.reset_pending) {
+ if(dma_trace_fn) dma_trace_fn("-- RESET occured --");
status.reset_pending = false;
add_clocks(186);
regs.pc.l = bus.read(0xfffc, false);
diff --git a/snes/cpu/timing/joypad.cpp b/snes/cpu/timing/joypad.cpp
index 3fd4d23e..afca7504 100755
--- a/snes/cpu/timing/joypad.cpp
+++ b/snes/cpu/timing/joypad.cpp
@@ -6,9 +6,9 @@ void CPU::step_auto_joypad_poll() {
//cache enable state at first iteration
if(status.auto_joypad_counter == 0) status.auto_joypad_latch = status.auto_joypad_poll;
status.auto_joypad_active = status.auto_joypad_counter <= 15;
-
if(status.auto_joypad_active && status.auto_joypad_latch) {
if(status.auto_joypad_counter == 0) {
+ if(dma_trace_fn) dma_trace_fn("-- Start automatic polling --");
interface->notifyLatched();
input.port1->latch(1);
input.port2->latch(1);
@@ -23,6 +23,12 @@ void CPU::step_auto_joypad_poll() {
status.joy2 = (status.joy2 << 1) | (bool)(port1 & 1);
status.joy3 = (status.joy3 << 1) | (bool)(port0 & 2);
status.joy4 = (status.joy4 << 1) | (bool)(port1 & 2);
+ if(status.auto_joypad_counter == 15) {
+ char buf[512];
+ sprintf(buf, "-- End automatic polling [%04x %04x %04x %04x] --",
+ status.joy1, status.joy2, status.joy3, status.joy4);
+ if(dma_trace_fn) dma_trace_fn(buf);
+ }
}
status.auto_joypad_counter++;
@@ -40,6 +46,7 @@ void CPU::step_auto_joypad_poll_NEW(bool polarity) {
status.auto_joypad_active = false;
} else {
if(status.auto_joypad_counter == 1) {
+ if(dma_trace_fn) dma_trace_fn("-- Start automatic polling --");
status.auto_joypad_active = true;
interface->notifyLatched();
input.port1->latch(1);
@@ -58,8 +65,13 @@ void CPU::step_auto_joypad_poll_NEW(bool polarity) {
status.joy3 = (status.joy3 << 1) | (bool)(port0 & 2);
status.joy4 = (status.joy4 << 1) | (bool)(port1 & 2);
}
- if(status.auto_joypad_counter == 34)
+ if(status.auto_joypad_counter == 34) {
status.auto_joypad_active = false;
+ char buf[512];
+ sprintf(buf, "-- End automatic polling [%04x %04x %04x %04x] --",
+ status.joy1, status.joy2, status.joy3, status.joy4);
+ if(dma_trace_fn) dma_trace_fn(buf);
+ }
}
status.auto_joypad_counter++;
}
--
2.15.0.rc1

View file

@ -1,50 +0,0 @@
From f2bbef8a4e12e05190a68dfe410cff3e4b1eb13f Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Sat, 8 Aug 2015 11:09:41 +0300
Subject: [PATCH 24/27] Build fixes for GCC 5.X
---
nall/bit.hpp | 21 +++++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/nall/bit.hpp b/nall/bit.hpp
index 67a35ad6..11d9d8de 100755
--- a/nall/bit.hpp
+++ b/nall/bit.hpp
@@ -8,18 +8,27 @@ namespace nall {
}
template<int bits> constexpr inline unsigned uclip(const unsigned x) {
- enum { m = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1) };
- return (x & m);
+ return x & ((1U << (bits - 1)) + ((1U << (bits - 1)) - 1));
+ }
+
+ template<int bits> constexpr inline signed sclamp_b() {
+ return 1U << (bits - 1);
+ }
+
+ template<int bits> constexpr inline signed sclamp_m() {
+ return (1U << (bits - 1)) - 1;
}
template<int bits> constexpr inline signed sclamp(const signed x) {
- enum { b = 1U << (bits - 1), m = (1U << (bits - 1)) - 1 };
- return (x > m) ? m : (x < -b) ? -b : x;
+ return (x > sclamp_m<bits>()) ? sclamp_m<bits>() : (x < -sclamp_b<bits>()) ? -sclamp_b<bits>() : x;
+ }
+
+ template<int bits> constexpr inline signed sclip_m() {
+ return (1U << (bits)) - 1;
}
template<int bits> constexpr inline signed sclip(const signed x) {
- enum { b = 1U << (bits - 1), m = (1U << bits) - 1 };
- return ((x & m) ^ b) - b;
+ return ((x & sclip_m<bits>()) ^ sclamp_b<bits>()) - sclamp_b<bits>();
}
namespace bit {
--
2.15.0.rc1

View file

@ -1,26 +0,0 @@
From d39571de650d49636778a73c66414aff372c08af Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Mon, 7 Sep 2015 20:48:14 +0300
Subject: [PATCH 25/27] Fix MSU-1 bug where write to MSU1BASE+4 is mirred to
MSUBASE+5
---
snes/chip/msu1/msu1.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/snes/chip/msu1/msu1.cpp b/snes/chip/msu1/msu1.cpp
index 71700e60..ec1cf46a 100755
--- a/snes/chip/msu1/msu1.cpp
+++ b/snes/chip/msu1/msu1.cpp
@@ -107,7 +107,7 @@ void MSU1::mmio_write(unsigned addr, uint8 data) {
if(datafile.open()) datafile.seek(mmio.data_offset);
mmio.data_busy = false;
break;
- case 4: mmio.audio_track = (mmio.audio_track & 0xff00) | (data << 0);
+ case 4: mmio.audio_track = (mmio.audio_track & 0xff00) | (data << 0); break;
case 5: mmio.audio_track = (mmio.audio_track & 0x00ff) | (data << 8);
if(audiofile.open()) audiofile.close();
if(audiofile.open(interface->path(Cartridge::Slot::Base, { "-", (unsigned)mmio.audio_track, ".pcm" }), file::mode::read)) {
--
2.15.0.rc1

View file

@ -1,25 +0,0 @@
From c0a2270cfd5f56e8a311b36011e1f15fac6c54ca Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilariliusvaara@welho.com>
Date: Tue, 9 Aug 2016 18:54:57 +0300
Subject: [PATCH 26/27] Add <vector> to avoid compile error due to missing
std::vector
---
snes/snes.hpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/snes/snes.hpp b/snes/snes.hpp
index 7c48ebb3..3a65e360 100755
--- a/snes/snes.hpp
+++ b/snes/snes.hpp
@@ -22,6 +22,7 @@ namespace SNES {
#include <libco/libco.h>
+#include <vector>
#include <nall/platform.hpp>
#include <nall/algorithm.hpp>
#include <nall/any.hpp>
--
2.15.0.rc1

View file

@ -1,377 +0,0 @@
From 4cfbbeadc3abe3e3911f7f59ce57b715edc76563 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilariliusvaara@welho.com>
Date: Wed, 25 Oct 2017 14:18:34 +0300
Subject: [PATCH 27/27] Bus fixes: Do not update MDR on read from CPU MMIO
space
Also, updates the controller read timings to be more accurate.
---
snes/config/config.cpp | 1 +
snes/config/config.hpp | 1 +
snes/cpu/cpu.cpp | 2 +
snes/cpu/memory/memory.cpp | 26 ++++++++-
snes/cpu/mmio/mmio.cpp | 14 +++--
snes/cpu/timing/joypad.cpp | 132 +++++++++++++++++++++++++++++++++++++++------
snes/cpu/timing/timing.cpp | 11 ++--
snes/cpu/timing/timing.hpp | 3 +-
snes/snes.hpp | 1 +
9 files changed, 166 insertions(+), 25 deletions(-)
diff --git a/snes/config/config.cpp b/snes/config/config.cpp
index 19831370..8dcfd7e8 100755
--- a/snes/config/config.cpp
+++ b/snes/config/config.cpp
@@ -15,6 +15,7 @@ Configuration::Configuration() {
cpu.pal_frequency = 21281370;
cpu.wram_init_value = 0x55;
cpu.alt_poll_timings = false;
+ cpu.bus_fixes = false;
smp.ntsc_frequency = 24607104; //32040.5 * 768
smp.pal_frequency = 24607104;
diff --git a/snes/config/config.hpp b/snes/config/config.hpp
index 68fe0bde..d8577e39 100755
--- a/snes/config/config.hpp
+++ b/snes/config/config.hpp
@@ -14,6 +14,7 @@ struct Configuration {
unsigned pal_frequency;
unsigned wram_init_value;
bool alt_poll_timings;
+ bool bus_fixes;
} cpu;
struct SMP {
diff --git a/snes/cpu/cpu.cpp b/snes/cpu/cpu.cpp
index e11fc882..5e8e3137 100755
--- a/snes/cpu/cpu.cpp
+++ b/snes/cpu/cpu.cpp
@@ -1,5 +1,7 @@
#include <snes/snes.hpp>
#include <cstdio>
+#include <iostream>
+#include <cassert>
#define CPU_CPP
namespace SNES {
diff --git a/snes/cpu/memory/memory.cpp b/snes/cpu/memory/memory.cpp
index 31f82c31..df439c22 100755
--- a/snes/cpu/memory/memory.cpp
+++ b/snes/cpu/memory/memory.cpp
@@ -14,10 +14,32 @@ uint8 CPU::op_read(uint32 addr, bool exec) {
status.clock_count = speed(addr);
dma_edge();
add_clocks(status.clock_count - 4);
- regs.mdr = bus.read(addr, exec);
+ //MDR presents the state held by parasitic capacitance of the external bus.
+ //This bus is not affected by reads from CPU-internal registers, only if
+ //some external device responds. SDD1 does hook some of these addresses, but
+ //passes read straight through, as expected (as the CPU probably won't
+ //monitor if external device responds, even if it broadcasts a read).
+ //
+ //We use 4000-43FF as CPU register range, and not 4000-437F it likely is
+ //for quickness of checking. This will only affect things if some device
+ //tries to map the 4380-43FF range (that device will still work correctly,
+ //but openbus in that range won't).
+ //
+ //This was discovered while investigating why one Super Metroid glitch
+ //worked on emulator but crashed on real console.
+ //
+ //a word fetch from 2f4017 AND 0xfffc results in 2f3c and a word fetch from
+ //2f4210 AND 0x7f7f results in 2f22. This also extends to long fetches
+ //by arguments. E.g. long argument fetch from 94420F with 2F already on
+ //the bus AND 0x7f7fff results in 2f222f.
+ //
+ //The reason for masking some bits in above explanation was to ignore some
+ //known bits in those registers (bits 7 of 4210 and 4211, bits 0&1 of 4017).
+ uint8_t tmp = bus.read(addr, exec);
+ if(!config.cpu.bus_fixes || (addr & 0x40FC00) != 0x004000) regs.mdr = tmp;
add_clocks(4);
alu_edge();
- return regs.mdr;
+ return tmp;
}
void CPU::op_write(uint32 addr, uint8 data) {
diff --git a/snes/cpu/mmio/mmio.cpp b/snes/cpu/mmio/mmio.cpp
index 30048c19..be2990a3 100755
--- a/snes/cpu/mmio/mmio.cpp
+++ b/snes/cpu/mmio/mmio.cpp
@@ -33,9 +33,17 @@ void CPU::mmio_w2183(uint8 data) {
//strobing $4016.d0 affects both controller port latches.
//$4017 bit 0 writes are ignored.
void CPU::mmio_w4016(uint8 data) {
- if(data&1) interface->notifyLatched();
- input.port1->latch(data & 1);
- input.port2->latch(data & 1);
+ //Only consider autoassert if both busfix and auto flags are set.
+ auto auto_asserted = (status.auto_joypad_counter & 384) == 384;
+ //Bit 6 of status.auto_joypad_counter follows "manual" latch.
+ auto oldstatus = auto_asserted || (status.auto_joypad_counter & 64) != 0;
+ status.auto_joypad_counter &= ~64;
+ status.auto_joypad_counter |= (data & 1) << 6;
+ auto newstatus = auto_asserted || (status.auto_joypad_counter & 64) != 0;
+ //If !oldstatus and newstatus, signal latch.
+ if(!oldstatus && newstatus) interface->notifyLatched();
+ input.port1->latch(newstatus);
+ input.port2->latch(newstatus);
}
//JOYSER0
diff --git a/snes/cpu/timing/joypad.cpp b/snes/cpu/timing/joypad.cpp
index afca7504..b60be020 100755
--- a/snes/cpu/timing/joypad.cpp
+++ b/snes/cpu/timing/joypad.cpp
@@ -3,11 +3,14 @@
//called every 256 clocks; see CPU::add_clocks()
void CPU::step_auto_joypad_poll() {
if(vcounter() >= (ppu.overscan() == false ? 225 : 240)) {
+ auto cycle = status.auto_joypad_counter & 63;
//cache enable state at first iteration
- if(status.auto_joypad_counter == 0) status.auto_joypad_latch = status.auto_joypad_poll;
- status.auto_joypad_active = status.auto_joypad_counter <= 15;
+ if(cycle == 0) status.auto_joypad_latch = status.auto_joypad_poll;
+ status.auto_joypad_active = cycle <= 15;
if(status.auto_joypad_active && status.auto_joypad_latch) {
- if(status.auto_joypad_counter == 0) {
+ if(cycle == 0) {
+ if(status.auto_joypad_counter & 128)
+ std::cerr << "step_auto_joypad_poll(): bus fixes set (counter=" << status.auto_joypad_counter << ")???" << std::endl;
if(dma_trace_fn) dma_trace_fn("-- Start automatic polling --");
interface->notifyLatched();
input.port1->latch(1);
@@ -23,7 +26,7 @@ void CPU::step_auto_joypad_poll() {
status.joy2 = (status.joy2 << 1) | (bool)(port1 & 1);
status.joy3 = (status.joy3 << 1) | (bool)(port0 & 2);
status.joy4 = (status.joy4 << 1) | (bool)(port1 & 2);
- if(status.auto_joypad_counter == 15) {
+ if(cycle == 15) {
char buf[512];
sprintf(buf, "-- End automatic polling [%04x %04x %04x %04x] --",
status.joy1, status.joy2, status.joy3, status.joy4);
@@ -31,32 +34,129 @@ void CPU::step_auto_joypad_poll() {
}
}
- status.auto_joypad_counter++;
+ //Only bits 0-5 are supposed to increment.
+ if(cycle < 60)
+ status.auto_joypad_counter++;
}
}
//called every 128 clocks; see CPU::add_clocks()
-void CPU::step_auto_joypad_poll_NEW(bool polarity) {
- if(status.auto_joypad_counter > 0 && status.auto_joypad_counter <= 34) {
+void CPU::step_auto_joypad_poll_NEW2(bool polarity) {
+ //Poll starts on multiple of 128 mod 256 clocks (polarity=false) on first
+ //vblank scanline. If autopoller is off, mark as done for the frame.
+ if(vcounter() >= (ppu.overscan() == false ? 225 : 240) && !polarity &&
+ (status.auto_joypad_counter & 63) == 0) {
+ if(!(status.auto_joypad_counter & 128))
+ std::cerr << "step_auto_joypad_poll_NEW2(): bus fixes clear???" << std::endl;
+ //Preserve high bits of autopoll counter.
+ auto x = status.auto_joypad_counter & ~63;
+ status.auto_joypad_counter = x | (status.auto_joypad_poll ? 1 : 36);
+ status.auto_joypad_latch = status.auto_joypad_poll;
+ }
+ //Abuse bit 6 of counter for "manual" poll flag. Bit 7 is supposed to be
+ //always set.
+ auto cycle = status.auto_joypad_counter & 63;
+ auto old_latchstate = (status.auto_joypad_counter & 320) != 0;
+ //If not enabled... This is not latched, as autopoll can be aborted.
+ if(!status.auto_joypad_poll && cycle > 0 && cycle < 36) {
+ if(dma_trace_fn) dma_trace_fn("-- Automatic polling ABORTED --");
+ status.auto_joypad_counter += (36 - cycle);
+ status.auto_joypad_active = false;
+ status.auto_joypad_latch = false;
+ //Release autopoll latch.
+ status.auto_joypad_counter &= ~256; //Autopoll clears latch.
+ auto new_latchstate = (status.auto_joypad_counter & 320) != 0;
+ if(old_latchstate && !new_latchstate) {
+ input.port1->latch(0);
+ input.port2->latch(0);
+ }
+ return;
+ }
+ //On cycle #1, latch is asserted (unless latch is already high, in this
+ //case the autopoller is supposed to force latch high too).
+ if(cycle == 1) {
+ if(dma_trace_fn) dma_trace_fn("-- Start automatic polling --");
+ //Assert autopoll latch.
+ status.auto_joypad_counter |= 256;
+ auto new_latchstate = (status.auto_joypad_counter & 320) != 0;
+ if(!old_latchstate && new_latchstate) {
+ interface->notifyLatched();
+ input.port1->latch(1);
+ input.port2->latch(1);
+ }
+ }
+ //On cycle #2, busy is asserted and controllers are cleared.
+ if(cycle == 2) {
+ status.joy1 = 0;
+ status.joy2 = 0;
+ status.joy3 = 0;
+ status.joy4 = 0;
+ status.auto_joypad_active = true;
+ }
+ //Then, on cycle #3, latch is deasserted, unless "manual" latch forces
+ //real latch high.
+ if(cycle == 3) {
+ //Release autopoll latch.
+ status.auto_joypad_counter &= ~256;
+ auto new_latchstate = (status.auto_joypad_counter & 320) != 0;
+ if(old_latchstate && !new_latchstate) {
+ input.port1->latch(0);
+ input.port2->latch(0);
+ }
+ }
+ //Then on cycles #4, #6, #8, ..., #34, a bit is shifted. Also, clock would
+ //go low, but we can not emulate that.
+ if(cycle >= 4 && cycle <= 34 && cycle % 2 == 0) {
+ uint2 port0 = input.port1->data();
+ uint2 port1 = input.port2->data();
+ status.joy1 = (status.joy1 << 1) | (bool)(port0 & 1);
+ status.joy2 = (status.joy2 << 1) | (bool)(port1 & 1);
+ status.joy3 = (status.joy3 << 1) | (bool)(port0 & 2);
+ status.joy4 = (status.joy4 << 1) | (bool)(port1 & 2);
+ }
+ //Then on cycles #5, #7, #9, ..., #35, clock drops high, But we can not
+ //emulate that.
+ //Then on cycle #35, busy flag is deasserted and poll is complete.
+ if(cycle == 35) {
+ status.auto_joypad_active = false;
+ char buf[512];
+ sprintf(buf, "-- End automatic polling [%04x %04x %04x %04x] --",
+ status.joy1, status.joy2, status.joy3, status.joy4);
+ if(dma_trace_fn) dma_trace_fn(buf);
+ }
+ //The entiere train is 35 cycles.
+ if(cycle > 0 && cycle < 36) {
+ status.auto_joypad_counter++;
+ }
+}
+
+
+//called every 128 clocks; see CPU::add_clocks()
+void CPU::step_auto_joypad_poll_NEW(bool polarity, bool new2) {
+ if(new2) return step_auto_joypad_poll_NEW2(polarity);
+ auto cycle = status.auto_joypad_counter & 63;
+ if(cycle > 0 && cycle <= 34) {
if(!status.auto_joypad_latch) {
//FIXME: Is this right, busy flag goes on even if not enabled???
- if(status.auto_joypad_counter == 1)
+ if(cycle == 1)
status.auto_joypad_active = true;
- if(status.auto_joypad_counter == 34)
+ if(cycle == 34)
status.auto_joypad_active = false;
} else {
- if(status.auto_joypad_counter == 1) {
+ if(cycle == 1) {
+ if(status.auto_joypad_counter & 128)
+ std::cerr << "step_auto_joypad_poll_NEW(): bus fixes set???" << std::endl;
if(dma_trace_fn) dma_trace_fn("-- Start automatic polling --");
status.auto_joypad_active = true;
interface->notifyLatched();
input.port1->latch(1);
input.port2->latch(1);
}
- if(status.auto_joypad_counter == 3) {
+ if(cycle == 3) {
input.port1->latch(0);
input.port2->latch(0);
}
- if((status.auto_joypad_counter & 1) != 0 && status.auto_joypad_counter != 1) {
+ if((cycle & 1) != 0 && cycle != 1) {
uint2 port0 = input.port1->data();
uint2 port1 = input.port2->data();
@@ -65,7 +165,7 @@ void CPU::step_auto_joypad_poll_NEW(bool polarity) {
status.joy3 = (status.joy3 << 1) | (bool)(port0 & 2);
status.joy4 = (status.joy4 << 1) | (bool)(port1 & 2);
}
- if(status.auto_joypad_counter == 34) {
+ if(cycle == 34) {
status.auto_joypad_active = false;
char buf[512];
sprintf(buf, "-- End automatic polling [%04x %04x %04x %04x] --",
@@ -75,9 +175,11 @@ void CPU::step_auto_joypad_poll_NEW(bool polarity) {
}
status.auto_joypad_counter++;
}
- if(vcounter() >= (ppu.overscan() == false ? 225 : 240) && status.auto_joypad_counter == 0 && !polarity) {
+ if(vcounter() >= (ppu.overscan() == false ? 225 : 240) && cycle == 0 && !polarity) {
+ //Preserve high bits of autopoller counter.
+ auto x = status.auto_joypad_counter & ~63;
status.auto_joypad_latch = status.auto_joypad_poll;
- status.auto_joypad_counter = 1;
+ status.auto_joypad_counter = x | 1;
}
}
diff --git a/snes/cpu/timing/timing.cpp b/snes/cpu/timing/timing.cpp
index d7cf24f3..ef81d891 100755
--- a/snes/cpu/timing/timing.cpp
+++ b/snes/cpu/timing/timing.cpp
@@ -17,12 +17,12 @@ void CPU::add_clocks(unsigned clocks) {
step(clocks);
- if(config.cpu.alt_poll_timings) {
+ if(config.cpu.alt_poll_timings || config.cpu.bus_fixes) {
bool opolarity = (status.auto_joypad_clock & 128);
status.auto_joypad_clock = (status.auto_joypad_clock + clocks) & 0xFF;
bool npolarity = (status.auto_joypad_clock & 128);
if(opolarity != npolarity)
- step_auto_joypad_poll_NEW(opolarity);
+ step_auto_joypad_poll_NEW(opolarity, config.cpu.bus_fixes);
} else {
status.auto_joypad_clock += clocks;
if(status.auto_joypad_clock >= 256) {
@@ -53,7 +53,8 @@ void CPU::scanline() {
status.hdma_init_position = (cpu_version == 1 ? 12 + 8 - dma_counter() : 12 + dma_counter());
status.hdma_init_triggered = false;
- status.auto_joypad_counter = 0;
+ //Only clear the low 6 bits (counter).
+ status.auto_joypad_counter &= ~63;
}
//DRAM refresh occurs once every scanline
@@ -200,7 +201,9 @@ void CPU::timing_reset() {
status.auto_joypad_active = false;
status.auto_joypad_latch = false;
- status.auto_joypad_counter = 0;
+ //Set bit 7 of joypad counter if bus fixes are active (for combined
+ //latch behavior).
+ status.auto_joypad_counter = config.cpu.bus_fixes ? 128 : 0;
status.auto_joypad_clock = 0;
}
diff --git a/snes/cpu/timing/timing.hpp b/snes/cpu/timing/timing.hpp
index bf15a727..8be2b830 100755
--- a/snes/cpu/timing/timing.hpp
+++ b/snes/cpu/timing/timing.hpp
@@ -22,4 +22,5 @@ alwaysinline bool irq_test();
//joypad.cpp
void step_auto_joypad_poll();
-void step_auto_joypad_poll_NEW(bool polarity);
+void step_auto_joypad_poll_NEW(bool polarity, bool new2);
+void step_auto_joypad_poll_NEW2(bool polarity);
diff --git a/snes/snes.hpp b/snes/snes.hpp
index 3a65e360..961842b3 100755
--- a/snes/snes.hpp
+++ b/snes/snes.hpp
@@ -3,6 +3,7 @@
#define BSNES_SUPPORTS_ADV_BREAKPOINTS
#define BSNES_SUPPORTS_ADV_BREAKPOINTS_PPU
#define BSNES_SUPPORTS_ALT_TIMINGS
+#define BSNES_SUPPORTS_BUS_FIXES
#define BSNES_SUPPORTS_TRACE_SA1
#define BSNES_SUPPORTS_DMA_TRACE
--
2.15.0.rc1

View file

@ -1,84 +0,0 @@
From 6c3da8eb6516d25e97b46d97fb0d3d24ca9ecfd0 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Wed, 9 Nov 2011 00:37:44 +0200
Subject: [PATCH 1/8] Don't use time() in emulating chips
Instead of using time() in chip emulation, create new interface method
currentTime(), defaulting to time(0). This way frontend can cleanly
override the current time bsnes is using.
---
snes/chip/bsx/satellaview/satellaview.cpp | 2 +-
snes/chip/spc7110/spc7110.cpp | 2 +-
snes/chip/srtc/srtc.cpp | 2 +-
snes/interface/interface.cpp | 5 +++++
snes/interface/interface.hpp | 1 +
5 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/snes/chip/bsx/satellaview/satellaview.cpp b/snes/chip/bsx/satellaview/satellaview.cpp
index 386fb62..3c98019 100755
--- a/snes/chip/bsx/satellaview/satellaview.cpp
+++ b/snes/chip/bsx/satellaview/satellaview.cpp
@@ -38,7 +38,7 @@ uint8 BSXSatellaview::mmio_read(unsigned addr) {
if(counter == 0) {
time_t rawtime;
- time(&rawtime);
+ rawtime = SNES::interface->currentTime();
tm *t = localtime(&rawtime);
regs.r2192_hour = t->tm_hour;
diff --git a/snes/chip/spc7110/spc7110.cpp b/snes/chip/spc7110/spc7110.cpp
index d2dc640..74a817a 100755
--- a/snes/chip/spc7110/spc7110.cpp
+++ b/snes/chip/spc7110/spc7110.cpp
@@ -101,7 +101,7 @@ void SPC7110::set_data_adjust(unsigned addr) { r4814 = addr; r4815 = addr >> 8;
void SPC7110::update_time(int offset) {
time_t rtc_time = (rtc[16] << 0) | (rtc[17] << 8) | (rtc[18] << 16) | (rtc[19] << 24);
- time_t current_time = time(0) - offset;
+ time_t current_time = SNES::interface->currentTime() - offset;
//sizeof(time_t) is platform-dependent; though rtc[] needs to be platform-agnostic.
//yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by
diff --git a/snes/chip/srtc/srtc.cpp b/snes/chip/srtc/srtc.cpp
index 1b2fd2a..78fc4c1 100755
--- a/snes/chip/srtc/srtc.cpp
+++ b/snes/chip/srtc/srtc.cpp
@@ -31,7 +31,7 @@ void SRTC::reset() {
void SRTC::update_time() {
time_t rtc_time = (rtc[16] << 0) | (rtc[17] << 8) | (rtc[18] << 16) | (rtc[19] << 24);
- time_t current_time = time(0);
+ time_t current_time = SNES::interface->currentTime();
//sizeof(time_t) is platform-dependent; though rtc[] needs to be platform-agnostic.
//yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by
diff --git a/snes/interface/interface.cpp b/snes/interface/interface.cpp
index a0e3a81..b3017c9 100755
--- a/snes/interface/interface.cpp
+++ b/snes/interface/interface.cpp
@@ -18,4 +18,9 @@ void Interface::message(const string &text) {
print(text, "\n");
}
+time_t Interface::currentTime()
+{
+ return time(0);
+}
+
}
diff --git a/snes/interface/interface.hpp b/snes/interface/interface.hpp
index f1a48c0..df975e8 100755
--- a/snes/interface/interface.hpp
+++ b/snes/interface/interface.hpp
@@ -5,6 +5,7 @@ struct Interface {
virtual string path(Cartridge::Slot slot, const string &hint) = 0;
virtual void message(const string &text);
+ virtual time_t currentTime();
};
extern Interface *interface;
--
1.8.4.4

View file

@ -1,346 +0,0 @@
From c87e7d9288a91db3b32b5ba4b2b74e52c0d3c11d Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Wed, 9 Nov 2011 01:52:08 +0200
Subject: [PATCH 2/8] Save controller state when savestating
When savestating, save the controller state and restore it upon loadstate.
Prevents libsnes from mixing up buttons.
---
snes/controller/controller.cpp | 8 +++++++
snes/controller/controller.hpp | 2 ++
snes/controller/gamepad/gamepad.cpp | 13 +++++++++++
snes/controller/gamepad/gamepad.hpp | 2 +-
snes/controller/justifier/justifier.cpp | 36 +++++++++++++++++++++++++++++++
snes/controller/justifier/justifier.hpp | 1 +
snes/controller/mouse/mouse.cpp | 13 +++++++++++
snes/controller/mouse/mouse.hpp | 2 +-
snes/controller/multitap/multitap.cpp | 16 ++++++++++++++
snes/controller/multitap/multitap.hpp | 2 +-
snes/controller/superscope/superscope.cpp | 31 ++++++++++++++++++++++++++
snes/controller/superscope/superscope.hpp | 1 +
snes/system/input.cpp | 16 ++++++++++++++
snes/system/input.hpp | 1 +
snes/system/serialization.cpp | 1 +
15 files changed, 142 insertions(+), 3 deletions(-)
diff --git a/snes/controller/controller.cpp b/snes/controller/controller.cpp
index 9091b21..f254bed 100755
--- a/snes/controller/controller.cpp
+++ b/snes/controller/controller.cpp
@@ -46,8 +46,16 @@ void Controller::iobit(bool data) {
}
}
+void Controller::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save a zero block.
+ unsigned char blockzeroes[SaveSize] = {0};
+ s.array(blockzeroes, SaveSize);
+}
+
Controller::Controller(bool port) : port(port) {
if(!thread) create(Controller::Enter, 1);
}
+
}
diff --git a/snes/controller/controller.hpp b/snes/controller/controller.hpp
index 7332712..827b2eb 100755
--- a/snes/controller/controller.hpp
+++ b/snes/controller/controller.hpp
@@ -13,12 +13,14 @@
struct Controller : Processor {
enum : bool { Port1 = 0, Port2 = 1 };
+ enum { SaveSize = 16 };
const bool port;
static void Enter();
virtual void enter();
void step(unsigned clocks);
void synchronize_cpu();
+ virtual void serialize(serializer& s);
bool iobit();
void iobit(bool data);
diff --git a/snes/controller/gamepad/gamepad.cpp b/snes/controller/gamepad/gamepad.cpp
index 594020d..4fa1c99 100755
--- a/snes/controller/gamepad/gamepad.cpp
+++ b/snes/controller/gamepad/gamepad.cpp
@@ -13,6 +13,19 @@ void Gamepad::latch(bool data) {
counter = 0;
}
+void Gamepad::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save block.
+ unsigned char block[Controller::SaveSize] = {0};
+ block[0] = latched ? 1 : 0;
+ block[1] = counter;
+ s.array(block, Controller::SaveSize);
+ if(s.mode() == nall::serializer::Load) {
+ latched = (block[0] != 0);
+ counter = block[1];
+ }
+}
+
Gamepad::Gamepad(bool port) : Controller(port) {
latched = 0;
counter = 0;
diff --git a/snes/controller/gamepad/gamepad.hpp b/snes/controller/gamepad/gamepad.hpp
index c5ca69c..a2392d1 100755
--- a/snes/controller/gamepad/gamepad.hpp
+++ b/snes/controller/gamepad/gamepad.hpp
@@ -2,7 +2,7 @@ struct Gamepad : Controller {
uint2 data();
void latch(bool data);
Gamepad(bool port);
-
+ void serialize(serializer& s);
private:
bool latched;
unsigned counter;
diff --git a/snes/controller/justifier/justifier.cpp b/snes/controller/justifier/justifier.cpp
index 6207916..ad13a9b 100755
--- a/snes/controller/justifier/justifier.cpp
+++ b/snes/controller/justifier/justifier.cpp
@@ -100,6 +100,42 @@ void Justifier::latch(bool data) {
if(latched == 0) active = !active; //toggle between both controllers, even when unchained
}
+void Justifier::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save block.
+ unsigned char block[Controller::SaveSize] = {0};
+ block[0] = latched ? 1 : 0;
+ block[1] = counter;
+ block[2] = active ? 1 : 0;
+ block[3] = player1.trigger ? 1 : 0;
+ block[4] = player2.trigger ? 1 : 0;
+ block[5] = player1.start ? 1 : 0;
+ block[6] = player2.start ? 1 : 0;
+ block[7] = (unsigned short)player1.x >> 8;
+ block[8] = (unsigned short)player1.x;
+ block[9] = (unsigned short)player2.x >> 8;
+ block[10] = (unsigned short)player2.x;
+ block[11] = (unsigned short)player1.y >> 8;
+ block[12] = (unsigned short)player1.y;
+ block[13] = (unsigned short)player2.y >> 8;
+ block[14] = (unsigned short)player2.y;
+ s.array(block, Controller::SaveSize);
+ if(s.mode() == nall::serializer::Load) {
+ latched = (block[0] != 0);
+ counter = block[1];
+ active = (block[2] != 0);
+ player1.trigger = (block[3] != 0);
+ player2.trigger = (block[4] != 0);
+ player1.start = (block[5] != 0);
+ player2.start = (block[6] != 0);
+ player1.x = (short)(((unsigned short)block[7] << 8) | (unsigned short)block[8]);
+ player2.x = (short)(((unsigned short)block[9] << 8) | (unsigned short)block[10]);
+ player1.y = (short)(((unsigned short)block[11] << 8) | (unsigned short)block[12]);
+ player2.y = (short)(((unsigned short)block[13] << 8) | (unsigned short)block[14]);
+ }
+}
+
+
Justifier::Justifier(bool port, bool chained) : Controller(port), chained(chained) {
create(Controller::Enter, 21477272);
latched = 0;
diff --git a/snes/controller/justifier/justifier.hpp b/snes/controller/justifier/justifier.hpp
index f927acf..6b7bba0 100755
--- a/snes/controller/justifier/justifier.hpp
+++ b/snes/controller/justifier/justifier.hpp
@@ -2,6 +2,7 @@ struct Justifier : Controller {
void enter();
uint2 data();
void latch(bool data);
+ void serialize(serializer& s);
Justifier(bool port, bool chained);
//private:
diff --git a/snes/controller/mouse/mouse.cpp b/snes/controller/mouse/mouse.cpp
index c9f5d16..6b26fae 100755
--- a/snes/controller/mouse/mouse.cpp
+++ b/snes/controller/mouse/mouse.cpp
@@ -61,6 +61,19 @@ void Mouse::latch(bool data) {
counter = 0;
}
+void Mouse::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save block.
+ unsigned char block[Controller::SaveSize] = {0};
+ block[0] = latched ? 1 : 0;
+ block[1] = counter;
+ s.array(block, Controller::SaveSize);
+ if(s.mode() == nall::serializer::Load) {
+ latched = (block[0] != 0);
+ counter = block[1];
+ }
+}
+
Mouse::Mouse(bool port) : Controller(port) {
latched = 0;
counter = 0;
diff --git a/snes/controller/mouse/mouse.hpp b/snes/controller/mouse/mouse.hpp
index 95e24b6..b66ea51 100755
--- a/snes/controller/mouse/mouse.hpp
+++ b/snes/controller/mouse/mouse.hpp
@@ -2,7 +2,7 @@ struct Mouse : Controller {
uint2 data();
void latch(bool data);
Mouse(bool port);
-
+ void serialize(serializer& s);
private:
bool latched;
unsigned counter;
diff --git a/snes/controller/multitap/multitap.cpp b/snes/controller/multitap/multitap.cpp
index 3a6eb72..146c41d 100755
--- a/snes/controller/multitap/multitap.cpp
+++ b/snes/controller/multitap/multitap.cpp
@@ -30,6 +30,22 @@ void Multitap::latch(bool data) {
counter2 = 0;
}
+void Multitap::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save block.
+ unsigned char block[Controller::SaveSize] = {0};
+ block[0] = latched ? 1 : 0;
+ block[1] = counter1;
+ block[2] = counter2;
+ s.array(block, Controller::SaveSize);
+ if(s.mode() == nall::serializer::Load) {
+ latched = (block[0] != 0);
+ counter1 = block[1];
+ counter2 = block[2];
+ }
+}
+
+
Multitap::Multitap(bool port) : Controller(port) {
latched = 0;
counter1 = 0;
diff --git a/snes/controller/multitap/multitap.hpp b/snes/controller/multitap/multitap.hpp
index 0540af7..e6324ac 100755
--- a/snes/controller/multitap/multitap.hpp
+++ b/snes/controller/multitap/multitap.hpp
@@ -2,7 +2,7 @@ struct Multitap : Controller {
uint2 data();
void latch(bool data);
Multitap(bool port);
-
+ void serialize(serializer& s);
private:
bool latched;
unsigned counter1;
diff --git a/snes/controller/superscope/superscope.cpp b/snes/controller/superscope/superscope.cpp
index 12068f0..1a1dfbf 100755
--- a/snes/controller/superscope/superscope.cpp
+++ b/snes/controller/superscope/superscope.cpp
@@ -100,6 +100,37 @@ void SuperScope::latch(bool data) {
counter = 0;
}
+void SuperScope::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save block.
+ unsigned char block[Controller::SaveSize] = {0};
+ block[0] = latched ? 1 : 0;
+ block[1] = counter;
+ block[2] = trigger ? 1 : 0;
+ block[3] = cursor ? 1 : 0;
+ block[4] = turbo ? 1 : 0;
+ block[5] = pause ? 1 : 0;
+ block[6] = offscreen ? 1 : 0;
+ block[7] = (unsigned short)x >> 8;
+ block[8] = (unsigned short)x;
+ block[9] = (unsigned short)y >> 8;
+ block[10] = (unsigned short)y;
+
+ s.array(block, Controller::SaveSize);
+ if(s.mode() == nall::serializer::Load) {
+ latched = (block[0] != 0);
+ counter = block[1];
+ trigger = (block[2] != 0);
+ cursor = (block[3] != 0);
+ turbo = (block[4] != 0);
+ pause = (block[5] != 0);
+ offscreen = (block[6] != 0);
+ x = (short)(((unsigned short)block[7] << 8) | (unsigned short)block[8]);
+ y = (short)(((unsigned short)block[9] << 8) | (unsigned short)block[10]);
+ }
+}
+
+
SuperScope::SuperScope(bool port) : Controller(port) {
create(Controller::Enter, 21477272);
latched = 0;
diff --git a/snes/controller/superscope/superscope.hpp b/snes/controller/superscope/superscope.hpp
index a7a90b7..93509d7 100755
--- a/snes/controller/superscope/superscope.hpp
+++ b/snes/controller/superscope/superscope.hpp
@@ -2,6 +2,7 @@ struct SuperScope : Controller {
void enter();
uint2 data();
void latch(bool data);
+ void serialize(serializer& s);
SuperScope(bool port);
//private:
diff --git a/snes/system/input.cpp b/snes/system/input.cpp
index 9050310..ec5559d 100755
--- a/snes/system/input.cpp
+++ b/snes/system/input.cpp
@@ -26,6 +26,22 @@ void Input::connect(bool port, Input::Device id) {
}
}
+void Input::serialize(serializer &s)
+{
+ int p1, p2;
+ p1 = (int)config.controller_port1;
+ p2 = (int)config.controller_port2;
+ s.integer(p1);
+ s.integer(p2);
+ if(s.mode() == nall::serializer::Load) {
+ connect(Controller::Port1, (Device)p1);
+ connect(Controller::Port2, (Device)p2);
+ }
+ port1->serialize(s);
+ port2->serialize(s);
+}
+
+
Input::Input() : port1(nullptr), port2(nullptr) {
connect(Controller::Port1, Input::Device::Joypad);
connect(Controller::Port2, Input::Device::Joypad);
diff --git a/snes/system/input.hpp b/snes/system/input.hpp
index 13ef46e..6832e82 100755
--- a/snes/system/input.hpp
+++ b/snes/system/input.hpp
@@ -31,6 +31,7 @@ struct Input {
Controller *port1;
Controller *port2;
+ void serialize(serializer &s);
void connect(bool port, Input::Device id);
Input();
~Input();
diff --git a/snes/system/serialization.cpp b/snes/system/serialization.cpp
index 9f5273d..005e731 100755
--- a/snes/system/serialization.cpp
+++ b/snes/system/serialization.cpp
@@ -56,6 +56,7 @@ void System::serialize_all(serializer &s) {
smp.serialize(s);
ppu.serialize(s);
dsp.serialize(s);
+ input.serialize(s);
if(cartridge.mode() == Cartridge::Mode::SufamiTurbo) sufamiturbo.serialize(s);
#if defined(GAMEBOY)
--
1.8.4.4

View file

@ -1,53 +0,0 @@
From a62794b0bfa1d2bfc8907a1e4d4e5aa6fe3ee426 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Fri, 11 Nov 2011 19:49:46 +0200
Subject: [PATCH 3/8] Allow frontend to control random number seed
---
snes/interface/interface.cpp | 5 +++++
snes/interface/interface.hpp | 1 +
snes/system/system.cpp | 2 +-
3 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/snes/interface/interface.cpp b/snes/interface/interface.cpp
index b3017c9..0a21a13 100755
--- a/snes/interface/interface.cpp
+++ b/snes/interface/interface.cpp
@@ -23,4 +23,9 @@ time_t Interface::currentTime()
return time(0);
}
+time_t Interface::randomSeed()
+{
+ return time(0);
+}
+
}
diff --git a/snes/interface/interface.hpp b/snes/interface/interface.hpp
index df975e8..30ee7fd 100755
--- a/snes/interface/interface.hpp
+++ b/snes/interface/interface.hpp
@@ -6,6 +6,7 @@ struct Interface {
virtual string path(Cartridge::Slot slot, const string &hint) = 0;
virtual void message(const string &text);
virtual time_t currentTime();
+ virtual time_t randomSeed();
};
extern Interface *interface;
diff --git a/snes/system/system.cpp b/snes/system/system.cpp
index 284e389..99901ff 100755
--- a/snes/system/system.cpp
+++ b/snes/system/system.cpp
@@ -151,7 +151,7 @@ void System::unload() {
}
void System::power() {
- random.seed((unsigned)time(0));
+ random.seed((unsigned)interface->randomSeed());
region = config.region;
expansion = config.expansion_port;
--
1.8.4.4

View file

@ -1,63 +0,0 @@
From 33ecd422954b7e15d9e83b7035b07ffb52f4e1e8 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Wed, 7 Mar 2012 16:57:18 +0200
Subject: [PATCH 4/8] Fix mouse polling
Don't poll for mouse motion excessive number of times (no need to poll it for
each bit!)
---
snes/controller/mouse/mouse.cpp | 14 ++++++++++++--
snes/controller/mouse/mouse.hpp | 2 ++
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/snes/controller/mouse/mouse.cpp b/snes/controller/mouse/mouse.cpp
index 6b26fae..1a066b9 100755
--- a/snes/controller/mouse/mouse.cpp
+++ b/snes/controller/mouse/mouse.cpp
@@ -3,9 +3,13 @@
uint2 Mouse::data() {
if(counter >= 32) return 1;
- int position_x = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::X); //-n = left, 0 = center, +n = right
- int position_y = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Y); //-n = up, 0 = center, +n = down
+ if(counter == 0) {
+ _position_x = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::X); //-n = left, 0 = center, +n = right
+ _position_y = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Y); //-n = up, 0 = center, +n = down
+ }
+ int position_x = _position_x;
+ int position_y = _position_y;
bool direction_x = position_x < 0; //0 = right, 1 = left
bool direction_y = position_y < 0; //0 = down, 1 = up
@@ -67,10 +71,16 @@ void Mouse::serialize(serializer& s) {
unsigned char block[Controller::SaveSize] = {0};
block[0] = latched ? 1 : 0;
block[1] = counter;
+ block[2] = (unsigned short)_position_x >> 8;
+ block[3] = (unsigned short)_position_x;
+ block[4] = (unsigned short)_position_y >> 8;
+ block[5] = (unsigned short)_position_y;
s.array(block, Controller::SaveSize);
if(s.mode() == nall::serializer::Load) {
latched = (block[0] != 0);
counter = block[1];
+ _position_x = (short)(((unsigned short)block[2] << 8) | (unsigned short)block[3]);
+ _position_y = (short)(((unsigned short)block[4] << 8) | (unsigned short)block[5]);
}
}
diff --git a/snes/controller/mouse/mouse.hpp b/snes/controller/mouse/mouse.hpp
index b66ea51..b07c8ab 100755
--- a/snes/controller/mouse/mouse.hpp
+++ b/snes/controller/mouse/mouse.hpp
@@ -6,4 +6,6 @@ struct Mouse : Controller {
private:
bool latched;
unsigned counter;
+ int _position_x;
+ int _position_y;
};
--
1.8.4.4

View file

@ -1,69 +0,0 @@
From 52a1a595f4473b4de0cdedcb018aef68108a2c73 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Mon, 24 Sep 2012 21:46:09 +0300
Subject: [PATCH 5/8] Add needed support for detecting true polls as opposed to
just autopolling
---
snes/cpu/cpu.hpp | 1 +
snes/cpu/mmio/mmio.cpp | 18 ++++++++++--------
2 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/snes/cpu/cpu.hpp b/snes/cpu/cpu.hpp
index 5eb963e..f6466cc 100755
--- a/snes/cpu/cpu.hpp
+++ b/snes/cpu/cpu.hpp
@@ -25,6 +25,7 @@ struct CPU : public Processor, public CPUcore, public PPUcounter {
CPU();
~CPU();
+ bool controller_flag;
privileged:
#include "dma/dma.hpp"
#include "memory/memory.hpp"
diff --git a/snes/cpu/mmio/mmio.cpp b/snes/cpu/mmio/mmio.cpp
index 8b6aaa6..c5ee930 100755
--- a/snes/cpu/mmio/mmio.cpp
+++ b/snes/cpu/mmio/mmio.cpp
@@ -42,6 +42,7 @@ void CPU::mmio_w4016(uint8 data) {
//1-0 = Joypad serial data
uint8 CPU::mmio_r4016() {
uint8 r = regs.mdr & 0xfc;
+ controller_flag = true;
r |= input.port1->data();
return r;
}
@@ -52,6 +53,7 @@ uint8 CPU::mmio_r4016() {
//1-0 = Joypad serial data
uint8 CPU::mmio_r4017() {
uint8 r = (regs.mdr & 0xe0) | 0x1c;
+ controller_flag = true;
r |= input.port2->data();
return r;
}
@@ -204,14 +206,14 @@ uint8 CPU::mmio_r4217() {
return status.rdmpy >> 8;
}
-uint8 CPU::mmio_r4218() { return status.joy1 >> 0; } //JOY1L
-uint8 CPU::mmio_r4219() { return status.joy1 >> 8; } //JOY1H
-uint8 CPU::mmio_r421a() { return status.joy2 >> 0; } //JOY2L
-uint8 CPU::mmio_r421b() { return status.joy2 >> 8; } //JOY2H
-uint8 CPU::mmio_r421c() { return status.joy3 >> 0; } //JOY3L
-uint8 CPU::mmio_r421d() { return status.joy3 >> 8; } //JOY3H
-uint8 CPU::mmio_r421e() { return status.joy4 >> 0; } //JOY4L
-uint8 CPU::mmio_r421f() { return status.joy4 >> 8; } //JOY4H
+uint8 CPU::mmio_r4218() { controller_flag = true; return status.joy1 >> 0; } //JOY1L
+uint8 CPU::mmio_r4219() { controller_flag = true; return status.joy1 >> 8; } //JOY1H
+uint8 CPU::mmio_r421a() { controller_flag = true; return status.joy2 >> 0; } //JOY2L
+uint8 CPU::mmio_r421b() { controller_flag = true; return status.joy2 >> 8; } //JOY2H
+uint8 CPU::mmio_r421c() { controller_flag = true; return status.joy3 >> 0; } //JOY3L
+uint8 CPU::mmio_r421d() { controller_flag = true; return status.joy3 >> 8; } //JOY3H
+uint8 CPU::mmio_r421e() { controller_flag = true; return status.joy4 >> 0; } //JOY4L
+uint8 CPU::mmio_r421f() { controller_flag = true; return status.joy4 >> 8; } //JOY4H
//DMAPx
uint8 CPU::mmio_r43x0(uint8 i) {
--
1.8.4.4

View file

@ -1,38 +0,0 @@
From 74b67f36961839fcbc1caa23930151bd9b3e9d7e Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Sun, 14 Oct 2012 23:29:40 +0300
Subject: [PATCH 6/8] Fix compiling on GCC 4.7
---
nall/string.hpp | 2 +-
ui-libsnes/libsnes.cpp | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/nall/string.hpp b/nall/string.hpp
index 996cd68..4747e4c 100755
--- a/nall/string.hpp
+++ b/nall/string.hpp
@@ -25,8 +25,8 @@
#include <nall/string/base.hpp>
#include <nall/string/bml.hpp>
#include <nall/string/bsv.hpp>
-#include <nall/string/core.hpp>
#include <nall/string/cast.hpp>
+#include <nall/string/core.hpp>
#include <nall/string/compare.hpp>
#include <nall/string/convert.hpp>
#include <nall/string/cstring.hpp>
diff --git a/ui-libsnes/libsnes.cpp b/ui-libsnes/libsnes.cpp
index 3b2be7a..ca90762 100755
--- a/ui-libsnes/libsnes.cpp
+++ b/ui-libsnes/libsnes.cpp
@@ -1,5 +1,6 @@
#include "libsnes.hpp"
#include <snes/snes.hpp>
+#include <gameboy/gameboy.hpp>
#include <nall/snes/cartridge.hpp>
#include <nall/gameboy/cartridge.hpp>
--
1.8.4.4

View file

@ -1,65 +0,0 @@
From 5dc532e67245f1e83504be4a21fef1ab15b08af2 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Sun, 27 Oct 2013 10:52:45 +0200
Subject: [PATCH 7/8] Support notifying latches
---
snes/cpu/mmio/mmio.cpp | 1 +
snes/cpu/timing/joypad.cpp | 1 +
snes/interface/interface.cpp | 5 +++++
snes/interface/interface.hpp | 1 +
4 files changed, 8 insertions(+)
diff --git a/snes/cpu/mmio/mmio.cpp b/snes/cpu/mmio/mmio.cpp
index c5ee930..b7afff0 100755
--- a/snes/cpu/mmio/mmio.cpp
+++ b/snes/cpu/mmio/mmio.cpp
@@ -33,6 +33,7 @@ void CPU::mmio_w2183(uint8 data) {
//strobing $4016.d0 affects both controller port latches.
//$4017 bit 0 writes are ignored.
void CPU::mmio_w4016(uint8 data) {
+ if(data&1) interface->notifyLatched();
input.port1->latch(data & 1);
input.port2->latch(data & 1);
}
diff --git a/snes/cpu/timing/joypad.cpp b/snes/cpu/timing/joypad.cpp
index 179df27..6a98de0 100755
--- a/snes/cpu/timing/joypad.cpp
+++ b/snes/cpu/timing/joypad.cpp
@@ -9,6 +9,7 @@ void CPU::step_auto_joypad_poll() {
if(status.auto_joypad_active && status.auto_joypad_latch) {
if(status.auto_joypad_counter == 0) {
+ interface->notifyLatched();
input.port1->latch(1);
input.port2->latch(1);
input.port1->latch(0);
diff --git a/snes/interface/interface.cpp b/snes/interface/interface.cpp
index 0a21a13..6685556 100755
--- a/snes/interface/interface.cpp
+++ b/snes/interface/interface.cpp
@@ -28,4 +28,9 @@ time_t Interface::randomSeed()
return time(0);
}
+void Interface::notifyLatched()
+{
+ //Nothing.
+}
+
}
diff --git a/snes/interface/interface.hpp b/snes/interface/interface.hpp
index 30ee7fd..203f7b0 100755
--- a/snes/interface/interface.hpp
+++ b/snes/interface/interface.hpp
@@ -7,6 +7,7 @@ struct Interface {
virtual void message(const string &text);
virtual time_t currentTime();
virtual time_t randomSeed();
+ virtual void notifyLatched();
};
extern Interface *interface;
--
1.8.4.4

View file

@ -1,22 +0,0 @@
From ce0634fe5a8dea973ca9c357ec788740fbcfcf09 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Sat, 30 Nov 2013 10:28:05 +0200
Subject: [PATCH 8/8] Support auto-detecting bsnes version
---
bsnes.mk | 3 +++
1 file changed, 3 insertions(+)
create mode 100644 bsnes.mk
diff --git a/bsnes.mk b/bsnes.mk
new file mode 100644
index 0000000..2248b71
--- /dev/null
+++ b/bsnes.mk
@@ -0,0 +1,3 @@
+BSNES_SUPPORTS_DEBUGGER=
+LIBSNES_DIR=ui-libsnes
+BSNES_VERSION=086
--
1.8.4.4

View file

@ -1,84 +0,0 @@
From a8018b3c90314bd0c112842fe81b27e978b891eb Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Wed, 9 Nov 2011 00:37:44 +0200
Subject: [PATCH 1/8] Don't use time() in emulating chips
Instead of using time() in chip emulation, create new interface method
currentTime(), defaulting to time(0). This way frontend can cleanly
override the current time bsnes is using.
---
snes/chip/bsx/satellaview/satellaview.cpp | 2 +-
snes/chip/spc7110/spc7110.cpp | 2 +-
snes/chip/srtc/srtc.cpp | 2 +-
snes/interface/interface.cpp | 5 +++++
snes/interface/interface.hpp | 1 +
5 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/snes/chip/bsx/satellaview/satellaview.cpp b/snes/chip/bsx/satellaview/satellaview.cpp
index 386fb62..3c98019 100755
--- a/snes/chip/bsx/satellaview/satellaview.cpp
+++ b/snes/chip/bsx/satellaview/satellaview.cpp
@@ -38,7 +38,7 @@ uint8 BSXSatellaview::mmio_read(unsigned addr) {
if(counter == 0) {
time_t rawtime;
- time(&rawtime);
+ rawtime = SNES::interface->currentTime();
tm *t = localtime(&rawtime);
regs.r2192_hour = t->tm_hour;
diff --git a/snes/chip/spc7110/spc7110.cpp b/snes/chip/spc7110/spc7110.cpp
index 27b8b77..061aa5e 100755
--- a/snes/chip/spc7110/spc7110.cpp
+++ b/snes/chip/spc7110/spc7110.cpp
@@ -101,7 +101,7 @@ void SPC7110::set_data_adjust(unsigned addr) { r4814 = addr; r4815 = addr >> 8;
void SPC7110::update_time(int offset) {
time_t rtc_time = (rtc[16] << 0) | (rtc[17] << 8) | (rtc[18] << 16) | (rtc[19] << 24);
- time_t current_time = time(0) - offset;
+ time_t current_time = SNES::interface->currentTime() - offset;
//sizeof(time_t) is platform-dependent; though rtc[] needs to be platform-agnostic.
//yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by
diff --git a/snes/chip/srtc/srtc.cpp b/snes/chip/srtc/srtc.cpp
index 0044113..725e891 100755
--- a/snes/chip/srtc/srtc.cpp
+++ b/snes/chip/srtc/srtc.cpp
@@ -31,7 +31,7 @@ void SRTC::reset() {
void SRTC::update_time() {
time_t rtc_time = (rtc[16] << 0) | (rtc[17] << 8) | (rtc[18] << 16) | (rtc[19] << 24);
- time_t current_time = time(0);
+ time_t current_time = SNES::interface->currentTime();
//sizeof(time_t) is platform-dependent; though rtc[] needs to be platform-agnostic.
//yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by
diff --git a/snes/interface/interface.cpp b/snes/interface/interface.cpp
index a0e3a81..b3017c9 100755
--- a/snes/interface/interface.cpp
+++ b/snes/interface/interface.cpp
@@ -18,4 +18,9 @@ void Interface::message(const string &text) {
print(text, "\n");
}
+time_t Interface::currentTime()
+{
+ return time(0);
+}
+
}
diff --git a/snes/interface/interface.hpp b/snes/interface/interface.hpp
index f1a48c0..df975e8 100755
--- a/snes/interface/interface.hpp
+++ b/snes/interface/interface.hpp
@@ -5,6 +5,7 @@ struct Interface {
virtual string path(Cartridge::Slot slot, const string &hint) = 0;
virtual void message(const string &text);
+ virtual time_t currentTime();
};
extern Interface *interface;
--
1.8.4.4

View file

@ -1,346 +0,0 @@
From de423d6ec33a20f33652c6b9c8ce703b867b51bd Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Wed, 9 Nov 2011 01:52:08 +0200
Subject: [PATCH 2/8] Save controller state when savestating
When savestating, save the controller state and restore it upon loadstate.
Prevents libsnes from mixing up buttons.
---
snes/controller/controller.cpp | 8 +++++++
snes/controller/controller.hpp | 2 ++
snes/controller/gamepad/gamepad.cpp | 13 +++++++++++
snes/controller/gamepad/gamepad.hpp | 2 +-
snes/controller/justifier/justifier.cpp | 36 +++++++++++++++++++++++++++++++
snes/controller/justifier/justifier.hpp | 1 +
snes/controller/mouse/mouse.cpp | 13 +++++++++++
snes/controller/mouse/mouse.hpp | 2 +-
snes/controller/multitap/multitap.cpp | 16 ++++++++++++++
snes/controller/multitap/multitap.hpp | 2 +-
snes/controller/superscope/superscope.cpp | 31 ++++++++++++++++++++++++++
snes/controller/superscope/superscope.hpp | 1 +
snes/system/input.cpp | 16 ++++++++++++++
snes/system/input.hpp | 1 +
snes/system/serialization.cpp | 1 +
15 files changed, 142 insertions(+), 3 deletions(-)
diff --git a/snes/controller/controller.cpp b/snes/controller/controller.cpp
index fa8e07d..5f37849 100755
--- a/snes/controller/controller.cpp
+++ b/snes/controller/controller.cpp
@@ -46,8 +46,16 @@ void Controller::iobit(bool data) {
}
}
+void Controller::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save a zero block.
+ unsigned char blockzeroes[SaveSize] = {0};
+ s.array(blockzeroes, SaveSize);
+}
+
Controller::Controller(bool port) : port(port) {
if(!thread) create(Controller::Enter, 1);
}
+
}
diff --git a/snes/controller/controller.hpp b/snes/controller/controller.hpp
index dd748a1..46095a8 100755
--- a/snes/controller/controller.hpp
+++ b/snes/controller/controller.hpp
@@ -13,12 +13,14 @@
struct Controller : Processor {
enum : bool { Port1 = 0, Port2 = 1 };
+ enum { SaveSize = 16 };
const bool port;
static void Enter();
virtual void enter();
void step(unsigned clocks);
void synchronize_cpu();
+ virtual void serialize(serializer& s);
bool iobit();
void iobit(bool data);
diff --git a/snes/controller/gamepad/gamepad.cpp b/snes/controller/gamepad/gamepad.cpp
index 594020d..4fa1c99 100755
--- a/snes/controller/gamepad/gamepad.cpp
+++ b/snes/controller/gamepad/gamepad.cpp
@@ -13,6 +13,19 @@ void Gamepad::latch(bool data) {
counter = 0;
}
+void Gamepad::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save block.
+ unsigned char block[Controller::SaveSize] = {0};
+ block[0] = latched ? 1 : 0;
+ block[1] = counter;
+ s.array(block, Controller::SaveSize);
+ if(s.mode() == nall::serializer::Load) {
+ latched = (block[0] != 0);
+ counter = block[1];
+ }
+}
+
Gamepad::Gamepad(bool port) : Controller(port) {
latched = 0;
counter = 0;
diff --git a/snes/controller/gamepad/gamepad.hpp b/snes/controller/gamepad/gamepad.hpp
index c5ca69c..a2392d1 100755
--- a/snes/controller/gamepad/gamepad.hpp
+++ b/snes/controller/gamepad/gamepad.hpp
@@ -2,7 +2,7 @@ struct Gamepad : Controller {
uint2 data();
void latch(bool data);
Gamepad(bool port);
-
+ void serialize(serializer& s);
private:
bool latched;
unsigned counter;
diff --git a/snes/controller/justifier/justifier.cpp b/snes/controller/justifier/justifier.cpp
index 6207916..ad13a9b 100755
--- a/snes/controller/justifier/justifier.cpp
+++ b/snes/controller/justifier/justifier.cpp
@@ -100,6 +100,42 @@ void Justifier::latch(bool data) {
if(latched == 0) active = !active; //toggle between both controllers, even when unchained
}
+void Justifier::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save block.
+ unsigned char block[Controller::SaveSize] = {0};
+ block[0] = latched ? 1 : 0;
+ block[1] = counter;
+ block[2] = active ? 1 : 0;
+ block[3] = player1.trigger ? 1 : 0;
+ block[4] = player2.trigger ? 1 : 0;
+ block[5] = player1.start ? 1 : 0;
+ block[6] = player2.start ? 1 : 0;
+ block[7] = (unsigned short)player1.x >> 8;
+ block[8] = (unsigned short)player1.x;
+ block[9] = (unsigned short)player2.x >> 8;
+ block[10] = (unsigned short)player2.x;
+ block[11] = (unsigned short)player1.y >> 8;
+ block[12] = (unsigned short)player1.y;
+ block[13] = (unsigned short)player2.y >> 8;
+ block[14] = (unsigned short)player2.y;
+ s.array(block, Controller::SaveSize);
+ if(s.mode() == nall::serializer::Load) {
+ latched = (block[0] != 0);
+ counter = block[1];
+ active = (block[2] != 0);
+ player1.trigger = (block[3] != 0);
+ player2.trigger = (block[4] != 0);
+ player1.start = (block[5] != 0);
+ player2.start = (block[6] != 0);
+ player1.x = (short)(((unsigned short)block[7] << 8) | (unsigned short)block[8]);
+ player2.x = (short)(((unsigned short)block[9] << 8) | (unsigned short)block[10]);
+ player1.y = (short)(((unsigned short)block[11] << 8) | (unsigned short)block[12]);
+ player2.y = (short)(((unsigned short)block[13] << 8) | (unsigned short)block[14]);
+ }
+}
+
+
Justifier::Justifier(bool port, bool chained) : Controller(port), chained(chained) {
create(Controller::Enter, 21477272);
latched = 0;
diff --git a/snes/controller/justifier/justifier.hpp b/snes/controller/justifier/justifier.hpp
index f927acf..6b7bba0 100755
--- a/snes/controller/justifier/justifier.hpp
+++ b/snes/controller/justifier/justifier.hpp
@@ -2,6 +2,7 @@ struct Justifier : Controller {
void enter();
uint2 data();
void latch(bool data);
+ void serialize(serializer& s);
Justifier(bool port, bool chained);
//private:
diff --git a/snes/controller/mouse/mouse.cpp b/snes/controller/mouse/mouse.cpp
index c9f5d16..6b26fae 100755
--- a/snes/controller/mouse/mouse.cpp
+++ b/snes/controller/mouse/mouse.cpp
@@ -61,6 +61,19 @@ void Mouse::latch(bool data) {
counter = 0;
}
+void Mouse::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save block.
+ unsigned char block[Controller::SaveSize] = {0};
+ block[0] = latched ? 1 : 0;
+ block[1] = counter;
+ s.array(block, Controller::SaveSize);
+ if(s.mode() == nall::serializer::Load) {
+ latched = (block[0] != 0);
+ counter = block[1];
+ }
+}
+
Mouse::Mouse(bool port) : Controller(port) {
latched = 0;
counter = 0;
diff --git a/snes/controller/mouse/mouse.hpp b/snes/controller/mouse/mouse.hpp
index 95e24b6..b66ea51 100755
--- a/snes/controller/mouse/mouse.hpp
+++ b/snes/controller/mouse/mouse.hpp
@@ -2,7 +2,7 @@ struct Mouse : Controller {
uint2 data();
void latch(bool data);
Mouse(bool port);
-
+ void serialize(serializer& s);
private:
bool latched;
unsigned counter;
diff --git a/snes/controller/multitap/multitap.cpp b/snes/controller/multitap/multitap.cpp
index 3a6eb72..146c41d 100755
--- a/snes/controller/multitap/multitap.cpp
+++ b/snes/controller/multitap/multitap.cpp
@@ -30,6 +30,22 @@ void Multitap::latch(bool data) {
counter2 = 0;
}
+void Multitap::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save block.
+ unsigned char block[Controller::SaveSize] = {0};
+ block[0] = latched ? 1 : 0;
+ block[1] = counter1;
+ block[2] = counter2;
+ s.array(block, Controller::SaveSize);
+ if(s.mode() == nall::serializer::Load) {
+ latched = (block[0] != 0);
+ counter1 = block[1];
+ counter2 = block[2];
+ }
+}
+
+
Multitap::Multitap(bool port) : Controller(port) {
latched = 0;
counter1 = 0;
diff --git a/snes/controller/multitap/multitap.hpp b/snes/controller/multitap/multitap.hpp
index 0540af7..e6324ac 100755
--- a/snes/controller/multitap/multitap.hpp
+++ b/snes/controller/multitap/multitap.hpp
@@ -2,7 +2,7 @@ struct Multitap : Controller {
uint2 data();
void latch(bool data);
Multitap(bool port);
-
+ void serialize(serializer& s);
private:
bool latched;
unsigned counter1;
diff --git a/snes/controller/superscope/superscope.cpp b/snes/controller/superscope/superscope.cpp
index 12068f0..1a1dfbf 100755
--- a/snes/controller/superscope/superscope.cpp
+++ b/snes/controller/superscope/superscope.cpp
@@ -100,6 +100,37 @@ void SuperScope::latch(bool data) {
counter = 0;
}
+void SuperScope::serialize(serializer& s) {
+ Processor::serialize(s);
+ //Save block.
+ unsigned char block[Controller::SaveSize] = {0};
+ block[0] = latched ? 1 : 0;
+ block[1] = counter;
+ block[2] = trigger ? 1 : 0;
+ block[3] = cursor ? 1 : 0;
+ block[4] = turbo ? 1 : 0;
+ block[5] = pause ? 1 : 0;
+ block[6] = offscreen ? 1 : 0;
+ block[7] = (unsigned short)x >> 8;
+ block[8] = (unsigned short)x;
+ block[9] = (unsigned short)y >> 8;
+ block[10] = (unsigned short)y;
+
+ s.array(block, Controller::SaveSize);
+ if(s.mode() == nall::serializer::Load) {
+ latched = (block[0] != 0);
+ counter = block[1];
+ trigger = (block[2] != 0);
+ cursor = (block[3] != 0);
+ turbo = (block[4] != 0);
+ pause = (block[5] != 0);
+ offscreen = (block[6] != 0);
+ x = (short)(((unsigned short)block[7] << 8) | (unsigned short)block[8]);
+ y = (short)(((unsigned short)block[9] << 8) | (unsigned short)block[10]);
+ }
+}
+
+
SuperScope::SuperScope(bool port) : Controller(port) {
create(Controller::Enter, 21477272);
latched = 0;
diff --git a/snes/controller/superscope/superscope.hpp b/snes/controller/superscope/superscope.hpp
index a7a90b7..93509d7 100755
--- a/snes/controller/superscope/superscope.hpp
+++ b/snes/controller/superscope/superscope.hpp
@@ -2,6 +2,7 @@ struct SuperScope : Controller {
void enter();
uint2 data();
void latch(bool data);
+ void serialize(serializer& s);
SuperScope(bool port);
//private:
diff --git a/snes/system/input.cpp b/snes/system/input.cpp
index 894de0e..4479acc 100755
--- a/snes/system/input.cpp
+++ b/snes/system/input.cpp
@@ -26,6 +26,22 @@ void Input::connect(bool port, Input::Device id) {
}
}
+void Input::serialize(serializer &s)
+{
+ int p1, p2;
+ p1 = (int)config.controller_port1;
+ p2 = (int)config.controller_port2;
+ s.integer(p1);
+ s.integer(p2);
+ if(s.mode() == nall::serializer::Load) {
+ connect(Controller::Port1, (Device)p1);
+ connect(Controller::Port2, (Device)p2);
+ }
+ port1->serialize(s);
+ port2->serialize(s);
+}
+
+
Input::Input() : port1(nullptr), port2(nullptr) {
connect(Controller::Port1, Input::Device::Joypad);
connect(Controller::Port2, Input::Device::Joypad);
diff --git a/snes/system/input.hpp b/snes/system/input.hpp
index 7a6bd9e..d2f5fef 100755
--- a/snes/system/input.hpp
+++ b/snes/system/input.hpp
@@ -31,6 +31,7 @@ struct Input {
Controller *port1;
Controller *port2;
+ void serialize(serializer &s);
void connect(bool port, Input::Device id);
Input();
~Input();
diff --git a/snes/system/serialization.cpp b/snes/system/serialization.cpp
index f746c3a..67e08a2 100755
--- a/snes/system/serialization.cpp
+++ b/snes/system/serialization.cpp
@@ -56,6 +56,7 @@ void System::serialize_all(serializer &s) {
smp.serialize(s);
ppu.serialize(s);
dsp.serialize(s);
+ input.serialize(s);
if(cartridge.mode() == Cartridge::Mode::SufamiTurbo) sufamiturbo.serialize(s);
#if defined(GAMEBOY)
--
1.8.4.4

View file

@ -1,53 +0,0 @@
From e1fce124df0a1ea43324df65b9d0ee7262eda988 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Fri, 11 Nov 2011 19:49:46 +0200
Subject: [PATCH 3/8] Allow frontend to control random number seed
---
snes/interface/interface.cpp | 5 +++++
snes/interface/interface.hpp | 1 +
snes/system/system.cpp | 2 +-
3 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/snes/interface/interface.cpp b/snes/interface/interface.cpp
index b3017c9..0a21a13 100755
--- a/snes/interface/interface.cpp
+++ b/snes/interface/interface.cpp
@@ -23,4 +23,9 @@ time_t Interface::currentTime()
return time(0);
}
+time_t Interface::randomSeed()
+{
+ return time(0);
+}
+
}
diff --git a/snes/interface/interface.hpp b/snes/interface/interface.hpp
index df975e8..30ee7fd 100755
--- a/snes/interface/interface.hpp
+++ b/snes/interface/interface.hpp
@@ -6,6 +6,7 @@ struct Interface {
virtual string path(Cartridge::Slot slot, const string &hint) = 0;
virtual void message(const string &text);
virtual time_t currentTime();
+ virtual time_t randomSeed();
};
extern Interface *interface;
diff --git a/snes/system/system.cpp b/snes/system/system.cpp
index 9b70bbf..cbd096c 100755
--- a/snes/system/system.cpp
+++ b/snes/system/system.cpp
@@ -151,7 +151,7 @@ void System::unload() {
}
void System::power() {
- random.seed((unsigned)time(0));
+ random.seed((unsigned)interface->randomSeed());
region = config.region;
expansion = config.expansion_port;
--
1.8.4.4

View file

@ -1,63 +0,0 @@
From 21e21c3b953f499bb3e309ff6a04b38763e7910a Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Wed, 7 Mar 2012 16:57:18 +0200
Subject: [PATCH 4/8] Fix mouse polling
Don't poll for mouse motion excessive number of times (no need to poll it for
each bit!)
---
snes/controller/mouse/mouse.cpp | 14 ++++++++++++--
snes/controller/mouse/mouse.hpp | 2 ++
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/snes/controller/mouse/mouse.cpp b/snes/controller/mouse/mouse.cpp
index 6b26fae..1a066b9 100755
--- a/snes/controller/mouse/mouse.cpp
+++ b/snes/controller/mouse/mouse.cpp
@@ -3,9 +3,13 @@
uint2 Mouse::data() {
if(counter >= 32) return 1;
- int position_x = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::X); //-n = left, 0 = center, +n = right
- int position_y = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Y); //-n = up, 0 = center, +n = down
+ if(counter == 0) {
+ _position_x = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::X); //-n = left, 0 = center, +n = right
+ _position_y = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Y); //-n = up, 0 = center, +n = down
+ }
+ int position_x = _position_x;
+ int position_y = _position_y;
bool direction_x = position_x < 0; //0 = right, 1 = left
bool direction_y = position_y < 0; //0 = down, 1 = up
@@ -67,10 +71,16 @@ void Mouse::serialize(serializer& s) {
unsigned char block[Controller::SaveSize] = {0};
block[0] = latched ? 1 : 0;
block[1] = counter;
+ block[2] = (unsigned short)_position_x >> 8;
+ block[3] = (unsigned short)_position_x;
+ block[4] = (unsigned short)_position_y >> 8;
+ block[5] = (unsigned short)_position_y;
s.array(block, Controller::SaveSize);
if(s.mode() == nall::serializer::Load) {
latched = (block[0] != 0);
counter = block[1];
+ _position_x = (short)(((unsigned short)block[2] << 8) | (unsigned short)block[3]);
+ _position_y = (short)(((unsigned short)block[4] << 8) | (unsigned short)block[5]);
}
}
diff --git a/snes/controller/mouse/mouse.hpp b/snes/controller/mouse/mouse.hpp
index b66ea51..b07c8ab 100755
--- a/snes/controller/mouse/mouse.hpp
+++ b/snes/controller/mouse/mouse.hpp
@@ -6,4 +6,6 @@ struct Mouse : Controller {
private:
bool latched;
unsigned counter;
+ int _position_x;
+ int _position_y;
};
--
1.8.4.4

View file

@ -1,69 +0,0 @@
From 9b14075f51587694015f8507f1c7cb565fee8225 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Mon, 24 Sep 2012 21:46:09 +0300
Subject: [PATCH 5/8] Add needed support for detecting true polls as opposed to
just autopolling
---
snes/cpu/cpu.hpp | 1 +
snes/cpu/mmio/mmio.cpp | 18 ++++++++++--------
2 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/snes/cpu/cpu.hpp b/snes/cpu/cpu.hpp
index 5eb963e..f6466cc 100755
--- a/snes/cpu/cpu.hpp
+++ b/snes/cpu/cpu.hpp
@@ -25,6 +25,7 @@ struct CPU : public Processor, public CPUcore, public PPUcounter {
CPU();
~CPU();
+ bool controller_flag;
privileged:
#include "dma/dma.hpp"
#include "memory/memory.hpp"
diff --git a/snes/cpu/mmio/mmio.cpp b/snes/cpu/mmio/mmio.cpp
index 8b6aaa6..c5ee930 100755
--- a/snes/cpu/mmio/mmio.cpp
+++ b/snes/cpu/mmio/mmio.cpp
@@ -42,6 +42,7 @@ void CPU::mmio_w4016(uint8 data) {
//1-0 = Joypad serial data
uint8 CPU::mmio_r4016() {
uint8 r = regs.mdr & 0xfc;
+ controller_flag = true;
r |= input.port1->data();
return r;
}
@@ -52,6 +53,7 @@ uint8 CPU::mmio_r4016() {
//1-0 = Joypad serial data
uint8 CPU::mmio_r4017() {
uint8 r = (regs.mdr & 0xe0) | 0x1c;
+ controller_flag = true;
r |= input.port2->data();
return r;
}
@@ -204,14 +206,14 @@ uint8 CPU::mmio_r4217() {
return status.rdmpy >> 8;
}
-uint8 CPU::mmio_r4218() { return status.joy1 >> 0; } //JOY1L
-uint8 CPU::mmio_r4219() { return status.joy1 >> 8; } //JOY1H
-uint8 CPU::mmio_r421a() { return status.joy2 >> 0; } //JOY2L
-uint8 CPU::mmio_r421b() { return status.joy2 >> 8; } //JOY2H
-uint8 CPU::mmio_r421c() { return status.joy3 >> 0; } //JOY3L
-uint8 CPU::mmio_r421d() { return status.joy3 >> 8; } //JOY3H
-uint8 CPU::mmio_r421e() { return status.joy4 >> 0; } //JOY4L
-uint8 CPU::mmio_r421f() { return status.joy4 >> 8; } //JOY4H
+uint8 CPU::mmio_r4218() { controller_flag = true; return status.joy1 >> 0; } //JOY1L
+uint8 CPU::mmio_r4219() { controller_flag = true; return status.joy1 >> 8; } //JOY1H
+uint8 CPU::mmio_r421a() { controller_flag = true; return status.joy2 >> 0; } //JOY2L
+uint8 CPU::mmio_r421b() { controller_flag = true; return status.joy2 >> 8; } //JOY2H
+uint8 CPU::mmio_r421c() { controller_flag = true; return status.joy3 >> 0; } //JOY3L
+uint8 CPU::mmio_r421d() { controller_flag = true; return status.joy3 >> 8; } //JOY3H
+uint8 CPU::mmio_r421e() { controller_flag = true; return status.joy4 >> 0; } //JOY4L
+uint8 CPU::mmio_r421f() { controller_flag = true; return status.joy4 >> 8; } //JOY4H
//DMAPx
uint8 CPU::mmio_r43x0(uint8 i) {
--
1.8.4.4

View file

@ -1,23 +0,0 @@
From 8c41bf9b792c08ecbf22a87d4e85f3e4801e62d2 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Sun, 14 Oct 2012 23:25:33 +0300
Subject: [PATCH 6/8] Add missing include to libsnes.cpp
---
target-libsnes/libsnes.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/target-libsnes/libsnes.cpp b/target-libsnes/libsnes.cpp
index 3b2be7a..ca90762 100755
--- a/target-libsnes/libsnes.cpp
+++ b/target-libsnes/libsnes.cpp
@@ -1,5 +1,6 @@
#include "libsnes.hpp"
#include <snes/snes.hpp>
+#include <gameboy/gameboy.hpp>
#include <nall/snes/cartridge.hpp>
#include <nall/gameboy/cartridge.hpp>
--
1.8.4.4

View file

@ -1,65 +0,0 @@
From 13643c86bfff3871968cf6e8b4f991465d4e81e7 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Sun, 27 Oct 2013 10:52:45 +0200
Subject: [PATCH 7/8] Support notifying latches
---
snes/cpu/mmio/mmio.cpp | 1 +
snes/cpu/timing/joypad.cpp | 1 +
snes/interface/interface.cpp | 5 +++++
snes/interface/interface.hpp | 1 +
4 files changed, 8 insertions(+)
diff --git a/snes/cpu/mmio/mmio.cpp b/snes/cpu/mmio/mmio.cpp
index c5ee930..b7afff0 100755
--- a/snes/cpu/mmio/mmio.cpp
+++ b/snes/cpu/mmio/mmio.cpp
@@ -33,6 +33,7 @@ void CPU::mmio_w2183(uint8 data) {
//strobing $4016.d0 affects both controller port latches.
//$4017 bit 0 writes are ignored.
void CPU::mmio_w4016(uint8 data) {
+ if(data&1) interface->notifyLatched();
input.port1->latch(data & 1);
input.port2->latch(data & 1);
}
diff --git a/snes/cpu/timing/joypad.cpp b/snes/cpu/timing/joypad.cpp
index 179df27..6a98de0 100755
--- a/snes/cpu/timing/joypad.cpp
+++ b/snes/cpu/timing/joypad.cpp
@@ -9,6 +9,7 @@ void CPU::step_auto_joypad_poll() {
if(status.auto_joypad_active && status.auto_joypad_latch) {
if(status.auto_joypad_counter == 0) {
+ interface->notifyLatched();
input.port1->latch(1);
input.port2->latch(1);
input.port1->latch(0);
diff --git a/snes/interface/interface.cpp b/snes/interface/interface.cpp
index 0a21a13..6685556 100755
--- a/snes/interface/interface.cpp
+++ b/snes/interface/interface.cpp
@@ -28,4 +28,9 @@ time_t Interface::randomSeed()
return time(0);
}
+void Interface::notifyLatched()
+{
+ //Nothing.
+}
+
}
diff --git a/snes/interface/interface.hpp b/snes/interface/interface.hpp
index 30ee7fd..203f7b0 100755
--- a/snes/interface/interface.hpp
+++ b/snes/interface/interface.hpp
@@ -7,6 +7,7 @@ struct Interface {
virtual void message(const string &text);
virtual time_t currentTime();
virtual time_t randomSeed();
+ virtual void notifyLatched();
};
extern Interface *interface;
--
1.8.4.4

View file

@ -1,22 +0,0 @@
From e74f6d6ce7b369d82abf1eed6d7c3e99af0d8f64 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Sat, 30 Nov 2013 10:28:40 +0200
Subject: [PATCH 8/8] Support auto-dectecting bsnes version
---
bsnes.mk | 3 +++
1 file changed, 3 insertions(+)
create mode 100644 bsnes.mk
diff --git a/bsnes.mk b/bsnes.mk
new file mode 100644
index 0000000..c31911f
--- /dev/null
+++ b/bsnes.mk
@@ -0,0 +1,3 @@
+BSNES_SUPPORTS_DEBUGGER=
+LIBSNES_DIR=target-libsnes
+BSNES_VERSION=087
--
1.8.4.4

7
build_qt.sh Executable file
View file

@ -0,0 +1,7 @@
#!/bin/sh
echo "cd libgambatte && scons"
(cd libgambatte && scons) || exit
echo "cd gambatte_qt && qmake && make"
(cd gambatte_qt && qmake && make)

7
build_sdl.sh Executable file
View file

@ -0,0 +1,7 @@
#!/bin/sh
echo "cd libgambatte && scons"
(cd libgambatte && scons) || exit
echo "cd gambatte_sdl && scons"
(cd gambatte_sdl && scons)

View file

@ -1,124 +0,0 @@
#include <boost/filesystem.hpp>
#include <sys/time.h>
#include <fstream>
#include <cctype>
#include <set>
#include <map>
#include <iostream>
#include <string>
namespace boost_fs = boost::filesystem;
bool is_cmdhelp_file(const std::string& filename)
{
std::string _filename = filename;
return (_filename.length() > 8 && _filename.substr(0, 8) == "cmdhelp/");
}
std::string search_include(const std::list<std::string>& searchpath, const std::string& _filename,
const std::string& ref_by)
{
std::string filename = _filename;
//Hack: process cmdhelp includes internally as the date were for the JSON include.
if(is_cmdhelp_file(filename)) {
if(filename != "cmdhelp/inverselist.hpp") {
filename = "../src/" + filename;
//Replace the extension with .json.
size_t split = filename.find_last_of("./\\");
if(split < filename.length() && filename[split] == '.') {
filename = filename.substr(0, split) + ".json";
}
}
}
size_t p = ref_by.find_last_of("/");
if(p < ref_by.length()) {
std::string i = ref_by;
i = i.substr(0, p);
std::string real_fn = i + "/" + filename;
boost_fs::path p(real_fn);
if(boost_fs::exists(p) && boost_fs::is_regular_file(p))
return real_fn;
}
for(auto& i : searchpath) {
std::string real_fn = i + "/" + filename;
boost_fs::path p(real_fn);
if(boost_fs::exists(p) && boost_fs::is_regular_file(p))
return real_fn;
}
std::cerr << "WARNING: Include file '" << filename << "' not found." << std::endl;
return "";
}
time_t get_timestamp(const std::string& path)
{
boost_fs::path p(path);
if(!boost_fs::exists(p)) return 0;
return boost_fs::last_write_time(p);
}
time_t recursive_scan(const std::list<std::string>& searchpath, const std::string& filename,
std::map<std::string, time_t>& scanned)
{
if(filename == "")
return 0;
if(scanned.count(filename))
return 0;
std::ifstream fp(filename);
if(!fp) {
std::cerr << "WARNING: File '" << filename << "' can't be opened." << std::endl;
return 0;
}
time_t newest = get_timestamp(filename);
scanned[filename] = newest;
std::string tmp;
while(std::getline(fp, tmp)) {
if(tmp.length() > 0 && tmp[0] == '#') {
//Possibly include.
std::string included;
if(strncmp(tmp.c_str(), "#include", 8))
continue;
size_t ptr = 8;
while(ptr < tmp.length() && isspace((unsigned char)tmp[ptr]))
ptr++;
if(ptr == tmp.length())
continue;
if(tmp[ptr] != '\"')
continue;
size_t iptr = ++ptr;
while(ptr < tmp.length() && tmp[ptr] != '\"')
ptr++;
if(ptr == tmp.length())
continue;
included = tmp.substr(iptr, ptr - iptr);
newest = std::max(newest, recursive_scan(searchpath, search_include(searchpath, included,
filename), scanned));
}
}
return newest;
}
int main(int argc, char** argv)
{
std::list<std::string> searchpath;
std::list<std::string> files;
bool step = false;
for(int i = 1; i < argc; i++) {
if(!step && !strcmp(argv[i], "--"))
step = true;
else if(!step)
searchpath.push_back(argv[i]);
else
files.push_back(argv[i]);
}
searchpath.push_back(".");
for(auto& i : files) {
std::map<std::string, time_t> x;
time_t t = recursive_scan(searchpath, i, x);
if(get_timestamp(i + ".dep") < t) {
std::ofstream y(i + ".dep");
for(auto& j : x)
y << j.second << " " << j.first << std::endl;
}
}
return 0;
}

View file

@ -1,91 +0,0 @@
#include <iostream>
#include <fstream>
#include <sstream>
#include <cstring>
const char* hexes = "0123456789ABCDEF";
struct encoder
{
encoder(std::ostream& _output) : output(_output)
{
have_quote = false;
}
size_t operator()(unsigned char* buf, size_t bufuse, bool eof)
{
if(!bufuse) return 0;
std::ostringstream out;
size_t i = 0;
while(i < bufuse) {
if(!have_quote) {
out << "\"";
have_quote = true;
}
unsigned char ch = buf[i];
if(ch == 9) {
out << "\\t";
} else if(ch == 10) {
out << "\\n\"" << std::endl;
have_quote = false;
} else if(ch == 13) {
out << "\\r";
} else if(ch < 32) {
out << "\\x" << hexes[(ch >> 4)] << hexes[ch & 15];
} else if(ch == '\"') {
out << "\\\"";
} else if(ch == '\\') {
out << "\\\\";
} else if(ch < 127) {
out << ch;
} else {
out << "\\x" << hexes[(ch >> 4)] << hexes[ch & 15];
}
i++;
}
output << out.str();
return i;
}
size_t operator()()
{
if(have_quote) {
output << "\"";
have_quote = false;
}
}
private:
std::ostream& output;
bool have_quote;
};
void do_encode(std::istream& input, std::ostream& output)
{
char buf[4096];
size_t bufuse = 0;
bool eof = false;
encoder e(output);
while(true) {
if(!eof) {
input.read(buf + bufuse, 4096 - bufuse);
bufuse += input.gcount();
}
if(!input)
eof = true;
size_t bytes = e(reinterpret_cast<unsigned char*>(buf), bufuse, eof);
memmove(buf, buf + bytes, bufuse - bytes);
bufuse -= bytes;
if(eof && !bufuse) break;
}
e();
}
int main(int argc, char** argv)
{
if(argc != 3) {
std::cerr << "Usage: txt2cstr <symbol> <file>" << std::endl;
return 1;
}
std::ifstream in(argv[2], std::ios::binary);
std::cout << "const char* " << argv[1] << " =" << std::endl;
do_encode(in, std::cout);
std::cout << ";" << std::endl;
}

View file

@ -1,85 +0,0 @@
#include <cstdio>
#include <cstdlib>
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
std::string X = "$Format:%h by %cn on %ci$";
std::string derive_format(std::string kwformat)
{
if(kwformat[0] != '$' || kwformat[1] != 'F' || kwformat[kwformat.length() - 1] != '$') {
std::cerr << "Bad keyword format '" << kwformat << "'" << std::endl;
exit(1);
}
return "--pretty=f" + kwformat.substr(2, kwformat.length() - 3);
}
std::string shellquote(std::string arg)
{
std::ostringstream x;
x << "'";
for(size_t i = 0; i < arg.length(); i++) {
if(arg[i] == '\'')
x << "\\'";
else
x << arg[i];
}
x << "'";
return x.str();
}
std::string runlog(std::string logformat)
{
std::string command = "git log " + shellquote(logformat) + " -1";
std::string retval;
int r;
char buf[4096] = {0};
FILE* out = popen(command.c_str(), "r");
if(!out) {
std::cerr << "Can't invoke git to get the version" << std::endl;
exit(1);
}
while((r = fread(buf, 1, 4095, out)) > 0) {
buf[r] = 0;
retval = retval + buf;
}
if(ferror(out)) {
std::cerr << "Error reading git version output" << std::endl;
exit(1);
}
pclose(out);
return retval;
}
std::string get_main_version()
{
std::ifstream x("VERSION");
if(!x) {
std::cerr << "Error reading main version" << std::endl;
exit(1);
}
std::string out;
std::getline(x, out);
if(out == "") {
std::cerr << "Error reading main version" << std::endl;
exit(1);
}
return out;
}
int main()
{
std::string gitversion;
std::string mainversion = get_main_version();
if(X[0] == '$') {
std::string logformat = derive_format(X);
gitversion = runlog(logformat);
} else
gitversion = X;
std::cout << "#include <string>" << std::endl;
std::cout << "std::string lsnes_git_revision = \"" << gitversion << "\";" << std::endl;
std::cout << "std::string lsnes_version = \"" << mainversion << "\";" << std::endl;
return 0;
}

535
changelog Normal file
View file

@ -0,0 +1,535 @@
-- 0.4.1 -- 2009-01-10
libgambatte:
- Fix HqXx filter pitch.
- Fix mbc2 not getting a rambank.
- Make sure to reset passed pointers when deleted. Fixes potential crash
when loading ROM during OAM busy.
common:
- Substantially improved rate estimation averaging.
- RateEst: Add a convenient way of filtering measures that extend beyond
a buffer time, and are as such probably invalid.
- RateEst: Allow using a custom timestamp in feed().
- RateEst: Keep a queue of the last ~100 msec worth of samples and
duration, and filter out collective samples that give a pre-estimate
that seems way off.
- Replace "Game Boy / Game Boy Color emulator" with "Game Boy Color
emulator" for now to avoid misleading anyone on the current status.
gambatte_qt:
- Disable BlitterWidget updates (paintEvents) while not paused.
- QGLBlitter: Do a cheap front blit rather than a vsynced flip if audio
buffers are low.
- Allow BlitterWidgets to opt in to get paintEvents while unpaused. Do so
for QGLBlitter since it may need to clear buffers afterwards.
- QGLBlitter: Try to blit right after sync in the case of single buffering.
- Up default audio buffer latency to 100 ms (some common system audio
servers require a lot of buffering to work well).
- Adaptively skip BlitterWidget syncs if audio buffer is low, in a manner
that should minimize wasted skips in sync to vblank situation, and tries
to be non-disturbing. This replaces frame time halving, and blitter
specific rescueing.
- Clear display buffers in DirectDrawBlitter and Direct3DBlitter in
exclusive mode, since blits don't necessarily cover the entire buffers.
- DirectDrawBlitter: Make sure that a minimum amount of time has passed
between calls to WaitForVerticalBlank, since it can return in the same
vblank period twice on a fast system.
- DirectDrawBlitter: Support vsync for refresh rate ~= 2x frame rate.
- DirectDrawBlitter: Refactor somewhat and get rid of a couple minor
potential bugs.
- DirectDrawBlitter: Some tweaks to get updates closer to sync time in
certain situations.
- DirectDrawBlitter: Some tweaks to better support DONOTWAIT.
- DirectDrawBlitter: Make only updating during vblank while page flipping
optional.
- Direct3DBlitter: Some tweaks to get updates closer to sync time in
certain situations.
- Filter out very short frame times in frame time estimation.
- Don't adjust frame time during turbo, but rather skip BlitterWidget
syncs to speed up, which avoids vsync limits without disabling vsync.
- DirectDrawBlitter: Add triple buffering option.
- Direct3DBlitter: Use D3DSWAPEFFECT_DISCARD in non-exclusive mode.
- Direct3DBlitter: Allow triple buffering and vblank-only updates in
non-excusive mode.
- Rename "Page flipping" in Direct3D and DirectDraw blitters to
"Exclusive full screen".
- Pause audio on win32 titlebar clicks/drags to avoid looping audio due to
underruns from blocked timerEvents.
- Use wildcards for platform detection to avoid being unnecessarily
compiler/architecture specific. Fixes bug 2377772.
- Rewrite most of DirectSoundEngine, supporting primary buffer option,
making it more robust, correct and hopefully cleaner. Only use part of
the primary buffer if the desired buffer size is lower than the
primary buffer size.
- Direct3DBlitter and DirectDrawBlitter: Force blocking updates when sync
to vblank is enabled. Some updates only block if there's a prior
unfinished update in progress. This screws up frame time estimation in
turn screwing up vsync. To fix this we do a double update (and extra blit)
if close to a frame time period has passed since the last update when
sync to vblank is enabled. I really should have noticed this earlier as
it pretty much breaks vsync adaption completely.
- Direct3DBlitter: Use the D3DCREATE_FPU_PRESERVE flag when creating
device. Omitting this flag can screw up floating point calculations in
other parts of the code. For instance WASAPI cursor timestamps get
utterly screwed up here.
- Direct3DBlitter: It appears that managed textures are updated before
they are unlocked, which screws up redraws, making things appear choppy
in some situations. Use a default memory texture and a system memory
texture and the UpdateTexure method instead.
- DirectSoundEngine: Make use of the sample period limit feature of
RateEst, rather than duplicating the feature.
- Add polling WASAPI engine with exclusive mode support. Latency and rate
estimation is generally better than DirectSound, and in exclusive mode
there is less blocking as well as exclusive mode being better than
shared mode in the other areas too.
- WasapiEngine: Add device selection.
- WasapiEngine: Add static isUsable() method. Only listed if isUsable().
Default engine if isUsable().
- WasapiEngine: Use default device if there's only one device available,
since we don't show the combobox anyway.
- DirectSoundEngine: Provide the integrated read and status get write
method optimization.
- XvBlitter: Set NosystemBackground attribute rather than OpaquePaintEvent.
Reimplement paintEngine to return NULL as suggested by Qt docs.
- X11Blitter: Reimplement paintEngine to return NULL.
- AlsaEngine: Make use of sample period limit feature of RateEst. Don't
increase estimated sample rate on underrun.
- OssEngine: Make use of sample period limit feature of RateEst. Don't
increase estimated sample rate on underrun.
- Esc exits fullscreen on macx.
- Drop OpenAL from default macx binary.
- Add some useful but commented build flags for macx to .pro files.
-- 0.4.0 -- 2008-10-27
libgambatte:
- less fixed-width type dependencies. don't assume unsigned int > 16 bits
- slightly faster sprite mapping
- Skip potential high frequency events when they don't matter.
- do sprite sorting and cycle calculations pr line as needed instead of all
at once
- fix broken volume on/off event notification
- less int > 16-bits assumptions
- more type width dependency fixes
- int width deps. Gambatte namespace
- wx affects sprite m3 cycles
- cache m3 cycles, related refactoring
- readjust cgb dma cycles to previously changed m3 timing
- clean up goofy lyc calculation.
- cgb dma from various areas results in 0xFF being written.
- 0xFEA0-0xFEFF not writable when OAM isn't
- unusable ioram bits fixes
- dmg ioram startup state fixes.
- various oamdma accuracy
- oamdma bus conflicts with cpu, ppu, cgbdma.
- rewritten memory read/write methods.
- accurate timing of ppu sprite mapping reads.
- fix recent cgb sprite cycles sorting slip up.
- preoffset mem pointers.
- get rid of unused memory.
- save state infrastructure,
- clean up video timing code,
- use save state for initialization and reset,
- do color conversion outside filters
- fast rgb32ToUyvy,
- add overlooked oamdma event,
- adjust subcycle irq timing (shouldn't affect anything),
- various refactoring
- save savedata before loading state
- fix silly initstate ifreg regression
- save state selection
- save state osd preview snapshots
- fix a few potential security holes when loading invalid state
- get rid of some undefined behaviour in statesaver
- always draw in rgb32, color convert afterwards, too bad for maemo/16-bit
depth users
- get rid of silly c string stuff
- add bitmap font rendering with font based on Bitstream Vera Sans
- osd state n saved/loaded text
- empty state osd thumbs marked with "Empty" text
- adjust thumbnail interpolation weighing slightly
- utilize templates for more flexible osd text printing
- use grey osd text with black outline for save/load state messages
- move state 0 OSD pos to rightmost to match kbd layout
- state 1 default on ROM load
- support external save state files
- missing includes
- missing virtual destructor
- std::ifstream construction missing binary flag
- fix gcc-4.3 compilation
- avoid signed overflow in constant (which is both undefined and likely
to cause problems on architectures where sizeof(long) != sizeof(int)) in
rgb2yuv code.
- Fix wrong pitch passed to filter if color conversion is needed.
- Fix potential problem with rgb32ToUyvy cache init values on 16-bit systems
- Correct unhalttime when resetting counters. Fixes perodic infinite halt
issue in Kirby's Star Stacker and probably other games.
- Fix LY display disable regression
- Use deltas and a running sum to decrease buffer writes in sound emulation
sample generation.
- Rearrange sound emulation event loop to optimize for high-frequency event
units.
- Initialize palette arrays to avoid valgrind noise.
- Don't do resampling in libgambatte. Update API to reflect this.
- No rambanks for ROMs that don't request any.
- Route invalid rombank addresses in non-power-of-2 number of rombanks
cases to disabled area assuming ceiled power of 2 address bus.
- no sprites or sprite mapping busy cycles on first line after display
enable. slight cleanup.
- small oam accessibility correction.
- Tile loading and tile rendering can seemingly get out of sync when
modifying scx at a critical time. Another pessimation with little gain in
the name of accuracy.
- Use a look-up table to do tile byte merging.
- Append "_dmg" to save base name when forcing DMG mode, to avoid
corrupting CGB save files and vice versa.
- saner ly write behaviour
- Add adapted and optimized hq3x.
- Revert to big f'ing switch hq2x code, as there's less duplication now.
Also optimized interpolation functions further. No idea how I missed that
initially.
- Lower opacity OSD text.
gambatte_sdl:
- less retarded indenting
- saner placement of fill_buffer function
- int width deps. Gambatte namespace
- Scalebuffer dstpitch aware.
- save state selection
- add number key slot selection shortcuts
- Estimate actual output sample rate in terms of OS timers
and derive frame rate from it.
- Move AudioData and RingBuffer classes to separate files.
- Make underruns slightly less painful, by resetting buffer
positions.
- Skip resampling when fast-forwarding
- Fill available buffer space before waiting for more.
- Audio buffer command line options.
- Use half video frame sleep time if audio buffer is close to underrun.
- Adjust estimated frame time each frame.
gambatte_qt:
- more likely to build on mac os x
- Fix fixed window size issues with various window managers (metacity,
xfwm4...)
- macx build fixes
- hopefully fix opengl clearing issues
- Gambatte namespace
- Decouple Qt GUI from gambatte.
- Lots of cleanups, flexibility added
- setting of various properties, frame time, aspect ratio, button events,
video sources, sample rates, pauseOnDialogExec, custom menus etc.
- Document some interfaces.
- Support for setting approximate sound buffer latency.
- Use rational math for 100% exact timers (even though the actual system
timers are unlikely to be accurate).
- Add fast-forward to input settings.
- timeGetTime() fallback for win32
- Store full screen mode values/text rather than less reliable indexes.
- Repaint on xvblitter port changes to avoid color key not getting
repainted.
- improved ALSA buffer reporting
- add sampleRate info to MediaSource::setSampleBuffer.
- clarify that "samples" refers to stereo samples
- fix 24-bit depth non-shm ximage creation
- fix blittercontainer incorrectly using minimumSize for integer scaling
- add unrestricted fast bilinear and nearest neighbor sw scaling to
x11/qpainter blitter
- swscale: remove forgotten static qualifiers
- swscale: center linear weighing bias
- swscale: exclude iostream
- swscale: less bloated
- macx fixed/variable window size change issue fixed
- macx opengl drawbuffer change issues worked around
- add openal engine, default on macx
- add macx quartz video mode toggler
- multi-head infrastructure
- support multiple monitors in macx quartz toggler
- more work-arounds for Qt failing to set correct geometry on video mode
changes.
- more explicit fast-forward button handling, to avoid missed key
press/release events on macx
- opengl doublebuffer preblitting, try to make actual screen updates as
close to right after sync wait is over as possible
- add xf86vidmode toggler (xrandrtoggler is default)
- x11blitter: check for other supported visuals if the default is
unsupported.
- temporarily return to original video mode and minimize on full screen
alt-tab (except on macx or if there are multiple screens), switch back on
focus-in
- hide mouse cursor after move timeout, or key/joystick pressed (more sane
on macx)
- exit fullscreen rather than toggle menubar on macx (note that the menubar
will automatically pop-up on macx full screen if the mouse is moved to
the top of the primary screen)
- add (independent) pause counter for non-client pauses.
- reset X11 screen saver on joystick activity
- change "turbo"-mode to temporarily set frametime as a way of avoiding
vsync issues (for a laugh, check out the video dialog while in
fast-forward mode and see "Sync to vertical blank in 65535 and 131070 Hz
modes").
- fix win32 compilation
- refix win32 fullscreen geometry correction
- neater win32 BlitterWidget::sync
- avoid misleading minimize on fullscreen close
- refactor Blitterwidget::sync
- directdrawblitter: remove unecessary turbo conditions
- gditoggler: add multi-monitor support (win32)
- videodialog: save actual hz values for real this time
- quartztoggler: avoid potentially reverting to the wrong mode on double
setFullMode(false) in multi-head configs
- make sure window is within screen after mode change, so Qt doesn't reset
it to the primary screen
- revert to previous win32 fullscreen geometry correction behaviour so that
the geometry gets properly reset after fullscreen
- Add directdraw device selection.
- directsoundengine: add device selection.
- directdrawblitter: only list devices if there are more than 2 devices
(including primary)
- directdrawblitter: use private static member rather than global friend
enumeration callback
- capitalization changes
- add direct3d9 blitter with support for vsync, bf, page flipping, triple
buffering, device selection, multi-head etc. d3d9.dll loaded at runtime
- more strict and thorough exclusive mode handling to support d3d fullscreen
- work around file open dialog not returning focus properly
- gditoggler: use current registry settings for return modes
- directsoundengine: set DSBCAPS_GETCURRENTPOSITION2 flag
- revert bad macx return from fullscreen on menu-toggle
- don't build xf86vidmodetoggler by default
- add save state actions to GUI menu
- clean up GUI menu creation code
- move GUI recent files to submenu
- support external save state files
- add number key slot selection shortcuts
- missing includes
- missing virtual destructor
- make sure windows path arguments don't use backslashes by using QFileInfo
- add Play menu with Pause, Frame Step, Dec/Inc/Reset Frame Rate
- Add tab support to input settings dialog.
- Add alternate key support to input settings dialog.
- Auto-focus to next likely input box after settings key in input dialog.
- Add "Play" and "State" input settings dialog tabs.
- Avoid using the most convenient keys as forced menu short-cuts, set them
as default keys in input settings dialog instead. This unfortunately
makes the more useful shortcuts less visible, but it allows remapping
the most convenient keyboard keys.
- Avoid duplicate joystick axis "press" events by keeping a map of axis
states.
- Make sure to discard irrelevant/old joystick events.
- Don't give MediaSource button events when stopped.
- Allow joystick-based button events while paused by using a very
low-frequency poll timer.
- Make some of the joystick event wrapping stuff less messy.
- missing string include
- use QString for videoSourceLabel passed to MainWindow constructor
- store currently selected scheme as string, since it appears ModelIndex
is neither tied to the data it points to nor invalidated by changes.
enforce valid state on reject since the list of schemes may have
changed.
- Direct3DCreate function pointer typedef needs WINAPI macro
- disable page flipping dependent checkboxes in constructor to ensure
correct start state
- add custom sample rate support
- change default buffer latency to 67 ms
- don't auto-repeat buttons bound to keyboard
- use enums for somewhat more robust gambattesource button setup
- fix silly "alsa not using default device by default" bug
- Only ask for xrandr config once to avoid potential server roundtrips in
some xrandr versions.
- Make sure xrandr version is >= 1.1 and < 2
- Prevent all text editing of input boxes.
- Add custom context menu to input boxes.
- Update AudioEngine to support sample rate estimation in terms of OS
timers.
- Implement sample rate estimation in ALSA and OSS audio engines.
- AlsaEngine: Revert to using snd_pcm_avail_update for buffer status since
snd_pcm_delay may consider external latencies.
- AlsaEngine: Use snd_pcm_hw_params_set_buffer_time_near. Don't request a
particular number of periods per buffer.
- AlsaEngine: Use hw as default custom device string, rather than hw:0,0.
- OssEngine: Don't trust GETOSPACE fragment info.
- Estimate optimal frame rate based on sample rate estimations.
- Extend BlitterWidget to support estimation of vsynced frame rate in terms
of OS timers.
- Implement vsync frame rate estimation in QGlBlitter, Direct3DBlitter and
DirectDrawBlitter.
- Use a combination of OS timer sample rate estimation and vsync frame rate
estimation to derive resampling ratio for no-frame-duplication vsync.
- Change API to reflect MediaSources not being responsible for resampling.
- Make sure to parent PaletteDialog list model, so it gets deleted properly.
- Various refactoring, small changes and stuff I forgot.
- limit vsync frame rate estimation deviation
- More averaging in estimation code.
- Stricter estimate deviation limit
- Adjust estimated frame time each frame.
- Use half frame time if audio buffer is close to underrun.
- Provide combined audioengine write and status get, to avoid doing
potentially expensive operations twice. Utilized in OSS and ALSA engines.
- Saner vsync estimate variance protection.
- allow dynamically setting samples per frame
- Don't bother allowing sources the choice of which output sample rates are
selecrable, as it's not really a per source thing at this point. If
resampling avoidance is desired, then that should rather be a user option
(to depend on the OS for resampling, which is mostly nonsensical for the
Game Boy/NES/PSG-system case btw).
- Move Qt media framework to a separate subdir
- postpone buffered x11 blits to after sync.
- Add support for XRandR 1.2 + multi-head
- use crtc mode dimensions rather than crtc dimensions when discarding
modes since crtc dimensions may be rotated
- Fractional bits for intermediate rate estimation averages.
- Add RateEst reset method. Initialize RateEst count to 1.
- Less refresh rate estimation averaging.
- Allow more refresh rate estimation deviation.
- Return NULL paintEngine in windows blitters that use the PaintToScreen
attribute.
- Add checks for things not being initialized in DirectDraw-blitter and
QPainterBlitter paintEvents.
- Don't reparent blitters (mainly to make a bug in Qt 4.4.3 win less
annoying, widgets that do internal reparenting are still affected).
- Check for window position less than screen top-left after mode change,
before full screen, to avoid Qt moving it to the primary screen.
- Add rate estimation to DirectSound engine.
- Better underrun detection in DirectSound engine.
- Don't duplicate blitter pointer in mainwindow.
- Use RateEst.reset rather than re-initing on pause.
- Add CoreAudio engine with rate estimation and buffer status support.
Default engine on Mac OS X.
- 44100 Hz default sample rate on OS X, since OS X tends to resample
everything to 44100 Hz.
- Get rid of buffer status averaging in OpenAlEngine, since it makes
assumptions on usage pattern that shouldn't be made.
- Fix CoreAudio engine reporting buffer status in samples rather than
frames.
- Update SDL_Joystick to SDL-1.2 SVN.
- #undef UNICODE in win32/SDL_mmjoystick.c to avoid joystick name mangling.
- work around annoying random non-updating OpenGL on Mac OS X after full
screen.
common/other:
- Fix GCC 4.3 warnings about people getting confused by operator precedence
by adding parentheses.
- Real-time, sophisticated resampling framework with several
performance/quality profiles for dynamically generated windowed sinc and
CIC chains based on analysis of fourier transforms and optimal cost
equations. Fast 2-tap linear as a low quality alternative.
- Move non-emulation common code to a common directory to avoid duplication.
- Update front-ends to new libgambatte API.
- Utilize resampling framework in front-ends. Selectable resamplers.
- Improved adaptive sleep class that estimates oversleep.
- Various refactoring, small changes and stuff I forgot.
- Do per phase normalization to avoid dc fluctuations.
- More averaging in estimation code.
- Stricter estimate deviation limit
- Fractional bits for intermediate rate estimation averages.
- Add RateEst reset method. Initialize RateEst count to 1.
- Extend ringbuffer.h to support resetting size, and move it to common dir
since gambatte_qt/coreaudioengine uses it too now.
- Add "force DMG mode" option.
- Allow more rate estimation deviation.
hwtests:
- wx affects sprite m3 cycles.
- cgb dma from various areas results in 0xFF being written.
- add hwtests for oam dma
- m3 cycles wo bg
- more oamdma tests
- various oamdma accuracy. oamdma bus conflicts with cpu, ppu, cgbdma.
- accurate timing of ppu sprite mapping reads.
-- 0.3.1 -- 2007-10-26 --
gambatte_qt:
- Enable Joystick POV-Hat events.
-- 0.3.0 -- 2007-10-26 --
libgambatte:
- Fix adc/sbc and add_hl_rr hfc calc, sp_plus_n cf/hcf calc and daa thanks
to blargg.
- document HF2 better
- Update sound core according to blargg's findings.
- Improve resampling quality and performance.
- Fix overlooked "add hl,sp" flag calculation.
- fix initial endtime value
- check for resampling ratio < 1
- Add support for DMG palette customization.
gambatte_sdl:
- use std::map for parser
- Don't bother hashing.
- Add input config support.
- Add joystick support.
- Fix horrid "turbo can affect emulation" bug.
- Add sw and yuv overlay scaling.
- Use cond/mutex for thread syncing, RAII, refactor.
- add option for sample rate choice
- Add option to list valid input keys
- don't die if audio fails
gambatte_qt:
- no point in filter being non-static anymore
- use std::map for input vectors
- remove unused unusedBool
- Fix horrid "turbo can affect emulation" bug.
- remove some useless optimizations
- auto_ptr love.
- support joystick hat.
- nicer input handling.
- Add sound dialog.
- Add custom dev choice for oss, alsa engines.
- Use rgb if available for xv.
- Get rid of BAD_MATCH warnings for setting non-existent xv attributes.
- make subblitters private nested classes
- add reset action
- Add support for DMG palette customization.
- Add global buffer option for dsound engine
-- 0.2.0 -- 2007-09-05 --
libgambatte:
- fix 64-bit compile and segfault. Thanks to Nach for noticing.
- Add zip support. Thanks to Nach for contributing nice, clear code
- fix sound ch4 frequency calculation
- Several PPU reads timings depend on wx. Thanks to franpa for noticing the
corrupt line in The LoZ: Oracle of Seasons.
- remove unused doubleSpeed parameter from m3ExtraCycles call
gambatte_sdl:
- Thread safety, bigger sound buffer
- Compile on more platforms. Thanks to Thristian for the find.
- actually increment iterator so the loop makes some sense (parser.cpp)
gambatte_qt:
- fix 64-bit compile. Thanks to Nach.
- better license info for x11getprocaddress.cpp
- initial joystick support, mostly using SDL's joystick code (separated from
the rest of SDL)
- use binary search for gb inputs.
all:
- make sure to use std:: despite sloppy compilers allowing omission. Thanks
to blargg for the reminder.
- get rid of some valgrind warnings. Thanks to Nach for noticing.
hwtests:
- add tests for wx effects on PPU read timings.
build:
- add -Wextra to default compile flags
doc:
- mention optional zlib dependency
- additions to thanks section
-- 0.1.1 -- 2007-08-29 --
libgambatte:
- fix integer overflow in color conversion to rgb16
- only accept valid filter indexes
gambatte_sdl:
- print version
- print usage
- support command line arguments.
- add option for starting in full-screen
- add option for using video filter
gambatte_qt:
- clean up obsolete includes.
- directdraw: only use alpha if primary surface uses it.
- add support for loading rom from porgam argument.
- s/"a highly accurate"/"an accuracy-focused"/ in about box
- gditoggler: fix unordered video mode listing
build:
- Support external CPPFLAGS
- Use sdl-config
doc:
- fix silly wording in README about section
- s/seperate/separate/
- s/Automake/Make/
- mention XShm dependency
- mention sys/shm.h requirement
- document key mapping better
- s/"a highly accurate"/"an accuracy-focused"/
- add man pages

16
clean.sh Executable file
View file

@ -0,0 +1,16 @@
#!/bin/sh
echo "cd gambatte_qt && make distclean"
(cd gambatte_qt && make distclean)
echo "cd gambatte_sdl && scons -c"
(cd gambatte_sdl && scons -c)
echo "cd libgambatte && scons -c"
(cd libgambatte && scons -c)
echo "rm -f *gambatte*/config.log"
rm -f *gambatte*/config.log
echo "rm -rf *gambatte*/.scon*"
rm -rf *gambatte*/.scon*

56
common/adaptivesleep.cpp Normal file
View file

@ -0,0 +1,56 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "adaptivesleep.h"
usec_t AdaptiveSleep::sleepUntil(usec_t base, usec_t inc) {
usec_t now = getusecs();
usec_t diff = now - base;
if (diff >= inc)
return diff - inc;
diff = inc - diff;
if (diff > oversleep + oversleepVar) {
diff -= oversleep + oversleepVar;
usecsleep(diff);
const usec_t ideal = now + diff;
now = getusecs();
{
usec_t curOversleep = now - ideal;
if (negate(curOversleep) < curOversleep)
curOversleep = 0;
oversleepVar = (oversleepVar * 15 + (curOversleep < oversleep ? oversleep - curOversleep : curOversleep - oversleep) + 8) >> 4;
oversleep = (oversleep * 15 + curOversleep + 8) >> 4;
}
noSleep = 60;
} else if (--noSleep == 0) {
noSleep = 60;
oversleep = oversleepVar = 0;
}
while (now - base < inc)
now = getusecs();
return 0;
}

34
common/adaptivesleep.h Normal file
View file

@ -0,0 +1,34 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef ADAPTIVE_SLEEP_H
#define ADAPTIVE_SLEEP_H
#include "usec.h"
class AdaptiveSleep {
usec_t oversleep;
usec_t oversleepVar;
unsigned noSleep;
public:
AdaptiveSleep() : oversleep(0), oversleepVar(0), noSleep(60) {}
usec_t sleepUntil(usec_t base, usec_t inc);
};
#endif

52
common/array.h Normal file
View file

@ -0,0 +1,52 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef ARRAY_H
#define ARRAY_H
#include <cstddef>
#include "uncopyable.h"
template<typename T>
class Array : Uncopyable {
T *a;
std::size_t sz;
public:
explicit Array(const std::size_t size = 0) : a(size ? new T[size] : 0), sz(size) {}
~Array() { delete []a; }
void reset(const std::size_t size = 0) { delete []a; a = size ? new T[size] : 0; sz = size; }
std::size_t size() const { return sz; }
T * get() const { return a; }
operator T*() const { return a; }
};
template<typename T>
class ScopedArray : Uncopyable {
T *a_;
public:
explicit ScopedArray(T *a = 0) : a_(a) {}
~ScopedArray() { delete []a_; }
void reset(T *a = 0) { delete []a_; a_ = a; }
T * release() { T *a = a_; a_ = 0; return a; }
T * get() const { return a_; }
operator T*() const { return a_; }
};
#endif

94
common/rateest.cpp Normal file
View file

@ -0,0 +1,94 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "rateest.h"
#include <cstdlib>
void RateEst::SumQueue::reset() {
q.clear();
samples_ = usecs_ = 0;
}
void RateEst::SumQueue::push(const long samples, const usec_t usecs) {
q.push_back(pair_t(samples, usecs));
samples_ += samples;
usecs_ += usecs;
}
void RateEst::SumQueue::pop() {
const pair_t &f = q.front();
samples_ -= f.first;
usecs_ -= f.second;
q.pop_front();
}
static usec_t sampleUsecs(long samples, long rate) {
return static_cast<usec_t>((samples * 1000000.0f) / (rate ? rate : 1) + 0.5f);
}
static long limit(long est, const long reference) {
if (est > reference + (reference >> 6))
est = reference + (reference >> 6);
else if (est < reference - (reference >> 6))
est = reference - (reference >> 6);
return est;
}
void RateEst::init(long srate, long reference, const long maxSamplePeriod) {
maxPeriod = sampleUsecs(maxSamplePeriod, reference);
srate <<= UPSHIFT;
reference <<= UPSHIFT;
this->srate = limit(srate, reference);
last = 0;
this->reference = reference;
samples = ((this->srate >> UPSHIFT) * 12) << 5;
usecs = 12000000 << 5;
sumq.reset();
}
void RateEst::feed(long samplesIn, const usec_t now) {
usec_t usecsIn = now - last;
if (last && usecsIn < maxPeriod) {
sumq.push(samplesIn, usecsIn);
while ((usecsIn = sumq.usecs()) > 100000) {
samplesIn = sumq.samples();
sumq.pop();
if (std::abs(static_cast<long>(samplesIn * (1000000.0f * UP) / usecsIn) - reference) < reference >> 1) {
samples += (samplesIn - sumq.samples()) << 5;
usecs += (usecsIn - sumq.usecs()) << 5;
long est = static_cast<long>(samples * (1000000.0f * UP) / usecs + 0.5f);
est = limit((srate * 31 + est + 16) >> 5, reference);
srate = est;
if (usecs > 16000000 << 5) {
samples = (samples * 3 + 2) >> 2;
usecs = (usecs * 3 + 2) >> 2;
}
}
}
}
last = now;
}

66
common/rateest.h Normal file
View file

@ -0,0 +1,66 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef RATEEST_H
#define RATEEST_H
#include "usec.h"
#include <deque>
#include <utility>
class RateEst {
class SumQueue {
typedef std::pair<long, usec_t> pair_t;
typedef std::deque<pair_t> q_t;
q_t q;
long samples_;
usec_t usecs_;
public:
SumQueue() : samples_(0), usecs_(0) {}
void reset();
long samples() const { return samples_; }
usec_t usecs() const { return usecs_; }
void push(long samples, usec_t usecs);
void pop();
};
enum { UPSHIFT = 5 };
enum { UP = 1 << UPSHIFT };
long srate;
SumQueue sumq;
usec_t last;
usec_t usecs;
usec_t maxPeriod;
long reference;
long samples;
public:
explicit RateEst(long srate = 0) { init(srate); }
RateEst(long srate, long reference) { init(srate, reference); }
void init(long srate) { init(srate, srate); }
void init(long srate, long reference) { init(srate, reference, reference); }
void init(long srate, long reference, long maxSamplePeriod);
void reset() { last = 0; }
void feed(long samples, usec_t usecs = getusecs());
long result() const { return (srate + UP / 2) >> UPSHIFT; }
};
#endif

View file

@ -0,0 +1,73 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef RESAMPLER_H
#define RESAMPLER_H
#include <cstddef>
/** Interface to a Resampler. */
class Resampler {
long inRate_;
long outRate_;
protected:
void setRate(const long inRate, const long outRate) { inRate_ = inRate; outRate_ = outRate; }
Resampler() : inRate_(0), outRate_(0) {}
public:
/** Returns the sampling rate of the input that this resampler expects. */
long inRate() const { return inRate_; }
/** Returns the approximate sampling rate of the output. */
long outRate() const { return outRate_; }
/** Can be used to adjust the input and output sampling rates slightly with minimal disturbance in the output.
* Should only be used for slight changes or the quality could detoriate.
* It can for instance be useful to tweak the output rate slightly to synchronize production speed to playback
* speed when synchronizing video frame rate to refresh rate while playing back audio from the same source.
* This can reduce skipped or duplicated video frames (or avoid audio underruns if no frame skipping is done).
*
* @param inRate New input sampling rate.
* @param outRate Desired new output sampling rate.
*/
virtual void adjustRate(long inRate, long outRate) = 0;
/** Returns the exact ratio that this resampler is configured to use,
* such that the actual output sampling rate is (input rate) * mul / div.
* outRate() / inRate() is not necessarily equal to mul / div.
* Many resampler are intended for real-time purposes where it does not matter
* much whether the output sampling rate is 100% exact. Playback hardware is also slightly off.
*/
virtual void exactRatio(unsigned long &mul, unsigned long &div) const = 0;
/** Returns an upper bound on how many samples are produced for 'inlen' input samples.
* Can be used to calculate buffer sizes.
*/
virtual std::size_t maxOut(std::size_t inlen) const = 0;
/** Resamples the samples in 'in' and puts the resulting samples in 'out'.
*
* @param inlen The number of samples in 'in' to be resampled/consumed.
* @return The number of samples produced in 'out'.
*/
virtual std::size_t resample(short *out, const short *in, std::size_t inlen) = 0;
virtual ~Resampler() {}
};
#endif

View file

@ -0,0 +1,52 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef RESAMPLER_INFO_H
#define RESAMPLER_INFO_H
#include "resampler.h"
/** Used for creating instances of resamplers, and getting information on available resamplers.
* Currently creates resamplers that expect stereo samples. All 'numbers of samples' are in
* number of stereo samples. (This can be changed by adjusting the 'channels' enum in src/chainresampler.h
* to the number of desired channels.).
*/
struct ResamplerInfo {
/** Short character string description of the resampler. */
const char *desc;
/** Points to a function that can be used to create an instance of the resampler.
* @param inRate The input sampling rate.
* @param outRate The desired output sampling rate.
* @param periodSz The maximum number of input samples to resample at a time. That is the maximal inlen passed to Resampler::resample.
* @return Pointer to the created instance (on the heap). Caller must free this with the delete operator.
*/
Resampler* (*create)(long inRate, long outRate, std::size_t periodSz);
/** Returns the number of ResamplerInfos that can be gotten with get(). */
static std::size_t num() { return num_; }
/** Returns ResamplerInfo number n. Where n is less than num(). */
static const ResamplerInfo& get(std::size_t n) { return resamplers[n]; }
private:
static const ResamplerInfo resamplers[];
static const std::size_t num_;
};
#endif

View file

@ -0,0 +1,78 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef BLACKMANSINC_H
#define BLACKMANSINC_H
#include "convoluter.h"
#include "subresampler.h"
#include "makesinckernel.h"
#include "cic4.h"
#include "array.h"
#include <algorithm>
#include <cmath>
#include <cstddef>
template<unsigned channels, unsigned phases>
class BlackmanSinc : public SubResampler {
Array<short> const kernel;
PolyPhaseConvoluter<channels, phases> convoluter_;
static double blackmanWin(const long i, const long M) {
const double PI = 3.14159265358979323846;
return 0.42 - 0.5 * std::cos(2 * PI * i / M) + 0.08 * std::cos(4 * PI * i / M);
}
public:
enum { MUL = phases };
typedef Cic4<channels> Cic;
static float cicLimit() { return 4.7f; }
class RollOff {
static unsigned toTaps(const float rollOffWidth) {
const float widthTimesTaps = 4.5f;
return std::max(static_cast<unsigned>(std::ceil(widthTimesTaps / rollOffWidth)), 4u);
}
static float toFc(const float rollOffStart, const int taps) {
const float startToFcDeltaTimesTaps = 1.69f;
return startToFcDeltaTimesTaps / taps + rollOffStart;
}
public:
const unsigned taps;
const float fc;
RollOff(float rollOffStart, float rollOffWidth) : taps(toTaps(rollOffWidth)), fc(toFc(rollOffStart, taps)) {}
};
BlackmanSinc(unsigned div, unsigned phaseLen, double fc)
: kernel(phaseLen * phases), convoluter_(kernel, phaseLen, div)
{ makeSincKernel(kernel, phases, phaseLen, fc, blackmanWin, 1.0); }
BlackmanSinc(unsigned div, RollOff ro, double gain)
: kernel(ro.taps * phases), convoluter_(kernel, ro.taps, div)
{ makeSincKernel(kernel, phases, ro.taps, ro.fc, blackmanWin, gain);}
std::size_t resample(short *out, const short *in, std::size_t inlen) { return convoluter_.filter(out, in, inlen); }
void adjustDiv(unsigned div) { convoluters_.adjustDiv(div); }
unsigned mul() const { return MUL; }
unsigned div() const { return convoluter_.div(); }
};
#endif

View file

@ -0,0 +1,170 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "chainresampler.h"
float ChainResampler::get2ChainMidRatio(const float ratio, const float finalRollOffLen, const float midRollOffStartPlusEnd) {
return 0.5f * (std::sqrt(ratio * midRollOffStartPlusEnd * finalRollOffLen) + midRollOffStartPlusEnd);
}
float ChainResampler::get2ChainCost(const float ratio, const float finalRollOffLen, const float midRatio, const float midRollOffStartPlusEnd) {
const float midRollOffLen = midRatio * 2 - midRollOffStartPlusEnd;
return midRatio * ratio / midRollOffLen + get1ChainCost(midRatio, finalRollOffLen);
}
float ChainResampler::get3ChainRatio1(float ratio1, const float finalRollOffLen, const float ratio, const float midRollOffStartPlusEnd) {
for (unsigned n = 8; n--;) {
const float ratio2 = get3ChainRatio2(ratio1, finalRollOffLen, midRollOffStartPlusEnd);
ratio1 = 0.5f * (std::sqrt(ratio * midRollOffStartPlusEnd * (2 - midRollOffStartPlusEnd / ratio2)) + midRollOffStartPlusEnd);
}
return ratio1;
}
float ChainResampler::get3ChainCost(const float ratio, const float finalRollOffLen,
const float ratio1, const float ratio2, const float midRollOffStartPlusEnd) {
const float firstRollOffLen = ratio1 * 2 - midRollOffStartPlusEnd;
return ratio1 * ratio / firstRollOffLen + get2ChainCost(ratio1, finalRollOffLen, ratio2, midRollOffStartPlusEnd);
}
ChainResampler::ChainResampler()
: bigSinc(0), buffer2(0), periodSize(0)
{
}
void ChainResampler::downinitAddSincResamplers(double ratio, float const outRate,
CreateSinc const createBigSinc, CreateSinc const createSmallSinc,
unsigned const bigSincMul, unsigned const smallSincMul, double gain) {
// For high outRate: Start roll-off at 36000 Hz continue until outRate Hz, then wrap around back down to 40000 Hz.
const float outPeriod = 1.0f / outRate;
const float finalRollOffLen = std::max((outRate - 36000.0f + outRate - 40000.0f) * outPeriod, 0.2f);
{
const float midRollOffStart = std::min(36000.0f * outPeriod, 1.0f);
const float midRollOffEnd = std::min(40000.0f * outPeriod, 1.0f); // after wrap at folding freq.
const float midRollOffStartPlusEnd = midRollOffStart + midRollOffEnd;
int div_2c = static_cast<int>(ratio * smallSincMul / get2ChainMidRatio(ratio, finalRollOffLen, midRollOffStartPlusEnd) + 0.5f);
double ratio_2c = ratio * smallSincMul / div_2c;
float cost_2c = get2ChainCost(ratio, finalRollOffLen, ratio_2c, midRollOffStartPlusEnd);
if (cost_2c < get1ChainCost(ratio, finalRollOffLen)) {
const int div1_3c = static_cast<int>(
ratio * smallSincMul / get3ChainRatio1(ratio_2c, finalRollOffLen, ratio, midRollOffStartPlusEnd) + 0.5f);
const double ratio1_3c = ratio * smallSincMul / div1_3c;
const int div2_3c = static_cast<int>(
ratio1_3c * smallSincMul / get3ChainRatio2(ratio1_3c, finalRollOffLen, midRollOffStartPlusEnd) + 0.5f);
const double ratio2_3c = ratio1_3c * smallSincMul / div2_3c;
if (get3ChainCost(ratio, finalRollOffLen, ratio1_3c, ratio2_3c, midRollOffStartPlusEnd) < cost_2c) {
list.push_back(createSmallSinc(div1_3c, 0.5f * midRollOffStart / ratio,
(ratio1_3c - 0.5f * midRollOffStartPlusEnd) / ratio, gain));
ratio = ratio1_3c;
div_2c = div2_3c;
ratio_2c = ratio2_3c;
gain = 1.0;
}
list.push_back(createSmallSinc(div_2c, 0.5f * midRollOffStart / ratio,
(ratio_2c - 0.5f * midRollOffStartPlusEnd) / ratio, gain));
ratio = ratio_2c;
gain = 1.0;
}
}
list.push_back(bigSinc =
createBigSinc(static_cast<int>(bigSincMul * ratio + 0.5),
0.5f * (1.0f + std::max((outRate - 40000.0f) * outPeriod, 0.0f) - finalRollOffLen) / ratio,
0.5f * finalRollOffLen / ratio, gain));
}
std::size_t ChainResampler::reallocateBuffer() {
std::size_t bufSz[2] = { 0, 0 };
std::size_t inSz = periodSize;
int i = -1;
for (list_t::iterator it = list.begin(); it != list.end(); ++it) {
inSz = (inSz * (*it)->mul() - 1) / (*it)->div() + 1;
++i;
if (inSz > bufSz[i&1])
bufSz[i&1] = inSz;
}
if (inSz >= bufSz[i&1])
bufSz[i&1] = 0;
if (buffer.size() < (bufSz[0] + bufSz[1]) * channels)
buffer.reset((bufSz[0] + bufSz[1]) * channels);
buffer2 = bufSz[1] ? buffer + bufSz[0] * channels : 0;
return (maxOut_ = inSz);
}
void ChainResampler::adjustRate(const long inRate, const long outRate) {
unsigned long mul, div;
exactRatio(mul, div);
bigSinc->adjustDiv(static_cast<int>(static_cast<double>(inRate) * mul / (static_cast<double>(div / bigSinc->div()) * outRate) + 0.5));
reallocateBuffer();
setRate(inRate, outRate);
}
void ChainResampler::exactRatio(unsigned long &mul, unsigned long &div) const {
mul = 1;
div = 1;
for (list_t::const_iterator it = list.begin(); it != list.end(); ++it) {
mul *= (*it)->mul();
div *= (*it)->div();
}
}
std::size_t ChainResampler::resample(short *const out, const short *const in, std::size_t inlen) {
assert(inlen <= periodSize);
short *const buf = buffer != buffer2 ? buffer : out;
short *const buf2 = buffer2 ? buffer2 : out;
const short *inbuf = in;
short *outbuf = 0;
for (list_t::iterator it = list.begin(); it != list.end(); ++it) {
outbuf = ++list_t::iterator(it) == list.end() ? out : (inbuf == buf ? buf2 : buf);
inlen = (*it)->resample(outbuf, inbuf, inlen);
inbuf = outbuf;
}
return inlen;
}
void ChainResampler::uninit() {
buffer2 = 0;
buffer.reset();
periodSize = 0;
bigSinc = 0;
for (list_t::iterator it = list.begin(); it != list.end(); ++it)
delete *it;
list.clear();
}

View file

@ -0,0 +1,172 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef CHAINRESAMPLER_H
#define CHAINRESAMPLER_H
#include <cmath>
#include <cstdlib>
#include <cassert>
#include <cstddef>
#include <list>
#include "array.h"
#include "subresampler.h"
#include "../resampler.h"
#include "upsampler.h"
class ChainResampler : public Resampler {
typedef std::list<SubResampler*> list_t;
typedef SubResampler * (*CreateSinc)(unsigned div, float rollOffStart, float rollOffWidth, double gain);
list_t list;
SubResampler *bigSinc;
Array<short> buffer;
short *buffer2;
std::size_t periodSize;
std::size_t maxOut_;
static float get1ChainCost(float ratio, float finalRollOffLen) { return ratio / finalRollOffLen; }
static float get2ChainMidRatio(float ratio, float finalRollOffLen, float midRollOffStartPlusEnd);
static float get2ChainCost(float ratio, float finalRollOffLen, float midRatio, float midRollOffStartPlusEnd);
static float get3ChainRatio2(float ratio1, float finalRollOffLen, float midRollOffStartPlusEnd) {
return get2ChainMidRatio(ratio1, finalRollOffLen, midRollOffStartPlusEnd);
}
static float get3ChainRatio1(float ratio1, float finalRollOffLen, float ratio, float midRollOffStartPlusEnd);
static float get3ChainCost(float ratio, float finalRollOffLen, float ratio1, float ratio2, float midRollOffStartPlusEnd);
void downinitAddSincResamplers(double ratio, float outRate,
CreateSinc createBigSinc, CreateSinc createSmallSinc,
unsigned bigSincMul, unsigned smallSincMul, double gain);
template<class Sinc>
static SubResampler * createSinc(unsigned div, float rollOffStart, float rollOffWidth, double gain) {
return new Sinc(div, typename Sinc::RollOff(rollOffStart, rollOffWidth), gain);
}
template<template<unsigned,unsigned> class Sinc>
std::size_t downinit(long inRate, long outRate, std::size_t periodSize);
template<template<unsigned,unsigned> class Sinc>
std::size_t upinit(long inRate, long outRate, std::size_t periodSize);
std::size_t reallocateBuffer();
public:
enum { channels = 2 };
ChainResampler();
~ChainResampler() { uninit(); }
void adjustRate(long inRate, long outRate);
void exactRatio(unsigned long &mul, unsigned long &div) const;
template<template<unsigned,unsigned> class Sinc>
std::size_t init(long inRate, long outRate, std::size_t periodSize);
std::size_t maxOut(std::size_t /*inlen*/) const { return maxOut_; }
std::size_t resample(short *out, const short *in, std::size_t inlen);
void uninit();
};
template<template<unsigned,unsigned> class Sinc>
std::size_t ChainResampler::init(const long inRate, const long outRate, const std::size_t periodSize) {
setRate(inRate, outRate);
if (outRate > inRate)
return upinit<Sinc>(inRate, outRate, periodSize);
else
return downinit<Sinc>(inRate, outRate, periodSize);
}
template<template<unsigned,unsigned> class Sinc>
std::size_t ChainResampler::downinit(const long inRate, const long outRate, const std::size_t periodSize) {
typedef Sinc<channels,2048> BigSinc;
typedef Sinc<channels, 32> SmallSinc;
uninit();
this->periodSize = periodSize;
double ratio = static_cast<double>(inRate) / outRate;
double gain = 1.0;
while (ratio >= BigSinc::cicLimit() * 2) {
const int div = std::min<int>(static_cast<int>(ratio / BigSinc::cicLimit()), BigSinc::Cic::MAX_DIV);
list.push_back(new typename BigSinc::Cic(div));
ratio /= div;
gain *= 1.0 / BigSinc::Cic::gain(div);
}
downinitAddSincResamplers(ratio, outRate, createSinc<BigSinc>,
createSinc<SmallSinc>, BigSinc::MUL, SmallSinc::MUL, gain);
return reallocateBuffer();
}
template<template<unsigned,unsigned> class Sinc>
std::size_t ChainResampler::upinit(const long inRate, const long outRate, const std::size_t periodSize) {
typedef Sinc<channels,2048> BigSinc;
typedef Sinc<channels,32> SmallSinc;
uninit();
this->periodSize = periodSize;
double ratio = static_cast<double>(outRate) / inRate;
// Spectral images above 20 kHz assumed inaudible
{
const int div = outRate / std::max(inRate, 40000l);
if (div >= 2) {
list.push_front(new Upsampler<channels>(div));
ratio /= div;
}
}
const float rollOff = std::max((inRate - 36000.0f) / inRate, 0.2f);
/*{
int div_2c = get2ChainMidRatio(ratio, rollOff) * SmallSinc::MUL / ratio + 0.5f;
double ratio_2c = ratio * div_2c / SmallSinc::MUL;
float cost_2c = get2ChainCost(ratio, rollOff, ratio_2c);
if (cost_2c < get1ChainCost(ratio, rollOff)) {
const int div1_3c = get3ChainRatio1(ratio_2c, rollOff, ratio) * SmallSinc::MUL / ratio + 0.5f;
const double ratio1_3c = ratio * div1_3c / SmallSinc::MUL;
const int div2_3c = get3ChainRatio2(ratio1_3c, rollOff) * SmallSinc::MUL / ratio1_3c + 0.5f;
const double ratio2_3c = ratio1_3c * div2_3c / SmallSinc::MUL;
if (get3ChainCost(ratio, rollOff, ratio1_3c, ratio2_3c) < cost_2c) {
list.push_front(new SmallSinc(div1_3c, typename SmallSinc::RollOff(0.5f / ratio1_3c, (ratio1_3c - 1) / ratio1_3c), 1.0));
ratio = ratio1_3c;
div_2c = div2_3c;
ratio_2c = ratio2_3c;
}
list.push_front(new SmallSinc(div_2c, typename SmallSinc::RollOff(0.5f / ratio_2c, (ratio_2c - 1) / ratio_2c), 1.0));
ratio = ratio_2c;
}
}*/
list.push_front(bigSinc = new BigSinc(static_cast<int>(BigSinc::MUL / ratio + 0.5),
typename BigSinc::RollOff(0.5f * (1 - rollOff), 0.5f * rollOff), 1.0));
return reallocateBuffer();
}
#endif

244
common/resample/src/cic2.h Normal file
View file

@ -0,0 +1,244 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef CIC2_H
#define CIC2_H
#include "subresampler.h"
#include "rshift16_round.h"
template<unsigned channels>
class Cic2Core {
// enum { BUFLEN = 64 };
// unsigned long buf[BUFLEN];
unsigned long sum1;
unsigned long sum2;
unsigned long prev1;
// unsigned long prev2;
unsigned div_;
unsigned nextdivn;
// unsigned bufpos;
// trouble if div is too large, may be better to only support power of 2 div
static long mulForDiv(unsigned div) { return 0x10000 / (div * div); }
public:
explicit Cic2Core(const unsigned div = 2) { reset(div); }
unsigned div() const { return div_; }
std::size_t filter(short *out, const short *in, std::size_t inlen);
void reset(unsigned div);
static double gain(unsigned div) { return rshift16_round(-32768l * (div * div) * mulForDiv(div)) / -32768.0; }
};
template<unsigned channels>
void Cic2Core<channels>::reset(const unsigned div) {
sum2 = sum1 = 0;
/*prev2 = */prev1 = 0;
this->div_ = div;
nextdivn = div;
// bufpos = div - 1;
}
template<unsigned channels>
std::size_t Cic2Core<channels>::filter(short *out, const short *const in, std::size_t inlen) {
// const std::size_t produced = (inlen + div_ - (bufpos + 1)) / div_;
const std::size_t produced = (inlen + div_ - nextdivn) / div_;
const long mul = mulForDiv(div_);
const short *s = in;
/*unsigned long sm1 = sum1;
unsigned long sm2 = sum2;
while (inlen >> 2) {
unsigned n = (inlen < BUFLEN ? inlen >> 2 : BUFLEN >> 2);
const unsigned end = n * 4;
unsigned i = 0;
do {
unsigned long s1 = sm1 += static_cast<long>(*s);
s += channels;
sm1 += static_cast<long>(*s);
s += channels;
buf[i++] = sm2 += s1;
buf[i++] = sm2 += sm1;
s1 = sm1 += static_cast<long>(*s);
s += channels;
sm1 += static_cast<long>(*s);
s += channels;
buf[i++] = sm2 += s1;
buf[i++] = sm2 += sm1;
} while (--n);
while (bufpos < end) {
const unsigned long out2 = buf[bufpos] - prev2;
prev2 = buf[bufpos];
bufpos += div_;
*out = rshift16_round(static_cast<long>(out2 - prev1) * mul);
prev1 = out2;
out += channels;
}
bufpos -= end;
inlen -= end;
}
if (inlen) {
unsigned n = inlen;
unsigned i = 0;
do {
sm1 += static_cast<long>(*s);
s += channels;
buf[i++] = sm2 += sm1;
} while (--n);
while (bufpos < inlen) {
const unsigned long out2 = buf[bufpos] - prev2;
prev2 = buf[bufpos];
bufpos += div_;
*out = rshift16_round(static_cast<long>(out2 - prev1) * mul);
prev1 = out2;
out += channels;
}
bufpos -= inlen;
}
sum1 = sm1;
sum2 = sm2;*/
unsigned long sm1 = sum1;
unsigned long sm2 = sum2;
if (inlen >= nextdivn) {
{
unsigned divn = nextdivn;
do {
sm1 += static_cast<long>(*s);
s += channels;
sm2 += sm1;
} while (--divn);
const unsigned long out2 = sm2;
sm2 = 0;
*out = rshift16_round(static_cast<long>(out2 - prev1) * mul);
prev1 = out2;
out += channels;
}
if (div_ & 1) {
std::size_t n = produced;
while (--n) {
unsigned divn = div_ >> 1;
do {
sm1 += static_cast<long>(*s);
s += channels;
sm2 += sm1;
sm1 += static_cast<long>(*s);
s += channels;
sm2 += sm1;
} while (--divn);
sm1 += static_cast<long>(*s);
s += channels;
sm2 += sm1;
*out = rshift16_round(static_cast<long>(sm2 - prev1) * mul);
out += channels;
prev1 = sm2;
sm2 = 0;
}
} else {
std::size_t n = produced;
while (--n) {
unsigned divn = div_ >> 1;
do {
sm1 += static_cast<long>(*s);
s += channels;
sm2 += sm1;
sm1 += static_cast<long>(*s);
s += channels;
sm2 += sm1;
} while (--divn);
*out = rshift16_round(static_cast<long>(sm2 - prev1) * mul);
out += channels;
prev1 = sm2;
sm2 = 0;
}
}
nextdivn = div_;
}
{
unsigned divn = (in + inlen * channels - s) / channels;
nextdivn -= divn;
while (divn--) {
sm1 += static_cast<long>(*s);
s += channels;
sm2 += sm1;
}
}
sum1 = sm1;
sum2 = sm2;
return produced;
}
template<unsigned channels>
class Cic2 : public SubResampler {
Cic2Core<channels> cics[channels];
public:
enum { MAX_DIV = 64 };
explicit Cic2(unsigned div);
std::size_t resample(short *out, const short *in, std::size_t inlen);
unsigned mul() const { return 1; }
unsigned div() const { return cics[0].div(); }
static double gain(unsigned div) { return Cic2Core<channels>::gain(div); }
};
template<unsigned channels>
Cic2<channels>::Cic2(const unsigned div) {
for (unsigned i = 0; i < channels; ++i)
cics[i].reset(div);
}
template<unsigned channels>
std::size_t Cic2<channels>::resample(short *const out, const short *const in, const std::size_t inlen) {
std::size_t samplesOut;
for (unsigned i = 0; i < channels; ++i) {
samplesOut = cics[i].filter(out + i, in + i, inlen);
}
return samplesOut;
}
#endif

380
common/resample/src/cic3.h Normal file
View file

@ -0,0 +1,380 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef CIC3_H
#define CIC3_H
#include "subresampler.h"
#include "rshift16_round.h"
template<unsigned channels>
class Cic3Core {
// enum { BUFLEN = 64 };
// unsigned long buf[BUFLEN];
unsigned long sum1;
unsigned long sum2;
unsigned long sum3;
unsigned long prev1;
unsigned long prev2;
unsigned div_;
unsigned nextdivn;
// unsigned bufpos;
// trouble if div is too large, may be better to only support power of 2 div
static long mulForDiv(unsigned div) { return 0x10000 / (div * div * div); }
public:
explicit Cic3Core(const unsigned div = 1) { reset(div); }
unsigned div() const { return div_; }
std::size_t filter(short *out, const short *in, std::size_t inlen);
void reset(unsigned div);
static double gain(unsigned div) { return rshift16_round(-32768l * (div * div * div) * mulForDiv(div)) / -32768.0; }
};
template<unsigned channels>
void Cic3Core<channels>::reset(const unsigned div) {
sum3 = sum2 = sum1 = 0;
prev2 = prev1 = 0;
this->div_ = div;
nextdivn = div;
// bufpos = div - 1;
}
template<unsigned channels>
std::size_t Cic3Core<channels>::filter(short *out, const short *const in, std::size_t inlen) {
// const std::size_t produced = (inlen + div_ - (bufpos + 1)) / div_;
const std::size_t produced = (inlen + div_ - nextdivn) / div_;
const short *s = in;
/*unsigned long sm1 = sum1;
unsigned long sm2 = sum2;
unsigned long sm3 = sum3;
while (inlen >> 1) {
unsigned n = (inlen < BUFLEN ? inlen >> 1 : BUFLEN >> 1);
const unsigned end = n * 2;
unsigned i = 0;
do {
unsigned long s1 = sm1 += static_cast<long>(*s);
s += channels;
sm1 += static_cast<long>(*s);
s += channels;
unsigned long s2 = sm2 += s1;
sm2 += sm1;
buf[i++] = sm3 += s2;
buf[i++] = sm3 += sm2;
} while (--n);
while (bufpos < end) {
const unsigned long out3 = buf[bufpos] - prev3;
prev3 = buf[bufpos];
bufpos += div_;
const unsigned long out2 = out3 - prev2;
prev2 = out3;
*out = rshift16_round(static_cast<long>(out2 - prev1) * mul);
prev1 = out2;
out += channels;
}
bufpos -= end;
inlen -= end;
}
if (inlen) {
unsigned n = inlen;
unsigned i = 0;
do {
sm1 += static_cast<long>(*s);
s += channels;
sm2 += sm1;
buf[i++] = sm3 += sm2;
} while (--n);
while (bufpos < inlen) {
const unsigned long out3 = buf[bufpos] - prev3;
prev3 = buf[bufpos];
bufpos += div_;
const unsigned long out2 = out3 - prev2;
prev2 = out3;
*out = rshift16_round(static_cast<long>(out2 - prev1) * mul);
prev1 = out2;
out += channels;
}
bufpos -= inlen;
}
sum1 = sm1;
sum2 = sm2;
sum3 = sm3;*/
unsigned long sm1 = sum1;
unsigned long sm2 = sum2;
unsigned long sm3 = sum3;
if (inlen >= nextdivn) {
const long mul = mulForDiv(div_);
unsigned divn = nextdivn;
std::size_t n = produced;
do {
do {
sm1 += static_cast<long>(*s);
sm2 += sm1;
sm3 += sm2;
s += channels;
} while (--divn);
const unsigned long out2 = sm3 - prev2;
prev2 = sm3;
*out = rshift16_round(static_cast<long>(out2 - prev1) * mul);
prev1 = out2;
out += channels;
divn = div_;
sm3 = 0;
} while (--n);
nextdivn = div_;
}
{
unsigned divn = (in + inlen * channels - s) / channels;
nextdivn -= divn;
while (divn--) {
sm1 += static_cast<long>(*s);
sm2 += sm1;
sm3 += sm2;
s += channels;
}
}
sum1 = sm1;
sum2 = sm2;
sum3 = sm3;
return produced;
}
/*template<unsigned channels>
class Cic3EvenOddCore {
unsigned long sum1;
unsigned long sum2;
unsigned long sum3;
unsigned long prev1;
unsigned long prev2;
unsigned long prev3;
unsigned div_;
unsigned nextdivn;
static int getMul(unsigned div) {
return 0x10000 / (div * div * div); // trouble if div is too large, may be better to only support power of 2 div
}
void filterEven(short *out, const short *s, std::size_t n);
void filterOdd(short *out, const short *s, std::size_t n);
public:
Cic3EvenOddCore(const unsigned div = 2) {
reset(div);
}
unsigned div() const { return div_; }
std::size_t filter(short *out, const short *in, std::size_t inlen);
void reset(unsigned div);
};
template<unsigned channels>
void Cic3EvenOddCore<channels>::reset(const unsigned div) {
sum3 = sum2 = sum1 = 0;
prev3 = prev2 = prev1 = 0;
this->div_ = div;
nextdivn = div;
}
template<unsigned channels>
void Cic3EvenOddCore<channels>::filterEven(short *out, const short *s, std::size_t n) {
const int mul = getMul(div_);
unsigned long sm1 = sum1;
unsigned long sm2 = sum2;
unsigned long sm3 = sum3;
while (n--) {
{
unsigned sn = div_ >> 1;
do {
unsigned long s1 = sm1 += static_cast<long>(*s);
s += channels;
sm1 += static_cast<long>(*s);
s += channels;
unsigned long s2 = sm2 += s1;
sm2 += sm1;
sm3 += s2;
sm3 += sm2;
} while (--sn);
}
const unsigned long out3 = sm3 - prev3;
prev3 = sm3;
const unsigned long out2 = out3 - prev2;
prev2 = out3;
*out = rshift16_round(static_cast<long>(out2 - prev1) * mul);
prev1 = out2;
out += channels;
}
sum1 = sm1;
sum2 = sm2;
sum3 = sm3;
}
template<unsigned channels>
void Cic3EvenOddCore<channels>::filterOdd(short *out, const short *s, std::size_t n) {
const int mul = getMul(div_);
unsigned long sm1 = sum1;
unsigned long sm2 = sum2;
unsigned long sm3 = sum3;
while (n--) {
{
unsigned sn = div_ >> 1;
do {
unsigned long s1 = sm1 += static_cast<long>(*s);
s += channels;
sm1 += static_cast<long>(*s);
s += channels;
unsigned long s2 = sm2 += s1;
sm2 += sm1;
sm3 += s2;
sm3 += sm2;
} while (--sn);
}
sm1 += static_cast<long>(*s);
s += channels;
sm2 += sm1;
sm3 += sm2;
const unsigned long out3 = sm3 - prev3;
prev3 = sm3;
const unsigned long out2 = out3 - prev2;
prev2 = out3;
*out = rshift16_round(static_cast<long>(out2 - prev1) * mul);
prev1 = out2;
out += channels;
}
sum1 = sm1;
sum2 = sm2;
sum3 = sm3;
}
template<unsigned channels>
std::size_t Cic3EvenOddCore<channels>::filter(short *out, const short *const in, std::size_t inlen) {
short *const outStart = out;
const short *s = in;
if (inlen >= nextdivn) {
{
{
unsigned divn = nextdivn;
do {
sum1 += static_cast<long>(*s);
s += channels;
sum2 += sum1;
sum3 += sum2;
} while (--divn);
}
const unsigned long out3 = sum3 - prev3;
prev3 = sum3;
const unsigned long out2 = out3 - prev2;
prev2 = out3;
*out = rshift16_round(static_cast<long>(out2 - prev1) * getMul(div_));
prev1 = out2;
out += channels;
}
std::size_t n = (inlen - nextdivn) / div_;
if (div_ & 1)
filterOdd(out, s, n);
else
filterEven(out, s, n);
s += n * div_ * channels;
out += n * channels;
nextdivn = div_;
}
{
unsigned divn = inlen - (s - in) / channels;
nextdivn -= divn;
while (divn--) {
sum1 += static_cast<long>(*s);
s += channels;
sum2 += sum1;
sum3 += sum2;
}
}
return (out - outStart) / channels;
}*/
template<unsigned channels>
class Cic3 : public SubResampler {
Cic3Core<channels> cics[channels];
public:
enum { MAX_DIV = 23 };
explicit Cic3(unsigned div);
std::size_t resample(short *out, const short *in, std::size_t inlen);
unsigned mul() const { return 1; }
unsigned div() const { return cics[0].div(); }
static double gain(unsigned div) { return Cic3Core<channels>::gain(div); }
};
template<unsigned channels>
Cic3<channels>::Cic3(const unsigned div) {
for (unsigned i = 0; i < channels; ++i)
cics[i].reset(div);
}
template<unsigned channels>
std::size_t Cic3<channels>::resample(short *const out, const short *const in, const std::size_t inlen) {
std::size_t samplesOut;
for (unsigned i = 0; i < channels; ++i) {
samplesOut = cics[i].filter(out + i, in + i, inlen);
}
return samplesOut;
}
#endif

246
common/resample/src/cic4.h Normal file
View file

@ -0,0 +1,246 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef CIC4_H
#define CIC4_H
#include "subresampler.h"
#include "rshift16_round.h"
template<unsigned channels>
class Cic4Core {
enum { BUFLEN = 64 };
unsigned long buf[BUFLEN];
unsigned long sum1;
unsigned long sum2;
unsigned long sum3;
unsigned long sum4;
unsigned long prev1;
unsigned long prev2;
unsigned long prev3;
unsigned long prev4;
unsigned div_;
// unsigned nextdivn;
unsigned bufpos;
// trouble if div is too large, may be better to only support power of 2 div
static long mulForDiv(unsigned div) { return 0x10000 / (div * div * div * div); }
public:
explicit Cic4Core(const unsigned div = 1) { reset(div); }
unsigned div() const { return div_; }
std::size_t filter(short *out, const short *in, std::size_t inlen);
void reset(unsigned div);
static double gain(unsigned div) { return rshift16_round(-32768l * (div * div * div * div) * mulForDiv(div)) / -32768.0; }
};
template<unsigned channels>
void Cic4Core<channels>::reset(const unsigned div) {
sum4 = sum3 = sum2 = sum1 = 0;
prev4 = prev3 = prev2 = prev1 = 0;
this->div_ = div;
// nextdivn = div;
bufpos = div - 1;
}
template<unsigned channels>
std::size_t Cic4Core<channels>::filter(short *out, const short *const in, std::size_t inlen) {
const std::size_t produced = (inlen + div_ - (bufpos + 1)) / div_;
// const std::size_t produced = (inlen + div_ - nextdivn) / div_;
const long mul = mulForDiv(div_);
const short *s = in;
unsigned long sm1 = sum1;
unsigned long sm2 = sum2;
unsigned long sm3 = sum3;
unsigned long sm4 = sum4;
unsigned long prv1 = prev1;
unsigned long prv2 = prev2;
unsigned long prv3 = prev3;
unsigned long prv4 = prev4;
while (inlen >> 2) {
const unsigned end = inlen < BUFLEN ? inlen & ~3 : BUFLEN & ~3;
unsigned long *b = buf;
unsigned n = end;
do {
unsigned long s1 = sm1 += static_cast<long>(s[0 * channels]);
sm1 += static_cast<long>(s[1 * channels]);
unsigned long s2 = sm2 += s1;
sm2 += sm1;
unsigned long s3 = sm3 += s2;
sm3 += sm2;
b[0] = sm4 += s3;
b[1] = sm4 += sm3;
s1 = sm1 += static_cast<long>(s[2 * channels]);
sm1 += static_cast<long>(s[3 * channels]);
s2 = sm2 += s1;
sm2 += sm1;
s3 = sm3 += s2;
sm3 += sm2;
b[2] = sm4 += s3;
b[3] = sm4 += sm3;
s += 4 * channels;
b += 4;
} while (n -= 4);
while (bufpos < end) {
const unsigned long out4 = buf[bufpos] - prv4;
prv4 = buf[bufpos];
bufpos += div_;
const unsigned long out3 = out4 - prv3;
prv3 = out4;
const unsigned long out2 = out3 - prv2;
prv2 = out3;
*out = rshift16_round(static_cast<long>(out2 - prv1) * mul);
prv1 = out2;
out += channels;
}
bufpos -= end;
inlen -= end;
}
if (inlen) {
unsigned n = inlen;
unsigned i = 0;
do {
sm1 += static_cast<long>(*s);
s += channels;
sm2 += sm1;
sm3 += sm2;
buf[i++] = sm4 += sm3;
} while (--n);
while (bufpos < inlen) {
const unsigned long out4 = buf[bufpos] - prv4;
prv4 = buf[bufpos];
bufpos += div_;
const unsigned long out3 = out4 - prv3;
prv3 = out4;
const unsigned long out2 = out3 - prv2;
prv2 = out3;
*out = rshift16_round(static_cast<long>(out2 - prv1) * mul);
prv1 = out2;
out += channels;
}
bufpos -= inlen;
}
sum1 = sm1;
sum2 = sm2;
sum3 = sm3;
sum4 = sm4;
prev1 = prv1;
prev2 = prv2;
prev3 = prv3;
prev4 = prv4;
/*unsigned long sm1 = sum1;
unsigned long sm2 = sum2;
unsigned long sm3 = sum3;
unsigned long sm4 = sum4;
if (produced) {
unsigned divn = nextdivn;
std::size_t n = produced;
do {
do {
sm1 += static_cast<long>(*s);
s += channels;
sm2 += sm1;
sm3 += sm2;
sm4 += sm3;
} while (--divn);
const unsigned long out4 = sm4 - prev4;
prev4 = sm4;
const unsigned long out3 = out4 - prev3;
prev3 = out4;
const unsigned long out2 = out3 - prev2;
prev2 = out3;
*out = rshift16_round(static_cast<long>(out2 - prev1) * mul);
prev1 = out2;
out += channels;
divn = div_;
} while (--n);
nextdivn = div_;
}
{
unsigned divn = (in + inlen * channels - s) / channels;
nextdivn -= divn;
while (divn--) {
sm1 += static_cast<long>(*s);
s += channels;
sm2 += sm1;
sm3 += sm2;
sm4 += sm3;
}
}
sum1 = sm1;
sum2 = sm2;
sum3 = sm3;
sum4 = sm4;*/
return produced;
}
template<unsigned channels>
class Cic4 : public SubResampler {
Cic4Core<channels> cics[channels];
public:
enum { MAX_DIV = 13 };
explicit Cic4(unsigned div);
std::size_t resample(short *out, const short *in, std::size_t inlen);
unsigned mul() const { return 1; }
unsigned div() const { return cics[0].div(); }
static double gain(unsigned div) { return Cic4Core<channels>::gain(div); }
};
template<unsigned channels>
Cic4<channels>::Cic4(const unsigned div) {
for (unsigned i = 0; i < channels; ++i)
cics[i].reset(div);
}
template<unsigned channels>
std::size_t Cic4<channels>::resample(short *const out, const short *const in, const std::size_t inlen) {
std::size_t samplesOut;
for (unsigned i = 0; i < channels; ++i) {
samplesOut = cics[i].filter(out + i, in + i, inlen);
}
return samplesOut;
}
#endif

View file

@ -0,0 +1,167 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef CONVOLUTER_H
#define CONVOLUTER_H
#include <algorithm>
#include <cstring>
#include "array.h"
#include "rshift16_round.h"
template<int channels, unsigned phases>
class PolyPhaseConvoluter {
const short *const kernel;
Array<short> const prevbuf;
unsigned div_;
unsigned x_;
public:
PolyPhaseConvoluter(const short *kernel, unsigned phaseLen, unsigned div);
std::size_t filter(short *out, const short *in, std::size_t inlen);
void adjustDiv(const unsigned div) { div_ = div; }
unsigned div() const { return div_; }
};
template<int channels, unsigned phases>
PolyPhaseConvoluter<channels, phases>::PolyPhaseConvoluter(
const short *const kernel, const unsigned phaseLen, const unsigned div)
: kernel(kernel), prevbuf(phaseLen * channels), div_(div), x_(0)
{
std::fill(prevbuf.get(), prevbuf.get() + prevbuf.size(), 0);
}
template<int channels, unsigned phases>
std::size_t PolyPhaseConvoluter<channels, phases>::filter(short *out, const short *const in, std::size_t inlen) {
if (!kernel || !inlen)
return 0;
// The gist of what happens here is given by the commented pseudo-code below.
// Note that the order of the kernel elements has been changed for efficiency in the real implementation.
/*for (std::size_t x = 0; x < inlen + M; ++x) {
const int end = x < inlen ? M + 1 : inlen + M - x;
int j = x < M ? M - x : 0;
j += (phases - (x - M + j) % phases) % phases; // adjust j so we don't start on a virtual 0 sample
for (; j < end; j += phases) {
buffer[x] += kernel[j] * start[(x - M + j) / phases];
}
}*/
// Slightly more optimized version.
/*for (std::size_t x = 0; x < inlen + M; ++x) {
const int end = x < inlen ? M + 1 : inlen + M - x;
int j = x < M ? M - x : 0;
j += (phases - (x - M + j) % phases) % phases; // adjust j so we don't start on a virtual 0 sample
const short *k = kernel + (j % phases) * phaseLen + j / phases;
const short *s = start + (x - M + j) / phases;
int n = ((end - j) + phases - 1) / phases;
do {
buffer[x] += *k++ * *s++;
} while (--n);
}*/
const std::size_t phaseLen = prevbuf.size() / channels;
const std::size_t M = phaseLen * phases - 1;
inlen *= phases;
std::size_t x = x_;
for (; x < (M < inlen ? M : inlen); x += div_) {
for (int c = 0; c < channels; ++c) {
const short *k = kernel + ((x + 1) % phases) * phaseLen; // adjust phase so we don't start on a virtual 0 sample
const short *s = prevbuf + phaseLen * channels + c;
long acc = 0;
unsigned n = phaseLen * channels - (x / phases + 1) * channels;
for (; n; n -= channels)
acc += *k++ * *(s-n);
n = (x / phases + 1) * channels;
s = in + n + c;
do {
acc += *k++ * *(s-n);
} while (n -= channels);
*out++ = rshift16_round(acc);
}
}
// We could easily get rid of the division and modulus here by updating the
// k and s pointers incrementally. However, we currently only use powers of 2
// and we would end up referencing more variables which often compiles to bad
// code on x86, which is why I'm also hesistant to get rid of the template arguments.
for (; x < inlen; x += div_) {
for (int c = 0; c < channels-1; c += 2) {
const short *k = kernel + ((x + 1) % phases) * phaseLen; // adjust phase so we don't start on a virtual 0 sample
const short *const s = in + (x / phases + 1) * channels + c;
long accl = 0, accr = 0;
int i = -static_cast<int>(phaseLen * channels);
do {
accl += *k * s[i ];
accr += *k * s[i+1];
++k;
} while (i += channels);
out[0] = rshift16_round(accl);
out[1] = rshift16_round(accr);
out += 2;
}
if (channels & 1) {
const short *k = kernel + ((x + 1) % phases) * phaseLen; // adjust phase so we don't start on a virtual 0 sample
const short *const s = in + (x / phases + 1) * channels + channels-1;
long acc = 0;
int i = -static_cast<int>(phaseLen * channels);
do {
acc += *k++ * s[i];
} while (i += channels);
*out++ = rshift16_round(acc);
}
}
const std::size_t produced = (x - x_) / div_;
x_ = x - inlen;
inlen /= phases;
{
short *p = prevbuf;
const short *s = in + (inlen - phaseLen) * channels;
unsigned n = phaseLen;
if (inlen < phaseLen) {
const unsigned i = phaseLen - inlen;
std::memmove(p, p + inlen * channels, i * channels * sizeof *p);
p += i * channels;
n -= i;
s = in;
}
std::memcpy(p, s, n * channels * sizeof *p);
}
return produced;
}
#endif

View file

@ -0,0 +1,78 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef HAMMINGSINC_H
#define HAMMINGSINC_H
#include "convoluter.h"
#include "subresampler.h"
#include "makesinckernel.h"
#include "cic3.h"
#include "array.h"
#include <algorithm>
#include <cmath>
#include <cstddef>
template<unsigned channels, unsigned phases>
class HammingSinc : public SubResampler {
Array<short> const kernel;
PolyPhaseConvoluter<channels, phases> convoluter_;
static double hammingWin(const long i, const long M) {
const double PI = 3.14159265358979323846;
return 0.53836 - 0.46164 * std::cos(2 * PI * i / M);
}
public:
enum { MUL = phases };
typedef Cic3<channels> Cic;
static float cicLimit() { return 4.2f; }
class RollOff {
static unsigned toTaps(const float rollOffWidth) {
const float widthTimesTaps = 3.0f;
return std::max(static_cast<unsigned>(std::ceil(widthTimesTaps / rollOffWidth)), 4u);
}
static float toFc(const float rollOffStart, const int taps) {
const float startToFcDeltaTimesTaps = 1.27f;
return startToFcDeltaTimesTaps / taps + rollOffStart;
}
public:
const unsigned taps;
const float fc;
RollOff(float rollOffStart, float rollOffWidth) : taps(toTaps(rollOffWidth)), fc(toFc(rollOffStart, taps)) {}
};
HammingSinc(unsigned div, unsigned phaseLen, double fc)
: kernel(phaseLen * phases), convoluter_(kernel, phaseLen, div)
{ makeSincKernel(kernel, phases, phaseLen, fc, hammingWin, 1.0); }
HammingSinc(unsigned div, RollOff ro, double gain)
: kernel(ro.taps * phases), convoluter_(kernel, ro.taps, div)
{ makeSincKernel(kernel, phases, ro.taps, ro.fc, hammingWin, gain);}
std::size_t resample(short *out, const short *in, std::size_t inlen) { return convoluter_.filter(out, in, inlen); }
void adjustDiv(unsigned div) { convoluter_.adjustDiv(div); }
unsigned mul() const { return MUL; }
unsigned div() const { return convoluter_.div(); }
};
#endif

36
common/resample/src/i0.cpp Executable file
View file

@ -0,0 +1,36 @@
/***************************************************************************
* Copyright (C) 2009 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "i0.h"
double i0(double x) {
double sum = 1.0;
double xpm_dmfac = 1.0;
double m = 1.0;
unsigned n = 16;
x = 0.25 * x * x;
do {
xpm_dmfac *= x / (m*m);
sum += xpm_dmfac;
m += 1.0;
} while (--n);
return sum;
}

24
common/resample/src/i0.h Executable file
View file

@ -0,0 +1,24 @@
/***************************************************************************
* Copyright (C) 2009 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef I0_H
#define I0_H
double i0(double x);
#endif

View file

@ -0,0 +1,32 @@
/***************************************************************************
* Copyright (C) 2008-2009 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "kaiser50sinc.h"
#include "i0.h"
#include <cmath>
double kaiser50SincWin(const long n, const long M) {
const double beta = 4.62;
static const double i0beta_rec = 1.0 / i0(beta);
double x = static_cast<double>(n * 2) / M - 1.0;
x = x * x;
x = beta * std::sqrt(1.0 - x);
return i0(x) * i0beta_rec;
}

View file

@ -0,0 +1,75 @@
/***************************************************************************
* Copyright (C) 2008-2009 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef KAISER50SINC_H
#define KAISER50SINC_H
#include "convoluter.h"
#include "subresampler.h"
#include "makesinckernel.h"
#include "cic3.h"
#include "array.h"
#include <algorithm>
#include <cmath>
#include <cstddef>
double kaiser50SincWin(long n, long M);
template<unsigned channels, unsigned phases>
class Kaiser50Sinc : public SubResampler {
Array<short> const kernel;
PolyPhaseConvoluter<channels, phases> convoluter_;
public:
enum { MUL = phases };
typedef Cic3<channels> Cic;
static float cicLimit() { return 4.2f; }
class RollOff {
static unsigned toTaps(const float rollOffWidth) {
const float widthTimesTaps = 2.715f;
return std::max(static_cast<unsigned>(std::ceil(widthTimesTaps / rollOffWidth)), 4u);
}
static float toFc(const float rollOffStart, const int taps) {
const float startToFcDeltaTimesTaps = 1.2f;
return startToFcDeltaTimesTaps / taps + rollOffStart;
}
public:
const unsigned taps;
const float fc;
RollOff(float rollOffStart, float rollOffWidth) : taps(toTaps(rollOffWidth)), fc(toFc(rollOffStart, taps)) {}
};
Kaiser50Sinc(unsigned div, unsigned phaseLen, double fc)
: kernel(phaseLen * phases), convoluter_(kernel, phaseLen, div)
{ makeSincKernel(kernel, phases, phaseLen, fc, kaiser50SincWin, 1.0); }
Kaiser50Sinc(unsigned div, RollOff ro, double gain)
: kernel(ro.taps * phases), convoluter_(kernel, ro.taps, div)
{ makeSincKernel(kernel, phases, ro.taps, ro.fc, kaiser50SincWin, gain);}
std::size_t resample(short *out, const short *in, std::size_t inlen) { return convoluter_.filter(out, in, inlen); }
void adjustDiv(unsigned div) { convoluter_.adjustDiv(div); }
unsigned mul() const { return MUL; }
unsigned div() const { return convoluter_.div(); }
};
#endif

View file

@ -0,0 +1,32 @@
/***************************************************************************
* Copyright (C) 2008-2009 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "kaiser70sinc.h"
#include "i0.h"
#include <cmath>
double kaiser70SincWin(const long n, const long M) {
const double beta = 6.9;
static const double i0beta_rec = 1.0 / i0(beta);
double x = static_cast<double>(n * 2) / M - 1.0;
x = x * x;
x = beta * std::sqrt(1.0 - x);
return i0(x) * i0beta_rec;
}

View file

@ -0,0 +1,75 @@
/***************************************************************************
* Copyright (C) 2008-2009 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef KAISER70SINC_H
#define KAISER70SINC_H
#include "convoluter.h"
#include "subresampler.h"
#include "makesinckernel.h"
#include "cic4.h"
#include "array.h"
#include <algorithm>
#include <cmath>
#include <cstddef>
double kaiser70SincWin(long n, long M);
template<unsigned channels, unsigned phases>
class Kaiser70Sinc : public SubResampler {
Array<short> const kernel;
PolyPhaseConvoluter<channels, phases> convoluter_;
public:
enum { MUL = phases };
typedef Cic4<channels> Cic;
static float cicLimit() { return 4.7f; }
class RollOff {
static unsigned toTaps(const float rollOffWidth) {
const float widthTimesTaps = 3.75f;
return std::max(static_cast<unsigned>(std::ceil(widthTimesTaps / rollOffWidth)), 4u);
}
static float toFc(const float rollOffStart, const int taps) {
const float startToFcDeltaTimesTaps = 1.5f;
return startToFcDeltaTimesTaps / taps + rollOffStart;
}
public:
const unsigned taps;
const float fc;
RollOff(float rollOffStart, float rollOffWidth) : taps(toTaps(rollOffWidth)), fc(toFc(rollOffStart, taps)) {}
};
Kaiser70Sinc(unsigned div, unsigned phaseLen, double fc)
: kernel(phaseLen * phases), convoluter_(kernel, phaseLen, div)
{ makeSincKernel(kernel, phases, phaseLen, fc, kaiser70SincWin, 1.0); }
Kaiser70Sinc(unsigned div, RollOff ro, double gain)
: kernel(ro.taps * phases), convoluter_(kernel, ro.taps, div)
{ makeSincKernel(kernel, phases, ro.taps, ro.fc, kaiser70SincWin, gain);}
std::size_t resample(short *out, const short *in, std::size_t inlen) { return convoluter_.filter(out, in, inlen); }
void adjustDiv(unsigned div) { convoluter_.adjustDiv(div); }
unsigned mul() const { return MUL; }
unsigned div() const { return convoluter_.div(); }
};
#endif

View file

@ -0,0 +1,136 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef LININT_H
#define LININT_H
#include <cstddef>
#include "../resampler.h"
#include "u48div.h"
#include "rshift16_round.h"
template<int channels>
class LinintCore {
unsigned long ratio_;
std::size_t pos_;
unsigned fracPos_;
int prevSample_;
public:
explicit LinintCore(long inRate = 1, long outRate = 1) { init(inRate, outRate); }
void adjustRate(long inRate, long outRate) {
ratio_ = static_cast<unsigned long>((static_cast<double>(inRate) / outRate) * 0x10000 + 0.5);
}
void exactRatio(unsigned long &mul, unsigned long &div) const { mul = 0x10000; div = ratio_; }
void init(long inRate, long outRate);
std::size_t maxOut(std::size_t inlen) const { return inlen ? u48div(inlen - 1, 0xFFFF, ratio_) + 1 : 0; }
std::size_t resample(short *out, const short *in, std::size_t inlen);
};
template<int channels>
void LinintCore<channels>::init(const long inRate, const long outRate) {
adjustRate(inRate, outRate);
pos_ = (ratio_ >> 16) + 1;
fracPos_ = ratio_ & 0xFFFF;
prevSample_ = 0;
}
template<int channels>
std::size_t LinintCore<channels>::resample(short *const out, const short *const in, const std::size_t inlen) {
if (pos_ < inlen) {
std::ptrdiff_t pos = pos_;
const unsigned long ratio = ratio_;
unsigned fracPos = fracPos_;
short *o = out;
while (pos == 0) {
long const lhs = prevSample_;
long const rhs = in[0];
*o = lhs + rshift16_round((rhs - lhs) * static_cast<long>(fracPos));
o += channels;
unsigned long const nfrac = fracPos + ratio;
fracPos = nfrac & 0xFFFF;
pos += nfrac >> 16;
}
const short *const inend = in + inlen * channels;
pos -= static_cast<std::ptrdiff_t>(inlen);
while (pos < 0) {
long const lhs = inend[(pos-1) * channels];
long const rhs = inend[ pos * channels];
*o = lhs + rshift16_round((rhs - lhs) * static_cast<long>(fracPos));
o += channels;
unsigned long const nfrac = fracPos + ratio;
fracPos = nfrac & 0xFFFF;
pos += nfrac >> 16;
}
prevSample_ = inend[-channels];
pos_ = pos;
fracPos_ = fracPos;
return (o - out) / channels;
}
return 0;
}
template<int channels>
class Linint : public Resampler {
LinintCore<channels> cores[channels];
public:
Linint(long inRate, long outRate);
void adjustRate(long inRate, long outRate);
void exactRatio(unsigned long &mul, unsigned long &div) const { cores[0].exactRatio(mul, div); }
std::size_t maxOut(std::size_t inlen) const { return cores[0].maxOut(inlen); }
std::size_t resample(short *out, const short *in, std::size_t inlen);
};
template<int channels>
Linint<channels>::Linint(const long inRate, const long outRate) {
setRate(inRate, outRate);
for (int i = 0; i < channels; ++i)
cores[i].init(inRate, outRate);
}
template<int channels>
void Linint<channels>::adjustRate(const long inRate, const long outRate) {
setRate(inRate, outRate);
for (int i = 0; i < channels; ++i)
cores[i].adjustRate(inRate, outRate);
}
template<int channels>
std::size_t Linint<channels>::resample(short *const out, const short *const in, const std::size_t inlen) {
std::size_t outlen = 0;
for (int i = 0; i < channels; ++i)
outlen = cores[i].resample(out + i, in + i, inlen);
return outlen;
}
#endif

View file

@ -0,0 +1,142 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "makesinckernel.h"
#include "array.h"
void makeSincKernel(short *const kernel, const unsigned phases,
const unsigned phaseLen, double fc, double (*win)(long m, long M), double const maxAllowedGain) {
static const double PI = 3.14159265358979323846;
fc /= phases;
/*{
const Array<double> dkernel(phaseLen * phases);
const long M = static_cast<long>(phaseLen) * phases - 1;
for (long i = 0; i < M + 1; ++i) {
const double sinc = i * 2 == M ?
PI * fc :
std::sin(PI * fc * (i * 2 - M)) / (i * 2 - M);
dkernel[((phases - (i % phases)) % phases) * phaseLen + i / phases] = win(i, M) * sinc;
}
double maxabsgain = 0;
for (unsigned ph = 0; ph < phases; ++ph) {
double gain = 0;
double absgain = 0;
for (unsigned i = 0; i < phaseLen; ++i) {
gain += dkernel[ph * phaseLen + i];
absgain += std::abs(dkernel[ph * phaseLen + i]);
}
gain = 1.0 / gain;
// Per phase normalization to avoid DC fluctuations.
for (unsigned i = 0; i < phaseLen; ++i)
dkernel[ph * phaseLen + i] *= gain;
absgain *= gain;
if (absgain > maxabsgain)
maxabsgain = absgain;
}
const double gain = (0x10000 - 0.5 * phaseLen) * maxAllowedGain / maxabsgain;
for (long i = 0; i < M + 1; ++i)
kernel[i] = std::floor(dkernel[i] * gain + 0.5);
}*/
// The following is equivalent to the more readable version above
const long M = static_cast<long>(phaseLen) * phases - 1;
const Array<double> dkernel(M / 2 + 1);
{
double *dk = dkernel;
for (unsigned ph = 0; ph < phases; ++ph) {
for (long i = ph; i < M / 2 + 1; i += phases) {
const double sinc = i * 2 == M ?
PI * fc :
std::sin(PI * fc * (i * 2 - M)) / (i * 2 - M);
*dk++ = win(i, M) * sinc;
}
}
}
double maxabsgain = 0.0;
{
double *dkp1 = dkernel;
double *dkp2 = dkernel + M / 2;
for (unsigned ph = 0; ph < (phases + 1) / 2; ++ph) {
double gain = 0.0;
double absgain = 0.0;
{
const double *kp1 = dkp1;
const double *kp2 = dkp2;
long i = ph;
for (; i < M / 2 + 1; i += phases) {
gain += *kp1;
absgain += std::abs(*kp1++);
}
for (; i < M + 1; i += phases) {
gain += *kp2;
absgain += std::abs(*kp2--);
}
}
gain = 1.0 / gain;
long i = ph;
for (; i < M / 2 + 1; i += phases)
*dkp1++ *= gain;
if (dkp1 < dkp2) {
for (; i < M + 1; i += phases)
*dkp2-- *= gain;
}
absgain *= gain;
if (absgain > maxabsgain)
maxabsgain = absgain;
}
}
const double gain = (0x10000 - 0.5 * phaseLen) * maxAllowedGain / maxabsgain;
const double *dk = dkernel;
for (unsigned ph = 0; ph < phases; ++ph) {
short *k = kernel + ((phases - ph) % phases) * phaseLen;
short *km = kernel + phaseLen - 1 + ((ph + 1) % phases) * phaseLen;
for (long i = ph; i < M / 2 + 1; i += phases)
*km-- = *k++ = static_cast<short>(std::floor(*dk++ * gain + 0.5));
}
}

View file

@ -0,0 +1,28 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef MAKE_SINC_KERNEL_H
#define MAKE_SINC_KERNEL_H
#include <cmath>
#include <cstdlib>
void makeSincKernel(short *kernel, unsigned phases, unsigned phaseLen,
double fc, double (*win)(long m, long M), double gain);
#endif

View file

@ -0,0 +1,75 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef RECTSINC_H
#define RECTSINC_H
#include "convoluter.h"
#include "subresampler.h"
#include "makesinckernel.h"
#include "cic2.h"
#include "array.h"
#include <algorithm>
#include <cmath>
#include <cstddef>
template<unsigned channels, unsigned phases>
class RectSinc : public SubResampler {
Array<short> const kernel;
PolyPhaseConvoluter<channels, phases> convoluter_;
static double rectWin(const long /*i*/, const long /*M*/) { return 1; }
public:
enum { MUL = phases };
typedef Cic2<channels> Cic;
static float cicLimit() { return 2.0f; }
class RollOff {
static unsigned toTaps(const float rollOffWidth) {
const float widthTimesTaps = 0.9f;
return std::max(static_cast<unsigned>(std::ceil(widthTimesTaps / rollOffWidth)), 4u);
}
static float toFc(const float rollOffStart, const int taps) {
const float startToFcDeltaTimesTaps = 0.43f;
return startToFcDeltaTimesTaps / taps + rollOffStart;
}
public:
const unsigned taps;
const float fc;
RollOff(float rollOffStart, float rollOffWidth) : taps(toTaps(rollOffWidth)), fc(toFc(rollOffStart, taps)) {}
};
RectSinc(unsigned div, unsigned phaseLen, double fc)
: kernel(phaseLen * phases), convoluter_(kernel, phaseLen, div)
{ makeSincKernel(kernel, phases, phaseLen, fc, rectWin, 1.0); }
RectSinc(unsigned div, RollOff ro, double gain)
: kernel(ro.taps * phases), convoluter_(kernel, ro.taps, div)
{ makeSincKernel(kernel, phases, ro.taps, ro.fc, rectWin, gain);}
std::size_t resample(short *out, const short *in, std::size_t inlen) { return convoluter_.filter(out, in, inlen); }
void adjustDiv(unsigned div) { convoluter_.adjustDiv(div); }
unsigned mul() const { return MUL; }
unsigned div() const { return convoluter_.div(); }
};
#endif

View file

@ -0,0 +1,50 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "../resamplerinfo.h"
#include "chainresampler.h"
#include "kaiser50sinc.h"
#include "kaiser70sinc.h"
// #include "hammingsinc.h"
// #include "blackmansinc.h"
#include "rectsinc.h"
#include "linint.h"
struct LinintInfo {
static Resampler* create(long inRate, long outRate, std::size_t) { return new Linint<ChainResampler::channels>(inRate, outRate); }
};
template<template<unsigned,unsigned> class T>
struct ChainSincInfo {
static Resampler* create(long inRate, long outRate, std::size_t periodSz) {
ChainResampler *r = new ChainResampler;
r->init<T>(inRate, outRate, periodSz);
return r;
}
};
const ResamplerInfo ResamplerInfo::resamplers[] = {
{ "Fast", LinintInfo::create },
{ "High quality (CIC + sinc chain)", ChainSincInfo<RectSinc>::create },
// { "Hamming windowed sinc (~50 dB SNR)", ChainSincInfo<HammingSinc>::create },
// { "Blackman windowed sinc (~70 dB SNR)", ChainSincInfo<BlackmanSinc>::create },
{ "Very high quality (CIC + sinc chain)", ChainSincInfo<Kaiser50Sinc>::create },
{ "Highest quality (CIC + sinc chain)", ChainSincInfo<Kaiser70Sinc>::create }
};
const std::size_t ResamplerInfo::num_ = sizeof ResamplerInfo::resamplers / sizeof *ResamplerInfo::resamplers;

View file

@ -0,0 +1,32 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* 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 version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef RSHIFT16_ROUND_H
#define RSHIFT16_ROUND_H
#ifdef NO_NEGATIVE_SHIFT
static inline long rshift16_round(const long l) {
return l < 0 ? -((-l + 0x8000) >> 16) : (l + 0x8000) >> 16;
}
#else
static inline long rshift16_round(const long l) {
return (l + 0x8000) >> 16;
}
#endif
#endif

Some files were not shown because too many files have changed in this diff Show more