46 Commits

Author SHA1 Message Date
clobber ef91ce464c Cleanup 2023-06-20 21:13:53 -06:00
clobber 0ba54f14d3 Merge pull request #8 from ShutOstrich/master
Update to Gambatte JG 0.5.1
2023-05-03 21:20:11 -06:00
ShutOstrich 0a330b490f Add display mode: CGB color correction 2023-05-01 18:55:17 +02:00
ShutOstrich 65e0083de0 Update to Gambatte JG 0.5.1 2023-05-01 07:39:23 +02:00
ShutOstrich 6237b9f50e Update to Gambatte JG 0.5.0 2023-05-01 07:39:23 +02:00
clobber ba04dbb4ef Merge pull request #7 from OpenEmu/revert-5-master
Revert "Add: Colour Palette Revisions"
2021-12-09 21:05:46 -07:00
clobber 2fffad141d Revert "Add: Colour Palette Revisions" 2021-12-09 21:02:29 -07:00
C.W. Betts 44182cc6eb Merge pull request #5 from TRIFORCE89/master
Add: Colour Palette Revisions
2021-12-09 19:21:12 -07:00
TRIFORCE89 72bb48afdd - Finally accurate SGB colours
- Remove usage of BGB colours
2021-12-02 20:16:14 -05:00
TRIFORCE89 ecbaa4142b correct SGB specific palettes; add real WideBoy values; add missing Smurfs from Goomba 2021-09-18 10:00:50 -04:00
TRIFORCE89 e69312eabd Redo Demo Vision after correcting NES Classic Mini palette in Nestopia 2021-07-23 21:24:24 -04:00
TRIFORCE89 b5a97f1d55 Multiple Revisions
Changes:
 - Double check against TheWolfBunny & nensondubois
 - improved commenting
 - nensondubois' revised Alley Way SGB palette
 - nensondubois' revised Tetris Attack SGB palette
 - Collection of SaGa palette
 - Donkey Kong '94 ending palette
 - SGB Super Mario Land palette for Arne's Unnamed Graphics Hack rather than 1-A
 - Wide-Boy palette option
 - Donkey Kong Land DMGesque menu palette option
 - Game & Watch palette option
 - Demo Vision palette option
 - remove unused palette additions (Superball, Squidlit)
 - update Dream Land GB with TheWolfBunny's version
2021-07-16 12:01:05 -04:00
TRIFORCE89 00e8ac983c Add: SGB Cool Spot from NP. Some unofficial Goomba Color entries for other games 2021-06-11 21:58:02 -04:00
TRIFORCE89 be62b682da MGB is default again (I play with LCD grids), swap Squidlit for proper GB Studio 2021-06-06 12:30:51 -04:00
TRIFORCE89 8868472869 Fix: Kirby Pocket && Add: custom Arne SML2013 (need to edit game header to activate) 2021-05-19 08:53:37 -04:00
TRIFORCE89 beba11c372 Kirby!
Changes:
- Fix: Wii Gray is now the default
- Add: Smash Ultimate Green replaces VC Green
- Fix: revise true color output

Note:
- new green palette was created by pushing LUT (https://scanmountgoat.github.io/Smush-Material-Research/post_processing) through https://hexcolor.co/image-to-colors and increasing brightness by 40%
- 3DS Dreamland GB looked like 3DS VC, so in my view Switch Dreamland GB is a TV-safe revision of that VC palette
2021-05-12 00:24:18 -04:00
TRIFORCE89 5c1a41ec41 Updates:
- rearrange palette menu
- add back SGB 4H (My Life in Gaming endorses it)
- replace 3DS grayscale with the nicer revision from Kirby's Dream Collection
- SGB Pokémon taken from disassembly
- accurate GB Studio
- code cleanup

Notes:
- recommended to not use a LCD-tint (like GBC/GBA/GBI) shader on SGB palettes
2021-05-10 20:21:24 -04:00
TRIFORCE89 01f163576d Fix: locations of the system plugin headers.
Changes:
- Minor Xcode maintenance.
2020-12-25 14:09:15 -05:00
TRIFORCE89 be0e841c42 Add: SGB/GBC support for Lunar Chase prototype 2020-10-23 00:28:03 -04:00
C.W. Betts 40e9ff65bb Poke the plists: get the development language from Xcode build. 2020-10-01 01:51:37 -06:00
C.W. Betts c4bd988726 Fix locations of the system plugin headers.
Minor Xcode maintenance.
2020-10-01 01:26:40 -06:00
C.W. Betts 17ccc013a8 Revert "set deployment target to match OpenEmuCore's, of 10.14.4." 2020-09-22 09:30:09 -06:00
C.W. Betts bb866b0f91 set deployment target to match OpenEmuCore's, of 10.14.4.
Also silence deprecated OpenGL warnings.
2020-09-22 02:53:07 -06:00
TRIFORCE89 ac3ceae429 Add: palette for Analogue Pocket / GB Studio 2020-09-20 19:14:11 -04:00
TRIFORCE89 69a6fe7856 Tweak PkMn palettes to match VBA-M and TheWolfBunny 2020-09-20 19:14:11 -04:00
TRIFORCE89 5fd058a7ea True Colour
Changes:
 - Remove: Gambatte's incorrect colour correction (this should be addressed via shaders)
 - Fix: change up the list of available palette selections again because now they all actually look correct
2020-09-20 19:13:16 -04:00
TRIFORCE89 f463dc6b7a Add: GBC Greyscale option & default to VC Greyscale instead of SGB as it approximates Pocket 2020-09-20 19:06:07 -04:00
TRIFORCE89 d8d6f0d807 Add: VirtualBoy palette 2020-09-20 19:06:07 -04:00
TRIFORCE89 08f893779e Fix: revise palettes used and include VC colours 2020-09-20 19:06:07 -04:00
TRIFORCE89 23b9a5de5a Add: Colour Palette Revisions 2020-09-20 19:06:07 -04:00
clobber e5b42325c9 Revert version number back to "counting" upstream commits
Counting the commits like other forks (https://github.com/pokemon-speedrunning/gambatte-speedrun) until there is a formal release again. NOTE: counting by `git rev-list --count HEAD` instead of `git rev-list --count --first-parent HEAD`
2020-08-20 12:23:49 -05:00
clobber fb4cc1323e Merge MBC3 RTC crash fix from https://github.com/sinamas/gambatte/pull/18 2020-08-20 11:59:52 -05:00
clobber 8e6c44a63e Bump version for sparkle updater 2020-08-20 01:42:56 -05:00
clobber 052a4b7020 Sync with upstream https://github.com/sinamas/gambatte/commit/56e3371151b5ee86dcdcf4868324ebc6de220bc9
This also includes our serialize/deserialize support and GBC GameShark cheat fixes from commits 9fbc420 and d08f236
2020-08-20 01:34:31 -05:00
Daniele Cattaneo fd5294ab09 Disable code signing.
Could cause problems with Sparkle in a future update built with a recent version of Xcode.
2020-08-16 18:03:41 +02:00
Daniele Cattaneo 3ee18feec9 Quiet Xcode warnings. 2020-08-16 15:42:50 +02:00
Daniele Cattaneo a69f54e944 Prevent passing too many samples to the resampler when playing certain games (i.e. Mario Tennis).
Fixes issue #6 (and its duplicate OpenEmu/OpenEmu#4282).
2020-08-16 15:14:23 +02:00
C.W. Betts c1823b4021 Update language resources.
This quiets warnings in newer Xcode releases.

Also update framework locations.
2020-01-07 16:37:18 -07:00
clobber 27b26480c6 Bump version for sparkle updater. Core is still 0.5.0 r572 2018-11-26 14:38:32 -06:00
clobber fd071b24b5 Cleanup 2018-11-26 14:37:03 -06:00
clobber 34bc63a962 More cleanup 2018-11-10 23:42:40 -06:00
clobber 41e7cbb149 Cleanup 2018-11-07 10:51:08 -06:00
clobber 4cc81aa4ac Add display mode change support 2018-11-04 13:10:46 -06:00
clobber eac78b39e1 Use -fileSystemRepresentation instead of -UTF8String for file names 2017-08-16 23:48:03 -05:00
Rudy Richter 8245238796 Use spaces 2017-07-20 08:51:07 -04:00
mrvacbob d16ec11885 Enable direct-rendering 2017-07-20 01:59:11 -07:00
151 changed files with 6348 additions and 15546 deletions
+339
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.
+551
View File
@@ -0,0 +1,551 @@
Version 0.5.1
-------------
- Build improvements
- Increase standards compliance and code quality
- Add the SoX Resampler for higher quality (but higher CPU usage) resampling
- Add SameBoy's "Modern - Accurate" colour correction
Version 0.5.0
-------------
- Initial tagged release after fork from final public revision of Gambatte
- Stricter build flags
- Removal of stale, bitrotten code
- Rearrange and simplify codebase layout
--------------------------------------------------------------------------------
-- 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
Binary file not shown.
+335 -196
View File
@@ -35,6 +35,14 @@
#include "resamplerinfo.h"
#include "resampler.h"
#define OptionDefault(_NAME_, _PREFKEY_) @{ OEGameCoreDisplayModeNameKey : _NAME_, OEGameCoreDisplayModePrefKeyNameKey : _PREFKEY_, OEGameCoreDisplayModeStateKey : @YES, }
#define Option(_NAME_, _PREFKEY_) @{ OEGameCoreDisplayModeNameKey : _NAME_, OEGameCoreDisplayModePrefKeyNameKey : _PREFKEY_, OEGameCoreDisplayModeStateKey : @NO, }
#define OptionIndented(_NAME_, _PREFKEY_) @{ OEGameCoreDisplayModeNameKey : _NAME_, OEGameCoreDisplayModePrefKeyNameKey : _PREFKEY_, OEGameCoreDisplayModeStateKey : @NO, OEGameCoreDisplayModeIndentationLevelKey : @(1), }
#define OptionToggleable(_NAME_, _PREFKEY_) @{ OEGameCoreDisplayModeNameKey : _NAME_, OEGameCoreDisplayModePrefKeyNameKey : _PREFKEY_, OEGameCoreDisplayModeStateKey : @NO, OEGameCoreDisplayModeAllowsToggleKey : @YES, }
#define OptionToggleableNoSave(_NAME_, _PREFKEY_) @{ OEGameCoreDisplayModeNameKey : _NAME_, OEGameCoreDisplayModePrefKeyNameKey : _PREFKEY_, OEGameCoreDisplayModeStateKey : @NO, OEGameCoreDisplayModeAllowsToggleKey : @YES, OEGameCoreDisplayModeDisallowPrefSaveKey : @YES, }
#define Label(_NAME_) @{ OEGameCoreDisplayModeLabelKey : _NAME_, }
#define SeparatorItem() @{ OEGameCoreDisplayModeSeparatorItemKey : @"",}
gambatte::GB gb;
Resampler *resampler;
uint32_t pad[OEGBButtonCount];
@@ -50,15 +58,22 @@ public:
@interface GBGameCore () <OEGBSystemResponderClient>
{
uint32_t *videoBuffer;
uint32_t *inSoundBuffer;
int16_t *outSoundBuffer;
double sampleRate;
int displayMode;
uint32_t *_videoBuffer;
uint32_t *_inSoundBuffer;
int16_t *_outSoundBuffer;
double _sampleRate;
NSMutableDictionary <NSString *, NSNumber *> *_cheatList;
NSMutableArray <NSMutableDictionary <NSString *, id> *> *_availableDisplayModes;
}
- (void)applyCheat:(NSString *)code;
- (void)loadDisplayModeOptions;
- (NSString *)gameInternalName;
- (BOOL)gameHasInternalPalette;
- (void)loadPalette;
- (void)loadPaletteDefault;
- (void)changePalette:(NSString *)palette;
@end
@@ -68,10 +83,9 @@ public:
{
if((self = [super init]))
{
videoBuffer = (uint32_t *)malloc(160 * 144 * 4);
inSoundBuffer = (uint32_t *)malloc(2064 * 2 * 4);
outSoundBuffer = (int16_t *)malloc(2064 * 2 * 2);
displayMode = 0;
_inSoundBuffer = (uint32_t *)malloc(2064 * 2 * 4);
_outSoundBuffer = (int16_t *)malloc(2064 * 2 * 2);
_cheatList = [NSMutableDictionary dictionary];
}
return self;
@@ -79,21 +93,21 @@ public:
- (void)dealloc
{
free(videoBuffer);
free(inSoundBuffer);
free(outSoundBuffer);
free(_videoBuffer);
free(_inSoundBuffer);
free(_outSoundBuffer);
}
# pragma mark - Execution
- (BOOL)loadFileAtPath:(NSString *)path error:(NSError **)error
{
memset(pad, 0, sizeof(uint32_t) * OEGBButtonCount);
memset(pad, 0, sizeof(pad));
// Set battery save dir
NSURL *batterySavesDirectory = [NSURL fileURLWithPath:[self batterySavesDirectoryPath]];
NSURL *batterySavesDirectory = [NSURL fileURLWithPath:self.batterySavesDirectoryPath];
[[NSFileManager defaultManager] createDirectoryAtURL:batterySavesDirectory withIntermediateDirectories:YES attributes:nil error:nil];
gb.setSaveDir([[batterySavesDirectory path] UTF8String]);
gb.setSaveDir(batterySavesDirectory.fileSystemRepresentation);
// Set input state callback
gb.setInputGetter(&GetInput);
@@ -109,14 +123,12 @@ public:
resampler->exactRatio(mul, div);
double outSampleRate = inSampleRate * mul / div;
sampleRate = outSampleRate; // 47994.326636
_sampleRate = outSampleRate; // 47994.326636
if (gb.load([path UTF8String]) != 0)
if (gb.load(path.fileSystemRepresentation) != 0)
return NO;
// Load built-in GBC palette for monochrome games if supported
if (!gb.isCgb())
[self loadPalette];
[self loadDisplayModeOptions];
return YES;
}
@@ -125,8 +137,10 @@ public:
{
size_t samples = 2064;
while (gb.runFor(videoBuffer, 160, inSoundBuffer, samples) == -1)
while (gb.runFor(_videoBuffer, 160, _inSoundBuffer, samples) == -1) {
[self outputAudio:samples];
samples = 2064;
}
[self outputAudio:samples];
}
@@ -152,9 +166,13 @@ public:
# pragma mark - Video
- (const void *)videoBuffer
- (const void *)getVideoBufferWithHint:(void *)hint
{
return videoBuffer;
if (!hint) {
if (!_videoBuffer) _videoBuffer = (uint32_t *)malloc(160 * 144 * 4);
hint = _videoBuffer;
}
return _videoBuffer = (uint32_t*)hint;
}
- (OEIntRect)screenRect
@@ -182,16 +200,11 @@ public:
return GL_UNSIGNED_INT_8_8_8_8_REV;
}
- (GLenum)internalPixelFormat
{
return GL_RGB8;
}
# pragma mark - Audio
- (double)audioSampleRate
{
return sampleRate;
return _sampleRate;
}
- (NSUInteger)channelCount
@@ -203,28 +216,28 @@ public:
- (void)saveStateToFileAtPath:(NSString *)fileName completionHandler:(void (^)(BOOL, NSError *))block
{
int success = gb.saveState(0, 0, [fileName UTF8String]);
int success = gb.saveState(0, 0, fileName.fileSystemRepresentation);
if(block) block(success==1, nil);
}
- (void)loadStateFromFileAtPath:(NSString *)fileName completionHandler:(void (^)(BOOL, NSError *))block
{
int success = gb.loadState([fileName UTF8String]);
int success = gb.loadState(fileName.fileSystemRepresentation);
if(block) block(success==1, nil);
}
- (NSData *)serializeStateWithError:(NSError **)outError
{
std::stringstream stream(std::ios::in|std::ios::out|std::ios::binary);
if(gb.serializeState(stream)) {
stream.seekg(0, std::ios::end);
NSUInteger length = stream.tellg();
stream.seekg(0, std::ios::beg);
char *bytes = (char *)malloc(length);
stream.read(bytes, length);
return [NSData dataWithBytesNoCopy:bytes length:length];
}
@@ -234,18 +247,18 @@ public:
NSLocalizedRecoverySuggestionErrorKey : @"The emulator could not write the state data."
}];
}
return nil;
}
- (BOOL)deserializeState:(NSData *)state withError:(NSError **)outError
{
std::stringstream stream(std::ios::in|std::ios::out|std::ios::binary);
char const *bytes = (char const *)([state bytes]);
std::streamsize size = [state length];
char const *bytes = (char const *)(state.bytes);
std::streamsize size = state.length;
stream.write(bytes, size);
if(gb.deserializeState(stream))
return YES;
@@ -273,189 +286,188 @@ const int GBMap[] = {gambatte::InputGetter::UP, gambatte::InputGetter::DOWN, gam
#pragma mark - Cheats
NSMutableDictionary *cheatList = [[NSMutableDictionary alloc] init];
- (void)setCheat:(NSString *)code setType:(NSString *)type setEnabled:(BOOL)enabled
{
// Sanitize
code = [code stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
code = [code stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
// Gambatte expects cheats UPPERCASE
code = [code uppercaseString];
code = code.uppercaseString;
// Remove any spaces
code = [code stringByReplacingOccurrencesOfString:@" " withString:@""];
if (enabled)
[cheatList setValue:@YES forKey:code];
_cheatList[code] = @YES;
else
[cheatList removeObjectForKey:code];
[_cheatList removeObjectForKey:code];
NSMutableArray *combinedGameSharkCodes = [[NSMutableArray alloc] init];
NSMutableArray *combinedGameGenieCodes = [[NSMutableArray alloc] init];
NSMutableArray <NSString *> *combinedGameSharkCodes = [NSMutableArray array];
NSMutableArray <NSString *> *combinedGameGenieCodes = [NSMutableArray array];
// Gambatte expects all cheats in one combined string per-type e.g. 01xxxxxx+01xxxxxx
// Add enabled per-type cheats to arrays and later join them all by a '+' separator
for (id key in cheatList)
for (NSString *key in _cheatList)
{
if ([[cheatList valueForKey:key] isEqual:@YES])
if ([_cheatList[key] boolValue])
{
// GameShark
if ([key rangeOfString:@"-"].location == NSNotFound)
if (![key containsString:@"-"])
[combinedGameSharkCodes addObject:key];
// Game Genie
else if ([key rangeOfString:@"-"].location != NSNotFound)
else if ([key containsString:@"-"])
[combinedGameGenieCodes addObject:key];
}
}
// Apply combined cheats or force a final reset if all cheats are disabled
[self applyCheat:[combinedGameSharkCodes count] != 0 ? [combinedGameSharkCodes componentsJoinedByString:@"+"] : @"0"];
[self applyCheat:[combinedGameGenieCodes count] != 0 ? [combinedGameGenieCodes componentsJoinedByString:@"+"] : @"0-"];
[self applyCheat:combinedGameSharkCodes.count != 0 ? [combinedGameSharkCodes componentsJoinedByString:@"+"] : @"0"];
[self applyCheat:combinedGameGenieCodes.count != 0 ? [combinedGameGenieCodes componentsJoinedByString:@"+"] : @"0-"];
}
# pragma mark - Display Mode
- (void)changeDisplayMode
- (NSArray <NSDictionary <NSString *, id> *> *)displayModes
{
if (gb.isCgb())
if (_availableDisplayModes.count == 0)
{
_availableDisplayModes = [NSMutableArray array];
NSArray <NSDictionary <NSString *, id> *> *availableModesWithDefault;
if (gb.isCgb())
{
availableModesWithDefault =
@[
Label(@"Color Correction"),
OptionDefault(@"Default", @"colorCorrection"),
Option(@"Modern", @"colorCorrection"),
];
}
else
{
availableModesWithDefault =
@[
Option(@"Internal", @"palette"),
Option(@"Grayscale", @"palette"),
Option(@"Greenscale", @"palette"),
Option(@"Pocket", @"palette"),
SeparatorItem(),
Label(@"GBC Palettes"),
Option(@"Blue", @"palette"),
Option(@"Dark Blue", @"palette"),
Option(@"Green", @"palette"),
Option(@"Dark Green", @"palette"),
Option(@"Brown", @"palette"),
Option(@"Dark Brown", @"palette"),
Option(@"Red", @"palette"),
Option(@"Yellow", @"palette"),
Option(@"Orange", @"palette"),
Option(@"Pastel Mix", @"palette"),
Option(@"Inverted", @"palette"),
];
}
// Deep mutable copy
_availableDisplayModes = (NSMutableArray *)CFBridgingRelease(CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFArrayRef)availableModesWithDefault, kCFPropertyListMutableContainers));
if (!gb.isCgb() && ![self gameHasInternalPalette])
[_availableDisplayModes removeObjectAtIndex:0];
}
return [_availableDisplayModes copy];
}
- (void)changeDisplayWithMode:(NSString *)displayMode
{
// NOTE: This is a more complex implementation to serve as an example for handling submenus,
// toggleable options and multiple groups of mutually exclusive options.
if (_availableDisplayModes.count == 0)
[self displayModes];
// First check if 'displayMode' is toggleable and grab its preference key
BOOL isDisplayModeToggleable = NO;
BOOL isValidDisplayMode = NO;
BOOL displayModeState = NO;
NSString *displayModePrefKey;
for (NSDictionary *modeDict in _availableDisplayModes)
{
if ([modeDict[OEGameCoreDisplayModeNameKey] isEqualToString:displayMode])
{
displayModeState = [modeDict[OEGameCoreDisplayModeStateKey] boolValue];
displayModePrefKey = modeDict[OEGameCoreDisplayModePrefKeyNameKey];
isDisplayModeToggleable = [modeDict[OEGameCoreDisplayModeAllowsToggleKey] boolValue];
isValidDisplayMode = YES;
break;
}
// Submenu Items
for (NSDictionary *subModeDict in modeDict[OEGameCoreDisplayModeGroupItemsKey])
{
if ([subModeDict[OEGameCoreDisplayModeNameKey] isEqualToString:displayMode])
{
displayModeState = [subModeDict[OEGameCoreDisplayModeStateKey] boolValue];
displayModePrefKey = subModeDict[OEGameCoreDisplayModePrefKeyNameKey];
isDisplayModeToggleable = [subModeDict[OEGameCoreDisplayModeAllowsToggleKey] boolValue];
isValidDisplayMode = YES;
break;
}
}
}
// Disallow a 'displayMode' not found in _availableDisplayModes
if (!isValidDisplayMode)
return;
unsigned short *gbc_bios_palette = NULL;
switch (displayMode)
// Handle option state changes
for (NSMutableDictionary *optionDict in _availableDisplayModes)
{
case 0:
NSString *modeName = optionDict[OEGameCoreDisplayModeNameKey];
NSString *prefKey = optionDict[OEGameCoreDisplayModePrefKeyNameKey];
if (!modeName && !optionDict[OEGameCoreDisplayModeGroupNameKey])
continue;
// Mutually exclusive option state change
else if ([modeName isEqualToString:displayMode] && !isDisplayModeToggleable)
optionDict[OEGameCoreDisplayModeStateKey] = @YES;
// Reset mutually exclusive options that are the same prefs group as 'displayMode'
else if (!isDisplayModeToggleable && [prefKey isEqualToString:displayModePrefKey])
optionDict[OEGameCoreDisplayModeStateKey] = @NO;
// Toggleable option state change
else if ([modeName isEqualToString:displayMode] && isDisplayModeToggleable)
optionDict[OEGameCoreDisplayModeStateKey] = @(!displayModeState);
// Submenu group
else if (optionDict[OEGameCoreDisplayModeGroupNameKey])
{
// GB Pea Soup Green
gb.setDmgPaletteColor(0, 0, 8369468);
gb.setDmgPaletteColor(0, 1, 6728764);
gb.setDmgPaletteColor(0, 2, 3629872);
gb.setDmgPaletteColor(0, 3, 3223857);
gb.setDmgPaletteColor(1, 0, 8369468);
gb.setDmgPaletteColor(1, 1, 6728764);
gb.setDmgPaletteColor(1, 2, 3629872);
gb.setDmgPaletteColor(1, 3, 3223857);
gb.setDmgPaletteColor(2, 0, 8369468);
gb.setDmgPaletteColor(2, 1, 6728764);
gb.setDmgPaletteColor(2, 2, 3629872);
gb.setDmgPaletteColor(2, 3, 3223857);
displayMode++;
return;
}
case 1:
{
// GB Pocket
gb.setDmgPaletteColor(0, 0, 13487791);
gb.setDmgPaletteColor(0, 1, 10987158);
gb.setDmgPaletteColor(0, 2, 6974033);
gb.setDmgPaletteColor(0, 3, 2828823);
gb.setDmgPaletteColor(1, 0, 13487791);
gb.setDmgPaletteColor(1, 1, 10987158);
gb.setDmgPaletteColor(1, 2, 6974033);
gb.setDmgPaletteColor(1, 3, 2828823);
gb.setDmgPaletteColor(2, 0, 13487791);
gb.setDmgPaletteColor(2, 1, 10987158);
gb.setDmgPaletteColor(2, 2, 6974033);
gb.setDmgPaletteColor(2, 3, 2828823);
displayMode++;
return;
}
case 2:
gbc_bios_palette = const_cast<unsigned short *>(findGbcDirPal("GBC - Blue"));
displayMode++;
break;
case 3:
gbc_bios_palette = const_cast<unsigned short *>(findGbcDirPal("GBC - Dark Blue"));
displayMode++;
break;
case 4:
gbc_bios_palette = const_cast<unsigned short *>(findGbcDirPal("GBC - Green"));
displayMode++;
break;
case 5:
gbc_bios_palette = const_cast<unsigned short *>(findGbcDirPal("GBC - Dark Green"));
displayMode++;
break;
case 6:
gbc_bios_palette = const_cast<unsigned short *>(findGbcDirPal("GBC - Brown"));
displayMode++;
break;
case 7:
gbc_bios_palette = const_cast<unsigned short *>(findGbcDirPal("GBC - Dark Brown"));
displayMode++;
break;
case 8:
gbc_bios_palette = const_cast<unsigned short *>(findGbcDirPal("GBC - Red"));
displayMode++;
break;
case 9:
gbc_bios_palette = const_cast<unsigned short *>(findGbcDirPal("GBC - Yellow"));
displayMode++;
break;
case 10:
gbc_bios_palette = const_cast<unsigned short *>(findGbcDirPal("GBC - Orange"));
displayMode++;
break;
case 11:
gbc_bios_palette = const_cast<unsigned short *>(findGbcDirPal("GBC - Pastel Mix"));
displayMode++;
break;
case 12:
gbc_bios_palette = const_cast<unsigned short *>(findGbcDirPal("GBC - Inverted"));
displayMode++;
break;
case 13:
{
std::string str = gb.romTitle(); // read ROM internal title
const char *internal_game_name = str.c_str();
gbc_bios_palette = const_cast<unsigned short *>(findGbcTitlePal(internal_game_name));
if (gbc_bios_palette == 0)
// Submenu items
for (NSMutableDictionary *subOptionDict in optionDict[OEGameCoreDisplayModeGroupItemsKey])
{
gbc_bios_palette = const_cast<unsigned short *>(findGbcDirPal("GBC - Grayscale"));
displayMode = 0;
NSString *modeName = subOptionDict[OEGameCoreDisplayModeNameKey];
NSString *prefKey = subOptionDict[OEGameCoreDisplayModePrefKeyNameKey];
if (!modeName)
continue;
// Mutually exclusive option state change
else if ([modeName isEqualToString:displayMode] && !isDisplayModeToggleable)
subOptionDict[OEGameCoreDisplayModeStateKey] = @YES;
// Reset mutually exclusive options that are the same prefs group as 'displayMode'
else if (!isDisplayModeToggleable && [prefKey isEqualToString:displayModePrefKey])
subOptionDict[OEGameCoreDisplayModeStateKey] = @NO;
// Toggleable option state change
else if ([modeName isEqualToString:displayMode] && isDisplayModeToggleable)
subOptionDict[OEGameCoreDisplayModeStateKey] = @(!displayModeState);
}
else
displayMode++;
break;
}
case 14:
gbc_bios_palette = const_cast<unsigned short *>(findGbcDirPal("GBC - Grayscale"));
displayMode = 0;
break;
default:
return;
break;
}
unsigned rgb32 = 0;
for (unsigned palnum = 0; palnum < 3; ++palnum)
{
for (unsigned colornum = 0; colornum < 4; ++colornum)
{
rgb32 = gbcToRgb32(gbc_bios_palette[palnum * 4 + colornum]);
gb.setDmgPaletteColor(palnum, colornum, rgb32);
continue;
}
}
// Set the new palette
if ([displayModePrefKey isEqualToString:@"palette"])
[self changePalette:displayMode];
else if ([displayModePrefKey isEqualToString:@"colorCorrection"])
gb.setCgbColorCorrection([displayMode isEqual:@"Modern"] ? 1 : 0);
}
# pragma mark - Misc Helper Methods
@@ -465,10 +477,10 @@ NSMutableDictionary *cheatList = [[NSMutableDictionary alloc] init];
if (!frames)
return;
size_t len = resampler->resample(outSoundBuffer, reinterpret_cast<const int16_t *>(inSoundBuffer), frames);
size_t len = resampler->resample(_outSoundBuffer, reinterpret_cast<const int16_t *>(_inSoundBuffer), frames);
if (len)
[[self ringBufferAtIndex:0] write:outSoundBuffer maxLength:len << 2];
[[self audioBufferAtIndex:0] write:_outSoundBuffer maxLength:len << 2];
}
- (void)applyCheat:(NSString *)code
@@ -480,22 +492,149 @@ NSMutableDictionary *cheatList = [[NSMutableDictionary alloc] init];
gb.setGameShark(s);
}
- (void)loadDisplayModeOptions
{
if (gb.isCgb())
{
// Restore color correction
NSString *lastColorCorrection = self.displayModeInfo[@"colorCorrection"] ?: @"Default";
[self changeDisplayWithMode:lastColorCorrection];
}
else
{
// Load built-in GBC palette for monochrome games if supported
[self loadPalette];
}
}
- (NSString *)gameInternalName
{
NSString *title = [NSString stringWithUTF8String:gb.romTitle().c_str()];
return title;
}
- (BOOL)gameHasInternalPalette
{
unsigned short *gbc_bios_palette = NULL;
NSString *title = [self gameInternalName];
gbc_bios_palette = const_cast<unsigned short *>(findGbcTitlePal(title.UTF8String));
return gbc_bios_palette != 0 ? YES : NO;
}
- (void)loadPalette
{
std::string str = gb.romTitle(); // read ROM internal title
const char *internal_game_name = str.c_str();
// load a GBC BIOS builtin palette
unsigned short *gbc_bios_palette = NULL;
gbc_bios_palette = const_cast<unsigned short *>(findGbcTitlePal(internal_game_name));
if (gbc_bios_palette == 0)
// Only temporary, so core doesn't crash on an older OpenEmu version
if (![self respondsToSelector:@selector(displayModeInfo)])
{
// no custom palette found, load the default (Original Grayscale)
gbc_bios_palette = const_cast<unsigned short *>(findGbcDirPal("GBC - Grayscale"));
[self loadPaletteDefault];
}
// No previous palette saved, set a default
else if (self.displayModeInfo[@"palette"] == nil)
{
[self loadPaletteDefault];
}
else
{
NSString *lastPalette = self.displayModeInfo[@"palette"];
// Don't try to load "Internal" palette for a game without one
if ([lastPalette isEqualToString:@"Internal"] && ![self gameHasInternalPalette])
[self changeDisplayWithMode:@"Grayscale"];
else
[self changeDisplayWithMode:lastPalette];
}
}
unsigned rgb32 = 0;
- (void)loadPaletteDefault
{
if ([self gameHasInternalPalette])
// load a GBC BIOS builtin palette
[self changeDisplayWithMode:@"Internal"];
else
// no custom palette found, load the default (Original Grayscale)
[self changeDisplayWithMode:@"Grayscale"];
}
- (void)changePalette:(NSString *)palette
{
NSDictionary <NSString *, NSString *> *paletteNames =
@{
@"Internal" : @"Internal",
@"Grayscale" : @"GBC - Grayscale",
@"Greenscale" : @"Greenscale",
@"Pocket" : @"Pocket",
@"Blue" : @"GBC - Blue",
@"Dark Blue" : @"GBC - Dark Blue",
@"Green" : @"GBC - Green",
@"Dark Green" : @"GBC - Dark Green",
@"Brown" : @"GBC - Brown",
@"Dark Brown" : @"GBC - Dark Brown",
@"Red" : @"GBC - Red",
@"Yellow" : @"GBC - Yellow",
@"Orange" : @"GBC - Orange",
@"Pastel Mix" : @"GBC - Pastel Mix",
@"Inverted" : @"GBC - Inverted",
};
palette = paletteNames[palette];
unsigned short *gbc_bios_palette = NULL;
if ([palette isEqualToString:@"Internal"])
{
NSString *title = [self gameInternalName];
gbc_bios_palette = const_cast<unsigned short *>(findGbcTitlePal(title.UTF8String));
}
else if ([palette isEqualToString:@"Greenscale"])
{
// GB Pea Soup Green
gb.setDmgPaletteColor(0, 0, 8369468);
gb.setDmgPaletteColor(0, 1, 6728764);
gb.setDmgPaletteColor(0, 2, 3629872);
gb.setDmgPaletteColor(0, 3, 3223857);
gb.setDmgPaletteColor(1, 0, 8369468);
gb.setDmgPaletteColor(1, 1, 6728764);
gb.setDmgPaletteColor(1, 2, 3629872);
gb.setDmgPaletteColor(1, 3, 3223857);
gb.setDmgPaletteColor(2, 0, 8369468);
gb.setDmgPaletteColor(2, 1, 6728764);
gb.setDmgPaletteColor(2, 2, 3629872);
gb.setDmgPaletteColor(2, 3, 3223857);
return;
}
else if ([palette isEqualToString:@"Pocket"])
{
// GB Pocket
gb.setDmgPaletteColor(0, 0, 13487791);
gb.setDmgPaletteColor(0, 1, 10987158);
gb.setDmgPaletteColor(0, 2, 6974033);
gb.setDmgPaletteColor(0, 3, 2828823);
gb.setDmgPaletteColor(1, 0, 13487791);
gb.setDmgPaletteColor(1, 1, 10987158);
gb.setDmgPaletteColor(1, 2, 6974033);
gb.setDmgPaletteColor(1, 3, 2828823);
gb.setDmgPaletteColor(2, 0, 13487791);
gb.setDmgPaletteColor(2, 1, 10987158);
gb.setDmgPaletteColor(2, 2, 6974033);
gb.setDmgPaletteColor(2, 3, 2828823);
// gb.setDmgPaletteColor(0, 0, 13029285);
// gb.setDmgPaletteColor(0, 1, 9213547);
// gb.setDmgPaletteColor(0, 2, 4870457);
// gb.setDmgPaletteColor(0, 3, 1580056);
// gb.setDmgPaletteColor(1, 0, 13029285);
// gb.setDmgPaletteColor(1, 1, 9213547);
// gb.setDmgPaletteColor(1, 2, 4870457);
// gb.setDmgPaletteColor(1, 3, 1580056);
// gb.setDmgPaletteColor(2, 0, 13029285);
// gb.setDmgPaletteColor(2, 1, 9213547);
// gb.setDmgPaletteColor(2, 2, 4870457);
// gb.setDmgPaletteColor(2, 3, 1580056);
return;
}
else
gbc_bios_palette = const_cast<unsigned short *>(findGbcDirPal(palette.UTF8String));
unsigned long rgb32 = 0;
for (unsigned palnum = 0; palnum < 3; ++palnum)
{
for (unsigned colornum = 0; colornum < 4; ++colornum)
+54 -135
View File
@@ -38,6 +38,7 @@
828387BB0E6CDB6500A96E2C /* gameboy.icns in Resources */ = {isa = PBXBuildFile; fileRef = B5F6D8A80E66914F001CA5D3 /* gameboy.icns */; };
828387BD0E6CDB6500A96E2C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165FFE840EACC02AAC07 /* InfoPlist.strings */; };
828387E20E6CDB6E00A96E2C /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A7FEA54F5311CA2CBB /* Cocoa.framework */; };
8F14C4F929FF7217000D080B /* statesaver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8F14C4F829FF7217000D080B /* statesaver.cpp */; };
9499B5D81AB242B200276D21 /* chainresampler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9499B54E1AB242B200276D21 /* chainresampler.cpp */; };
9499B5D91AB242B200276D21 /* i0.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9499B5541AB242B200276D21 /* i0.cpp */; };
9499B5DA1AB242B200276D21 /* kaiser50sinc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9499B5561AB242B200276D21 /* kaiser50sinc.cpp */; };
@@ -67,7 +68,6 @@
9499B5FE1AB242B300276D21 /* length_counter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9499B5BA1AB242B200276D21 /* length_counter.cpp */; };
9499B5FF1AB242B300276D21 /* sound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9499B5BF1AB242B200276D21 /* sound.cpp */; };
9499B6001AB242B300276D21 /* state_osd_elements.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9499B5C11AB242B200276D21 /* state_osd_elements.cpp */; };
9499B6011AB242B300276D21 /* statesaver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9499B5C31AB242B200276D21 /* statesaver.cpp */; };
9499B6021AB242B300276D21 /* tima.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9499B5C51AB242B200276D21 /* tima.cpp */; };
9499B6031AB242B300276D21 /* ly_counter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9499B5C91AB242B200276D21 /* ly_counter.cpp */; };
9499B6041AB242B300276D21 /* lyc_irq.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9499B5CB1AB242B200276D21 /* lyc_irq.cpp */; };
@@ -109,17 +109,14 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
089C1660FE840EACC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
1058C7A7FEA54F5311CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
089C1660FE840EACC02AAC07 /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
1058C7A7FEA54F5311CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
828387810E6CDB2200A96E2C /* Gambatte.oecoreplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Gambatte.oecoreplugin; sourceTree = BUILT_PRODUCTS_DIR; };
828387820E6CDB2200A96E2C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
942597C7151470210074E3A3 /* OpenEmuBase.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenEmuBase.framework; path = ../OpenEmu/build/Release/OpenEmuBase.framework; sourceTree = "<group>"; };
9499B5431AB242B200276D21 /* adaptivesleep.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = adaptivesleep.cpp; sourceTree = "<group>"; };
9499B5441AB242B200276D21 /* adaptivesleep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = adaptivesleep.h; sourceTree = "<group>"; };
8F14C4F729FF7204000D080B /* memfile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = memfile.h; sourceTree = "<group>"; };
8F14C4F829FF7217000D080B /* statesaver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = statesaver.cpp; sourceTree = "<group>"; };
9499B5451AB242B200276D21 /* array.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = array.h; sourceTree = "<group>"; };
9499B5461AB242B200276D21 /* defined_ptr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = defined_ptr.h; sourceTree = "<group>"; };
9499B5471AB242B200276D21 /* rateest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rateest.cpp; sourceTree = "<group>"; };
9499B5481AB242B200276D21 /* rateest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rateest.h; sourceTree = "<group>"; };
9499B54A1AB242B200276D21 /* resampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = resampler.h; sourceTree = "<group>"; };
9499B54B1AB242B200276D21 /* resamplerinfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = resamplerinfo.h; sourceTree = "<group>"; };
9499B54D1AB242B200276D21 /* blackmansinc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = blackmansinc.h; sourceTree = "<group>"; };
@@ -146,34 +143,14 @@
9499B5621AB242B200276D21 /* u48div.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = u48div.cpp; sourceTree = "<group>"; };
9499B5631AB242B200276D21 /* u48div.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = u48div.h; sourceTree = "<group>"; };
9499B5641AB242B200276D21 /* upsampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = upsampler.h; sourceTree = "<group>"; };
9499B5651AB242B200276D21 /* ringbuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ringbuffer.h; sourceTree = "<group>"; };
9499B5661AB242B200276D21 /* scoped_ptr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scoped_ptr.h; sourceTree = "<group>"; };
9499B5671AB242B200276D21 /* skipsched.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = skipsched.cpp; sourceTree = "<group>"; };
9499B5681AB242B200276D21 /* skipsched.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = skipsched.h; sourceTree = "<group>"; };
9499B5691AB242B200276D21 /* transfer_ptr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = transfer_ptr.h; sourceTree = "<group>"; };
9499B56A1AB242B200276D21 /* uncopyable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = uncopyable.h; sourceTree = "<group>"; };
9499B56B1AB242B200276D21 /* usec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = usec.h; sourceTree = "<group>"; };
9499B56D1AB242B200276D21 /* rgb32conv.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rgb32conv.cpp; sourceTree = "<group>"; };
9499B56E1AB242B200276D21 /* rgb32conv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rgb32conv.h; sourceTree = "<group>"; };
9499B56F1AB242B200276D21 /* vfilterinfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = vfilterinfo.cpp; sourceTree = "<group>"; };
9499B5701AB242B200276D21 /* vfilterinfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vfilterinfo.h; sourceTree = "<group>"; };
9499B5721AB242B200276D21 /* catrom2x.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = catrom2x.cpp; sourceTree = "<group>"; };
9499B5731AB242B200276D21 /* catrom2x.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = catrom2x.h; sourceTree = "<group>"; };
9499B5741AB242B200276D21 /* catrom3x.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = catrom3x.cpp; sourceTree = "<group>"; };
9499B5751AB242B200276D21 /* catrom3x.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = catrom3x.h; sourceTree = "<group>"; };
9499B5761AB242B200276D21 /* kreed2xsai.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = kreed2xsai.cpp; sourceTree = "<group>"; };
9499B5771AB242B200276D21 /* kreed2xsai.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kreed2xsai.h; sourceTree = "<group>"; };
9499B5781AB242B200276D21 /* maxsthq2x.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = maxsthq2x.cpp; sourceTree = "<group>"; };
9499B5791AB242B200276D21 /* maxsthq2x.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = maxsthq2x.h; sourceTree = "<group>"; };
9499B57A1AB242B200276D21 /* maxsthq3x.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = maxsthq3x.cpp; sourceTree = "<group>"; };
9499B57B1AB242B200276D21 /* maxsthq3x.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = maxsthq3x.h; sourceTree = "<group>"; };
9499B57C1AB242B200276D21 /* videolink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = videolink.h; sourceTree = "<group>"; };
9499B57F1AB242B200276D21 /* gambatte.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gambatte.h; sourceTree = "<group>"; };
9499B5801AB242B200276D21 /* gbint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gbint.h; sourceTree = "<group>"; };
9499B5811AB242B200276D21 /* inputgetter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = inputgetter.h; sourceTree = "<group>"; };
9499B5821AB242B200276D21 /* loadres.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = loadres.h; sourceTree = "<group>"; };
9499B5831AB242B200276D21 /* pakinfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pakinfo.h; sourceTree = "<group>"; };
9499B5841AB242B200276D21 /* SConstruct */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = SConstruct; sourceTree = "<group>"; };
9499B5861AB242B200276D21 /* bitmap_font.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bitmap_font.cpp; sourceTree = "<group>"; };
9499B5871AB242B200276D21 /* bitmap_font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bitmap_font.h; sourceTree = "<group>"; };
9499B5881AB242B200276D21 /* counterdef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = counterdef.h; sourceTree = "<group>"; };
@@ -181,13 +158,7 @@
9499B58A1AB242B200276D21 /* cpu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cpu.h; sourceTree = "<group>"; };
9499B58C1AB242B200276D21 /* file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file.cpp; sourceTree = "<group>"; };
9499B58D1AB242B200276D21 /* file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file.h; sourceTree = "<group>"; };
9499B58E1AB242B200276D21 /* file_zip.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_zip.cpp; sourceTree = "<group>"; };
9499B58F1AB242B200276D21 /* stdfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stdfile.h; sourceTree = "<group>"; };
9499B5911AB242B200276D21 /* crypt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = crypt.h; sourceTree = "<group>"; };
9499B5921AB242B200276D21 /* ioapi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ioapi.c; sourceTree = "<group>"; };
9499B5931AB242B200276D21 /* ioapi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ioapi.h; sourceTree = "<group>"; };
9499B5941AB242B200276D21 /* unzip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = unzip.c; sourceTree = "<group>"; };
9499B5951AB242B200276D21 /* unzip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = unzip.h; sourceTree = "<group>"; };
9499B5961AB242B200276D21 /* gambatte.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gambatte.cpp; sourceTree = "<group>"; };
9499B5971AB242B200276D21 /* initstate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = initstate.cpp; sourceTree = "<group>"; };
9499B5981AB242B200276D21 /* initstate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = initstate.h; sourceTree = "<group>"; };
@@ -240,7 +211,6 @@
9499B5CA1AB242B200276D21 /* ly_counter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ly_counter.h; sourceTree = "<group>"; };
9499B5CB1AB242B200276D21 /* lyc_irq.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lyc_irq.cpp; sourceTree = "<group>"; };
9499B5CC1AB242B200276D21 /* lyc_irq.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lyc_irq.h; sourceTree = "<group>"; };
9499B5CD1AB242B200276D21 /* m0_irq.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = m0_irq.h; sourceTree = "<group>"; };
9499B5CE1AB242B200276D21 /* next_m0_time.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = next_m0_time.cpp; sourceTree = "<group>"; };
9499B5CF1AB242B200276D21 /* next_m0_time.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = next_m0_time.h; sourceTree = "<group>"; };
9499B5D01AB242B200276D21 /* ppu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ppu.cpp; sourceTree = "<group>"; };
@@ -249,11 +219,11 @@
9499B5D31AB242B200276D21 /* sprite_mapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sprite_mapper.h; sourceTree = "<group>"; };
9499B5D41AB242B200276D21 /* video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = video.cpp; sourceTree = "<group>"; };
9499B5D51AB242B200276D21 /* video.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = video.h; sourceTree = "<group>"; };
B5350B7C0E62EC0800A0903A /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = /System/Library/Frameworks/OpenGL.framework; sourceTree = "<absolute>"; };
B5350B7C0E62EC0800A0903A /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
B5EC4D410E6312DF0046BD93 /* GBGameCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GBGameCore.h; sourceTree = "<group>"; };
B5EC4D420E6312DF0046BD93 /* GBGameCore.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = GBGameCore.mm; sourceTree = "<group>"; };
B5F6D8A80E66914F001CA5D3 /* gameboy.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = gameboy.icns; sourceTree = "<group>"; };
C6B947DE1364FD0C00A425F0 /* OEGBSystemResponderClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OEGBSystemResponderClient.h; path = ../OpenEmu/GameBoy/OEGBSystemResponderClient.h; sourceTree = "<group>"; };
C6B947DE1364FD0C00A425F0 /* OEGBSystemResponderClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OEGBSystemResponderClient.h; path = ../OpenEmu/SystemPlugins/GameBoy/OEGBSystemResponderClient.h; sourceTree = "<group>"; };
C6D120ED1711308C00E868A8 /* OpenEmuBase.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OpenEmuBase.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
@@ -273,7 +243,6 @@
1058C7A6FEA54F5311CA2CBB /* Linked Frameworks */ = {
isa = PBXGroup;
children = (
942597C7151470210074E3A3 /* OpenEmuBase.framework */,
B5350B7C0E62EC0800A0903A /* OpenGL.framework */,
1058C7A7FEA54F5311CA2CBB /* Cocoa.framework */,
);
@@ -307,6 +276,7 @@
);
name = Gambatte;
sourceTree = "<group>";
usesTabs = 0;
};
2A37F4ABFDCFA73011CA2CEA /* Classes */ = {
isa = PBXGroup;
@@ -314,6 +284,7 @@
C6B947DE1364FD0C00A425F0 /* OEGBSystemResponderClient.h */,
B5EC4D410E6312DF0046BD93 /* GBGameCore.h */,
B5EC4D420E6312DF0046BD93 /* GBGameCore.mm */,
8F14C4F829FF7217000D080B /* statesaver.cpp */,
);
name = Classes;
sourceTree = "<group>";
@@ -345,26 +316,18 @@
name = Frameworks;
sourceTree = "<group>";
};
9499B5421AB242B200276D21 /* common */ = {
9499B5421AB242B200276D21 /* src */ = {
isa = PBXGroup;
children = (
9499B5431AB242B200276D21 /* adaptivesleep.cpp */,
9499B5441AB242B200276D21 /* adaptivesleep.h */,
9499B5451AB242B200276D21 /* array.h */,
9499B5461AB242B200276D21 /* defined_ptr.h */,
9499B5471AB242B200276D21 /* rateest.cpp */,
9499B5481AB242B200276D21 /* rateest.h */,
9499B5851AB242B200276D21 /* libgambatte */,
9499B5491AB242B200276D21 /* resample */,
9499B5651AB242B200276D21 /* ringbuffer.h */,
9499B5661AB242B200276D21 /* scoped_ptr.h */,
9499B5671AB242B200276D21 /* skipsched.cpp */,
9499B5681AB242B200276D21 /* skipsched.h */,
9499B5691AB242B200276D21 /* transfer_ptr.h */,
9499B56A1AB242B200276D21 /* uncopyable.h */,
9499B56B1AB242B200276D21 /* usec.h */,
9499B56C1AB242B200276D21 /* videolink */,
);
path = common;
path = src;
sourceTree = "<group>";
};
9499B5491AB242B200276D21 /* resample */ = {
@@ -372,14 +335,6 @@
children = (
9499B54A1AB242B200276D21 /* resampler.h */,
9499B54B1AB242B200276D21 /* resamplerinfo.h */,
9499B54C1AB242B200276D21 /* src */,
);
path = resample;
sourceTree = "<group>";
};
9499B54C1AB242B200276D21 /* src */ = {
isa = PBXGroup;
children = (
9499B54D1AB242B200276D21 /* blackmansinc.h */,
9499B54E1AB242B200276D21 /* chainresampler.cpp */,
9499B54F1AB242B200276D21 /* chainresampler.h */,
@@ -405,62 +360,10 @@
9499B5631AB242B200276D21 /* u48div.h */,
9499B5641AB242B200276D21 /* upsampler.h */,
);
path = src;
path = resample;
sourceTree = "<group>";
};
9499B56C1AB242B200276D21 /* videolink */ = {
isa = PBXGroup;
children = (
9499B56D1AB242B200276D21 /* rgb32conv.cpp */,
9499B56E1AB242B200276D21 /* rgb32conv.h */,
9499B56F1AB242B200276D21 /* vfilterinfo.cpp */,
9499B5701AB242B200276D21 /* vfilterinfo.h */,
9499B5711AB242B200276D21 /* vfilters */,
9499B57C1AB242B200276D21 /* videolink.h */,
);
path = videolink;
sourceTree = "<group>";
};
9499B5711AB242B200276D21 /* vfilters */ = {
isa = PBXGroup;
children = (
9499B5721AB242B200276D21 /* catrom2x.cpp */,
9499B5731AB242B200276D21 /* catrom2x.h */,
9499B5741AB242B200276D21 /* catrom3x.cpp */,
9499B5751AB242B200276D21 /* catrom3x.h */,
9499B5761AB242B200276D21 /* kreed2xsai.cpp */,
9499B5771AB242B200276D21 /* kreed2xsai.h */,
9499B5781AB242B200276D21 /* maxsthq2x.cpp */,
9499B5791AB242B200276D21 /* maxsthq2x.h */,
9499B57A1AB242B200276D21 /* maxsthq3x.cpp */,
9499B57B1AB242B200276D21 /* maxsthq3x.h */,
);
path = vfilters;
sourceTree = "<group>";
};
9499B57D1AB242B200276D21 /* libgambatte */ = {
isa = PBXGroup;
children = (
9499B57E1AB242B200276D21 /* include */,
9499B5841AB242B200276D21 /* SConstruct */,
9499B5851AB242B200276D21 /* src */,
);
path = libgambatte;
sourceTree = "<group>";
};
9499B57E1AB242B200276D21 /* include */ = {
isa = PBXGroup;
children = (
9499B57F1AB242B200276D21 /* gambatte.h */,
9499B5801AB242B200276D21 /* gbint.h */,
9499B5811AB242B200276D21 /* inputgetter.h */,
9499B5821AB242B200276D21 /* loadres.h */,
9499B5831AB242B200276D21 /* pakinfo.h */,
);
path = include;
sourceTree = "<group>";
};
9499B5851AB242B200276D21 /* src */ = {
9499B5851AB242B200276D21 /* libgambatte */ = {
isa = PBXGroup;
children = (
9499B5861AB242B200276D21 /* bitmap_font.cpp */,
@@ -470,19 +373,24 @@
9499B58A1AB242B200276D21 /* cpu.h */,
9499B58B1AB242B200276D21 /* file */,
9499B5961AB242B200276D21 /* gambatte.cpp */,
9499B57F1AB242B200276D21 /* gambatte.h */,
9499B5801AB242B200276D21 /* gbint.h */,
9499B5971AB242B200276D21 /* initstate.cpp */,
9499B5981AB242B200276D21 /* initstate.h */,
9499B5811AB242B200276D21 /* inputgetter.h */,
9499B5991AB242B200276D21 /* insertion_sort.h */,
9499B59A1AB242B200276D21 /* interrupter.cpp */,
9499B59B1AB242B200276D21 /* interrupter.h */,
9499B59C1AB242B200276D21 /* interruptrequester.cpp */,
9499B59D1AB242B200276D21 /* interruptrequester.h */,
9499B59E1AB242B200276D21 /* loadres.cpp */,
9499B5821AB242B200276D21 /* loadres.h */,
9499B59F1AB242B200276D21 /* mem */,
9499B5A81AB242B200276D21 /* memory.cpp */,
9499B5A91AB242B200276D21 /* memory.h */,
9499B5AA1AB242B200276D21 /* minkeeper.h */,
9499B5AB1AB242B200276D21 /* osd_element.h */,
9499B5831AB242B200276D21 /* pakinfo.h */,
9499B5AC1AB242B200276D21 /* savestate.h */,
9499B5AD1AB242B200276D21 /* sound */,
9499B5BF1AB242B200276D21 /* sound.cpp */,
@@ -497,7 +405,7 @@
9499B5D41AB242B200276D21 /* video.cpp */,
9499B5D51AB242B200276D21 /* video.h */,
);
path = src;
path = libgambatte;
sourceTree = "<group>";
};
9499B58B1AB242B200276D21 /* file */ = {
@@ -505,25 +413,12 @@
children = (
9499B58C1AB242B200276D21 /* file.cpp */,
9499B58D1AB242B200276D21 /* file.h */,
9499B58E1AB242B200276D21 /* file_zip.cpp */,
8F14C4F729FF7204000D080B /* memfile.h */,
9499B58F1AB242B200276D21 /* stdfile.h */,
9499B5901AB242B200276D21 /* unzip */,
);
path = file;
sourceTree = "<group>";
};
9499B5901AB242B200276D21 /* unzip */ = {
isa = PBXGroup;
children = (
9499B5911AB242B200276D21 /* crypt.h */,
9499B5921AB242B200276D21 /* ioapi.c */,
9499B5931AB242B200276D21 /* ioapi.h */,
9499B5941AB242B200276D21 /* unzip.c */,
9499B5951AB242B200276D21 /* unzip.h */,
);
path = unzip;
sourceTree = "<group>";
};
9499B59F1AB242B200276D21 /* mem */ = {
isa = PBXGroup;
children = (
@@ -571,7 +466,6 @@
9499B5CA1AB242B200276D21 /* ly_counter.h */,
9499B5CB1AB242B200276D21 /* lyc_irq.cpp */,
9499B5CC1AB242B200276D21 /* lyc_irq.h */,
9499B5CD1AB242B200276D21 /* m0_irq.h */,
9499B5CE1AB242B200276D21 /* next_m0_time.cpp */,
9499B5CF1AB242B200276D21 /* next_m0_time.h */,
9499B5D01AB242B200276D21 /* ppu.cpp */,
@@ -585,8 +479,7 @@
B5A6D9310E617C4900622CCF /* Core */ = {
isa = PBXGroup;
children = (
9499B5421AB242B200276D21 /* common */,
9499B57D1AB242B200276D21 /* libgambatte */,
9499B5421AB242B200276D21 /* src */,
);
name = Core;
sourceTree = "<group>";
@@ -617,15 +510,15 @@
2A37F4A9FDCFA73011CA2CEA /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 0700;
LastUpgradeCheck = 1130;
};
buildConfigurationList = C05733CB08A9546B00998B17 /* Build configuration list for PBXProject "Gambatte" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
developmentRegion = en;
hasScannedForEncodings = 1;
knownRegions = (
en,
Base,
);
mainGroup = 2A37F4AAFDCFA73011CA2CEA /* Gambatte */;
projectDirPath = "";
@@ -703,7 +596,7 @@
9499B5F71AB242B300276D21 /* memory.cpp in Sources */,
9499B5FF1AB242B300276D21 /* sound.cpp in Sources */,
9499B6001AB242B300276D21 /* state_osd_elements.cpp in Sources */,
9499B6011AB242B300276D21 /* statesaver.cpp in Sources */,
8F14C4F929FF7217000D080B /* statesaver.cpp in Sources */,
9499B6021AB242B300276D21 /* tima.cpp in Sources */,
9499B6081AB242B300276D21 /* video.cpp in Sources */,
9499B5F31AB242B300276D21 /* cartridge.cpp in Sources */,
@@ -744,7 +637,7 @@
089C165FFE840EACC02AAC07 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
089C1660FE840EACC02AAC07 /* English */,
089C1660FE840EACC02AAC07 /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
@@ -756,6 +649,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_IDENTITY = "";
COMBINE_HIDPI_IMAGES = YES;
HEADER_SEARCH_PATHS = (
"\"$(PROJECT_DIR)/gambatte\"",
@@ -777,6 +671,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_IDENTITY = "";
COMBINE_HIDPI_IMAGES = YES;
HEADER_SEARCH_PATHS = (
"\"$(PROJECT_DIR)/gambatte\"",
@@ -840,20 +735,32 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = NO;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = c99;
GCC_INCREASE_PRECOMPILED_HEADER_SHARING = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = GL_SILENCE_DEPRECATION;
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@@ -875,11 +782,21 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
DEAD_CODE_STRIPPING = YES;
@@ -888,7 +805,9 @@
ENABLE_TESTABILITY = NO;
GCC_C_LANGUAGE_STANDARD = c99;
GCC_INCREASE_PRECOMPILED_HEADER_SHARING = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 3;
GCC_PREPROCESSOR_DEFINITIONS = GL_SILENCE_DEPRECATION;
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+5 -3
View File
@@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
@@ -17,7 +17,7 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>0.5.0.573</string>
<string>0.5.1</string>
<key>NSPrincipalClass</key>
<string>OEGameCoreController</string>
<key>OEGameCoreClass</key>
@@ -32,6 +32,8 @@
<integer>0</integer>
<key>OEGameCoreSupportsCheatCode</key>
<true/>
<key>OEGameCoreSupportsDisplayModeChange</key>
<true/>
<key>OEGameCoreSupportsRewinding</key>
<true/>
</dict>
@@ -39,7 +41,7 @@
<key>OEGameCorePlayerCount</key>
<string>1</string>
<key>OEProjectURL</key>
<string>https://github.com/sinamas/gambatte</string>
<string>https://gitlab.com/jgemu/gambatte</string>
<key>OESystemIdentifiers</key>
<array>
<string>openemu.system.gb</string>
+58
View File
@@ -0,0 +1,58 @@
Gambatte JG
-----------
Gambatte JG is an emulator for the Nintendo Game Boy/Game Boy Color.
This is a fork of the final public revision of Gambatte.
This repository lives at https://gitlab.com/jgemu/gambatte
Compiling
---------
Make sure you have The Jolly Good API's header files installed. If you did
not install them, you will be required to include their path in CXXFLAGS.
GNU Make's default behaviour for compiling C++ sources is to use g++. If your
platform of choice uses an unpatched GNU Make, you will need to override the
CXX implicit variable if you wish to use a different compiler.
Options:
USE_VENDORED_SOXR - Set non-zero to use vendored soxr
Linux:
make
macOS:
make
BSD:
gmake
Windows (MSYS2):
make
The build will be output to "gambatte/". This directory may be used as is
locally by copying it to your local "cores" directory, or may be installed
system-wide using the "install" target specified in the Makefile.
Settings
--------
palette = 0
0 = Internal, 1 = Original Green, 2 = GB Pocket, 3 = Blue, 4 = Brown,
5 = Dark Blue, 6 = Dark Brown, 7 = Dark Green, 8 = Grayscale, 9 = Green,
10 = Inverted, 11 = Orange, 12 = Pastel Mix, 13 = Red, 14 = Yellow
system = 0
0 = Auto (CGB), 1 = DMG, 2 = GBA-CGB
turbo_rate = 3
N = Pulse every N frames
Copyright
---------
Gambatte JG (GPL-2.0-or-later)
Copyright (c) 2007-2020 Sindre Aamås
Copyright (c) 2020-2022 Rupert Carmichael
SoX Resampler Library (LGPL-2.1-or-later)
Copyright (c) 2007-2018 Rob Sykes
See source files in deps/soxr/ (https://sourceforge.net/projects/soxr/)
-52
View File
@@ -1,52 +0,0 @@
/***************************************************************************
* 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"
static usec_t absdiff(usec_t a, usec_t b) { return a < b ? b - a : a - b; }
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);
usec_t const sleepTarget = now + diff;
now = getusecs();
usec_t curOversleep = now - sleepTarget;
if (curOversleep > usec_t(-1) / 2)
curOversleep = 0;
oversleepVar_ = (oversleepVar_ * 15 + absdiff(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;
}
-35
View File
@@ -1,35 +0,0 @@
/***************************************************************************
* 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 {
public:
AdaptiveSleep() : oversleep_(0), oversleepVar_(0), noSleep_(60) {}
usec_t sleepUntil(usec_t base, usec_t inc);
private:
usec_t oversleep_;
usec_t oversleepVar_;
unsigned noSleep_;
};
#endif
-91
View File
@@ -1,91 +0,0 @@
/***************************************************************************
* 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::push(std::ptrdiff_t const samples, usec_t const usecs) {
q_.push_back(std::make_pair(samples, usecs));
samples_ += samples;
usecs_ += usecs;
}
void RateEst::SumQueue::pop() {
std::pair<std::ptrdiff_t, usec_t> const &f = q_.front();
samples_ -= f.first;
usecs_ -= f.second;
q_.pop_front();
}
static usec_t sampleUsecs(std::ptrdiff_t samples, long rate) {
return usec_t((samples * 1000000.0f) / (rate ? rate : 1) + 0.5f);
}
static long limit(long est, long const reference) {
if (est > reference + (reference >> 6))
est = reference + (reference >> 6);
else if (est < reference - (reference >> 6))
est = reference - (reference >> 6);
return est;
}
RateEst::RateEst(long const nominalSampleRate, std::size_t const maxValidFeedPeriodSamples)
: srate_(nominalSampleRate * est_scale)
, reference_(srate_)
, maxPeriod_(sampleUsecs(maxValidFeedPeriodSamples, nominalSampleRate))
, last_(0)
, t_(6000)
, s_(nominalSampleRate * 6)
, st_(s_ * t_)
, t2_(t_ * t_)
{
}
void RateEst::feed(std::ptrdiff_t samplesIn, usec_t const now) {
usec_t usecsIn = now - last_;
if (last_ && usecsIn < maxPeriod_) {
sumq_.push(samplesIn, usecsIn);
while ((usecsIn = sumq_.usecs()) > 100000) {
samplesIn = sumq_.samples();
sumq_.pop();
long const srateIn = long(samplesIn * (1000000.0f * est_scale) / usecsIn);
if (std::abs(srateIn - reference_) < reference_ >> 1) {
s_ += samplesIn - sumq_.samples() ;
t_ += ( usecsIn - sumq_.usecs() ) * 0.001;
st_ += s_ * t_;
t2_ += t_ * t_;
long est = long(st_ * (1000.0 * est_scale) / t2_ + 0.5);
srate_ = limit((srate_ * 31 + est + 16) >> 5, reference_);
if (t_ > 8000) {
s_ *= 3.0 / 4;
t_ *= 3.0 / 4;
st_ *= 9.0 / 16;
t2_ *= 9.0 / 16;
}
}
}
}
last_ = now;
}
-61
View File
@@ -1,61 +0,0 @@
/***************************************************************************
* 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 <cstddef>
#include <deque>
#include <utility>
class RateEst {
public:
RateEst() { *this = RateEst(0, 0); }
RateEst(long nominalSampleRate, std::size_t maxValidFeedPeriodSamples);
void resetLastFeedTimeStamp() { last_ = 0; }
void feed(std::ptrdiff_t samples, usec_t usecsNow = getusecs());
long result() const { return (srate_ + est_scale / 2) >> est_lshift; }
private:
class SumQueue {
public:
SumQueue() : samples_(0), usecs_(0) {}
std::ptrdiff_t samples() const { return samples_; }
usec_t usecs() const { return usecs_; }
void push(std::ptrdiff_t samples, usec_t usecs);
void pop();
private:
std::deque< std::pair<std::ptrdiff_t, usec_t> > q_;
std::ptrdiff_t samples_;
usec_t usecs_;
};
enum { est_lshift = 5 };
enum { est_scale = 1 << est_lshift };
SumQueue sumq_;
long srate_;
long reference_;
usec_t maxPeriod_;
usec_t last_;
double t_, s_, st_, t2_;
};
#endif
-109
View File
@@ -1,109 +0,0 @@
/***************************************************************************
* 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 RINGBUFFER_H
#define RINGBUFFER_H
#include "array.h"
#include <algorithm>
#include <cstddef>
#include <cstring>
template<typename T>
class RingBuffer {
public:
explicit RingBuffer(std::size_t size = 0)
: endpos_(0), rpos_(0), wpos_(0)
{
reset(size);
}
void reset(std::size_t size);
void clear() {
wpos_ = rpos_ = 0;
}
void fill(T value);
void read(T *out, std::size_t num);
void write(T const *in, std::size_t num);
std::size_t avail() const {
return (wpos_ < rpos_ ? 0 : endpos_) + rpos_ - wpos_ - 1;
}
std::size_t used() const {
return (wpos_ < rpos_ ? endpos_ : 0) + wpos_ - rpos_;
}
std::size_t size() const {
return endpos_ - 1;
}
private:
Array<T> buf_;
std::size_t endpos_;
std::size_t rpos_;
std::size_t wpos_;
};
template<typename T>
void RingBuffer<T>::reset(std::size_t size) {
endpos_ = size + 1;
rpos_ = wpos_ = 0;
buf_.reset(size ? endpos_ : 0);
}
template<typename T>
void RingBuffer<T>::fill(T value) {
std::fill(buf_.get(), buf_.get() + buf_.size(), value);
rpos_ = 0;
wpos_ = endpos_ - 1;
}
template<typename T>
void RingBuffer<T>::read(T *out, std::size_t num) {
if (rpos_ + num > endpos_) {
std::size_t const n = endpos_ - rpos_;
std::memcpy(out, buf_ + rpos_, n * sizeof *out);
rpos_ = 0;
num -= n;
out += n;
}
std::memcpy(out, buf_ + rpos_, num * sizeof *out);
if ((rpos_ += num) == endpos_)
rpos_ = 0;
}
template<typename T>
void RingBuffer<T>::write(T const *in, std::size_t num) {
if (wpos_ + num > endpos_) {
std::size_t const n = endpos_ - wpos_;
std::memcpy(buf_ + wpos_, in, n * sizeof *buf_);
wpos_ = 0;
num -= n;
in += n;
}
std::memcpy(buf_ + wpos_, in, num * sizeof *buf_);
if ((wpos_ += num) == endpos_)
wpos_ = 0;
}
#endif
-35
View File
@@ -1,35 +0,0 @@
/***************************************************************************
* 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 "skipsched.h"
bool SkipSched::skipNext(bool skip) {
if (skipped_) {
if (skipped_ < skippedmax_ / 2)
skip = true;
else
skipped_ = skip = 0;
} else if (skip) {
skippedmax_ += skippedmax_ / 2 < 8;
} else if (skippedmax_ / 2)
--skippedmax_;
skipped_ += skip;
return skip;
}
-32
View File
@@ -1,32 +0,0 @@
/***************************************************************************
* 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 SKIPSCHED_H
#define SKIPSCHED_H
class SkipSched {
public:
SkipSched() : skipped_(0), skippedmax_(2 - 1) {}
bool skipNext(bool wantskip);
private:
unsigned skipped_;
unsigned skippedmax_;
};
#endif
-27
View File
@@ -1,27 +0,0 @@
/***************************************************************************
* 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 USEC_H
#define USEC_H
typedef unsigned long usec_t;
usec_t getusecs();
void usecsleep(usec_t usecs);
#endif
-199
View File
@@ -1,199 +0,0 @@
/***************************************************************************
* 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 "rgb32conv.h"
#include "array.h"
#include "gbint.h"
#include "videolink.h"
#include <algorithm>
namespace {
static bool isBigEndian() {
union {
gambatte::uint_least32_t ul32;
unsigned char uc[sizeof(gambatte::uint_least32_t)];
} u;
u.ul32 = -0x10000;
return u.uc[0];
}
class Rgb32ToUyvy {
public:
Rgb32ToUyvy();
void operator()(gambatte::uint_least32_t *d, std::ptrdiff_t dstPitch,
gambatte::uint_least32_t const *s, std::ptrdiff_t srcPitch,
unsigned w, unsigned h);
private:
struct CacheUnit {
gambatte::uint_least32_t rgb32;
gambatte::uint_least32_t uyvy;
};
enum { cache_size = 0x100 };
enum { cache_mask = cache_size - 1 };
CacheUnit cache_[cache_size];
};
Rgb32ToUyvy::Rgb32ToUyvy() {
if (isBigEndian()) {
CacheUnit c = { 0, 128ul << 24 | 16ul << 16 | 128u << 8 | 16 };
std::fill(cache_, cache_ + cache_size, c);
} else {
CacheUnit c = { 0, 16ul << 24 | 128ul << 16 | 16 << 8 | 128 };
std::fill(cache_, cache_ + cache_size, c);
}
}
void Rgb32ToUyvy::operator()(gambatte::uint_least32_t *dst,
std::ptrdiff_t const dstPitch,
gambatte::uint_least32_t const *src,
std::ptrdiff_t const srcPitch,
unsigned const w,
unsigned h)
{
while (h--) {
gambatte::uint_least32_t *d = dst;
gambatte::uint_least32_t const *s = src;
gambatte::uint_least32_t const *const sEnd = s + w - 1;
while (s < sEnd) {
if ((cache_[s[0] & cache_mask].rgb32 - s[0]) | (cache_[s[1] & cache_mask].rgb32 - s[1])) {
cache_[s[0] & cache_mask].rgb32 = s[0];
cache_[s[1] & cache_mask].rgb32 = s[1];
unsigned long const r = (s[0] >> 16 & 0x000000FF) | (s[1] & 0x00FF0000);
unsigned long const g = (s[0] >> 8 & 0x000000FF) | (s[1] << 8 & 0x00FF0000);
unsigned long const b = (s[0] & 0x000000FF) | (s[1] << 16 & 0x00FF0000);
unsigned long const y = r * 66 + g * 129 + b * 25 + ( 16 * 256u + 128) * 0x00010001ul;
unsigned long const u = b * 112 - r * 38 - g * 74 + (128 * 256u + 128) * 0x00010001ul;
unsigned long const v = r * 112 - g * 94 - b * 18 + (128 * 256u + 128) * 0x00010001ul;
if (isBigEndian()) {
d[0] = cache_[s[0] & cache_mask].uyvy = (u << 16 & 0xFF000000)
| (y << 8 & 0x00FF0000)
| (v & 0x0000FF00)
| (y >> 8 & 0x000000FF);
d[1] = cache_[s[1] & cache_mask].uyvy = (u & 0xFF000000)
| (y >> 8 & 0x00FF0000)
| (v >> 16 & 0x0000FF00)
| y >> 24 ;
} else {
d[0] = cache_[s[0] & cache_mask].uyvy = (y << 16 & 0xFF000000)
| (v << 8 & 0x00FF0000)
| (y & 0x0000FF00)
| (u >> 8 & 0x000000FF);
d[1] = cache_[s[1] & cache_mask].uyvy = (y & 0xFF000000)
| (v >> 8 & 0x00FF0000)
| (y >> 16 & 0x0000FF00)
| u >> 24 ;
}
} else {
gambatte::uint_least32_t const s0 = s[0], s1 = s[1];
d[0] = cache_[s0 & cache_mask].uyvy;
d[1] = cache_[s1 & cache_mask].uyvy;
}
s += 2;
d += 2;
}
src += srcPitch;
dst += dstPitch;
}
}
static void rgb32ToRgb16(gambatte::uint_least16_t *d,
std::ptrdiff_t const dstPitch,
gambatte::uint_least32_t const *s,
std::ptrdiff_t const srcPitch,
unsigned const w,
unsigned h)
{
do {
std::ptrdiff_t i = -static_cast<std::ptrdiff_t>(w);
s += w;
d += w;
do {
d[i] = (s[i] >> 8 & 0xF800) | (s[i] & 0xFC00) >> 5 | (s[i] & 0xFF) >> 3;
} while (++i);
s += srcPitch - static_cast<std::ptrdiff_t>(w);
d += dstPitch - static_cast<std::ptrdiff_t>(w);
} while (--h);
}
class Rgb32ToUyvyLink : public VideoLink {
public:
Rgb32ToUyvyLink(unsigned width, unsigned height)
: inbuf_(static_cast<std::size_t>(width) * height)
, width_(width)
, height_(height)
{
}
virtual void * inBuf() const { return inbuf_; }
virtual std::ptrdiff_t inPitch() const { return width_; }
virtual void draw(void *dst, std::ptrdiff_t dstPitch) {
rgb32ToUyvy_(static_cast<gambatte::uint_least32_t *>(dst), dstPitch,
inbuf_, width_, width_, height_);
}
private:
SimpleArray<gambatte::uint_least32_t> const inbuf_;
Rgb32ToUyvy rgb32ToUyvy_;
unsigned const width_;
unsigned const height_;
};
class Rgb32ToRgb16Link : public VideoLink {
public:
Rgb32ToRgb16Link(unsigned width, unsigned height)
: inbuf_(static_cast<std::size_t>(width) * height)
, width_(width)
, height_(height)
{
}
virtual void * inBuf() const { return inbuf_; }
virtual std::ptrdiff_t inPitch() const { return width_; }
virtual void draw(void *dst, std::ptrdiff_t dstPitch) {
if (!inbuf_)
return;
rgb32ToRgb16(static_cast<gambatte::uint_least16_t *>(dst), dstPitch,
inbuf_, width_, width_, height_);
}
private:
SimpleArray<gambatte::uint_least32_t> const inbuf_;
unsigned const width_;
unsigned const height_;
};
} // anon namespace
VideoLink * Rgb32Conv::create(PixelFormat pf, unsigned width, unsigned height) {
switch (pf) {
case RGB16: return new Rgb32ToRgb16Link(width, height);
case UYVY: return new Rgb32ToUyvyLink(width, height);
default: return 0;
}
}
-30
View File
@@ -1,30 +0,0 @@
/***************************************************************************
* 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 RGB32CONV_H
#define RGB32CONV_H
class VideoLink;
class Rgb32Conv {
public:
enum PixelFormat { RGB32, RGB16, UYVY };
static VideoLink * create(PixelFormat pf, unsigned width, unsigned height);
};
#endif
-48
View File
@@ -1,48 +0,0 @@
/***************************************************************************
* 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 "vfilterinfo.h"
#include "vfilters/catrom2x.h"
#include "vfilters/catrom3x.h"
#include "vfilters/kreed2xsai.h"
#include "vfilters/maxsthq2x.h"
#include "vfilters/maxsthq3x.h"
static VideoLink * createNone() { return 0; }
template<class T>
static VideoLink * createT() { return new T; }
#define VFINFO(handle, Type) { handle, Type::out_width, Type::out_height, createT<Type> }
static VfilterInfo const vfinfos[] = {
{ "None", VfilterInfo::in_width, VfilterInfo::in_height, createNone },
VFINFO("Bicubic Catmull-Rom spline 2x", Catrom2x),
VFINFO("Bicubic Catmull-Rom spline 3x", Catrom3x),
VFINFO("Kreed's 2xSaI", Kreed2xSaI),
VFINFO("MaxSt's hq2x", MaxStHq2x),
VFINFO("MaxSt's hq3x", MaxStHq3x),
};
std::size_t VfilterInfo::numVfilters() {
return sizeof vfinfos / sizeof vfinfos[0];
}
VfilterInfo const & VfilterInfo::get(std::size_t n) {
return vfinfos[n];
}
-39
View File
@@ -1,39 +0,0 @@
/***************************************************************************
* 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 VFILTERINFO_H
#define VFILTERINFO_H
#include <cstddef>
class VideoLink;
struct VfilterInfo {
enum { in_width = 160 };
enum { in_height = 144 };
char const *handle;
unsigned outWidth;
unsigned outHeight;
VideoLink * (*create)();
static VfilterInfo const & get(std::size_t n);
static std::size_t numVfilters();
};
#endif
-179
View File
@@ -1,179 +0,0 @@
/***************************************************************************
* 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. *
***************************************************************************/
#include "catrom2x.h"
#include <algorithm>
namespace {
enum { in_width = VfilterInfo::in_width };
enum { in_height = VfilterInfo::in_height };
enum { in_pitch = in_width + 3 };
struct Colorsum {
gambatte::uint_least32_t r, g, b;
};
static void mergeColumns(gambatte::uint_least32_t *dest, Colorsum const *sums) {
for (unsigned w = in_width; w--;) {
{
gambatte::uint_least32_t rsum = sums[1].r;
gambatte::uint_least32_t gsum = sums[1].g;
gambatte::uint_least32_t bsum = sums[1].b;
if (rsum >= 0x80000000) rsum = 0;
if (gsum >= 0x80000000) gsum = 0;
if (bsum >= 0x80000000) bsum = 0;
rsum <<= 12;
rsum += 0x008000;
gsum >>= 4;
gsum += 0x0080;
bsum += 0x0008;
bsum >>= 4;
if (rsum > 0xFF0000) rsum = 0xFF0000;
if (gsum > 0x00FF00) gsum = 0x00FF00;
if (bsum > 0x0000FF) bsum = 0x0000FF;
*dest++ = (rsum & 0xFF0000) | (gsum & 0x00FF00) | bsum;
}
{
gambatte::uint_least32_t rsum = sums[1].r * 9;
gambatte::uint_least32_t gsum = sums[1].g * 9;
gambatte::uint_least32_t bsum = sums[1].b * 9;
rsum -= sums[0].r;
gsum -= sums[0].g;
bsum -= sums[0].b;
rsum += sums[2].r * 9;
gsum += sums[2].g * 9;
bsum += sums[2].b * 9;
rsum -= sums[3].r;
gsum -= sums[3].g;
bsum -= sums[3].b;
if (rsum >= 0x80000000) rsum = 0;
if (gsum >= 0x80000000) gsum = 0;
if (bsum >= 0x80000000) bsum = 0;
rsum <<= 8;
rsum += 0x008000;
gsum >>= 8;
gsum += 0x000080;
bsum += 0x000080;
bsum >>= 8;
if (rsum > 0xFF0000) rsum = 0xFF0000;
if (gsum > 0x00FF00) gsum = 0x00FF00;
if (bsum > 0x0000FF) bsum = 0x0000FF;
*dest++ = (rsum & 0xFF0000) | (gsum & 0x00FF00) | bsum;
}
++sums;
}
}
static void filter(gambatte::uint_least32_t *dline,
std::ptrdiff_t const pitch,
gambatte::uint_least32_t const *sline)
{
Colorsum sums[in_pitch];
for (unsigned h = in_height; h--;) {
{
gambatte::uint_least32_t const *s = sline;
Colorsum *sum = sums;
unsigned n = in_pitch;
while (n--) {
unsigned long pixel = *s;
sum->r = pixel >> 12 & 0x000FF0 ;
pixel <<= 4;
sum->g = pixel & 0x0FF000;
sum->b = pixel & 0x000FF0;
++s;
++sum;
}
}
mergeColumns(dline, sums);
dline += pitch;
{
gambatte::uint_least32_t const *s = sline;
Colorsum *sum = sums;
unsigned n = in_pitch;
while (n--) {
unsigned long pixel = *s;
unsigned long rsum = (pixel >> 16) * 9;
unsigned long gsum = (pixel & 0x00FF00) * 9;
unsigned long bsum = (pixel & 0x0000FF) * 9;
pixel = s[-1 * in_pitch];
rsum -= pixel >> 16;
gsum -= pixel & 0x00FF00;
bsum -= pixel & 0x0000FF;
pixel = s[1 * in_pitch];
rsum += (pixel >> 16) * 9;
gsum += (pixel & 0x00FF00) * 9;
bsum += (pixel & 0x0000FF) * 9;
pixel = s[2 * in_pitch];
rsum -= pixel >> 16;
gsum -= pixel & 0x00FF00;
bsum -= pixel & 0x0000FF;
sum->r = rsum;
sum->g = gsum;
sum->b = bsum;
++s;
++sum;
}
}
mergeColumns(dline, sums);
dline += pitch;
sline += in_pitch;
}
}
} // anon namespace
Catrom2x::Catrom2x()
: buffer_((in_height + 3UL) * in_pitch)
{
std::fill_n(buffer_.get(), buffer_.size(), 0);
}
void * Catrom2x::inBuf() const {
return buffer_ + in_pitch + 1;
}
std::ptrdiff_t Catrom2x::inPitch() const {
return in_pitch;
}
void Catrom2x::draw(void *dbuffer, std::ptrdiff_t pitch) {
::filter(static_cast<gambatte::uint_least32_t *>(dbuffer), pitch, buffer_ + in_pitch);
}
-41
View File
@@ -1,41 +0,0 @@
/***************************************************************************
* 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 CATROM2X_H
#define CATROM2X_H
#include "../videolink.h"
#include "../vfilterinfo.h"
#include "array.h"
#include "gbint.h"
class Catrom2x : public VideoLink {
public:
enum { out_width = VfilterInfo::in_width * 2 };
enum { out_height = VfilterInfo::in_height * 2 };
Catrom2x();
virtual void * inBuf() const;
virtual std::ptrdiff_t inPitch() const;
virtual void draw(void *dst, std::ptrdiff_t dstpitch);
private:
Array<gambatte::uint_least32_t> const buffer_;
};
#endif
-345
View File
@@ -1,345 +0,0 @@
/***************************************************************************
* 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. *
***************************************************************************/
#include "catrom3x.h"
#include <algorithm>
namespace {
enum { in_width = VfilterInfo::in_width };
enum { in_height = VfilterInfo::in_height };
enum { in_pitch = in_width + 3 };
struct Colorsum {
gambatte::uint_least32_t r, g, b;
};
static void mergeColumns(gambatte::uint_least32_t *dest, Colorsum const *sums) {
for (unsigned w = in_width; w--;) {
{
gambatte::uint_least32_t rsum = sums[1].r;
gambatte::uint_least32_t gsum = sums[1].g;
gambatte::uint_least32_t bsum = sums[1].b;
if (rsum >= 0x80000000) {
rsum = 0;
} else if (rsum > 6869) {
rsum = 0xFF0000;
} else {
rsum *= 607;
rsum <<= 2;
rsum += 0x008000;
rsum &= 0xFF0000;
}
if (gsum >= 0x80000000) {
gsum = 0;
} else if (gsum > 1758567) {
gsum = 0xFF00;
} else {
gsum *= 607;
gsum >>= 14;
gsum += 0x000080;
gsum &= 0x00FF00;
}
if (bsum >= 0x80000000) {
bsum = 0;
} else if (bsum > 6869) {
bsum = 0xFF;
} else {
bsum *= 607;
bsum += 8192;
bsum >>= 14;
}
/*rsum/=27;
rsum<<=8;
gsum/=27;
gsum<<=5;
bsum<<=4;
bsum+=27;
bsum/=54;
rsum+=0x008000;
gsum+=0x000080;
if(rsum>0xFF0000) rsum=0xFF0000;
if(gsum>0x00FF00) gsum=0x00FF00;
if(bsum>0x0000FF) bsum=0x0000FF;*/
*dest++ = rsum/*&0xFF0000*/ | gsum/*&0x00FF00*/ | bsum;
}
{
gambatte::uint_least32_t rsum = sums[1].r * 21;
gambatte::uint_least32_t gsum = sums[1].g * 21;
gambatte::uint_least32_t bsum = sums[1].b * 21;
rsum -= sums[0].r << 1;
gsum -= sums[0].g << 1;
bsum -= sums[0].b << 1;
rsum += sums[2].r * 9;
gsum += sums[2].g * 9;
bsum += sums[2].b * 9;
rsum -= sums[3].r;
gsum -= sums[3].g;
bsum -= sums[3].b;
if (rsum >= 0x80000000) {
rsum = 0;
} else if (rsum > 185578) {
rsum = 0xFF0000;
} else {
rsum *= 719;
rsum >>= 3;
rsum += 0x008000;
rsum &= 0xFF0000;
}
if (gsum >= 0x80000000) {
gsum = 0;
} else if (gsum > 47508223) {
gsum = 0x00FF00;
} else {
gsum >>= 8;
gsum *= 719;
gsum >>= 11;
gsum += 0x000080;
gsum &= 0x00FF00;
}
if (bsum >= 0x80000000) {
bsum = 0;
} else if (bsum > 185578) {
bsum = 0x0000FF;
} else {
bsum *= 719;
bsum += 0x040000;
bsum >>= 19;
}
/*rsum/=729;
rsum<<=8;
gsum/=729;
gsum<<=5;
bsum<<=4;
bsum+=729;
bsum/=1458;
rsum+=0x008000;
gsum+=0x000080;
if(rsum>0xFF0000) rsum=0xFF0000;
if(gsum>0x00FF00) gsum=0x00FF00;
if(bsum>0x0000FF) bsum=0x0000FF;*/
*dest++ = rsum/*&0xFF0000*/ | gsum/*&0x00FF00*/ | bsum;
}
{
gambatte::uint_least32_t rsum = sums[1].r * 9;
gambatte::uint_least32_t gsum = sums[1].g * 9;
gambatte::uint_least32_t bsum = sums[1].b * 9;
rsum -= sums[0].r;
gsum -= sums[0].g;
bsum -= sums[0].b;
rsum += sums[2].r * 21;
gsum += sums[2].g * 21;
bsum += sums[2].b * 21;
rsum -= sums[3].r << 1;
gsum -= sums[3].g << 1;
bsum -= sums[3].b << 1;
if (rsum >= 0x80000000) {
rsum = 0;
} else if (rsum > 185578) {
rsum = 0xFF0000;
} else {
rsum *= 719;
rsum >>= 3;
rsum += 0x008000;
rsum &= 0xFF0000;
}
if (gsum >= 0x80000000) {
gsum = 0;
} else if (gsum > 47508223) {
gsum = 0xFF00;
} else {
gsum >>= 8;
gsum *= 719;
gsum >>= 11;
gsum += 0x000080;
gsum &= 0x00FF00;
}
if (bsum >= 0x80000000) {
bsum = 0;
} else if (bsum > 185578) {
bsum = 0x0000FF;
} else {
bsum *= 719;
bsum += 0x040000;
bsum >>= 19;
}
/*rsum/=729;
rsum<<=8;
gsum/=729;
gsum<<=5;
bsum<<=4;
bsum+=729;
bsum/=1458;
rsum+=0x008000;
gsum+=0x000080;
if(rsum>0xFF0000) rsum=0xFF0000;
if(gsum>0x00FF00) gsum=0x00FF00;
if(bsum>0x0000FF) bsum=0x0000FF;*/
*dest++ = rsum/*&0xFF0000*/ | gsum/*&0x00FF00*/ | bsum;
}
++sums;
}
}
static void filter(gambatte::uint_least32_t *dline,
std::ptrdiff_t const pitch,
gambatte::uint_least32_t const *sline)
{
Colorsum sums[in_pitch];
for (unsigned h = in_height; h--;) {
{
gambatte::uint_least32_t const *s = sline;
Colorsum *sum = sums;
unsigned n = in_pitch;
while (n--) {
unsigned long const pixel = *s;
sum->r = (pixel >> 16) * 27;
sum->g = (pixel & 0x00FF00) * 27;
sum->b = (pixel & 0x0000FF) * 27;
++s;
++sum;
}
}
mergeColumns(dline, sums);
dline += pitch;
{
gambatte::uint_least32_t const *s = sline;
Colorsum *sum = sums;
unsigned n = in_pitch;
while (n--) {
unsigned long pixel = *s;
unsigned long rsum = (pixel >> 16) * 21;
unsigned long gsum = (pixel & 0x00FF00) * 21;
unsigned long bsum = (pixel & 0x0000FF) * 21;
pixel = s[-1 * in_pitch];
rsum -= (pixel >> 16) << 1;
pixel <<= 1;
gsum -= pixel & 0x01FE00;
bsum -= pixel & 0x0001FE;
pixel = s[1 * in_pitch];
rsum += (pixel >> 16) * 9;
gsum += (pixel & 0x00FF00) * 9;
bsum += (pixel & 0x0000FF) * 9;
pixel = s[2 * in_pitch];
rsum -= pixel >> 16;
gsum -= pixel & 0x00FF00;
bsum -= pixel & 0x0000FF;
sum->r = rsum;
sum->g = gsum;
sum->b = bsum;
++s;
++sum;
}
}
mergeColumns(dline, sums);
dline += pitch;
{
gambatte::uint_least32_t const *s = sline;
Colorsum *sum = sums;
unsigned n = in_pitch;
while (n--) {
unsigned long pixel = *s;
unsigned long rsum = (pixel >> 16) * 9;
unsigned long gsum = (pixel & 0x00FF00) * 9;
unsigned long bsum = (pixel & 0x0000FF) * 9;
pixel = s[-1 * in_pitch];
rsum -= pixel >> 16;
gsum -= pixel & 0x00FF00;
bsum -= pixel & 0x0000FF;
pixel = s[1 * in_pitch];
rsum += (pixel >> 16) * 21;
gsum += (pixel & 0x00FF00) * 21;
bsum += (pixel & 0x0000FF) * 21;
pixel = s[2 * in_pitch];
rsum -= (pixel >> 16) << 1;
pixel <<= 1;
gsum -= pixel & 0x01FE00;
bsum -= pixel & 0x0001FE;
sum->r = rsum;
sum->g = gsum;
sum->b = bsum;
++s;
++sum;
}
}
mergeColumns(dline, sums);
dline += pitch;
sline += in_pitch;
}
}
} // anon namespace
Catrom3x::Catrom3x()
: buffer_((in_height + 3UL) * in_pitch)
{
std::fill_n(buffer_.get(), buffer_.size(), 0);
}
void * Catrom3x::inBuf() const {
return buffer_ + in_pitch + 1;
}
std::ptrdiff_t Catrom3x::inPitch() const {
return in_pitch;
}
void Catrom3x::draw(void *dbuffer, std::ptrdiff_t pitch) {
::filter(static_cast<gambatte::uint_least32_t *>(dbuffer), pitch, buffer_ + in_pitch);
}
-41
View File
@@ -1,41 +0,0 @@
/***************************************************************************
* 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 CATROM3X_H
#define CATROM3X_H
#include "../videolink.h"
#include "../vfilterinfo.h"
#include "array.h"
#include "gbint.h"
class Catrom3x : public VideoLink {
public:
enum { out_width = VfilterInfo::in_width * 3 };
enum { out_height = VfilterInfo::in_height * 3 };
Catrom3x();
virtual void * inBuf() const;
virtual std::ptrdiff_t inPitch() const;
virtual void draw(void *dst, std::ptrdiff_t dstpitch);
private:
Array<gambatte::uint_least32_t> const buffer_;
};
#endif
-233
View File
@@ -1,233 +0,0 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* Copyright (C) 1999 Derek Liauw Kie Fa (Kreed) *
* *
* 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 "kreed2xsai.h"
#include <algorithm>
namespace {
static int getResult1(unsigned long const a,
unsigned long const b,
unsigned long const c,
unsigned long const d)
{
int x = 0;
int y = 0;
int r = 0;
if (a == c) ++x;
else if (b == c) ++y;
if (a == d) ++x;
else if (b == d) ++y;
if (x <= 1) ++r;
if (y <= 1) --r;
return r;
}
static int getResult2(unsigned long const a,
unsigned long const b,
unsigned long const c,
unsigned long const d)
{
int x = 0;
int y = 0;
int r = 0;
if (a == c) ++x;
else if (b == c) ++y;
if (a == d) ++x;
else if (b == d) ++y;
if (x <= 1) --r;
if (y <= 1) ++r;
return r;
}
static unsigned long interpolate(unsigned long a, unsigned long b) {
return (a + b - ((a ^ b) & 0x010101)) >> 1;
}
static unsigned long qInterpolate(unsigned long const a,
unsigned long const b,
unsigned long const c,
unsigned long const d)
{
unsigned long lowBits = ((a & 0x030303)
+ (b & 0x030303)
+ (c & 0x030303)
+ (d & 0x030303)) & 0x030303;
return (a + b + c + d - lowBits) >> 2;
}
template<std::ptrdiff_t srcPitch, unsigned width, unsigned height>
static void filter(gambatte::uint_least32_t *dstPtr,
std::ptrdiff_t const dstPitch,
gambatte::uint_least32_t const *srcPtr)
{
for (unsigned h = height; h--;) {
gambatte::uint_least32_t const *bP = srcPtr;
gambatte::uint_least32_t *dP = dstPtr;
for (unsigned w = width; w--;) {
unsigned long colorA, colorB, colorC, colorD,
colorE, colorF, colorG, colorH,
colorI, colorJ, colorK, colorL,
colorM, colorN, colorO/*, colorP*/;
//---------------------------------------
// Map of the pixels: I|E F|J
// G|A B|K
// H|C D|L
// M|N O|P
colorI = *(bP - srcPitch - 1);
colorE = *(bP - srcPitch );
colorF = *(bP - srcPitch + 1);
colorJ = *(bP - srcPitch + 2);
colorG = *(bP - 1);
colorA = *(bP );
colorB = *(bP + 1);
colorK = *(bP + 2);
colorH = *(bP + srcPitch - 1);
colorC = *(bP + srcPitch );
colorD = *(bP + srcPitch + 1);
colorL = *(bP + srcPitch + 2);
colorM = *(bP + srcPitch * 2 - 1);
colorN = *(bP + srcPitch * 2 );
colorO = *(bP + srcPitch * 2 + 1);
// colorP = *(bP + srcPitch * 2 + 2);
unsigned long product0, product1, product2;
if (colorA == colorD && colorB != colorC) {
product0 = (colorA == colorE && colorB == colorL)
|| (colorA == colorC && colorA == colorF
&& colorB != colorE && colorB == colorJ)
? colorA
: interpolate(colorA, colorB);
product1 = (colorA == colorG && colorC == colorO)
|| (colorA == colorB && colorA == colorH
&& colorG != colorC && colorC == colorM)
? colorA
: interpolate(colorA, colorC);
product2 = colorA;
} else if (colorB == colorC && colorA != colorD) {
product0 = (colorB == colorF && colorA == colorH)
|| (colorB == colorE && colorB == colorD
&& colorA != colorF && colorA == colorI)
? colorB
: interpolate(colorA, colorB);
product1 = (colorC == colorH && colorA == colorF)
|| (colorC == colorG && colorC == colorD
&& colorA != colorH && colorA == colorI)
? colorC
: interpolate(colorA, colorC);
product2 = colorB;
} else if (colorA == colorD && colorB == colorC) {
if (colorA == colorB) {
product0 = colorA;
product1 = colorA;
product2 = colorA;
} else {
product0 = interpolate(colorA, colorB);
product1 = interpolate(colorA, colorC);
int r = 0;
r += getResult1(colorA, colorB, colorG, colorE);
r += getResult2(colorB, colorA, colorK, colorF);
r += getResult2(colorB, colorA, colorH, colorN);
r += getResult1(colorA, colorB, colorL, colorO);
if (r > 0) {
product2 = colorA;
} else if (r < 0) {
product2 = colorB;
} else {
product2 = qInterpolate(colorA, colorB, colorC, colorD);
}
}
} else {
product2 = qInterpolate(colorA, colorB, colorC, colorD);
if (colorA == colorC && colorA == colorF
&& colorB != colorE && colorB == colorJ) {
product0 = colorA;
} else if (colorB == colorE && colorB == colorD
&& colorA != colorF && colorA == colorI) {
product0 = colorB;
} else {
product0 = interpolate(colorA, colorB);
}
if (colorA == colorB && colorA == colorH
&& colorG != colorC && colorC == colorM) {
product1 = colorA;
} else if (colorC == colorG && colorC == colorD
&& colorA != colorH && colorA == colorI) {
product1 = colorC;
} else {
product1 = interpolate(colorA, colorC);
}
}
*(dP ) = colorA;
*(dP + 1) = product0;
*(dP + dstPitch ) = product1;
*(dP + dstPitch + 1) = product2;
dP += 2;
++bP;
}
srcPtr += srcPitch;
dstPtr += dstPitch * 2;
}
}
enum { in_width = VfilterInfo::in_width };
enum { in_height = VfilterInfo::in_height };
enum { in_pitch = in_width + 3 };
enum { buf_size = (in_height + 3ul) * in_pitch };
enum { buf_offset = in_pitch + 1 };
} // anon namespace
Kreed2xSaI::Kreed2xSaI()
: buffer_(buf_size)
{
std::fill_n(buffer_.get(), buffer_.size(), 0);
}
void * Kreed2xSaI::inBuf() const {
return buffer_ + buf_offset;
}
std::ptrdiff_t Kreed2xSaI::inPitch() const {
return in_pitch;
}
void Kreed2xSaI::draw(void *dbuffer, std::ptrdiff_t dpitch) {
::filter<in_pitch, in_width, in_height>(static_cast<gambatte::uint_least32_t *>(dbuffer),
dpitch, buffer_ + buf_offset);
}
-41
View File
@@ -1,41 +0,0 @@
/***************************************************************************
* 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 KREED2XSAI_H
#define KREED2XSAI_H
#include "../videolink.h"
#include "../vfilterinfo.h"
#include "array.h"
#include "gbint.h"
class Kreed2xSaI : public VideoLink {
public:
enum { out_width = VfilterInfo::in_width * 2 };
enum { out_height = VfilterInfo::in_height * 2 };
Kreed2xSaI();
virtual void * inBuf() const;
virtual std::ptrdiff_t inPitch() const;
virtual void draw(void *dst, std::ptrdiff_t dstpitch);
private:
Array<gambatte::uint_least32_t> const buffer_;
};
#endif
File diff suppressed because it is too large Load Diff
-41
View File
@@ -1,41 +0,0 @@
/***************************************************************************
* 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 MAXSTHQ2X_H
#define MAXSTHQ2X_H
#include "../videolink.h"
#include "../vfilterinfo.h"
#include "array.h"
#include "gbint.h"
class MaxStHq2x : public VideoLink {
public:
enum { out_width = VfilterInfo::in_width * 2 };
enum { out_height = VfilterInfo::in_height * 2 };
MaxStHq2x();
virtual void * inBuf() const;
virtual std::ptrdiff_t inPitch() const;
virtual void draw(void *dst, std::ptrdiff_t dstpitch);
private:
SimpleArray<gambatte::uint_least32_t> const buffer_;
};
#endif
File diff suppressed because it is too large Load Diff
-41
View File
@@ -1,41 +0,0 @@
/***************************************************************************
* 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 MAXSTHQ3X_H
#define MAXSTHQ3X_H
#include "../videolink.h"
#include "../vfilterinfo.h"
#include "array.h"
#include "gbint.h"
class MaxStHq3x : public VideoLink {
public:
enum { out_width = VfilterInfo::in_width * 3 };
enum { out_height = VfilterInfo::in_height * 3 };
MaxStHq3x();
virtual void * inBuf() const;
virtual std::ptrdiff_t inPitch() const;
virtual void draw(void *dst, std::ptrdiff_t dstpitch);
private:
SimpleArray<gambatte::uint_least32_t> const buffer_;
};
#endif
-32
View File
@@ -1,32 +0,0 @@
/***************************************************************************
* 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 VIDEOLINK_H
#define VIDEOLINK_H
#include <cstddef>
class VideoLink {
public:
virtual ~VideoLink() {}
virtual void * inBuf() const = 0;
virtual std::ptrdiff_t inPitch() const = 0;
virtual void draw(void *dst, std::ptrdiff_t dstpitch) = 0;
};
#endif
+2
View File
@@ -0,0 +1,2 @@
/* Localized versions of Info.plist keys */
+506 -506
View File
File diff suppressed because it is too large Load Diff
-56
View File
@@ -1,56 +0,0 @@
global_cflags = ARGUMENTS.get('CFLAGS', '-Wall -Wextra -O2 -fomit-frame-pointer')
global_cxxflags = ARGUMENTS.get('CXXFLAGS', global_cflags + ' -fno-exceptions -fno-rtti')
global_defines = ' -DHAVE_STDINT_H'
vars = Variables()
vars.Add('CC')
vars.Add('CXX')
env = Environment(CPPPATH = ['src', 'include', '../common'],
CFLAGS = global_cflags + global_defines,
CXXFLAGS = global_cxxflags + global_defines,
variables = vars)
sourceFiles = Split('''
src/bitmap_font.cpp
src/cpu.cpp
src/gambatte.cpp
src/initstate.cpp
src/interrupter.cpp
src/interruptrequester.cpp
src/loadres.cpp
src/memory.cpp
src/sound.cpp
src/state_osd_elements.cpp
src/statesaver.cpp
src/tima.cpp
src/video.cpp
src/mem/cartridge.cpp
src/mem/memptrs.cpp
src/mem/pakinfo.cpp
src/mem/rtc.cpp
src/sound/channel1.cpp
src/sound/channel2.cpp
src/sound/channel3.cpp
src/sound/channel4.cpp
src/sound/duty_unit.cpp
src/sound/envelope_unit.cpp
src/sound/length_counter.cpp
src/video/ly_counter.cpp
src/video/lyc_irq.cpp
src/video/next_m0_time.cpp
src/video/ppu.cpp
src/video/sprite_mapper.cpp
''')
conf = env.Configure()
if conf.CheckHeader('zlib.h'):
sourceFiles.append('src/file/unzip/unzip.c')
sourceFiles.append('src/file/unzip/ioapi.c')
sourceFiles.append('src/file/file_zip.cpp')
else:
sourceFiles.append('src/file/file.cpp')
conf.Finish()
env.Library('gambatte', sourceFiles)
-222
View File
@@ -1,222 +0,0 @@
/***************************************************************************
Copyright (C) 2007 by Nach
http://nsrt.edgeemu.com
Copyright (C) 2007-2011 by sinamas <sinamas at users.sourceforge.net>
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 "stdfile.h"
#include <cctype>
#include <cstring>
namespace zlib {
#include "unzip/unzip.h"
}
namespace {
class ZipFile : public gambatte::File {
private:
std::size_t fsize, count;
void *zipfile;
bool zip_sub_open;
void zip(const char *filename);
public:
ZipFile(const char *filename);
virtual ~ZipFile();
virtual void rewind();
bool is_open() const;
virtual void close();
virtual std::size_t size() const { return fsize; };
virtual void read(char *buffer, std::size_t amount);
std::size_t gcount() const { return count; }
virtual bool fail() const { return !is_open(); }
};
using namespace std;
using namespace zlib;
static const unsigned int MAX_FILE_NAME = 512;
ZipFile::ZipFile(const char *filename) : fsize(0), count(0)
{
zip(filename);
}
void ZipFile::zip(const char *filename)
{
zipfile = unzOpen(filename);
if (zipfile)
{
zip_sub_open = false;
unz_file_info cFileInfo;
char ourFile[MAX_FILE_NAME] = { '\n' };
for (int cFile = unzGoToFirstFile((unzFile)zipfile); cFile == UNZ_OK; cFile = unzGoToNextFile((unzFile)zipfile))
{
//Temporary char array for file name
char cFileName[MAX_FILE_NAME];
//Gets info on current file, and places it in cFileInfo
unzGetCurrentFileInfo((unzFile)zipfile, &cFileInfo, cFileName, MAX_FILE_NAME, 0, 0, 0, 0);
//Check for largest file which should be the ROM
if ((size_t)cFileInfo.uncompressed_size > fsize)
{
strcpy(ourFile, cFileName);
fsize = (size_t)cFileInfo.uncompressed_size;
}
}
if (ourFile[0] != '\n')
{
//Sets current file to the file we liked before
unzLocateFile((unzFile)zipfile, ourFile, 1);
if (unzOpenCurrentFile((unzFile)zipfile) == UNZ_OK)
{
zip_sub_open = true;
}
}
if (!zip_sub_open)
{
unzClose((unzFile)zipfile);
zipfile = 0;
}
}
}
ZipFile::~ZipFile()
{
close();
}
void ZipFile::rewind()
{
if (is_open())
{
unzCloseCurrentFile((unzFile)zipfile);
unzOpenCurrentFile((unzFile)zipfile);
}
}
bool ZipFile::is_open() const
{
return(zipfile && zip_sub_open);
}
void ZipFile::close()
{
if (is_open())
{
unzOpenCurrentFile((unzFile)zipfile);
unzClose((unzFile)zipfile);
zipfile = 0;
zip_sub_open = false;
}
}
void ZipFile::read(char *buffer, size_t amount)
{
if (is_open())
{
count = (size_t)unzReadCurrentFile((unzFile)zipfile, buffer, amount);
}
else
{
count = 0;
}
}
class GzFile : public gambatte::File {
public:
explicit GzFile(char const *filename)
: file_(gzopen(filename, "rb"))
, fsize_(0)
{
if (file_) {
char buf[256];
int ret;
while ((ret = gzread(file_, buf, sizeof buf)) > 0)
fsize_ += ret;
if (ret < 0) {
close();
fsize_ = 0;
}
}
rewind();
}
virtual ~GzFile() { close(); }
virtual void rewind() {
if (file_ && gzrewind(file_) < 0)
close();
}
virtual std::size_t size() const { return fsize_; };
virtual void read(char *buffer, std::size_t amount) {
if (file_ && gzread(file_, buffer, amount) < 0)
close();
}
virtual bool fail() const { return !file_; }
private:
gzFile file_;
std::size_t fsize_;
GzFile(GzFile const &);
GzFile & operator=(GzFile const &);
void close();
};
void GzFile::close() {
if (file_) {
gzclose(file_);
file_ = 0;
}
}
}
// Avoid checking magic header values, because there are no values that cannot occur in a GB ROM.
transfer_ptr<gambatte::File> gambatte::newFileInstance(std::string const &filepath) {
std::size_t const extpos = filepath.rfind('.');
if (extpos != std::string::npos) {
std::string const &ext = filepath.substr(extpos + 1);
if (ext.length() == 3 && std::tolower(ext[0]) == 'z'
&& std::tolower(ext[1]) == 'i' && std::tolower(ext[2]) == 'p') {
return transfer_ptr<File>(new ZipFile(filepath.c_str()));
}
if (!ext.empty() && std::tolower(ext[ext.length() - 1]) == 'z')
return transfer_ptr<File>(new GzFile(filepath.c_str()));
}
return transfer_ptr<File>(new StdFile(filepath.c_str()));
}
-132
View File
@@ -1,132 +0,0 @@
/* crypt.h -- base code for crypt/uncrypt ZIPfile
Version 1.01e, February 12th, 2005
Copyright (C) 1998-2005 Gilles Vollant
This code is a modified version of crypting code in Infozip distribution
The encryption/decryption parts of this source code (as opposed to the
non-echoing password parts) were originally written in Europe. The
whole source package can be freely distributed, including from the USA.
(Prior to January 2000, re-export from the US was a violation of US law.)
This encryption code is a direct transcription of the algorithm from
Roger Schlafly, described by Phil Katz in the file appnote.txt. This
file (appnote.txt) is distributed with the PKZIP program (even in the
version without encryption capabilities).
If you don't need crypting in your application, just define symbols
NOCRYPT and NOUNCRYPT.
This code support the "Traditional PKWARE Encryption".
The new AES encryption added on Zip format by Winzip (see the page
http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong
Encryption is not supported.
*/
#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8))
/***********************************************************************
* Return the next byte in the pseudo-random sequence
*/
static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab)
{
unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an
* unpredictable manner on 16-bit systems; not a problem
* with any known compiler so far, though */
temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2;
return (int)(((temp * (temp ^ 1)) >> 8) & 0xff);
}
/***********************************************************************
* Update the encryption keys with the next byte of plain text
*/
static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c)
{
(*(pkeys+0)) = CRC32((*(pkeys+0)), c);
(*(pkeys+1)) += (*(pkeys+0)) & 0xff;
(*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1;
{
register int keyshift = (int)((*(pkeys+1)) >> 24);
(*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift);
}
return c;
}
/***********************************************************************
* Initialize the encryption keys and the random header according to
* the given password.
*/
static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab)
{
*(pkeys+0) = 305419896L;
*(pkeys+1) = 591751049L;
*(pkeys+2) = 878082192L;
while (*passwd != '\0') {
update_keys(pkeys,pcrc_32_tab,(int)*passwd);
passwd++;
}
}
#define zdecode(pkeys,pcrc_32_tab,c) \
(update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab)))
#define zencode(pkeys,pcrc_32_tab,c,t) \
(t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c))
#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED
#define RAND_HEAD_LEN 12
/* "last resort" source for second part of crypt seed pattern */
# ifndef ZCR_SEED2
# define ZCR_SEED2 3141592654UL /* use PI as default pattern */
# endif
static int crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting)
const char *passwd; /* password string */
unsigned char *buf; /* where to write header */
int bufSize;
unsigned long* pkeys;
const unsigned long* pcrc_32_tab;
unsigned long crcForCrypting;
{
int n; /* index in random header */
int t; /* temporary */
int c; /* random byte */
unsigned char header[RAND_HEAD_LEN-2]; /* random header */
static unsigned calls = 0; /* ensure different random header each time */
if (bufSize<RAND_HEAD_LEN)
return 0;
/* First generate RAND_HEAD_LEN-2 random bytes. We encrypt the
* output of rand() to get less predictability, since rand() is
* often poorly implemented.
*/
if (++calls == 1)
{
srand((unsigned)(time(NULL) ^ ZCR_SEED2));
}
init_keys(passwd, pkeys, pcrc_32_tab);
for (n = 0; n < RAND_HEAD_LEN-2; n++)
{
c = (rand() >> 7) & 0xff;
header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t);
}
/* Encrypt random header (last two bytes is high word of crc) */
init_keys(passwd, pkeys, pcrc_32_tab);
for (n = 0; n < RAND_HEAD_LEN-2; n++)
{
buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t);
}
buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t);
buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t);
return n;
}
#endif
-177
View File
@@ -1,177 +0,0 @@
/* ioapi.c -- IO base function header for compress/uncompress .zip
files using zlib + zip or unzip API
Version 1.01e, February 12th, 2005
Copyright (C) 1998-2005 Gilles Vollant
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
#include "ioapi.h"
/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */
#ifndef SEEK_CUR
#define SEEK_CUR 1
#endif
#ifndef SEEK_END
#define SEEK_END 2
#endif
#ifndef SEEK_SET
#define SEEK_SET 0
#endif
voidpf ZCALLBACK fopen_file_func OF((
voidpf opaque,
const char* filename,
int mode));
uLong ZCALLBACK fread_file_func OF((
voidpf opaque,
voidpf stream,
void* buf,
uLong size));
uLong ZCALLBACK fwrite_file_func OF((
voidpf opaque,
voidpf stream,
const void* buf,
uLong size));
long ZCALLBACK ftell_file_func OF((
voidpf opaque,
voidpf stream));
long ZCALLBACK fseek_file_func OF((
voidpf opaque,
voidpf stream,
uLong offset,
int origin));
int ZCALLBACK fclose_file_func OF((
voidpf opaque,
voidpf stream));
int ZCALLBACK ferror_file_func OF((
voidpf opaque,
voidpf stream));
voidpf ZCALLBACK fopen_file_func (opaque, filename, mode)
voidpf opaque;
const char* filename;
int mode;
{
FILE* file = NULL;
const char* mode_fopen = NULL;
if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ)
mode_fopen = "rb";
else
if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
mode_fopen = "r+b";
else
if (mode & ZLIB_FILEFUNC_MODE_CREATE)
mode_fopen = "wb";
if ((filename!=NULL) && (mode_fopen != NULL))
file = fopen(filename, mode_fopen);
return file;
}
uLong ZCALLBACK fread_file_func (opaque, stream, buf, size)
voidpf opaque;
voidpf stream;
void* buf;
uLong size;
{
uLong ret;
ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream);
return ret;
}
uLong ZCALLBACK fwrite_file_func (opaque, stream, buf, size)
voidpf opaque;
voidpf stream;
const void* buf;
uLong size;
{
uLong ret;
ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream);
return ret;
}
long ZCALLBACK ftell_file_func (opaque, stream)
voidpf opaque;
voidpf stream;
{
long ret;
ret = ftell((FILE *)stream);
return ret;
}
long ZCALLBACK fseek_file_func (opaque, stream, offset, origin)
voidpf opaque;
voidpf stream;
uLong offset;
int origin;
{
int fseek_origin=0;
long ret;
switch (origin)
{
case ZLIB_FILEFUNC_SEEK_CUR :
fseek_origin = SEEK_CUR;
break;
case ZLIB_FILEFUNC_SEEK_END :
fseek_origin = SEEK_END;
break;
case ZLIB_FILEFUNC_SEEK_SET :
fseek_origin = SEEK_SET;
break;
default: return -1;
}
ret = 0;
fseek((FILE *)stream, offset, fseek_origin);
return ret;
}
int ZCALLBACK fclose_file_func (opaque, stream)
voidpf opaque;
voidpf stream;
{
int ret;
ret = fclose((FILE *)stream);
return ret;
}
int ZCALLBACK ferror_file_func (opaque, stream)
voidpf opaque;
voidpf stream;
{
int ret;
ret = ferror((FILE *)stream);
return ret;
}
void fill_fopen_filefunc (pzlib_filefunc_def)
zlib_filefunc_def* pzlib_filefunc_def;
{
pzlib_filefunc_def->zopen_file = fopen_file_func;
pzlib_filefunc_def->zread_file = fread_file_func;
pzlib_filefunc_def->zwrite_file = fwrite_file_func;
pzlib_filefunc_def->ztell_file = ftell_file_func;
pzlib_filefunc_def->zseek_file = fseek_file_func;
pzlib_filefunc_def->zclose_file = fclose_file_func;
pzlib_filefunc_def->zerror_file = ferror_file_func;
pzlib_filefunc_def->opaque = NULL;
}
-75
View File
@@ -1,75 +0,0 @@
/* ioapi.h -- IO base function header for compress/uncompress .zip
files using zlib + zip or unzip API
Version 1.01e, February 12th, 2005
Copyright (C) 1998-2005 Gilles Vollant
*/
#ifndef _ZLIBIOAPI_H
#define _ZLIBIOAPI_H
#define ZLIB_FILEFUNC_SEEK_CUR (1)
#define ZLIB_FILEFUNC_SEEK_END (2)
#define ZLIB_FILEFUNC_SEEK_SET (0)
#define ZLIB_FILEFUNC_MODE_READ (1)
#define ZLIB_FILEFUNC_MODE_WRITE (2)
#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3)
#define ZLIB_FILEFUNC_MODE_EXISTING (4)
#define ZLIB_FILEFUNC_MODE_CREATE (8)
#ifndef ZCALLBACK
#if (defined(WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK)
#define ZCALLBACK CALLBACK
#else
#define ZCALLBACK
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode));
typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size));
typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size));
typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream));
typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin));
typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream));
typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream));
typedef struct zlib_filefunc_def_s
{
open_file_func zopen_file;
read_file_func zread_file;
write_file_func zwrite_file;
tell_file_func ztell_file;
seek_file_func zseek_file;
close_file_func zclose_file;
testerror_file_func zerror_file;
voidpf opaque;
} zlib_filefunc_def;
void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def));
#define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size))
#define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size))
#define ZTELL(filefunc,filestream) ((*((filefunc).ztell_file))((filefunc).opaque,filestream))
#define ZSEEK(filefunc,filestream,pos,mode) ((*((filefunc).zseek_file))((filefunc).opaque,filestream,pos,mode))
#define ZCLOSE(filefunc,filestream) ((*((filefunc).zclose_file))((filefunc).opaque,filestream))
#define ZERROR(filefunc,filestream) ((*((filefunc).zerror_file))((filefunc).opaque,filestream))
#ifdef __cplusplus
}
#endif
#endif
File diff suppressed because it is too large Load Diff
-358
View File
@@ -1,358 +0,0 @@
/* unzip.h -- IO for uncompress .zip files using zlib
Version 1.01e, February 12th, 2005
Copyright (C) 1998-2005 Gilles Vollant
This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g
WinZip, InfoZip tools and compatible.
Multi volume ZipFile (span) are not supported.
Encryption compatible with pkzip 2.04g only supported
Old compressions used by old PKZip 1.x are not supported
I WAIT FEEDBACK at mail info@winimage.com
Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution
Condition of use and distribution are the same than zlib :
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/* for more info about .ZIP format, see
http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip
http://www.info-zip.org/pub/infozip/doc/
PkWare has also a specification at :
ftp://ftp.pkware.com/probdesc.zip
*/
#ifndef _unz_H
#define _unz_H
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _ZLIB_H
#include <zlib.h>
#endif
#ifndef OF
#define OF(args) args
#endif
#ifndef _ZLIBIOAPI_H
#include "ioapi.h"
#endif
#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP)
/* like the STRICT of WIN32, we define a pointer that cannot be converted
from (void*) without cast */
typedef struct TagunzFile__ { int unused; } unzFile__;
typedef unzFile__ *unzFile;
#else
typedef voidp unzFile;
#endif
#define UNZ_OK (0)
#define UNZ_END_OF_LIST_OF_FILE (-100)
#define UNZ_ERRNO (Z_ERRNO)
#define UNZ_EOF (0)
#define UNZ_PARAMERROR (-102)
#define UNZ_BADZIPFILE (-103)
#define UNZ_INTERNALERROR (-104)
#define UNZ_CRCERROR (-105)
/* tm_unz contain date/time info */
typedef struct tm_unz_s
{
uInt tm_sec; /* seconds after the minute - [0,59] */
uInt tm_min; /* minutes after the hour - [0,59] */
uInt tm_hour; /* hours since midnight - [0,23] */
uInt tm_mday; /* day of the month - [1,31] */
uInt tm_mon; /* months since January - [0,11] */
uInt tm_year; /* years - [1980..2044] */
} tm_unz;
/* unz_global_info structure contain global data about the ZIPfile
These data comes from the end of central dir */
typedef struct unz_global_info_s
{
uLong number_entry; /* total number of entries in
the central dir on this disk */
uLong size_comment; /* size of the global comment of the zipfile */
} unz_global_info;
/* unz_file_info contain information about a file in the zipfile */
typedef struct unz_file_info_s
{
uLong version; /* version made by 2 bytes */
uLong version_needed; /* version needed to extract 2 bytes */
uLong flag; /* general purpose bit flag 2 bytes */
uLong compression_method; /* compression method 2 bytes */
uLong dosDate; /* last mod file date in Dos fmt 4 bytes */
uLong crc; /* crc-32 4 bytes */
uLong compressed_size; /* compressed size 4 bytes */
uLong uncompressed_size; /* uncompressed size 4 bytes */
uLong size_filename; /* filename length 2 bytes */
uLong size_file_extra; /* extra field length 2 bytes */
uLong size_file_comment; /* file comment length 2 bytes */
uLong disk_num_start; /* disk number start 2 bytes */
uLong internal_fa; /* internal file attributes 2 bytes */
uLong external_fa; /* external file attributes 4 bytes */
tm_unz tmu_date;
} unz_file_info;
extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1,
const char* fileName2,
int iCaseSensitivity));
/*
Compare two filename (fileName1,fileName2).
If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
or strcasecmp)
If iCaseSenisivity = 0, case sensitivity is defaut of your operating system
(like 1 on Unix, 2 on Windows)
*/
extern unzFile ZEXPORT unzOpen OF((const char *path));
/*
Open a Zip file. path contain the full pathname (by example,
on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer
"zlib/zlib113.zip".
If the zipfile cannot be opened (file don't exist or in not valid), the
return value is NULL.
Else, the return value is a unzFile Handle, usable with other function
of this unzip package.
*/
extern unzFile ZEXPORT unzOpen2 OF((const char *path,
zlib_filefunc_def* pzlib_filefunc_def));
/*
Open a Zip file, like unzOpen, but provide a set of file low level API
for read/write the zip file (see ioapi.h)
*/
extern int ZEXPORT unzClose OF((unzFile file));
/*
Close a ZipFile opened with unzipOpen.
If there is files inside the .Zip opened with unzOpenCurrentFile (see later),
these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
return UNZ_OK if there is no problem. */
extern int ZEXPORT unzGetGlobalInfo OF((unzFile file,
unz_global_info *pglobal_info));
/*
Write info about the ZipFile in the *pglobal_info structure.
No preparation of the structure is needed
return UNZ_OK if there is no problem. */
extern int ZEXPORT unzGetGlobalComment OF((unzFile file,
char *szComment,
uLong uSizeBuf));
/*
Get the global comment string of the ZipFile, in the szComment buffer.
uSizeBuf is the size of the szComment buffer.
return the number of byte copied or an error code <0
*/
/***************************************************************************/
/* Unzip package allow you browse the directory of the zipfile */
extern int ZEXPORT unzGoToFirstFile OF((unzFile file));
/*
Set the current file of the zipfile to the first file.
return UNZ_OK if there is no problem
*/
extern int ZEXPORT unzGoToNextFile OF((unzFile file));
/*
Set the current file of the zipfile to the next file.
return UNZ_OK if there is no problem
return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
*/
extern int ZEXPORT unzLocateFile OF((unzFile file,
const char *szFileName,
int iCaseSensitivity));
/*
Try locate the file szFileName in the zipfile.
For the iCaseSensitivity signification, see unzStringFileNameCompare
return value :
UNZ_OK if the file is found. It becomes the current file.
UNZ_END_OF_LIST_OF_FILE if the file is not found
*/
/* ****************************************** */
/* Ryan supplied functions */
/* unz_file_info contain information about a file in the zipfile */
typedef struct unz_file_pos_s
{
uLong pos_in_zip_directory; /* offset in zip file directory */
uLong num_of_file; /* # of file */
} unz_file_pos;
extern int ZEXPORT unzGetFilePos(
unzFile file,
unz_file_pos* file_pos);
extern int ZEXPORT unzGoToFilePos(
unzFile file,
unz_file_pos* file_pos);
/* ****************************************** */
extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file,
unz_file_info *pfile_info,
char *szFileName,
uLong fileNameBufferSize,
void *extraField,
uLong extraFieldBufferSize,
char *szComment,
uLong commentBufferSize));
/*
Get Info about the current file
if pfile_info!=NULL, the *pfile_info structure will contain somes info about
the current file
if szFileName!=NULL, the filemane string will be copied in szFileName
(fileNameBufferSize is the size of the buffer)
if extraField!=NULL, the extra field information will be copied in extraField
(extraFieldBufferSize is the size of the buffer).
This is the Central-header version of the extra field
if szComment!=NULL, the comment string of the file will be copied in szComment
(commentBufferSize is the size of the buffer)
*/
/***************************************************************************/
/* for reading the content of the current zipfile, you can open it, read data
from it, and close it (you can close it before reading all the file)
*/
extern int ZEXPORT unzOpenCurrentFile OF((unzFile file));
/*
Open for reading data the current file in the zipfile.
If there is no error, the return value is UNZ_OK.
*/
extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file,
const char* password));
/*
Open for reading data the current file in the zipfile.
password is a crypting password
If there is no error, the return value is UNZ_OK.
*/
extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file,
int* method,
int* level,
int raw));
/*
Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
if raw==1
*method will receive method of compression, *level will receive level of
compression
note : you can set level parameter as NULL (if you did not want known level,
but you CANNOT set method parameter as NULL
*/
extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file,
int* method,
int* level,
int raw,
const char* password));
/*
Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
if raw==1
*method will receive method of compression, *level will receive level of
compression
note : you can set level parameter as NULL (if you did not want known level,
but you CANNOT set method parameter as NULL
*/
extern int ZEXPORT unzCloseCurrentFile OF((unzFile file));
/*
Close the file in zip opened with unzOpenCurrentFile
Return UNZ_CRCERROR if all the file was read but the CRC is not good
*/
extern int ZEXPORT unzReadCurrentFile OF((unzFile file,
voidp buf,
unsigned len));
/*
Read bytes from the current file (opened by unzOpenCurrentFile)
buf contain buffer where data must be copied
len the size of buf.
return the number of byte copied if somes bytes are copied
return 0 if the end of file was reached
return <0 with error code if there is an error
(UNZ_ERRNO for IO error, or zLib error for uncompress error)
*/
extern z_off_t ZEXPORT unztell OF((unzFile file));
/*
Give the current position in uncompressed data
*/
extern int ZEXPORT unzeof OF((unzFile file));
/*
return 1 if the end of file was reached, 0 elsewhere
*/
extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file,
voidp buf,
unsigned len));
/*
Read extra field from the current file (opened by unzOpenCurrentFile)
This is the local-header version of the extra field (sometimes, there is
more info in the local-header version than in the central-header)
if buf==NULL, it return the size of the local extra field
if buf!=NULL, len is the size of the buffer, the extra header is copied in
buf.
the return value is the number of bytes copied in buf, or (if <0)
the error code
*/
/***************************************************************************/
/* Get the current file offset */
extern uLong ZEXPORT unzGetOffset (unzFile file);
/* Set the current file offset */
extern int ZEXPORT unzSetOffset (unzFile file, uLong pos);
#ifdef __cplusplus
}
#endif
#endif /* _unz_H */
-158
View File
@@ -1,158 +0,0 @@
//
// Copyright (C) 2007-2010 by sinamas <sinamas at 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 "memptrs.h"
#include <algorithm>
#include <cstring>
namespace gambatte {
MemPtrs::MemPtrs()
: rmem_()
, wmem_()
, romdata_()
, wramdata_()
, vrambankptr_(0)
, rsrambankptr_(0)
, wsrambankptr_(0)
, memchunk_(0)
, rambankdata_(0)
, wramdataend_(0)
, oamDmaSrc_(oam_dma_src_off)
{
}
MemPtrs::~MemPtrs() {
delete []memchunk_;
}
void MemPtrs::reset(unsigned const rombanks, unsigned const rambanks, unsigned const wrambanks) {
delete []memchunk_;
memchunk_ = new unsigned char[
0x4000
+ rombanks * 0x4000ul
+ 0x4000
+ rambanks * 0x2000ul
+ wrambanks * 0x1000ul
+ 0x4000];
romdata_[0] = romdata();
rambankdata_ = romdata_[0] + rombanks * 0x4000ul + 0x4000;
wramdata_[0] = rambankdata_ + rambanks * 0x2000ul;
wramdataend_ = wramdata_[0] + wrambanks * 0x1000ul;
std::memset(rdisabledRamw(), 0xFF, 0x2000);
oamDmaSrc_ = oam_dma_src_off;
rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0];
rmem_[0xC] = wmem_[0xC] = wramdata_[0] - 0xC000;
rmem_[0xE] = wmem_[0xE] = wramdata_[0] - 0xE000;
setRombank(1);
setRambank(0, 0);
setVrambank(0);
setWrambank(1);
}
void MemPtrs::setRombank0(unsigned bank) {
romdata_[0] = romdata() + bank * 0x4000ul;
rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0];
disconnectOamDmaAreas();
}
void MemPtrs::setRombank(unsigned bank) {
romdata_[1] = romdata() + bank * 0x4000ul - 0x4000;
rmem_[0x7] = rmem_[0x6] = rmem_[0x5] = rmem_[0x4] = romdata_[1];
disconnectOamDmaAreas();
}
void MemPtrs::setRambank(unsigned const flags, unsigned const rambank) {
unsigned char *srambankptr = 0;
if (!(flags & rtc_en)) {
srambankptr = rambankdata() != rambankdataend()
? rambankdata_ + rambank * 0x2000ul - 0xA000
: wdisabledRam() - 0xA000;
}
rsrambankptr_ = (flags & read_en) && srambankptr != wdisabledRam() - 0xA000
? srambankptr
: rdisabledRamw() - 0xA000;
wsrambankptr_ = flags & write_en ? srambankptr : wdisabledRam() - 0xA000;
rmem_[0xB] = rmem_[0xA] = rsrambankptr_;
wmem_[0xB] = wmem_[0xA] = wsrambankptr_;
disconnectOamDmaAreas();
}
void MemPtrs::setWrambank(unsigned bank) {
wramdata_[1] = wramdata_[0] + (bank & 0x07 ? bank & 0x07 : 1) * 0x1000;
rmem_[0xD] = wmem_[0xD] = wramdata_[1] - 0xD000;
disconnectOamDmaAreas();
}
void MemPtrs::setOamDmaSrc(OamDmaSrc oamDmaSrc) {
rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0];
rmem_[0x7] = rmem_[0x6] = rmem_[0x5] = rmem_[0x4] = romdata_[1];
rmem_[0xB] = rmem_[0xA] = rsrambankptr_;
wmem_[0xB] = wmem_[0xA] = wsrambankptr_;
rmem_[0xC] = wmem_[0xC] = wramdata_[0] - 0xC000;
rmem_[0xD] = wmem_[0xD] = wramdata_[1] - 0xD000;
rmem_[0xE] = wmem_[0xE] = wramdata_[0] - 0xE000;
oamDmaSrc_ = oamDmaSrc;
disconnectOamDmaAreas();
}
void MemPtrs::disconnectOamDmaAreas() {
if (isCgb(*this)) {
switch (oamDmaSrc_) {
case oam_dma_src_rom: // fall through
case oam_dma_src_sram:
case oam_dma_src_invalid:
std::fill(rmem_, rmem_ + 8, static_cast<unsigned char *>(0));
rmem_[0xB] = rmem_[0xA] = 0;
wmem_[0xB] = wmem_[0xA] = 0;
break;
case oam_dma_src_vram:
break;
case oam_dma_src_wram:
rmem_[0xE] = rmem_[0xD] = rmem_[0xC] = 0;
wmem_[0xE] = wmem_[0xD] = wmem_[0xC] = 0;
break;
case oam_dma_src_off:
break;
}
} else {
switch (oamDmaSrc_) {
case oam_dma_src_rom: // fall through
case oam_dma_src_sram:
case oam_dma_src_wram:
case oam_dma_src_invalid:
std::fill(rmem_, rmem_ + 8, static_cast<unsigned char *>(0));
rmem_[0xB] = rmem_[0xA] = 0;
wmem_[0xB] = wmem_[0xA] = 0;
rmem_[0xE] = rmem_[0xD] = rmem_[0xC] = 0;
wmem_[0xE] = wmem_[0xD] = wmem_[0xC] = 0;
break;
case oam_dma_src_vram:
break;
case oam_dma_src_off:
break;
}
}
}
}
-21
View File
@@ -1,21 +0,0 @@
#ifndef LCDDEF_H
#define LCDDEF_H
namespace gambatte {
enum { lcdc_bgen = 0x01,
lcdc_objen = 0x02,
lcdc_obj2x = 0x04,
lcdc_tdsel = 0x10,
lcdc_we = 0x20,
lcdc_en = 0x80 };
enum { lcdstat_lycflag = 0x04,
lcdstat_m0irqen = 0x08,
lcdstat_m1irqen = 0x10,
lcdstat_m2irqen = 0x20,
lcdstat_lycirqen = 0x40 };
}
#endif
-63
View File
@@ -1,63 +0,0 @@
#ifndef M0_IRQ_H
#define M0_IRQ_H
#include "lcddef.h"
#include "../savestate.h"
namespace gambatte {
class M0Irq {
public:
M0Irq()
: statReg_(0)
, lycReg_(0)
{
}
void lcdReset(unsigned statReg, unsigned lycReg) {
statReg_ = statReg;
lycReg_ = lycReg;
}
void statRegChange(unsigned statReg,
unsigned long nextM0IrqTime, unsigned long cc, bool cgb) {
if (nextM0IrqTime - cc > cgb * 2U)
statReg_ = statReg;
}
void lycRegChange(unsigned lycReg,
unsigned long nextM0IrqTime, unsigned long cc,
bool ds, bool cgb) {
if (nextM0IrqTime - cc > cgb * 5 + 1U - ds)
lycReg_ = lycReg;
}
void doEvent(unsigned char *ifreg, unsigned ly, unsigned statReg, unsigned lycReg) {
if (((statReg_ | statReg) & lcdstat_m0irqen)
&& (!(statReg_ & lcdstat_lycirqen) || ly != lycReg_)) {
*ifreg |= 2;
}
statReg_ = statReg;
lycReg_ = lycReg;
}
void saveState(SaveState &state) const {
state.ppu.m0lyc = lycReg_;
}
void loadState(SaveState const &state) {
lycReg_ = state.ppu.m0lyc;
statReg_ = state.mem.ioamhram.get()[0x141];
}
unsigned statReg() const { return statReg_; }
private:
unsigned char statReg_;
unsigned char lycReg_;
};
}
#endif
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -14,7 +14,7 @@
* 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. *
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
#ifndef ARRAY_H
#define ARRAY_H
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
/*
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef BITMAP_FONT_H
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "cpu.h"
@@ -23,7 +23,7 @@
namespace gambatte {
CPU::CPU()
: mem_(Interrupter(sp, pc_))
: mem_(Interrupter(sp, pc_, opcode_, prefetched_))
, cycleCounter_(0)
, pc_(0x100)
, sp(0xFFFE)
@@ -38,7 +38,8 @@ CPU::CPU()
, e(0xD8)
, h(0x01)
, l(0x4D)
, skip_(false)
, opcode_(0)
, prefetched_(false)
{
}
@@ -99,7 +100,9 @@ void CPU::saveState(SaveState &state) {
state.cpu.f = toF(hf2, cf, zf);
state.cpu.h = h;
state.cpu.l = l;
state.cpu.skip = skip_;
state.cpu.opcode = opcode_;
state.cpu.prefetched = prefetched_;
state.cpu.skip = false;
}
void CPU::loadState(SaveState const &state) {
@@ -118,7 +121,12 @@ void CPU::loadState(SaveState const &state) {
cf = cfFromF(state.cpu.f);
h = state.cpu.h & 0xFF;
l = state.cpu.l & 0xFF;
skip_ = state.cpu.skip;
opcode_ = state.cpu.opcode;
prefetched_ = state.cpu.prefetched;
if (state.cpu.skip) {
opcode_ = mem_.read(pc_, cycleCounter_);
prefetched_ = true;
}
}
// The main reasons for the use of macros is to more conveniently be able to tweak
@@ -126,9 +134,9 @@ void CPU::loadState(SaveState const &state) {
// time they were written GCC had a tendency to not be able to keep hot variables
// in regs if you took an address/reference in an inline function.
#define bc() ( b << 8 | c )
#define de() ( d << 8 | e )
#define hl() ( h << 8 | l )
#define bc() ( b * 0x100u | c )
#define de() ( d * 0x100u | e )
#define hl() ( h * 0x100u | l )
#define READ(dest, addr) do { (dest) = mem_.read(addr, cycleCounter); cycleCounter += 4; } while (0)
#define PC_READ(dest) do { (dest) = mem_.read(pc, cycleCounter); pc = (pc + 1) & 0xFFFF; cycleCounter += 4; } while (0)
@@ -187,7 +195,7 @@ void CPU::loadState(SaveState const &state) {
// Rotate 8-bit register right through CF, store old bit0 in CF, old CF value becomes bit7. Reset SF and HCF, Check ZF:
#define rr_r(r) do { \
unsigned const oldcf = cf & 0x100; \
cf = (r) << 8; \
cf = (r) * 0x100u; \
(r) = zf = ((r) | oldcf) >> 1; \
hf2 = 0; \
} while (0)
@@ -203,7 +211,7 @@ void CPU::loadState(SaveState const &state) {
// sra r (8 cycles):
// Shift 8-bit register right, store old bit0 in CF. bit7=old bit7. Reset SF and HCF, Check ZF:
#define sra_r(r) do { \
cf = (r) << 8; \
cf = (r) * 0x100u; \
zf = (r) >> 1; \
(r) = zf | ((r) & 0x80); \
hf2 = 0; \
@@ -213,7 +221,7 @@ void CPU::loadState(SaveState const &state) {
// Shift 8-bit register right, store old bit0 in CF. Reset SF and HCF, Check ZF:
#define srl_r(r) do { \
zf = (r); \
cf = (r) << 8; \
cf = (r) * 0x100u; \
zf >>= 1; \
(r) = zf; \
hf2 = 0; \
@@ -274,7 +282,7 @@ void CPU::loadState(SaveState const &state) {
unsigned const hl = hl(); \
unsigned val; \
READ(val, hl); \
val &= ~(1 << (n)); \
val &= ~(1u << (n)); \
WRITE(hl, val); \
} while (0)
@@ -290,8 +298,8 @@ void CPU::loadState(SaveState const &state) {
// push rr (16 cycles):
// Push value of register pair onto stack:
#define push_rr(r1, r2) do { \
PUSH(r1, r2); \
cycleCounter += 4; \
PUSH(r1, r2); \
} while (0)
// pop rr (12 cycles):
@@ -474,8 +482,8 @@ void CPU::loadState(SaveState const &state) {
// rst n (16 Cycles):
// Push present address onto stack, jump to address n (one of 00h,08h,10h,18h,20h,28h,30h,38h):
#define rst_n(n) do { \
PUSH(pc >> 8, pc & 0xFF); \
PC_MOD(n); \
push_rr(pc >> 8, pc & 0xFF); \
pc = (n); \
} while (0)
// ret (16 cycles):
@@ -486,6 +494,20 @@ void CPU::loadState(SaveState const &state) {
PC_MOD(high << 8 | low); \
} while (0)
namespace {
unsigned long freeze(Memory &mem, unsigned long cc) {
mem.freeze(cc);
if (cc < mem.nextEventTime()) {
unsigned long cycles = mem.nextEventTime() - cc;
cc += cycles + (-cycles & 3);
}
return cc;
}
}
void CPU::process(unsigned long const cycles) {
mem_.setEndtime(cycleCounter_, cycles);
mem_.updateInput();
@@ -498,17 +520,18 @@ void CPU::process(unsigned long const cycles) {
if (mem_.halted()) {
if (cycleCounter < mem_.nextEventTime()) {
unsigned long cycles = mem_.nextEventTime() - cycleCounter;
cycleCounter += cycles + (-cycles & 3);
unsigned long cpu_cycles = mem_.nextEventTime() - cycleCounter;
cycleCounter += cpu_cycles + (-cpu_cycles & 3);
}
} else while (cycleCounter < mem_.nextEventTime()) {
unsigned char opcode;
PC_READ(opcode);
if (skip_) {
pc = (pc - 1) & 0xFFFF;
skip_ = false;
if (!prefetched_) {
PC_READ(opcode);
} else {
opcode = opcode_;
cycleCounter += 4;
prefetched_ = false;
}
switch (opcode) {
@@ -579,7 +602,7 @@ void CPU::process(unsigned long const cycles) {
// rrca (4 cycles):
// Rotate 8-bit register A right, store old bit0 in CF. Reset SF, HCF, ZF:
case 0x0F:
cf = a << 8 | a;
cf = a * 0x100u | a;
a = cf >> 1 & 0xFF;
hf2 = 0;
zf = 1;
@@ -588,13 +611,11 @@ void CPU::process(unsigned long const cycles) {
// stop (4 cycles):
// Halt CPU and LCD display until button pressed:
case 0x10:
pc = (pc + 1) & 0xFFFF;
cycleCounter = mem_.stop(cycleCounter);
PC_READ(opcode_);
cycleCounter = mem_.stop(cycleCounter - 4, prefetched_);
if (cycleCounter < mem_.nextEventTime()) {
unsigned long cycles = mem_.nextEventTime() - cycleCounter;
cycleCounter += cycles + (-cycles & 3);
unsigned long cpu_cycles = mem_.nextEventTime() - cycleCounter;
cycleCounter += cpu_cycles + (-cpu_cycles & 3);
}
break;
@@ -660,7 +681,7 @@ void CPU::process(unsigned long const cycles) {
case 0x1F:
{
unsigned oldcf = cf & 0x100;
cf = a << 8;
cf = a * 0x100u;
a = (a | oldcf) >> 1;
}
@@ -994,21 +1015,17 @@ void CPU::process(unsigned long const cycles) {
case 0x74: WRITE(hl(), h); break;
case 0x75: WRITE(hl(), l); break;
// halt (4 cycles):
// halt (4n cycles):
case 0x76:
if (!mem_.ime()
&& ( mem_.ff_read(0x0F, cycleCounter)
& mem_.ff_read(0xFF, cycleCounter) & 0x1F)) {
if (mem_.isCgb())
cycleCounter += 4;
else
skip_ = true;
opcode_ = mem_.read(pc, cycleCounter);
if (mem_.pendingIrqs(cycleCounter)) {
prefetched_ = true;
} else {
mem_.halt();
prefetched_ = mem_.halt(cycleCounter);
cycleCounter += 4 + 4 * !mem_.isCgb();
if (cycleCounter < mem_.nextEventTime()) {
unsigned long cycles = mem_.nextEventTime() - cycleCounter;
cycleCounter += cycles + (-cycles & 3);
unsigned long cpu_cycles = mem_.nextEventTime() - cycleCounter;
cycleCounter += cpu_cycles + (-cpu_cycles & 3);
}
}
@@ -1680,6 +1697,7 @@ void CPU::process(unsigned long const cycles) {
break;
case 0xD3: // not specified. should freeze.
cycleCounter = freeze(mem_, cycleCounter);
break;
// call nc,nn (24;12 cycles):
@@ -1747,6 +1765,7 @@ void CPU::process(unsigned long const cycles) {
break;
case 0xDB: // not specified. should freeze.
cycleCounter = freeze(mem_, cycleCounter);
break;
// call z,nn (24;12 cycles):
@@ -1797,8 +1816,8 @@ void CPU::process(unsigned long const cycles) {
break;
case 0xE3: // not specified. should freeze.
break;
case 0xE4: // not specified. should freeze.
cycleCounter = freeze(mem_, cycleCounter);
break;
case 0xE5:
@@ -1845,10 +1864,9 @@ void CPU::process(unsigned long const cycles) {
break;
case 0xEB: // not specified. should freeze.
break;
case 0xEC: // not specified. should freeze.
break;
case 0xED: // not specified. should freeze.
cycleCounter = freeze(mem_, cycleCounter);
break;
case 0xEE:
@@ -1898,6 +1916,7 @@ void CPU::process(unsigned long const cycles) {
break;
case 0xF4: // not specified. should freeze.
cycleCounter = freeze(mem_, cycleCounter);
break;
case 0xF5:
@@ -1962,9 +1981,10 @@ void CPU::process(unsigned long const cycles) {
break;
case 0xFC: // not specified. should freeze.
break;
case 0xFD: // not specified. should freeze
cycleCounter = freeze(mem_, cycleCounter);
break;
case 0xFE:
{
unsigned data;
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef CPU_H
@@ -23,6 +23,8 @@
namespace gambatte {
class File;
class CPU {
public:
CPU();
@@ -53,8 +55,8 @@ public:
mem_.setOsdElement(osdElement);
}
LoadRes load(std::string const &romfile, bool forceDmg, bool multicartCompat) {
return mem_.loadROM(romfile, forceDmg, multicartCompat);
LoadRes load(File &file, std::string const &filename, bool forceDmg, bool multicartCompat) {
return mem_.loadROM(file, filename, forceDmg, multicartCompat);
}
bool loaded() const { return mem_.loaded(); }
@@ -64,6 +66,10 @@ public:
std::size_t fillSoundBuffer() { return mem_.fillSoundBuffer(cycleCounter_); }
bool isCgb() const { return mem_.isCgb(); }
void setCgbColorCorrection(int optNum) {
mem_.setCgbColorCorrection(optNum);
}
void setDmgPaletteColor(int palNum, int colorNum, unsigned long rgb32) {
mem_.setDmgPaletteColor(palNum, colorNum, rgb32);
}
@@ -78,7 +84,8 @@ private:
unsigned short sp;
unsigned hf1, hf2, zf, cf;
unsigned char a_, b, c, d, e, /*f,*/ h, l;
bool skip_;
unsigned char opcode_;
bool prefetched_;
void process(unsigned long cycles);
};
@@ -17,7 +17,7 @@ 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.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
***************************************************************************/
#include "stdfile.h"
@@ -17,7 +17,7 @@ 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.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
***************************************************************************/
#ifndef GAMBATTE_FILE_H
#define GAMBATTE_FILE_H
+58
View File
@@ -0,0 +1,58 @@
/***************************************************************************
Copyright (C) 2007 by Nach
http://nsrt.edgeemu.com
Copyright (C) 2007-2011 by sinamas <sinamas at users.sourceforge.net>
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.,
51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
***************************************************************************/
#ifndef GAMBATTE_MEMFILE_H
#define GAMBATTE_MEMFILE_H
#include <string.h>
#include "file.h"
namespace gambatte {
class MemFile : public File {
public:
explicit MemFile(const void *data, size_t size)
: data_(static_cast<const char*>(data))
, size_(size)
, offset_(0)
{
}
virtual void read(char *buffer, size_t amount) {
size_t remaining = size_ - offset_;
size_t bytes_to_read = amount < remaining ? amount : remaining;
memcpy(buffer, data_ + offset_, bytes_to_read);
offset_ += bytes_to_read;
}
virtual void rewind() { offset_ = 0; }
virtual size_t size() const { return size_; };
virtual bool fail() const { return false; }
private:
const char *data_;
size_t size_;
size_t offset_;
};
}
#endif
@@ -17,7 +17,7 @@ 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.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
***************************************************************************/
#ifndef GAMBATTE_STD_FILE_H
#define GAMBATTE_STD_FILE_H
@@ -13,29 +13,36 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "file/file.h"
#include "file/memfile.h"
#include "gambatte.h"
#include "cpu.h"
#include "initstate.h"
#include "savestate.h"
#include "state_osd_elements.h"
#include "statesaver.h"
#include <cstring>
#include <sstream>
static std::string const itos(int i) {
using namespace gambatte;
namespace {
std::string to_string(int i) {
std::stringstream ss;
ss << i;
return ss.str();
}
static std::string const statePath(std::string const &basePath, int stateNo) {
return basePath + "_" + itos(stateNo) + ".gqs";
std::string statePath(std::string const &basePath, int stateNo) {
return basePath + '_' + to_string(stateNo) + ".gqs";
}
namespace gambatte {
}
struct GB::Priv {
CPU cpu;
@@ -91,11 +98,24 @@ void GB::setSaveDir(std::string const &sdir) {
p_->cpu.setSaveDir(sdir);
}
LoadRes GB::load(std::string const &romfile, unsigned const flags) {
LoadRes GB::load(const void *rom,
size_t size,
std::string const &filename,
unsigned const flags) {
MemFile file(rom, size);
return load(file, filename, flags);
}
LoadRes GB::load(std::string const &filename, unsigned const flags) {
transfer_ptr<File> file = newFileInstance(filename);
return load(*file, filename, flags);
}
LoadRes GB::load(File &file, std::string const &filename, unsigned const flags) {
if (p_->cpu.loaded())
p_->cpu.saveSavedata();
LoadRes const loadres = p_->cpu.load(romfile,
LoadRes const loadres = p_->cpu.load(file, filename,
flags & FORCE_DMG,
flags & MULTICART_COMPAT);
if (loadres == LOADRES_OK) {
@@ -126,10 +146,15 @@ void GB::saveSavedata() {
p_->cpu.saveSavedata();
}
void GB::setCgbColorCorrection(int optNum) {
p_->cpu.setCgbColorCorrection(optNum);
}
void GB::setDmgPaletteColor(int palNum, int colorNum, unsigned long rgb32) {
p_->cpu.setDmgPaletteColor(palNum, colorNum, rgb32);
}
//> OpenEmu
bool GB::serializeState(std::ostream &stream) {
if (p_->cpu.loaded()) {
SaveState state;
@@ -137,31 +162,32 @@ bool GB::serializeState(std::ostream &stream) {
p_->cpu.saveState(state);
return StateSaver::serializeState(state, stream);
}
return false;
}
bool GB::deserializeState(std::istream &stream) {
if (p_->cpu.loaded()) {
p_->cpu.saveSavedata();
SaveState state;
p_->cpu.setStatePtrs(state);
if (StateSaver::deserializeState(state, stream)) {
p_->cpu.loadState(state);
return true;
}
}
return false;
}
//< OpenEmu
bool GB::loadState(std::string const &filepath) {
if (p_->cpu.loaded()) {
p_->cpu.saveSavedata();
SaveState state;
SaveState state = SaveState();
p_->cpu.setStatePtrs(state);
if (StateSaver::loadState(state, filepath)) {
@@ -235,5 +261,3 @@ void GB::setGameGenie(std::string const &codes) {
void GB::setGameShark(std::string const &codes) {
p_->cpu.setGameShark(codes);
}
}
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef GAMBATTE_H
@@ -27,6 +27,8 @@
namespace gambatte {
class File;
enum { BG_PALETTE = 0, SP1_PALETTE = 1, SP2_PALETTE = 2 };
class GB {
@@ -52,6 +54,17 @@ public:
*/
LoadRes load(std::string const &romfile, unsigned flags = 0);
/**
* Load an in-memory ROM image.
*
* @param rom The ROM image data to load.
* @param size The size of the ROM image, in bytes.
* @param filename A filename, which is only used for naming save files.
* @param flags ORed combination of LoadFlags.
* @return 0 on success, negative value on failure.
*/
LoadRes load(const void *rom, size_t size, std::string const &filename, unsigned flags = 0);
/**
* Emulates until at least 'samples' audio samples are produced in the
* supplied audio buffer, or until a video frame has been drawn.
@@ -89,6 +102,11 @@ public:
*/
void reset();
/**
* @param optNum 0 <= palNum < 1
*/
void setCgbColorCorrection(int optNum);
/**
* @param palNum 0 <= palNum < 3. One of BG_PALETTE, SP1_PALETTE and SP2_PALETTE.
* @param colorNum 0 <= colorNum < 4
@@ -111,18 +129,20 @@ public:
bool isLoaded() const;
/** Writes persistent cartridge data to disk. Done implicitly on ROM close. */
void saveSavedata();
void saveSavedata();
//> OpenEmu
/** Serializes state data to 'stream'
* @return success
*/
bool serializeState(std::ostream &stream);
/** Deserializes state data from 'stream'.
* @return success
*/
bool deserializeState(std::istream &stream);
//< OpenEmu
/**
* Saves emulator state to the state slot selected with selectState().
* The data will be stored in the directory given by setSaveDir().
@@ -191,6 +211,8 @@ public:
void setGameShark(std::string const &codes);
private:
LoadRes load(File &file, std::string const &filename, unsigned flags);
struct Priv;
Priv *const p_;
@@ -13,23 +13,12 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef GAMBATTE_INT_H
#define GAMBATTE_INT_H
#ifdef HAVE_CSTDINT
#include <cstdint>
namespace gambatte {
using std::uint_least32_t;
using std::uint_least16_t;
}
#elif defined(HAVE_STDINT_H)
#include <stdint.h>
namespace gambatte {
@@ -37,26 +26,4 @@ using ::uint_least32_t;
using ::uint_least16_t;
}
#else
namespace gambatte {
#ifdef CHAR_LEAST_32
typedef unsigned char uint_least32_t;
#elif defined(SHORT_LEAST_32)
typedef unsigned short uint_least32_t;
#elif defined(INT_LEAST_32)
typedef unsigned uint_least32_t;
#else
typedef unsigned long uint_least32_t;
#endif
#ifdef CHAR_LEAST_16
typedef unsigned char uint_least16_t;
#else
typedef unsigned short uint_least16_t;
#endif
}
#endif
#endif
@@ -13,13 +13,14 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "initstate.h"
#include "counterdef.h"
#include "savestate.h"
#include "sound/sound_unit.h"
#include <algorithm>
#include <cstring>
#include <ctime>
@@ -1178,6 +1179,8 @@ void gambatte::setInitState(SaveState &state, bool const cgb, bool const gbaCgbM
state.cpu.f = 0xB0;
state.cpu.h = 0x01;
state.cpu.l = 0x4D;
state.cpu.opcode = 0x00;
state.cpu.prefetched = false;
state.cpu.skip = false;
std::memset(state.mem.sram.ptr, 0xFF, state.mem.sram.size());
@@ -1192,11 +1195,14 @@ void gambatte::setInitState(SaveState &state, bool const cgb, bool const gbaCgbM
setInitialDmgIoamhram(state.mem.ioamhram.ptr);
}
state.mem.ioamhram.ptr[0x104] = 0x1C;
state.mem.ioamhram.ptr[0x104] = 0x00;
state.mem.ioamhram.ptr[0x140] = 0x91;
state.mem.ioamhram.ptr[0x144] = 0x00;
state.mem.divLastUpdate = 0;
// DIV, TIMA, and the PSG frame sequencer are clocked by bits of the
// cycle counter less divLastUpdate (equivalent to a counter that is
// reset on DIV write).
state.mem.divLastUpdate = -0x1C00;
state.mem.timaLastUpdate = 0;
state.mem.tmatime = disabled_time;
state.mem.nextSerialtime = disabled_time;
@@ -1208,13 +1214,13 @@ void gambatte::setInitState(SaveState &state, bool const cgb, bool const gbaCgbM
state.mem.dmaDestination = 0;
state.mem.rambank = 0;
state.mem.oamDmaPos = 0xFE;
state.mem.haltHdmaState = 0;
state.mem.IME = false;
state.mem.halted = false;
state.mem.enableRam = false;
state.mem.rambankMode = false;
state.mem.hdmaTransfer = false;
for (int i = 0x00; i < 0x40; i += 0x02) {
state.ppu.bgpData.ptr[i ] = 0xFF;
state.ppu.bgpData.ptr[i + 1] = 0x7F;
@@ -1261,11 +1267,12 @@ void gambatte::setInitState(SaveState &state, bool const cgb, bool const gbaCgbM
// spu.cycleCounter >> 12 & 7 represents the frame sequencer position.
state.spu.cycleCounter = (cgb ? 0x1E00 : 0x2400) | (state.cpu.cycleCounter >> 1 & 0x1FF);
state.spu.lastUpdate = 0;
state.spu.ch1.sweep.counter = SoundUnit::counter_disabled;
state.spu.ch1.sweep.shadow = 0;
state.spu.ch1.sweep.nr0 = 0;
state.spu.ch1.sweep.negging = false;
state.spu.ch1.sweep.neg = false;
if (cgb) {
state.spu.ch1.duty.nextPosUpdate = (state.spu.cycleCounter & ~1ul) + 37 * 2;
state.spu.ch1.duty.pos = 6;
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef INITSTATE_H
+1 -1
View File
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef GAMBATTE_INPUTGETTER_H
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef INSERTION_SORT_H
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "interrupter.h"
@@ -21,21 +21,47 @@
namespace gambatte {
Interrupter::Interrupter(unsigned short &sp, unsigned short &pc)
Interrupter::Interrupter(unsigned short &sp, unsigned short &pc, unsigned char &opcode, bool &prefetched)
: sp_(sp)
, pc_(pc)
, opcode_(opcode)
, prefetched_(prefetched)
{
}
unsigned long Interrupter::interrupt(unsigned const address, unsigned long cc, Memory &memory) {
cc += 8;
void Interrupter::prefetch(unsigned long cc, Memory &mem) {
if (!prefetched_) {
opcode_ = mem.read(pc_, cc);
pc_ = (pc_ + 1) & 0xFFFF;
prefetched_ = true;
}
}
unsigned long Interrupter::interrupt(unsigned long cc, Memory &memory) {
// undo prefetch (presumably unconditional on hw).
if (prefetched_) {
pc_ = (pc_ - 1) & 0xFFFF;
prefetched_ = false;
}
cc += 12;
sp_ = (sp_ - 1) & 0xFFFF;
memory.write(sp_, pc_ >> 8, cc);
cc += 4;
unsigned const pendingIrqs = memory.pendingIrqs(cc);
unsigned const n = pendingIrqs & -pendingIrqs;
unsigned address;
if (n <= 4) {
static unsigned char const lut[] = { 0x00, 0x40, 0x48, 0x48, 0x50 };
address = lut[n];
} else
address = 0x50 + n;
sp_ = (sp_ - 1) & 0xFFFF;
memory.write(sp_, pc_ & 0xFF, cc);
memory.ackIrq(n, cc);
pc_ = address;
cc += 8;
cc += 4;
if (address == 0x40 && !gsCodes_.empty())
applyVblankCheats(cc, memory);
@@ -52,6 +78,8 @@ void Interrupter::setGameShark(std::string const &codes) {
gsCodes_.clear();
for (std::size_t pos = 0; pos < codes.length(); pos += code.length() + 1) {
// OpenEmu
//code = codes.substr(pos, codes.find(';', pos) - pos);
code = codes.substr(pos, codes.find('+', pos) - pos);
if (code.length() >= 8) {
GsCode gs;
@@ -68,6 +96,8 @@ void Interrupter::setGameShark(std::string const &codes) {
void Interrupter::applyVblankCheats(unsigned long const cc, Memory &memory) {
for (std::size_t i = 0, size = gsCodes_.size(); i < size; ++i) {
// OpenEmu
//if (gsCodes_[i].type == 0x01)
if (gsCodes_[i].type == 0x01 || gsCodes_[i].type == 0x91)
memory.write(gsCodes_[i].address, gsCodes_[i].value, cc);
}
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef INTERRUPTER_H
@@ -34,13 +34,16 @@ class Memory;
class Interrupter {
public:
Interrupter(unsigned short &sp, unsigned short &pc);
unsigned long interrupt(unsigned address, unsigned long cycleCounter, Memory &memory);
Interrupter(unsigned short &sp, unsigned short &pc, unsigned char &opcode, bool &prefetched);
void prefetch(unsigned long cc, Memory &mem);
unsigned long interrupt(unsigned long cycleCounter, Memory &memory);
void setGameShark(std::string const &codes);
private:
unsigned short &sp_;
unsigned short &pc_;
unsigned char &opcode_;
bool &prefetched_;
std::vector<GsCode> gsCodes_;
void applyVblankCheats(unsigned long cc, Memory &mem);
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "interruptrequester.h"
@@ -89,9 +89,14 @@ void InterruptRequester::flagIrq(unsigned bit) {
eventTimes_.setValue<intevent_interrupts>(minIntTime_);
}
void InterruptRequester::ackIrq(unsigned bit) {
ifreg_ ^= bit;
di();
void InterruptRequester::flagIrq(unsigned bit, unsigned long cc) {
unsigned const prevPending = pendingIrqs();
ifreg_ |= bit;
if (!prevPending && pendingIrqs() && intFlags_.imeOrHalted()) {
minIntTime_ = std::max(minIntTime_, cc);
eventTimes_.setValue<intevent_interrupts>(minIntTime_);
}
}
void InterruptRequester::setIereg(unsigned iereg) {
@@ -114,4 +119,11 @@ void InterruptRequester::setIfreg(unsigned ifreg) {
}
}
void InterruptRequester::setMinIntTime(unsigned long cc) {
minIntTime_ = cc;
if (eventTimes_.value(intevent_interrupts) < minIntTime_)
eventTimes_.setValue<intevent_interrupts>(minIntTime_);
}
}
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef INTERRUPT_REQUESTER_H
@@ -51,9 +51,11 @@ public:
void halt();
void unhalt();
void flagIrq(unsigned bit);
void ackIrq(unsigned bit);
void flagIrq(unsigned bit, unsigned long cc);
void ackIrq(unsigned bit) { ifreg_ &= ~bit; }
void setIereg(unsigned iereg);
void setIfreg(unsigned ifreg);
void setMinIntTime(unsigned long cc);
IntEventId minEventId() const { return static_cast<IntEventId>(eventTimes_.min()); }
unsigned long minEventTime() const { return eventTimes_.minValue(); }
@@ -69,9 +71,9 @@ private:
bool halted() const { return flags_ & flag_halted; }
bool imeOrHalted() const { return flags_; }
void setIme() { flags_ |= flag_ime; }
void unsetIme() { flags_ &= ~flag_ime; }
void unsetIme() { flags_ &= ~(1u * flag_ime); }
void setHalted() { flags_ |= flag_halted; }
void unsetHalted() { flags_ &= ~flag_halted; }
void unsetHalted() { flags_ &= ~(1u * flag_halted); }
void set(bool ime, bool halted) { flags_ = halted * flag_halted + ime * flag_ime; }
private:
@@ -13,28 +13,32 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "cartridge.h"
#include "file/file.h"
// OpenEmu
//#include "file/file.h"
#include "../file/file.h"
#include "../savestate.h"
#include "pakinfo_internal.h"
#include <algorithm>
#include <cstring>
#include <fstream>
namespace gambatte {
using namespace gambatte;
namespace {
static unsigned toMulti64Rombank(unsigned rombank) {
unsigned toMulti64Rombank(unsigned rombank) {
return (rombank >> 1 & 0x30) | (rombank & 0xF);
}
class DefaultMbc : public Mbc {
public:
virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank) const {
return (addr< 0x4000) == (bank == 0);
return (addr < 0x4000) == (bank == 0);
}
};
@@ -67,12 +71,12 @@ private:
bool enableRam_;
};
static inline unsigned rambanks(MemPtrs const &memptrs) {
return std::size_t(memptrs.rambankdataend() - memptrs.rambankdata()) / 0x2000;
inline unsigned rambanks(MemPtrs const &memptrs) {
return (memptrs.rambankdataend() - memptrs.rambankdata()) / rambank_size();
}
static inline unsigned rombanks(MemPtrs const &memptrs) {
return std::size_t(memptrs.romdataend() - memptrs.romdata() ) / 0x4000;
inline unsigned rombanks(MemPtrs const &memptrs) {
return (memptrs.romdataend() - memptrs.romdata()) / rombank_size();
}
class Mbc1 : public DefaultMbc {
@@ -107,8 +111,7 @@ public:
break;
case 3:
// Pretty sure this should take effect immediately, but I have a policy not to change old behavior
// unless I have something (eg. a verified test or a game) that justifies it.
// Should this take effect immediately rather?
rambankMode_ = data & 1;
break;
}
@@ -448,22 +451,82 @@ private:
unsigned char rambank_;
bool enableRam_;
static unsigned adjustedRombank(unsigned bank) { return bank ? bank : 1; }
void setRambank() const {
memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0,
rambank_ & (rambanks(memptrs_) - 1));
}
void setRombank() const { memptrs_.setRombank(adjustedRombank(rombank_) & (rombanks(memptrs_) - 1)); }
void setRombank() const { memptrs_.setRombank(rombank_ & (rombanks(memptrs_) - 1)); }
};
static bool hasRtc(unsigned headerByte0x147) {
std::string stripExtension(std::string const &str) {
std::string::size_type const lastDot = str.find_last_of('.');
std::string::size_type const lastSlash = str.find_last_of('/');
if (lastDot != std::string::npos && (lastSlash == std::string::npos || lastSlash < lastDot))
return str.substr(0, lastDot);
return str;
}
std::string stripDir(std::string const &str) {
std::string::size_type const lastSlash = str.find_last_of('/');
if (lastSlash != std::string::npos)
return str.substr(lastSlash + 1);
return str;
}
void enforce8bit(unsigned char *data, std::size_t size) {
if (static_cast<unsigned char>(0x100))
while (size--)
*data++ &= 0xFF;
}
unsigned pow2ceil(unsigned n) {
--n;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
++n;
return n;
}
bool presumedMulti64Mbc1(unsigned char const header[], unsigned rombanks) {
return header[0x147] == 1 && header[0x149] == 0 && rombanks == 64;
}
bool hasBattery(unsigned char headerByte0x147) {
switch (headerByte0x147) {
case 0x03:
case 0x06:
case 0x09:
case 0x0F:
case 0x10:
case 0x13:
case 0x1B:
case 0x1E:
case 0xFF:
return true;
}
return false;
}
bool hasRtc(unsigned headerByte0x147) {
switch (headerByte0x147) {
case 0x0F:
case 0x10: return true;
default: return false;
case 0x10:
return true;
}
return false;
}
int asHex(char c) {
return c >= 'A' ? c - 'A' + 0xA : c - '0';
}
}
@@ -484,24 +547,6 @@ void Cartridge::loadState(SaveState const &state) {
mbc_->loadState(state.mem);
}
static std::string const stripExtension(std::string const &str) {
std::string::size_type const lastDot = str.find_last_of('.');
std::string::size_type const lastSlash = str.find_last_of('/');
if (lastDot != std::string::npos && (lastSlash == std::string::npos || lastSlash < lastDot))
return str.substr(0, lastDot);
return str;
}
static std::string const stripDir(std::string const &str) {
std::string::size_type const lastSlash = str.find_last_of('/');
if (lastSlash != std::string::npos)
return str.substr(lastSlash + 1);
return str;
}
std::string const Cartridge::saveBasePath() const {
return saveDir_.empty()
? defaultSaveBasePath_
@@ -514,33 +559,12 @@ void Cartridge::setSaveDir(std::string const &dir) {
saveDir_ += '/';
}
static void enforce8bit(unsigned char *data, std::size_t size) {
if (static_cast<unsigned char>(0x100))
while (size--)
*data++ &= 0xFF;
}
static unsigned pow2ceil(unsigned n) {
--n;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
++n;
return n;
}
static bool presumedMulti64Mbc1(unsigned char const header[], unsigned rombanks) {
return header[0x147] == 1 && header[0x149] == 0 && rombanks == 64;
}
LoadRes Cartridge::loadROM(std::string const &romfile,
LoadRes Cartridge::loadROM(File &file,
std::string const &filename,
bool const forceDmg,
bool const multicartCompat)
{
scoped_ptr<File> const rom(newFileInstance(romfile));
if (rom->fail())
if (file.fail())
return LOADRES_IO_ERROR;
enum Cartridgetype { type_plain,
@@ -556,7 +580,7 @@ LoadRes Cartridge::loadROM(std::string const &romfile,
{
unsigned char header[0x150];
rom->read(reinterpret_cast<char *>(header), sizeof header);
file.read(reinterpret_cast<char *>(header), sizeof header);
switch (header[0x0147]) {
case 0x00: type = type_plain; break;
@@ -613,7 +637,7 @@ LoadRes Cartridge::loadROM(std::string const &romfile,
cgb = header[0x0143] >> 7 & (1 ^ forceDmg);
}
std::size_t const filesize = rom->size();
std::size_t const filesize = file.size();
rombanks = std::max(pow2ceil(filesize / 0x4000), 2u);
defaultSaveBasePath_.clear();
@@ -622,17 +646,17 @@ LoadRes Cartridge::loadROM(std::string const &romfile,
memptrs_.reset(rombanks, rambanks, cgb ? 8 : 2);
rtc_.set(false, 0);
rom->rewind();
rom->read(reinterpret_cast<char*>(memptrs_.romdata()), filesize / 0x4000 * 0x4000ul);
file.rewind();
file.read(reinterpret_cast<char*>(memptrs_.romdata()), filesize / 0x4000 * 0x4000ul);
std::memset(memptrs_.romdata() + filesize / 0x4000 * 0x4000ul,
0xFF,
(rombanks - filesize / 0x4000) * 0x4000ul);
enforce8bit(memptrs_.romdata(), rombanks * 0x4000ul);
if (rom->fail())
if (file.fail())
return LOADRES_IO_ERROR;
defaultSaveBasePath_ = stripExtension(romfile);
defaultSaveBasePath_ = stripExtension(filename);
switch (type) {
case type_plain: mbc_.reset(new Mbc0(memptrs_)); break;
@@ -654,21 +678,6 @@ LoadRes Cartridge::loadROM(std::string const &romfile,
return LOADRES_OK;
}
static bool hasBattery(unsigned char headerByte0x147) {
switch (headerByte0x147) {
case 0x03:
case 0x06:
case 0x09:
case 0x0F:
case 0x10:
case 0x13:
case 0x1B:
case 0x1E:
case 0xFF: return true;
default: return false;
}
}
void Cartridge::loadSavedata() {
std::string const &sbp = saveBasePath();
@@ -713,10 +722,6 @@ void Cartridge::saveSavedata() {
}
}
static int asHex(char c) {
return c >= 'A' ? c - 'A' + 0xA : c - '0';
}
void Cartridge::applyGameGenie(std::string const &code) {
if (6 < code.length()) {
unsigned const val = (asHex(code[0]) << 4 | asHex(code[1])) & 0xFF;
@@ -730,12 +735,12 @@ void Cartridge::applyGameGenie(std::string const &code) {
cmp = ((cmp >> 2 | cmp << 6) ^ 0x45) & 0xFF;
}
for (unsigned bank = 0; bank < std::size_t(memptrs_.romdataend() - memptrs_.romdata()) / 0x4000; ++bank) {
for (unsigned bank = 0; bank < rombanks(memptrs_); ++bank) {
if (mbc_->isAddressWithinAreaRombankCanBeMappedTo(addr, bank)
&& (cmp > 0xFF || memptrs_.romdata()[bank * 0x4000ul + (addr & 0x3FFF)] == cmp)) {
ggUndoList_.push_back(AddrData(bank * 0x4000ul + (addr & 0x3FFF),
memptrs_.romdata()[bank * 0x4000ul + (addr & 0x3FFF)]));
memptrs_.romdata()[bank * 0x4000ul + (addr & 0x3FFF)] = val;
&& (cmp > 0xFF || memptrs_.romdata()[bank * rombank_size() + addr % rombank_size()] == cmp)) {
ggUndoList_.push_back(AddrData(bank * rombank_size() + addr % rombank_size(),
memptrs_.romdata()[bank * rombank_size() + addr % rombank_size()]));
memptrs_.romdata()[bank * rombank_size() + addr % rombank_size()] = val;
}
}
}
@@ -753,6 +758,8 @@ void Cartridge::setGameGenie(std::string const &codes) {
std::string code;
for (std::size_t pos = 0; pos < codes.length(); pos += code.length() + 1) {
// OpenEmu
//code = codes.substr(pos, codes.find(';', pos) - pos);
code = codes.substr(pos, codes.find('+', pos) - pos);
applyGameGenie(code);
}
@@ -769,5 +776,3 @@ PakInfo const Cartridge::pakInfo(bool const multipakCompat) const {
return PakInfo();
}
}
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef CARTRIDGE_H
@@ -24,11 +24,14 @@
#include "rtc.h"
#include "savestate.h"
#include "scoped_ptr.h"
#include <string>
#include <vector>
namespace gambatte {
class File;
class Mbc {
public:
virtual ~Mbc() {}
@@ -54,6 +57,7 @@ public:
unsigned char * wsrambankptr() const { return memptrs_.wsrambankptr(); }
unsigned char * vrambankptr() const { return memptrs_.vrambankptr(); }
OamDmaSrc oamDmaSrc() const { return memptrs_.oamDmaSrc(); }
bool isInOamDmaConflictArea(unsigned p) const { return memptrs_.isInOamDmaConflictArea(p); }
void setVrambank(unsigned bank) { memptrs_.setVrambank(bank); }
void setWrambank(unsigned bank) { memptrs_.setWrambank(bank); }
void setOamDmaSrc(OamDmaSrc oamDmaSrc) { memptrs_.setOamDmaSrc(oamDmaSrc); }
@@ -65,7 +69,7 @@ public:
void saveSavedata();
std::string const saveBasePath() const;
void setSaveDir(std::string const &dir);
LoadRes loadROM(std::string const &romfile, bool forceDmg, bool multicartCompat);
LoadRes loadROM(File &file, std::string const &filename, bool forceDmg, bool multicartCompat);
char const * romTitle() const { return reinterpret_cast<char const *>(memptrs_.romdata() + 0x134); }
class PakInfo const pakInfo(bool multicartCompat) const;
void setGameGenie(std::string const &codes);
@@ -74,7 +78,7 @@ private:
struct AddrData {
unsigned long addr;
unsigned char data;
AddrData(unsigned long addr, unsigned data) : addr(addr), data(data) {}
AddrData(unsigned long _addr, unsigned _data) : addr(_addr), data(_data) {}
};
MemPtrs memptrs_;
+176
View File
@@ -0,0 +1,176 @@
//
// Copyright (C) 2007-2010 by sinamas <sinamas at 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "memptrs.h"
#include <algorithm>
using namespace gambatte;
namespace {
template <OamDmaSrc src, bool cgb> struct OamDmaConflictMap;
template <bool cgb> struct OamDmaConflictMap<oam_dma_src_rom, cgb> { enum { r = 0xFCFF }; };
template <bool cgb> struct OamDmaConflictMap<oam_dma_src_sram, cgb> { enum { r = 0xFCFF }; };
template <bool cgb> struct OamDmaConflictMap<oam_dma_src_vram, cgb> { enum { r = 0x0300 }; };
template <bool cgb> struct OamDmaConflictMap<oam_dma_src_wram, cgb> { enum { r = cgb ? 0xF000 : 0xFCFF }; };
template <bool cgb> struct OamDmaConflictMap<oam_dma_src_invalid, cgb> { enum { r = cgb ? 0xFCFF : 0x0000 }; };
template <bool cgb>
bool isInOamDmaConflictArea(OamDmaSrc src, unsigned p)
{
static unsigned short const m[] = {
OamDmaConflictMap<oam_dma_src_rom, cgb>::r,
OamDmaConflictMap<oam_dma_src_sram, cgb>::r,
OamDmaConflictMap<oam_dma_src_vram, cgb>::r,
OamDmaConflictMap<oam_dma_src_wram, cgb>::r,
OamDmaConflictMap<oam_dma_src_invalid, cgb>::r,
0 };
return p < mm_oam_begin && (m[src] >> (p >> 12) & 1);
}
template <OamDmaSrc src, bool cgb>
void disconnectOamDmaAreas(unsigned char const *(&rmem)[0x10], unsigned char *(&wmem)[0x10])
{
if (OamDmaConflictMap<src, cgb>::r & 0x00FF)
std::fill_n(rmem, 8, static_cast<unsigned char *>(0));
if (OamDmaConflictMap<src, cgb>::r & 0x0C00)
rmem[0xB] = rmem[0xA] = wmem[0xB] = wmem[0xA] = 0;
if (OamDmaConflictMap<src, cgb>::r & 0x7000)
rmem[0xE] = rmem[0xD] = rmem[0xC] = wmem[0xE] = wmem[0xD] = wmem[0xC] = 0;
}
template <bool cgb>
void disconnectOamDmaAreas(unsigned char const *(&rmem)[0x10], unsigned char *(&wmem)[0x10],
OamDmaSrc src)
{
switch (src) {
case oam_dma_src_rom: disconnectOamDmaAreas<oam_dma_src_rom, cgb>(rmem, wmem); break;
case oam_dma_src_sram: disconnectOamDmaAreas<oam_dma_src_sram, cgb>(rmem, wmem); break;
case oam_dma_src_vram: disconnectOamDmaAreas<oam_dma_src_vram, cgb>(rmem, wmem); break;
case oam_dma_src_wram: disconnectOamDmaAreas<oam_dma_src_wram, cgb>(rmem, wmem); break;
case oam_dma_src_invalid: disconnectOamDmaAreas<oam_dma_src_invalid, cgb>(rmem, wmem); break;
case oam_dma_src_off: break;
}
}
} // unnamed namespace.
MemPtrs::MemPtrs()
: rmem_()
, wmem_()
, romdata_()
, wramdata_()
, vrambankptr_(0)
, rsrambankptr_(0)
, wsrambankptr_(0)
, rambankdata_(0)
, wramdataend_(0)
, oamDmaSrc_(oam_dma_src_off)
{
}
void MemPtrs::reset(unsigned const rombanks, unsigned const rambanks, unsigned const wrambanks) {
int const num_disabled_ram_areas = 2;
memchunk_.reset(
pre_rom_pad_size()
+ rombanks * rombank_size()
+ max_num_vrambanks * vrambank_size()
+ rambanks * rambank_size()
+ wrambanks * wrambank_size()
+ num_disabled_ram_areas * rambank_size());
romdata_[0] = romdata();
rambankdata_ = romdata_[0] + rombanks * rombank_size() + max_num_vrambanks * vrambank_size();
wramdata_[0] = rambankdata_ + rambanks * rambank_size();
wramdataend_ = wramdata_[0] + wrambanks * wrambank_size();
std::fill_n(rdisabledRamw(), rambank_size(), 0xFF);
oamDmaSrc_ = oam_dma_src_off;
rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0];
rmem_[0xC] = wmem_[0xC] = wramdata_[0] - mm_wram_begin;
rmem_[0xE] = wmem_[0xE] = wramdata_[0] - mm_wram_mirror_begin;
setRombank(1);
setRambank(0, 0);
setVrambank(0);
setWrambank(1);
}
void MemPtrs::setRombank0(unsigned bank) {
romdata_[0] = romdata() + bank * rombank_size();
rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0];
disconnectOamDmaAreas();
}
void MemPtrs::setRombank(unsigned bank) {
romdata_[1] = romdata() + bank * rombank_size() - mm_rom1_begin;
rmem_[0x7] = rmem_[0x6] = rmem_[0x5] = rmem_[0x4] = romdata_[1];
disconnectOamDmaAreas();
}
void MemPtrs::setRambank(unsigned const flags, unsigned const rambank) {
unsigned char *srambankptr = 0;
if (!(flags & rtc_en)) {
srambankptr = rambankdata() != rambankdataend()
? rambankdata_ + rambank * rambank_size() - mm_sram_begin
: wdisabledRam() - mm_sram_begin;
}
rsrambankptr_ = (flags & read_en) && srambankptr != wdisabledRam() - mm_sram_begin
? srambankptr
: rdisabledRamw() - mm_sram_begin;
wsrambankptr_ = flags & write_en
? srambankptr
: wdisabledRam() - mm_sram_begin;
rmem_[0xB] = rmem_[0xA] = rsrambankptr_;
wmem_[0xB] = wmem_[0xA] = wsrambankptr_;
disconnectOamDmaAreas();
}
void MemPtrs::setWrambank(unsigned bank) {
wramdata_[1] = wramdata_[0] + (bank & 0x07 ? bank & 0x07 : 1) * wrambank_size();
rmem_[0xD] = wmem_[0xD] = wramdata_[1] - mm_wram1_begin;
disconnectOamDmaAreas();
}
void MemPtrs::setOamDmaSrc(OamDmaSrc oamDmaSrc) {
rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0];
rmem_[0x7] = rmem_[0x6] = rmem_[0x5] = rmem_[0x4] = romdata_[1];
rmem_[0xB] = rmem_[0xA] = rsrambankptr_;
wmem_[0xB] = wmem_[0xA] = wsrambankptr_;
rmem_[0xC] = wmem_[0xC] = wramdata_[0] - mm_wram_begin;
rmem_[0xD] = wmem_[0xD] = wramdata_[1] - mm_wram1_begin;
rmem_[0xE] = wmem_[0xE] = wramdata_[0] - mm_wram_mirror_begin;
oamDmaSrc_ = oamDmaSrc;
disconnectOamDmaAreas();
}
void MemPtrs::disconnectOamDmaAreas() {
return isCgb(*this)
? ::disconnectOamDmaAreas<true>(rmem_, wmem_, oamDmaSrc_)
: ::disconnectOamDmaAreas<false>(rmem_, wmem_, oamDmaSrc_);
}
bool MemPtrs::isInOamDmaConflictArea(unsigned p) const
{
return isCgb(*this)
? ::isInOamDmaConflictArea<true>(oamDmaSrc_, p)
: ::isInOamDmaConflictArea<false>(oamDmaSrc_, p);
}
@@ -13,50 +13,71 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef MEMPTRS_H
#define MEMPTRS_H
#include "array.h"
namespace gambatte {
enum OamDmaSrc { oam_dma_src_rom,
oam_dma_src_sram,
oam_dma_src_vram,
oam_dma_src_wram,
oam_dma_src_invalid,
oam_dma_src_off, };
enum OamDmaSrc {
oam_dma_src_rom,
oam_dma_src_sram,
oam_dma_src_vram,
oam_dma_src_wram,
oam_dma_src_invalid,
oam_dma_src_off };
enum {
mm_rom_begin = 0x0000,
mm_rom1_begin = 0x4000,
mm_vram_begin = 0x8000,
mm_sram_begin = 0xA000,
mm_wram_begin = 0xC000,
mm_wram1_begin = 0xD000,
mm_wram_mirror_begin = 0xE000,
mm_oam_begin = 0xFE00,
mm_io_begin = 0xFF00,
mm_hram_begin = 0xFF80 };
enum { max_num_vrambanks = 2 };
inline std::size_t rambank_size() { return 0x2000; }
inline std::size_t rombank_size() { return 0x4000; }
inline std::size_t vrambank_size() { return 0x2000; }
inline std::size_t wrambank_size() { return 0x1000; }
class MemPtrs {
public:
enum RamFlag { read_en = 1, write_en = 2, rtc_en = 4 };
MemPtrs();
~MemPtrs();
void reset(unsigned rombanks, unsigned rambanks, unsigned wrambanks);
unsigned char const * rmem(unsigned area) const { return rmem_[area]; }
unsigned char * wmem(unsigned area) const { return wmem_[area]; }
unsigned char * vramdata() const { return rambankdata_ - 0x4000; }
unsigned char * vramdataend() const { return rambankdata_; }
unsigned char * romdata() const { return memchunk_ + 0x4000; }
unsigned char * romdata() const { return memchunk_ + pre_rom_pad_size(); }
unsigned char * romdata(unsigned area) const { return romdata_[area]; }
unsigned char * romdataend() const { return rambankdata_ - 0x4000; }
unsigned char * wramdata(unsigned area) const { return wramdata_[area]; }
unsigned char * wramdataend() const { return wramdataend_; }
unsigned char * romdataend() const { return rambankdata_ - max_num_vrambanks * vrambank_size(); }
unsigned char * vramdata() const { return romdataend(); }
unsigned char * vramdataend() const { return rambankdata_; }
unsigned char * rambankdata() const { return rambankdata_; }
unsigned char * rambankdataend() const { return wramdata_[0]; }
unsigned char * wramdata(unsigned area) const { return wramdata_[area]; }
unsigned char * wramdataend() const { return wramdataend_; }
unsigned char const * rdisabledRam() const { return rdisabledRamw(); }
unsigned char const * rsrambankptr() const { return rsrambankptr_; }
unsigned char * wsrambankptr() const { return wsrambankptr_; }
unsigned char * vrambankptr() const { return vrambankptr_; }
OamDmaSrc oamDmaSrc() const { return oamDmaSrc_; }
bool isInOamDmaConflictArea(unsigned p) const;
void setRombank0(unsigned bank);
void setRombank(unsigned bank);
void setRambank(unsigned ramFlags, unsigned rambank);
void setVrambank(unsigned bank) { vrambankptr_ = vramdata() + bank * 0x2000ul - 0x8000; }
void setVrambank(unsigned bank) { vrambankptr_ = vramdata() + bank * vrambank_size() - mm_vram_begin; }
void setWrambank(unsigned bank);
void setOamDmaSrc(OamDmaSrc oamDmaSrc);
@@ -68,20 +89,21 @@ private:
unsigned char *vrambankptr_;
unsigned char *rsrambankptr_;
unsigned char *wsrambankptr_;
unsigned char *memchunk_;
SimpleArray<unsigned char> memchunk_;
unsigned char *rambankdata_;
unsigned char *wramdataend_;
OamDmaSrc oamDmaSrc_;
MemPtrs(MemPtrs const &);
MemPtrs & operator=(MemPtrs const &);
static std::size_t pre_rom_pad_size() { return mm_rom1_begin; }
void disconnectOamDmaAreas();
unsigned char * rdisabledRamw() const { return wramdataend_ ; }
unsigned char * wdisabledRam() const { return wramdataend_ + 0x2000; }
unsigned char * rdisabledRamw() const { return wramdataend_; }
unsigned char * wdisabledRam() const { return wramdataend_ + rambank_size(); }
};
inline bool isCgb(MemPtrs const &memptrs) {
return memptrs.wramdataend() - memptrs.wramdata(0) == 0x8000;
int const num_cgb_wrambanks = 8;
std::size_t const wramsize = memptrs.wramdataend() - memptrs.wramdata(0);
return wramsize == num_cgb_wrambanks * wrambank_size();
}
}
@@ -1,9 +1,10 @@
#include "pakinfo_internal.h"
#include <cstring>
namespace gambatte {
enum { flag_multipak = 1, flag_header_checksum_ok = 2, };
enum { flag_multipak = 1, flag_header_checksum_ok = 2 };
static bool isHeaderChecksumOk(unsigned const char header[]) {
unsigned csum = 0;
@@ -20,9 +21,10 @@ unsigned numRambanksFromH14x(unsigned char h147, unsigned char h149) {
case 0x00: return isMbc2(h147) ? 1 : 0;
case 0x01:
case 0x02: return 1;
default: case 0x03: return 4;
case 0x04: return 16;
case 0x05: return 8;
}
return 4;
}
PakInfo::PakInfo()
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "rtc.h"
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef RTC_H
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "memory.h"
@@ -21,23 +21,40 @@
#include "savestate.h"
#include "sound.h"
#include "video.h"
#include <cstring>
namespace gambatte {
#include <algorithm>
using namespace gambatte;
namespace {
int const oam_size = 4 * lcd_num_oam_entries;
void decCycles(unsigned long &counter, unsigned long dec) {
if (counter != disabled_time)
counter -= dec;
}
int serialCntFrom(unsigned long cyclesUntilDone, bool cgbFast) {
return cgbFast ? (cyclesUntilDone + 0xF) >> 4 : (cyclesUntilDone + 0x1FF) >> 9;
}
} // unnamed namespace.
Memory::Memory(Interrupter const &interrupter)
: getInput_(0)
, divLastUpdate_(0)
, lastOamDmaUpdate_(disabled_time)
, lcd_(ioamhram_, 0, VideoInterruptRequester(intreq_))
, interrupter_(interrupter)
, dmaSource_(0)
, dmaDestination_(0)
, oamDmaPos_(0xFE)
, oamDmaPos_(-2u & 0xFF)
, oamDmaStartPos_(0)
, serialCnt_(0)
, blanklcd_(false)
, haltHdmaState_(hdma_low)
{
intreq_.setEventTime<intevent_blit>(144 * 456ul);
intreq_.setEventTime<intevent_blit>(1l * lcd_vres * lcd_cycles_per_line);
intreq_.setEventTime<intevent_end>(0);
}
@@ -51,17 +68,20 @@ void Memory::setStatePtrs(SaveState &state) {
unsigned long Memory::saveState(SaveState &state, unsigned long cc) {
cc = resetCounters(cc);
ioamhram_[0x104] = 0;
nontrivial_ff_read(0x05, cc);
nontrivial_ff_read(0x0F, cc);
nontrivial_ff_read(0x26, cc);
state.mem.divLastUpdate = divLastUpdate_;
state.mem.nextSerialtime = intreq_.eventTime(intevent_serial);
state.mem.unhaltTime = intreq_.eventTime(intevent_unhalt);
state.mem.lastOamDmaUpdate = lastOamDmaUpdate_;
state.mem.lastOamDmaUpdate = oamDmaStartPos_
? lastOamDmaUpdate_ + ((oamDmaStartPos_ - oamDmaPos_) & 0xFF) * 4
: lastOamDmaUpdate_;
state.mem.dmaSource = dmaSource_;
state.mem.dmaDestination = dmaDestination_;
state.mem.oamDmaPos = oamDmaPos_;
state.mem.haltHdmaState = haltHdmaState_;
intreq_.saveState(state);
cart_.saveState(state);
@@ -72,18 +92,13 @@ unsigned long Memory::saveState(SaveState &state, unsigned long cc) {
return cc;
}
static int serialCntFrom(unsigned long cyclesUntilDone, bool cgbFast) {
return cgbFast ? (cyclesUntilDone + 0xF) >> 4 : (cyclesUntilDone + 0x1FF) >> 9;
}
void Memory::loadState(SaveState const &state) {
psg_.loadState(state);
lcd_.loadState(state, state.mem.oamDmaPos < 0xA0 ? cart_.rdisabledRam() : ioamhram_);
lcd_.loadState(state, state.mem.oamDmaPos < oam_size ? cart_.rdisabledRam() : ioamhram_);
tima_.loadState(state, TimaInterruptRequester(intreq_));
cart_.loadState(state);
intreq_.loadState(state);
divLastUpdate_ = state.mem.divLastUpdate;
intreq_.setEventTime<intevent_serial>(state.mem.nextSerialtime > state.cpu.cycleCounter
? state.mem.nextSerialtime
: state.cpu.cycleCounter);
@@ -92,36 +107,42 @@ void Memory::loadState(SaveState const &state) {
dmaSource_ = state.mem.dmaSource;
dmaDestination_ = state.mem.dmaDestination;
oamDmaPos_ = state.mem.oamDmaPos;
oamDmaStartPos_ = 0;
haltHdmaState_ = static_cast<HdmaState>(std::min(1u * state.mem.haltHdmaState, 1u * hdma_requested));
serialCnt_ = intreq_.eventTime(intevent_serial) != disabled_time
? serialCntFrom(intreq_.eventTime(intevent_serial) - state.cpu.cycleCounter,
ioamhram_[0x102] & isCgb() * 2)
: 8;
? serialCntFrom(intreq_.eventTime(intevent_serial) - state.cpu.cycleCounter,
ioamhram_[0x102] & isCgb() * 2)
: 8;
cart_.setVrambank(ioamhram_[0x14F] & isCgb());
cart_.setOamDmaSrc(oam_dma_src_off);
cart_.setWrambank(isCgb() && (ioamhram_[0x170] & 0x07) ? ioamhram_[0x170] & 0x07 : 1);
if (lastOamDmaUpdate_ != disabled_time) {
if (lastOamDmaUpdate_ > state.cpu.cycleCounter) {
oamDmaStartPos_ = (oamDmaPos_ + (lastOamDmaUpdate_ - state.cpu.cycleCounter) / 4) & 0xFF;
lastOamDmaUpdate_ = state.cpu.cycleCounter;
}
oamDmaInitSetup();
unsigned oamEventPos = oamDmaPos_ < 0xA0 ? 0xA0 : 0x100;
unsigned oamEventPos = oamDmaPos_ < oam_size ? oam_size : oamDmaStartPos_;
intreq_.setEventTime<intevent_oam>(
lastOamDmaUpdate_ + (oamEventPos - oamDmaPos_) * 4);
lastOamDmaUpdate_ + ((oamEventPos - oamDmaPos_) & 0xFF) * 4);
}
intreq_.setEventTime<intevent_blit>(ioamhram_[0x140] & lcdc_en
? lcd_.nextMode1IrqTime()
: state.cpu.cycleCounter);
? lcd_.nextMode1IrqTime()
: state.cpu.cycleCounter);
blanklcd_ = false;
if (!isCgb())
std::memset(cart_.vramdata() + 0x2000, 0, 0x2000);
std::fill_n(cart_.vramdata() + vrambank_size(), vrambank_size(), 0);
}
void Memory::setEndtime(unsigned long cc, unsigned long inc) {
if (intreq_.eventTime(intevent_blit) <= cc) {
intreq_.setEventTime<intevent_blit>(intreq_.eventTime(intevent_blit)
+ (70224 << isDoubleSpeed()));
+ (lcd_cycles_per_frame << isDoubleSpeed()));
}
intreq_.setEventTime<intevent_end>(cc + (inc << isDoubleSpeed()));
@@ -132,11 +153,11 @@ void Memory::updateSerial(unsigned long const cc) {
if (intreq_.eventTime(intevent_serial) <= cc) {
ioamhram_[0x101] = (((ioamhram_[0x101] + 1) << serialCnt_) - 1) & 0xFF;
ioamhram_[0x102] &= 0x7F;
intreq_.flagIrq(8, intreq_.eventTime(intevent_serial));
intreq_.setEventTime<intevent_serial>(disabled_time);
intreq_.flagIrq(8);
} else {
int const targetCnt = serialCntFrom(intreq_.eventTime(intevent_serial) - cc,
ioamhram_[0x102] & isCgb() * 2);
ioamhram_[0x102] & isCgb() * 2);
ioamhram_[0x101] = (((ioamhram_[0x101] + 1) << (serialCnt_ - targetCnt)) - 1) & 0xFF;
serialCnt_ = targetCnt;
}
@@ -160,6 +181,11 @@ unsigned long Memory::event(unsigned long cc) {
switch (intreq_.minEventId()) {
case intevent_unhalt:
if ((lcd_.hdmaIsEnabled() && lcd_.isHdmaPeriod(cc) && haltHdmaState_ == hdma_low)
|| haltHdmaState_ == hdma_requested) {
flagHdmaReq(intreq_);
}
intreq_.unhalt();
intreq_.setEventTime<intevent_unhalt>(disabled_time);
break;
@@ -187,7 +213,7 @@ unsigned long Memory::event(unsigned long cc) {
while (cc >= intreq_.minEventTime())
cc = event(cc);
} else
blitTime += 70224 << isDoubleSpeed();
blitTime += lcd_cycles_per_frame << isDoubleSpeed();
blanklcd_ = lcden ^ 1;
intreq_.setEventTime<intevent_blit>(blitTime);
@@ -197,77 +223,22 @@ unsigned long Memory::event(unsigned long cc) {
updateSerial(cc);
break;
case intevent_oam:
intreq_.setEventTime<intevent_oam>(lastOamDmaUpdate_ == disabled_time
? static_cast<unsigned long>(disabled_time)
: intreq_.eventTime(intevent_oam) + 0xA0 * 4);
if (lastOamDmaUpdate_ != disabled_time) {
unsigned const oamEventPos = oamDmaPos_ < oam_size ? oam_size : oamDmaStartPos_;
intreq_.setEventTime<intevent_oam>(
lastOamDmaUpdate_ + ((oamEventPos - oamDmaPos_) & 0xFF) * 4);
} else
intreq_.setEventTime<intevent_oam>(disabled_time);
break;
case intevent_dma:
{
bool const doubleSpeed = isDoubleSpeed();
unsigned dmaSrc = dmaSource_;
unsigned dmaDest = dmaDestination_;
unsigned dmaLength = ((ioamhram_[0x155] & 0x7F) + 0x1) * 0x10;
unsigned length = hdmaReqFlagged(intreq_) ? 0x10 : dmaLength;
ackDmaReq(intreq_);
if ((static_cast<unsigned long>(dmaDest) + length) & 0x10000) {
length = 0x10000 - dmaDest;
ioamhram_[0x155] |= 0x80;
}
dmaLength -= length;
if (!(ioamhram_[0x140] & lcdc_en))
dmaLength = 0;
{
unsigned long lOamDmaUpdate = lastOamDmaUpdate_;
lastOamDmaUpdate_ = disabled_time;
while (length--) {
unsigned const src = dmaSrc++ & 0xFFFF;
unsigned const data = (src & 0xE000) == 0x8000 || src > 0xFDFF
? 0xFF
: read(src, cc);
cc += 2 << doubleSpeed;
if (cc - 3 > lOamDmaUpdate) {
oamDmaPos_ = (oamDmaPos_ + 1) & 0xFF;
lOamDmaUpdate += 4;
if (oamDmaPos_ < 0xA0) {
if (oamDmaPos_ == 0)
startOamDma(lOamDmaUpdate - 1);
ioamhram_[src & 0xFF] = data;
} else if (oamDmaPos_ == 0xA0) {
endOamDma(lOamDmaUpdate - 1);
lOamDmaUpdate = disabled_time;
}
}
nontrivial_write(0x8000 | (dmaDest++ & 0x1FFF), data, cc);
}
lastOamDmaUpdate_ = lOamDmaUpdate;
}
cc += 4;
dmaSource_ = dmaSrc;
dmaDestination_ = dmaDest;
ioamhram_[0x155] = ((dmaLength / 0x10 - 0x1) & 0xFF) | (ioamhram_[0x155] & 0x80);
if ((ioamhram_[0x155] & 0x80) && lcd_.hdmaIsEnabled()) {
if (lastOamDmaUpdate_ != disabled_time)
updateOamDma(cc);
lcd_.disableHdma(cc);
}
interrupter_.prefetch(cc, *this);
cc = dma(cc);
if (haltHdmaState_ == hdma_requested) {
haltHdmaState_ = hdma_low;
intreq_.setMinIntTime(cc);
cc -= 4;
}
break;
case intevent_tima:
tima_.doIrqEvent(TimaInterruptRequester(intreq_));
@@ -277,25 +248,25 @@ unsigned long Memory::event(unsigned long cc) {
break;
case intevent_interrupts:
if (halted()) {
if (isCgb())
cc += 4;
cc += 4 * (isCgb() || cc - intreq_.eventTime(intevent_interrupts) < 2);
if (cc > lastOamDmaUpdate_)
updateOamDma(cc);
if ((lcd_.hdmaIsEnabled() && lcd_.isHdmaPeriod(cc) && haltHdmaState_ == hdma_low)
|| haltHdmaState_ == hdma_requested) {
flagHdmaReq(intreq_);
}
intreq_.unhalt();
intreq_.setEventTime<intevent_unhalt>(disabled_time);
}
if (cc >= intreq_.eventTime(intevent_video))
lcd_.update(cc);
if (cc >= intreq_.eventTime(intevent_dma))
break;
if (ime()) {
unsigned const pendingIrqs = intreq_.pendingIrqs();
unsigned const n = pendingIrqs & -pendingIrqs;
unsigned address;
if (n <= 4) {
static unsigned char const lut[] = { 0x40, 0x48, 0x48, 0x50 };
address = lut[n-1];
} else
address = 0x50 + n;
intreq_.ackIrq(n);
cc = interrupter_.interrupt(address, cc, *this);
di();
cc = interrupter_.interrupt(cc, *this);
}
break;
@@ -304,33 +275,169 @@ unsigned long Memory::event(unsigned long cc) {
return cc;
}
unsigned long Memory::stop(unsigned long cc) {
cc += 4 + 4 * isDoubleSpeed();
unsigned long Memory::dma(unsigned long cc) {
bool const doubleSpeed = isDoubleSpeed();
unsigned dmaSrc = dmaSource_;
unsigned dmaDest = dmaDestination_;
unsigned dmaLength = ((ioamhram_[0x155] & 0x7F) + 1) * 0x10;
unsigned length = hdmaReqFlagged(intreq_) ? 0x10 : dmaLength;
if (ioamhram_[0x14D] & isCgb()) {
psg_.generateSamples(cc, isDoubleSpeed());
lcd_.speedChange(cc);
ioamhram_[0x14D] ^= 0x81;
intreq_.setEventTime<intevent_blit>(ioamhram_[0x140] & lcdc_en
? lcd_.nextMode1IrqTime()
: cc + (70224 << isDoubleSpeed()));
if (intreq_.eventTime(intevent_end) > cc) {
intreq_.setEventTime<intevent_end>(cc
+ ( isDoubleSpeed()
? (intreq_.eventTime(intevent_end) - cc) << 1
: (intreq_.eventTime(intevent_end) - cc) >> 1));
}
if (1ul * dmaDest + length >= 0x10000) {
length = 0x10000 - dmaDest;
ioamhram_[0x155] |= 0x80;
}
dmaLength -= length;
if (!(ioamhram_[0x140] & lcdc_en))
dmaLength = 0;
unsigned long lOamDmaUpdate = lastOamDmaUpdate_;
lastOamDmaUpdate_ = disabled_time;
while (length--) {
unsigned const src = dmaSrc++ & 0xFFFF;
unsigned const data = (src & -vrambank_size()) == mm_vram_begin || src >= mm_oam_begin
? 0xFF
: read(src, cc);
cc += 2 + 2 * doubleSpeed;
if (cc - 3 > lOamDmaUpdate && !halted()) {
oamDmaPos_ = (oamDmaPos_ + 1) & 0xFF;
lOamDmaUpdate += 4;
if (oamDmaPos_ == oamDmaStartPos_)
startOamDma(lOamDmaUpdate);
if (oamDmaPos_ < oam_size) {
ioamhram_[src & 0xFF] = data;
} else if (oamDmaPos_ == oam_size) {
endOamDma(lOamDmaUpdate);
if (oamDmaStartPos_ == 0)
lOamDmaUpdate = disabled_time;
}
}
nontrivial_write(mm_vram_begin | dmaDest++ % vrambank_size(), data, cc);
}
lastOamDmaUpdate_ = lOamDmaUpdate;
ackDmaReq(intreq_);
cc += 4;
dmaSource_ = dmaSrc;
dmaDestination_ = dmaDest;
ioamhram_[0x155] = halted()
? ioamhram_[0x155] | 0x80
: ((dmaLength / 0x10 - 1) & 0xFF) | (ioamhram_[0x155] & 0x80);
if ((ioamhram_[0x155] & 0x80) && lcd_.hdmaIsEnabled()) {
if (lastOamDmaUpdate_ != disabled_time)
updateOamDma(cc);
lcd_.disableHdma(cc);
}
intreq_.halt();
intreq_.setEventTime<intevent_unhalt>(cc + 0x20000 + isDoubleSpeed() * 8);
return cc;
}
static void decCycles(unsigned long &counter, unsigned long dec) {
if (counter != disabled_time)
counter -= dec;
void Memory::freeze(unsigned long cc) {
// permanently halt CPU.
// simply halt and clear IE to avoid unhalt from occuring,
// which avoids additional state to represent a "frozen" state.
nontrivial_ff_write(0xFF, 0, cc);
ackDmaReq(intreq_);
intreq_.halt();
}
bool Memory::halt(unsigned long cc) {
if (lastOamDmaUpdate_ != disabled_time)
updateOamDma(cc);
haltHdmaState_ = lcd_.hdmaIsEnabled() && lcd_.isHdmaPeriod(cc)
? hdma_high : hdma_low;
bool const hdmaReq = hdmaReqFlagged(intreq_);
if (hdmaReq)
haltHdmaState_ = hdma_requested;
if (lastOamDmaUpdate_ != disabled_time)
updateOamDma(cc + 4);
ackDmaReq(intreq_);
intreq_.halt();
return hdmaReq;
}
unsigned Memory::pendingIrqs(unsigned long cc) {
if (lastOamDmaUpdate_ != disabled_time)
updateOamDma(cc);
updateIrqs(cc);
return intreq_.pendingIrqs();
}
void Memory::ackIrq(unsigned bit, unsigned long cc) {
if (lastOamDmaUpdate_ != disabled_time)
updateOamDma(cc);
// TODO: adjust/extend IRQ assertion time rather than use the odd cc offsets?
// NOTE: a minimum offset of 2 is required for the LCD due to implementation assumptions w.r.t. cc headroom.
updateSerial(cc + 3 + isCgb());
updateTimaIrq(cc + 2 + isCgb());
lcd_.update(cc + 2);
intreq_.ackIrq(bit);
}
unsigned long Memory::stop(unsigned long cc, bool &skip) {
// FIXME: this is incomplete.
intreq_.setEventTime<intevent_unhalt>(cc + 0x20000 + 4);
// speed change.
if (ioamhram_[0x14D] & isCgb()) {
tima_.speedChange(TimaInterruptRequester(intreq_));
// DIV reset.
nontrivial_ff_write(0x04, 0, cc);
haltHdmaState_ = lcd_.hdmaIsEnabled() && lcd_.isHdmaPeriod(cc)
? hdma_high : hdma_low;
skip = hdmaReqFlagged(intreq_);
if (skip && isDoubleSpeed())
haltHdmaState_ = hdma_requested;
unsigned long const cc_ = cc + 8 * !isDoubleSpeed();
if (cc_ >= cc + 4) {
if (lastOamDmaUpdate_ != disabled_time)
updateOamDma(cc + 4);
if (!skip || isDoubleSpeed())
ackDmaReq(intreq_);
intreq_.halt();
}
psg_.speedChange(cc_, isDoubleSpeed());
lcd_.speedChange(cc_);
ioamhram_[0x14D] ^= 0x81;
// TODO: perhaps make this a bit nicer?
intreq_.setEventTime<intevent_blit>(ioamhram_[0x140] & lcdc_en
? lcd_.nextMode1IrqTime()
: cc + (lcd_cycles_per_frame << isDoubleSpeed()));
if (intreq_.eventTime(intevent_end) > cc_) {
intreq_.setEventTime<intevent_end>(cc_
+ (isDoubleSpeed()
? (intreq_.eventTime(intevent_end) - cc_) * 2
: (intreq_.eventTime(intevent_end) - cc_) / 2));
}
if (cc_ < cc + 4) {
if (lastOamDmaUpdate_ != disabled_time)
updateOamDma(cc + 4);
if (!skip || !isDoubleSpeed())
ackDmaReq(intreq_);
intreq_.halt();
}
// ensure that no updates with a previous cc occur.
cc += 8;
} else {
// FIXME: test and implement stop correctly.
skip = halt(cc);
cc += 4;
}
return cc;
}
void Memory::decEventCycles(IntEventId eventId, unsigned long dec) {
@@ -344,16 +451,9 @@ unsigned long Memory::resetCounters(unsigned long cc) {
updateIrqs(cc);
{
unsigned long divinc = (cc - divLastUpdate_) >> 8;
ioamhram_[0x104] = (ioamhram_[0x104] + divinc) & 0xFF;
divLastUpdate_ += divinc << 8;
}
unsigned long const dec = cc < 0x10000
? 0
: (cc & ~0x7FFFul) - 0x8000;
decCycles(divLastUpdate_, dec);
unsigned long const dec = cc < 0x20000
? 0
: (cc & -0x10000l) - 0x10000;
decCycles(lastOamDmaUpdate_, dec);
decEventCycles(intevent_serial, dec);
decEventCycles(intevent_oam, dec);
@@ -393,62 +493,64 @@ void Memory::updateOamDma(unsigned long const cc) {
unsigned char const *const oamDmaSrc = oamDmaSrcPtr();
unsigned cycles = (cc - lastOamDmaUpdate_) >> 2;
while (cycles--) {
if (halted()) {
lastOamDmaUpdate_ += 4 * cycles;
} else while (cycles--) {
oamDmaPos_ = (oamDmaPos_ + 1) & 0xFF;
lastOamDmaUpdate_ += 4;
if (oamDmaPos_ == oamDmaStartPos_)
startOamDma(lastOamDmaUpdate_);
if (oamDmaPos_ < 0xA0) {
if (oamDmaPos_ == 0)
startOamDma(lastOamDmaUpdate_ - 1);
if (oamDmaPos_ < oam_size) {
ioamhram_[oamDmaPos_] = oamDmaSrc ? oamDmaSrc[oamDmaPos_] : cart_.rtcRead();
} else if (oamDmaPos_ == 0xA0) {
endOamDma(lastOamDmaUpdate_ - 1);
lastOamDmaUpdate_ = disabled_time;
break;
} else if (oamDmaPos_ == oam_size) {
endOamDma(lastOamDmaUpdate_);
if (oamDmaStartPos_ == 0) {
lastOamDmaUpdate_ = disabled_time;
break;
}
}
}
}
void Memory::oamDmaInitSetup() {
if (ioamhram_[0x146] < 0xA0) {
cart_.setOamDmaSrc(ioamhram_[0x146] < 0x80 ? oam_dma_src_rom : oam_dma_src_vram);
} else if (ioamhram_[0x146] < 0xFE - isCgb() * 0x1E) {
cart_.setOamDmaSrc(ioamhram_[0x146] < 0xC0 ? oam_dma_src_sram : oam_dma_src_wram);
if (ioamhram_[0x146] < mm_sram_begin / 0x100) {
cart_.setOamDmaSrc(ioamhram_[0x146] < mm_vram_begin / 0x100 ? oam_dma_src_rom : oam_dma_src_vram);
} else if (ioamhram_[0x146] < 0x100 - isCgb() * 0x20) {
cart_.setOamDmaSrc(ioamhram_[0x146] < mm_wram_begin / 0x100 ? oam_dma_src_sram : oam_dma_src_wram);
} else
cart_.setOamDmaSrc(oam_dma_src_invalid);
}
static unsigned char const * oamDmaSrcZero() {
static unsigned char zeroMem[0xA0];
return zeroMem;
}
unsigned char const * Memory::oamDmaSrcPtr() const {
switch (cart_.oamDmaSrc()) {
case oam_dma_src_rom:
return cart_.romdata(ioamhram_[0x146] >> 6) + (ioamhram_[0x146] << 8);
return cart_.romdata(ioamhram_[0x146] >> 6) + ioamhram_[0x146] * 0x100l;
case oam_dma_src_sram:
return cart_.rsrambankptr() ? cart_.rsrambankptr() + (ioamhram_[0x146] << 8) : 0;
return cart_.rsrambankptr() ? cart_.rsrambankptr() + ioamhram_[0x146] * 0x100l : 0;
case oam_dma_src_vram:
return cart_.vrambankptr() + (ioamhram_[0x146] << 8);
return cart_.vrambankptr() + ioamhram_[0x146] * 0x100l;
case oam_dma_src_wram:
return cart_.wramdata(ioamhram_[0x146] >> 4 & 1) + (ioamhram_[0x146] << 8 & 0xFFF);
return cart_.wramdata(ioamhram_[0x146] >> 4 & 1) + (ioamhram_[0x146] * 0x100l & 0xFFF);
case oam_dma_src_invalid:
case oam_dma_src_off:
break;
}
return ioamhram_[0x146] == 0xFF && !isCgb() ? oamDmaSrcZero() : cart_.rdisabledRam();
return cart_.rdisabledRam();
}
void Memory::startOamDma(unsigned long cc) {
oamDmaPos_ = 0;
oamDmaStartPos_ = 0;
lcd_.oamChange(cart_.rdisabledRam(), cc);
}
void Memory::endOamDma(unsigned long cc) {
oamDmaPos_ = 0xFE;
cart_.setOamDmaSrc(oam_dma_src_off);
if (oamDmaStartPos_ == 0) {
oamDmaPos_ = -2u & 0xFF;
cart_.setOamDmaSrc(oam_dma_src_off);
}
lcd_.oamChange(ioamhram_, cc);
}
@@ -465,13 +567,7 @@ unsigned Memory::nontrivial_ff_read(unsigned const p, unsigned long const cc) {
updateSerial(cc);
break;
case 0x04:
{
unsigned long divcycles = (cc - divLastUpdate_) >> 8;
ioamhram_[0x104] = (ioamhram_[0x104] + divcycles) & 0xFF;
divLastUpdate_ += divcycles << 8;
}
break;
return (cc - tima_.divLastUpdate()) >> 8 & 0xFF;
case 0x05:
ioamhram_[0x105] = tima_.tima(cc);
break;
@@ -520,47 +616,28 @@ unsigned Memory::nontrivial_ff_read(unsigned const p, unsigned long const cc) {
return ioamhram_[p + 0x100];
}
static bool isInOamDmaConflictArea(OamDmaSrc const oamDmaSrc, unsigned const p, bool const cgb) {
struct Area { unsigned short areaUpper, exceptAreaLower, exceptAreaWidth, pad; };
static Area const cgbAreas[] = {
{ 0xC000, 0x8000, 0x2000, 0 },
{ 0xC000, 0x8000, 0x2000, 0 },
{ 0xA000, 0x0000, 0x8000, 0 },
{ 0xFE00, 0x0000, 0xC000, 0 },
{ 0xC000, 0x8000, 0x2000, 0 },
{ 0x0000, 0x0000, 0x0000, 0 }
};
static Area const dmgAreas[] = {
{ 0xFE00, 0x8000, 0x2000, 0 },
{ 0xFE00, 0x8000, 0x2000, 0 },
{ 0xA000, 0x0000, 0x8000, 0 },
{ 0xFE00, 0x8000, 0x2000, 0 },
{ 0xFE00, 0x8000, 0x2000, 0 },
{ 0x0000, 0x0000, 0x0000, 0 }
};
Area const *a = cgb ? cgbAreas : dmgAreas;
return p < a[oamDmaSrc].areaUpper
&& p - a[oamDmaSrc].exceptAreaLower >= a[oamDmaSrc].exceptAreaWidth;
}
unsigned Memory::nontrivial_read(unsigned const p, unsigned long const cc) {
if (p < 0xFF80) {
if (p < mm_hram_begin) {
if (lastOamDmaUpdate_ != disabled_time) {
updateOamDma(cc);
if (isInOamDmaConflictArea(cart_.oamDmaSrc(), p, isCgb()) && oamDmaPos_ < 0xA0)
return ioamhram_[oamDmaPos_];
if (cart_.isInOamDmaConflictArea(p) && oamDmaPos_ < oam_size) {
int const r = isCgb() && cart_.oamDmaSrc() != oam_dma_src_wram && p >= mm_wram_begin
? cart_.wramdata(ioamhram_[0x146] >> 4 & 1)[p & 0xFFF]
: ioamhram_[oamDmaPos_];
if (isCgb() && cart_.oamDmaSrc() == oam_dma_src_vram)
ioamhram_[oamDmaPos_] = 0;
return r;
}
}
if (p < 0xC000) {
if (p < 0x8000)
if (p < mm_wram_begin) {
if (p < mm_vram_begin)
return cart_.romdata(p >> 14)[p];
if (p < 0xA000) {
if (!lcd_.vramAccessible(cc))
if (p < mm_sram_begin) {
if (!lcd_.vramReadable(cc))
return 0xFF;
return cart_.vrambankptr()[p];
@@ -572,18 +649,18 @@ unsigned Memory::nontrivial_read(unsigned const p, unsigned long const cc) {
return cart_.rtcRead();
}
if (p < 0xFE00)
if (p < mm_oam_begin)
return cart_.wramdata(p >> 12 & 1)[p & 0xFFF];
long const ffp = long(p) - 0xFF00;
long const ffp = static_cast<long>(p) - mm_io_begin;
if (ffp >= 0)
return nontrivial_ff_read(ffp, cc);
if (!lcd_.oamReadable(cc) || oamDmaPos_ < 0xA0)
if (!lcd_.oamReadable(cc) || oamDmaPos_ < oam_size)
return 0xFF;
}
return ioamhram_[p - 0xFE00];
return ioamhram_[p - mm_oam_begin];
}
void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long const cc) {
@@ -604,19 +681,27 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
case 0x02:
updateSerial(cc);
serialCnt_ = 8;
if ((data & 0x81) == 0x81) {
intreq_.setEventTime<intevent_serial>(data & isCgb() * 2
? (cc & ~0x07ul) + 0x010 * 8
: (cc & ~0xFFul) + 0x200 * 8);
? cc - (cc - tima_.divLastUpdate()) % 8 + 0x10 * serialCnt_
: cc - (cc - tima_.divLastUpdate()) % 0x100 + 0x200 * serialCnt_);
} else
intreq_.setEventTime<intevent_serial>(disabled_time);
data |= 0x7E - isCgb() * 2;
break;
case 0x04:
ioamhram_[0x104] = 0;
divLastUpdate_ = cc;
if (intreq_.eventTime(intevent_serial) != disabled_time
&& intreq_.eventTime(intevent_serial) > cc) {
unsigned long const t = intreq_.eventTime(intevent_serial);
unsigned long const n = ioamhram_[0x102] & isCgb() * 2
? t + (cc - t) % 8 - 2 * ((cc - t) & 4)
: t + (cc - t) % 0x100 - 2 * ((cc - t) & 0x80);
intreq_.setEventTime<intevent_serial>(std::max(cc, n));
}
psg_.generateSamples(cc, isDoubleSpeed());
psg_.divReset(isDoubleSpeed());
tima_.divReset(cc, TimaInterruptRequester(intreq_));
return;
case 0x05:
tima_.setTima(data, cc, TimaInterruptRequester(intreq_));
@@ -629,7 +714,7 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
tima_.setTac(data, cc, TimaInterruptRequester(intreq_));
break;
case 0x0F:
updateIrqs(cc);
updateIrqs(cc + 1 + isDoubleSpeed());
intreq_.setIfreg(0xE0 | data);
return;
case 0x10:
@@ -671,7 +756,7 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
return;
psg_.generateSamples(cc, isDoubleSpeed());
psg_.setNr14(data);
psg_.setNr14(data, isDoubleSpeed());
data |= 0xBF;
break;
case 0x16:
@@ -705,7 +790,7 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
return;
psg_.generateSamples(cc, isDoubleSpeed());
psg_.setNr24(data);
psg_.setNr24(data, isDoubleSpeed());
data |= 0xBF;
break;
case 0x1A:
@@ -799,7 +884,7 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
psg_.setEnabled(false);
} else {
psg_.reset();
psg_.reset(isDoubleSpeed());
psg_.setEnabled(true);
}
}
@@ -828,8 +913,7 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
case 0x40:
if (ioamhram_[0x140] != data) {
if ((ioamhram_[0x140] ^ data) & lcdc_en) {
unsigned const lyc = lcd_.getStat(ioamhram_[0x145], cc)
& lcdstat_lycflag;
unsigned const stat = data & lcdc_en ? ioamhram_[0x141] : lcd_.getStat(ioamhram_[0x145], cc);
bool const hdmaEnabled = lcd_.hdmaIsEnabled();
lcd_.lcdcChange(data, cc);
@@ -837,14 +921,16 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
ioamhram_[0x141] &= 0xF8;
if (data & lcdc_en) {
if (ioamhram_[0x141] & lcdstat_lycirqen && ioamhram_[0x145] == 0 && !(stat & lcdstat_lycflag))
intreq_.flagIrq(2);
intreq_.setEventTime<intevent_blit>(blanklcd_
? lcd_.nextMode1IrqTime()
: lcd_.nextMode1IrqTime()
+ (70224 << isDoubleSpeed()));
: lcd_.nextMode1IrqTime() + (lcd_cycles_per_frame << isDoubleSpeed()));
} else {
ioamhram_[0x141] |= lyc;
ioamhram_[0x141] |= stat & lcdstat_lycflag;
intreq_.setEventTime<intevent_blit>(
cc + (456 * 4 << isDoubleSpeed()));
cc + (lcd_cycles_per_line * 4 << isDoubleSpeed()));
if (hdmaEnabled)
flagHdmaReq(intreq_);
@@ -858,6 +944,10 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
return;
case 0x41:
lcd_.lcdstatChange(data, cc);
if (!(ioamhram_[0x140] & lcdc_en) && (ioamhram_[0x141] & lcdstat_lycflag)
&& (~ioamhram_[0x141] & lcdstat_lycirqen & (isCgb() ? data : -1))) {
intreq_.flagIrq(2);
}
data = (ioamhram_[0x141] & 0x87) | (data & 0x78);
break;
case 0x42:
@@ -870,11 +960,9 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
lcd_.lycRegChange(data, cc);
break;
case 0x46:
if (lastOamDmaUpdate_ != disabled_time)
endOamDma(cc);
lastOamDmaUpdate_ = cc;
intreq_.setEventTime<intevent_oam>(cc + 8);
oamDmaStartPos_ = (oamDmaPos_ + 2) & 0xFF;
intreq_.setEventTime<intevent_oam>(std::min(intreq_.eventTime(intevent_oam), cc + 8));
ioamhram_[0x146] = data;
oamDmaInitSetup();
return;
@@ -959,8 +1047,8 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
if (isCgb()) {
unsigned index = ioamhram_[0x168] & 0x3F;
lcd_.cgbBgColorChange(index, data, cc);
ioamhram_[0x168] = (ioamhram_[0x168] & ~0x3F)
| ((index + (ioamhram_[0x168] >> 7)) & 0x3F);
ioamhram_[0x168] = (ioamhram_[0x168] & ~0x3Fu)
| ((index + (ioamhram_[0x168] >> 7)) & 0x3F);
}
return;
@@ -973,8 +1061,8 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long
if (isCgb()) {
unsigned index = ioamhram_[0x16A] & 0x3F;
lcd_.cgbSpColorChange(index, data, cc);
ioamhram_[0x16A] = (ioamhram_[0x16A] & ~0x3F)
| ((index + (ioamhram_[0x16A] >> 7)) & 0x3F);
ioamhram_[0x16A] = (ioamhram_[0x16A] & ~0x3Fu)
| ((index + (ioamhram_[0x16A] >> 7)) & 0x3F);
}
return;
@@ -1016,42 +1104,53 @@ void Memory::nontrivial_write(unsigned const p, unsigned const data, unsigned lo
if (lastOamDmaUpdate_ != disabled_time) {
updateOamDma(cc);
if (isInOamDmaConflictArea(cart_.oamDmaSrc(), p, isCgb()) && oamDmaPos_ < 0xA0) {
ioamhram_[oamDmaPos_] = data;
if (cart_.isInOamDmaConflictArea(p) && oamDmaPos_ < oam_size) {
if (isCgb()) {
if (p < mm_wram_begin)
ioamhram_[oamDmaPos_] = cart_.oamDmaSrc() != oam_dma_src_vram ? data : 0;
else if (cart_.oamDmaSrc() != oam_dma_src_wram)
cart_.wramdata(ioamhram_[0x146] >> 4 & 1)[p & 0xFFF] = data;
} else {
ioamhram_[oamDmaPos_] = cart_.oamDmaSrc() == oam_dma_src_wram
? ioamhram_[oamDmaPos_] & data
: data;
}
return;
}
}
if (p < 0xFE00) {
if (p < 0xA000) {
if (p < 0x8000) {
if (p < mm_oam_begin) {
if (p < mm_sram_begin) {
if (p < mm_vram_begin) {
cart_.mbcWrite(p, data);
} else if (lcd_.vramAccessible(cc)) {
} else if (lcd_.vramWritable(cc)) {
lcd_.vramChange(cc);
cart_.vrambankptr()[p] = data;
}
} else if (p < 0xC000) {
} else if (p < mm_wram_begin) {
if (cart_.wsrambankptr())
cart_.wsrambankptr()[p] = data;
else
cart_.rtcWrite(data);
} else
cart_.wramdata(p >> 12 & 1)[p & 0xFFF] = data;
} else if (p - 0xFF80u >= 0x7Fu) {
long const ffp = long(p) - 0xFF00;
} else if (p - mm_hram_begin >= 0x7Fu) {
long const ffp = static_cast<long>(p) - mm_io_begin;
if (ffp < 0) {
if (lcd_.oamWritable(cc) && oamDmaPos_ >= 0xA0 && (p < 0xFEA0 || isCgb())) {
if (lcd_.oamWritable(cc) && oamDmaPos_ >= oam_size
&& (p < mm_oam_begin + oam_size || isCgb())) {
lcd_.oamChange(cc);
ioamhram_[p - 0xFE00] = data;
ioamhram_[p - mm_oam_begin] = data;
}
} else
nontrivial_ff_write(ffp, data, cc);
} else
ioamhram_[p - 0xFE00] = data;
ioamhram_[p - mm_oam_begin] = data;
}
LoadRes Memory::loadROM(std::string const &romfile, bool const forceDmg, bool const multicartCompat) {
if (LoadRes const fail = cart_.loadROM(romfile, forceDmg, multicartCompat))
LoadRes Memory::loadROM(File &file, std::string const &filename, bool const forceDmg, bool const multicartCompat) {
if (LoadRes const fail = cart_.loadROM(file, filename, forceDmg, multicartCompat))
return fail;
psg_.init(cart_.isCgb());
@@ -1065,5 +1164,3 @@ std::size_t Memory::fillSoundBuffer(unsigned long cc) {
psg_.generateSamples(cc, isDoubleSpeed());
return psg_.fillBuffer();
}
}
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef MEMORY_H
@@ -28,8 +28,9 @@
namespace gambatte {
class InputGetter;
class File;
class FilterInfo;
class InputGetter;
class Memory {
public:
@@ -48,7 +49,7 @@ public:
lcd_.setOsdElement(osdElement);
}
unsigned long stop(unsigned long cycleCounter);
unsigned long stop(unsigned long cycleCounter, bool &skip);
bool isCgb() const { return lcd_.isCgb(); }
bool ime() const { return intreq_.ime(); }
bool halted() const { return intreq_.halted(); }
@@ -62,9 +63,12 @@ public:
return (cc - intreq_.eventTime(intevent_blit)) >> isDoubleSpeed();
}
void halt() { intreq_.halt(); }
void freeze(unsigned long cc);
bool halt(unsigned long cc);
void ei(unsigned long cycleCounter) { if (!ime()) { intreq_.ei(cycleCounter); } }
void di() { intreq_.di(); }
unsigned pendingIrqs(unsigned long cc);
void ackIrq(unsigned bit, unsigned long cc);
unsigned ff_read(unsigned p, unsigned long cc) {
return p < 0x80 ? nontrivial_ff_read(p, cc) : ioamhram_[p + 0x100];
@@ -90,7 +94,7 @@ public:
unsigned long event(unsigned long cycleCounter);
unsigned long resetCounters(unsigned long cycleCounter);
LoadRes loadROM(std::string const &romfile, bool forceDmg, bool multicartCompat);
LoadRes loadROM(File &file, std::string const &filename, bool forceDmg, bool multicartCompat);
void setSaveDir(std::string const &dir) { cart_.setSaveDir(dir); }
void setInputGetter(InputGetter *getInput) { getInput_ = getInput; }
void setEndtime(unsigned long cc, unsigned long inc);
@@ -101,6 +105,10 @@ public:
lcd_.setVideoBuffer(videoBuf, pitch);
}
void setCgbColorCorrection(int optNum) {
lcd_.setCgbColorCorrection(optNum);
}
void setDmgPaletteColor(int palNum, int colorNum, unsigned long rgb32) {
lcd_.setDmgPaletteColor(palNum, colorNum, rgb32);
}
@@ -113,7 +121,6 @@ private:
Cartridge cart_;
unsigned char ioamhram_[0x200];
InputGetter *getInput_;
unsigned long divLastUpdate_;
unsigned long lastOamDmaUpdate_;
InterruptRequester intreq_;
Tima tima_;
@@ -123,8 +130,10 @@ private:
unsigned short dmaSource_;
unsigned short dmaDestination_;
unsigned char oamDmaPos_;
unsigned char oamDmaStartPos_;
unsigned char serialCnt_;
bool blanklcd_;
enum HdmaState { hdma_low, hdma_high, hdma_requested } haltHdmaState_;
void decEventCycles(IntEventId eventId, unsigned long dec);
void oamDmaInitSetup();
@@ -132,6 +141,7 @@ private:
void startOamDma(unsigned long cycleCounter);
void endOamDma(unsigned long cycleCounter);
unsigned char const * oamDmaSrcPtr() const;
unsigned long dma(unsigned long cc);
unsigned nontrivial_ff_read(unsigned p, unsigned long cycleCounter);
unsigned nontrivial_read(unsigned p, unsigned long cycleCounter);
void nontrivial_ff_write(unsigned p, unsigned data, unsigned long cycleCounter);
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef MINKEEPER_H
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef OSD_ELEMENT_H
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef SAVESTATE_H
@@ -54,7 +54,9 @@ struct SaveState {
unsigned char f;
unsigned char h;
unsigned char l;
bool skip;
unsigned char opcode;
unsigned char /*bool*/ prefetched;
unsigned char /*bool*/ skip;
} cpu;
struct Mem {
@@ -74,11 +76,12 @@ struct SaveState {
unsigned short dmaDestination;
unsigned char rambank;
unsigned char oamDmaPos;
bool IME;
bool halted;
bool enableRam;
bool rambankMode;
bool hdmaTransfer;
unsigned char haltHdmaState;
unsigned char /*bool*/ IME;
unsigned char /*bool*/ halted;
unsigned char /*bool*/ enableRam;
unsigned char /*bool*/ rambankMode;
unsigned char /*bool*/ hdmaTransfer;
} mem;
struct PPU {
@@ -112,8 +115,8 @@ struct SaveState {
unsigned char oldWy;
unsigned char winDrawState;
unsigned char wscx;
bool weMaster;
bool pendingLcdstatIrq;
unsigned char /*bool*/ weMaster;
unsigned char /*bool*/ pendingLcdstatIrq;
} ppu;
struct SPU {
@@ -121,7 +124,7 @@ struct SaveState {
unsigned long nextPosUpdate;
unsigned char nr3;
unsigned char pos;
bool high;
unsigned char /*bool*/ high;
};
struct Env {
@@ -139,13 +142,13 @@ struct SaveState {
unsigned long counter;
unsigned short shadow;
unsigned char nr0;
bool negging;
unsigned char /*bool*/ neg;
} sweep;
Duty duty;
Env env;
LCounter lcounter;
unsigned char nr4;
bool master;
unsigned char /*bool*/ master;
} ch1;
struct {
@@ -153,7 +156,7 @@ struct SaveState {
Env env;
LCounter lcounter;
unsigned char nr4;
bool master;
unsigned char /*bool*/ master;
} ch2;
struct {
@@ -165,7 +168,7 @@ struct SaveState {
unsigned char nr4;
unsigned char wavePos;
unsigned char sampleBuf;
bool master;
unsigned char /*bool*/ master;
} ch3;
struct {
@@ -176,10 +179,11 @@ struct SaveState {
Env env;
LCounter lcounter;
unsigned char nr4;
bool master;
unsigned char /*bool*/ master;
} ch4;
unsigned long cycleCounter;
unsigned char lastUpdate;
} spu;
struct RTC {
@@ -190,7 +194,7 @@ struct SaveState {
unsigned char dataH;
unsigned char dataM;
unsigned char dataS;
bool lastLatchData;
unsigned char /*bool*/ lastLatchData;
} rtc;
};
@@ -13,11 +13,12 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "sound.h"
#include "savestate.h"
#include <algorithm>
#include <cstring>
@@ -43,12 +44,13 @@ Clock) clock timer on transition to step.
*/
namespace gambatte {
using namespace gambatte;
PSG::PSG()
: buffer_(0)
, bufferPos_(0)
, lastUpdate_(0)
, cycleCounter_(0)
, soVol_(0)
, rsum_(0x8000) // initialize to 0x8000 to prevent borrows from high word, xor away later
, enabled_(false)
@@ -60,11 +62,41 @@ void PSG::init(bool cgb) {
ch3_.init(cgb);
}
void PSG::reset() {
void PSG::reset(bool ds) {
int const divOffset = lastUpdate_ & ds;
unsigned long const cc = cycleCounter_ + divOffset;
// cycleCounter >> 12 & 7 represents the frame sequencer position.
cycleCounter_ = (cc & 0xFFF) + 2 * (~(cc + 1 + !ds) & 0x800);
lastUpdate_ = ((lastUpdate_ + 3) & -4) - !ds;
ch1_.reset();
ch2_.reset();
ch3_.reset();
ch4_.reset();
ch4_.reset(cycleCounter_);
}
void PSG::divReset(bool ds) {
int const divOffset = lastUpdate_ & ds;
unsigned long const cc = cycleCounter_ + divOffset;
cycleCounter_ = (cc & -0x1000) + 2 * (cc & 0x800) - divOffset;
ch1_.resetCc(cc - divOffset, cycleCounter_);
ch2_.resetCc(cc - divOffset, cycleCounter_);
ch3_.resetCc(cc - divOffset, cycleCounter_);
ch4_.resetCc(cc - divOffset, cycleCounter_);
}
void PSG::speedChange(unsigned long const cpuCc, bool const ds) {
generateSamples(cpuCc, ds);
lastUpdate_ -= ds;
// correct for cycles since DIV reset (if any).
if (!ds) {
unsigned long const cc = cycleCounter_;
unsigned const divCycles = cc & 0xFFF;
cycleCounter_ = cc - divCycles / 2 - lastUpdate_ % 2;
ch1_.resetCc(cc, cycleCounter_);
ch2_.resetCc(cc, cycleCounter_);
ch3_.resetCc(cc, cycleCounter_);
ch4_.resetCc(cc, cycleCounter_);
}
}
void PSG::setStatePtrs(SaveState &state) {
@@ -72,10 +104,12 @@ void PSG::setStatePtrs(SaveState &state) {
}
void PSG::saveState(SaveState &state) {
ch1_.saveState(state);
ch2_.saveState(state);
state.spu.cycleCounter = cycleCounter_;
state.spu.lastUpdate = (lastUpdate_ + 1) % 4;
ch1_.saveState(state, cycleCounter_);
ch2_.saveState(state, cycleCounter_);
ch3_.saveState(state);
ch4_.saveState(state);
ch4_.saveState(state, cycleCounter_);
}
void PSG::loadState(SaveState const &state) {
@@ -84,23 +118,26 @@ void PSG::loadState(SaveState const &state) {
ch3_.loadState(state);
ch4_.loadState(state);
lastUpdate_ = state.cpu.cycleCounter;
cycleCounter_ = state.spu.cycleCounter;
lastUpdate_ = state.cpu.cycleCounter - (1 - state.spu.lastUpdate) % 4u;
setSoVolume(state.mem.ioamhram.get()[0x124]);
mapSo(state.mem.ioamhram.get()[0x125]);
enabled_ = state.mem.ioamhram.get()[0x126] >> 7 & 1;
}
void PSG::accumulateChannels(unsigned long const cycles) {
inline void PSG::accumulateChannels(unsigned long const cycles) {
unsigned long const cc = cycleCounter_;
uint_least32_t *const buf = buffer_ + bufferPos_;
std::memset(buf, 0, cycles * sizeof *buf);
ch1_.update(buf, soVol_, cycles);
ch2_.update(buf, soVol_, cycles);
ch3_.update(buf, soVol_, cycles);
ch4_.update(buf, soVol_, cycles);
ch1_.update(buf, soVol_, cc, cc + cycles);
ch2_.update(buf, soVol_, cc, cc + cycles);
ch3_.update(buf, soVol_, cc, cc + cycles);
ch4_.update(buf, soVol_, cc, cc + cycles);
cycleCounter_ = (cc + cycles) % SoundUnit::counter_max;
}
void PSG::generateSamples(unsigned long const cycleCounter, bool const doubleSpeed) {
unsigned long const cycles = (cycleCounter - lastUpdate_) >> (1 + doubleSpeed);
void PSG::generateSamples(unsigned long const cpuCc, bool const doubleSpeed) {
unsigned long const cycles = (cpuCc - lastUpdate_) >> (1 + doubleSpeed);
lastUpdate_ += cycles << (1 + doubleSpeed);
if (cycles)
@@ -175,10 +212,10 @@ void PSG::setSoVolume(unsigned nr50) {
void PSG::mapSo(unsigned nr51) {
unsigned long so = nr51 * so1Mul() + (nr51 >> 4) * so2Mul();
ch1_.setSo((so & 0x00010001) * 0xFFFF);
ch2_.setSo((so >> 1 & 0x00010001) * 0xFFFF);
ch1_.setSo((so & 0x00010001) * 0xFFFF, cycleCounter_);
ch2_.setSo((so >> 1 & 0x00010001) * 0xFFFF, cycleCounter_);
ch3_.setSo((so >> 2 & 0x00010001) * 0xFFFF);
ch4_.setSo((so >> 3 & 0x00010001) * 0xFFFF);
ch4_.setSo((so >> 3 & 0x00010001) * 0xFFFF, cycleCounter_);
}
unsigned PSG::getStatus() const {
@@ -187,5 +224,3 @@ unsigned PSG::getStatus() const {
| ch3_.isActive() << 2
| ch4_.isActive() << 3;
}
}
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef SOUND_H
@@ -30,13 +30,15 @@ class PSG {
public:
PSG();
void init(bool cgb);
void reset();
void reset(bool ds);
void divReset(bool ds);
void setStatePtrs(SaveState &state);
void saveState(SaveState &state);
void loadState(SaveState const &state);
void generateSamples(unsigned long cycleCounter, bool doubleSpeed);
void resetCounter(unsigned long newCc, unsigned long oldCc, bool doubleSpeed);
void speedChange(unsigned long cc, bool doubleSpeed);
std::size_t fillBuffer();
void setBuffer(uint_least32_t *buf) { buffer_ = buf; bufferPos_ = 0; }
@@ -44,28 +46,28 @@ public:
void setEnabled(bool value) { enabled_ = value; }
void setNr10(unsigned data) { ch1_.setNr0(data); }
void setNr11(unsigned data) { ch1_.setNr1(data); }
void setNr12(unsigned data) { ch1_.setNr2(data); }
void setNr13(unsigned data) { ch1_.setNr3(data); }
void setNr14(unsigned data) { ch1_.setNr4(data); }
void setNr11(unsigned data) { ch1_.setNr1(data, cycleCounter_); }
void setNr12(unsigned data) { ch1_.setNr2(data, cycleCounter_); }
void setNr13(unsigned data) { ch1_.setNr3(data, cycleCounter_); }
void setNr14(unsigned data, bool ds) { ch1_.setNr4(data, cycleCounter_, !(lastUpdate_ & ds)); }
void setNr21(unsigned data) { ch2_.setNr1(data); }
void setNr22(unsigned data) { ch2_.setNr2(data); }
void setNr23(unsigned data) { ch2_.setNr3(data); }
void setNr24(unsigned data) { ch2_.setNr4(data); }
void setNr21(unsigned data) { ch2_.setNr1(data, cycleCounter_); }
void setNr22(unsigned data) { ch2_.setNr2(data, cycleCounter_); }
void setNr23(unsigned data) { ch2_.setNr3(data, cycleCounter_); }
void setNr24(unsigned data, bool ds) { ch2_.setNr4(data, cycleCounter_, !(lastUpdate_ & ds)); }
void setNr30(unsigned data) { ch3_.setNr0(data); }
void setNr31(unsigned data) { ch3_.setNr1(data); }
void setNr31(unsigned data) { ch3_.setNr1(data, cycleCounter_); }
void setNr32(unsigned data) { ch3_.setNr2(data); }
void setNr33(unsigned data) { ch3_.setNr3(data); }
void setNr34(unsigned data) { ch3_.setNr4(data); }
unsigned waveRamRead(unsigned index) const { return ch3_.waveRamRead(index); }
void waveRamWrite(unsigned index, unsigned data) { ch3_.waveRamWrite(index, data); }
void setNr34(unsigned data) { ch3_.setNr4(data, cycleCounter_); }
unsigned waveRamRead(unsigned index) const { return ch3_.waveRamRead(index, cycleCounter_); }
void waveRamWrite(unsigned index, unsigned data) { ch3_.waveRamWrite(index, data, cycleCounter_); }
void setNr41(unsigned data) { ch4_.setNr1(data); }
void setNr42(unsigned data) { ch4_.setNr2(data); }
void setNr43(unsigned data) { ch4_.setNr3(data); }
void setNr44(unsigned data) { ch4_.setNr4(data); }
void setNr41(unsigned data) { ch4_.setNr1(data, cycleCounter_); }
void setNr42(unsigned data) { ch4_.setNr2(data, cycleCounter_); }
void setNr43(unsigned data) { ch4_.setNr3(data, cycleCounter_); }
void setNr44(unsigned data) { ch4_.setNr4(data, cycleCounter_); }
void setSoVolume(unsigned nr50);
void mapSo(unsigned nr51);
@@ -79,6 +81,7 @@ private:
uint_least32_t *buffer_;
std::size_t bufferPos_;
unsigned long lastUpdate_;
unsigned long cycleCounter_;
unsigned long soVol_;
uint_least32_t rsum_;
bool enabled_;
@@ -13,34 +13,33 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "channel1.h"
#include "psgdef.h"
#include "../savestate.h"
#include <algorithm>
namespace gambatte {
using namespace gambatte;
Channel1::SweepUnit::SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit)
: disableMaster_(disabler)
, dutyUnit_(dutyUnit)
, shadow_(0)
, nr0_(0)
, negging_(false)
, neg_(false)
, cgb_(false)
{
}
unsigned Channel1::SweepUnit::calcFreq() {
unsigned freq = shadow_ >> (nr0_ & 0x07);
if (nr0_ & 0x08) {
freq = shadow_ - freq;
negging_ = true;
} else
freq = shadow_ + freq;
unsigned const freq = nr0_ & psg_nr10_neg
? shadow_ - (shadow_ >> (nr0_ & psg_nr10_rsh))
: shadow_ + (shadow_ >> (nr0_ & psg_nr10_rsh));
if (nr0_ & psg_nr10_neg)
neg_ = true;
if (freq & 2048)
disableMaster_();
@@ -48,12 +47,12 @@ unsigned Channel1::SweepUnit::calcFreq() {
}
void Channel1::SweepUnit::event() {
unsigned long const period = nr0_ >> 4 & 0x07;
unsigned long const period = (nr0_ & psg_nr10_time) / (1u * psg_nr10_time & -psg_nr10_time);
if (period) {
unsigned const freq = calcFreq();
if (!(freq & 2048) && (nr0_ & 0x07)) {
if (!(freq & 2048) && nr0_ & psg_nr10_rsh) {
shadow_ = freq;
dutyUnit_.setFreq(freq, counter_);
calcFreq();
@@ -65,25 +64,25 @@ void Channel1::SweepUnit::event() {
}
void Channel1::SweepUnit::nr0Change(unsigned newNr0) {
if (negging_ && !(newNr0 & 0x08))
if (neg_ && !(newNr0 & psg_nr10_neg))
disableMaster_();
nr0_ = newNr0;
}
void Channel1::SweepUnit::nr4Init(unsigned long const cc) {
negging_ = false;
neg_ = false;
shadow_ = dutyUnit_.freq();
unsigned const period = nr0_ >> 4 & 0x07;
unsigned const shift = nr0_ & 0x07;
unsigned const period = (nr0_ & psg_nr10_time) / (1u * psg_nr10_time & -psg_nr10_time);
unsigned const rsh = nr0_ & psg_nr10_rsh;
if (period | shift)
if (period | rsh)
counter_ = ((((cc + 2 + cgb_ * 2) >> 14) + (period ? period : 8)) << 14) + 2;
else
counter_ = counter_disabled;
if (shift)
if (rsh)
calcFreq();
}
@@ -95,14 +94,14 @@ void Channel1::SweepUnit::saveState(SaveState &state) const {
state.spu.ch1.sweep.counter = counter_;
state.spu.ch1.sweep.shadow = shadow_;
state.spu.ch1.sweep.nr0 = nr0_;
state.spu.ch1.sweep.negging = negging_;
state.spu.ch1.sweep.neg = neg_;
}
void Channel1::SweepUnit::loadState(SaveState const &state) {
counter_ = std::max(state.spu.ch1.sweep.counter, state.spu.cycleCounter);
shadow_ = state.spu.ch1.sweep.shadow;
nr0_ = state.spu.ch1.sweep.nr0;
negging_ = state.spu.ch1.sweep.negging;
neg_ = state.spu.ch1.sweep.neg;
}
Channel1::Channel1()
@@ -112,7 +111,6 @@ Channel1::Channel1()
, envelopeUnit_(staticOutputTest_)
, sweepUnit_(disableMaster_, dutyUnit_)
, nextEventUnit_(0)
, cycleCounter_(0)
, soMask_(0)
, prevOut_(0)
, nr4_(0)
@@ -134,52 +132,48 @@ void Channel1::setNr0(unsigned data) {
setEvent();
}
void Channel1::setNr1(unsigned data) {
lengthCounter_.nr1Change(data, nr4_, cycleCounter_);
dutyUnit_.nr1Change(data, cycleCounter_);
void Channel1::setNr1(unsigned data, unsigned long cc) {
lengthCounter_.nr1Change(data, nr4_, cc);
dutyUnit_.nr1Change(data, cc);
setEvent();
}
void Channel1::setNr2(unsigned data) {
void Channel1::setNr2(unsigned data, unsigned long cc) {
if (envelopeUnit_.nr2Change(data))
disableMaster_();
else
staticOutputTest_(cycleCounter_);
staticOutputTest_(cc);
setEvent();
}
void Channel1::setNr3(unsigned data) {
dutyUnit_.nr3Change(data, cycleCounter_);
void Channel1::setNr3(unsigned data, unsigned long cc) {
dutyUnit_.nr3Change(data, cc);
setEvent();
}
void Channel1::setNr4(unsigned const data) {
lengthCounter_.nr4Change(nr4_, data, cycleCounter_);
void Channel1::setNr4(unsigned data, unsigned long cc, unsigned long ref) {
lengthCounter_.nr4Change(nr4_, data, cc);
dutyUnit_.nr4Change(data, cc, ref);
nr4_ = data;
dutyUnit_.nr4Change(data, cycleCounter_);
if (data & 0x80) { // init-bit
nr4_ &= 0x7F;
master_ = !envelopeUnit_.nr4Init(cycleCounter_);
sweepUnit_.nr4Init(cycleCounter_);
staticOutputTest_(cycleCounter_);
if (nr4_ & psg_nr4_init) {
nr4_ -= psg_nr4_init;
master_ = !envelopeUnit_.nr4Init(cc);
sweepUnit_.nr4Init(cc);
staticOutputTest_(cc);
}
setEvent();
}
void Channel1::setSo(unsigned long soMask) {
void Channel1::setSo(unsigned long soMask, unsigned long cc) {
soMask_ = soMask;
staticOutputTest_(cycleCounter_);
staticOutputTest_(cc);
setEvent();
}
void Channel1::reset() {
// cycleCounter >> 12 & 7 represents the frame sequencer position.
cycleCounter_ &= 0xFFF;
cycleCounter_ += ~(cycleCounter_ + 2) << 1 & 0x1000;
dutyUnit_.reset();
envelopeUnit_.reset();
sweepUnit_.reset();
@@ -190,13 +184,12 @@ void Channel1::init(bool cgb) {
sweepUnit_.init(cgb);
}
void Channel1::saveState(SaveState &state) {
void Channel1::saveState(SaveState &state, unsigned long cc) {
sweepUnit_.saveState(state);
dutyUnit_.saveState(state.spu.ch1.duty, cycleCounter_);
dutyUnit_.saveState(state.spu.ch1.duty, cc);
envelopeUnit_.saveState(state.spu.ch1.env);
lengthCounter_.saveState(state.spu.ch1.lcounter);
state.spu.cycleCounter = cycleCounter_;
state.spu.ch1.nr4 = nr4_;
state.spu.ch1.master = master_;
}
@@ -204,59 +197,50 @@ void Channel1::saveState(SaveState &state) {
void Channel1::loadState(SaveState const &state) {
sweepUnit_.loadState(state);
dutyUnit_.loadState(state.spu.ch1.duty, state.mem.ioamhram.get()[0x111],
state.spu.ch1.nr4, state.spu.cycleCounter);
state.spu.ch1.nr4, state.spu.cycleCounter);
envelopeUnit_.loadState(state.spu.ch1.env, state.mem.ioamhram.get()[0x112],
state.spu.cycleCounter);
state.spu.cycleCounter);
lengthCounter_.loadState(state.spu.ch1.lcounter, state.spu.cycleCounter);
cycleCounter_ = state.spu.cycleCounter;
nr4_ = state.spu.ch1.nr4;
master_ = state.spu.ch1.master;
}
void Channel1::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cycles) {
void Channel1::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cc, unsigned long const end) {
unsigned long const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0;
unsigned long const outLow = outBase * (0 - 15ul);
unsigned long const endCycles = cycleCounter_ + cycles;
unsigned long const outLow = outBase * -15;
for (;;) {
while (cc < end) {
unsigned long const outHigh = master_
? outBase * (envelopeUnit_.getVolume() * 2 - 15ul)
: outLow;
unsigned long const nextMajorEvent = std::min(nextEventUnit_->counter(), endCycles);
? outBase * (envelopeUnit_.getVolume() * 2l - 15)
: outLow;
unsigned long const nextMajorEvent = std::min(nextEventUnit_->counter(), end);
unsigned long out = dutyUnit_.isHighState() ? outHigh : outLow;
while (dutyUnit_.counter() <= nextMajorEvent) {
*buf = out - prevOut_;
prevOut_ = out;
buf += dutyUnit_.counter() - cycleCounter_;
cycleCounter_ = dutyUnit_.counter();
buf += dutyUnit_.counter() - cc;
cc = dutyUnit_.counter();
dutyUnit_.event();
out = dutyUnit_.isHighState() ? outHigh : outLow;
}
if (cycleCounter_ < nextMajorEvent) {
if (cc < nextMajorEvent) {
*buf = out - prevOut_;
prevOut_ = out;
buf += nextMajorEvent - cycleCounter_;
cycleCounter_ = nextMajorEvent;
buf += nextMajorEvent - cc;
cc = nextMajorEvent;
}
if (nextEventUnit_->counter() == nextMajorEvent) {
nextEventUnit_->event();
setEvent();
} else
break;
}
}
if (cycleCounter_ >= SoundUnit::counter_max) {
dutyUnit_.resetCounters(cycleCounter_);
lengthCounter_.resetCounters(cycleCounter_);
envelopeUnit_.resetCounters(cycleCounter_);
sweepUnit_.resetCounters(cycleCounter_);
cycleCounter_ -= SoundUnit::counter_max;
if (cc >= SoundUnit::counter_max) {
dutyUnit_.resetCounters(cc);
lengthCounter_.resetCounters(cc);
envelopeUnit_.resetCounters(cc);
sweepUnit_.resetCounters(cc);
}
}
}
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef SOUND_CHANNEL1_H
@@ -34,16 +34,17 @@ class Channel1 {
public:
Channel1();
void setNr0(unsigned data);
void setNr1(unsigned data);
void setNr2(unsigned data);
void setNr3(unsigned data);
void setNr4(unsigned data);
void setSo(unsigned long soMask);
void setNr1(unsigned data, unsigned long cc);
void setNr2(unsigned data, unsigned long cc);
void setNr3(unsigned data, unsigned long cc);
void setNr4(unsigned data, unsigned long cc, unsigned long ref);
void setSo(unsigned long soMask, unsigned long cc);
bool isActive() const { return master_; }
void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cc, unsigned long end);
void reset();
void resetCc(unsigned long cc, unsigned long ncc) { dutyUnit_.resetCc(cc, ncc); }
void init(bool cgb);
void saveState(SaveState &state);
void saveState(SaveState &state, unsigned long cc);
void loadState(SaveState const &state);
private:
@@ -63,7 +64,7 @@ private:
DutyUnit &dutyUnit_;
unsigned short shadow_;
unsigned char nr0_;
bool negging_;
bool neg_;
bool cgb_;
unsigned calcFreq();
@@ -78,7 +79,6 @@ private:
EnvelopeUnit envelopeUnit_;
SweepUnit sweepUnit_;
SoundUnit *nextEventUnit_;
unsigned long cycleCounter_;
unsigned long soMask_;
unsigned long prevOut_;
unsigned char nr4_;
@@ -13,21 +13,22 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "channel2.h"
#include "psgdef.h"
#include "../savestate.h"
#include <algorithm>
namespace gambatte {
using namespace gambatte;
Channel2::Channel2()
: staticOutputTest_(*this, dutyUnit_)
, disableMaster_(master_, dutyUnit_)
, lengthCounter_(disableMaster_, 0x3F)
, envelopeUnit_(staticOutputTest_)
, cycleCounter_(0)
, soMask_(0)
, prevOut_(0)
, nr4_(0)
@@ -42,58 +43,54 @@ void Channel2::setEvent() {
nextEventUnit = &lengthCounter_;
}
void Channel2::setNr1(unsigned data) {
lengthCounter_.nr1Change(data, nr4_, cycleCounter_);
dutyUnit_.nr1Change(data, cycleCounter_);
void Channel2::setNr1(unsigned data, unsigned long cc) {
lengthCounter_.nr1Change(data, nr4_, cc);
dutyUnit_.nr1Change(data, cc);
setEvent();
}
void Channel2::setNr2(unsigned data) {
void Channel2::setNr2(unsigned data, unsigned long cc) {
if (envelopeUnit_.nr2Change(data))
disableMaster_();
else
staticOutputTest_(cycleCounter_);
staticOutputTest_(cc);
setEvent();
}
void Channel2::setNr3(unsigned data) {
dutyUnit_.nr3Change(data, cycleCounter_);
void Channel2::setNr3(unsigned data, unsigned long cc) {
dutyUnit_.nr3Change(data, cc);
setEvent();
}
void Channel2::setNr4(unsigned const data) {
lengthCounter_.nr4Change(nr4_, data, cycleCounter_);
void Channel2::setNr4(unsigned data, unsigned long cc, unsigned long ref) {
lengthCounter_.nr4Change(nr4_, data, cc);
nr4_ = data;
if (data & 0x80) { // init-bit
nr4_ &= 0x7F;
master_ = !envelopeUnit_.nr4Init(cycleCounter_);
staticOutputTest_(cycleCounter_);
if (nr4_ & psg_nr4_init) {
nr4_ -= psg_nr4_init;
master_ = !envelopeUnit_.nr4Init(cc);
staticOutputTest_(cc);
}
dutyUnit_.nr4Change(data, cycleCounter_);
dutyUnit_.nr4Change(data, cc, ref);
setEvent();
}
void Channel2::setSo(unsigned long soMask) {
void Channel2::setSo(unsigned long soMask, unsigned long cc) {
soMask_ = soMask;
staticOutputTest_(cycleCounter_);
staticOutputTest_(cc);
setEvent();
}
void Channel2::reset() {
// cycleCounter >> 12 & 7 represents the frame sequencer position.
cycleCounter_ &= 0xFFF;
cycleCounter_ += ~(cycleCounter_ + 2) << 1 & 0x1000;
dutyUnit_.reset();
envelopeUnit_.reset();
setEvent();
}
void Channel2::saveState(SaveState &state) {
dutyUnit_.saveState(state.spu.ch2.duty, cycleCounter_);
void Channel2::saveState(SaveState &state, unsigned long cc) {
dutyUnit_.saveState(state.spu.ch2.duty, cc);
envelopeUnit_.saveState(state.spu.ch2.env);
lengthCounter_.saveState(state.spu.ch2.lcounter);
@@ -103,58 +100,49 @@ void Channel2::saveState(SaveState &state) {
void Channel2::loadState(SaveState const &state) {
dutyUnit_.loadState(state.spu.ch2.duty, state.mem.ioamhram.get()[0x116],
state.spu.ch2.nr4, state.spu.cycleCounter);
state.spu.ch2.nr4, state.spu.cycleCounter);
envelopeUnit_.loadState(state.spu.ch2.env, state.mem.ioamhram.get()[0x117],
state.spu.cycleCounter);
state.spu.cycleCounter);
lengthCounter_.loadState(state.spu.ch2.lcounter, state.spu.cycleCounter);
cycleCounter_ = state.spu.cycleCounter;
nr4_ = state.spu.ch2.nr4;
master_ = state.spu.ch2.master;
}
void Channel2::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cycles) {
void Channel2::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cc, unsigned long const end) {
unsigned long const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0;
unsigned long const outLow = outBase * (0 - 15ul);
unsigned long const endCycles = cycleCounter_ + cycles;
unsigned long const outLow = outBase * -15;
for (;;) {
while (cc < end) {
unsigned long const outHigh = master_
? outBase * (envelopeUnit_.getVolume() * 2 - 15ul)
: outLow;
unsigned long const nextMajorEvent = std::min(nextEventUnit->counter(), endCycles);
? outBase * (envelopeUnit_.getVolume() * 2l - 15)
: outLow;
unsigned long const nextMajorEvent = std::min(nextEventUnit->counter(), end);
unsigned long out = dutyUnit_.isHighState() ? outHigh : outLow;
while (dutyUnit_.counter() <= nextMajorEvent) {
*buf += out - prevOut_;
prevOut_ = out;
buf += dutyUnit_.counter() - cycleCounter_;
cycleCounter_ = dutyUnit_.counter();
buf += dutyUnit_.counter() - cc;
cc = dutyUnit_.counter();
dutyUnit_.event();
out = dutyUnit_.isHighState() ? outHigh : outLow;
}
if (cycleCounter_ < nextMajorEvent) {
if (cc < nextMajorEvent) {
*buf += out - prevOut_;
prevOut_ = out;
buf += nextMajorEvent - cycleCounter_;
cycleCounter_ = nextMajorEvent;
buf += nextMajorEvent - cc;
cc = nextMajorEvent;
}
if (nextEventUnit->counter() == nextMajorEvent) {
nextEventUnit->event();
setEvent();
} else
break;
}
}
if (cycleCounter_ >= SoundUnit::counter_max) {
dutyUnit_.resetCounters(cycleCounter_);
lengthCounter_.resetCounters(cycleCounter_);
envelopeUnit_.resetCounters(cycleCounter_);
cycleCounter_ -= SoundUnit::counter_max;
if (cc >= SoundUnit::counter_max) {
dutyUnit_.resetCounters(cc);
lengthCounter_.resetCounters(cc);
envelopeUnit_.resetCounters(cc);
}
}
}
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef SOUND_CHANNEL2_H
@@ -32,15 +32,16 @@ struct SaveState;
class Channel2 {
public:
Channel2();
void setNr1(unsigned data);
void setNr2(unsigned data);
void setNr3(unsigned data);
void setNr4(unsigned data);
void setSo(unsigned long soMask);
void setNr1(unsigned data, unsigned long cc);
void setNr2(unsigned data, unsigned long cc);
void setNr3(unsigned data, unsigned long cc);
void setNr4(unsigned data, unsigned long cc, unsigned long ref);
void setSo(unsigned long soMask, unsigned long cc);
bool isActive() const { return master_; }
void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cc, unsigned long end);
void reset();
void saveState(SaveState &state);
void resetCc(unsigned long cc, unsigned long ncc) { dutyUnit_.resetCc(cc, ncc); }
void saveState(SaveState &state, unsigned long cc);
void loadState(SaveState const &state);
private:
@@ -52,7 +53,6 @@ private:
DutyUnit dutyUnit_;
EnvelopeUnit envelopeUnit_;
SoundUnit *nextEventUnit;
unsigned long cycleCounter_;
unsigned long soMask_;
unsigned long prevOut_;
unsigned char nr4_;
@@ -13,24 +13,29 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "channel3.h"
#include "psgdef.h"
#include "../savestate.h"
#include <algorithm>
#include <cstring>
static inline unsigned toPeriod(unsigned nr3, unsigned nr4) {
using namespace gambatte;
namespace {
unsigned toPeriod(unsigned nr3, unsigned nr4) {
return 0x800 - ((nr4 << 8 & 0x700) | nr3);
}
namespace gambatte {
}
Channel3::Channel3()
: disableMaster_(master_, waveCounter_)
, lengthCounter_(disableMaster_, 0xFF)
, cycleCounter_(0)
, soMask_(0)
, prevOut_(0)
, waveCounter_(SoundUnit::counter_disabled)
@@ -47,25 +52,22 @@ Channel3::Channel3()
}
void Channel3::setNr0(unsigned data) {
nr0_ = data & 0x80;
if (!(data & 0x80))
nr0_ = data & psg_nr4_init;
if (!nr0_)
disableMaster_();
}
void Channel3::setNr2(unsigned data) {
rshift_ = (data >> 5 & 3U) - 1;
if (rshift_ > 3)
rshift_ = 4;
rshift_ = std::min((data >> 5 & 3) - 1, 4u);
}
void Channel3::setNr4(unsigned const data) {
lengthCounter_.nr4Change(nr4_, data, cycleCounter_);
nr4_ = data & 0x7F;
void Channel3::setNr4(unsigned const data, unsigned long const cc) {
lengthCounter_.nr4Change(nr4_, data, cc);
nr4_ = data & ~(1u * psg_nr4_init);
if (data & nr0_/* & 0x80*/) {
if (!cgb_ && waveCounter_ == cycleCounter_ + 1) {
unsigned const pos = ((wavePos_ + 1) & 0x1F) >> 1;
if (data & nr0_) {
if (!cgb_ && waveCounter_ == cc + 1) {
int const pos = (wavePos_ + 1) / 2 % sizeof waveRam_;
if (pos < 4)
waveRam_[0] = waveRam_[pos];
@@ -75,7 +77,7 @@ void Channel3::setNr4(unsigned const data) {
master_ = true;
wavePos_ = 0;
lastReadTime_ = waveCounter_ = cycleCounter_ + toPeriod(nr3_, data) + 3;
lastReadTime_ = waveCounter_ = cc + toPeriod(nr3_, data) + 3;
}
}
@@ -84,13 +86,15 @@ void Channel3::setSo(unsigned long soMask) {
}
void Channel3::reset() {
// cycleCounter >> 12 & 7 represents the frame sequencer position.
cycleCounter_ &= 0xFFF;
cycleCounter_ += ~(cycleCounter_ + 2) << 1 & 0x1000;
sampleBuf_ = 0;
}
void Channel3::resetCc(unsigned long cc, unsigned long newCc) {
lastReadTime_ -= cc - newCc;
if (waveCounter_ != SoundUnit::counter_disabled)
waveCounter_ -= cc - newCc;
}
void Channel3::init(bool cgb) {
cgb_ = cgb;
}
@@ -114,16 +118,15 @@ void Channel3::saveState(SaveState &state) const {
void Channel3::loadState(SaveState const &state) {
lengthCounter_.loadState(state.spu.ch3.lcounter, state.spu.cycleCounter);
cycleCounter_ = state.spu.cycleCounter;
waveCounter_ = std::max(state.spu.ch3.waveCounter, state.spu.cycleCounter);
lastReadTime_ = state.spu.ch3.lastReadTime;
nr3_ = state.spu.ch3.nr3;
nr4_ = state.spu.ch3.nr4;
wavePos_ = state.spu.ch3.wavePos & 0x1F;
wavePos_ = state.spu.ch3.wavePos % (2 * sizeof waveRam_);
sampleBuf_ = state.spu.ch3.sampleBuf;
master_ = state.spu.ch3.master;
nr0_ = state.mem.ioamhram.get()[0x11A] & 0x80;
nr0_ = state.mem.ioamhram.get()[0x11A] & psg_nr4_init;
setNr2(state.mem.ioamhram.get()[0x11C]);
}
@@ -134,78 +137,78 @@ void Channel3::updateWaveCounter(unsigned long const cc) {
lastReadTime_ = waveCounter_ + periods * period;
waveCounter_ = lastReadTime_ + period;
wavePos_ += periods + 1;
wavePos_ &= 0x1F;
sampleBuf_ = waveRam_[wavePos_ >> 1];
wavePos_ = (wavePos_ + periods + 1) % (2 * sizeof waveRam_);
sampleBuf_ = waveRam_[wavePos_ / 2];
}
}
void Channel3::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cycles) {
unsigned long const outBase = nr0_/* & 0x80*/ ? soBaseVol & soMask_ : 0;
void Channel3::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cc, unsigned long const end) {
unsigned long const outBase = nr0_ ? soBaseVol & soMask_ : 0;
if (outBase && rshift_ != 4) {
unsigned long const endCycles = cycleCounter_ + cycles;
for (;;) {
while (std::min(waveCounter_, lengthCounter_.counter()) <= end) {
unsigned pos = wavePos_;
unsigned const period = toPeriod(nr3_, nr4_), rsh = rshift_;
unsigned long const nextMajorEvent =
std::min(lengthCounter_.counter(), endCycles);
std::min(lengthCounter_.counter(), end);
unsigned long cnt = waveCounter_, prevOut = prevOut_;
unsigned long out = master_
? ((sampleBuf_ >> (~wavePos_ << 2 & 4) & 0xF) >> rshift_) * 2 - 15ul
: 0 - 15ul;
? ((pos % 2 ? sampleBuf_ & 0xF : sampleBuf_ >> 4) >> rsh) * 2l - 15
: -15;
out *= outBase;
while (waveCounter_ <= nextMajorEvent) {
*buf += out - prevOut_;
prevOut_ = out;
buf += waveCounter_ - cycleCounter_;
cycleCounter_ = waveCounter_;
lastReadTime_ = waveCounter_;
waveCounter_ += toPeriod(nr3_, nr4_);
++wavePos_;
wavePos_ &= 0x1F;
sampleBuf_ = waveRam_[wavePos_ >> 1];
out = ((sampleBuf_ >> (~wavePos_ << 2 & 4) & 0xF) >> rshift_) * 2 - 15ul;
while (cnt <= nextMajorEvent) {
*buf += out - prevOut;
prevOut = out;
buf += cnt - cc;
cc = cnt;
cnt += period;
++pos;
unsigned const s = waveRam_[pos / 2 % sizeof waveRam_];
out = ((pos % 2 ? s & 0xF : s >> 4) >> rsh) * 2l - 15;
out *= outBase;
}
if (cycleCounter_ < nextMajorEvent) {
if (cnt != waveCounter_) {
wavePos_ = pos % (2 * sizeof waveRam_);
sampleBuf_ = waveRam_[wavePos_ / 2];
prevOut_ = prevOut;
waveCounter_ = cnt;
lastReadTime_ = cc;
}
if (cc < nextMajorEvent) {
*buf += out - prevOut_;
prevOut_ = out;
buf += nextMajorEvent - cycleCounter_;
cycleCounter_ = nextMajorEvent;
buf += nextMajorEvent - cc;
cc = nextMajorEvent;
}
if (lengthCounter_.counter() == nextMajorEvent) {
if (lengthCounter_.counter() == nextMajorEvent)
lengthCounter_.event();
} else
break;
}
if (cc < end) {
unsigned long out = master_
? ((wavePos_ % 2 ? sampleBuf_ & 0xF : sampleBuf_ >> 4) >> rshift_) * 2l - 15
: -15;
out *= outBase;
*buf += out - prevOut_;
prevOut_ = out;
cc = end;
}
} else {
unsigned long const out = outBase * (0 - 15ul);
unsigned long const out = outBase * -15;
*buf += out - prevOut_;
prevOut_ = out;
cycleCounter_ += cycles;
while (lengthCounter_.counter() <= cycleCounter_) {
cc = end;
while (lengthCounter_.counter() <= cc) {
updateWaveCounter(lengthCounter_.counter());
lengthCounter_.event();
}
updateWaveCounter(cycleCounter_);
updateWaveCounter(cc);
}
if (cycleCounter_ >= SoundUnit::counter_max) {
lengthCounter_.resetCounters(cycleCounter_);
if (cc >= SoundUnit::counter_max) {
lengthCounter_.resetCounters(cc);
lastReadTime_ -= SoundUnit::counter_max;
if (waveCounter_ != SoundUnit::counter_disabled)
waveCounter_ -= SoundUnit::counter_max;
lastReadTime_ -= SoundUnit::counter_max;
cycleCounter_ -= SoundUnit::counter_max;
}
}
}
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef SOUND_CHANNEL3_H
@@ -31,36 +31,38 @@ class Channel3 {
public:
Channel3();
bool isActive() const { return master_; }
bool isCgb() const { return cgb_; }
void reset();
void resetCc(unsigned long cc, unsigned long newCc);
void init(bool cgb);
void setStatePtrs(SaveState &state);
void saveState(SaveState &state) const;
void loadState(const SaveState &state);
void loadState(SaveState const &state);
void setNr0(unsigned data);
void setNr1(unsigned data) { lengthCounter_.nr1Change(data, nr4_, cycleCounter_); }
void setNr1(unsigned data, unsigned long cc) { lengthCounter_.nr1Change(data, nr4_, cc); }
void setNr2(unsigned data);
void setNr3(unsigned data) { nr3_ = data; }
void setNr4(unsigned data);
void setNr4(unsigned data, unsigned long cc);
void setSo(unsigned long soMask);
void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cc, unsigned long end);
unsigned waveRamRead(unsigned index) const {
unsigned waveRamRead(unsigned index, unsigned long cc) const {
if (master_) {
if (!cgb_ && cycleCounter_ != lastReadTime_)
if (!cgb_ && cc != lastReadTime_)
return 0xFF;
index = wavePos_ >> 1;
index = wavePos_ / 2;
}
return waveRam_[index];
}
void waveRamWrite(unsigned index, unsigned data) {
void waveRamWrite(unsigned index, unsigned data, unsigned long cc) {
if (master_) {
if (!cgb_ && cycleCounter_ != lastReadTime_)
if (!cgb_ && cc != lastReadTime_)
return;
index = wavePos_ >> 1;
index = wavePos_ / 2;
}
waveRam_[index] = data;
@@ -83,7 +85,6 @@ private:
unsigned char waveRam_[0x10];
Ch3MasterDisabler disableMaster_;
LengthCounter lengthCounter_;
unsigned long cycleCounter_;
unsigned long soMask_;
unsigned long prevOut_;
unsigned long waveCounter_;
@@ -13,16 +13,22 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "channel4.h"
#include "psgdef.h"
#include "../savestate.h"
#include <algorithm>
static unsigned long toPeriod(unsigned const nr3) {
unsigned s = (nr3 >> 4) + 3;
unsigned r = nr3 & 7;
using namespace gambatte;
namespace {
unsigned long toPeriod(unsigned const nr3) {
unsigned s = nr3 / (1u * psg_nr43_s & -psg_nr43_s) + 3;
unsigned r = nr3 & psg_nr43_r;
if (!r) {
r = 1;
@@ -32,7 +38,7 @@ static unsigned long toPeriod(unsigned const nr3) {
return r << s;
}
namespace gambatte {
}
Channel4::Lfsr::Lfsr()
: backupCounter_(counter_disabled)
@@ -48,16 +54,16 @@ void Channel4::Lfsr::updateBackupCounter(unsigned long const cc) {
unsigned long periods = (cc - backupCounter_) / period + 1;
backupCounter_ += periods * period;
if (master_ && nr3_ < 0xE0) {
if (nr3_ & 8) {
if (master_ && nr3_ < 0xE * (1u * psg_nr43_s & -psg_nr43_s)) {
if (nr3_ & psg_nr43_7biten) {
while (periods > 6) {
unsigned const xored = (reg_ << 1 ^ reg_) & 0x7E;
reg_ = (reg_ >> 6 & ~0x7E) | xored | xored << 8;
reg_ = (reg_ >> 6 & ~0x7Eu) | xored | xored << 8;
periods -= 6;
}
unsigned const xored = ((reg_ ^ reg_ >> 1) << (7 - periods)) & 0x7F;
reg_ = (reg_ >> periods & ~(0x80 - (0x80 >> periods))) | xored | xored << 8;
reg_ = (reg_ >> periods & ~(0x80u - (0x80 >> periods))) | xored | xored << 8;
} else {
while (periods > 15) {
reg_ = reg_ ^ reg_ >> 1;
@@ -76,13 +82,13 @@ void Channel4::Lfsr::reviveCounter(unsigned long cc) {
}
inline void Channel4::Lfsr::event() {
if (nr3_ < 0xE0) {
if (nr3_ < 0xE * (1u * psg_nr43_s & -psg_nr43_s)) {
unsigned const shifted = reg_ >> 1;
unsigned const xored = (reg_ ^ shifted) & 1;
reg_ = shifted | xored << 14;
if (nr3_ & 8)
reg_ = (reg_ & ~0x40) | xored << 6;
if (nr3_ & psg_nr43_7biten)
reg_ = (reg_ & ~0x40u) | xored << 6;
}
counter_ += toPeriod(nr3_);
@@ -108,10 +114,15 @@ void Channel4::Lfsr::reset(unsigned long cc) {
backupCounter_ = cc + toPeriod(nr3_);
}
void Channel4::Lfsr::resetCounters(unsigned long oldCc) {
updateBackupCounter(oldCc);
backupCounter_ -= counter_max;
SoundUnit::resetCounters(oldCc);
void Channel4::Lfsr::resetCc(unsigned long cc, unsigned long newCc) {
updateBackupCounter(cc);
backupCounter_ -= cc - newCc;
if (counter_ != counter_disabled)
counter_ -= cc - newCc;
}
void Channel4::Lfsr::resetCounters(unsigned long cc) {
resetCc(cc, cc - counter_max);
}
void Channel4::Lfsr::saveState(SaveState &state, unsigned long cc) {
@@ -133,7 +144,6 @@ Channel4::Channel4()
, lengthCounter_(disableMaster_, 0x3F)
, envelopeUnit_(staticOutputTest_)
, nextEventUnit_(0)
, cycleCounter_(0)
, soMask_(0)
, prevOut_(0)
, nr4_(0)
@@ -148,55 +158,50 @@ void Channel4::setEvent() {
nextEventUnit_ = &lengthCounter_;
}
void Channel4::setNr1(unsigned data) {
lengthCounter_.nr1Change(data, nr4_, cycleCounter_);
void Channel4::setNr1(unsigned data, unsigned long cc) {
lengthCounter_.nr1Change(data, nr4_, cc);
setEvent();
}
void Channel4::setNr2(unsigned data) {
void Channel4::setNr2(unsigned data, unsigned long cc) {
if (envelopeUnit_.nr2Change(data))
disableMaster_();
else
staticOutputTest_(cycleCounter_);
staticOutputTest_(cc);
setEvent();
}
void Channel4::setNr4(unsigned const data) {
lengthCounter_.nr4Change(nr4_, data, cycleCounter_);
void Channel4::setNr4(unsigned const data, unsigned long const cc) {
lengthCounter_.nr4Change(nr4_, data, cc);
nr4_ = data;
if (data & 0x80) { // init-bit
nr4_ &= 0x7F;
master_ = !envelopeUnit_.nr4Init(cycleCounter_);
if (nr4_ & psg_nr4_init) {
nr4_ -= psg_nr4_init;
master_ = !envelopeUnit_.nr4Init(cc);
if (master_)
lfsr_.nr4Init(cycleCounter_);
lfsr_.nr4Init(cc);
staticOutputTest_(cycleCounter_);
staticOutputTest_(cc);
}
setEvent();
}
void Channel4::setSo(unsigned long soMask) {
void Channel4::setSo(unsigned long soMask, unsigned long cc) {
soMask_ = soMask;
staticOutputTest_(cycleCounter_);
staticOutputTest_(cc);
setEvent();
}
void Channel4::reset() {
// cycleCounter >> 12 & 7 represents the frame sequencer position.
cycleCounter_ &= 0xFFF;
cycleCounter_ += ~(cycleCounter_ + 2) << 1 & 0x1000;
lfsr_.reset(cycleCounter_);
void Channel4::reset(unsigned long cc) {
lfsr_.reset(cc);
envelopeUnit_.reset();
setEvent();
}
void Channel4::saveState(SaveState &state) {
lfsr_.saveState(state, cycleCounter_);
void Channel4::saveState(SaveState &state, unsigned long cc) {
lfsr_.saveState(state, cc);
envelopeUnit_.saveState(state.spu.ch4.env);
lengthCounter_.saveState(state.spu.ch4.lcounter);
@@ -207,54 +212,48 @@ void Channel4::saveState(SaveState &state) {
void Channel4::loadState(SaveState const &state) {
lfsr_.loadState(state);
envelopeUnit_.loadState(state.spu.ch4.env, state.mem.ioamhram.get()[0x121],
state.spu.cycleCounter);
state.spu.cycleCounter);
lengthCounter_.loadState(state.spu.ch4.lcounter, state.spu.cycleCounter);
cycleCounter_ = state.spu.cycleCounter;
nr4_ = state.spu.ch4.nr4;
master_ = state.spu.ch4.master;
}
void Channel4::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cycles) {
void Channel4::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cc, unsigned long const end) {
unsigned long const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0;
unsigned long const outLow = outBase * (0 - 15ul);
unsigned long const endCycles = cycleCounter_ + cycles;
unsigned long const outLow = outBase * -15;
for (;;) {
unsigned long const outHigh = outBase * (envelopeUnit_.getVolume() * 2 - 15ul);
unsigned long const nextMajorEvent = std::min(nextEventUnit_->counter(), endCycles);
while (cc < end) {
unsigned long const outHigh = outBase * (envelopeUnit_.getVolume() * 2l - 15);
unsigned long const nextMajorEvent = std::min(nextEventUnit_->counter(), end);
unsigned long out = lfsr_.isHighState() ? outHigh : outLow;
while (lfsr_.counter() <= nextMajorEvent) {
if (lfsr_.counter() <= nextMajorEvent) {
Lfsr lfsr = lfsr_;
while (lfsr.counter() <= nextMajorEvent) {
*buf += out - prevOut_;
prevOut_ = out;
buf += lfsr.counter() - cc;
cc = lfsr.counter();
lfsr.event();
out = lfsr.isHighState() ? outHigh : outLow;
}
lfsr_ = lfsr;
}
if (cc < nextMajorEvent) {
*buf += out - prevOut_;
prevOut_ = out;
buf += lfsr_.counter() - cycleCounter_;
cycleCounter_ = lfsr_.counter();
lfsr_.event();
out = lfsr_.isHighState() ? outHigh : outLow;
buf += nextMajorEvent - cc;
cc = nextMajorEvent;
}
if (cycleCounter_ < nextMajorEvent) {
*buf += out - prevOut_;
prevOut_ = out;
buf += nextMajorEvent - cycleCounter_;
cycleCounter_ = nextMajorEvent;
}
if (nextEventUnit_->counter() == nextMajorEvent) {
nextEventUnit_->event();
setEvent();
} else
break;
}
}
if (cycleCounter_ >= SoundUnit::counter_max) {
lengthCounter_.resetCounters(cycleCounter_);
lfsr_.resetCounters(cycleCounter_);
envelopeUnit_.resetCounters(cycleCounter_);
cycleCounter_ -= SoundUnit::counter_max;
if (cc >= SoundUnit::counter_max) {
lengthCounter_.resetCounters(cc);
lfsr_.resetCounters(cc);
envelopeUnit_.resetCounters(cc);
}
}
}
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef SOUND_CHANNEL4_H
@@ -32,15 +32,16 @@ struct SaveState;
class Channel4 {
public:
Channel4();
void setNr1(unsigned data);
void setNr2(unsigned data);
void setNr3(unsigned data) { lfsr_.nr3Change(data, cycleCounter_); }
void setNr4(unsigned data);
void setSo(unsigned long soMask);
void setNr1(unsigned data, unsigned long cc);
void setNr2(unsigned data, unsigned long cc);
void setNr3(unsigned data, unsigned long cc) { lfsr_.nr3Change(data, cc); }
void setNr4(unsigned data, unsigned long cc);
void setSo(unsigned long soMask, unsigned long cc);
bool isActive() const { return master_; }
void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
void reset();
void saveState(SaveState &state);
void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cc, unsigned long end);
void reset(unsigned long cc);
void resetCc(unsigned long cc, unsigned long newCc) { lfsr_.resetCc(cc, newCc); }
void saveState(SaveState &state, unsigned long cc);
void loadState(SaveState const &state);
private:
@@ -53,6 +54,7 @@ private:
void nr3Change(unsigned newNr3, unsigned long cc);
void nr4Init(unsigned long cc);
void reset(unsigned long cc);
void resetCc(unsigned long cc, unsigned long newCc);
void saveState(SaveState &state, unsigned long cc);
void loadState(SaveState const &state);
void disableMaster() { killCounter(); master_ = false; reg_ = 0x7FFF; }
@@ -85,7 +87,6 @@ private:
EnvelopeUnit envelopeUnit_;
Lfsr lfsr_;
SoundUnit *nextEventUnit_;
unsigned long cycleCounter_;
unsigned long soMask_;
unsigned long prevOut_;
unsigned char nr4_;
@@ -13,21 +13,29 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "duty_unit.h"
#include "psgdef.h"
#include <algorithm>
static inline bool toOutState(unsigned duty, unsigned pos) {
return 0x7EE18180 >> (duty * 8 + pos) & 1;
namespace {
int const duty_pattern_len = 8;
bool toOutState(unsigned duty, unsigned pos) {
return 0x7EE18180 >> (duty * duty_pattern_len + pos) & 1;
}
static inline unsigned toPeriod(unsigned freq) {
unsigned toPeriod(unsigned freq) {
return (2048 - freq) * 2;
}
namespace gambatte {
}
using namespace gambatte;
DutyUnit::DutyUnit()
: nextPosUpdate_(counter_disabled)
@@ -44,27 +52,26 @@ void DutyUnit::updatePos(unsigned long const cc) {
if (cc >= nextPosUpdate_) {
unsigned long const inc = (cc - nextPosUpdate_) / period_ + 1;
nextPosUpdate_ += period_ * inc;
pos_ += inc;
pos_ &= 7;
pos_ = (pos_ + inc) % duty_pattern_len;
high_ = toOutState(duty_, pos_);
}
}
void DutyUnit::setCounter() {
static unsigned char const nextStateDistance[4 * 8] = {
7, 6, 5, 4, 3, 2, 1, 1,
1, 6, 5, 4, 3, 2, 1, 2,
1, 4, 3, 2, 1, 4, 3, 2,
1, 6, 5, 4, 3, 2, 1, 2
static unsigned char const nextStateDistance[][duty_pattern_len] = {
{ 7, 6, 5, 4, 3, 2, 1, 1 },
{ 1, 6, 5, 4, 3, 2, 1, 2 },
{ 1, 4, 3, 2, 1, 4, 3, 2 },
{ 1, 6, 5, 4, 3, 2, 1, 2 }
};
if (enableEvents_ && nextPosUpdate_ != counter_disabled) {
unsigned const npos = (pos_ + 1) & 7;
unsigned const npos = (pos_ + 1) % duty_pattern_len;
counter_ = nextPosUpdate_;
inc_ = nextStateDistance[duty_ * 8 + npos];
inc_ = nextStateDistance[duty_][npos];
if (toOutState(duty_, npos) == high_) {
counter_ += period_ * inc_;
inc_ = nextStateDistance[duty_ * 8 + ((npos + inc_) & 7)];
inc_ = nextStateDistance[duty_][(npos + inc_) % duty_pattern_len];
}
} else
counter_ = counter_disabled;
@@ -77,16 +84,16 @@ void DutyUnit::setFreq(unsigned newFreq, unsigned long cc) {
}
void DutyUnit::event() {
static unsigned char const inc[] = {
1, 7,
2, 6,
4, 4,
6, 2,
static unsigned char const inc[][2] = {
{ 1, 7 },
{ 2, 6 },
{ 4, 4 },
{ 6, 2 }
};
high_ ^= true;
counter_ += inc_ * period_;
inc_ = inc[duty_ * 2 + high_];
inc_ = inc[duty_][high_];
}
void DutyUnit::nr1Change(unsigned newNr1, unsigned long cc) {
@@ -99,11 +106,11 @@ void DutyUnit::nr3Change(unsigned newNr3, unsigned long cc) {
setFreq((freq() & 0x700) | newNr3, cc);
}
void DutyUnit::nr4Change(unsigned const newNr4, unsigned long const cc) {
void DutyUnit::nr4Change(unsigned const newNr4, unsigned long const cc, unsigned long const ref) {
setFreq((newNr4 << 8 & 0x700) | (freq() & 0xFF), cc);
if (newNr4 & 0x80) {
nextPosUpdate_ = (cc & ~1ul) + period_ + 4;
if (newNr4 & psg_nr4_init) {
nextPosUpdate_ = cc - (cc - ref) % 2 + period_ + 4;
setCounter();
}
}
@@ -115,6 +122,15 @@ void DutyUnit::reset() {
setCounter();
}
void DutyUnit::resetCc(unsigned long cc, unsigned long newCc) {
if (nextPosUpdate_ == counter_disabled)
return;
updatePos(cc);
nextPosUpdate_ -= cc - newCc;
setCounter();
}
void DutyUnit::saveState(SaveState::SPU::Duty &dstate, unsigned long const cc) {
updatePos(cc);
setCounter();
@@ -127,7 +143,7 @@ void DutyUnit::saveState(SaveState::SPU::Duty &dstate, unsigned long const cc) {
void DutyUnit::loadState(SaveState::SPU::Duty const &dstate,
unsigned const nr1, unsigned const nr4, unsigned long const cc) {
nextPosUpdate_ = std::max(dstate.nextPosUpdate, cc);
pos_ = dstate.pos & 7;
pos_ = dstate.pos % duty_pattern_len;
high_ = dstate.high;
duty_ = nr1 >> 6;
period_ = toPeriod((nr4 << 8 & 0x700) | dstate.nr3);
@@ -135,13 +151,8 @@ void DutyUnit::loadState(SaveState::SPU::Duty const &dstate,
setCounter();
}
void DutyUnit::resetCounters(unsigned long const oldCc) {
if (nextPosUpdate_ == counter_disabled)
return;
updatePos(oldCc);
nextPosUpdate_ -= counter_max;
setCounter();
void DutyUnit::resetCounters(unsigned long cc) {
resetCc(cc, cc - counter_max);
}
void DutyUnit::killCounter() {
@@ -154,5 +165,3 @@ void DutyUnit::reviveCounter(unsigned long const cc) {
enableEvents_ = true;
setCounter();
}
}
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef DUTY_UNIT_H
@@ -33,8 +33,9 @@ public:
bool isHighState() const { return high_; }
void nr1Change(unsigned newNr1, unsigned long cc);
void nr3Change(unsigned newNr3, unsigned long cc);
void nr4Change(unsigned newNr4, unsigned long cc);
void nr4Change(unsigned newNr4, unsigned long cc, unsigned long ref);
void reset();
void resetCc(unsigned long cc, unsigned long newCc);
void saveState(SaveState::SPU::Duty &dstate, unsigned long cc);
void loadState(SaveState::SPU::Duty const &dstate, unsigned nr1, unsigned nr4, unsigned long cc);
void killCounter();
@@ -13,13 +13,15 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "envelope_unit.h"
#include "psgdef.h"
#include <algorithm>
namespace gambatte {
using namespace gambatte;
EnvelopeUnit::VolOnOffEvent EnvelopeUnit::nullEvent_;
@@ -46,16 +48,16 @@ void EnvelopeUnit::loadState(SaveState::SPU::Env const &estate, unsigned nr2, un
}
void EnvelopeUnit::event() {
unsigned long const period = nr2_ & 7;
unsigned long const period = nr2_ & psg_nr2_step;
if (period) {
unsigned newVol = volume_;
if (nr2_ & 8)
if (nr2_ & psg_nr2_inc)
++newVol;
else
--newVol;
if (newVol < 0x10U) {
if (newVol < 0x10) {
volume_ = newVol;
if (volume_ < 2)
volOnOffEvent_(counter_);
@@ -68,29 +70,27 @@ void EnvelopeUnit::event() {
}
bool EnvelopeUnit::nr2Change(unsigned const newNr2) {
if (!(nr2_ & 7) && counter_ != counter_disabled)
if (!(nr2_ & psg_nr2_step) && counter_ != counter_disabled)
++volume_;
else if (!(nr2_ & 8))
else if (!(nr2_ & psg_nr2_inc))
volume_ += 2;
if ((nr2_ ^ newNr2) & 8)
if ((nr2_ ^ newNr2) & psg_nr2_inc)
volume_ = 0x10 - volume_;
volume_ &= 0xF;
nr2_ = newNr2;
return !(newNr2 & 0xF8);
return !(newNr2 & (psg_nr2_initvol | psg_nr2_inc));
}
bool EnvelopeUnit::nr4Init(unsigned long const cc) {
unsigned long period = nr2_ & 7 ? nr2_ & 7 : 8;
unsigned long period = nr2_ & psg_nr2_step ? nr2_ & psg_nr2_step : 8;
if (((cc + 2) & 0x7000) == 0x0000)
++period;
counter_ = cc - ((cc - 0x1000) & 0x7FFF) + period * 0x8000;
volume_ = nr2_ >> 4;
return !(nr2_ & 0xF8);
}
volume_ = nr2_ / (1u * psg_nr2_initvol & -psg_nr2_initvol);
return !(nr2_ & (psg_nr2_initvol | psg_nr2_inc));
}
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef ENVELOPE_UNIT_H
@@ -13,14 +13,16 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "length_counter.h"
#include "master_disabler.h"
#include "psgdef.h"
#include <algorithm>
namespace gambatte {
using namespace gambatte;
LengthCounter::LengthCounter(MasterDisabler &disabler, unsigned const mask)
: disableMaster_(disabler)
@@ -38,35 +40,30 @@ void LengthCounter::event() {
void LengthCounter::nr1Change(unsigned const newNr1, unsigned const nr4, unsigned long const cc) {
lengthCounter_ = (~newNr1 & lengthMask_) + 1;
counter_ = nr4 & 0x40
? ((cc >> 13) + lengthCounter_) << 13
: static_cast<unsigned long>(counter_disabled);
counter_ = nr4 & psg_nr4_lcen
? ((cc >> 13) + lengthCounter_) << 13
: 1 * counter_disabled;
}
void LengthCounter::nr4Change(unsigned const oldNr4, unsigned const newNr4, unsigned long const cc) {
if (counter_ != counter_disabled)
lengthCounter_ = (counter_ >> 13) - (cc >> 13);
{
unsigned dec = 0;
if (newNr4 & 0x40) {
dec = ~cc >> 12 & 1;
if (!(oldNr4 & 0x40) && lengthCounter_) {
if (!(lengthCounter_ -= dec))
disableMaster_();
}
unsigned dec = 0;
if (newNr4 & psg_nr4_lcen) {
dec = ~cc >> 12 & 1;
if (!(oldNr4 & psg_nr4_lcen) && lengthCounter_) {
if (!(lengthCounter_ -= dec))
disableMaster_();
}
if ((newNr4 & 0x80) && !lengthCounter_)
lengthCounter_ = lengthMask_ + 1 - dec;
}
if ((newNr4 & 0x40) && lengthCounter_)
counter_ = ((cc >> 13) + lengthCounter_) << 13;
else
counter_ = counter_disabled;
if (newNr4 & psg_nr4_init && !lengthCounter_)
lengthCounter_ = lengthMask_ + 1 - dec;
counter_ = newNr4 & psg_nr4_lcen && lengthCounter_
? ((cc >> 13) + lengthCounter_) << 13
: 1 * counter_disabled;
}
void LengthCounter::saveState(SaveState::SPU::LCounter &lstate) const {
@@ -78,5 +75,3 @@ void LengthCounter::loadState(SaveState::SPU::LCounter const &lstate, unsigned l
counter_ = std::max(lstate.counter, cc);
lengthCounter_ = lstate.lengthCounter;
}
}
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef LENGTH_COUNTER_H
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef MASTER_DISABLER_H
+31
View File
@@ -0,0 +1,31 @@
#ifndef PSGDEF_H
#define PSGDEF_H
namespace gambatte {
enum {
psg_nr10_rsh = 0x07,
psg_nr10_neg = 0x08,
psg_nr10_time = 0x70
};
enum {
psg_nr2_step = 0x07,
psg_nr2_inc = 0x08,
psg_nr2_initvol = 0xF0
};
enum {
psg_nr43_r = 0x07,
psg_nr43_7biten = 0x08,
psg_nr43_s = 0xF0
};
enum {
psg_nr4_lcen = 0x40,
psg_nr4_init = 0x80
};
}
#endif
@@ -13,7 +13,7 @@
// 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.
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef SOUND_UNIT_H

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