Compare commits

...

365 Commits
v105 ... v114

Author SHA1 Message Date
byuu
55e78b03de Point to specific project. 2020-01-08 18:50:07 +09:00
byuu
47dcdc1b4f Added Windows binary release link. 2020-01-08 18:49:40 +09:00
byuu
e13ab011eb v114
Added fast PPU override for Marvelous (fixes text rendering)
Fixed disassembly of SNES CPU opcodes 0x74-0x76 [invertego]
2020-01-08 18:46:53 +09:00
byuu
892f202945 Updated SNES game databases 2020-01-01 18:45:11 +09:00
byuu
e575196abc . 2019-12-31 19:42:11 +09:00
byuu
404caeab50 The input workaround for Taikyoku Igo benefits:
Williams Arcade's Greatest Hits and World Masters Golf;
in very subtle ways, so enable it for those two titles as well.
2019-12-31 19:40:35 +09:00
byuu
dde9b4c2c7 v113.5
It seems auto-joypad poll timing is needed for most games.
So that's back in as before. Instead, I added an override for
Taikyoku Igo - Goliath specifically, until auto-joypad emulation
can be improved further.
2019-12-31 10:22:31 +09:00
byuu
793f2e5bf4 v113.4
Completely disabled auto-joypad timing (happens immediately)
(fixes World Masters until this can be emulated fully)
Disabled fast PPU for Winter Olympic Games
(changes OAM tiledata address mid-frame)
Disabled fast PPU for World Cup Striker
(I'm not sure yet why it's not compatible)
Cleared overscan region when disabling overscan
(fixes World Class Service SNES Tester)
Added override for invalid SNES header in Yuyu no Quiz de Go! Go!
2019-12-30 06:00:17 +09:00
byuu
cc4ab9bc25 Added workaround to reduce auto-joypad polling delays
(until we can emulate the behavior more faithfully)
2019-12-28 13:53:06 +09:00
byuu
2551f20f3a v113.3
Fixed region heuristics for the one Scandanavian SNES game release
2019-12-28 13:41:57 +09:00
byuu
5b29ddbcaa Add hotfix for Nichibutsu Arcade Classics (Japan)
* Frisky Tom hangs sometimes when memory is randomized
2019-12-27 08:58:09 +09:00
byuu
ac4d16c917 Revert gamepak firmware naming to use architecture instead of identifier
* identifier naming interferes with game ROM naming lookup
2019-12-27 08:51:05 +09:00
byuu
01c16dcf4d Fix Taikyoku Igo - Goliath
(clear $4218-421f to 1s instead of 0s at start of auto-joypad polling)
2019-12-27 08:32:18 +09:00
byuu
169c0871c7 Added Super SWIV fast PPU override 2019-12-27 08:17:39 +09:00
byuu
ffee61a1b1 Merge pull request #250 from Sintendo/xcode11-opengl-fix
ruby/CGL: explicitly set current OpenGL context
2019-12-24 18:51:01 +09:00
Sintendo
526df86ee6 ruby/CGL: explicitly set current OpenGL context
On builds made with Xcode 11+ the current OpenGL context wasn't being
properly configured anymore, resulting in shader compilation errors and
a red screen.

Explicitly calling makeCurrentContext fixes this.
2019-12-23 22:30:58 +01:00
byuu
748cf44f35 Added run-ahead support to libretro target [realnc]
Fixed typo in the GUI regarding HD mode 7 +/- hotkeys
2019-12-19 22:04:14 +09:00
byuu
a4f96f0648 Fix link. 2019-12-19 21:16:53 +09:00
byuu
2ca1bab9ed Updated links in readme file. 2019-12-19 21:16:06 +09:00
byuu
1e6a745f19 v113.1
Emergency hotfix for an issue affecting manually created save states.
Still need to determine root cause, but for now, reverting the code.
2019-12-16 01:59:39 +09:00
byuu
90b1350110 v113 2019-12-11 22:02:23 +09:00
byuu
357d054c19 Fix LLE gamepak firmware name lookups (use identifier, not architecture) 2019-12-11 21:56:29 +09:00
byuu
d62e3f3362 v112.14
Finally corrected Super Game Boy 2 audio [LIJI]
2019-12-11 21:40:07 +09:00
byuu
4ec45a7453 Some more testing for Super Game Boy audio mixing. 2019-12-09 09:23:33 +09:00
byuu
f5d40bd1ee Testing: added Super Game Boy audio mixing test function. 2019-12-09 00:08:37 +09:00
byuu
6aa7c944d5 v112.3
Improvements to ruby driver crash detection.
Workaround added for rare crash on close on Windows.
2019-12-08 01:39:46 +09:00
byuu
dafd673177 v112.12
Update to SameBoy-master [2019-12-02]
2019-12-02 20:22:51 +09:00
byuu
a64c1adaa8 v112.11
SFC: Disable color blending for first hires pixel with accuracy PPU
(fixes a green scanline on the left-edge of Jurassic Park)
libco: Don't include <sys/mman.h> when not using mprotect
nall: Detect Windows without invoking uname [Alcaro]
2019-12-02 19:54:03 +09:00
byuu
0d1d6f329d Scanline PPU render position override for Suguro Quest++ 2019-11-12 23:17:25 +09:00
byuu
7cd897b53b . 2019-11-10 10:18:24 +09:00
byuu
011f470b07 Add images to readme 2019-11-10 10:17:42 +09:00
byuu
6edad01fb8 . 2019-11-10 10:05:52 +09:00
byuu
3ecea80ecb v112.10
Fix accuracy PPU mosaic rendering when size!=0 && enable==0
2019-11-10 10:01:13 +09:00
byuu
b7b848eff5 Fix audio balance below 50% 2019-11-10 09:37:56 +09:00
byuu
da7350ac5c Rename functions for consistency. 2019-11-08 16:12:33 +09:00
byuu
ba3fca27ad Fix GUI typos. 2019-11-08 16:00:27 +09:00
byuu
5775155714 v112.9
CPU IRQ improvement to fix Shin Nihon Pro Wrestling Kouhin '95
2019-11-08 15:56:27 +09:00
byuu
f1108408a8 Updated libretro resources file again 2019-11-05 09:01:54 +09:00
byuu
996358da66 v112.8
Made the main window canvas area (program icon) droppable for games
Merged the latest SameBoy core, but disabled it due to an input problem
2019-11-05 08:58:59 +09:00
byuu
c717a0e7bd Mapping fix for RPG Tsukuru 2 2019-11-01 06:26:24 +09:00
byuu
2884cd87d2 v112.7
Added BSC-1A7M-10 board
Corrected BSC-1AxM-xx masking
2019-11-01 05:36:02 +09:00
byuu
454b90be24 v112.6
Fix for Kishin Douji Zenki - Tenchi Meidou
2019-10-31 10:56:16 +09:00
byuu
2b9a22e1d8 Merge IOKit hotplug support patch [Sintendo]
Merge libretro Super Game Boy support improvement patch [fr500]
2019-10-31 09:19:27 +09:00
byuu
1c1cfd086b v112.5
Added game hotfix for Rendering Ranger R2.
2019-10-31 09:13:37 +09:00
byuu
f2978247c1 v112.4
Reverted Kishin Douji Zenki fix, as it seems to have been incorrect.
Disabled supersampling when EXTBG mode is active.
Fixed MSU1 and SGB audio when using run-ahead and overclocking.
macOS: fixed a serious issue with the IOKit joypad driver [kode54]
2019-10-27 13:13:59 +09:00
byuu
4f32551430 . 2019-10-27 01:31:33 +09:00
byuu
c61c3cabc6 . 2019-10-27 01:27:36 +09:00
byuu
819d6dbde4 v112.3
Fixed offset-per-tile regression with accurate PPU renderer.
2019-10-27 00:51:15 +09:00
byuu
f51bc06739 v112.2
Temporarily disabled crash detector to work around Windows issue.
Corrected PPU OAM address latching with the accuracy PPU.
2019-10-26 23:34:24 +09:00
byuu
4f09a3873d v112.1
Add SA1 generic board mapping without RAM.
2019-10-22 12:35:14 +09:00
byuu
55bfe402e7 v112 2019-10-20 02:18:37 +09:00
byuu
30d7fa1923 v111.10
Fixed deterministic serialization on Windows.
2019-10-17 21:42:42 +09:00
byuu
9f86a3be26 v111.9
Cleanups.
2019-10-16 16:17:56 +09:00
byuu
6b7e6e01bb v111.8
Serialize SDD1 decompressor
Major speedup to nall/serializer [Alcaro]
Removed fast PPU tile cache (major speedup for run-ahead mode)
2019-10-16 16:12:28 +09:00
byuu
53f8de6ac3 Merge pull request #174 from Alcaro/master
Optimize serialization stuff a bit
2019-10-16 09:26:50 +09:00
Alcaro
cd18cdb1d6 Optimize serialization stuff a bit 2019-10-15 20:29:04 +02:00
byuu
6cb7d89d64 Update features. 2019-10-15 22:27:18 +09:00
byuu
19f3cdfd5e v111.8
Added fully working deterministic save state support (non-portable.)
Rewind is now 100% deterministic, disk save states are still portable.
Added run-ahead support.
2019-10-15 22:12:10 +09:00
byuu
a32b6fae74 Minor syntax edit. 2019-10-14 23:47:17 +09:00
byuu
03a6e1c7de Added CONTRIBUTING.md 2019-10-14 23:46:21 +09:00
byuu
6b34f134bf More libretro changes. 2019-10-14 23:16:25 +09:00
byuu
2de906ea46 v111.7
Added System/Serialization/Synchronize setting to settings.bml
This option is for experimental deterministic rewind support.
It does not currently work with SuperFX and SA-1 games,
and as such is set to true (force synchronize) for now.
2019-10-14 23:04:38 +09:00
byuu
95addddc46 v111.6
Added support for multiple serialization methods.
Revert to the fast method for games by default.
Default Tales of Phantasia and Star Ocean to the new strict method.
Added new Synchronization/Method settings file override.
Added new pseudo-fullscreen hotkey toggle by request.
Added new preset settings buttons to the driver settings panel.
Merged Super Game Boy support for the libretro target [rtretiakov]
2019-10-13 23:44:53 +09:00
byuu
45e9e0f0ea Language detection fix attempt 1. 2019-10-12 15:47:34 +09:00
byuu
0aea7fd5c5 NHL '94 (Japan) scanline override for fast PPU 2019-10-12 14:38:09 +09:00
byuu
fb95d5b59f v111.5
Updated frame advance to run after first advance when paused previously.
Moved frame events into the CPU core to prevent PPU<>NMI race condition.
Credit to r5 for pointing out there being an issue during frame advance.
2019-10-12 14:28:03 +09:00
byuu
3d646aef73 Added SHVC-2P3B-01 2019-10-10 11:34:45 +09:00
byuu
3fb7ff6bfe v110.4
Save state improvements.
2019-10-09 00:48:59 +09:00
byuu
d8bc2050be v111.4
Serialization improvements.
2019-10-09 00:45:57 +09:00
byuu
e71da4d8c8 Fix detection of ST010 HLE mode when firmware is missing.
Fix display of ST011 missing firmware message.
2019-10-07 16:03:53 +09:00
byuu
e78aca34b9 v111.3
Save state improvements: rewind should be fully stable now.
Before, Star Ocean and Tales of Phantasia would rarely hang with rewind.
2019-10-07 13:32:21 +09:00
byuu
0c82cc325e v111.2
Two sprite fixes for the accurate PPU and Star Ocean.
2019-10-06 18:11:53 +09:00
byuu
78c76962ec v111.1 2019-10-06 10:45:03 +09:00
byuu
e22167cf82 v111.1
More improvements to SameBoy audio interface for Super Game Boy.
Added a fix for a very rare crashing issue with SDL 2.0 joypad support.
2019-10-06 10:14:30 +09:00
byuu
1698533774 v111 2019-10-05 15:09:50 +09:00
byuu
7b66e1c531 libretro Makefile improvements [orbea] 2019-10-05 14:34:09 +09:00
byuu
c6f92b782c Minor touchups. 2019-10-05 14:30:55 +09:00
byuu
e3f2e634c8 Fixed audio crackling in Super Game Boy emulation. 2019-10-05 14:29:31 +09:00
byuu
e598e81ab9 Add compatibility fixes to libretro target. 2019-10-05 13:48:35 +09:00
byuu
d37fb1c12e Added 17 new pixel shaders courtesy of hunterk porting and testing them. 2019-10-05 13:44:51 +09:00
byuu
eaf33cb078 Fix issue with bsnes not remembering user-selected audio frequency. 2019-10-05 13:37:04 +09:00
byuu
07427e4697 v110.7
Improvements to HDMA timing (courtesy of test ROM from undisbeliever]
This fixes flickering in Full Throttle - All-American Racing
2019-10-05 13:24:14 +09:00
byuu
b5301b7ea8 v110.6
Revert AVX2 (see pull request for details)
2019-10-05 10:34:32 +09:00
byuu
57c53a86b4 v110.5
Merged Alcaro's AVX2 mode 7 renderer.
Enable by editing mode7hd.cpp: USE_AVX2=1
2019-10-02 09:56:13 +09:00
byuu
f19f31938b Allow 2 megabit SRAM for SA-1 homebrew. 2019-10-01 06:56:50 +09:00
byuu
4efee7e9f1 Hotfix for Magical Drop (Japan)'s "tokoton mode" 2019-10-01 06:51:17 +09:00
byuu
3701236ca0 v107.4
Shrink Pixel struct for ~1.75% speedup [Alcaro]
2019-10-01 06:41:38 +09:00
byuu
2f684caa7c Merge pull request #132 from Alcaro/pixel4
Shrink struct SuperFamicom::PPUFast::Pixel from 12 to 4 bytes
2019-10-01 06:41:00 +09:00
Alcaro
3a064fc5a3 Shrink struct SuperFamicom::PPUFast::Pixel from 12 to 4 bytes
This gives approximately 3% speedup (118->122fps) on the F-Zero title screen
2019-09-29 13:02:20 +02:00
byuu
5a4b667eae v110.3
Added hotfix for bug in "The Hurricanes" (happens on a real SNES)
Added scanline override for NHL '94
Added fix for entering folder names into BrowserDialog filename box
2019-09-27 10:26:55 +09:00
byuu
62729df2d1 Build fixes [Screwtape] 2019-09-26 03:38:01 +09:00
byuu
1ef227f482 v110.2
Added CRT-Royale [hunterk]
Improved libretro target [rtretiakov]
2019-09-25 15:13:12 +09:00
byuu
6e5542aa20 v110.1
Fixed region detection issue in Hanguk Pro Yagu
Fixed boot hanging issue in Kishin Douji Zenki - Tenchi Meidou
Fixed slowdown issue in Mega Man X2 & X3
Added mute hotkey
Added HD mode 7 hotkeys (likely temporary, we'll see)
2019-09-23 09:50:40 +09:00
byuu
675662e739 v110
Corrections for IOKit joypad driver [Sintendo]
2019-09-21 04:59:29 +09:00
byuu
409dd371b9 v109.5
Added SHVC-4PV5B-01 prototype PCB to database.
Added Firepower 2000 fast PPU render cycle override.
Backported higan's newer accuracy PPU with sprite caching support.
2019-09-21 04:26:27 +09:00
byuu
18d2ab6435 v109.4
Rename hiro::Property to hiro::Attribute
Disable XChaCha20 CSPRNG on Android for now due to compilation issues
Add macOS IOKit joypad support [Sintendo]
2019-09-17 03:37:03 +09:00
byuu
1e626e75ef v109.3
Fixed crash when idling with the snow effect enabled.
Added Android target to libretro port [rtretiakov]
Various nall library improvements.
2019-09-13 22:15:11 +09:00
byuu
c6d90d3ff1 Dyslexia. 2019-09-10 22:35:30 +09:00
byuu
29caf77751 v109.2
Fixed alt-key menu activation on Windows.
Removed 2160p HD mode 7 due to Direct3D limit of 2048x2048 textures.
Reverted to safe ruby drivers when no configuration file is present.
Removed ASIO driver because nobody is interested in improving it.
Added macOS libretro target [rtretiakov]
2019-09-10 22:32:33 +09:00
byuu
29b13083d5 . 2019-09-07 18:22:55 +09:00
byuu
3883172a4e v109.1
Mask A23 for ExLoROM board mappings (fixes Thracia 776 fan translation)
2019-09-07 18:03:12 +09:00
byuu
fa77fc6a8f v109
Typo fix.
2019-09-06 23:54:58 +09:00
byuu
56c9a5195e v109
Keep focus on the panel list when changing settings / tools panels.
Fixed Windows combo box flickering when changing panels.
Suppress Alt+F4 on Windows in fullscreen mode.
2019-09-06 23:19:44 +09:00
byuu
a6ebce428f Minor cleanups. 2019-09-06 12:33:18 +09:00
byuu
0788627898 Minor cleanups. 2019-09-06 12:30:54 +09:00
byuu
1195c46ac0 v108.17
Enhanced perspective correction support [DerKoun]
2019-09-03 18:50:46 +09:00
byuu
90f094b931 Fix SGB JOYP incrementing behavior [endrift]
Fix GB JOYP read setting d6+d7 in SameBoy
Improve headered IPS patch handling
2019-09-03 17:55:54 +09:00
byuu
2bb1606552 v108.16
Added compatibility option to disable accurate CPU ALU emulation
Refactored settings panels
Added dialog to choose whether IPS patches are for headered ROMs
Disabled extended SNES header decoding thanks to ROM hacks ignoring them
2019-09-03 12:01:45 +09:00
byuu
08e5e81609 v108.15
Add "No VRAM Blocking" and "Echo Shadow RAM" options:
These allow compatibility with very old ROM hacks that
only previously ran in ZSNES and earlier Snes9X releases.
2019-09-02 13:51:04 +09:00
byuu
03aaaba889 Minor SGB change. 2019-09-02 11:03:14 +09:00
byuu
556ab4c809 Added a fix for a programming bug in Dirt Racer (Europe)
The game relies on uninitialized memory containing certain values.
The game will periodically freeze at boot even on real hardware.
This commit adds a workaround to allow the game to always boot.
2019-09-02 10:53:53 +09:00
byuu
23467b5b1f Merged libretro target [Themaister and rtretiakov] 2019-09-02 10:19:18 +09:00
byuu
5ae1bcd973 Force disable entropy when recording movies from reset.
This prevents potential desyncs in games that don't initialize RAM/IO.
2019-08-31 09:27:14 +09:00
byuu
64e3658bcb Minor code restructuring. 2019-08-31 09:13:16 +09:00
byuu
ab515b59d4 Crayon Shin-chan - Arashi o Yobu Enji fix 2019-08-31 08:14:52 +09:00
byuu
bb7b2f2e60 Added entropy setting (none, low, high) to settings->emulator 2019-08-31 07:08:10 +09:00
byuu
639b9db961 v108.14
* extremely pedantic mosaic improvement for the fast PPU
2019-08-31 06:38:24 +09:00
byuu
f857f35e72 Fix Super Mario RPG regression. 2019-08-26 02:46:08 +09:00
byuu
5dc27a9fb3 Bubsy II (PAL) fix. 2019-08-26 01:37:24 +09:00
byuu
ce3dba130c v108.13
* fix CPU DMA regression from higan v106.62 (fixes Battle Grand Prix)
2019-08-25 01:13:19 +09:00
byuu
f9ca7a4927 Fix an IRQ regression from a few releases back.
Add fast PPU render cycle position setting.
2019-08-24 01:23:18 +09:00
byuu
db1c37c799 v108.12
* fix sprite index mask (Addams Family bugfix)
* fix exclusive mode in Direct3D driver
2019-08-24 08:34:17 +09:00
byuu
7a98db84ac Fix SGB Killer Instinct MLT_REQ to start with player 1 2019-08-22 01:02:34 +09:00
byuu
b73493b492 Fix for Great Battle IV background graphics. 2019-08-22 00:50:59 +09:00
byuu
95831d3675 v108.11
* added (primary-monitor only) fullscreen support for macOS
* improved settings windows a bit
* correct Program::focused() move from Video::exclusive()->fullScreen()
2019-08-18 03:20:14 +09:00
byuu
ab25877af4 macOS fixes. 2019-08-17 23:53:21 +09:00
byuu
5ba538ee39 v108.10
* provide actual display device names in ruby::Video::hasMonitors()
* macOS: fixed LineEdit::onChange (fixes state manager state naming)
* macOS: fixed RadioLabel height to not clip off bottom of radio icons
* macOS: fixed RadioLabel initial check states (fixed input settings
focus mode options)
* macOS: fixed TableViewItem::setSelected (fixes initial selection on
settings/tools windows)
* macOS: fixed TextEdit geometry (fixed manifest viewer)
* macOS: don't allow multiple TableViewItems to be selected in
single-selection mode
** (fixes settings/tools windows selecting multiple items via menubar
options)
2019-08-17 23:41:44 +09:00
byuu
04b85ade6b Add funding icon. 2019-08-16 19:49:48 +09:00
byuu
0b088b6b55 v108.9
* multi-monitor support
* improved pause/frame advance support
* added option to disable video dimming when idle
2019-08-16 19:44:16 +09:00
byuu
252f479b22 v108.8
* added deinterlacing option
2019-08-12 23:12:34 +09:00
byuu
46dbe00f14 Add dismissable warning when using nightlies. 2019-08-07 10:18:26 +09:00
byuu
66ad62b79f Fix refactoring regression and simplify dot PPU mode 7 code. 2019-08-07 10:04:31 +09:00
byuu
27b2d11839 Small fix. 2019-08-06 13:51:22 +09:00
byuu
96c381f91f v108.7
* removed emulator/bits.hpp dependency
2019-08-06 13:45:04 +09:00
byuu
5757949023 v108.6
* fix IRQ regression in Power Rangers: Fighting Edition
2019-08-06 02:48:28 +09:00
byuu
bad27bb8f3 . 2019-08-05 11:09:57 +09:00
byuu
06ceb7d29e Minor speedups for SuperFX and Cx4 emulation. 2019-08-05 11:08:25 +09:00
byuu
e030428054 v108.5
* double-click a cheat finder result to add a new cheat code
* fixed v108.1 regression not enabling coprocessor LLE when requested
* add "[HLE] " title bar indicator for HLE mode
* default to LLE mode for coprocessors
* simplify game titles in main window (eg omit SGB BIOS name)
* add more GUI tooltips to explain options
* pause emulator during modal loops (helps Windows menubar navigation)
* add support for decoding Game Genie + Pro Action Replay SNES cheats
* add support for decoding Game Genie + GameShark Game Boy cheats
* add tool-tip explanation to verified/unverified status bar icon
2019-08-05 09:27:51 +09:00
byuu
24dce7dd92 Remove CPU::boot() 2019-08-02 11:25:59 +09:00
byuu
b577e6d5d0 Simplifications to CPU interrupt handling. 2019-08-02 11:23:31 +09:00
byuu
db988d9588 Improvements to muting and snow. 2019-08-02 06:35:35 +09:00
byuu
f6303518d5 v108.4
* ~4.6% speedup (with the fast PPU)
* fix out-of-bounds DSP memory access [Sour]
2019-08-02 04:45:06 +09:00
byuu
9e8913cea0 Remove icarus, merge heuristics into bsnes. 2019-08-01 09:05:01 +09:00
byuu
0e56b27228 Okay maybe not all of them, but close to all of them ... 2019-08-01 08:54:40 +09:00
byuu
fe81130f54 Remove all template integer types from fast PPU core. 2019-08-01 08:49:18 +09:00
byuu
216b472418 Cirrus CI update, Cocoa compilation fix (hopefully.) 2019-08-01 03:15:52 +09:00
byuu
bc7456246c v108.3
* add support for TableView::onActivate(TableViewCell) to Windows, macOS
** allows the new input/hotkey mapping panels to work on Windows, macOS
* polish BrowserDialog behavior
2019-08-01 02:40:35 +09:00
byuu
a7b30b069c Fix blur emulation in the accurate PPU mode. 2019-08-01 01:10:27 +09:00
byuu
454b39cb78 v108.2
* reverted higan's thread scheduler to olde bsnes scheduler
** this allows CPU overclocking compatibility with coprocessors
2019-08-01 00:33:28 +09:00
byuu
f65b7a8528 v108.1
* added CPU and SA1 overclocking support
* added fast forward speed limiting
* added option to mute during fast forwarding and rewinding
* lowered volume when not muting during FF/rewind
* reformatted settings/tools windows from tabs to lists
* moved focus settings to input settings panel
* redesigned input and hotkey settings panels to be easier to use
* fixed offscreen placement issue with path settings panel
* added hotkey combinational logic option (AND / OR mode setting)
* added search support to file browser dialog
* fixed --fullscreen command-line option
2019-07-31 06:57:31 +09:00
byuu
7e88bdde09 Cirrus CI updates. 2019-07-29 04:01:54 +09:00
byuu
4291a0cae6 v108 release. 2019-07-29 01:30:07 +09:00
byuu
ee6498258f v107.19
* HD mode 7 EXTBG fix
* macOS: embed databases into .app bundle
2019-07-29 00:59:45 +09:00
byuu
296f2c094d v107.18
* fix EXLOROM detection
* fix new CPU masking error
2019-07-28 06:44:38 +09:00
byuu
6b284bb247 Minor cleanups. 2019-07-28 06:13:48 +09:00
byuu
e6d7df41da v107.17
* initialize ppu-fast tilecaches on startup
** (fixes graphical residue on Windows)
2019-07-28 01:44:59 +09:00
byuu
c77ecef3e0 SameBoy cross-compilation fix for Windows. 2019-07-28 00:45:26 +09:00
byuu
a89acc2695 Updated readme. 2019-07-28 00:44:06 +09:00
byuu
45df47f08b Add Cirrus CI support. 2019-07-28 00:39:02 +09:00
byuu
ff0fa9bb19 Fix SA1 typo. 2019-07-27 01:50:01 +09:00
byuu
4d2244ed5f More macOS Cocoa improvements. 2019-07-27 01:27:56 +09:00
byuu
cdf4784468 macOS Cocoa improvements. 2019-07-27 00:52:29 +09:00
byuu
3934be89ff . 2019-07-26 02:15:45 +09:00
byuu
2f67beeba9 v107.15
* added Super Game Boy save RAM/RTC support
2019-07-26 01:19:47 +09:00
byuu
c2a181dbc5 v107.14
* fix support for light guns (Super Scope, Justifier(s))
2019-07-26 00:44:36 +09:00
byuu
42e5bcc604 SameBoy Windows compilation fix that doesn't modify upstream. 2019-07-25 22:48:13 +09:00
byuu
ea75d1bc7c SameBoy Windows getline() compilation fix. 2019-07-25 22:41:16 +09:00
byuu
73f0b7bb41 Add _GNU_SOURCE to SameBoy/gb.c 2019-07-25 05:01:10 +09:00
byuu
b637ae34fe v107.13
* libco: added ppc64v2 implementation [Shawn Anastasio]
2019-07-24 22:06:55 +09:00
byuu
30c606fdc8 About screen improvements. 2019-07-23 03:01:29 +09:00
byuu
b4c4b318e8 Ease of use improvements to rewind support. 2019-07-23 02:53:54 +09:00
byuu
32e2abdd90 v107.12
* added movie recording and playback support
* added rewind support
2019-07-23 02:13:28 +09:00
byuu
2e5f6c56c6 Minor changes.
* previous push also corrected screenshot support.
* == -> = for clarity.
* added cheat search to features list.
2019-07-22 01:51:29 +09:00
byuu
55799c4230 v107.11
* added cheat code support to Super Game Boy emulation
* added cheat code search support (not for Game Boy, sorry)
* compilation fixes
2019-07-22 01:39:32 +09:00
byuu
78a6a2e7d7 v107.10
* improved Super Game Boy emulation and fixed SGB save states [LIJI32]
2019-07-20 21:28:55 +09:00
byuu
f3022fd907 Merge pull request #9 from LIJI32/serialization_fix
Fix SGB serialization
2019-07-20 21:22:31 +09:00
byuu
e00fa027aa . 2019-07-20 18:57:01 +09:00
Lior Halphon
a0671bc8a9 Fix SGB save states 2019-07-19 19:57:11 +03:00
Lior Halphon
32c781c531 Accuracy fixed, major regression fix 2019-07-19 18:19:53 +03:00
Lior Halphon
10038ec76d Emulate ICD desyncing 2019-07-18 23:08:16 +03:00
byuu
0623d6ac2b v107.9
* started removing nall-specific components from the fast PPU renderer
* corrected compilation issues with Super Game Boy support
2019-07-19 00:44:09 +09:00
byuu
14d87f6bf3 Readme updates. 2019-07-18 19:39:22 +09:00
byuu
601b749e50 Compile-time corrections for SameBoy and hiro/cocoa. 2019-07-18 19:34:23 +09:00
byuu
0aa13bd44b Updated license copyright dates. 2019-07-17 21:43:18 +09:00
byuu
c343b0c08f Updated license copyright dates. 2019-07-17 21:37:43 +09:00
byuu
9297e0a238 Updated license copyright dates. 2019-07-17 22:31:51 +10:00
byuu
903d1e4012 v107.8
* GB: integrated SameBoy v0.12.1 by Lior Halphon
* SFC: added HG51B169 (Cx4) math tables into bsnes binary
2019-07-17 21:11:46 +09:00
Tim Allen
382e192647 I've just noticed bsnes doesn't need a top-level firmware directory.
The firmware is in icarus/Firmware/ these days.
2019-07-17 20:59:04 +10:00
Tim Allen
d19fe54c64 bsnes now has a separate, official repo on GitHub. Update the docs. 2019-07-17 13:50:26 +10:00
byuu
1979df20ad Merge pull request #1 from PeterLemon/master
Fix libco aarch64 typo
2019-07-17 09:54:20 +09:00
peterlemon
7c7421c951 Fix libco aarch64 typo 2019-07-16 20:12:41 +01:00
byuu
244cecd8f4 Update README.md 2019-07-16 23:01:04 +09:00
byuu
c6465ae101 Update README.md 2019-07-16 23:00:32 +09:00
Tim Allen
bd8e94a7c7 Update to bsnes v107r7 beta release.
byuu says:

  - bsnes: reset all thread clocks on power cycle
  - bsnes: use uint64 instead of uint128 for scheduler clocks
  - bsnes: use float instead of double for audio resampling
  - bsnes: begin work of integrating SameBoy (incomplete; needs
    additional features)
2019-07-15 21:02:13 +10:00
Tim Allen
52f34ea470 Update to bsnes v107r6 beta release.
byuu says:

  - converted (u)int(8,16,32,64) from Natural/Integer<T> to
    (u)int(8,16,32,64)_t types
  - SFC: mostly rewritten WDC65816 CPU core
  - removed 487KiB of code! (unused CPU cores from other higan cores)
2019-07-12 18:48:24 +10:00
Tim Allen
a03d91882c Update to bsnes v107r5 beta release.
byuu says:

  - bsnes: allow video filtering even when the emulator is paused
  - bsnes: improve overscan masking, especially with HD mode 7
  - bsnes: improve snow support, especially with HD mode 7
  - bsnes: replace real-time cheat code replace with per-frame replace
    (ala Pro Action Replay, Snes9X)
  - bsnes: treat the latter step() half of CPU::read() calls as idle
    cycles
  - bsnes: templatize step() where possible (not always practical)
  - bsnes: removed Natural<T> templates from key portions of the fast
    PPU renderer
  - bsnes: dethreaded peripherals (controllers and expansion port
    devices)
  - bsnes: above optimizations result in a ~20-25% speedup over v107.4
    with no accuracy loss

Note that light guns aren't going to work for now, I'll have to fix them
before we can release v108.
2019-07-10 17:33:13 +10:00
Tim Allen
d87a0f633d Update to bsnes v107r4 beta release.
byuu says:

  - bsnes: added video filters from bsnes v082
  - bsnes: added ZSNES snow effect option when games paused or unloaded
    (no, I'm not joking)
  - bsnes: added 7-zip support (LZMA 19.00 SDK)

[Recent higan WIPs have also mentioned bsnes changes, although the higan code
no longer includes the bsnes code. These changes include:

  - higan, bsnes: added EXLOROM, EXLOROM-RAM, EXHIROM mappings
  - higan, bsnes: focus the viewport after leaving fullscreen exclusive
    mode
  - bsnes: re-added mightymo's cheat code database
  - bsnes: improved make install rules for the game and cheat code
    databases
  - bsnes: delayed construction of hiro::Window objects to properly show
    bsnes window icons

- Ed.]
2019-07-07 19:44:09 +10:00
Tim Allen
becbca47d4 Update to bsnes v107r3 beta release.
byuu says:

Changes: HD mode 7 supersampling support, HD mode 7 mosaic disable option,
various HD mode 7 bugfixes, default waveOut audio latency to 128 instead of 512,
removed 512x240 hires mode 7 mode.

There's also a small experiment, making this release a beta release as well:
for a large speedup, when in EXTBG mode, I'm bypassing rendering BG1 for a
performance boost. EXTBG is only used as a priority layer, and is overwritten by
BG2 except in one extremely pathological case.
2019-04-19 18:02:31 +10:00
Tim Allen
7a548482ed Update in-repo docs since this is now "bsnes" not "higan". 2019-04-18 18:14:01 +10:00
Tim Allen
30ed7f7e0b Remove higan docs from the bsnes fork. 2019-04-18 18:04:44 +10:00
Tim Allen
e05672183b gitlab-ci: Source code moved from "higan" to "bsnes" directories. 2019-04-18 18:03:57 +10:00
Tim Allen
922a0e420c Update to bsnes v107r2 beta release.
byuu says:

Added DerKoun's HD mode 7 (up to 2160p), ~100fps boost for fast forwarding,
configurable latency settings for waveOut (please configure this yourself),
filename case insensitivity, and a few other things.
2019-04-18 17:27:44 +10:00
Tim Allen
c25d20a8d9 Don't build higan on the bsnes branch.
Since bsnes has hard-forked from bsnes, let's not expect higan stuff to keep
building indefinitely.
2019-04-13 10:49:28 +10:00
Tim Allen
4d7bb510f2 Update to bsnes v107.1 release.
byuu says:

Don't let the point release fool you, there are many significant changes in this
release. I will be keeping bsnes releases using a point system until the new
higan release is ready.

Changelog:

  - GUI: added high DPI support
  - GUI: fixed the state manager image preview
  - Windows: added a new waveOut driver with support for dynamic rate control
  - Windows: corrected the XAudio 2.1 dynamic rate control support [BearOso]
  - Windows: corrected the Direct3D 9.0 fullscreen exclusive window centering
  - Windows: fixed XInput controller support on Windows 10
  - SFC: added high-level emulation for the DSP1, DSP2, DSP4, ST010, and Cx4
    coprocessors
  - SFC: fixed a slight rendering glitch in the intro to Megalomania

If the coprocessor firmware is missing, bsnes will fallback on HLE where it is
supported, which is everything other than SD Gundam GX and the two Hayazashi
Nidan Morita Shougi games.

The Windows dynamic rate control works best with Direct3D in fullscreen
exclusive mode. I recommend the waveOut driver over the XAudio 2.1 driver, as it
is not possible to target a single XAudio2 version on all Windows OS releases.
The waveOut driver should work everywhere out of the box.

Note that with DRC, the synchronization source is your monitor, so you will
want to be running at 60hz (NTSC) or 50hz (PAL). If you have an adaptive sync
monitor, you should instead use the WASAPI (exclusive) or ASIO audio driver.
2019-04-09 11:16:30 +10:00
Tim Allen
7786206a4f Update to bsnes v107 release.
[This is specifically a release of bsnes, not the whole higan suite, even though
it contains all the higan source. -Ed.]

byuu says:

Today I am posting the first release of the new bsnes emulator.

bsnes is designed to be a revival of the classic bsnes design, focusing
specifically on performance and ease of use for SNES emulation.

In addition to all of the features of higan, bsnes supports the following
features:

  - 300% faster (than higan) scanline-based, multi-threaded graphics renderer
  - option to disable sprite limits in games
  - option to enable hires mode 7 graphics
  - option to enable more accurate pixel-based graphics renderer
  - option to overclock SuperFX games by up to 800%
  - periodic auto-saving of game save RAM
  - save state manager with state screenshots
  - several new save state hotkeys such as increment/decrement slot#
  - option to auto-save states when unloading a game or closing the emulator
  - option to auto-load aforementioned states when loading games
  - save state undo and redo support (with associated hotkeys)
  - speed override modes (50%, 75%, 100%, 150%, 200%)
  - recent games list
  - frame advance mode
  - screenshot hotkey
  - path selection for games, patches, saves, cheats, states, and screenshots
  - dynamic video, audio, input driver changes
  - direct loading and playing of games without the use of the higan library
  - ZIP archive and multiple file extension support for games
  - firmware folder for unappended coprocessor firmware (see documentation for
    more)
  - compatibility with sd2snes and Snes9X MSU1 game file naming
  - compatibility with higan gamepaks (game folders)
  - soft-patching support for both BPS and IPS patches
  - menubar that does not pause emulation when entered
  - video pixel shaders (requires OpenGL 3.2)
  - built-in game database with over 1,200 games to ensure perfect memory
    mapping
  - (Linux, BSD only:) audio dynamic rate control to eliminate stuttering
  - and much more!

The one feature I regret not being able to support in this release is Windows
dynamic rate control. I put in my best attempt, but XAudio2's API is simply not
fine-grained enough, and the WASAPI driver is not mature enough. I hope that DRC
support can be added to the Windows port in the near future, and I would like to
offer a large cash bounty to anyone who can help me make this happen.
2019-02-22 17:48:52 +11:00
Tim Allen
fbc1571889 Update to v106r85 release.
byuu says:

The bad instruction was due to the instruction before it fetching one
too many bytes. Didn't notice right away as the disassembler got it
right.

The register map was incorrect on the active 16-bit flags.

I fixed and improved some other things along those lines. Hooked up some
basic KnGE (VPU) timings, made it print out VRAM and some of the WRAM
onto the screen each frame, tried to drive Vblank and Hblank IRQs, but
... I don't know for sure what vector addresses they belong to.

MAME says "INT4" for Vblank, and says nothing for Hblank. I am wildly
guessing INT4==SWI 4==0xffff10, but ... I have no idea. I'm also not
emulating the interrupts properly based on line levels, I'm just firing
on the 0→1 transitions. Sounds like Vblank is more nuanced too, but I
guess we'll see.

Emulation is running further along now, even to the point of it
successfully enabling the KnGE IRQs, but VRAM doesn't appear to get much
useful stuff written into it yet.

I reverted the nall/primitive changes, so request for testing is I guess
rescinded, for whatever it was worth.
2019-01-22 11:26:20 +11:00
Tim Allen
53843934c0 Update to v106r84 release.
byuu says:

Changelog:

  - fixed a few TLCS900H CPU and disassembler bugs
  - hooked up a basic Neo Geo Pocket emulator skeleton and memory map;
    can run a few instructions from the BIOS
  - emulated the flash memory used by Neo Geo Pocket games
  - added sourcery to the higan source archives
  - fixed ternary expressions in sfc/ppu-fast [hex_usr]
2019-01-21 16:27:24 +11:00
Tim Allen
37b610da53 Update to v106r83 release.
byuu says:

Changelog:

  - reverted nall/inline-if.hpp usage for now, since the
    nall/primitives.hpp math operators still cast to (u)int64_t
  - improved nall/primitives.hpp more; integer8 x = -128; print(-x) will
    now print 128 (unary operator+ and - cast to (u)int64_t)
  - renamed processor/lr35902 to processor/sm83; after the Sharp SM83
    CPU core [gekkio discovered the name]
  - a few bugfixes to the TLCS900H CPU core
  - completed the disassembler for the TLCS900H core

As a result of reverting most of the inline if stuff, I guess the
testing priority has been reduced. Which is probably a good thing,
considering I seem to have a smaller pool of testers these days.

Indeed, the TLCS900H core has ended up at 131KiB compared to the M68000
core at 128KiB. So it's now the largest CPU core in all of higan. It's
even more ridiculous because the M68000 core would ordinarily be quite a
bit smaller, had I not gone overboard with the extreme templating to
reduce instruction decoding overhead (you kind of have to do this for
RISC CPUs, and the inverted design of the TLCS900H kind of makes it
infeasible to do the same there.)

This CPU core is bound to have dozens of extremely difficult CPU bugs,
and there's no easy way for me to test them. I would greatly appreciate
any help in looking over the core for bugs. A fresh pair of eyes to spot
a mistake could save me up to several days of tedious debugging work.

The core still isn't ready to actually be tested: I have to hook up
cartridge loading, a memory bus, interrupts, timers, and the micro DMA
controller before it's likely that anything happens at all.
2019-01-19 12:34:17 +11:00
Tim Allen
2d9ce59e99 Update to v106r82 release.
byuu says:

Half of the disassembler is implemented now. Well, the decoding half
anyway. I'm splitting the decoding and string building into separate
components this time around, on account of the instruction encoding
being in reverse order. The string building portion hasn't been written
yet, either.

We're up to 112KiB now, compared to 128KiB for the 68K.
2019-01-18 10:59:49 +11:00
Tim Allen
559a6585ef Update to v106r81 release.
byuu says:

First 32 instructions implemented in the TLCS900H disassembler. Only 992
to go!

I removed the use of anonymous namespaces in nall. It was something I
rarely used, because it rarely did what I wanted.

I updated all nested namespaces to use C++17-style namespace Foo::Bar {}
syntax instead of classic C++-style namespace Foo { namespace Bar {}}.

I updated ruby::Video::acquire() to return a struct, so we can use C++17
structured bindings. Long term, I want to get away from all functions
that take references for output only. Even though C++ botched structured
bindings by not allowing you to bind to existing variables, it's even
worse to have function calls that take arguments by reference and then
write to them. From the caller side, you can't tell the value is being
written, nor that the value passed in doesn't matter, which is terrible.
2019-01-16 13:02:24 +11:00
Tim Allen
25145f59cc Update to v106r80 release.
byuu says:

Any usage of natural and integer cast to 64-bit math operations now.
Hopefully this will be the last of the major changes for a bit on
nall/primitives, at least until serious work begins on removing implicit
conversion to primitive types.

I also completed the initial TLCS900H core, sans SWI (kind of a ways off
from support interrupts.) I really shouldn't say completed, though. The
micro DMA unit is missing, interrupt priority handling is missing,
there's no debugger, and, of course, there's surely dozens of absolutely
critical CPU bugs that are going to be an absolute hellscape nightmare
to track down.

It was a damn shame, right up until the very last eight instructions,
[CP|LD][I|D](R), the instruction encoding was consistent. Of course,
there could be other inconsistencies that I missed. In fact, that's
somewhat likely ... sigh.
2019-01-16 00:09:50 +11:00
Tim Allen
17fc6d8d51 Update to v106r79 release.
byuu says:

This WIP is just work on nall/primitives ...

Basically, I'm coming to the conclusion that it's just not practical to
try and make Natural/Integer implicitly castable to primitive signed and
unsigned integers. C++ just has too many edge cases there.

I also want to get away from the problem of C++ deciding that all math
operations return 32-bit values, unless one of the parameters is 64-bit,
in which case you get a 64-bit value. You know, so things like
array[-1] won't end up accessing the 4 billionth element of the array.
It's nice to be fancy and minimally size operations (eg 32-bit+32-bit =
33-bit), but it's just too unintuitive. I think all
Natural<X>+Natural<Y> expessions should result in a Natural<64> (eg
natural) type.

nall/primitives/operators.hpp has been removed, and new
Natural<>Natural / Integer<>Integer casts exist. My feeling is that
signed and unsigned types should not be implicitly convertible where
data loss can occur. In the future, I think an integer8*natural8 is
fine to return an integer64, and the bitwise operators are probably all
fine between the two types. I could probably add
(Integer,Natural)+Boolean conversions as well.

To simplify expressions, there are new user-defined literals for _b
(boolean), _n (natural), _i (integer), _r (real), _n# (eg _n8),
_i# (eg _i8), _r# (eg _r32), and _s (nall::string).

In the long-term, my intention is to make the conversion and cast
constructors explicit for primitive types, but obviously that'll shatter
most of higan, so for now that won't be the case.

Something I can do in the future is allow implicit conversion and
casting to (u)int64_t. That may be a nice balance.
2019-01-15 15:33:20 +11:00
Tim Allen
6871e0e32a Update to v106r78 release.
byuu says:

I've implemented a lot more TLCS900H instructions. There are currently
20 missing spots, all of which are unique instructions (well, MINC and
MDEC could be considered pairs of 3 each), from a map of 1024 slots.

After that, I have to write the disassembler. Then the memory bus. Then
I get to start the fun process of debugging this monstrosity.

Also new is nall/inline-if.hpp. Note that this file is technically a war
crime, so be careful when opening it. This replaces ternary() from the
previous WIP.
2019-01-14 17:16:28 +11:00
Tim Allen
bb1dd8c609 Update to v106r77 release.
byuu says:

So this turned out to be a rather unproductive ten-hour rabbit hole, but
...

I reworked nall/primitives.hpp a lot. And because the changes are
massive, testing of this WIP for regressions is critically important. I
really can't stress that enough, we're almost certainly going to have
some hidden regressions here ...

We now have a nall/primitives/ subfolder that splits up the classes into
manageable components. The bit-field support is now shared between both
Natural and Integer. All of the assignment operator overloads are now
templated and take references instead of values. Things like the
GSU::Register class are non-copyable on account of the function<>
object inside of it, and previously only operator= would work with
classes like that.

The big change is nall/primitives/operators.hpp, which is a really
elaborate system to compute the minimum number of bits needed for any
operation, and to return a Natural<T> or Integer<T> when one or both of
the arguments are such a type.

Unfortunately, it doesn't really work yet ... Kirby's Dream Land 3
breaks if we include operators.hpp. Zelda 3 runs fine with this, but I
had to make a huge amount of core changes, including introducing a new
ternary(bool, lhs, rhs) function to nall/algorithm to get past
Natural<X> and Natural<Y> not being equivalent (is_integral types get a
special exemption to ternary ?: type equivalence, yet it's impossible to
simulate with our own classes, which is bullshit.) The horrifying part
is that ternary() will evaluate both lhs and rhs, unlike ?:

I converted some of the functions to test ? uint(x) : uint(y), and
others to ternary(test, x, y) ... I don't have a strong preference
either way yet.

But the part where things may have gotten broken is in the changes to
where ternary() was placed. Some cases like in the GBA PPU renderer, it
was rather unclear the order of evaluations, so I may have made a
mistake somewhere.

So again, please please test this if you can. Or even better, look over
the diff.

Longer-term, I'd really like the enable nall/primitives/operators.hpp,
but right now I'm not sure why Kirby's Dream Land 3 is breaking. Help
would be appreciated, but ... it's gonna be really complex and difficult
to debug, so I'm probably gonna be on my own here ... sigh.
2019-01-13 17:25:14 +11:00
Tim Allen
c9f7c6c4be Update to v106r76 release.
byuu says:

I added some useful new functions to nall/primitives:

    auto Natural<T>::integer() const -> Integer<T>;
    auto Integer<T>::natural() const -> Natural<T>;

These let you cast between signed and unsigned representation without
having to care about the value of T (eg if you take a Natural<T> as a
template parameter.) So for instance when you're given an unsigned type
but it's supposed to be a sign-extended type (example: signed
multiplication), eg Natural<T> → Integer<T>, you can just say:

    x = y.integer() * z.integer();

The TLCS900H core gained some more pesky instructions such as DAA, BS1F,
BS1B.

I stole an optimization from RACE for calculating the overflow flag on
addition. Assuming: z = x + y + c;

    Before: ~(x ^ y) & (x ^ z) & signBit;
    After: (x ^ z) & (y ^ z) & signBit;

Subtraction stays the same. Assuming: z = x - y - c;

    Same: (x ^ y) & (x ^ z) & signBit;

However, taking a speed penalty, I've implemented the carry computation
in a way that doesn't require an extra bit.

Adding before:

    uint9 z = x + y + c;
    c = z & 0x100;

Subtracting before:

    uint9 z = x - y - c;
    c = z & 0x100;

Adding after:

    uint8 z = x + y + c;
    c = z < x || z == x && c;

Subtracting after:

    uint8 z = x - y - c;
    c = z > x || z == x && c;

I haven't been able to code golf the new carry computation to be any
shorter, unless I include an extra bit, eg for adding:

    c = z < x + c;

But that defeats the entire point of the change. I want the computation
to work even when T is uintmax_t.

If anyone can come up with a faster method, please let me know.

Anyway ... I also had to split off INC and DEC because they compute
flags differently (word and long modes don't set flags at all, byte mode
doesn't set carry at all.)

I also added division by zero support, although I don't know if it's
actually hardware accurate. It's what other emulators do, though.
2019-01-11 12:51:18 +11:00
Tim Allen
95d0020297 Update to v106r75 release.
byuu says:

So tired ... so much left to do still ... sigh.

If someone's up for some code golf, open to suggestions on how to handle
the INTNEST control register. It's the only pure 16-bit register on the
system, and it breaks my `map`/`load`/`store<uint8,16,32>` abstraction.
Basically what I suspect happens is when you access INTNEST in 32-bit
mode, the upper 16-bits are just undefined (probably zero.) But
`map<uint32>(INTNEST)` must return a uint32& or nothing at all. So for the
time being, I'm just making store(ControlRegister) check if it's the
INTNEST ID, and clearing the upper bits of the written byte in that
case. It's hacky, but ... it's the best I can think of.

I added LDX, which is a 900H-only instruction, and the control register
map is for the 900/H CPU. I found the detailed differences between the
CPUs, and it doesn't look likely that I'm gonna support the 900 or
900/H1 at all. Not that there was a reason to anyway, but it's nice to
support more stuff when possible. Oh well.

The 4-byte instruction fetch queue is going to have to get implemented
inside fetch, or just not implemented at all ... not like I'd be able to
figure out the details of it anyway.

The manual isn't clear on how the MULA flags are calculated, but since
MUL doesn't set any flags, I assume the flags are based on the addition
after the multiplication, eg:

    uint32 a = indirect<int16>(XDE) * indirect<int16>(XHL);
    uint32 b = reg16; //opcode parameter
    uint32 c = a + b; //flags set based on a+b

No idea if it's right or not. It doesn't set carry or half-carry, so
it's not just simply the same as calling algorithmAdd.

Up to almost 70KB, not even halfway done, don't even have a disassembler
started yet. There's a real chance this could overtake the 68K for the
biggest CPU core in higan, although at this point I'm still thinking the
68K will end up larger.
2019-01-10 13:21:18 +11:00
Tim Allen
41148b1024 Update to v106r74 release.
byuu says:

So I spent the better part of eight hours refactoring the TLCS900H core
to be more flexible in light of new edge cases.

I'm now including the size information inside of the types (eg
Register<Byte>, Memory<Word>) rather than as parameters to the
instruction handlers. This allows me to eg implement RETI without
needing template arguments in all the instructions. pop(SR), pop(PC) can
deduce how much to pop off of the stack. It's still highly templated,
but not unrolling the 3-bit register indexes and instead going through
the switch table to access registers is going to hurt the performance a
good deal.

A benefit of this is that Register{A} != Register{WA} != Register{XWA}
anymore, despite them sharing IDs.

I also renamed read/write to load/store for the CPU core, because
implicit conversions are nasty. They all call the virtual read/write.

I added more instructions, improved the memory addressing mode support,
and some other things.

I got rid of Byte, Word, Long because there's too many alternate sizes
needed: int8, int16, uint24, etc.

Ran into a really annoying C++ case ...

    struct TLCS900H {
      template<typename T> auto store(Register<T> target, T source) -> void;
    };

If you call store(Register<uint32>(x), uint16(y)); it errors out since
the T types don't match. But you can't specialize it:

    template<typename T, typename U> auto store(Register<T>, U) -> void;
    template<typename U> auto TLCS900H::store<uint32, U>(Register<uint32>, U) -> void;

Because somehow it's 2019 and we still can't do partial template
specialization inside classes ...

So as a result, I had to make T source be type uint32 even for
Register<uint8> and Register<uint16>. Doesn't matter too much, just
annoying.
2019-01-09 10:36:03 +11:00
Tim Allen
dbee893408 Update to v106r73 release.
byuu says:

This probably won't fix the use of register yet (I imagine ruby and hiro
will complain now), but ... oh well, it's a start. We'll get it
compiling again eventually.

I added JP, JR, JRL, LD instructions this time around. I'm also starting
to feel that Byte, Word, Long labels for the TLCS900H aren't really
working. There's cases of needing uint24, int8, int16, ... it may just
be better to name the types instead of trying to be fancy.

At this point, all of the easy instructions are in. Now it's down to a
whole lot of very awkward bit-manipulation and special-use instructions.
Sigh.
2019-01-07 18:59:04 +11:00
Tim Allen
cb86cd116c Update to v106r72 release.
byuu says:

For this WIP, I added more TLCS900H instructions. All of the
ADC,ADD,SBB/SBC,SUB,AND,OR,XOR.CP,PUSH,POP instructions are in.

Still an incredible amount of work left to do on this core ... it has all kinds
of novel instructions that aren't on any other processors.

Still no disassembler support yet, so I can't even test what I'm doing. Fun!
2019-01-05 18:04:27 +11:00
Tim Allen
1a889ae232 Update to v106r71 release.
byuu says:

I started working on the Toshiba TLCS900H CPU core today.

It's basically, "what if we took the Z80, added in 32-bit support, added
in SPARC register windows, added a ton of additional addressing modes,
added control registers, and added a bunch of additional instructions?"
-- or in other words, it's basically hell for me.

It took several hours just to wrap my head around the way the opcode
decoder needed to function, but I think I have a decent strategy for
implementing it now.

I should have all of the first-byte register/memory address decoding in
place, although I'm sure there's lots of bugs. I don't have anything in
the way of a disassembler yet.
2019-01-05 11:35:26 +11:00
Tim Allen
79be6f2355 Update to v106r70 release.
byuu says:

Changelog:

  - Interface::displays() -> vector<Display> → Interface::display() -> Display
  - <Platform::videoRefresh(display>, ...) → <Platform::videoFrame>(...)
  - <Platform::audioSample>(...) → <Platform::audioFrame>(...)
  - higan, icarus: use AboutDialog class instead of ad-hoc
    implementations
      - about dialog is now modal, but now has a clickable website URL
  - icarus: reverted if constexpr for now
  - MSX: implemented basic CPU, VDP support

I took out the multiple displays support thing because it was never
really implemented fully (Emulator::Video and the GUIs both ignored it)
or used anyway. If it ends up necessary in the future, I'll worry about
it then.

There's enough MSX emulation now to run Mr. Do! without sound or input.
I'm shipping higan with C-BIOS 0.29a, although it likely won't be good
enough in the future (eg it can't do BASIC, floppy disk, or cassette
loading.) I have keyboard and (not working) AY-3-8910 support in a
different branch, so that won't take too long to implement. Main problem
is naming all the darned keyboard keys. I think I need to change
settings.bml's input mapping lines so that the key names are values
instead of node names, so that any characters can appear inside of them.

It turns out my MSX set uses .rom for the file extensions ... gods. So,
icarus can't really import them like this. I may have to re-design
icarus' importer to stop caring about the file extension and instead ask
you what kind of games you are importing. There's no way icarus can
heuristically guess what systems the images belong to, because many
systems don't have any standardized magic bytes.

I'm struggling with where to put SG-1000, SC-3000, ColecoVision, Coleco
Adam stuff. I think they need to be split to two separate higan
subfolders (sg and cv, most likely ...) The MS/GG share a very
customized and extended VDP that the other systems don't have. The Sega
and Coleco older hardware share the same TMS9918 as the MSX, yet have
very different memory maps and peripherals that I don't want to mix
together. Especially if we start getting into the computer-variants
more.
2019-01-03 21:05:20 +11:00
Tim Allen
cac3858f65 Document that we now require GCC7 and/or C++17 features. 2019-01-03 20:43:08 +11:00
Tim Allen
1fd6d983da Build with Ubuntu LTS instead of Debian Stable.
Debian has served us well, but byuu would like to start using C++17 features
which generally requires GCC7. Debian Stable only has GCC6 right now, while
Ubuntu LTS has the required version, so that should get things going again.
2019-01-03 20:37:30 +11:00
Tim Allen
aaf094e7c4 Update to v106r69 release.
byuu says:

The biggest change was improving WonderSwan emulation. With help from
trap15, I tracked down a bug where I was checking the wrong bit for
reverse DMA transfers. Then I also emulated VTOTAL to support variable
refresh rate. Then I improved HyperVoice emulation which should be
unsigned samples in three of four modes. That got Fire Lancer running
great. I also rewrote the disassembler. The old one disassembled many
instructions completely wrong, and deviated too much from any known x86
syntax. I also emulated some of the quirks of the V30 (two-byte POP into
registers fails, SALC is just XLAT mirrored, etc) which probably don't
matter unless someone tries to run code to verify it's a NEC CPU and not
an Intel CPU, but hey, why not?

I also put more work into the MSX skeleton, but it's still just a
skeleton with no real emulation yet.
2019-01-02 10:52:08 +11:00
Tim Allen
3159285eaa Update to v106r68 release.
byuu says:

Changelog:

  - nall: converted range, iterator, vector to 64-bit
  - added (very poor) ColecoVision emulation (including Coleco Adam
    expansion)
  - added MSX skeleton
  - added Neo Geo Pocket skeleton
  - moved audio,video,resource folders into emulator folder
  - SFC heuristics: BS-X Town cart is "ZBSJ" [hex_usr]

The nall change is for future work on things like BPA: I need to be able
to handle files larger than 4GB. It is extremely possible that there are
still some truncations to 32-bit lurking around, and even more
disastrously, possibly some -1s lurking that won't sign-extend to
`(uint64_t)0-1`. There's a lot more classes left to do: `string`,
`array_view`, `array_span`, etc.
2018-12-22 21:28:15 +11:00
Tim Allen
90da691717 Update to v106r67 release.
byuu says:

Changelog:

  - added all pre-requisite to make install rule (note: only for higan,
    icarus so far)
  - added SG-1000 emulation
  - added SC-3000 emulation (no keyboard support yet)
  - added MS graphics mode 1 emulation (SC-1000)
  - added MS graphics mode 2 emulation (F-16 Fighter)
  - improve Audio::process() to prevent a possible hang
  - higan: repeat monaural audio to both left+right speakers
  - icarus: add heuristics for importing MSX games (not emulated in
    higan yet in this WIP)
  - added DC bias removal filter [jsd1982]
  - improved Audio::Stream::reset() [jsd1982]

I was under the impression that the 20hz highpass filter would have
removed DC bias ... if not, then I don't know why I added that filter to
all of the emulation cores that have it. In any case, if anyone is up
for helping me out ... if we could analyze the output with and without
the DC bias filter to see if it's actually helping, then I'll enable it
if it is. To enable it, edit
higan/audio/stream.cpp::addDCRemovalFilter() and remove the return
statement at the top of the function.
2018-12-21 11:01:14 +11:00
Tim Allen
598076e400 Fix typo in CI script, introduced in commit 23dd28. 2018-12-20 19:58:33 +11:00
Tim Allen
075f540ec4 The libretro core is broken after v106, we know it's broken, no need to test. 2018-12-20 16:12:43 +11:00
Tim Allen
41eccf6ec4 Update .gitignore.
At some point, higan moved system metadata from higan/profile to higan/systems.
Also, higan's Super Famicom core added config options of its own, which it now
writes out.
2018-12-20 12:15:34 +11:00
Tim Allen
4c4e79aa0e Update to v106r66 release.
byuu says:

Changelog:

  - moved to GCC 8.2 and C++17
  - fixed compilation under FreeBSD 12.0
  - don't read beyond the file size in
    SuperFamicom::Cartridge::loadMemory
  - add missing I/O cycle HuC6280::instructionImmediate
  - serialize Mega Drive's Game Genie state
  - serialize SPC7110::Thread information
  - enable 30-bit color depth support under the GLX/OpenGL 2.0 driver
    (doesn't work with OpenGL 3.2 yet)

The 30-bit color depth option isn't super useful, but why not? I need to
update ruby to detect that the display is actually capable of it before
exposing an option that can result in the driver failing to initialize,
however.
2018-12-20 11:55:47 +11:00
Tim Allen
0b44399c0a docs: Review and update docs for v107.
Changes include:

- The "Library" menu was replaced with the "Systems" menu
- The "Settings" menu was reorganised
- Game Boy rumble is now under the MBC5 "controller" for the cartridge "port",
  instead of being presented as a part of the base console
- Import instructions now mention that icarus ships with some firmware files,
  and describe the "Firmware" directory that icarus will use for firmware
  it needs.
- Apparently the correct name is "MSU1", not "MSU-1"
- v107 changes the way MSU1 data is stored in game folders
- PowerFest '94 import instructions removed, since I can't get it to work
  with v107
- Links to the official forum have been replaced with links to the unofficial
  forum archive, since the official forum is shutting down
- Links to Mercurial Magic updated to point at qwertymodo's archive, since
  hex_usr is no longer developing it
- Links to nSide updated, since hex_usr no longer uses GitHub.
- Windows build instructions now describe a compiler that is actually
  maintained, instead of stale TDM64-GCC.
- Linux build instructions now mention higan requires SDL 2.0.
- minor wording changes, typos, broken links fixed, etc.
2018-11-16 16:09:30 +11:00
Tim Allen
23dd28952b Update build instructions.
The latest WIP renames icarus/database and icarus/firmware to
icarus/Database and icarus/Firmware (in title case).

Also, somewhere along the line we stopped building icarus for higan/Linux,
which seems an oversight.
2018-10-04 22:17:49 +10:00
Tim Allen
03b06257d3 Update to v106r65 release.
byuu says:

This synchronizes bsnes/higan with many recent internal nall changes.

This will be the last WIP until I am situated in Japan. Apologies for the
bugfixes that didn't get applied yet, I ran out of time.
2018-10-04 20:12:11 +10:00
Tim Allen
336d20123f Update to v106r64 release.
byuu says:

Changelog:

  - sfc: completed BS Memory Cassette emulation (sans bugs, of course --
    testing appreciated)
  - bsnes: don't strip - on MSU1 track names in game ROM mode
    [hex_usr]

I'm going with "metadata.bml" for the flash metadata filename for the
time being, but I'll say that it's subject to change. I'll have to make
a new extension for it to be supported with bsnes.
2018-09-13 21:13:00 +10:00
Tim Allen
c58169945c Update to v106r63 release.
byuu says:
Changelog:

  - gb/mbc7: rewrote the 93LCx6 EEPROM emulation
  - sfc/slot/bsmemory: rewrote the flash emulation for Satellaview
    cartridges

As of this release, flash-based BS Memory cartridges will be writable.
So without the bsnes patch to disable write limits, some games will lock
out after a few plays.
2018-09-11 21:16:58 +10:00
Tim Allen
c2d0ed4ca8 Update to v106r62 release.
byuu says:

Changelog:

  - sfc/cx4: added missing instructions [info from Overload]
  - sfc/cx4: added instruction cache emulation [info from ikari]
  - sfc/sa1: don't let CPU access SA1-only I/O registers, and vice versa
  - sfc/sa1: fixed IRQs that were broken from the recent WIP
  - sfc/sa1: significantly improved bus conflict emulation
      - all tests match hardware now, other than HDMA ROM↔ROM, which
        is 0.5 - 0.8% too fast
  - sfc/cpu: fixed a bug with DMA→CPU alignment timing
  - sfc/cpu: removed the DMA pipe; performs writes on the same cycles as
    reads [info from nocash]
  - sfc/memory: fix a crashing bug due to not clearing Memory size field
    [hex_usr]
  - bsnes/gb: use .rtc for real-time clock file extensions on the Game
    Boy [hex_usr]
  - ruby/cgl: compilation fix [Sintendo]

Now let's see if I can accept being off by ~0.65% on one of twelve SA1
timing tests for the time being and prioritize much more important
things or not.
2018-09-10 12:11:19 +10:00
Tim Allen
3d34517f3e Update to v106r61 release.
byuu says:

This release adds ikari's Cx4 notes to bsnes. It fixes the MMX2 intro's
boss fight sequence to be frame perfect to real hardware. It's also very
slightly faster than before.

I've also added an option to toggle the CPU↔coprocessor cycle
synchronization to the emulation settings panel, so you don't have to
recompile to get the more accurate SA1 timings. I'm most likely going to
default this to disabled in bsnes, and *maybe* enabled in higan out of
the box.

StaticRAM (wasn't used) and MappedRAM are gone from the Super Famicom
core. Instead, there's now ReadableMemory, WritableMemory, and
ProtectedMemory (WritableMemory with a toggle for write protection.)
Cartridge::loadMap now takes a template Memory object, which bypasses an
extra virtual function call on memory accesses, but it doesn't really
impact speed much. Whatever.
2018-09-04 15:44:35 +10:00
Tim Allen
a3e0f6da25 Update to v106r60 release.
byuu says:

I added (imperfect) memory conflict timing to the SA1.

Before:

  - WRAM↔↔ROM ran 7% too fast
  - ROM↔↔ROM ran 100% too fast
  - WRAM↔↔IRAM ran 7% too fast
  - ROM↔↔IRAM ran 7% too fast
  - IRAM↔↔IRAM ran 287% too fast
  - BWRAM↔↔BWRAM ran 100% too fast
  - HDMA ROM↔↔ROM ran 15% too fast
  - HDMA WRAM↔↔ROM ran 15% too fast
  - DMA ROM↔↔ROM ran 100% too fast

After:

  - ROM↔↔ROM runs 14% too fast
  - HDMA WRAM↔↔ROM runs 7% too fast
  - DMA ROM↔↔ROM runs 4% too fast

If you enable this with the fast PPU + DSP, your framerate in SA1 games
will drop by 51%. And even if you disable it, you'll still lose 9% speed
in SA1 games, and 2% speed in non-SA1 games, because of changes needed
to make this support possible.

By default, I'm leaving this off. Compile with `-DACCURATE_SA1` (or
uncomment the line in sfc/sfc.hpp) if you want to try it out.

This'll almost certainly cause some SA1 regressions, so I guess we'll
tackle those as they arise.
2018-09-03 00:06:41 +10:00
Tim Allen
bd814f0358 Update to v106r59 release.
byuu says:

Changelog:

  - fixed bug in Emulator::Game::Memory::operator bool()
  - nall: renamed view<string> back to `string_view`
  - nall:: implemented `array_view`
  - Game Boy: split cartridge-specific input mappings (rumble,
    accelerometer) to their own separate ports
  - Game Boy: fixed MBC7 accelerometer x-axis
  - icarus: Game Boy, Super Famicom, Mega Drive cores output internal
    header game titles to heuristics manifests
  - higan, icarus, hiro/gtk: improve viewport geometry configuration;
    fixed higan crashing bug with XShm driver
  - higan: connect Video::poll(),update() functionality
  - hiro, ruby: several compilation / bugfixes, should get the macOS
    port compiling again, hopefully [Sintendo]
  - ruby/video/xshm: fix crashing bug on window resize
      - a bit hacky; it's throwing BadAccess Xlib warnings, but they're
        not fatal, so I am catching and ignoring them
  - bsnes: removed Application::Windows::onModalChange hook that's no
    longer needed [Screwtape]
2018-08-26 16:49:54 +10:00
Tim Allen
f9adb4d2c6 Update to v106r58 release.
byuu says:

The main thing I worked on today was emulating the MBC7 EEPROM.

And... I have many things to say about that, but not here, and not now...

The missing EEPROM support is why the accelerometer was broken. Although
it's not evidently clear that I'm emulating the actual values
incorrectly. I'll think about it and get it fixed, though.

bsnes went from ~308fps to ~328fps, and I don't even know why. Probably
something somewhere in the 140KB of changes to other things made in this
WIP.
2018-08-21 13:17:12 +10:00
Tim Allen
9a6ae6dacb Update to 20180809 release.
byuu says:

The Windows port can now run the emulation while navigating menus,
moving windows, and resizing windows. The main window also doesn't try
so hard to constantly clear itself. This may leave a bit of unwelcome
residue behind in some video drivers during resize, but under most
drivers, it lets you resize without a huge amount of flickering.

On all platforms, I now also run the emulation during MessageWindow
modal events, where I didn't before.

I'm thinking we should probably mute the audio during modal periods,
since it can generate a good deal of distortion.

The tooltip timeout was increased to ten seconds.

On Windows, the enter key can now activate buttons, so you can more
quickly dismiss MessageDialog windows. This part may not actually work
... I'm in the middle of trying to get messages out of the global
`Application_windowProc` hook and into the individual `Widget_windowProc`
hooks, so I need to do some testing.

I fixed a bug where changing the input driver wouldn't immediately
reload the input/hotkey settings lists properly.

I also went from disabling the driver "Change" button when the currently
active driver is selected in the list, to instead setting it to say
"Reload", and I also added a tool tip to the input driver reload button,
advising that if you're using DirectInput or SDL, you can hit "Reload"
to rescan for hotplugged gamepads without needing to restart the
emulator. XInput and udev have auto hotswap support. If we can ever get
that into DirectInput and SDL, then I'll remove the tooltip. But
regardless, the reload functionality is nice to have for all drivers.

I'm not sure what should happen when a user changes their driver
selection while a game is loaded, gets the warning dialog, chooses not
to change it, and then closes the emulator. Currently, it will make the
change happen the next time you start the emulator. This feels a bit
unexpected, but when you change the selection without a game loaded, it
takes immediate effect. So I'm not really sure what's best here.
2018-08-10 15:02:59 +10:00
Tim Allen
1e4affe5f9 Update to 20180808 release.
byuu says:

This release fixes the XAudio 2.1 and WASAPI drivers on Windows, and
extends XAudio to support device selection (eg headphones, speakers,
monitor, etc.) It also adds DRC to XAudio, however it's not currently
working.

The code is courtesy of Talarubi, I just botched it somewhere upon
porting it to the newer version of ruby.
2018-08-09 14:16:46 +10:00
Tim Allen
93a6a1ce7e Update to v106r57 release.
byuu says:

I've added tool tips to hiro for Windows, GTK, and Qt. I'm unsure how to
add them for Cocoa. I wasted am embarrassing ~14 hours implementing tool
tips from scratch on Windows, because the `TOOLTIPS_CLASS` widget just
absolutely refused to show up, no matter what I tried. As such, they're
not quite 100% native, but I would really appreciate any patch
submissions to help improve my implementation.

I added tool tips to all of the confusing settings in bsnes. And of
course, for those of you who don't like them, there's a configuration
file setting to turn them off globally.

I also improved Mega Drive handling of the Game Genie a bit, and
restructured the way the Settings class works in bsnes.

Starting now, I'm feature-freezing bsnes and higan. From this point
forward:

  - polishing up and fixing bugs caused by the ruby/hiro changes
  - adding DRC to XAudio2, and maybe exclusive mode to WGL
  - correcting FEoEZ (English) to load and work again out of the box

Once that's done, a final beta of bsnes will go out, I'll fix any
reported bugs that I'm able to, and then v107 should be ready. This time
with higan being functional, but marked as v107 beta. v108 will restore
higan to production status again, alongside bsnes.
2018-08-08 18:46:58 +10:00
Tim Allen
3b4e8b6d75 Update to v106r56 release.
byuu says:

I fixed all outstanding bugs that I'm aware of, including all of the
errata I listed yesterday.

And now it's time for lots of regression testing.

After that, I need to add Talarubi's XAudio2 DRC code, and then get a
new public bsnes WIP out for final testing.

New errata: when setting an icon (nall::image) larger than a Canvas on
Windows, it's not centering the image, so you end up seeing the overscan
area in the state manager previews, and the bottom of the image gets cut
off. I also need to forcefully disable the Xlib screensaver disable
support. I think I'll remove the GUI option to bypass it as well, and
just force screensaver disable always on with Windows. I'll improve it
in the future to toggle the effect between emulator pauses.
2018-08-06 17:46:00 +10:00
Tim Allen
b2b51d544f Make genius and icarus also ignore dynamically-generated dependencies. 2018-08-06 17:41:55 +10:00
Tim Allen
5da4532771 Update to v106r55 release.
byuu says:

Everything *should* be working again, but of course that won't
actually be the case. Here's where things stand:

  - bsnes, higan, icarus, and genius compile and run fine on FreeBSD
    with GTK
  - ruby video and audio drivers are untested on Windows, macOS, and
    Linux
  - hiro is untested on macOS
  - bsnes' status bar is not showing up properly with hiro/qt
  - bsnes and higan's about screen is not showing up properly with
    hiro/qt (1x1 window size)
  - bsnes on Windows crashes often when saving states, and I'm not sure
    why ... it happens inside Encode::RLE
  - bsnes on Windows crashes with ruby.input.windows (unsure why)
  - bsnes on Windows fails to show the verified emblem on the status bar
    properly
  - hiro on Windows flickers when changing tabs

To build the Windows bsnes and higan ports, use

    ruby="video.gdi audio.directsound"

Compilation error logs for Linux will help me fix the inevitable list of
typos there. I can fix the typos on other platforms, I just haven't
gotten to it yet.
2018-08-05 19:00:15 +10:00
Tim Allen
552d385031 Fix ST018 firmware hashes. 2018-08-05 09:06:51 +10:00
Tim Allen
0595e9e866 Remove redundant use of "classic". 2018-08-05 09:02:30 +10:00
Tim Allen
23da4e4e91 Don't mention console dates in the documentation.
The WonderSwan Color came out in 2000 and the GBA in 2001, so technically
they're not "video-game consoles of the 1980s and 1990s". Since there's no
elegant way to talk about the 2000-2009 timespan, let's just not mention
dates at all.
2018-08-04 21:45:44 +10:00
Tim Allen
41e127a07c Update to v106r54 release.
byuu says:

Changes to hiro will break all but the GTK target. Not that it matters
much given that the only ruby drivers that function are all on BSD
anyway.

But if you are fortunate enough to be able to run this ... you'll find
lots of polishing improvements to the bsnes GUI. I posted some
screenshots on Twitter, if anyone were interested.
2018-08-04 21:44:00 +10:00
Tim Allen
5d135b556d Update to v106r53 release.
byuu says:

Okay, so the WIPs-within-WIPs thing wasn't achieving its desired effect,
and it ended up causing me to have to redo some work on hiro since my
last local snapshot was of r52. So, heck it. I'll just do mostly
non-functional WIPs for a bit, and worry about the fallout years later
when I'm trying to find an emulation regression and cursing that the
WIPs aren't compiling.

I ported all of the ruby input drivers to the new syntax, as well as the
OpenAL driver. If you patch the ruby drivers for Linux with this in
mind, bsnes should compile and run there again.

Also, the bsnes program icon has returned, now that the new hiro layout
code is mature enough and I can simply add and remove the icon as a
Canvas instead of having to try and render into a viewport. The icon
shows up instantly with the main window.
2018-08-01 19:07:28 +10:00
Tim Allen
2335bb0df8 Update to 20180731 release.
byuu says:

I've completed moving all the class objects from `unique_pointer<T>` to
just T. The one exception is the Emulator::Interface instance. I can
absolutely make that a global object, but only in bsnes where there's
just the one emulation core.

I also moved all the SettingsWindow and ToolsWindow panels out to their
own global objects, and fixed a very difficult bug with GTK TabFrame
controls.

The configuration settings panel is now the emulator settings panel. And
I added some spacing between bold label sections on both the emulator
and driver settings panels.

I gave fixing ComboButtonItem my best shot, given I can't reproduce the
crash. Probably won't work, though.

Also made a very slight consistency improvement to ruby and renamed
driverName() to driver().

...

An important change ... as a result of moving bsnes to global objects,
this means that the constructors for all windows run before the
presentation window is displayed. Before this change, only the
presentation window was constructed first berore displaying it, followed
by the construction of the rest of the GUI windows.

The upside to this is that as soon as you see the main window, the GUI
is ready to go without a period where it's unresponsive.

The downside to this is it takes about 1.5 seconds to show the main
window, compared to around 0.75 seconds before.

I've no intention of changing that back. So if the startup time becomes
a problem, then we'll just have to work on optimizing hiro, so that it
can construct all the global Window objects quicker. The main way to do
that would be to not do calls to the Layout::setGeometry functions for
every widget added, and instead wait until the window is displayed. But
I don't have an easy way to do that, because you want the widget
geometry values to be sane even before the window is visible to help
size certain things.
2018-07-31 20:56:45 +10:00
Tim Allen
212da0a966 Update to 20180730 release.
byuu says:

These WIPs-within-WIPs are getting more and more broken ... this isn't
going the way I wanted.

But ... this time around, I've revamped the entire ruby API again, to
solve a bunch of tough problems that have always made using ruby really
clunky.

But there are *so many* ruby drivers that it's going to take a long
time to work through them all. This WIP is only going to run bsnes, and
only on FreeBSD, and only with some drivers.

hiro's Application::initialize() now calls hiro::initialize(), which you
define inside of your hiro apps. This lets you call
Application::setName(...) before anything else in hiro runs. This is
essential on Xorg to set program icons, for instance.

With the ruby rewrite and the change to hiro, I can get away from the
need to make everything in bsnes/higan pointers to objects, and can now
just declare them as regular objects.
2018-07-31 12:23:12 +10:00
Tim Allen
5deba5cbc1 Update to 20180729 release.
byuu wrote:

Sigh ...

asio.hpp needs #include <nall/windows/registry.hpp>

[Since the last WIP, byuu also posted the following message. -Ed.]

ruby drivers have all been updated (but not tested outside of BSD), and
I redesigned the settings window. The driver functionality all exists on
a new "Drivers" panel, the emulator/hack settings go to a
"Configuration" panel, and the video/audio panels lose driver settings.
As does the settings menu and its synchronize options.

I want to start pushing toward a v107 release. Critically, I will need
DirectSound and ALSA to support dynamic rate control. I'd also like to
eliminate the other system manifest.bml files. I need to update the
cheat code database format, and bundle at least a few quark shaders --
although I still need to default to Direct3D on Windows.

Turbo keys would be nice, if it's not too much effort. Aside from
netplay, it's the last significant feature I'm missing.

I think for v107, higan is going to be a bit rough around the edges
compared to bsnes. And I don't think it's practical to finish the bsnes
localization support.

I'm thinking we probably want another WIP to iron out any critical
issues, but this time there should be a feature freeze with the next
WIP.
2018-07-29 23:24:38 +10:00
Tim Allen
716c95f279 Update to 20180728 release.
byuu says:

Sigh, I seem to be spiraling a bit here ... but the work is very
important. Hopefully I can get a solid WIP together soon. But for now...

I've integrated dynamic rate control into ruby::Audio via
setDynamic(bool) for now. It's very demanding, as you would expect. When
it's not in use, I realized the OSS driver's performance was pretty bad
due to calling write() for every sample for every channel. I implemented
a tiny 256-sample buffer and bsnes went from 290fps to 330fps on my
FreeBSD desktop. It may be possible to do the same buffering with DRC,
but for now, I'm not doing so, and adjusting the audio input frequency
on every sample.

I also added ruby::Video::setFlush(bool), which is available only in the
OpenGL drivers, and this causes glFinish() to be called after swapping
display buffers. I really couldn't think of a good name for this, "hard
GPU sync" sounds kind of silly. In my view, flush is what commits queued
events. Eg fflush(). OpenGL of course treats glFlush differently (I
really don't even know what the point of it is even after reading the
manual ...), and then has glFinish ... meh, whatever. It's
setFlush(bool) until I come up with something better. Also as expected,
this one's a big hit to performance.

To implement the DRC, I started putting helper functions into the ruby
video/audio/input core classes. And then the XVideo driver started
crashing. It took hours and hours and hours to track down the problem:
you have to clear XSetWindowAttributes to zero before calling
XCreateWindow. No amount of `--sync`, `gdb break gdk_x_error`, `-Og`,
etc will make Xlib be even remotely helpful in debugging errors like
this.

The GLX, GLX2, and XVideo drivers basically worked by chance before. If
the stack frame had the right memory cleared, it worked. Otherwise it'd
crash with BadValue, and my changing things broke that condition on the
XVideo driver. So this has been fixed in all three now.

Once XVideo was running again, I realized that non-power of two video
sizes were completely broken for the YUV formats. It took a while, but I
managed to fix all of that as well.

At this point, most of ruby is going to be broken outside of FreeBSD, as
I still need to finish updating all the drivers.
2018-07-28 21:25:42 +10:00
Tim Allen
876b4be1d2 Update to 20180726 release.
byuu says:

Once again, I wasn't able to complete a full WIP revision.

This WIP-WIP adds very sophisticated emulation of the Sega Genesis
Lock-On and Game Genie cartridges ... essentially, through recursion and
a linked list, higan supports an infinite nesting of cartridges.

Of course, on real hardware, after you stack more than three or four
cartridges, the power draw gets too high and things start glitching out
more and more as you keep stacking. I've heard that someone chained up
to ten Sonic & Knuckles cartridges before it finally became completely
unplayable.

And so of course, higan emulates this limitation as well ^-^. On the
fourth cartridge and beyond, it will become more and more likely that
address and/or data lines "glitch" out randomly, causing various
glitches. It's a completely silly easter egg that requires no speed
impact whatsoever beyond the impact of the new linked list cartridge
system.

I also designed the successor to Emulator::Interface::cap,get,set. Those
were holdovers from the older, since-removed ruby-style accessors.

In its place is the new Emulator::Interface::configuration,configure
API. There's the usual per-property access, and there's also access to
read and write all configurable options at once. In essence, this
enables introspection into core-specific features.

So far, you can control processor version#s, PPU VRAM size, video
settings, and hacks. As such, the .sys/manifest.bml files are no longer
necessary. Instead, it all goes into .sys/configuration.bml, which is
generated by the emulator if it's missing.

higan is going to take this even further and allow each option under
"Systems" to have its own editable configuration file. So if you wanted,
you could have a 1/1/1 SNES menu option, and a 2/1/3 SNES menu option.
Or a Model 1 Genesis option, and a Model 2 Genesis option. Or the
various Game Boy model revisions. Or an "SNES-Fast" and "SNES-Accurate"
option.

I've not fully settled on the syntax of the new configuration API. I
feel it might be useful to provide type information, but I really quite
passionately hate any<T> container objects. For now it's all
string-based, because strings can hold anything in nall.

I might also change the access rules. Right now it's like:
emulator→configure("video/blurEmulation", true); but it might be nicer
as "Video::Blur Emulation", or "Video.BlurEmulation", or something like
that.
2018-07-26 20:36:43 +10:00
Tim Allen
22bd4b9277 Update to v106r52 release.
byuu says:

I stand corrected, I managed to create and even larger diff than ever.
This one weighs in at 309KiB `>__>`

I'll have to create a changelog later, I'm too tired right now to go
through all of that.
2018-07-25 22:24:03 +10:00
Tim Allen
f1a4576ac4 Update to 20180724 release.
byuu says:

I failed to complete a WIP, have five of eight cores updated with some
major changes to Emulator::Interface. I'll just post a quick temporary
WIP in the off chance someone wants to look over the new interface and
comment on it.

Also implemented screen saver suppression into hiro/GTK.

I should also add ... a plan of mine is to develop target-bsnes into a
more generic user interface, with the general idea being that
target-higan is for multiple Emulator::Interface cores at the same time,
and target-bsnes is for just one Emulator::Interface core.

The idea being that if one were to compile target-bsnes with the GBA
core, it'd become bgba, for instance.
I don't plan on releasing single-core emulators like this, but ... I don't see any downsides to being more flexible.
2018-07-24 23:41:41 +10:00
Tim Allen
0aedb3430c Update to v106r51 release.
byuu says:

Changelog:

  - added `Emulator::Interface::connected(uint port) -> uint device;`
  - higan, bsnes: updated emulators to use the new
    Emulator::Interface::connected() function
  - hiro: fixed Object::cast<T> finally

So, Emulator::Interface::connected() solves two annoying problems at the
same time.

First, on first run of the emulator when the settings file is blank, it
will retrieve the default "sane" device ID, which is usually a gamepad
for a controller port, or nothing for an expansion/extension port.

Second, if you were to select a multi-port device, like the NES Four
Score, the core will set the other port to the Four Score device as
well, and the GUIs query connected() right after any call to connect(),
so it gets updated without needing a system for the emulation core to
send messages alerting the GUI of changes.
2018-07-21 21:49:48 +10:00
Tim Allen
35ff15f83e Update to v106r50 release.
byuu says:

Changelog:

  - emulator/video,audio: various cleanups
  - emulator/audio: removed reverb effect (it breaks very badly on
    high-frequency systems)
  - emulator/audio: the Nyquist anti-aliasing lowpass filter is now
    generated automatically instead of set per-core
      - at 44.1KHz output, it's set to 22KHz; at 48KHz, it's set to
        22KHz; at 96KHz, it's set to 25KHz
      - this filter now takes the bsnes emulation speed setting into
        account
  - all system/video.cpp files removed; inlined in System::power() and
    Interface::set() instead
  - sfc/cpu: pre-compute `HTIME` as `HTIME+1<<2` for faster comparisons of
    HIRQs
  - sfc/cpu: re-add check to block IRQs on the last dot of each frame
    (minor speed hit)
  - hiro/gtk3: fixed headers for Linux compilation finally
  - hiro/gtk,qt: fixed settings.cpp logic so initial values are used
    when no settings.bml file exists
  - hiro/gtk: started a minor experiment to specify theming information
    in settings.bml files
  - nall/dsp: allow the precision type (double) to be overridden (to
    float)
  - nall: add some helpers for generating pre-compiled headers
      - it was a failure to try using them for higan, however ...
  - nall: add some helpers for reading fallback values from empty
    `Markup::Node[search]` statements

Todo:

  - CRITICAL: a lot of my IRQ/NMI/HDMA timing tests are failing with the
    fast PPU ... need to figure out why
  - space between Emulator::video functions and Emulator::audio
    functions in gb/system/system.cpp
  - remove Audio/Reverb/Enable from settings.bml in target-bsnes
2018-07-21 21:06:40 +10:00
Tim Allen
65a3e6c676 Update to v106r49 release.
byuu says:

This is a fairly radical WIP with extreme changes to lots of very
important parts.

The result is a ~7% emulation speedup (with bsnes, unsure how much it
helps higan), but it's quite possible there are regressions. As such, I
would really appreciate testing as many games as possible ... especially
the old finnicky games that had issues with DMA and/or interrupts.

One thing to note is that I removed an edge case test that suppresses
IRQs from firing on the very last dot of every field, which is a
behavior I've verified on real hardware in the past. I feel that the
main interrupt polling function (the hottest portion of the entire
emulator) is not the appropriate place for it, and I should instead
factor it into assignment of NMITIMEN/VTIME/HTIME using the new
io.irqEnable (==virqEnable||hirqEnable) flag. But since I haven't done
that yet ... there's an old IRQ test ROM of mine that'll fail for this
WIP. No commercial games will ever rely on this, so it's fine for
testing.

Changelog:

  - sfc/cpu.smp: inlined the global status functions
  - sfc/cpu: added readRAM, writeRAM to use a function pointer instead
    of a lambda for WRAM access
  - sfc/cpu,smp,ppu/counter: updated reset functionality to new style
    using class inline initializers
  - sfc/cpu: fixed power(false) to invoke the reset vector properly
  - sfc/cpu: completely rewrote DMA handling to have per-channel
    functions
  - sfc/cpu: removed unused joylatch(), io.joypadStrobeLatch
  - sfc/cpu: cleaned up io.cpp handlers
  - sfc/cpu: simplified interrupt polling code using
    nall::boolean::flip(),raise(),lower() functions
  - sfc/ppu/counter: cleaned up the class significantly and also
    optimized things for efficiency
  - sfc/ppu/counter: emulated PAL 1368-clock long scanline when
    interlace=1, field=1, vcounter=311
  - sfc/smp: factored out the I/O and port handlers to io.cpp
2018-07-19 19:01:44 +10:00
Tim Allen
393c2395bb Update to v106r48 release.
byuu says:

The problems with the Windows and Qt4 ports have all been resolved,
although there's a fairly gross hack on a few Qt widgets to not destruct
once Application::quit() is called to avoid a double free crash (I'm
unsure where Qt is destructing the widgets internally.) The Cocoa port
compiles again at least, though it's bound to have endless problems. I
improved the Label painting in the GTK ports, which fixes the background
color on labels inside TabFrame widgets.

I've optimized the Makefile system even further.

I added a "redo state" command to bsnes, which is created whenever you
load the undo state. There are also hotkeys for both now, although I
don't think they're really something you want to map hotkeys to.

I moved the nall::Locale object inside hiro::Application, so that it can
be used to translate the BrowserDialog and MessageDialog window strings.

I improved the Super Game Boy emulation of `MLT_REQ`, fixing Pokemon
Yellow's custom border and probably more stuff.

Lots of other small fixes and improvements. Things are finally stable
once again after the harrowing layout redesign catastrophe.

Errata:

  - ICD::joypID should be set to 3 on reset(). joypWrite() may as well
    take uint1 instead of bool.
  - hiro/Qt: remove pWindow::setMaximumSize() comment; found a
    workaround for it
  - nall/GNUmakefile: don't set object.path if it's already set (allow
    overrides before including the file)
2018-07-16 16:16:26 +10:00
Tim Allen
6090c63958 Update to v106r47 release.
byuu says:

This is probably the largest code-change diff I've done in years.

I spent four days working 10-16 hours a day reworking layouts in hiro
completely.

The result is we now have TableLayout, which will allow for better
horizontal+vertical combined alignment.

Windows, GTK2, and now GTK3 are fully supported.

Windows is getting the initial window geometry wrong by a bit.

GTK2 and GTK3 work perfectly. I basically abandoned trying to detect
resize signals, and instead keep a list of all hiro windows that are
allocated, and every time the main loop runs, it will query all of them
to see if they've been resized. I'm disgusted that I have to do this,
but after fighting with GTK for years, I'm about sick of it. GTK was
doing this crazy thing where it would trigger another size-allocate
inside of a previous size-allocate, and so my layouts would be halfway
through resizing all the widgets, and then the size-allocate would kick
off another one. That would end up leaving the rest of the first layout
loop with bad widget sizes. And if I detected a second re-entry and
blocked it, then the entire window would end up with the older geometry.
I started trying to build a message queue system to allow the second
layout resize to occur after the first one completed, but this was just
too much madness, so I went with the simpler solution.

Qt4 has some geometry problems, and doesn't show tab frame layouts
properly yet.

Qt5 causes an ICE error and tanks my entire Xorg display server, so ...
something is seriously wrong there, and it's not hiro's fault. Creating
a dummy Qt5 application without even using hiro, just int main() {
TestObject object; } with object performing a dynamic\_cast to a derived
type segfaults. Memory is getting corrupted where GCC allocates the
vtables for classes, just by linking in Qt. Could be somehow related to
the -fPIC requirement that only Qt5 has ... could just be that FreeBSD
10.1 has a buggy implementation of Qt5. I don't know. It's beyond my
ability to debug, so this one's going to stay broken.

The Cocoa port is busted. I'll fix it up to compile again, but that's
about all I'm going to do.

Many optimizations mean bsnes and higan open faster. GTK2 and GTK3 both
resize windows very quickly now.

higan crashes when you load a game, so that's not good. bsnes works
though.

bsnes also has the start of a localization engine now. Still a long way
to go.

The makefiles received a rather substantial restructuring. Including the
ruby and hiro makefiles will add the necessary compilation rules for
you, which also means that moc will run for the qt4 and qt5 targets, and
windres will run for the Windows targets.
2018-07-14 13:59:29 +10:00
Tim Allen
0c55796060 Update to v106r46 release.
byuu says:

Changelog:

  - bsnes, higan: simplified make output; reordered rules
  - hiro: added Window::set(Minimum,Maximum)Size() [only implemented in
    GTK+ so far]
  - bsnes: only allow the window to be shrunk to the 1x multiplier size
  - bsnes: refactored Integral Scaling checkbox to {Center, Scale,
    Stretch} radio selection
  - nall: call fflush() after nall::print() to stdout or stderr [needed
    for msys2/bash]
  - bsnes, higan: program/interface.cpp renamed to program/platform.cpp
  - bsnes: trim ".shader/" from names in Settings→Shader menu
  - bsnes: Settings→Shader menu updated on video driver changes
  - bsnes: remove missing games from recent files list each time it is
    updated
  - bsnes: video multiplier menu generated dynamically based on largest
    monitor size at program startup
  - bsnes: added shrink window and center window function to video
    multiplier menu
  - bsnes: de-minimize presentation window when exiting fullscreen mode
    or changing video multiplier
  - bsnes: center the load game dialog against the presentation window
    (important for multi-monitor setups)
  - bsnes: screenshots are not immediate instead of delayed one frame
  - bsnes: added frame advance menu option and hotkey
  - bsnes: added enable cheats checkbox and hotkey; can be used to
    quickly enable/disable all active cheats

Errata:

  - hiro/Windows: `SW_MINIMIZED`, `SW_MAXIMIZED `=> `SW_MINIMIZE`,
    `SW_MAXIMIZE`
  - hiro/Windows: add pMonitor::workspace()
  - hiro/Windows: add setMaximized(), setMinimized() in
    pWindow::construct()
  - bsnes: call setCentered() after setMaximized(false)
2018-07-08 14:58:27 +10:00
Tim Allen
372e9ef42b Update to v106r45 release.
byuu says:

Changelog:

  - sfc/ppu-fast: added hires mode 7 option (doubles the sampling rate
    of mode 7 pixels to reduce aliasing)
  - sfc/ppu-fast: fixed mode 7 horizontal screen flip [hex_usr]
  - bsnes: added capture screenshot function and path selection
      - for now, it saves as BMP. I need a deflate implementation that
        won't add an external dependency for PNG
      - the output resolution is from the emulator: (256 or 512)x(240 or
        480 minus overscan cropping if enabled)
      - it captures the NEXT output frame, not the current one ... but
        it may be wise to change this behavior
      - it'd be a problem if the core were to exit and an image was
        captured halfway through frame rendering
  - bsnes: recovery state renamed to undo state
  - bsnes: added manifest viewer tool
  - bsnes: mention if game has been verified or not on the status bar
    message at load time
  - bsnes, nall: fixed a few missing function return values
    [SuperMikeMan]
  - bsnes: guard more strongly against failure to load games to avoid
    crashes
  - hiro, ruby: various fixes for macOS [Sintendo]
  - hiro/Windows: paint on `WM_ERASEBKGND` to prevent status bar
    flickering at startup
  - icarus: SPC7110 heuristics fixes [hex_usr]

Errata:

  - sfc/ppu-fast: remove debug hires mode7 force disable comment from
    PPU::power()

[The `WM_ERASEBKGND` fix was already present in the 106r44 public
beta -Ed.]
2018-07-02 11:57:04 +10:00
Tim Allen
40a5fbe605 Update to v106r44 public beta release:
byuu says (in the public announcement):

I'm releasing a beta version of bsnes, for the purpose of gathering feedback and
ensuring that the first official release of bsnes is as solid as possible.

With the exception of dynamic rate control for automatic audio/video sync, and
no pack-in video shaders or cheat code database, it is mostly feature complete.
However, please do not form a lasting opinion of bsnes based on this beta.
2018-06-28 16:35:49 +10:00
Tim Allen
ec960c5172 Update to v106r44 release.
byuu says:

Changelog:

  - hiro/Windows: use `WS_CLIPSIBLINGS` on Label to prevent resize
    drawing issues
  - bsnes: correct viewport resizing
  - bsnes: speed up window resizing a little bit
  - bsnes: fix the cheat editor list enable checkbox
  - bsnes: fix the state manager filename display in game ROM mode
  - bsnes: fix the state manager save/rename/remove functionality in
    game ROM mode
  - bsnes: correct path searching for IPS and BPS patches in game ROM
    mode
  - bsnes: patch BS-X town cartridge to disable play limits
  - bsnes: do not load (program,data,expansion).(rom,flash) from disk in
    game pak mode
      - this is required to support soft-patching and ROM hacks
  - bsnes: added speed mode selection (50%, 75%, 100%, 150%, 200%);
    maintains proper pitch
  - bsnes: added icons to the menubar
      - this is particularly useful to tell game ROMs from game paks in
        the load recent game menu
  - bsnes: added emblem at bottom left of status bar to indicate if a
    game is verified or not
      - verified means it is in the icarus verified game dump database
      - the verified diamond is orange; the unverified diamond is blue
  - bsnes: added an option (which defaults to off) to warn when loading
    unverified games
      - working around a bug in GTK, I have to use the uglier
        MessageWindow instead of MessageDialog
  - bsnes: added (non-functional) link to <https://doc.byuu.org/bsnes/>
    to the help menu
  - bsnes: added GUI setting to toggle memory auto-save feature
  - bsnes: added GUI setting to toggle capturing a backup save state
    when closing the emulator
  - bsnes: made auto-saving states on exit an option
  - bsnes: added an option to auto-load the auto-saved state on load
      - basically, the two combined implements auto-resume
  - bsnes: when firmware is missing, offer to take the user to the
    online help documentation
  - bsnes: added fast PPU option to disable the sprite limit
      - increase from 32 items/line + 34 tiles/line to 128 items/line +
        128 tiles/line
      - technically, 1024 tiles/line are possible with 128 sprites at
        64-width
      - but this is just a waste of cache locality and worst-case
        performance; it'll never happen

Errata:

  - hiro/Windows: fallthrough on Canvas `WM_ERASEBKGND` to prevent
    startup flicker
2018-06-28 16:28:27 +10:00
Tim Allen
b14c6bf155 Update to v106r43 release.
byuu says:

Changelog:

  - bsnes: added video settings panel
  - bsnes: added audio settings panel
  - bsnes: disable assign/clear buttons at startup for hotkeys panel
  - bsnes: program initialization restructured: drivers initialize last
      - this lets me reinitialize the settings panel values on driver
        changes
      - so eg things like input/hotkey remappings should work after
        input driver changes now
      - ... but I had to disable the window icon for this ... it takes
        too long to show up this way
  - bsnes: added synchronize video/audio options to settings menu
  - bsnes: added audio skew slider for video/audio synchronization
  - bsnes: state manager edit/remove works on game ROM .bsz archives now
  - bsnes: removed View→Color Emulation; default to 150% gamma instead
    (it's a touch brighter but similar)

At this point, I'm pretty much ready to make an initial beta release for
wider testing.

Please use this WIP to indicate any must-fix issues before I do so.
2018-06-27 11:56:27 +10:00
Tim Allen
5b97fa2415 Update to v106r42 release.
byuu says:

Changelog:

  - emulator: added `Thread::setHandle(cothread_t)`
  - icarus: added special heuristics support for the Tengai Maykou Zero
    fan translation
      - board identifier is: EXSPC7110-RAM-EPSONRTC (match on SPC7110 +
        ROM size=56mbit)
      - board ROM contents are: 8mbit program, 40mbit data, 8mbit
        expansion (sizes are fixed)
  - bsnes: show messages on game load, unload, and reset
  - bsnes: added support for BS Memory and Sufami Turbo games
  - bsnes: added support for region selection (Auto [default], NTSC,
    PAL)
  - bsnes: correct presentation window size from 223/239 to 224/240
  - bsnes: add SA-1 internal RAM on cartridges with BS Memory slot
  - bsnes: fixed recovery state to store inside .bsz archive
  - bsnes: added support for custom manifests in both game pak and game
    ROM modes
  - bsnes: added icarus game database support (manifest → database →
    heuristics)
  - bsnes: added flexible SuperFX overclocking
  - bsnes: added IPS and BPS soft-patching support to all ROM types
    (sfc,smc,gb,gbc,bs,st)
      - can load patches inside of ZIP archives (matches first “.ips” or
        “.bps” file)
  - bsnes/ppu: cache interlace/overscan/vdisp (277 → 291fps with fast
    PPU)
  - hiro/Windows: faster painting of Label widget on expose
  - hiro/Windows: immediately apply LineEdit::setBackgroundColor changes
  - hiro/Qt: inherit Window backgroundColor when one is not assigned to
    Label

Errata:

  - sfc/ppu-fast: remove `renderMode7Hires()` function (the body isn't in
    the codebase)
  - bsnes: advanced note label should probably use a lighter text color
    and/or smaller font size instead of italics

I didn't test the soft-patching at all, as I don't have any patches on
my dev box. If anyone wants to test, that'd be great. The Tengai Makyou
Zero fan translation would be a great test case.
2018-06-26 13:17:26 +10:00
Tim Allen
f70a20bc42 Update to v106r41 release.
byuu says:

Changelog:

  - hiro: added Label::set(Background,Foreground)Color (not implemented
    on Cocoa backend)
  - hiro: added (Horizontal,Vertical)Layout::setPadding()
      - setMargin(m) is now an alias to setPadding({m, m, m, m})
  - hiro/Windows: update Label rendering to draw to an offscreen canvas
    to prevent flickering
  - sfc: reverted back to 224/240-line height (from 223/239-line height
    in earlier v106 WIPs)
  - bsnes: new multi-segment status bar added
  - bsnes: exiting fullscreen mode will resize and recenter window
      - this is required; the window geometry gets all scrambled when
        toggling fullscreen mode
  - bsnes: updated to a new logo [Ange Albertini]

Errata:

  - hiro/Windows: try to paint Label backgroundColor quicker to avoid
    startup flicker
      - `WM_ERASEBKGND` fallthrough to `WM_PAINT` seems to work
  - hiro/Qt: use Window backgroundColor for Label when no Label
    backgroundColor set
  - bsnes: update size multipliers in presentation.cpp to 224/240 (main
    window size is off in this WIP)
2018-06-24 14:53:44 +10:00
Tim Allen
470e27323d accuracy/fast is now a runtime toggle, not compile-time. 2018-06-11 14:54:49 +10:00
Tim Allen
5a8c814e25 Update to v106r40 release.
byuu says:

Changelog:

  - hiro: added BrowserDialog::openObject() [match file *or* folder
    by filters]
  - hiro: BrowserDialog accept button is now disabled when it would
    otherwise do nothing
      - eg openFile without a folder to enter or file to open selected
      - eg saveFile without a file name or with a file name that matches
        a folder name
  - bsnes: added support for gamepaks (game folders)
  - bsnes: store all save states inside per-game .bsz (ZIP) archives
    instead of .bst/ folders
      - this reduces the number of state files from 10+ to 1; without
        having folders sort before files
  - hiro: both gtk2 and gtk3 now use cairo to render Canvas; supports
    sx,sy [BearOso]
  - higan, bsnes: fast PPU/DSP are now run-time options instead of
    compile-time options
  - bsnes: disable fast PPU when loading Air Strike Patrol / Desert
    Fighter
  - bsnes: disable fast DSP when loading Koushien 2
  - bsnes: added options to advanced panel to disable fast PPU and/or
    fast DSP
2018-06-11 14:50:18 +10:00
Tim Allen
91bb781b73 Update to v106r39 release.
byuu says:

Changelog:

  - ruby/video: implement onUpdate() callback to signal when redraws are
    necessary
  - ruby/video/GLX,GLX2,XVideo,XShm: implement onUpdate() support
  - bsnes: implement Video::onUpdate() support to redraw Viewport icon
    as needed
  - bsnes: save RAM before ruby driver changes
  - sfc/sa1: clip signed multiplication to 32-bit [Jonas Quinn]
  - sfc/sa1: handle negative dividends in division [Jonas Quinn]
  - hiro/gtk3: a few improvements
  - bsnes: added empty stub video and audio settings panels
  - bsnes: restructured advanced settings panel
  - bsnes: experiment: input/hotkeys name column bolded and colored for
    increased visual distinction
  - bsnes: added save button to state manager
2018-06-10 18:07:19 +10:00
Tim Allen
15b67922b3 Update to v106r38 release.
byuu says:

Changelog:

  - hiro: added Qt5 support
  - hiro: added GTK3 support (currently runs very poorly)
  - bsnes: number of recent games and quick state slots can be changed
    programmatically now
      - I may expose this as a configuration file setting, but probably
        not within the GUI
  - nall: use -Wno-everything when compiling with Clang
      - sorry, Clang's meaningless warning messages are just endless ...
2018-06-10 18:06:02 +10:00
Tim Allen
173a5d67bc Update to v106r37 release.
byuu says:

Changelog:

  - bsnes: cheat code “enabled” option changed to “enable”
  - bsnes: connected “Cancel” action on add/edit cheat code window
  - hiro: improved BrowserDialog::selectFolder() behavior
      - can choose “Select” inside of a target folder when no items are
        selected
  - bsnes: implemented state manager
  - bsnes: save a recovery state before loading a state, quitting, or
    changing drivers
  - bsnes: input settings, hotkey settings, cheat editor, state manager
    entries are now batchable
      - this allows bulk clearing/deleting of entries
  - bsnes: cheat code list now auto-sorts alphabetically instead of
    using up/down move arrows

I know most people will probably prefer to order cheat codes the way
they want, but the issue is that the state manager can't really work
this way. Each state is a file on disk. So yes, we could store a
states-manifest.bml to track the order of the states, or try to insert
numbers into the filenames and do bulk filesystem rename operations on
sorting, but then we would run into oddities when users delete state
files manually. And really, manual sorting is just clumsy. If you really
want a specific ordering, you can prefix cheats/states with numeric
indices instead.
2018-06-07 21:48:41 +10:00
Tim Allen
ec9729a9e1 Update to v106r36 release.
byuu says:

Changelog:

  - nall: renamed array to adaptive_array; marked it as deprecated
  - nall: created new array class; which is properly static (ala
    std::array) with optional bounds-checking
  - sfc/ppu-fast: converted unmanaged arrays to use nall/array (no speed
    penalty)
  - bsnes: rewrote the cheat code editor to a new design
  - nall: string class can stringify pointer types directly now, so
    pointer() was removed
  - nall: added array_view and pointer types (still unsure if/how I'll
    use pointer)
2018-06-04 12:44:57 +10:00
Tim Allen
77ac5f9e88 Update to v106r35 release.
byuu says:

Changelog:

  - sfc/ppu-fast: fixed overscan crash
  - sfc/ppu-fast: fixed direct color mode
  - sfc: reconnected MSU1 support
      - higan: game.sfc/msu1/data.rom, game.sfc/msu1/track-#.pcm
      - bsnes: game.msu, game-#.pcm
  - bsnes: added cheat code editor
  - bsnes: added cheat code database support
  - sfc/ppu-fast: clear overscan lines when overscan disabled
  - sfc: output 223/239 lines instead of 224/240 lines
  - bsnes: fix aspect correction calculation
  - bsnes: crop line 224 when overscan masking is enabled
  - bsnes: exposed Expansion Port menu; but hid “21fx” from the list of
    devices
  - bsnes: tools menu is hidden until a game is loaded
  - ruby/input/keyboard/quartz: fixed compilation error

So only bsnes the automated overscan cropping option. In higan, you can
crop however many lines you like from the top or bottom of the image.
But for bsnes, it automatically eats sixteen lines. My view right now is
that if bsnes is meant to be the casual gaming emulator, that it should
eat line 224 in this mode. Most games show content here, but because of
the way the SNES PPU works, the very last line ends up on its very own
tile row (line 0 isn't rendered), if the scroll registers don't account
for it. There's a small number of games that will draw junk data to the
very last scanline of the frame as a result of this. So I chose, at
least for now, to hide it. Users can obviously disable overscan cropping
to see this scanline. I'm open to being convinced not to do this, if
someone has a compelling reason.

We're pretty much screwed one way or the other with no overscan masking.
If we output 239 lines, then most games will render 7 blank lines + 224
drawn lines + 8 blank lines, and the black top and bottom aren't
centered. But if we output 240 lines to get 8 + 224 + 8, then games that
do use overscan will have a blank line at the very bottom of the window.

I'm also trying out a modified cheat code file format. It's been forever
since I bothered to look at it, and the “cartridge” parent node doesn't
match what I'm doing with trying to rename “cartridge” to “game” in
manifests. And indeed, the idea of requiring a root node is rather
superfluous for a cheat code file. Current format looks like this:

    cheat
      description: foo
      code: 7e2000=20+7e2001=30?40
      enabled

    cheat
      description: bar
      code: 7e4000=80

Open to discussing this, and I'd like to sync up with Snes9X before they
push out a new release, and I'll agree to finalize and never change this
format again.

I chose to use .cht for the extension when using game files (eg
gamename.cht)
2018-06-03 23:14:42 +10:00
Tim Allen
8c337d4ac6 Make sure the libretro core builds with the accuracy profile. 2018-06-03 15:01:54 +10:00
Tim Allen
73354923eb Add another task to the release procedure. 2018-06-03 14:55:45 +10:00
Tim Allen
3aa90590ca bsnes binaries do not need the systems directory. 2018-06-02 14:43:40 +10:00
Tim Allen
52d0cd8dfb Now that byuu is ready for bsnes bug reports, I'll enable automated builds.
Also, since byuu changes the defaults according to what he's working on, let's
be explicit tat higan gets the accuracy core and bsnes gets the performance
core.
2018-06-02 13:21:16 +10:00
Tim Allen
c67fb2c726 Update to v106r34 release.
byuu says:

Changelog:

  - sfc/ppu-fast:
      - don't use mosaicSize unless mosaicEnable is set
      - fix background tiles that aren't 8x8 in size
      - flush (render) queued lines whenever VRAM or OAM are modified
        mid-frame
      - queue tile outputs to buffer for object rendering final pass
      - fix object window mask indexing
      - disable color bleed when output width is 256 pixels
      - handle reset(bool) events
      - implemented save states
  - icarus: fixed SPC7110-RAM-EPSONRTC mapping typo [hex_usr]
  - bsnes: fixed overscan masking mode when output height is 240

Todo:

  - sfc/ppu-fast: should not have deleted the tilecache freeing in
    ~PPU()
  - ruby/input/carbon: change setPath() call to setPathID()

Errata:

  - Rendering Ranger R2 crashes at startup, seems to be an issue with
    the expansion port device

Bug reports on the new fast SNES PPU are now welcome.
2018-06-02 12:47:37 +10:00
Tim Allen
5d29700fa1 Update to v106r33 release.
byuu says:

Changelog:

  - nall/GNUmakefile: added `openmp=(true,false)` option; can be toggled
    when building higan/bsnes
      - defaults to disabled on macOS, because Xcode doesn't stupidly
        doesn't ship with support for it
  - higan/GNUmakefile: forgot to switch target,profile back from
    bsnes,fast to higan,accurate
      - this is just gonna happen from time to time, sorry
  - sfc/dsp: when using the fast profile, the DSP syncs per sample
    instead of per clock
      - should only negatively impact Koushien 2, but is a fairly
        significant speedup otherwise
  - sfc/ppc,ppu-fast: optimized the code a bit (ppu 130fps to 133fps)
  - sfc/ppu-fast: basic vertical mosaic support (not accurate, but
    should look okay hopefully)
  - sfc/ppu-fast: added missing mode7 hflip support
  - sfc/ppu-fast: added support to render at 256-width and/or 240-height
      - gives a decent speed boost, and also allows all of the older
        quark shaders to work nicely again
      - it does violate the contract of Emulator::Interface, but oh
        well, it works fine in the bsnes GUI
  - sfc/ppu-fast: use cached CGRAM values for mode7 and sprites
  - sfc/ppu-fast: use global range/time over flags in object rendering
      - may not actually work as we intended since it's a race condition
        even if it's only ORing the flags
      - really don't want to have to make those variables atomic if I
        don't have to
  - sfc/ppu-fast: should fully support interlace and overscan modes now
  - hiro/cocoa: updated macOS Gatekeeper disable support to work on
    10.13+
  - ruby: forgot to fix macOS input driver, sorry
  - nall/GNUmakefile: if uname is present, then just default to rm
    instead of del (fixes Msys)

Note: blur emulation option will break pretty badly in 256x240 output
mode. I'll fix it later.
2018-05-31 17:06:55 +10:00
Tim Allen
5e7fdbe2c0 Update to v106r32 release.
byuu says:

Changelog:

  - sfc/ppu-fast: everything other than vertical mosaic and interlace
    support is in

Games are quite playable now, and you're welcome to try things out, but
please don't report bugs yet. It's still too early for that.
2018-05-29 21:26:48 +10:00
Tim Allen
51e3fcd3fa Update to v106r31 release.
byuu says:

Changelog:

  - sfc/ppu-fast: added a barebones background renderer; very incomplete

Right now, the 2bpp Mega Man X2 splash screen is rendering correctly,
but everything else looks really garbled. I'm thinking my tile cache
conversions from 4bpp to bitmap pixels is wrong, but I'm not seeing any
obvious issues.

If anyone wants to take a look at it, I'd appreciate it. The renderer is
mostly modeled after ppu-performance's.
2018-05-28 11:51:38 +10:00
Tim Allen
18852bcbe2 We definitely don't need genius with bsnes for Windows. 2018-05-28 11:44:06 +10:00
Tim Allen
bcc2627793 Don't hack up nall/GNUmakefile for Windows builds.
All our changes are now in official nall releases, so we don't need to mess with
it anymore.
2018-05-28 11:42:52 +10:00
Tim Allen
685cec6583 Update to v106r30 release.
byuu says:

Changelog:

  - nall/GNUmakefile: fixed findstring parameter arguments [Screwtape]
  - nall/Windows: always include -mthreads -lpthread for all
    applications
  - nall/memory: code restructuring

I really wanted to work on the new PPU today, but I thought I'd spend a
few minutes making some minor improvements to nall::memory, that was
five and a half hours ago. Now I have a 67KiB diff of changes. Sigh.
2018-05-28 11:16:27 +10:00
Tim Allen
6882bd98cf Update to v106r29 release.
byuu says:

Changelog:

  - sfc/ppu: collapsed folders to a single directory to match all other
    emulated processors
  - sfc/ppu-fast: implemented I/O registers
2018-05-27 09:04:43 +10:00
Tim Allen
6c8e3c885d Update to v106r28 release.
byuu says:

Changelog:

  - SNES: started on skeleton of the new parallel PPU core

To build the new PPU core, set profile=fast via GNU make. The old core
is profile=accurate.

The names of the profiles, and the name of the folder for the fast PPU
are subject to change.

The new PPU core doesn't do anything but demonstrate the proof of
concept: every scanline, make a copy of all the PPU registers and CGRAM.
Share the VRAM and OAM. Batch render all scanlines at once using OpenMP
at the end of each frame and blit the result.

With no PPU core at all, bsnes runs 91% faster than with the accuracy
PPU (230fps vs 120fps.) That's the absolute theoretical best-case
scenario. With the skeleton in place, we're already around 220fps. It'll
go down more as the PPU line renderer starts to do real work. I don't
know where things will end up yet. I suppose we'll find out in time.

My own copy of TDM/GCC can't use OpenMP on Windows, so ... it won't
parallelize if you build with that. I'm going to have to switch to a
different MinGW distribution once this is complete, I suppose.
2018-05-26 13:29:14 +10:00
Tim Allen
8f5bc80f01 Ignore generated file dependency information. 2018-05-25 18:07:03 +10:00
Tim Allen
2b8df2e70e Update to v106r27 release.
byuu says:

Changelog:

  - nall: merged Path::config() and Path::local() to Path::userData()
      - ~/.local/share or %appdata or ~/Library/ApplicationSupport
  - higan, bsnes: render main window icon onto viewport instead of
    canvas
      - should hopefully fix a brief flickering glitch that appears on
        Windows
  - icarus: improved Super Famicom heuristics for Starfox / Starwing RAM
  - ruby/Direct3D: handle viewport size changes in lock() instead of
    output()
      - fixes icon disappearing when resizing main window
  - hiro/Windows: remove WS_DISABLED from StatusBar to fix window
    resize grip
      - this is experimental: I initially used WS_DISABLED to work
        around a focus bug
      - yet trying things now, said bug seems(?) to have gone away at
        some point ...
  - bsnes: added advanced settings panel with real-time driver change
    support

I'd like feedback on the real-time driver change, for possible
consideration into adding this to higan as well.

Some drivers just crash, it's a fact of life. The ASIO driver in
particular likes to crash inside the driver itself, without any error
messages ever returned to try and catch.

When you try to change a driver with a game loaded, it gives you a scary
warning, asking if you want to proceed.

When you change a driver, it sets a crash flag, and if the driver
crashes while initializing, then restarting bsnes will disable the
errant driver. If it fails in a recoverable way, then it sets the driver
to “None” and warns you that the driver cannot be used.

What I'm thinking of further adding is to call emulator→save() to
write out the save RAM contents beforehand (although the periodic
auto-saving RAM will handle this anyway when it's enabled), and possibly
it might be wise to capture an emulator save state, although those can't
be taken without advancing the emulator to the next frame, so that might
not be a good idea.

I'm also thinking we should show some kind of message somewhere when a
driver is set to “None”. The status bar can be hidden, so perhaps on the
title bar? Or maybe just a warning on startup that a driver is set to
“None”.
2018-05-25 18:02:38 +10:00
Tim Allen
ec4ab1dc11 Update GitLab CI settings.
We shouldn't need to specify static compiler helpers anymore,
and ruby now uses SDL2.0 rather than 1.2.
2018-05-24 13:10:24 +10:00
Tim Allen
5961ea9c03 Update to v106r26 release.
byuu says:

Changelog:

  - nall: added -static-libgcc -static-libstdc++ to Windows/GCC link
    flags
  - bsnes, higan: added program icons to main window when game isn't
    loaded
  - bsnes: improved recent games menu sorting
  - bsnes: fixed multi-game recent game loading on Windows
  - bsnes: completed path override support
  - bsnes, higan: added screensaver suppression on Windows
  - icarus: add 32K volatile RAM to SuperFX boards that report no RAM
    (fixes Starfox)
  - bsnes, higan: added automatic dependency generation [Talarubi]
  - hiro/GTK: appending actions to menus restores enabled() state
  - higan: use board node inside manifest.bml if it exists
  - bsnes: added blur emulation and color emulation options to view menu
  - ruby: upgraded input.sdl to SDL 2.0 (though it makes no functional
    difference sadly)
  - ruby: removed video.sdl (due to deprecating SDL 1.2)
  - nall, ruby: improvements to HID class (generic vendor and product
    IDs)

Errata:

  - bsnes, higan: on Windows, Application::Windows::onScreenSaver needs
    `[&]` lambda capture, not `[]`
      - find it in presentation/presentation.cpp
2018-05-24 12:14:17 +10:00
Tim Allen
3353efd3a1 Update to v106r25 release.
byuu says:

Changelog:

  - bsnes:
      - added full input mapping support (multi-mapping, digital+analog
        inputs, rumble, hotkeys, etc)
      - can now load multi-part games (eg Super Game Boy) from the
        command-line
      - added recent games menu with list clear function; supports
        multi-part games (sorting logic incomplete)
      - added automatic binding of gamepads on new configuration files
      - added view scaling support with aspect correction, overscan
        cropping, and integral scaling modes
      - added video shader support
      - added status bar (can be hidden)
      - added save states (both menu and hotkeys)
      - added fullscreen mode support
      - added support for loading compressed (ZIP) archives for any
        supported media type (SNES, GB, etc)
      - added frame counter
      - added auto-memory saving
      - added pause / block-input modes when main window loses focus
      - added --fullscreen command-line option to start bsnes in
        fullscreen mode
      - added input settings panel
      - added hotkeys settings panel
      - added path settings panel (paths aren't actually used set, but
        can be assigned)
  - higan: fixed macOS install rule [Sintendo]
  - higan: minor UI code cleanups
  - nall: renamed Processor to Architecture to fix macOS builds
    [Sintendo]

Yeah, you read right: recent games menu, path settings. And dynamic rate
control + screensaver suppression is on the todo list. I'm not fucking
around this time. I really want to make something special here.
2018-05-23 13:45:24 +10:00
Tim Allen
a73a94f331 Update to v106r24 release.
byuu says:

Changelog:
* yes.

But seriously, a list of changes on a pre-alpha GUI is going to get annoying.

Basically, work on embedding stuff in the binary, firmware loading (both
appended to the ROM and in a firmware/ subfolder) added, SGB games can be
loaded, config file holds more values for driver settings, added ruby drivers to
other platforms, etc.
2018-05-20 14:39:29 +10:00
Tim Allen
7ee1534093 bsnes won't need icarus to load games. 2018-05-19 15:39:48 +10:00
Tim Allen
87e2154ea1 Update the link to the nightly builds. 2018-05-19 12:54:41 +10:00
Tim Allen
d8bd1fca1f Temporarily disable bsnes binaries at byuu's request.
Once it gets to the point of actually doing something, we can re-enable them.
2018-05-19 12:52:59 +10:00
Tim Allen
7acbf5c3dd Ignore bsnes binaries too. 2018-05-19 12:52:26 +10:00
Tim Allen
ea11c6d098 Update to v106r23 release.
byuu says:

Changelog:

  - bsnes: work on the new GUI; can load games now, but no input support
    yet
  - icarus: heuristics game/label uses filename instead of internal
    header name
2018-05-19 12:51:34 +10:00
Tim Allen
f5b96e9e9e Set up automatic WIP builds for the new bsnes front-end too. 2018-05-18 15:26:41 +10:00
Tim Allen
6078cdacbb Update to v106r22 release.
byuu says:

Changelog:

  - created new bsnes target (it currently does nothing)
  - Super Famicom: fixed BS Memory pack support in the MCC emulation
  - icarus: fixed manifest-free support for BS Memory flash-based
    cartridges
  - icarus: database improvements
2018-05-18 15:21:22 +10:00
Tim Allen
c2648faeab Mention the final manifest spec. 2018-05-17 20:20:31 +10:00
Tim Allen
2e14bd1c81 Let's prefer footnote-style link syntax. 2018-05-17 20:11:07 +10:00
Tim Allen
cd5dde0f62 Update more docs for v107. 2018-05-17 20:10:57 +10:00
Tim Allen
e5f19e49d4 Update importing docs for v107. 2018-05-17 17:10:06 +10:00
Tim Allen
c6ed8bb4b1 Warn that icarus in command-line mode doesn't offer much feedback. 2018-05-17 17:09:28 +10:00
Tim Allen
c24eb6e592 Update some import instructions. 2018-05-17 14:32:43 +10:00
Tim Allen
d537eaa0fd Start updating the docs for v107. 2018-05-17 14:32:42 +10:00
Tim Allen
8bbbc5e737 Update to v106r21 release.
byuu says:

Changelog:

  - higan: target-tomoko has been renamed to target-higan
  - Super Famicom: event has been renamed to
    processor(architecture=uPD78214)
  - Super Famicom: SNES-EVENT supported once more; under board IDs
    EVENT-CC92 and EVENT-PF94
  - Super Famicom: SNES-EVENT preliminarily set up to use DIP switch
    settings ala the Nintendo Super System (incomplete)
  - Super Famicom: MCC PSRAM moved inside the MCU, as it is remappable
  - Super Famicom: MCC emulation rewritten from scratch; it is now
    vastly more accurate than before
  - Super Famicom: added BSC-1A5B9P-01 board definition to database;
    corrected BS-MCC-RAM board definition
  - Super Famicom: moved SHVC-LN3B-01 RAM outside of
    processor(identifier=SDD1)
  - higan: when selecting a default game to load for a new system entry,
    it will change the system option to match the media type
  - higan: the load text box on the system entry window is now editable;
    can be used to erase entries
  - icarus: fixed bug in Famicom importing
  - icarus: importing unappended SNES coprocessor firmware will now
    rename the firmware properly
  - hiro/GTK,Qt: WM_CLASS is now set correctly in `argv[0]`, so
    applications should show “higan”, “icarus” instead of “hiro” now

Note: if you wish to run the BS-X town cartridge, the database currently
lists the download RAM as type “PSRAM”. This needs to be changed to
“RAM” in order to load properly. Otherwise, the emulator will bomb
out on the load window, because BSC-1A5B9P-01 expects PSRAM to always be
present, but it won't find it with the wrong memory type. I'll correct
this in the database in a later release. For now, you can copy the game
portion of the manifest to a new manifest.bml file and drop it into the
gamepak folder until I fix the database.
2018-05-17 13:37:29 +10:00
Tim Allen
210306e661 Update to v106r20 release.
byuu says:
Changelog:

  - Super Famicom: fixed loading of BS Memory and Sufami Turbo
    cartridges
  - Super Famicom: renamed NSS to DIP; as that's really all it is, it's
    not true NSS emulation
  - Super Famicom: slot loading now happens inside of board parsing
    instead of generically in loadCartridge()
  - Super Famicom: BS-X cartridges with flash memory now serialize their
    data and write it out to disk¹
  - icarus: fixed Famicom game importing (hopefully) and set file import
    title to “Load ROM File”

¹: there's no emulation of write commands yet, so the data is never
going to change anyway. This is just in preparation for more advanced
emulation of BS Memory cartridges.
2018-05-15 00:13:30 +10:00
Tim Allen
6847058210 Update to v106r19 release.
byuu says:

Changelog:

  - Super Famicom: everything outside of Nintendo Super System, Campus
    Challenge '92 and Powerfest '94 should play
  - Super Famicom: removed RAM from coprocessor/event (should use global
    RAM)
  - Super Famicom: removed RAM from SDD1 (should use global RAM)
  - icarus: fixed Super Famicom game importing [hex_usr]

Also worth reminding that you'll need to disable database lookup in
order to run the BS-X Town cartridge right now. Plus, Star Ocean's
database entry still has the RAM in the wrong spot. The MSU1 code is not
looking at the right locations for data, so it's not going to work in
this release either.

I need to figure out what to call coprocessor/event and coprocessor/nss,
as neither are slots or processors like everything else.

Outside of those issues, all games for all systems should be playable,
at least to the extent they were in v106.
2018-05-13 23:00:48 +10:00
Tim Allen
b7dca2f317 Update the CI builds to match the new directory structure. 2018-05-09 14:04:45 +10:00
Tim Allen
b69909be8d Update to v106r18 release.
byuu says:

Changelog:

  - major restructuring of board manifests
  - cleanup of generic board names
  - Super Famicom: updates to SA1, SuperFX, Cx4, SPC7110, EpsonRTC,
    SharpRTC load/save code
  - Super Famicom: added experimental SuperFX plot dithering fix
    [qwertymodo]
  - higan, icarus: rename shared folders to lowercase names; put .sys
    folders into new subfolder
      - Video Shaders/ → shaders/

      - Database/ → database/

      - Firmware/ → firmware/

      - \*.sys/ → systems/\*.sys/

So right now, only standard SNES games, SA-1, SuperFX, and Cx4 games
load. I have not tested SPC7110 or RTC support, because icarus import
seems to be completely broken? It's creating blank folders when I try it
now. I'll have to fix that ...

Since we are now up to thirteen systems, I've put the .sys folders into
a subfolder. This should declutter the main higan-windows release folder
a good deal. Linux users will need to re-run make install, or manually
move things into a new systems/ subfolder.

Same goes for icarus: lowercase the database/ and firmware/ folders or
re-run make install.

I don't know if qwertymodo's SuperFX fix is exactly correct or not.
Hopefully it is, but I didn't write a test ROM or anything to be
certain. Since SuperFX games should run, if people could please play
through some of them and look for any regressions, that'd be very much
appreciated.
2018-05-09 12:12:06 +10:00
Tim Allen
8617711ea2 Update to v106r17 release.
byuu says:

Changelog:

  - tomoko: the library menu is now called the systems menu (even in
    code)
  - tomoko: added icons to menus (disambiguates systems menu entries)
  - icarus: added missing .ws, .wsc extensions to scan dialog search
    list
  - higan: added Benesse - Pocket Challenge V2 emulation¹

¹: the Benesse - Pocket Challenge V2 is a WonderSwan (ASWAN) SoC
inside a custom designed shell. Games made for the WonderSwan (mostly)
run on the Pocket Challenge V2 and vice versa. The big difference is
that the Benesse has a different number of input buttons, that are also
named differently. Of course, right now, I don't know what the buttons
are named or where they're mapped on the 16-input keypad matrix I/O
port. It's also possible that the internal EEPROM doesn't exist, it
definitely has a unique (and also undumped) IPLROM, and other things.
The ROMs have their own .pc2 file extension. So it's getting its own
system entry.

What I'm going to do for v107 and above is utilize the new systems
configuration to mark the Benesse as hidden by default from the main
menu. I don't think anyone in the world will actually care or want to
play this, but there was really no reason not to add it.
2018-04-25 19:34:43 +10:00
Tim Allen
540d960e30 ikari_01 pointed out they contributed to commit bc0b86891
https://board.byuu.org/viewtopic.php?p=53609#p53609
2018-04-17 21:48:09 +10:00
Tim Allen
8023d9cbe8 Update to v106r15 release.
byuu says:

Changelog:

  - main menu renamed again (Library→System→Systems)
  - the 'Hidden' checkbox on system properties was moved to the main
    list as a 'Show' checkbox instead
  - the move up/move down buttons on the systems panel now function
  - added icons to indicate 'system' versus 'game boot' entries in the
    systems list

I still didn't add ComboEdit to the Windows hiro port, so once again
this will be Linux/BSD only.

I polished the browse button for selecting a boot game. It'll use a list
of all bootable media file extensions, so that if you double-click any
supported game, it'll select it instead of going inside the folder. If
you pick a non-bootable folder, like say a Sufami Turbo cartridge, it
will go inside the folder instead, because it will treat it like any
other regular folder.

Having a checkbox next to text in the same cell doesn't work so well on
lists that have an onActivate action. Say you tried to double-click the
“Name” field on the Systems tab, it would toggle the checkbox twice
before popping open the system properties editor window. So because of
this, I made the show checkbox its own column. And for consistency, I
did the same for the slot# on the cheat editor window.

As a bit of really pedantic polish: if there are no systems enabled,
then the main menu won't show the separator before the “Load ROM Image”
option anymore.

I think something is wrong with the Markup::Node::insert syntax, but I
realized I have Markup::Node::swap, and just used that for the up/down
arrows on the systems panel for now. But we should probably fix insert()
at some point ... sigh.
2018-04-17 19:18:14 +10:00
Tim Allen
0ea17abfea Update to v106r15 release.
byuu says:

Changelog:

  - Super Game Boy: fixed loading of boot ROM
  - hiro: added ComboEdit::setEditable(bool = true);
  - tomoko: added new systems settings panel

Note!!: this release will not compile on Windows or macOS due to the
missing ComboEdit control! I'll try to merge in hex's implementation
for the Windows release here soon. macOS users will probably be out of
luck for a while, sorry.

The new systems panel is an idea I've been meaning to implement for
quite a while, but finally got around to starting on it. It's still
fairly unpolished, but the basic idea is there for Linux/BSD users to
try out now.

So imagine the Super Game Boy, BS-X Satellaview, Sufami Turbo, and the
associated BS Memory Pack-slotted SNES cartridges. To play any of those,
you needed to choose Nintendo→Super Famicom, and then select the
relevant cartridge, and then select any slotted cartridges to play with
it.

This was acceptable-ish, if not ideal. But now imagine in the future if
we wanted to support the Famicom Disk System, which is technically a
cartridge that plugs into the Famicom deck. Or the PC Engine CD, which
has one of three special HuCards that must be inserted (ignoring the
Turbo Duo where it's built-in—I'm going to be emulating the Super CD
as if you're using a stock PCE CD.) Or the Mega CD, where there are
probably a half dozen or more BIOS + hardware revisions that are
region-specific, which connect to an expansion port that is identical to
the cartridge port save for the Mega Drive seeing an I/O register bit
toggled here.

In all of these cases, it's going to be a real pain to have to choose
the 'BIOS' every time you want to play a game for them.

I can't distribute these BIOSes with higan due to copyright
restrictions, and trying to ship dummy folders for every possible
combination would become quite odious, and difficult for people to use
(compare to setting up the Game Boy Advance system BIOS.)

And so I've created the new systems settings panel. Here, you can manage
a list of systems that show up under the higan library menu (now renamed
to “System”), where each entry contains name, boot, and hidden
parameters.

The name parameter is what shows up in the system menu. You can call any
system higan emulates whatever you like here. Don't like “Super
Famicom”? Change it to “SNES”, then.

The boot parameter is a combo edit with a dropdown for all of the
systems higan emulates. If you choose one of these, then the higan
system menu option will work exactly like in previous releases, and
prompt you for a cartridge. But if you choose the browse button next to
the combo edit control, you'll get to pick any gamepak from the higan
library of your choosing.

So you could choose the SGB2 BIOS, and name the menu option “Super Game
Boy 2”, and when you choose the menu option, it will load the SFC core,
load the SGB2 BIOS, and only prompt you for the Game Boy game you wish
to play on it. The same deal goes for the FDS, PCE-CD, Mega CD, Mega
Drive Sonic & Knuckles lock-on cartridge, BS-X Satellaview, SD Gundam
G-Next, etc. Whatever you want to be in the menu, you can put in there
by pointing higan at the appropriate 'BIOS' gamepak to load.

Astute readers have probably already noticed, but you can technically
use this on non-slotted games as well, thus creating instant boot
options for your absolute favorite games, if you so wanted. Point it at
Zelda 3, and you can boot it instantly from the main menu, without any
need for file selection.

The hidden option is a way to hide the system entries from the system
menu. Primarily this would be a fast way for users to disable emulation
cores they never use in higan, without having to remove the options.

The major concession with this change is the collapsing of the
per-manufacturer submenus. What this means is you will now have all
twelve higan emulated systems in the main menu by default. This makes
the list rather long, but ... oh well. I may try to offer some form of
grouping in the future, but the grouping defeats the “list order =
display order” design, and I'm not willing to auto-sort the list. I want
people to be able to control the ordering of the system menu, and have
added (as yet non-functional) sorting arrows for that purpose. I also
don't have a combined tree+table view widget in higan to try to and
group things. But ... we'll see how things go in the future.

Another idea is to add a specialty load option that opens up the user's
Emulation library path, and lets you pick a gamepak for any system,
which would boot the same way as when you drop a gamepak onto the higan
executable or main window. So say you almost never play Wonderswan
games, this would be a way to play them without them cluttering your
system menu list.

The “import ROM files” option has been removed. All it does is launch
icarus directly. I would rather users become familiar with using icarus.
The “load ROM file” option remains.

Anyway, this is all still a work in progress, so please give it time and
don't overload me with too many suggested changes right now, thanks :3
2018-04-16 18:58:13 +10:00
Tim Allen
8f61c267c5 Update to v106r14 release.
byuu says:

Changelog:

  - game/memory/type/battery → game/memory/volatile
  - (manufacturer.)content.type → (architecture.)content.type
  - nall: Markup::find() strips spaces from values in comparisons
  - higan: updated game manifest loading/saving code for all cores
  - GBA: flash memory ID is internally selected based on the
    manufacturer and memory size
  - SFC: ST018 (ARM6) frequency can be modified via game manifest now
  - WS: EEPROM::name removed (not useful)
  - icarus, genius: battery→volatile updates

I did my best to look over the diff between r13 and r14, but it's 84KiB
excluding the game database changes. It's just too much for me. I'd
greatly appreciate if someone could look over it and check for any
errors in this update. But more than likely, I suppose we'll iron out
any issues by determining which games fail to load.

Right now, I know the Super Game Boy support doesn't seem to work. But
all non-SFC cores should work fully, and all normal + NEC DSP SFC games
should work as well. Unsure about the rest.

Also, I'm planning to change the Game Boy “MBC1M” mapper to “MBC1#A” to
indicate it's an alternate wiring configuration of the stock MBC1, and
not a new mapper type.
2018-04-15 15:49:53 +10:00
Tim Allen
eaa2c1f6c0 Update to v106r13 release.
byuu says:

Changelog:

  - game/memory/category → game/memory/content
  - game/memory/model → game/memory/architecture
  - game/memory/identity → game/memory/identifier
  - Super Famicom: memory/content=Bitmap → memory/content=Save
  - Super Famicom: memory/architecture=DMG,MGB →
    memory/architecture=LR35902

The game manifest field names are now officially set in stone. I won't
change them again, I'll only add new fields if required.

As for the values in the field, I'm still undecided on the manufacturer
of the ST018, and I could be talked into different identifiers for the
Super Game Boy (SGB1/SGB2, DMG/MGB, or just ICD(2)?)

The board manifest format is still in flux, as is the choice of what to
name firmware files (it's between manufacturer and architecture, where
I'm leaning toward the latter currently.)

Board memory to Game memory mappings will require both the manufacturer
and architecture fields to match.

I'll be updating doc.byuu.org soon with the finalized game manifest
format.
2018-04-09 09:50:42 +10:00
Tim Allen
985610c167 Update to v106r12 release.
byuu says:

Changelog:

  - Emulator: update to final manifest syntax
  - Super Famicom: new board syntax (still experimental)
  - Super Famicom: match (manufacturer.)category.type instead of
    (model.)category.type

Errata:

  - Markup::Node::find() needs to be extended to support multiple
    subtype matches
  - Sufami Turbo ROM/RAM nodes are part of separate gamepaks; need to
    refactor this
2018-04-03 17:40:03 +10:00
Tim Allen
72b824cf1a Update to v106r11 release.
byuu says:

Changelog:

  - genius: improve sorting when game name is identical (eg revisions)
  - icarus, genius: update to finalized manifest syntax
2018-03-14 14:51:35 +11:00
Tim Allen
2dd35f984d Update to v106r10 release.
byuu says:

Changelog:

  - manifest: memory/battery now resides under type at
    memory/type/battery
  - genius: volatile option changed to battery; auto-disables when not
    RAM or RTC type
  - higan: added new Emulator::Game class to parse manifests for all
    emulated systems consistently
  - Super Famicom: board manifest appended to manifest viewer now
  - Super Famicom: cartridge class updated to use Emulator::Game objects
  - hiro: improve suppression of userland callbacks once
    Application::quit() is called
      - this fixes a crash in genius when closing the window with a tree
        view item selected

My intention is to remove Emulator::Interface::sha256(), as it's not
really useful. They'll be removed from save states as well. I never
bothered validating the SHA256 within them, because that'd be really
annoying for ROM hackers.

I also intend to rename Emulator::Interface::title() to label() instead.

Most everything is still broken. The SNES still needs all the board
definitions updated, all the other cores need to move to using
Emulator::Game.
2018-03-06 09:42:10 +11:00
Tim Allen
e216912ca3 Update to v106r09 release.
byuu says:

Changelog:

  - higan, icarus, genius: new manifest syntax (work in progress)

Pretty much only LoROM and HiROM SNES games will load right now, and RAM
will only work right if the save.ram file already exists to pull its
file size from (a temporary cheap hack was used.)

Basically, I'm just getting this out there for evaluation.

One minor errata is that I switched icarus to using “memory/battery” to
indicate battery-backed RAM, whereas genius still uses “memory/volatile”
to indicate non-battery-backed RAM.

I intend to make it “memory/battery” in genius, and have the field
auto-enable when RAM or RTC is selected for type (obviously allowing it
to be unchecked for volatile memory.)

I need to update all 64 production boards, and 25 of 29 generic boards,
to use the new slot syntax; and I also need to update every single core
in higan to use the new manifest game syntax. I want to build out a
generic manifest game parser that all emulation cores will use.

Once I finish this, I'll also need to write a database converter to
update all of my licensed game dumps to the new database syntax.

I also need to write up something for doc.byuu.org explaining the new
manifest game syntax. The manifest board syntax will still be “internal”
and subject to revisions, but once v107 is out, the gamepak manifest
format will be set in stone sans extensions.
2018-03-05 15:34:07 +11:00
Tim Allen
a4a3d611a6 The SGB2 timing change is fixed! 2018-02-21 20:58:39 +11:00
Tim Allen
5c55cc2c94 Update to v106r08 release.
byuu says:

Changelog:

  - Game Boy: fixed RAM/RTC saving¹
  - Super Famicom: ICD2 renamed to ICD (there exists an SGB prototype
    with a functionally identical ICD1)
  - Sufami Turbo: removed short-circuiting when loading an unlinkable
    cartridge into slot A²
  - Super Game Boy: the 20971520hz clock of the SGB2 is now emulated
  - Super Famicom: BSC-1Lxx (SA1) boards now prompt for BS memory
    cartridges; and can make use of them³
  - Super Famicom: fixed a potential for out-of-bounds reads with BS
    Memory flash carts

¹: I'm using a gross hack of replacing `type: ` with `type:` so that
`memory(type=...)` will match without the extra spaces. I need to
think about whether I want the BPath query syntax to strip whitespace or
not. But longer term, I want to finalize game/memory's design, and build
a higan/emulation/manifest parser that produces a nicer interface to
reading manifests for all cores, which will make this irrelevant for
higan anyway.

²: I don't think it's appropriate for higan to enforce this. Nothing
stops you from inserting games that can't be linked into a real Sufami
Turbo. I do short-circuit if you cancel the first load, but I may allow
loading an empty slot A with a populated slot B. I think the BIOS does
something when you do that. Probably just yells at you.

³: I know it's emulated correctly now, but I still don't know what
the heck changes when you load the SD Gundam G Next - Unit & Map
Collection BS Memory cartridge with SD Gundam G Next to actually test
it.
2018-02-21 20:53:49 +11:00
Tim Allen
c49d3b2006 Update to v106r07 release.
byuu says:

Changelog:

  - Super Game Boy: for the 50th time, higan won't segfault if you
    cancel the Game Boy cartridge load request
  - icarus: moved to new manifest syntax for all remaining systems
  - Game Boy: moved to new manifest syntax

Errata:

  - Game Boy: save RAM does not appear to be working for some reason
  - Famicom: higan won't even start to run this system; it just acts
    like a cartridge was never loaded ...
  - cores outside of the Super Famicom and Game Boy/Color will not run
    due to icarus/higan manifest syntax differences
2018-02-21 11:12:09 +11:00
Tim Allen
61091167b8 Disable Windows builds of genius. 2018-02-16 12:53:19 +11:00
Tim Allen
610d42d573 Update the docs to describe the new v106r06 firmware import system. 2018-02-16 12:33:32 +11:00
Tim Allen
f8a6cc2cbd Include icarus firmware with nightly builds. 2018-02-16 12:09:23 +11:00
Tim Allen
3a175ad2b0 Update to v106r06 release.
byuu says:

Changelog:

  - icarus: new Firmware/ folder, which is used to import external
    firmware when it's missing from the ROM image
  - icarus: improved Super Famicom heuristics; including Shift-JIS to
    UTF-8 encoding of game titles

Errata:

  - if firmware isn't appended, it still cuts out the size from the
    memory/program.rom file
  - boards.bml is still missing the new Japanese production boards
2018-02-16 12:07:49 +11:00
Tim Allen
5e330da4e8 Update to v106r05 release.
byuu says:

Changelog:

  - Super Famicom: added remaining generic board types
  - icarus: improved Super Famicom heuristics
  - icarus: reworked BS Memory heuristics
  - icarus: reworked Sufami Turbo heuristics

Notes: this is really complicated, and is going to take a long time to
work 100% smoothly again.

Starting off, I am trying to get rid of the weird edge case zero-byte
SRAM mapping for the Cx4. It has the RAM region present, but returns
logic low (0x00) instead of open bus, when SRAM isn't present. I started
by making it `map=ram` instead of `ram/map`, which is gross, and then it ended
up detecing the map tag ending in RAM and pulling the Cx4 data RAM into that
slot. Ugh. The preservation board mapping is still as it was before and will
need to be updated once I get the syntax down.

The BS Memory and Sufami Turbo moving to the new `game/memory`
ending means I can't use the SuperFamicom::Cartridge::loadMemory
function that looks at the old-style rom/ram tags. Because I didn't
write more code, the result is those sub-carts won't load now.

The old heuristics were short-circuiting on SA1 before bothering with
BS-X slots, so that's why SD Gundam G-Next wasn't asking for a data
pack. The problem is, I don't know where the BS-X pack maps to on this
cartridge. It's at c0-ef on the other BS-X slotted cartridges, but
that's mapped to the SA1 on regular SA1 cartridges, so ... for now, it's
not actually mapped in.

I'm still struggling with naming conventions on all these boards. I'll
make a public post about that, though.
2018-02-11 08:45:44 +11:00
Tim Allen
c38a771f22 Update to v106r04 release.
byuu says:

Changelog:

  - nall: `Markup::Node::operator[]` now uses `find()` instead of `lookup()`
    behind the scenes
  - Super Famicom: RAM memory ordering is now independent of ROM memory
    ordering
  - Super Famicom: added 19 new generic board definitions
  - icarus: improved Super Famicom heuristics generation

Not putting it in the changelog, but the SPC7110 RAM now has write
protection disabled again.

99% of games should now be playable with heuristics. The exceptions
should be:

  - 4MB LoROM games with SRAM (Ys 3, FE: Thracia 776)
  - 2MB DSP LoROM games
  - BS-X Town
  - BS-X slotted games
  - SA1 BSX slotted games
  - SPC7110 games without the RTC (Momotarou Dentetsu Happy, Super Power
    League 4)
  - SPC7110 7MB fan translation (wasn't supported earlier either)
  - ExLoROM games (wasn't supported earlier either)
  - Sufami Turbo
  - Campus Challenge '92 and Powerfest '94
  - ST010 is going to run at 15MHz instead of 11MHz
  - MSU1 (needs to be supported in higan, not icarus)

I'll add support for most of these before the release of v107.
2018-02-08 21:32:46 +11:00
Tim Allen
3d8be92550 Update to v106r3 release.
byuu says:

Changelog:

  - Super Famicom: update to newer board markup syntax
  - Super Famicom: update all mapped ROMs to be write-protected
      - errata: SPC7110 set ram.writeProtect(true), I'll fix it in the
        next WIP
  - icarus: rewrote the Super Famicom heuristics module from scratch

Instead of icarus heuristics generating higan-specific mappings, it now
generates generic board IDs that can be used by any emulator. I had
originally planned to print out real PCB ID codes here, but these board
mappings are meant to be more generic, and I don't want them to look
real. The pseudo-codes are easy to parse, for example: `DSP-LOROM-NVRAM`
for Super Mario Kart, `SUPERFX-RAM` for Doom.

I'm going to make a `Boards (Generic).bml` file that will contain mapping
definitions for every board. Until this is done, any games not in the SNES
preservation database will fail to play because the mapping information is
now missing.
2018-02-05 20:58:02 +11:00
Tim Allen
38fbcd5277 Include the new genius tool in higan nightlies, because why not? 2018-02-01 19:29:18 +11:00
Tim Allen
0cb3529547 higan's makefiles no longer require overriding the compiler. 2018-02-01 19:26:42 +11:00
Tim Allen
2f81b5a3e7 Update to v106r2 release.
byuu says:

Changelog:

  - Super Famicom: added support for loading manifests without embedded
    mapping information¹
  - genius: initial commit
  - various Makefile cleanups

¹: so the idea here is to try and aim for a stable manifest format,
and to allow direct transposition of icarus/genius database entries into
manifest files. The exact mechanics of how this is going to work is
currently in flux, but we'll get there.

For right now, `Super Famicom.sys` gains `boards.bml`, which is the raw
database from my board-editor tool, and higan itself tries to load
`boards.bml`, match an entry to game/board from the game's `manifest.bml`
file, and then transform it into the format currently used by higan. It
does this only when the game's `manifest.bml` file lacks a board node.
When such a board node exists, it works as previous versions of higan
did.

The only incompatible change right now is information/title is now
located at game/label. I may transition window title display to just use
the filenames instead.

Longer term, some thought is going to need to go into the format of the
`boards.bml` database itself, and at which point in the process I should
be transforming things.

Give it time, we'll refine this into something nicer.
2018-02-01 19:20:37 +11:00
Tim Allen
aef8d5e962 Update to v106r1 release.
byuu says:

Changelog:

  - Z80: infinite DD/FD prefixes will no longer cause an emulator crash;
    but will still deadlock savestates
  - Z80: emulated R incrementing on M1 cycles
  - Z80: `LD a, [ir]` should update flags [hex_usr]
  - Z80: minor code cleanups
  - tomoko: added “Pause Emulation” toggle to Tools menu
      - you can still use the hotkey to pause emulation before starting
        a game if you really want to
      - this will be useful if and when I re-add trace logging to
        capture instructions from power-on
  - icarus: more PAL games added to the SNES database

I hope I've implemented R correctly. It should only increment twice on
DD,FD CB xx instructions. LDI/LDD/LDIR/LDDR should work as expected as
well. It increments once when interrupts are executed (and not maksed.)
The top bit is ignored in increments.
2017-12-27 08:11:03 +11:00
Tim Allen
a5af5eab3c Always hyphenate "hand-held".
There were previously three instances of "hand-held" and one of
"handheld", so let's go with the majority
2017-11-21 14:47:42 +11:00
Tim Allen
41efdba45a Document the "soft reset" functionality.
This was added in v105r01, but I forgot about it until now. :/
2017-11-21 14:47:11 +11:00
Tim Allen
b55783c322 Update to v106 release.
byuu says:

Changelog (since v105tr1):

Changelog:

  - added Emulation/AutoSaveMemory/Interval setting to specify number of
    seconds between auto-saves
  - added Game Notes tool
  - added 64 new SNES PAL games to the icarus preservation database

The Games Notes tool is a new feature that gives you a blank text box to
enter notes about a game that you're playing: so you can write down
things like level select codes for games with save RAM, combo moves, or
whatever other information you'd like quick and easy access to.

This is kind of an experiment. Ideally, we'd wanna allow more
personalized information, drawings, etc. But hey, let's try it out and
see what people think.
2017-11-19 23:05:02 +11:00
Tim Allen
3ec08cebbe Add credits from Talarubi's README.TXT to the docs. 2017-11-12 17:10:37 +11:00
Tim Allen
8d7d452534 We don't need to override compiler when building.
It seems on Windows, `compiler` has defaulted to `g++` for a while now,
so we didn't need to override it in the `make` invocation.

Since v105r01, `compiler` defaults to `g++` on Linux too, so we don't
need to override it there either.
2017-11-11 22:21:31 +11:00
Tim Allen
56cb9c01a5 Hack around some code-block formatting errors.
By default, mkdocs uses "highlight.js" to apply syntax-highlighting
to code blocks. Left to its own devices, highlight.js will guess the
language being used in the code block, apply the "hljs" CSS class
(so the code block will be given a nice border, a sensible font-size,
etc.) and apply the appropriate formatting markup. If it guesses wrongly,
you can give a language hint on the opening line of the block.

If you use a language that highlight.js does not recognise, or
the special name "nohighlight", highlight.js will leave the block
alone. Unfortunately, in mkdocs' default theme, that means it will be
formatted like *inline* code, with a border and background that wraps
behind each line of the block.

In order to make a code-block that looks like a code-block, you have
to carefully pick a language that highlight.js has heard of, but whose
syntax is sufficiently different from whatever's in the block that no
unwanted highlighting will occur.

This seems to be fixed in the readthedocs theme that comes with mkdocs
0.16.1, but ReadTheDocs doesn't actually seem to be using that version. :/
2017-11-11 18:22:08 +11:00
Tim Allen
e9d2d56df9 Update to v105r1 release.
byuu says:

Changelog:

  - higan: readded support for soft-reset to Famicom, Super Famicom,
    Mega Drive cores (work in progress)
      - handhelds lack soft reset obviously
      - the PC Engine also lacks a physical reset button
      - the Master System's reset button acts like a gamepad button, so
        can't show up in the menu
  - Mega Drive: power cycle wasn't initializing CPU (M68K) or APU (Z80)
    RAM
  - Super Famicom: fix SPC700 opcode 0x3b regression; fixes Majuu Ou
    [Jonas Quinn]
  - Super Famicom: fix SharpRTC save regression; fixes Dai Kaijuu
    Monogatari II's real-time clock [Talarubi]
  - Super Famicom: fix EpsonRTC save regression; fixes Tengai Makyou
    Zero's real-time clock [Talarubi]
  - Super Famicom: removed `*::init()` functions, as they were never used
  - Super Famicom: removed all but two `*::load()` functions, as they
    were not used
  - higan: added option to auto-save backup RAM every five seconds
    (enabled by default)
      - this is in case the emulator crashes, or there's a power outage;
        turn it off under advanced settings if you want
  - libco: updated license from public domain to ISC, for consistency
    with nall, ruby, hiro
  - nall: Linux compiler defaults to g++; override with g++-version if
    g++ is <= 4.8
      - FreeBSD compiler default is going to remain g++49 until my dev
        box OS ships with g++ >= 4.9

Errata: I have weird RAM initialization constants, thanks to hex_usr
and onethirdxcubed for both finding this:
http://wiki.nesdev.com/w/index.php?title=CPU_power_up_state&diff=11711&oldid=11184

I'll remove this in the next WIP.
2017-11-07 09:05:54 +11:00
Tim Allen
6d487925d0 Build documentation in a UTF-8 locale. 2017-10-27 17:16:29 +11:00
Tim Allen
7aad868adb Fix copy/pasto for libretro build. 2017-10-27 17:04:46 +11:00
Tim Allen
dd06dd0fed Include the licence and documentation with each WIP build. 2017-10-27 16:58:41 +11:00
Tim Allen
3c26736d4b Fix mojibake in README.txt 2017-10-27 14:45:52 +11:00
Talarubi
f53cb33eb9 Add icarus to LICENSE.txt 2017-10-25 18:22:10 -04:00
Talarubi
426de198b7 Initial attempt at a README.txt for release archives 2017-10-25 18:02:54 -04:00
Talarubi
9e06857e4d Update version and license
Added LICENSE.txt and GPLv3.txt. Also updated libco documentation.

After discussion with byuu, libco gets a more specific ISC license
to match nall, ruby and hiro. higan, as clarified in LICENSE.txt,
continues to be GPL version 3 only (no "or later" clause).
2017-10-24 23:37:22 -04:00
Talarubi
e28aa32324 Fixed: Typo in SPC700 instruction table
https://board.byuu.org/viewtopic.php?p=48325#p48325

Per Screwtape and Jonas Quinn, this fixes 魔獣王 (Majuu Ou)
hanging at the title.
2017-10-24 23:37:21 -04:00
Talarubi
a9571ff5b8 Fixed: Restore SPC7110 and S-RTC time properly
Loading and unloading the RTC is a little odd, since it's normally
always powered in the first place. What we want, and what the load()
functions really do, is to resync using the saved timestamps or
reset it. unload() proper doesn't do anything.

However, an interface refactoring after v098 reordered the above
operations, and this (along with a typo, shh!) was causing the already
synced time to be cleared.

I've added checks so that whenever rtc.ram can't be found, load() gets
called with empty arguments to initialise the defaults (like putting
in a fresh battery).
2017-10-24 23:16:22 -04:00
Tim Allen
3d21e9afe0 Mention that higan does not yet emulate the timing of the SGB2. 2017-10-23 13:24:14 +11:00
Tim Allen
1cb37fc974 Fix another stupid typo. :( 2017-10-08 18:51:47 +11:00
Tim Allen
f92fc276af Fix typo in libretro path. 2017-10-08 18:44:25 +11:00
Tim Allen
18afd41a80 Using "--no-commit" didn't work, let's try something else. 2017-10-08 18:17:48 +11:00
Tim Allen
3507c522f4 Don't commit the libretro test merge.
git requires a name and email to be configured before it can commit a change. We're not going to push the merge result anywhere, so we don't strictly speaking need it to commit.
2017-10-08 17:54:49 +11:00
Tim Allen
7a4bfca106 Fix git merge command 2017-10-08 17:29:14 +11:00
Tim Allen
0db886f91d Building the libretro core doesn't need UI libraries, but needs git.
The git dependency is only because we want to do some merge shenanigans for testing purposes.
2017-10-08 14:33:16 +11:00
Tim Allen
904d11a3f7 Next release, we need to update the libretro branch too. 2017-10-08 14:33:11 +11:00
Tim Allen
214b921388 Test that the libretro core builds with each change to higan. 2017-10-08 14:25:42 +11:00
Tim Allen
b8b5aef165 Update the release instructions after their first use. 2017-10-07 20:00:17 +11:00
2217 changed files with 355787 additions and 88694 deletions

84
.cirrus.yml Normal file
View File

@@ -0,0 +1,84 @@
linux-x86_64-binaries_task:
container:
image: ubuntu:latest
setup_script:
- apt-get update && apt-get -y install build-essential libgtk2.0-dev libpulse-dev mesa-common-dev libgtksourceview2.0-dev libcairo2-dev libsdl2-dev libxv-dev libao-dev libopenal-dev libudev-dev zip
compile_script:
- make -C bsnes local=false
package_script:
- mkdir bsnes-nightly
- mkdir bsnes-nightly/Database
- mkdir bsnes-nightly/Firmware
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes
- cp -a bsnes/Database/* bsnes-nightly/Database
- cp -a shaders bsnes-nightly/Shaders
- cp -a GPLv3.txt bsnes-nightly
- zip -r bsnes-nightly.zip bsnes-nightly
bsnes-nightly_artifacts:
path: "bsnes-nightly.zip"
freebsd-x86_64-binaries_task:
freebsd_instance:
image: freebsd-12-0-release-amd64
setup_script:
- pkg install --yes gmake gdb gcc8 pkgconf sdl2 openal-soft gtksourceview2 libXv zip
compile_script:
- gmake -C bsnes local=false
package_script:
- mkdir bsnes-nightly
- mkdir bsnes-nightly/Database
- mkdir bsnes-nightly/Firmware
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes
- cp -a bsnes/Database/* bsnes-nightly/Database
- cp -a shaders bsnes-nightly/Shaders
- cp -a GPLv3.txt bsnes-nightly
- zip -r bsnes-nightly.zip bsnes-nightly
bsnes-nightly_artifacts:
path: "bsnes-nightly.zip"
windows-x86_64-binaries_task:
container:
image: ubuntu:latest
setup_script:
- apt-get update && apt-get -y install build-essential mingw-w64 zip
compile_script:
- make -C bsnes local=false platform=windows compiler="x86_64-w64-mingw32-g++" windres="x86_64-w64-mingw32-windres"
package_script:
- mkdir bsnes-nightly
- mkdir bsnes-nightly/Database
- mkdir bsnes-nightly/Firmware
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes.exe
- cp -a bsnes/Database/* bsnes-nightly/Database
- cp -a shaders bsnes-nightly/Shaders
- cp -a GPLv3.txt bsnes-nightly
- zip -r bsnes-nightly.zip bsnes-nightly
bsnes-nightly_artifacts:
path: "bsnes-nightly.zip"
macOS-x86_64-binaries_task:
osx_instance:
image: mojave-base
compile_script:
- make -C bsnes local=false
package_script:
- mkdir bsnes-nightly
- cp -a bsnes/out/bsnes.app bsnes-nightly
- cp -a GPLv3.txt bsnes-nightly
- zip -r bsnes-nightly.zip bsnes-nightly
bsnes-nightly_artifacts:
path: "bsnes-nightly.zip"

2
.gitattributes vendored Normal file
View File

@@ -0,0 +1,2 @@
*.fs linguist-detectable=false
*.vs linguist-detectable=false

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
patreon: byuu

3
.gitignore vendored
View File

@@ -1,3 +0,0 @@
higan/profile/WonderSwan.sys/internal.ram
higan/profile/WonderSwan Color.sys/internal.ram
docs_build/

View File

@@ -1,39 +0,0 @@
# NOTE: This file is not part of the official higan source, it's been added
# to help build WIP binaries with minimal fuss.
image: debian:stable
linux-x86_64-binaries:
script:
- apt-get update && apt-get -y install build-essential libgtk2.0-dev libpulse-dev mesa-common-dev libgtksourceview2.0-dev libcairo2-dev libsdl1.2-dev libxv-dev libao-dev libopenal-dev libudev-dev
- make -C icarus compiler=g++
- make -C higan compiler=g++
- mkdir higan-nightly
- cp -a icarus/out/icarus higan-nightly/icarus
- cp -a icarus/Database higan-nightly/
- cp -a higan/out/higan higan-nightly/higan
- cp -a higan/systems/* higan-nightly/
- cp -a shaders "higan-nightly/Video Shaders"
artifacts:
paths:
- higan-nightly/*
windows-x86_64-binaries:
# This is a normal Windows cross-compile process, except that
# nall::chrono tries to use clock_gettime on Windows even
# though it's a POSIX function, and for some weird reason mingw has
# clock_gettime() in the pthread library.
script:
- apt-get update && apt-get -y install build-essential mingw-w64
- sed -i -e 's/-lole32/& -static -lpthread/' nall/GNUmakefile
- make -C icarus platform=windows compiler="x86_64-w64-mingw32-g++ -static-libgcc -static-libstdc++" windres="x86_64-w64-mingw32-windres"
- make -C higan platform=windows compiler="x86_64-w64-mingw32-g++ -static-libgcc -static-libstdc++" windres="x86_64-w64-mingw32-windres"
- mkdir higan-nightly
- cp -a icarus/out/icarus higan-nightly/icarus.exe
- cp -a icarus/Database higan-nightly/
- cp -a higan/out/higan higan-nightly/higan.exe
- cp -a higan/systems/* higan-nightly/
- cp -a shaders "higan-nightly/Video Shaders"
artifacts:
paths:
- higan-nightly/*

View File

@@ -1,9 +1,89 @@
Contributing to higan
=====================
Contributing
============
If you would like to propose a change to higan,
you should create an account on the [official forums][f],
go to the "Projects" forum and the "higan" sub-forum,
and post your idea in a new topic there.
Code contributions are most welcome and highly appreciated!
[f]: https://board.byuu.org/
But first, please note that although bsnes is licensed under the GPLv3 license,
in order to be merged upstream, any code contributions must be provided under
the ISC source code license.
This is *not* a CLA (community license agreement), no legal contract needs to be
signed, and you will maintain full and exclusive copyright ownership over any
contributed source code.
There are two reasons for this requirement:
GPLv4+
------
bsnes is currently licensed under the GPLv3 license only. I do not license bsnes
under the GPLv3 or later license, because there is no way of knowing what the
GPLv4 and later licenses will change, and if they will be in the best interests
of emulator development and video game preservation.
Although I put a good deal of trust into the FSF, no one is an oracle that can
predict the future. Would *you* agree to a license before being able to read it?
However, the GPLv4 may prove beneficial, and close important holes in the GPLv3
license, just as the GPLv3 license closed the GPLv2's TiVoization loophole. And
so it is important that bsnes retains the option of relicensing to the GPLv4+ in
the future.
As a point of interest, there have been projects with similar concerns about
using a GPLv2 or later clause, that are now permanently stuck on the GPLv2
license. There have also been projects that did use a GPLv2 or later clause,
only to disagree with the changes introduced in the GPLv3.
ISC
---
The more important reason for this requirement is that it is my intention to
release the entirety of bsnes under the ISC license once official upstream
development has ceased.
The reason I would want to relicense bsnes to the ISC license upon its official
discontinuation is because once again, no one is an oracle, and I cannot predict
what future issues bsnes permanently remaining under the GPLv3 license may
cause.
For instance, imagine a world where a certain vendor took over the world, and
the only way to distribute applications was with their approval, and their store
rules forbade GPLv3 software. Or perhaps a world where the GPL was abandoned in
favor of the new OSSv1 license. But GPLv3 software was incompatible with the
OSSv1 license. Other open source developers would not be able to use bsnes in
that scenario.
It would be very disappointing if all of our work ended up unusable 50+ years
into the future because it was permanently bound to the GPLv3 license.
GPLv3
-----
The reason I use the GPLv3 license currently is because it is a balance between
altruism and self-interest. The GPLv3 allows other vendors to sell my own code
without sharing revenue with me, and indeed this has already happened. But the
GPLv3 also prevents other vendors from improving upon bsnes without sharing
their work with everyone else as I have.
While I am actively developing bsnes, I do not wish to compete against myself.
As such, I believe the GPLv3 is the best license during active development, and
the ISC is the best license once bsnes is officially discontinued.
Considerations
--------------
This is the part that should concern you as a contributor: I am not requesting
contributed source code to be released under the ISC so that I personally may
sell GPLv3 commercial license exemptions to your work, but in the future when
bsnes is released under the ISC license, that will open the door for anyone to
sell the work commercially in a closed source form.
If this is not acceptable to you, I wholly understand and I welcome you to
release your work under the GPLv3 in the form of a bsnes fork. And if your work
is not an essential part of the core emulation -- that is to say, it may be
optionally disabled -- then I am still willing to work with you in merging such
work upstream anyway under the full GPLv3 license, but please reach out to me
first before developing under the assumption your work will be merged upstream.
Thank you very much for reading and hopefully for your understanding.

674
GPLv3.txt Normal file
View File

@@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

43
LICENSE.txt Normal file
View File

@@ -0,0 +1,43 @@
----------------------------------------------------------------------
bsnes - Super Nintendo emulator
Copyright © 2004-2019 byuu
https://byuu.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, specifically version 3 of the License
and no other version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
----------------------------------------------------------------------
----------------------------------------------------------------------
libco - C cooperative threading library
nall - C++ template library
ruby - Hardware abstraction library
hiro - User interface library
Copyright © 2006-2019 byuu
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all
copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
----------------------------------------------------------------------

129
README.md
View File

@@ -1,48 +1,99 @@
The unofficial higan repository
===============================
bsnes
=====
higan emulates a number of classic video-game consoles of the 1980s and 1990s,
allowing you to play classic games on a modern general-purpose computer.
![bsnes logo © byuu](https://byuu.org/images/bsnes/github/byuu-bsnes-logo.png)
This repository includes
the source-code for
stable and WIP releases of higan,
starting during the development of v068.
It also includes community-maintained documentation.
bsnes is a multi-platform Super Nintendo (Super Famicom) emulator from
[byuu](https://byuu.org) that focuses on performance, features, and ease of use.
Basically,
apart from `.gitignore` files,
anything in the
[higan](higan/),
[hiro](hiro/),
[icarus](icarus/),
[libco](libco/),
[nall](nall/),
[ruby](ruby/),
or [shaders](shaders/)
directories should be exactly as it appeared in official releases.
Everything else has been added for various reasons.
bsnes currently enjoys 100% known, bug-free compatibility with the entire SNES
library when configured to its most accurate settings, giving it the same
accuracy level as higan. Accuracy can also optionally be traded for performance,
allowing bsnes to operate more than 300% faster than higan while still remaining
almost as accurate.
Official higan resources
------------------------
Development
-----------
- [Official homepage](https://byuu.org/emulation/higan/)
- [Official forum](https://board.byuu.org/viewforum.php?f=4)
Git is used for the development of new releases, and represents a staging
environment. As bsnes is rather mature, things should generally be quite stable.
However, bugs will exist, regressions will occur, so proceed at your own risk.
Unofficial higan resources
--------------------------
If stability is required, please download the latest stable release from the
[official website.](https://bsnes.byuu.org)
- Documentation for
[the current stable version][stadocs]
- [Source code repository](https://gitlab.com/higan/higan/)
archives official higan releases
and WIP snapshots
since approximately v067r21
- [Latest WIP build for Windows][wipwin]
- Documentation for
[the latest WIP version][wipdocs]
Unique Features
---------------
- 100% (known) bug-free compatibility with the entire officially licensed SNES games library
- True Super Game Boy emulation (using the SameBoy core by Lior Halphon)
- HD mode 7 graphics with optional supersampling (by DerKoun)
- Low-level emulation of all SNES coprocessors (DSP-n, ST-01n, Cx4)
- Multi-threaded PPU graphics renderer
- Speed mode settings which retain smooth audio output (50%, 75%, 100%, 150%, 200%)
- Built-in games database with thousands of game entries
- Built-in cheat code database for hundreds of popular games (by mightymo)
- Built-in save state manager with screenshot previews and naming capabilities
- Customizable per-byte game mappings to support any cartridges, including prototype games
- 7-zip decompression support
- Extensive Satellaview emulation, including BS Memory flash write and wear-leveling emulation
- Optional higan game folder support (standard game ROM files are also fully supported!)
- Advanced mapping system allowing multiple bindings to every emulated input
[wipwin]: https://gitlab.com/higan/higan/-/jobs/artifacts/master/download?job=windows-x86_64-binaries
[stadocs]: https://higan.readthedocs.io/
[wipdocs]: https://higan.readthedocs.io/en/latest/
Standard Features
-----------------
- MSU1 support
- BPS and IPS soft-patching support
- Save states with undo and redo support (for reverting accidental saves and loads)
- OpenGL multi-pass pixel shaders
- Several built-in software filters, including HQ2x (by MaxSt) and snes_ntsc (by blargg)
- Adaptive sync and dynamic rate control for perfect audio/video synchronization
- Just-in-time input polling for minimal input latency
- Run-ahead support for removing internal game engine input latency
- Support for Direct3D exclusive mode video
- Support for WASAPI exclusive mode audio
- Periodic auto-saving of game saves
- Auto-saving of states when unloading games, and auto-resuming of states when reloading games
- Sprite limit disable support
- Cubic audio interpolation support
- Optional high-level emulation of most SNES coprocessors
- Optional emulation of flaws in older emulators for compatibility with older unofficial software
- CPU, SA1, and SuperFX overclocking support
- Frame advance support
- Screenshot support
- Cheat code search support
- Movie recording and playback support
- Rewind support
- HiDPI support
- Multi-monitor support
- Turbo support for controller inputs
Links
-----
- [Official website](https://byuu.org/bsnes)
- [Official git repository](https://github.com/byuu/bsnes)
- [Developer resources](https://byuu.net)
- [Donations](https://patreon.com/byuu)
Release Builds
--------------
- [Windows binaries](https://byuu.itch.io/bsnes)
Nightly Builds
--------------
- [Download](https://cirrus-ci.com/github/byuu/bsnes/master)
- ![Build status](https://api.cirrus-ci.com/github/byuu/bsnes.svg?task=windows-x86_64-binaries)
- ![Build status](https://api.cirrus-ci.com/github/byuu/bsnes.svg?task=macOS-x86_64-binaries)
- ![Build status](https://api.cirrus-ci.com/github/byuu/bsnes.svg?task=linux-x86_64-binaries)
- ![Build status](https://api.cirrus-ci.com/github/byuu/bsnes.svg?task=freebsd-x86_64-binaries)
Preview
-------
![bsnes user interface © byuu](https://byuu.org/images/bsnes/github/byuu-bsnes-user-interface.png)
![bsnes running Bahamut Lagoon © byuu](https://byuu.org/images/bsnes/github/byuu-bsnes-bahamut-lagoon.png)
![bsnes running Tengai Makyou Zero © byuu](https://byuu.org/images/bsnes/github/byuu-bsnes-tengai-makyou-zero.png)

View File

@@ -0,0 +1,44 @@
database
revision: 2020-01-01
//BS Memory (JPN)
database
revision: 2018-04-14
game
sha256: 80c34b50817d58820bc8c88d2d9fa462550b4a76372e19c6467cbfbc8cf5d9ef
label: 鮫亀 キャラカセット
name: Same Game - Chara Cassette
region: BSMC-ZS5J-JPN
revision: BSMC-ZS5J-0
board: BSMC-CR-01
memory
type: ROM
size: 0x80000
content: Program
game
sha256: 859c7f7b4771d920a5bdb11f1d247ab6b43fb026594d1062f6f72d32cd340a0a
label: 鮫亀 キャラデータ集
name: Same Game - Chara Data Shuu
region: BSMC-YS5J-JPN
revision: BSMC-YS5J-0
board: BSMC-CR-01
memory
type: ROM
size: 0x80000
content: Program
game
sha256: c92a15fdd9b0133f9ea69105d0230a3acd1cdeef98567462eca86ea02a959e4e
label: SDガンダム ジーネクスト ユニット&マップコレクション
name: SD Gundam G Next - Unit & Map Collection
region: BSMC-ZX3J-JPN
revision: BSMC-ZX3J-0
board: BSMC-BR-01
memory
type: ROM
size: 0x80000
content: Program

87497
bsnes/Database/Cheat Codes.bml Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,213 @@
database
revision: 2020-01-01
//Sufami Turbo (JPN)
database
revision: 2018-04-14
game
sha256: f73bda08743565e0bd101632ebbac2d363d703a3ab39d23f49d95217cab29269
label: 美少女戦士セーラームーン セーラースターズ ふわふわパニック2
name: Bishoujo Senshi Sailor Moon Sailor Stars - Fuwafuwa Panic 2
region: SFT-0112-JPN
revision: SAILOR MOON
board: PT-911
memory
type: ROM
size: 0x100000
content: Program
note: Unlinkable
game
sha256: afb3f2a83b5bfcb1b8829b6995f108cc4d64ca322d1ba4a50b83af6e1f2e89bd
label: クレヨンしんちゃん 長ぐつドボン!!
name: Crayon Shin-chan - Nagagutsu Dobon!!
region: SFT-0113-JPN
revision: SHINCYAN
board: PT-911
memory
type: ROM
size: 0x80000
content: Program
note: Unlinkable
game
sha256: d93b3a570e7cf343f680ab0768a50b77e3577f9c555007e2de3decd6bc4765c8
label: ゲゲゲの鬼太郎 妖怪ドンジャラ
name: Gegege no Kitarou - Youkai Donjara
region: SFT-0106-JPN
revision: KITARO DONJYAR
board: PT-911
memory
type: ROM
size: 0x80000
content: Program
note: Unlinkable
game
sha256: 89aecd4e23d8219c8de3e71bb75db3dfe667d51c1eba4ea7239d2f772743d0cc
label: 激走戦隊カーレンジャ 全開!レーサー戦士
name: Gekisou Sentai Carranger - Zenkai! Racer Senshi
region: SFT-0109-JPN
revision: CAR RANGER
board: PT-911
memory
type: ROM
size: 0x80000
content: Program
note: Unlinkable
game
sha256: 602b20b788640f5743487108a10f3f77bca5ce2d24208b25b1ca498a96eb0d69
label: ぽいぽい忍者ワールド
name: Poi Poi Ninja World
region: SFT-0103-JPN
revision: POI POI NINJYA
board: PT-911
memory
type: ROM
size: 0x80000
content: Program
memory
type: RAM
size: 0x800
content: Save
note: Linkable: SFT-0103-JPN
game
sha256: 2a9d7c9a61318861028a73ca03e32a48cff162d76cba36fbaab8690b212efe9b
label: SDガンダムジェネレーション アクシズ戦記
name: SD Gundam Generation - Axis Senki
region: SFT-0107-JPN
revision: GUNDAM C
board: PT-912
memory
type: ROM
size: 0x80000
content: Program
memory
type: RAM
size: 0x2000
content: Save
note: Linkable: SFT-0104-JPN, SFT-0105-JPN, SFT-0107-JPN, SFT-0108-JPN, SFT-0110-JPN, SFT-0111-JPN
game
sha256: 60ac017c18f534e8cf24ca7f38e22ce92db95ea6c30b2d59d76f13c4f1c8a6e4
label: SDガンダムジェネレーション バビロニア建国戦記
name: SD Gundam Generation - Babylonia Kenkoku Senki
region: SFT-0108-JPN
revision: GUNDAM D
board: PT-912
memory
type: ROM
size: 0x80000
content: Program
memory
type: RAM
size: 0x2000
content: Save
note: Linkable: SFT-0104-JPN, SFT-0105-JPN, SFT-0107-JPN, SFT-0108-JPN, SFT-0110-JPN, SFT-0111-JPN
game
sha256: e639b5d5d722432b6809ccc6801dc584e1a3016379f34b335ed2dfa73b1ebf69
label: SDガンダムジェネレーション コロニー格闘記
name: SD Gundam Generation - Colony Kakutouki
region: SFT-0111-JPN
revision: GUNDAM F
board: PT-912
memory
type: ROM
size: 0x80000
content: Program
memory
type: RAM
size: 0x2000
content: Save
note: Linkable: SFT-0104-JPN, SFT-0105-JPN, SFT-0107-JPN, SFT-0108-JPN, SFT-0110-JPN, SFT-0111-JPN
game
sha256: 8547a08ed11fe408eac282a90ac46654bd2e5f49bda3aec8e5edf166a0a4b9af
label: SDガンダムジェネレーション グリプス戦記
name: SD Gundam Generation - Gryps Senki
region: SFT-0105-JPN
revision: GUNDAM B
board: PT-912
memory
type: ROM
size: 0x80000
content: Program
memory
type: RAM
size: 0x2000
content: Save
note: Linkable: SFT-0104-JPN, SFT-0105-JPN, SFT-0107-JPN, SFT-0108-JPN, SFT-0110-JPN, SFT-0111-JPN
game
sha256: 3e82215bed08274874b30d461fc4a965c6bca932229da5d46d56e36f484d65eb
label: SDガンダムジェネレーション 一年戦争記
name: SD Gundam Generation - Ichinen Sensouki
region: SFT-0104-JPN
revision: GUNDAM A
board: PT-912
memory
type: ROM
size: 0x80000
content: Program
memory
type: RAM
size: 0x2000
content: Save
note: Linkable: SFT-0104-JPN, SFT-0105-JPN, SFT-0107-JPN, SFT-0108-JPN, SFT-0110-JPN, SFT-0111-JPN
game
sha256: 5951a58a91d8e397d0a237ccc2b1248e17c7312cb9cc11cbc350200a97b4e021
label: SDガンダムジェネレーション ザンスカール戦記
name: SD Gundam Generation - Zanscare Senki
region: SFT-0110-JPN
revision: GUNDAM E
board: PT-912
memory
type: ROM
size: 0x80000
content: Program
memory
type: RAM
size: 0x2000
content: Save
note: Linkable: SFT-0104-JPN, SFT-0105-JPN, SFT-0107-JPN, SFT-0108-JPN, SFT-0110-JPN, SFT-0111-JPN
game
sha256: 2fec5f2bc7dee010af10569a3d2bc18715a79a126940800c3eade5abbd625e3f
label: SDウルトラバトル セブン伝説
name: SD Ultra Battle - Seven Densetsu
region: SFT-0102-JPN
revision: ULTRA SEVEN 1
board: PT-911
memory
type: ROM
size: 0x80000
content: Program
memory
type: RAM
size: 0x800
content: Save
note: Linkable: SFT-0101-JPN, SFT-0102-JPN
game
sha256: 2bb55214fb668ca603d7b944b14f105dfb10b987a8902d420fe4ae1cb69c1d4a
label: SDウルトラバトル ウルトラマン伝説
name: SD Ultra Battle - Ultraman Densetsu
region: SFT-0101-JPN
revision: ULTRA MAN 1
board: PT-911
memory
type: ROM
size: 0x80000
content: Program
memory
type: RAM
size: 0x800
content: Save
note: Linkable: SFT-0101-JPN, SFT-0102-JPN

File diff suppressed because it is too large Load Diff

64
bsnes/GNUmakefile Normal file
View File

@@ -0,0 +1,64 @@
target := bsnes
binary := application
build := performance
openmp := true
local := true
flags += -I. -I..
# in order for this to work, obj/lzma.o must be omitted or bsnes will hang on startup.
# further, only the X-Video driver works reliably. OpenGL 3.2, OpenGL 2.0, and XShm crash bsnes.
ifeq ($(profile),true)
flags += -pg
options += -pg
endif
# binaries built with this flag are faster, but are not portable to multiple machines.
ifeq ($(local),true)
flags += -march=native
endif
nall.path := ../nall
include $(nall.path)/GNUmakefile
ifeq ($(platform),windows)
ifeq ($(binary),application)
options += -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32
options += -Wl,-enable-auto-import
options += -Wl,-enable-runtime-pseudo-reloc
else ifeq ($(binary),library)
options += -shared
endif
else ifeq ($(platform),macos)
ifeq ($(binary),application)
else ifeq ($(binary),library)
flags += -fPIC
options += -dynamiclib
endif
else ifneq ($(filter $(platform),linux bsd),)
ifeq ($(binary),application)
options += -Wl,-export-dynamic
options += -lX11 -lXext
else ifeq ($(binary),library)
flags += -fPIC
options += -shared
endif
endif
objects := libco emulator filter lzma
obj/libco.o: ../libco/libco.c
obj/emulator.o: emulator/emulator.cpp
obj/filter.o: filter/filter.cpp
obj/lzma.o: lzma/lzma.cpp
include sfc/GNUmakefile
include gb/GNUmakefile
include processor/GNUmakefile
ui := target-$(target)
include $(ui)/GNUmakefile
-include obj/*.d
clean:
$(call delete,obj/*)
$(call delete,out/*)

127
bsnes/Locale/Japanese.bml Normal file
View File

@@ -0,0 +1,127 @@
locale
language: 日本語
namespace: MessageDialog
map
input: Yes
value: はい
map
input: No
value: いいえ
map
input: Cancel
value: キャンセル
namespace: BrowserDialog
map
input: Open
value: 開く
map
input: Save
value: 保存
map
input: Select
value: 選択
map
input: Cancel
value: キャンセル
namespace: Program
map
input: Unloaded
value: アンロードされる
map
input: Paused
value: ポーズ
namespace: Presentation
map
input: System
value: システム
map
input: Load Game
value: ゲームを読み込み
map
input: Load Recent Game
value: 最新ゲームを読み込み
map
input: empty
value: なし
map
input: Clear List
value: 全部を消す
map
input: Reset System
value: システムをリセット
map
input: Unload Game
value: ゲームをアンロード
map
input: Controller Port 1
value: コントローラポート1
map
input: Controller Port 2
value: コントローラポート2
map
input: Expansion Port
value: 拡張ポート
map
input: Gamepad
value: ゲームパッド
map
input: Mouse
value: マウス
map
input: Super Multitap
value: スーパーマルチタップ
map
input: Super Scope
value: スーパースコップ
map
input: Justifier
value: 1挺のジャスティファイアー
map
input: Justifiers
value: 2挺のジャスティファイアー
map
input: None
value: なし
map
input: Satellaview
value: サテラビュー
map
input: Quit
value: 終了
map
input: Settings
value: 設定
map
input: Tools
value: ツール
map
input: Help
value: ヘルプ
map
input: Documentation
value: オンラインマニュアル
map
input: About
value: 情報
namespace: AboutWindow
map
input: About {0}
value: {0}について
map
input: Version
value: バージョン
map
input: Author
value: 作者
map
input: License
value: ライセンス
map
input: Website
value: 公式サイト

View File

@@ -0,0 +1,75 @@
#define double float
namespace Emulator {
#include "stream.cpp"
Audio audio;
Audio::~Audio() {
reset(nullptr);
}
auto Audio::reset(Interface* interface) -> void {
_interface = interface;
_streams.reset();
_channels = 0;
}
auto Audio::setFrequency(double frequency) -> void {
_frequency = frequency;
for(auto& stream : _streams) {
stream->setFrequency(stream->inputFrequency, frequency);
}
}
auto Audio::setVolume(double volume) -> void {
_volume = volume;
}
auto Audio::setBalance(double balance) -> void {
_balance = balance;
}
auto Audio::createStream(uint channels, double frequency) -> shared_pointer<Stream> {
_channels = max(_channels, channels);
shared_pointer<Stream> stream = new Stream;
stream->reset(channels, frequency, _frequency);
_streams.append(stream);
return stream;
}
auto Audio::process() -> void {
while(_streams) {
for(auto& stream : _streams) {
if(!stream->pending()) return;
}
double samples[_channels];
for(auto& sample : samples) sample = 0.0;
for(auto& stream : _streams) {
double buffer[_channels];
uint length = stream->read(buffer), offset = 0;
for(auto& sample : samples) {
sample += buffer[offset];
if(++offset >= length) offset = 0;
}
}
for(auto c : range(_channels)) {
samples[c] = max(-1.0, min(+1.0, samples[c] * _volume));
}
if(_channels == 2) {
if(_balance < 0.0) samples[1] *= 1.0 + _balance;
if(_balance > 0.0) samples[0] *= 1.0 - _balance;
}
platform->audioFrame(samples, _channels);
}
}
}
#undef double

View File

@@ -0,0 +1,95 @@
#pragma once
#define double float
#include <nall/dsp/iir/dc-removal.hpp>
#include <nall/dsp/iir/one-pole.hpp>
#include <nall/dsp/iir/biquad.hpp>
#include <nall/dsp/resampler/cubic.hpp>
namespace Emulator {
struct Interface;
struct Audio;
struct Filter;
struct Stream;
struct Audio {
~Audio();
auto reset(Interface* interface) -> void;
inline auto channels() const -> uint { return _channels; }
inline auto frequency() const -> double { return _frequency; }
inline auto volume() const -> double { return _volume; }
inline auto balance() const -> double { return _balance; }
auto setFrequency(double frequency) -> void;
auto setVolume(double volume) -> void;
auto setBalance(double balance) -> void;
auto createStream(uint channels, double frequency) -> shared_pointer<Stream>;
private:
auto process() -> void;
Interface* _interface = nullptr;
vector<shared_pointer<Stream>> _streams;
uint _channels = 0;
double _frequency = 48000.0;
double _volume = 1.0;
double _balance = 0.0;
friend class Stream;
};
struct Filter {
enum class Mode : uint { DCRemoval, OnePole, Biquad } mode;
enum class Type : uint { None, LowPass, HighPass } type;
enum class Order : uint { None, First, Second } order;
DSP::IIR::DCRemoval dcRemoval;
DSP::IIR::OnePole onePole;
DSP::IIR::Biquad biquad;
};
struct Stream {
auto reset(uint channels, double inputFrequency, double outputFrequency) -> void;
auto reset() -> void;
auto frequency() const -> double;
auto setFrequency(double inputFrequency, maybe<double> outputFrequency = nothing) -> void;
auto addDCRemovalFilter() -> void;
auto addLowPassFilter(double cutoffFrequency, Filter::Order order, uint passes = 1) -> void;
auto addHighPassFilter(double cutoffFrequency, Filter::Order order, uint passes = 1) -> void;
auto pending() const -> uint;
auto read(double samples[]) -> uint;
auto write(const double samples[]) -> void;
template<typename... P> auto sample(P&&... p) -> void {
double samples[sizeof...(P)] = {forward<P>(p)...};
write(samples);
}
auto serialize(serializer&) -> void;
private:
struct Channel {
vector<Filter> filters;
vector<DSP::IIR::Biquad> nyquist;
DSP::Resampler::Cubic resampler;
};
vector<Channel> channels;
double inputFrequency;
double outputFrequency;
friend class Audio;
};
extern Audio audio;
}
#undef double

View File

@@ -0,0 +1,125 @@
auto Stream::reset(uint channelCount, double inputFrequency, double outputFrequency) -> void {
channels.reset();
channels.resize(channelCount);
for(auto& channel : channels) {
channel.filters.reset();
}
setFrequency(inputFrequency, outputFrequency);
}
auto Stream::reset() -> void {
for(auto& channel : channels) {
channel.resampler.reset(this->inputFrequency, this->outputFrequency);
}
}
auto Stream::frequency() const -> double {
return inputFrequency;
}
auto Stream::setFrequency(double inputFrequency, maybe<double> outputFrequency) -> void {
this->inputFrequency = inputFrequency;
if(outputFrequency) this->outputFrequency = outputFrequency();
for(auto& channel : channels) {
channel.nyquist.reset();
channel.resampler.reset(this->inputFrequency, this->outputFrequency);
}
if(this->inputFrequency >= this->outputFrequency * 2) {
//add a low-pass filter to prevent aliasing during resampling
double cutoffFrequency = min(25000.0, this->outputFrequency / 2.0 - 2000.0);
for(auto& channel : channels) {
uint passes = 3;
for(uint pass : range(passes)) {
DSP::IIR::Biquad filter;
double q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
filter.reset(DSP::IIR::Biquad::Type::LowPass, cutoffFrequency, this->inputFrequency, q);
channel.nyquist.append(filter);
}
}
}
}
auto Stream::addDCRemovalFilter() -> void {
return; //todo: test to ensure this is desirable before enabling
for(auto& channel : channels) {
Filter filter{Filter::Mode::DCRemoval, Filter::Type::None, Filter::Order::None};
channel.filters.append(filter);
}
}
auto Stream::addLowPassFilter(double cutoffFrequency, Filter::Order order, uint passes) -> void {
for(auto& channel : channels) {
for(uint pass : range(passes)) {
if(order == Filter::Order::First) {
Filter filter{Filter::Mode::OnePole, Filter::Type::LowPass, Filter::Order::First};
filter.onePole.reset(DSP::IIR::OnePole::Type::LowPass, cutoffFrequency, inputFrequency);
channel.filters.append(filter);
}
if(order == Filter::Order::Second) {
Filter filter{Filter::Mode::Biquad, Filter::Type::LowPass, Filter::Order::Second};
double q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
filter.biquad.reset(DSP::IIR::Biquad::Type::LowPass, cutoffFrequency, inputFrequency, q);
channel.filters.append(filter);
}
}
}
}
auto Stream::addHighPassFilter(double cutoffFrequency, Filter::Order order, uint passes) -> void {
for(auto& channel : channels) {
for(uint pass : range(passes)) {
if(order == Filter::Order::First) {
Filter filter{Filter::Mode::OnePole, Filter::Type::HighPass, Filter::Order::First};
filter.onePole.reset(DSP::IIR::OnePole::Type::HighPass, cutoffFrequency, inputFrequency);
channel.filters.append(filter);
}
if(order == Filter::Order::Second) {
Filter filter{Filter::Mode::Biquad, Filter::Type::HighPass, Filter::Order::Second};
double q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
filter.biquad.reset(DSP::IIR::Biquad::Type::HighPass, cutoffFrequency, inputFrequency, q);
channel.filters.append(filter);
}
}
}
}
auto Stream::pending() const -> uint {
if(!channels) return 0;
return channels[0].resampler.pending();
}
auto Stream::read(double samples[]) -> uint {
for(uint c : range(channels.size())) samples[c] = channels[c].resampler.read();
return channels.size();
}
auto Stream::write(const double samples[]) -> void {
for(auto c : range(channels.size())) {
double sample = samples[c] + 1e-25; //constant offset used to suppress denormals
for(auto& filter : channels[c].filters) {
switch(filter.mode) {
case Filter::Mode::DCRemoval: sample = filter.dcRemoval.process(sample); break;
case Filter::Mode::OnePole: sample = filter.onePole.process(sample); break;
case Filter::Mode::Biquad: sample = filter.biquad.process(sample); break;
}
}
for(auto& filter : channels[c].nyquist) {
sample = filter.process(sample);
}
channels[c].resampler.write(sample);
}
audio.process();
}
auto Stream::serialize(serializer& s) -> void {
for(auto& channel : channels) {
channel.resampler.serialize(s);
}
s.real(inputFrequency);
s.real(outputFrequency);
}

57
bsnes/emulator/cheat.hpp Normal file
View File

@@ -0,0 +1,57 @@
#pragma once
namespace Emulator {
struct Cheat {
struct Code {
auto operator==(const Code& code) const -> bool {
if(address != code.address) return false;
if(data != code.data) return false;
if((bool)compare != (bool)code.compare) return false;
if(compare && code.compare && compare() != code.compare()) return false;
return true;
}
uint address;
uint data;
maybe<uint> compare;
bool enable;
uint restore;
};
explicit operator bool() const {
return codes.size() > 0;
}
auto reset() -> void {
codes.reset();
}
auto append(uint address, uint data, maybe<uint> compare = {}) -> void {
codes.append({address, data, compare});
}
auto assign(const vector<string>& list) -> void {
reset();
for(auto& entry : list) {
for(auto code : entry.split("+")) {
auto part = code.transform("=?", "//").split("/");
if(part.size() == 2) append(part[0].hex(), part[1].hex());
if(part.size() == 3) append(part[0].hex(), part[2].hex(), part[1].hex());
}
}
}
auto find(uint address, uint compare) -> maybe<uint> {
for(auto& code : codes) {
if(code.address == address && (!code.compare || code.compare() == compare)) {
return code.data;
}
}
return nothing;
}
vector<Code> codes;
};
}

View File

@@ -1,4 +1,5 @@
#include <emulator/emulator.hpp>
#include <emulator/audio/audio.cpp>
namespace Emulator {

View File

@@ -0,0 +1,58 @@
#pragma once
#include <libco/libco.h>
#include <nall/platform.hpp>
#include <nall/adaptive-array.hpp>
#include <nall/any.hpp>
#include <nall/chrono.hpp>
#include <nall/dl.hpp>
#include <nall/endian.hpp>
#include <nall/image.hpp>
#include <nall/literals.hpp>
#include <nall/random.hpp>
#include <nall/serializer.hpp>
#include <nall/shared-pointer.hpp>
#include <nall/string.hpp>
#include <nall/traits.hpp>
#include <nall/unique-pointer.hpp>
#include <nall/vector.hpp>
#include <nall/vfs.hpp>
#include <nall/hash/crc32.hpp>
#include <nall/hash/sha256.hpp>
using namespace nall;
#include <emulator/types.hpp>
#include <emulator/memory/readable.hpp>
#include <emulator/memory/writable.hpp>
#include <emulator/audio/audio.hpp>
namespace Emulator {
static const string Name = "bsnes";
static const string Version = "114";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "https://byuu.org";
//incremented only when serialization format changes
static const string SerializerVersion = "112";
namespace Constants {
namespace Colorburst {
static constexpr double NTSC = 315.0 / 88.0 * 1'000'000.0;
static constexpr double PAL = 283.75 * 15'625.0 + 25.0;
}
}
//nall/vfs shorthand constants for open(), load()
namespace File {
static const auto Read = vfs::file::mode::read;
static const auto Write = vfs::file::mode::write;
static const auto Optional = false;
static const auto Required = true;
};
}
#include "platform.hpp"
#include "interface.hpp"
#include "game.hpp"

112
bsnes/emulator/game.hpp Normal file
View File

@@ -0,0 +1,112 @@
#pragma once
namespace Emulator {
struct Game {
struct Memory;
struct Oscillator;
inline auto load(string_view) -> void;
inline auto memory(Markup::Node) -> maybe<Memory>;
inline auto oscillator(natural = 0) -> maybe<Oscillator>;
struct Memory {
Memory() = default;
inline Memory(Markup::Node);
explicit operator bool() const { return (bool)type; }
inline auto name() const -> string;
string type;
natural size;
string content;
string manufacturer;
string architecture;
string identifier;
boolean nonVolatile;
};
struct Oscillator {
Oscillator() = default;
inline Oscillator(Markup::Node);
explicit operator bool() const { return frequency; }
natural frequency;
};
Markup::Node document;
string sha256;
string label;
string name;
string title;
string region;
string revision;
string board;
vector<Memory> memoryList;
vector<Oscillator> oscillatorList;
};
auto Game::load(string_view text) -> void {
document = BML::unserialize(text);
sha256 = document["game/sha256"].text();
label = document["game/label"].text();
name = document["game/name"].text();
title = document["game/title"].text();
region = document["game/region"].text();
revision = document["game/revision"].text();
board = document["game/board"].text();
for(auto node : document.find("game/board/memory")) {
memoryList.append(Memory{node});
}
for(auto node : document.find("game/board/oscillator")) {
oscillatorList.append(Oscillator{node});
}
}
auto Game::memory(Markup::Node node) -> maybe<Memory> {
if(!node) return nothing;
for(auto& memory : memoryList) {
auto type = node["type"].text();
auto size = node["size"].natural();
auto content = node["content"].text();
auto manufacturer = node["manufacturer"].text();
auto architecture = node["architecture"].text();
auto identifier = node["identifier"].text();
if(type && type != memory.type) continue;
if(size && size != memory.size) continue;
if(content && content != memory.content) continue;
if(manufacturer && manufacturer != memory.manufacturer) continue;
if(architecture && architecture != memory.architecture) continue;
if(identifier && identifier != memory.identifier) continue;
return memory;
}
return nothing;
}
auto Game::oscillator(natural index) -> maybe<Oscillator> {
if(index < oscillatorList.size()) return oscillatorList[index];
return nothing;
}
Game::Memory::Memory(Markup::Node node) {
type = node["type"].text();
size = node["size"].natural();
content = node["content"].text();
manufacturer = node["manufacturer"].text();
architecture = node["architecture"].text();
identifier = node["identifier"].text();
nonVolatile = !(bool)node["volatile"];
}
auto Game::Memory::name() const -> string {
if(architecture) return string{architecture, ".", content, ".", type}.downcase();
return string{content, ".", type}.downcase();
}
Game::Oscillator::Oscillator(Markup::Node node) {
frequency = node["frequency"].natural();
}
}

View File

@@ -0,0 +1,109 @@
#pragma once
namespace Emulator {
struct Interface {
struct Information {
string manufacturer;
string name;
string extension;
bool resettable = false;
};
struct Display {
struct Type { enum : uint {
CRT,
LCD,
};};
uint id = 0;
string name;
uint type = 0;
uint colors = 0;
uint width = 0;
uint height = 0;
uint internalWidth = 0;
uint internalHeight = 0;
double aspectCorrection = 0;
};
struct Port {
uint id;
string name;
};
struct Device {
uint id;
string name;
};
struct Input {
struct Type { enum : uint {
Hat,
Button,
Trigger,
Control,
Axis,
Rumble,
};};
uint type;
string name;
};
//information
virtual auto information() -> Information { return {}; }
virtual auto display() -> Display { return {}; }
virtual auto color(uint32 color) -> uint64 { return 0; }
//game interface
virtual auto loaded() -> bool { return false; }
virtual auto hashes() -> vector<string> { return {}; }
virtual auto manifests() -> vector<string> { return {}; }
virtual auto titles() -> vector<string> { return {}; }
virtual auto title() -> string { return {}; }
virtual auto load() -> bool { return false; }
virtual auto save() -> void {}
virtual auto unload() -> void {}
//system interface
virtual auto ports() -> vector<Port> { return {}; }
virtual auto devices(uint port) -> vector<Device> { return {}; }
virtual auto inputs(uint device) -> vector<Input> { return {}; }
virtual auto connected(uint port) -> uint { return 0; }
virtual auto connect(uint port, uint device) -> void {}
virtual auto power() -> void {}
virtual auto reset() -> void {}
virtual auto run() -> void {}
//time functions
virtual auto rtc() -> bool { return false; }
virtual auto synchronize(uint64 timestamp = 0) -> void {}
//state functions
virtual auto serialize(bool synchronize = true) -> serializer { return {}; }
virtual auto unserialize(serializer&) -> bool { return false; }
//cheat functions
virtual auto read(uint24 address) -> uint8 { return 0; }
virtual auto cheats(const vector<string>& = {}) -> void {}
//configuration
virtual auto configuration() -> string { return {}; }
virtual auto configuration(string name) -> string { return {}; }
virtual auto configure(string configuration = "") -> bool { return false; }
virtual auto configure(string name, string value) -> bool { return false; }
//settings
virtual auto cap(const string& name) -> bool { return false; }
virtual auto get(const string& name) -> any { return {}; }
virtual auto set(const string& name, const any& value) -> bool { return false; }
virtual auto frameSkip() -> uint { return 0; }
virtual auto setFrameSkip(uint frameSkip) -> void {}
virtual auto runAhead() -> bool { return false; }
virtual auto setRunAhead(bool runAhead) -> void {}
};
}

View File

@@ -0,0 +1,30 @@
#pragma once
namespace Emulator::Memory {
inline auto mirror(uint address, uint size) -> uint {
if(size == 0) return 0;
uint base = 0;
uint mask = 1 << 31;
while(address >= size) {
while(!(address & mask)) mask >>= 1;
address -= mask;
if(size > mask) {
size -= mask;
base += mask;
}
mask >>= 1;
}
return base + address;
}
inline auto reduce(uint address, uint mask) -> uint {
while(mask) {
uint bits = (mask & -mask) - 1;
address = address >> 1 & ~bits | address & bits;
mask = (mask & mask - 1) >> 1;
}
return address;
}
}

View File

@@ -0,0 +1,63 @@
#pragma once
#include <emulator/memory/memory.hpp>
namespace Emulator::Memory {
template<typename T>
struct Readable {
~Readable() { reset(); }
inline auto reset() -> void {
delete[] self.data;
self.data = nullptr;
self.size = 0;
self.mask = 0;
}
inline auto allocate(uint size, T fill = ~0ull) -> void {
if(!size) return reset();
delete[] self.data;
self.size = size;
self.mask = bit::round(self.size) - 1;
self.data = new T[self.mask + 1];
memory::fill<T>(self.data, self.mask + 1, fill);
}
inline auto load(shared_pointer<vfs::file> fp) -> void {
fp->read(self.data, min(fp->size(), self.size * sizeof(T)));
for(uint address = self.size; address <= self.mask; address++) {
self.data[address] = self.data[mirror(address, self.size)];
}
}
inline auto save(shared_pointer<vfs::file> fp) -> void {
fp->write(self.data, self.size * sizeof(T));
}
explicit operator bool() const { return (bool)self.data; }
inline auto data() const -> const T* { return self.data; }
inline auto size() const -> uint { return self.size; }
inline auto mask() const -> uint { return self.mask; }
inline auto operator[](uint address) const -> T { return self.data[address & self.mask]; }
inline auto read(uint address) const -> T { return self.data[address & self.mask]; }
inline auto write(uint address, T data) const -> void {}
auto serialize(serializer& s) -> void {
const uint size = self.size;
s.integer(self.size);
s.integer(self.mask);
if(self.size != size) allocate(self.size);
s.array(self.data, self.size);
}
private:
struct {
T* data = nullptr;
uint size = 0;
uint mask = 0;
} self;
};
}

View File

@@ -0,0 +1,65 @@
#pragma once
#include <emulator/memory/memory.hpp>
namespace Emulator::Memory {
template<typename T>
struct Writable {
~Writable() { reset(); }
inline auto reset() -> void {
delete[] self.data;
self.data = nullptr;
self.size = 0;
self.mask = 0;
}
inline auto allocate(uint size, T fill = ~0ull) -> void {
if(!size) return reset();
delete[] self.data;
self.size = size;
self.mask = bit::round(self.size) - 1;
self.data = new T[self.mask + 1];
memory::fill<T>(self.data, self.mask + 1, fill);
}
inline auto load(shared_pointer<vfs::file> fp) -> void {
fp->read(self.data, min(fp->size(), self.size * sizeof(T)));
for(uint address = self.size; address <= self.mask; address++) {
self.data[address] = self.data[mirror(address, self.size)];
}
}
inline auto save(shared_pointer<vfs::file> fp) -> void {
fp->write(self.data, self.size * sizeof(T));
}
explicit operator bool() const { return (bool)self.data; }
inline auto data() -> T* { return self.data; }
inline auto data() const -> const T* { return self.data; }
inline auto size() const -> uint { return self.size; }
inline auto mask() const -> uint { return self.mask; }
inline auto operator[](uint address) -> T& { return self.data[address & self.mask]; }
inline auto operator[](uint address) const -> T { return self.data[address & self.mask]; }
inline auto read(uint address) const -> T { return self.data[address & self.mask]; }
inline auto write(uint address, T data) -> void { self.data[address & self.mask] = data; }
auto serialize(serializer& s) -> void {
const uint size = self.size;
s.integer(self.size);
s.integer(self.mask);
if(self.size != size) allocate(self.size);
s.array(self.data, self.size);
}
private:
struct {
T* data = nullptr;
uint size = 0;
uint mask = 0;
} self;
};
}

View File

@@ -0,0 +1,29 @@
#pragma once
namespace Emulator {
struct Platform {
struct Load {
Load() = default;
Load(uint pathID, string option = "") : valid(true), pathID(pathID), option(option) {}
explicit operator bool() const { return valid; }
bool valid = false;
uint pathID = 0;
string option;
};
virtual auto path(uint id) -> string { return ""; }
virtual auto open(uint id, string name, vfs::file::mode mode, bool required = false) -> shared_pointer<vfs::file> { return {}; }
virtual auto load(uint id, string name, string type, vector<string> options = {}) -> Load { return {}; }
virtual auto videoFrame(const uint16* data, uint pitch, uint width, uint height, uint scale) -> void {}
virtual auto audioFrame(const float* samples, uint channels) -> void {}
virtual auto inputPoll(uint port, uint device, uint input) -> int16 { return 0; }
virtual auto inputRumble(uint port, uint device, uint input, bool enable) -> void {}
virtual auto dipSettings(Markup::Node node) -> uint { return 0; }
virtual auto notify(string text) -> void {}
};
extern Platform* platform;
}

View File

@@ -65,10 +65,10 @@ struct Random {
if((random() & 1) == 0) hivalue = ~lovalue;
for(uint32 address : range(size)) {
uint8 value = address.bit(lobit) ? lovalue : hivalue;
if(address.bit(hibit)) value = ~value;
if((random() & 511) == 0) value.bit(random() & 7) ^= 1;
if((random() & 2047) == 0) value.bit(random() & 7) ^= 1;
uint8 value = (address & 1ull << lobit) ? lovalue : hivalue;
if((address & 1ull << hibit)) value = ~value;
if((random() & 511) == 0) value ^= 1 << (random() & 7);
if((random() & 2047) == 0) value ^= 1 << (random() & 7);
data[address] = value;
}
}

72
bsnes/emulator/types.hpp Normal file
View File

@@ -0,0 +1,72 @@
#pragma once
using int1 = nall::Integer< 1>;
using int2 = nall::Integer< 2>;
using int3 = nall::Integer< 3>;
using int4 = nall::Integer< 4>;
using int5 = nall::Integer< 5>;
using int6 = nall::Integer< 6>;
using int7 = nall::Integer< 7>;
using int8 = int8_t;
using int9 = nall::Integer< 9>;
using int10 = nall::Integer<10>;
using int11 = nall::Integer<11>;
using int12 = nall::Integer<12>;
using int13 = nall::Integer<13>;
using int14 = nall::Integer<14>;
using int15 = nall::Integer<15>;
using int16 = int16_t;
using int17 = nall::Integer<17>;
using int18 = nall::Integer<18>;
using int19 = nall::Integer<19>;
using int20 = nall::Integer<20>;
using int21 = nall::Integer<21>;
using int22 = nall::Integer<22>;
using int23 = nall::Integer<23>;
using int24 = nall::Integer<24>;
using int25 = nall::Integer<25>;
using int26 = nall::Integer<26>;
using int27 = nall::Integer<27>;
using int28 = nall::Integer<28>;
using int29 = nall::Integer<29>;
using int30 = nall::Integer<30>;
using int31 = nall::Integer<31>;
using int32 = int32_t;
using int48 = nall::Integer<48>; //Cx4
using int64 = int64_t;
using uint1 = nall::Natural< 1>;
using uint2 = nall::Natural< 2>;
using uint3 = nall::Natural< 3>;
using uint4 = nall::Natural< 4>;
using uint5 = nall::Natural< 5>;
using uint6 = nall::Natural< 6>;
using uint7 = nall::Natural< 7>;
using uint8 = uint8_t;
using uint9 = nall::Natural< 9>;
using uint10 = nall::Natural<10>;
using uint11 = nall::Natural<11>;
using uint12 = nall::Natural<12>;
using uint13 = nall::Natural<13>;
using uint14 = nall::Natural<14>;
using uint15 = nall::Natural<15>;
using uint16 = uint16_t;
using uint17 = nall::Natural<17>;
using uint18 = nall::Natural<18>;
using uint19 = nall::Natural<19>;
using uint20 = nall::Natural<20>;
using uint21 = nall::Natural<21>;
using uint22 = nall::Natural<22>;
using uint23 = nall::Natural<23>;
using uint24 = nall::Natural<24>;
using uint25 = nall::Natural<25>;
using uint26 = nall::Natural<26>;
using uint27 = nall::Natural<27>;
using uint28 = nall::Natural<28>;
using uint29 = nall::Natural<29>;
using uint30 = nall::Natural<30>;
using uint31 = nall::Natural<31>;
using uint32 = uint32_t;
using uint40 = nall::Natural<40>; //SA1
using uint48 = nall::Natural<48>; //Cx4
using uint64 = uint64_t;

25
bsnes/filter/2xsai.cpp Normal file
View File

@@ -0,0 +1,25 @@
namespace Filter::_2xSaI {
auto size(uint& width, uint& height) -> void {
width *= 2;
height *= 2;
}
uint32_t temp[512 * 480];
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void {
for(unsigned y = 0; y < height; y++) {
const uint16_t *line_in = (const uint16_t*)(((const uint8_t*)input) + pitch * y);
uint32_t *line_out = temp + y * width;
for(unsigned x = 0; x < width; x++) {
line_out[x] = colortable[line_in[x]];
}
}
_2xSaI32((unsigned char*)temp, width * sizeof(uint32_t), 0, (unsigned char*)output, outpitch, width, height);
}
}

25
bsnes/filter/filter.cpp Normal file
View File

@@ -0,0 +1,25 @@
#include <emulator/emulator.hpp>
#undef register
#define register
#include "sai/sai.cpp"
uint32_t* colortable;
#include "snes_ntsc/snes_ntsc.h"
#include "snes_ntsc/snes_ntsc.c"
#include "none.cpp"
#include "scanlines-light.cpp"
#include "scanlines-dark.cpp"
#include "scanlines-black.cpp"
#include "pixellate2x.cpp"
#include "scale2x.cpp"
#include "2xsai.cpp"
#include "super-2xsai.cpp"
#include "super-eagle.cpp"
#include "lq2x.cpp"
#include "hq2x.cpp"
#include "ntsc-rf.cpp"
#include "ntsc-composite.cpp"
#include "ntsc-svideo.cpp"
#include "ntsc-rgb.cpp"

129
bsnes/filter/filter.hpp Normal file
View File

@@ -0,0 +1,129 @@
#pragma once
#include <emulator/emulator.hpp>
namespace Filter {
using Size = auto (*)(uint& width, uint& height) -> void;
using Render = auto (*)(uint32_t* palette, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height) -> void;
}
namespace Filter::None {
auto size(uint& width, uint& height) -> void;
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void;
}
namespace Filter::ScanlinesLight {
auto size(uint& width, uint& height) -> void;
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void;
}
namespace Filter::ScanlinesDark {
auto size(uint& width, uint& height) -> void;
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void;
}
namespace Filter::ScanlinesBlack {
auto size(uint& width, uint& height) -> void;
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void;
}
namespace Filter::Pixellate2x {
auto size(uint& width, uint& height) -> void;
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void;
}
namespace Filter::Scale2x {
auto size(uint& width, uint& height) -> void;
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void;
}
namespace Filter::_2xSaI {
auto size(uint& width, uint& height) -> void;
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void;
}
namespace Filter::Super2xSaI {
auto size(uint& width, uint& height) -> void;
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void;
}
namespace Filter::SuperEagle {
auto size(uint& width, uint& height) -> void;
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void;
}
namespace Filter::LQ2x {
auto size(uint& width, uint& height) -> void;
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void;
}
namespace Filter::HQ2x {
auto size(uint& width, uint& height) -> void;
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void;
}
namespace Filter::NTSC_RF {
auto size(uint& width, uint& height) -> void;
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void;
}
namespace Filter::NTSC_Composite {
auto size(uint& width, uint& height) -> void;
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void;
}
namespace Filter::NTSC_SVideo {
auto size(uint& width, uint& height) -> void;
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void;
}
namespace Filter::NTSC_RGB {
auto size(uint& width, uint& height) -> void;
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void;
}

200
bsnes/filter/hq2x.cpp Normal file
View File

@@ -0,0 +1,200 @@
namespace Filter::HQ2x {
enum {
diff_offset = (0x440 << 21) + (0x207 << 11) + 0x407,
diff_mask = (0x380 << 21) + (0x1f0 << 11) + 0x3f0,
};
uint32_t *yuvTable;
uint8_t rotate[256];
const uint8_t hqTable[256] = {
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 15, 12, 5, 3, 17, 13,
4, 4, 6, 18, 4, 4, 6, 18, 5, 3, 12, 12, 5, 3, 1, 12,
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 17, 13, 5, 3, 16, 14,
4, 4, 6, 18, 4, 4, 6, 18, 5, 3, 16, 12, 5, 3, 1, 14,
4, 4, 6, 2, 4, 4, 6, 2, 5, 19, 12, 12, 5, 19, 16, 12,
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 12,
4, 4, 6, 2, 4, 4, 6, 2, 5, 19, 1, 12, 5, 19, 1, 14,
4, 4, 6, 2, 4, 4, 6, 18, 5, 3, 16, 12, 5, 19, 1, 14,
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 15, 12, 5, 3, 17, 13,
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 12,
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 17, 13, 5, 3, 16, 14,
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 13, 5, 3, 1, 14,
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 13,
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 1, 12,
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 1, 14,
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 1, 12, 5, 3, 1, 14,
};
static void initialize() {
static bool initialized = false;
if(initialized == true) return;
initialized = true;
yuvTable = new uint32_t[32768];
for(unsigned i = 0; i < 32768; i++) {
uint8_t R = (i >> 0) & 31;
uint8_t G = (i >> 5) & 31;
uint8_t B = (i >> 10) & 31;
//bgr555->bgr888
double r = (R << 3) | (R >> 2);
double g = (G << 3) | (G >> 2);
double b = (B << 3) | (B >> 2);
//bgr888->yuv
double y = (r + g + b) * (0.25f * (63.5f / 48.0f));
double u = ((r - b) * 0.25f + 128.0f) * (7.5f / 7.0f);
double v = ((g * 2.0f - r - b) * 0.125f + 128.0f) * (7.5f / 6.0f);
yuvTable[i] = ((unsigned)y << 21) + ((unsigned)u << 11) + ((unsigned)v);
}
//counter-clockwise rotation table; one revolution:
//123 369 12346789
//4.6 -> 2.8 =
//789 147 36928147
for(unsigned n = 0; n < 256; n++) {
rotate[n] = ((n >> 2) & 0x11) | ((n << 2) & 0x88)
| ((n & 0x01) << 5) | ((n & 0x08) << 3)
| ((n & 0x10) >> 3) | ((n & 0x80) >> 5);
}
}
static void terminate() {
delete[] yuvTable;
}
static bool same(uint16_t x, uint16_t y) {
return !((yuvTable[x] - yuvTable[y] + diff_offset) & diff_mask);
}
static bool diff(uint32_t x, uint16_t y) {
return ((x - yuvTable[y]) & diff_mask);
}
static void grow(uint32_t &n) { n |= n << 16; n &= 0x03e07c1f; }
static uint16_t pack(uint32_t n) { n &= 0x03e07c1f; return n | (n >> 16); }
static uint16_t blend1(uint32_t A, uint32_t B) {
grow(A); grow(B);
return pack((A * 3 + B) >> 2);
}
static uint16_t blend2(uint32_t A, uint32_t B, uint32_t C) {
grow(A); grow(B); grow(C);
return pack((A * 2 + B + C) >> 2);
}
static uint16_t blend3(uint32_t A, uint32_t B, uint32_t C) {
grow(A); grow(B); grow(C);
return pack((A * 5 + B * 2 + C) >> 3);
}
static uint16_t blend4(uint32_t A, uint32_t B, uint32_t C) {
grow(A); grow(B); grow(C);
return pack((A * 6 + B + C) >> 3);
}
static uint16_t blend5(uint32_t A, uint32_t B, uint32_t C) {
grow(A); grow(B); grow(C);
return pack((A * 2 + (B + C) * 3) >> 3);
}
static uint16_t blend6(uint32_t A, uint32_t B, uint32_t C) {
grow(A); grow(B); grow(C);
return pack((A * 14 + B + C) >> 4);
}
static uint16_t blend(unsigned rule, uint16_t E, uint16_t A, uint16_t B, uint16_t D, uint16_t F, uint16_t H) {
switch(rule) { default:
case 0: return E;
case 1: return blend1(E, A);
case 2: return blend1(E, D);
case 3: return blend1(E, B);
case 4: return blend2(E, D, B);
case 5: return blend2(E, A, B);
case 6: return blend2(E, A, D);
case 7: return blend3(E, B, D);
case 8: return blend3(E, D, B);
case 9: return blend4(E, D, B);
case 10: return blend5(E, D, B);
case 11: return blend6(E, D, B);
case 12: return same(B, D) ? blend2(E, D, B) : E;
case 13: return same(B, D) ? blend5(E, D, B) : E;
case 14: return same(B, D) ? blend6(E, D, B) : E;
case 15: return same(B, D) ? blend2(E, D, B) : blend1(E, A);
case 16: return same(B, D) ? blend4(E, D, B) : blend1(E, A);
case 17: return same(B, D) ? blend5(E, D, B) : blend1(E, A);
case 18: return same(B, F) ? blend3(E, B, D) : blend1(E, D);
case 19: return same(D, H) ? blend3(E, D, B) : blend1(E, B);
}
}
auto size(uint& width, uint& height) -> void {
width *= 2;
height *= 2;
}
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void {
initialize();
pitch >>= 1;
outpitch >>= 2;
for(uint y = 0; y < height; y++) {
const uint16_t* in = input + y * pitch;
uint32_t* out0 = output + y * outpitch * 2;
uint32_t* out1 = output + y * outpitch * 2 + outpitch;
int prevline = (y == 0 ? 0 : pitch);
int nextline = (y == height - 1 ? 0 : pitch);
in++;
*out0++ = 0; *out0++ = 0;
*out1++ = 0; *out1++ = 0;
for(unsigned x = 1; x < width - 1; x++) {
uint16_t A = *(in - prevline - 1);
uint16_t B = *(in - prevline + 0);
uint16_t C = *(in - prevline + 1);
uint16_t D = *(in - 1);
uint16_t E = *(in + 0);
uint16_t F = *(in + 1);
uint16_t G = *(in + nextline - 1);
uint16_t H = *(in + nextline + 0);
uint16_t I = *(in + nextline + 1);
uint32_t e = yuvTable[E] + diff_offset;
uint8_t pattern;
pattern = diff(e, A) << 0;
pattern |= diff(e, B) << 1;
pattern |= diff(e, C) << 2;
pattern |= diff(e, D) << 3;
pattern |= diff(e, F) << 4;
pattern |= diff(e, G) << 5;
pattern |= diff(e, H) << 6;
pattern |= diff(e, I) << 7;
*(out0 + 0) = colortable[blend(hqTable[pattern], E, A, B, D, F, H)]; pattern = rotate[pattern];
*(out0 + 1) = colortable[blend(hqTable[pattern], E, C, F, B, H, D)]; pattern = rotate[pattern];
*(out1 + 1) = colortable[blend(hqTable[pattern], E, I, H, F, D, B)]; pattern = rotate[pattern];
*(out1 + 0) = colortable[blend(hqTable[pattern], E, G, D, H, B, F)];
in++;
out0 += 2;
out1 += 2;
}
in++;
*out0++ = 0; *out0++ = 0;
*out1++ = 0; *out1++ = 0;
}
}
}

46
bsnes/filter/lq2x.cpp Normal file
View File

@@ -0,0 +1,46 @@
namespace Filter::LQ2x {
auto size(uint& width, uint& height) -> void {
width *= 2;
height *= 2;
}
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void {
pitch >>= 1;
outpitch >>= 2;
for(uint y = 0; y < height; y++) {
const uint16_t* in = input + y * pitch;
uint32_t* out0 = output + y * outpitch * 2;
uint32_t* out1 = output + y * outpitch * 2 + outpitch;
int prevline = (y == 0 ? 0 : pitch);
int nextline = (y == height - 1 ? 0 : pitch);
for(uint x = 0; x < width; x++) {
uint16_t A = *(in - prevline);
uint16_t B = (x > 0) ? *(in - 1) : *in;
uint16_t C = *in;
uint16_t D = (x < width - 1) ? *(in + 1) : *in;
uint16_t E = *(in++ + nextline);
uint32_t c = colortable[C];
if(A != E && B != D) {
*out0++ = (A == B ? colortable[C + A - ((C ^ A) & 0x0421) >> 1] : c);
*out0++ = (A == D ? colortable[C + A - ((C ^ A) & 0x0421) >> 1] : c);
*out1++ = (E == B ? colortable[C + E - ((C ^ E) & 0x0421) >> 1] : c);
*out1++ = (E == D ? colortable[C + E - ((C ^ E) & 0x0421) >> 1] : c);
} else {
*out0++ = c;
*out0++ = c;
*out1++ = c;
*out1++ = c;
}
}
}
}
}

24
bsnes/filter/none.cpp Normal file
View File

@@ -0,0 +1,24 @@
namespace Filter::None {
auto size(uint& width, uint& height) -> void {
width = width;
height = height;
}
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void {
pitch >>= 1;
outpitch >>= 2;
for(uint y = 0; y < height; y++) {
const uint16_t* in = input + y * pitch;
uint32_t* out = output + y * outpitch;
for(uint x = 0; x < width; x++) {
*out++ = colortable[*in++];
}
}
}
}

View File

@@ -0,0 +1,50 @@
namespace Filter::NTSC_Composite {
struct snes_ntsc_t *ntsc;
snes_ntsc_setup_t setup;
int burst;
int burst_toggle;
void initialize() {
static bool initialized = false;
if(initialized == true) return;
initialized = true;
ntsc = (snes_ntsc_t*)malloc(sizeof *ntsc);
setup = snes_ntsc_composite;
setup.merge_fields = 1;
snes_ntsc_init(ntsc, &setup);
burst = 0;
burst_toggle = (setup.merge_fields ? 0 : 1);
}
void terminate() {
if(ntsc) free(ntsc);
}
auto size(uint& width, uint& height) -> void {
width = SNES_NTSC_OUT_WIDTH(256);
height = height;
}
auto render(
uint32_t* colortable_, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void {
initialize();
colortable = colortable_;
pitch >>= 1;
outpitch >>= 2;
if(width <= 256) {
snes_ntsc_blit (ntsc, input, pitch, burst, width, height, output, outpitch << 2);
} else {
snes_ntsc_blit_hires(ntsc, input, pitch, burst, width, height, output, outpitch << 2);
}
burst ^= burst_toggle;
}
}

50
bsnes/filter/ntsc-rf.cpp Normal file
View File

@@ -0,0 +1,50 @@
namespace Filter::NTSC_RF {
struct snes_ntsc_t *ntsc;
snes_ntsc_setup_t setup;
int burst;
int burst_toggle;
void initialize() {
static bool initialized = false;
if(initialized == true) return;
initialized = true;
ntsc = (snes_ntsc_t*)malloc(sizeof *ntsc);
setup = snes_ntsc_composite;
setup.merge_fields = 0;
snes_ntsc_init(ntsc, &setup);
burst = 0;
burst_toggle = (setup.merge_fields ? 0 : 1);
}
void terminate() {
if(ntsc) free(ntsc);
}
auto size(uint& width, uint& height) -> void {
width = SNES_NTSC_OUT_WIDTH(256);
height = height;
}
auto render(
uint32_t* colortable_, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void {
initialize();
colortable = colortable_;
pitch >>= 1;
outpitch >>= 2;
if(width <= 256) {
snes_ntsc_blit (ntsc, input, pitch, burst, width, height, output, outpitch << 2);
} else {
snes_ntsc_blit_hires(ntsc, input, pitch, burst, width, height, output, outpitch << 2);
}
burst ^= burst_toggle;
}
}

50
bsnes/filter/ntsc-rgb.cpp Normal file
View File

@@ -0,0 +1,50 @@
namespace Filter::NTSC_RGB {
struct snes_ntsc_t *ntsc;
snes_ntsc_setup_t setup;
int burst;
int burst_toggle;
void initialize() {
static bool initialized = false;
if(initialized == true) return;
initialized = true;
ntsc = (snes_ntsc_t*)malloc(sizeof *ntsc);
setup = snes_ntsc_rgb;
setup.merge_fields = 1;
snes_ntsc_init(ntsc, &setup);
burst = 0;
burst_toggle = (setup.merge_fields ? 0 : 1);
}
void terminate() {
if(ntsc) free(ntsc);
}
auto size(uint& width, uint& height) -> void {
width = SNES_NTSC_OUT_WIDTH(256);
height = height;
}
auto render(
uint32_t* colortable_, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void {
initialize();
colortable = colortable_;
pitch >>= 1;
outpitch >>= 2;
if(width <= 256) {
snes_ntsc_blit (ntsc, input, pitch, burst, width, height, output, outpitch << 2);
} else {
snes_ntsc_blit_hires(ntsc, input, pitch, burst, width, height, output, outpitch << 2);
}
burst ^= burst_toggle;
}
}

View File

@@ -0,0 +1,50 @@
namespace Filter::NTSC_SVideo {
struct snes_ntsc_t *ntsc;
snes_ntsc_setup_t setup;
int burst;
int burst_toggle;
void initialize() {
static bool initialized = false;
if(initialized == true) return;
initialized = true;
ntsc = (snes_ntsc_t*)malloc(sizeof *ntsc);
setup = snes_ntsc_svideo;
setup.merge_fields = 1;
snes_ntsc_init(ntsc, &setup);
burst = 0;
burst_toggle = (setup.merge_fields ? 0 : 1);
}
void terminate() {
if(ntsc) free(ntsc);
}
auto size(uint& width, uint& height) -> void {
width = SNES_NTSC_OUT_WIDTH(256);
height = height;
}
auto render(
uint32_t* colortable_, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void {
initialize();
colortable = colortable_;
pitch >>= 1;
outpitch >>= 2;
if(width <= 256) {
snes_ntsc_blit (ntsc, input, pitch, burst, width, height, output, outpitch << 2);
} else {
snes_ntsc_blit_hires(ntsc, input, pitch, burst, width, height, output, outpitch << 2);
}
burst ^= burst_toggle;
}
}

View File

@@ -0,0 +1,40 @@
namespace Filter::Pixellate2x {
auto size(uint& width, uint& height) -> void {
width = (width <= 256) ? width * 2 : width;
height = (height <= 240) ? height * 2 : height;
}
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void {
pitch >>= 1;
outpitch >>= 2;
uint32_t *out0 = output;
uint32_t *out1 = output + outpitch;
for(unsigned y = 0; y < height; y++) {
for(unsigned x = 0; x < width; x++) {
uint32_t p = colortable[*input++];
*out0++ = p;
if(height <= 240) *out1++ = p;
if(width > 256) continue;
*out0++ = p;
if(height <= 240) *out1++ = p;
}
input += pitch - width;
if(height <= 240) {
out0 += outpitch + outpitch - 512;
out1 += outpitch + outpitch - 512;
} else {
out0 += outpitch - 512;
}
}
}
}

1175
bsnes/filter/sai/sai.cpp Normal file

File diff suppressed because it is too large Load Diff

46
bsnes/filter/scale2x.cpp Normal file
View File

@@ -0,0 +1,46 @@
namespace Filter::Scale2x {
auto size(uint& width, uint& height) -> void {
width *= 2;
height *= 2;
}
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void {
pitch >>= 1;
outpitch >>= 2;
for(uint y = 0; y < height; y++) {
const uint16_t* in = input + y * pitch;
uint32_t* out0 = output + y * outpitch * 2;
uint32_t* out1 = output + y * outpitch * 2 + outpitch;
int prevline = (y == 0 ? 0 : pitch);
int nextline = (y == height - 1 ? 0 : pitch);
for(unsigned x = 0; x < width; x++) {
uint16_t A = *(in - prevline);
uint16_t B = (x > 0) ? *(in - 1) : *in;
uint16_t C = *in;
uint16_t D = (x < width - 1) ? *(in + 1) : *in;
uint16_t E = *(in++ + nextline);
uint32_t c = colortable[C];
if(A != E && B != D) {
*out0++ = (A == B ? colortable[A] : c);
*out0++ = (A == D ? colortable[A] : c);
*out1++ = (E == B ? colortable[E] : c);
*out1++ = (E == D ? colortable[E] : c);
} else {
*out0++ = c;
*out0++ = c;
*out1++ = c;
*out1++ = c;
}
}
}
}
}

View File

@@ -0,0 +1,28 @@
namespace Filter::ScanlinesBlack {
auto size(uint& width, uint& height) -> void {
width = width;
height = height * 2;
}
auto render(
uint32_t* palette, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void {
pitch >>= 1;
outpitch >>= 2;
for(unsigned y = 0; y < height; y++) {
const uint16_t *in = input + y * pitch;
uint32_t *out0 = output + y * outpitch * 2;
uint32_t *out1 = output + y * outpitch * 2 + outpitch;
for(unsigned x = 0; x < width; x++) {
uint16_t color = *in++;
*out0++ = palette[color];
*out1++ = 0;
}
}
}
}

View File

@@ -0,0 +1,48 @@
namespace Filter::ScanlinesDark {
uint16_t adjust[32768];
void initialize() {
static bool initialized = false;
if(initialized == true) return;
initialized = true;
for(unsigned i = 0; i < 32768; i++) {
uint8_t r = (i >> 10) & 31;
uint8_t g = (i >> 5) & 31;
uint8_t b = (i >> 0) & 31;
r *= 0.333;
g *= 0.333;
b *= 0.333;
adjust[i] = (r << 10) + (g << 5) + (b << 0);
}
}
auto size(uint& width, uint& height) -> void {
width = width;
height = height * 2;
}
auto render(
uint32_t* palette, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void {
initialize();
pitch >>= 1;
outpitch >>= 2;
for(unsigned y = 0; y < height; y++) {
const uint16_t *in = input + y * pitch;
uint32_t *out0 = output + y * outpitch * 2;
uint32_t *out1 = output + y * outpitch * 2 + outpitch;
for(unsigned x = 0; x < width; x++) {
uint16_t color = *in++;
*out0++ = palette[color];
*out1++ = palette[adjust[color]];
}
}
}
}

View File

@@ -0,0 +1,48 @@
namespace Filter::ScanlinesLight {
uint16_t adjust[32768];
void initialize() {
static bool initialized = false;
if(initialized == true) return;
initialized = true;
for(unsigned i = 0; i < 32768; i++) {
uint8_t r = (i >> 10) & 31;
uint8_t g = (i >> 5) & 31;
uint8_t b = (i >> 0) & 31;
r *= 0.666;
g *= 0.666;
b *= 0.666;
adjust[i] = (r << 10) + (g << 5) + (b << 0);
}
}
auto size(uint& width, uint& height) -> void {
width = width;
height = height * 2;
}
auto render(
uint32_t* palette, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void {
initialize();
pitch >>= 1;
outpitch >>= 2;
for(unsigned y = 0; y < height; y++) {
const uint16_t *in = input + y * pitch;
uint32_t *out0 = output + y * outpitch * 2;
uint32_t *out1 = output + y * outpitch * 2 + outpitch;
for(unsigned x = 0; x < width; x++) {
uint16_t color = *in++;
*out0++ = palette[color];
*out1++ = palette[adjust[color]];
}
}
}
}

View File

@@ -0,0 +1,251 @@
/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */
#include "snes_ntsc.h"
/* Copyright (C) 2006-2007 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module 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 Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
snes_ntsc_setup_t const snes_ntsc_monochrome = { 0,-1, 0, 0,.2, 0,.2,-.2,-.2,-1, 1, 0, 0 };
snes_ntsc_setup_t const snes_ntsc_composite = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 };
snes_ntsc_setup_t const snes_ntsc_svideo = { 0, 0, 0, 0,.2, 0,.2, -1, -1, 0, 1, 0, 0 };
snes_ntsc_setup_t const snes_ntsc_rgb = { 0, 0, 0, 0,.2, 0,.7, -1, -1,-1, 1, 0, 0 };
#define alignment_count 3
#define burst_count 3
#define rescale_in 8
#define rescale_out 7
#define artifacts_mid 1.0f
#define fringing_mid 1.0f
#define std_decoder_hue 0
#define rgb_bits 7 /* half normal range to allow for doubled hires pixels */
#define gamma_size 32
#include "snes_ntsc_impl.h"
/* 3 input pixels -> 8 composite samples */
pixel_info_t const snes_ntsc_pixels [alignment_count] = {
{ PIXEL_OFFSET( -4, -9 ), { 1, 1, .6667f, 0 } },
{ PIXEL_OFFSET( -2, -7 ), { .3333f, 1, 1, .3333f } },
{ PIXEL_OFFSET( 0, -5 ), { 0, .6667f, 1, 1 } },
};
static void merge_kernel_fields( snes_ntsc_rgb_t* io )
{
int n;
for ( n = burst_size; n; --n )
{
snes_ntsc_rgb_t p0 = io [burst_size * 0] + rgb_bias;
snes_ntsc_rgb_t p1 = io [burst_size * 1] + rgb_bias;
snes_ntsc_rgb_t p2 = io [burst_size * 2] + rgb_bias;
/* merge colors without losing precision */
io [burst_size * 0] =
((p0 + p1 - ((p0 ^ p1) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
io [burst_size * 1] =
((p1 + p2 - ((p1 ^ p2) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
io [burst_size * 2] =
((p2 + p0 - ((p2 ^ p0) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
++io;
}
}
static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out )
{
int n;
for ( n = burst_count; n; --n )
{
unsigned i;
for ( i = 0; i < rgb_kernel_size / 2; i++ )
{
snes_ntsc_rgb_t error = color -
out [i ] - out [(i+12)%14+14] - out [(i+10)%14+28] -
out [i + 7] - out [i + 5 +14] - out [i + 3 +28];
DISTRIBUTE_ERROR( i+3+28, i+5+14, i+7 );
}
out += alignment_count * rgb_kernel_size;
}
}
void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup )
{
int merge_fields;
int entry;
init_t impl;
if ( !setup )
setup = &snes_ntsc_composite;
init( &impl, setup );
merge_fields = setup->merge_fields;
if ( setup->artifacts <= -1 && setup->fringing <= -1 )
merge_fields = 1;
for ( entry = 0; entry < snes_ntsc_palette_size; entry++ )
{
/* Reduce number of significant bits of source color. Clearing the
low bits of R and B were least notictable. Modifying green was too
noticeable. */
int ir = entry >> 8 & 0x1E;
int ig = entry >> 4 & 0x1F;
int ib = entry << 1 & 0x1E;
#if SNES_NTSC_BSNES_COLORTBL
if ( setup->bsnes_colortbl )
{
int bgr15 = (ib << 10) | (ig << 5) | ir;
unsigned long rgb16 = setup->bsnes_colortbl [bgr15];
ir = rgb16 >> 11 & 0x1E;
ig = rgb16 >> 6 & 0x1F;
ib = rgb16 & 0x1E;
}
#endif
{
float rr = impl.to_float [ir];
float gg = impl.to_float [ig];
float bb = impl.to_float [ib];
float y, i, q = RGB_TO_YIQ( rr, gg, bb, y, i );
int r, g, b = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, r, g );
snes_ntsc_rgb_t rgb = PACK_RGB( r, g, b );
snes_ntsc_rgb_t* out = ntsc->table [entry];
gen_kernel( &impl, y, i, q, out );
if ( merge_fields )
merge_kernel_fields( out );
correct_errors( rgb, out );
}
}
}
#ifndef SNES_NTSC_NO_BLITTERS
void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width,
int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch )
{
int chunk_count = (in_width - 1) / snes_ntsc_in_chunk;
for ( ; in_height; --in_height )
{
SNES_NTSC_IN_T const* line_in = input;
SNES_NTSC_BEGIN_ROW( ntsc, burst_phase,
snes_ntsc_black, snes_ntsc_black, SNES_NTSC_ADJ_IN( *line_in ) );
snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out;
int n;
++line_in;
for ( n = chunk_count; n; --n )
{
/* order of input and output pixels must not be altered */
SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) );
SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) );
SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) );
SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
line_in += 3;
line_out += 7;
}
/* finish final pixels */
SNES_NTSC_COLOR_IN( 0, snes_ntsc_black );
SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 1, snes_ntsc_black );
SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 2, snes_ntsc_black );
SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
burst_phase = (burst_phase + 1) % snes_ntsc_burst_count;
input += in_row_width;
rgb_out = (char*) rgb_out + out_pitch;
}
}
void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width,
int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch )
{
int chunk_count = (in_width - 2) / (snes_ntsc_in_chunk * 2);
for ( ; in_height; --in_height )
{
SNES_NTSC_IN_T const* line_in = input;
SNES_NTSC_HIRES_ROW( ntsc, burst_phase,
snes_ntsc_black, snes_ntsc_black, snes_ntsc_black,
SNES_NTSC_ADJ_IN( line_in [0] ),
SNES_NTSC_ADJ_IN( line_in [1] ) );
snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out;
int n;
line_in += 2;
for ( n = chunk_count; n; --n )
{
/* twice as many input pixels per chunk */
SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) );
SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) );
SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) );
SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 3, SNES_NTSC_ADJ_IN( line_in [3] ) );
SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 4, SNES_NTSC_ADJ_IN( line_in [4] ) );
SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 5, SNES_NTSC_ADJ_IN( line_in [5] ) );
SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
line_in += 6;
line_out += 7;
}
SNES_NTSC_COLOR_IN( 0, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 1, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 2, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 3, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 4, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 5, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
burst_phase = (burst_phase + 1) % snes_ntsc_burst_count;
input += in_row_width;
rgb_out = (char*) rgb_out + out_pitch;
}
}
#endif

View File

@@ -0,0 +1,228 @@
/* SNES NTSC video filter */
/* snes_ntsc 0.2.2 */
#ifndef SNES_NTSC_H
#define SNES_NTSC_H
#include "snes_ntsc_config.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Image parameters, ranging from -1.0 to 1.0. Actual internal values shown
in parenthesis and should remain fairly stable in future versions. */
typedef struct snes_ntsc_setup_t
{
/* Basic parameters */
double hue; /* -1 = -180 degrees +1 = +180 degrees */
double saturation; /* -1 = grayscale (0.0) +1 = oversaturated colors (2.0) */
double contrast; /* -1 = dark (0.5) +1 = light (1.5) */
double brightness; /* -1 = dark (0.5) +1 = light (1.5) */
double sharpness; /* edge contrast enhancement/blurring */
/* Advanced parameters */
double gamma; /* -1 = dark (1.5) +1 = light (0.5) */
double resolution; /* image resolution */
double artifacts; /* artifacts caused by color changes */
double fringing; /* color artifacts caused by brightness changes */
double bleed; /* color bleed (color resolution reduction) */
int merge_fields; /* if 1, merges even and odd fields together to reduce flicker */
float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */
unsigned long const* bsnes_colortbl; /* undocumented; set to 0 */
} snes_ntsc_setup_t;
/* Video format presets */
extern snes_ntsc_setup_t const snes_ntsc_composite; /* color bleeding + artifacts */
extern snes_ntsc_setup_t const snes_ntsc_svideo; /* color bleeding only */
extern snes_ntsc_setup_t const snes_ntsc_rgb; /* crisp image */
extern snes_ntsc_setup_t const snes_ntsc_monochrome;/* desaturated + artifacts */
/* Initializes and adjusts parameters. Can be called multiple times on the same
snes_ntsc_t object. Can pass NULL for either parameter. */
typedef struct snes_ntsc_t snes_ntsc_t;
void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup );
/* Filters one or more rows of pixels. Input pixel format is set by SNES_NTSC_IN_FORMAT
and output RGB depth is set by SNES_NTSC_OUT_DEPTH. Both default to 16-bit RGB.
In_row_width is the number of pixels to get to the next input row. Out_pitch
is the number of *bytes* to get to the next output row. */
void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input,
long in_row_width, int burst_phase, int in_width, int in_height,
void* rgb_out, long out_pitch );
void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input,
long in_row_width, int burst_phase, int in_width, int in_height,
void* rgb_out, long out_pitch );
/* Number of output pixels written by low-res blitter for given input width. Width
might be rounded down slightly; use SNES_NTSC_IN_WIDTH() on result to find rounded
value. Guaranteed not to round 256 down at all. */
#define SNES_NTSC_OUT_WIDTH( in_width ) \
((((in_width) - 1) / snes_ntsc_in_chunk + 1) * snes_ntsc_out_chunk)
/* Number of low-res input pixels that will fit within given output width. Might be
rounded down slightly; use SNES_NTSC_OUT_WIDTH() on result to find rounded
value. */
#define SNES_NTSC_IN_WIDTH( out_width ) \
(((out_width) / snes_ntsc_out_chunk - 1) * snes_ntsc_in_chunk + 1)
/* Interface for user-defined custom blitters */
enum { snes_ntsc_in_chunk = 3 }; /* number of input pixels read per chunk */
enum { snes_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */
enum { snes_ntsc_black = 0 }; /* palette index for black */
enum { snes_ntsc_burst_count = 3 }; /* burst phase cycles through 0, 1, and 2 */
/* Begins outputting row and starts three pixels. First pixel will be cut off a bit.
Use snes_ntsc_black for unused pixels. Declares variables, so must be before first
statement in a block (unless you're using C++). */
#define SNES_NTSC_BEGIN_ROW( ntsc, burst, pixel0, pixel1, pixel2 ) \
char const* ktable = \
(char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\
SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, SNES_NTSC_IN_FORMAT, ktable )
/* Begins input pixel */
#define SNES_NTSC_COLOR_IN( index, color ) \
SNES_NTSC_COLOR_IN_( index, color, SNES_NTSC_IN_FORMAT, ktable )
/* Generates output pixel. Bits can be 24, 16, 15, 14, 32 (treated as 24), or 0:
24: RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8 RGB)
16: RRRRRGGG GGGBBBBB (5-6-5 RGB)
15: RRRRRGG GGGBBBBB (5-5-5 RGB)
14: BBBBBGG GGGRRRRR (5-5-5 BGR, native SNES format)
0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format; x = junk bits) */
#define SNES_NTSC_RGB_OUT( index, rgb_out, bits ) \
SNES_NTSC_RGB_OUT_14_( index, rgb_out, bits, 1 )
/* Hires equivalents */
#define SNES_NTSC_HIRES_ROW( ntsc, burst, pixel1, pixel2, pixel3, pixel4, pixel5 ) \
char const* ktable = \
(char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\
unsigned const snes_ntsc_pixel1_ = (pixel1);\
snes_ntsc_rgb_t const* kernel1 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel1_ );\
unsigned const snes_ntsc_pixel2_ = (pixel2);\
snes_ntsc_rgb_t const* kernel2 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel2_ );\
unsigned const snes_ntsc_pixel3_ = (pixel3);\
snes_ntsc_rgb_t const* kernel3 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel3_ );\
unsigned const snes_ntsc_pixel4_ = (pixel4);\
snes_ntsc_rgb_t const* kernel4 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel4_ );\
unsigned const snes_ntsc_pixel5_ = (pixel5);\
snes_ntsc_rgb_t const* kernel5 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel5_ );\
snes_ntsc_rgb_t const* kernel0 = kernel1;\
snes_ntsc_rgb_t const* kernelx0;\
snes_ntsc_rgb_t const* kernelx1 = kernel1;\
snes_ntsc_rgb_t const* kernelx2 = kernel1;\
snes_ntsc_rgb_t const* kernelx3 = kernel1;\
snes_ntsc_rgb_t const* kernelx4 = kernel1;\
snes_ntsc_rgb_t const* kernelx5 = kernel1
#define SNES_NTSC_HIRES_OUT( x, rgb_out, bits ) {\
snes_ntsc_rgb_t raw_ =\
kernel0 [ x ] + kernel2 [(x+5)%7+14] + kernel4 [(x+3)%7+28] +\
kernelx0 [(x+7)%7+7] + kernelx2 [(x+5)%7+21] + kernelx4 [(x+3)%7+35] +\
kernel1 [(x+6)%7 ] + kernel3 [(x+4)%7+14] + kernel5 [(x+2)%7+28] +\
kernelx1 [(x+6)%7+7] + kernelx3 [(x+4)%7+21] + kernelx5 [(x+2)%7+35];\
SNES_NTSC_CLAMP_( raw_, 0 );\
SNES_NTSC_RGB_OUT_( rgb_out, (bits), 0 );\
}
/* private */
enum { snes_ntsc_entry_size = 128 };
enum { snes_ntsc_palette_size = 0x2000 };
typedef unsigned long snes_ntsc_rgb_t;
struct snes_ntsc_t {
snes_ntsc_rgb_t table [snes_ntsc_palette_size] [snes_ntsc_entry_size];
};
enum { snes_ntsc_burst_size = snes_ntsc_entry_size / snes_ntsc_burst_count };
#define SNES_NTSC_RGB16( ktable, n ) \
(snes_ntsc_rgb_t const*) (ktable + ((n & 0x001E) | (n >> 1 & 0x03E0) | (n >> 2 & 0x3C00)) * \
(snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t)))
#define SNES_NTSC_BGR15( ktable, n ) \
(snes_ntsc_rgb_t const*) (ktable + ((n << 9 & 0x3C00) | (n & 0x03E0) | (n >> 10 & 0x001E)) * \
(snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t)))
/* common 3->7 ntsc macros */
#define SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, ENTRY, table ) \
unsigned const snes_ntsc_pixel0_ = (pixel0);\
snes_ntsc_rgb_t const* kernel0 = ENTRY( table, snes_ntsc_pixel0_ );\
unsigned const snes_ntsc_pixel1_ = (pixel1);\
snes_ntsc_rgb_t const* kernel1 = ENTRY( table, snes_ntsc_pixel1_ );\
unsigned const snes_ntsc_pixel2_ = (pixel2);\
snes_ntsc_rgb_t const* kernel2 = ENTRY( table, snes_ntsc_pixel2_ );\
snes_ntsc_rgb_t const* kernelx0;\
snes_ntsc_rgb_t const* kernelx1 = kernel0;\
snes_ntsc_rgb_t const* kernelx2 = kernel0
#define SNES_NTSC_RGB_OUT_14_( x, rgb_out, bits, shift ) {\
snes_ntsc_rgb_t raw_ =\
kernel0 [x ] + kernel1 [(x+12)%7+14] + kernel2 [(x+10)%7+28] +\
kernelx0 [(x+7)%14] + kernelx1 [(x+ 5)%7+21] + kernelx2 [(x+ 3)%7+35];\
SNES_NTSC_CLAMP_( raw_, shift );\
SNES_NTSC_RGB_OUT_( rgb_out, bits, shift );\
}
/* common ntsc macros */
#define snes_ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1))
#define snes_ntsc_clamp_mask (snes_ntsc_rgb_builder * 3 / 2)
#define snes_ntsc_clamp_add (snes_ntsc_rgb_builder * 0x101)
#define SNES_NTSC_CLAMP_( io, shift ) {\
snes_ntsc_rgb_t sub = (io) >> (9-(shift)) & snes_ntsc_clamp_mask;\
snes_ntsc_rgb_t clamp = snes_ntsc_clamp_add - sub;\
io |= clamp;\
clamp -= sub;\
io &= clamp;\
}
#define SNES_NTSC_COLOR_IN_( index, color, ENTRY, table ) {\
unsigned color_;\
kernelx##index = kernel##index;\
kernel##index = (color_ = (color), ENTRY( table, color_ ));\
}
/* x is always zero except in snes_ntsc library */
/* original routine */
/*
#define SNES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\
if ( bits == 16 )\
rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\
if ( bits == 24 || bits == 32 )\
rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\
if ( bits == 15 )\
rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\
if ( bits == 14 )\
rgb_out = (raw_>>(24-x)& 0x001F)|(raw_>>(9-x)&0x03E0)|(raw_<<(6+x)&0x7C00);\
if ( bits == 0 )\
rgb_out = raw_ << x;\
}
*/
/* custom bsnes routine -- hooks into bsnes colortable */
#define SNES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\
if ( bits == 16 ) {\
rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\
rgb_out = ((rgb_out&0xf800)>>11)|((rgb_out&0x07c0)>>1)|((rgb_out&0x001f)<<10);\
rgb_out = colortable[rgb_out];\
} else if ( bits == 24 || bits == 32 ) {\
rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\
rgb_out = ((rgb_out&0xf80000)>>19)|((rgb_out&0x00f800)>>6)|((rgb_out&0x0000f8)<<7);\
rgb_out = colortable[rgb_out];\
} else if ( bits == 15 ) {\
rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\
rgb_out = ((rgb_out&0x7c00)>>10)|((rgb_out&0x03e0))|((rgb_out&0x001f)<<10);\
rgb_out = colortable[rgb_out];\
} else {\
rgb_out = raw_ << x;\
}\
}
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,26 @@
/* Configure library by modifying this file */
#ifndef SNES_NTSC_CONFIG_H
#define SNES_NTSC_CONFIG_H
/* Format of source pixels */
/* #define SNES_NTSC_IN_FORMAT SNES_NTSC_RGB16 */
#define SNES_NTSC_IN_FORMAT SNES_NTSC_BGR15
/* The following affect the built-in blitter only; a custom blitter can
handle things however it wants. */
/* Bits per pixel of output. Can be 15, 16, 32, or 24 (same as 32). */
#define SNES_NTSC_OUT_DEPTH 32
/* Type of input pixel values */
#define SNES_NTSC_IN_T unsigned short
/* Each raw pixel input value is passed through this. You might want to mask
the pixel index if you use the high bits as flags, etc. */
#define SNES_NTSC_ADJ_IN( in ) in
/* For each pixel, this is the basic operation:
output_color = SNES_NTSC_ADJ_IN( SNES_NTSC_IN_T ) */
#endif

View File

@@ -0,0 +1,439 @@
/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */
/* Common implementation of NTSC filters */
#include <assert.h>
#include <math.h>
/* Copyright (C) 2006 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module 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 Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#define DISABLE_CORRECTION 0
#undef PI
#define PI 3.14159265358979323846f
#ifndef LUMA_CUTOFF
#define LUMA_CUTOFF 0.20
#endif
#ifndef gamma_size
#define gamma_size 1
#endif
#ifndef rgb_bits
#define rgb_bits 8
#endif
#ifndef artifacts_max
#define artifacts_max (artifacts_mid * 1.5f)
#endif
#ifndef fringing_max
#define fringing_max (fringing_mid * 2)
#endif
#ifndef STD_HUE_CONDITION
#define STD_HUE_CONDITION( setup ) 1
#endif
#define ext_decoder_hue (std_decoder_hue + 15)
#define rgb_unit (1 << rgb_bits)
#define rgb_offset (rgb_unit * 2 + 0.5f)
enum { burst_size = snes_ntsc_entry_size / burst_count };
enum { kernel_half = 16 };
enum { kernel_size = kernel_half * 2 + 1 };
typedef struct init_t
{
float to_rgb [burst_count * 6];
float to_float [gamma_size];
float contrast;
float brightness;
float artifacts;
float fringing;
float kernel [rescale_out * kernel_size * 2];
} init_t;
#define ROTATE_IQ( i, q, sin_b, cos_b ) {\
float t;\
t = i * cos_b - q * sin_b;\
q = i * sin_b + q * cos_b;\
i = t;\
}
static void init_filters( init_t* impl, snes_ntsc_setup_t const* setup )
{
#if rescale_out > 1
float kernels [kernel_size * 2];
#else
float* const kernels = impl->kernel;
#endif
/* generate luma (y) filter using sinc kernel */
{
/* sinc with rolloff (dsf) */
float const rolloff = 1 + (float) setup->sharpness * (float) 0.032;
float const maxh = 32;
float const pow_a_n = (float) pow( rolloff, maxh );
float sum;
int i;
/* quadratic mapping to reduce negative (blurring) range */
float to_angle = (float) setup->resolution + 1;
to_angle = PI / maxh * (float) LUMA_CUTOFF * (to_angle * to_angle + 1);
kernels [kernel_size * 3 / 2] = maxh; /* default center value */
for ( i = 0; i < kernel_half * 2 + 1; i++ )
{
int x = i - kernel_half;
float angle = x * to_angle;
/* instability occurs at center point with rolloff very close to 1.0 */
if ( x || pow_a_n > (float) 1.056 || pow_a_n < (float) 0.981 )
{
float rolloff_cos_a = rolloff * (float) cos( angle );
float num = 1 - rolloff_cos_a -
pow_a_n * (float) cos( maxh * angle ) +
pow_a_n * rolloff * (float) cos( (maxh - 1) * angle );
float den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff;
float dsf = num / den;
kernels [kernel_size * 3 / 2 - kernel_half + i] = dsf - (float) 0.5;
}
}
/* apply blackman window and find sum */
sum = 0;
for ( i = 0; i < kernel_half * 2 + 1; i++ )
{
float x = PI * 2 / (kernel_half * 2) * i;
float blackman = 0.42f - 0.5f * (float) cos( x ) + 0.08f * (float) cos( x * 2 );
sum += (kernels [kernel_size * 3 / 2 - kernel_half + i] *= blackman);
}
/* normalize kernel */
sum = 1.0f / sum;
for ( i = 0; i < kernel_half * 2 + 1; i++ )
{
int x = kernel_size * 3 / 2 - kernel_half + i;
kernels [x] *= sum;
assert( kernels [x] == kernels [x] ); /* catch numerical instability */
}
}
/* generate chroma (iq) filter using gaussian kernel */
{
float const cutoff_factor = -0.03125f;
float cutoff = (float) setup->bleed;
int i;
if ( cutoff < 0 )
{
/* keep extreme value accessible only near upper end of scale (1.0) */
cutoff *= cutoff;
cutoff *= cutoff;
cutoff *= cutoff;
cutoff *= -30.0f / 0.65f;
}
cutoff = cutoff_factor - 0.65f * cutoff_factor * cutoff;
for ( i = -kernel_half; i <= kernel_half; i++ )
kernels [kernel_size / 2 + i] = (float) exp( i * i * cutoff );
/* normalize even and odd phases separately */
for ( i = 0; i < 2; i++ )
{
float sum = 0;
int x;
for ( x = i; x < kernel_size; x += 2 )
sum += kernels [x];
sum = 1.0f / sum;
for ( x = i; x < kernel_size; x += 2 )
{
kernels [x] *= sum;
assert( kernels [x] == kernels [x] ); /* catch numerical instability */
}
}
}
/*
printf( "luma:\n" );
for ( i = kernel_size; i < kernel_size * 2; i++ )
printf( "%f\n", kernels [i] );
printf( "chroma:\n" );
for ( i = 0; i < kernel_size; i++ )
printf( "%f\n", kernels [i] );
*/
/* generate linear rescale kernels */
#if rescale_out > 1
{
float weight = 1.0f;
float* out = impl->kernel;
int n = rescale_out;
do
{
float remain = 0;
int i;
weight -= 1.0f / rescale_in;
for ( i = 0; i < kernel_size * 2; i++ )
{
float cur = kernels [i];
float m = cur * weight;
*out++ = m + remain;
remain = cur - m;
}
}
while ( --n );
}
#endif
}
static float const default_decoder [6] =
{ 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f };
static void init( init_t* impl, snes_ntsc_setup_t const* setup )
{
impl->brightness = (float) setup->brightness * (0.5f * rgb_unit) + rgb_offset;
impl->contrast = (float) setup->contrast * (0.5f * rgb_unit) + rgb_unit;
#ifdef default_palette_contrast
if ( !setup->palette )
impl->contrast *= default_palette_contrast;
#endif
impl->artifacts = (float) setup->artifacts;
if ( impl->artifacts > 0 )
impl->artifacts *= artifacts_max - artifacts_mid;
impl->artifacts = impl->artifacts * artifacts_mid + artifacts_mid;
impl->fringing = (float) setup->fringing;
if ( impl->fringing > 0 )
impl->fringing *= fringing_max - fringing_mid;
impl->fringing = impl->fringing * fringing_mid + fringing_mid;
init_filters( impl, setup );
/* generate gamma table */
if ( gamma_size > 1 )
{
float const to_float = 1.0f / (gamma_size - (gamma_size > 1));
float const gamma = 1.1333f - (float) setup->gamma * 0.5f;
/* match common PC's 2.2 gamma to TV's 2.65 gamma */
int i;
for ( i = 0; i < gamma_size; i++ )
impl->to_float [i] =
(float) pow( i * to_float, gamma ) * impl->contrast + impl->brightness;
}
/* setup decoder matricies */
{
float hue = (float) setup->hue * PI + PI / 180 * ext_decoder_hue;
float sat = (float) setup->saturation + 1;
float const* decoder = setup->decoder_matrix;
if ( !decoder )
{
decoder = default_decoder;
if ( STD_HUE_CONDITION( setup ) )
hue += PI / 180 * (std_decoder_hue - ext_decoder_hue);
}
{
float s = (float) sin( hue ) * sat;
float c = (float) cos( hue ) * sat;
float* out = impl->to_rgb;
int n;
n = burst_count;
do
{
float const* in = decoder;
int n = 3;
do
{
float i = *in++;
float q = *in++;
*out++ = i * c - q * s;
*out++ = i * s + q * c;
}
while ( --n );
if ( burst_count <= 1 )
break;
ROTATE_IQ( s, c, 0.866025f, -0.5f ); /* +120 degrees */
}
while ( --n );
}
}
}
/* kernel generation */
#define RGB_TO_YIQ( r, g, b, y, i ) (\
(y = (r) * 0.299f + (g) * 0.587f + (b) * 0.114f),\
(i = (r) * 0.596f - (g) * 0.275f - (b) * 0.321f),\
((r) * 0.212f - (g) * 0.523f + (b) * 0.311f)\
)
#define YIQ_TO_RGB( y, i, q, to_rgb, type, r, g ) (\
r = (type) (y + to_rgb [0] * i + to_rgb [1] * q),\
g = (type) (y + to_rgb [2] * i + to_rgb [3] * q),\
(type) (y + to_rgb [4] * i + to_rgb [5] * q)\
)
#define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1)
enum { rgb_kernel_size = burst_size / alignment_count };
enum { rgb_bias = rgb_unit * 2 * snes_ntsc_rgb_builder };
typedef struct pixel_info_t
{
int offset;
float negate;
float kernel [4];
} pixel_info_t;
#if rescale_in > 1
#define PIXEL_OFFSET_( ntsc, scaled ) \
(kernel_size / 2 + ntsc + (scaled != 0) + (rescale_out - scaled) % rescale_out + \
(kernel_size * 2 * scaled))
#define PIXEL_OFFSET( ntsc, scaled ) \
PIXEL_OFFSET_( ((ntsc) - (scaled) / rescale_out * rescale_in),\
(((scaled) + rescale_out * 10) % rescale_out) ),\
(1.0f - (((ntsc) + 100) & 2))
#else
#define PIXEL_OFFSET( ntsc, scaled ) \
(kernel_size / 2 + (ntsc) - (scaled)),\
(1.0f - (((ntsc) + 100) & 2))
#endif
extern pixel_info_t const snes_ntsc_pixels [alignment_count];
/* Generate pixel at all burst phases and column alignments */
static void gen_kernel( init_t* impl, float y, float i, float q, snes_ntsc_rgb_t* out )
{
/* generate for each scanline burst phase */
float const* to_rgb = impl->to_rgb;
int burst_remain = burst_count;
y -= rgb_offset;
do
{
/* Encode yiq into *two* composite signals (to allow control over artifacting).
Convolve these with kernels which: filter respective components, apply
sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack
into integer. Based on algorithm by NewRisingSun. */
pixel_info_t const* pixel = snes_ntsc_pixels;
int alignment_remain = alignment_count;
do
{
/* negate is -1 when composite starts at odd multiple of 2 */
float const yy = y * impl->fringing * pixel->negate;
float const ic0 = (i + yy) * pixel->kernel [0];
float const qc1 = (q + yy) * pixel->kernel [1];
float const ic2 = (i - yy) * pixel->kernel [2];
float const qc3 = (q - yy) * pixel->kernel [3];
float const factor = impl->artifacts * pixel->negate;
float const ii = i * factor;
float const yc0 = (y + ii) * pixel->kernel [0];
float const yc2 = (y - ii) * pixel->kernel [2];
float const qq = q * factor;
float const yc1 = (y + qq) * pixel->kernel [1];
float const yc3 = (y - qq) * pixel->kernel [3];
float const* k = &impl->kernel [pixel->offset];
int n;
++pixel;
for ( n = rgb_kernel_size; n; --n )
{
float i = k[0]*ic0 + k[2]*ic2;
float q = k[1]*qc1 + k[3]*qc3;
float y = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 +
k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset;
if ( rescale_out <= 1 )
k--;
else if ( k < &impl->kernel [kernel_size * 2 * (rescale_out - 1)] )
k += kernel_size * 2 - 1;
else
k -= kernel_size * 2 * (rescale_out - 1) + 2;
{
int r, g, b = YIQ_TO_RGB( y, i, q, to_rgb, int, r, g );
*out++ = PACK_RGB( r, g, b ) - rgb_bias;
}
}
}
while ( alignment_count > 1 && --alignment_remain );
if ( burst_count <= 1 )
break;
to_rgb += 6;
ROTATE_IQ( i, q, -0.866025f, -0.5f ); /* -120 degrees */
}
while ( --burst_remain );
}
static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out );
#if DISABLE_CORRECTION
#define CORRECT_ERROR( a ) { out [i] += rgb_bias; }
#define DISTRIBUTE_ERROR( a, b, c ) { out [i] += rgb_bias; }
#else
#define CORRECT_ERROR( a ) { out [a] += error; }
#define DISTRIBUTE_ERROR( a, b, c ) {\
snes_ntsc_rgb_t fourth = (error + 2 * snes_ntsc_rgb_builder) >> 2;\
fourth &= (rgb_bias >> 1) - snes_ntsc_rgb_builder;\
fourth -= rgb_bias >> 2;\
out [a] += fourth;\
out [b] += fourth;\
out [c] += fourth;\
out [i] += error - (fourth * 3);\
}
#endif
#define RGB_PALETTE_OUT( rgb, out_ )\
{\
unsigned char* out = (out_);\
snes_ntsc_rgb_t clamped = (rgb);\
SNES_NTSC_CLAMP_( clamped, (8 - rgb_bits) );\
out [0] = (unsigned char) (clamped >> 21);\
out [1] = (unsigned char) (clamped >> 11);\
out [2] = (unsigned char) (clamped >> 1);\
}
/* blitter related */
#ifndef restrict
#if defined (__GNUC__)
#define restrict __restrict__
#elif defined (_MSC_VER) && _MSC_VER > 1300
#define restrict __restrict
#else
/* no support for restricted pointers */
#define restrict
#endif
#endif
#include <limits.h>
#if SNES_NTSC_OUT_DEPTH <= 16
#if USHRT_MAX == 0xFFFF
typedef unsigned short snes_ntsc_out_t;
#else
#error "Need 16-bit int type"
#endif
#else
#if UINT_MAX == 0xFFFFFFFF
typedef unsigned int snes_ntsc_out_t;
#elif ULONG_MAX == 0xFFFFFFFF
typedef unsigned long snes_ntsc_out_t;
#else
#error "Need 32-bit int type"
#endif
#endif

View File

@@ -0,0 +1,25 @@
namespace Filter::Super2xSaI {
auto size(uint& width, uint& height) -> void {
width *= 2;
height *= 2;
}
uint32_t temp[512 * 480];
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void {
for(unsigned y = 0; y < height; y++) {
const uint16_t *line_in = (const uint16_t*)(((const uint8_t*)input) + pitch * y);
uint32_t *line_out = temp + y * width;
for(unsigned x = 0; x < width; x++) {
line_out[x] = colortable[line_in[x]];
}
}
Super2xSaI32((unsigned char*)temp, width * sizeof(uint32_t), 0, (unsigned char*)output, outpitch, width, height);
}
}

View File

@@ -0,0 +1,25 @@
namespace Filter::SuperEagle {
auto size(uint& width, uint& height) -> void {
width *= 2;
height *= 2;
}
uint32_t temp[512 * 480];
auto render(
uint32_t* colortable, uint32_t* output, uint outpitch,
const uint16_t* input, uint pitch, uint width, uint height
) -> void {
for(unsigned y = 0; y < height; y++) {
const uint16_t *line_in = (const uint16_t*)(((const uint8_t*)input) + pitch * y);
uint32_t *line_out = temp + y * width;
for(unsigned x = 0; x < width; x++) {
line_out[x] = colortable[line_in[x]];
}
}
SuperEagle32((unsigned char*)temp, width * sizeof(uint32_t), 0, (unsigned char*)output, outpitch, width, height);
}
}

1040
bsnes/gb/Core/apu.c Normal file

File diff suppressed because it is too large Load Diff

164
bsnes/gb/Core/apu.h Normal file
View File

@@ -0,0 +1,164 @@
#ifndef apu_h
#define apu_h
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include "gb_struct_def.h"
#ifdef GB_INTERNAL
/* Speed = 1 / Length (in seconds) */
#define DAC_DECAY_SPEED 20000
#define DAC_ATTACK_SPEED 20000
/* Divides nicely and never overflows with 4 channels and 8 (1-8) volume levels */
#ifdef WIIU
/* Todo: Remove this hack once https://github.com/libretro/RetroArch/issues/6252 is fixed*/
#define MAX_CH_AMP (0xFF0 / 2)
#else
#define MAX_CH_AMP 0xFF0
#endif
#define CH_STEP (MAX_CH_AMP/0xF/8)
#endif
/* APU ticks are 2MHz, triggered by an internal APU clock. */
typedef struct
{
int16_t left;
int16_t right;
} GB_sample_t;
typedef struct
{
double left;
double right;
} GB_double_sample_t;
enum GB_CHANNELS {
GB_SQUARE_1,
GB_SQUARE_2,
GB_WAVE,
GB_NOISE,
GB_N_CHANNELS
};
typedef void (*GB_sample_callback_t)(GB_gameboy_t *gb, GB_sample_t *sample);
typedef struct
{
bool global_enable;
uint8_t apu_cycles;
uint8_t samples[GB_N_CHANNELS];
bool is_active[GB_N_CHANNELS];
uint8_t div_divider; // The DIV register ticks the APU at 512Hz, but is then divided
// once more to generate 128Hz and 64Hz clocks
uint8_t lf_div; // The APU runs in 2MHz, but channels 1, 2 and 4 run in 1MHZ so we divide
// need to divide the signal.
uint8_t square_sweep_countdown; // In 128Hz
uint8_t square_sweep_calculate_countdown; // In 2 MHz
uint16_t new_sweep_sample_legnth;
uint16_t shadow_sweep_sample_legnth;
bool sweep_enabled;
bool sweep_decreasing;
struct {
uint16_t pulse_length; // Reloaded from NRX1 (xorred), in 256Hz DIV ticks
uint8_t current_volume; // Reloaded from NRX2
uint8_t volume_countdown; // Reloaded from NRX2
uint8_t current_sample_index; /* For save state compatibility,
highest bit is reused (See NR14/NR24's
write code)*/
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF)
uint16_t sample_length; // From NRX3, NRX4, in APU ticks
bool length_enabled; // NRX4
} square_channels[2];
struct {
bool enable; // NR30
uint16_t pulse_length; // Reloaded from NR31 (xorred), in 256Hz DIV ticks
uint8_t shift; // NR32
uint16_t sample_length; // NR33, NR34, in APU ticks
bool length_enabled; // NR34
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF)
uint8_t current_sample_index;
uint8_t current_sample; // Current sample before shifting.
int8_t wave_form[32];
bool wave_form_just_read;
} wave_channel;
struct {
uint16_t pulse_length; // Reloaded from NR41 (xorred), in 256Hz DIV ticks
uint8_t current_volume; // Reloaded from NR42
uint8_t volume_countdown; // Reloaded from NR42
uint16_t lfsr;
bool narrow;
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length)
uint16_t sample_length; // From NR43, in APU ticks
bool length_enabled; // NR44
uint8_t alignment; // If (NR43 & 7) != 0, samples are aligned to 512KHz clock instead of
// 1MHz. This variable keeps track of the alignment.
} noise_channel;
bool skip_div_event;
bool current_lfsr_sample;
} GB_apu_t;
typedef enum {
GB_HIGHPASS_OFF, // Do not apply any filter, keep DC offset
GB_HIGHPASS_ACCURATE, // Apply a highpass filter similar to the one used on hardware
GB_HIGHPASS_REMOVE_DC_OFFSET, // Remove DC Offset without affecting the waveform
GB_HIGHPASS_MAX
} GB_highpass_mode_t;
typedef struct {
unsigned sample_rate;
double sample_cycles; // In 8 MHz units
double cycles_per_sample;
// Samples are NOT normalized to MAX_CH_AMP * 4 at this stage!
unsigned cycles_since_render;
unsigned last_update[GB_N_CHANNELS];
GB_sample_t current_sample[GB_N_CHANNELS];
GB_sample_t summed_samples[GB_N_CHANNELS];
double dac_discharge[GB_N_CHANNELS];
GB_highpass_mode_t highpass_mode;
double highpass_rate;
GB_double_sample_t highpass_diff;
GB_sample_callback_t sample_callback;
bool rate_set_in_clocks;
} GB_apu_output_t;
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate);
void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample); /* Cycles are in 8MHz units */
void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode);
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback);
#ifdef GB_INTERNAL
bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index);
void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value);
uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg);
void GB_apu_div_event(GB_gameboy_t *gb);
void GB_apu_init(GB_gameboy_t *gb);
void GB_apu_run(GB_gameboy_t *gb);
void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb);
#endif
#endif /* apu_h */

149
bsnes/gb/Core/camera.c Normal file
View File

@@ -0,0 +1,149 @@
#include "gb.h"
static int noise_seed = 0;
/* This is not a complete emulation of the camera chip. Only the features used by the GameBoy Camera ROMs are supported.
We also do not emulate the timing of the real cart, as it might be actually faster than the webcam. */
static uint8_t generate_noise(uint8_t x, uint8_t y)
{
int value = (x + y * 128 + noise_seed);
uint8_t *data = (uint8_t *) &value;
unsigned hash = 0;
while ((int *) data != &value + 1) {
hash ^= (*data << 8);
if (hash & 0x8000) {
hash ^= 0x8a00;
hash ^= *data;
}
data++;
hash <<= 1;
}
return (hash >> 8);
}
static long get_processed_color(GB_gameboy_t *gb, uint8_t x, uint8_t y)
{
if (x >= 128) {
x = 0;
}
if (y >= 112) {
y = 0;
}
long color = gb->camera_get_pixel_callback? gb->camera_get_pixel_callback(gb, x, y) : (generate_noise(x, y));
static const double gain_values[] =
{0.8809390, 0.9149149, 0.9457498, 0.9739758,
1.0000000, 1.0241412, 1.0466537, 1.0677433,
1.0875793, 1.1240310, 1.1568911, 1.1868043,
1.2142561, 1.2396208, 1.2743837, 1.3157323,
1.3525190, 1.3856512, 1.4157897, 1.4434309,
1.4689574, 1.4926697, 1.5148087, 1.5355703,
1.5551159, 1.5735801, 1.5910762, 1.6077008,
1.6235366, 1.6386550, 1.6531183, 1.6669808};
/* Multiply color by gain value */
color *= gain_values[gb->camera_registers[GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS] & 0x1F];
/* Color is multiplied by the exposure register to simulate exposure. */
color = color * ((gb->camera_registers[GB_CAMERA_EXPOSURE_HIGH] << 8) + gb->camera_registers[GB_CAMERA_EXPOSURE_LOW]) / 0x1000;
return color;
}
uint8_t GB_camera_read_image(GB_gameboy_t *gb, uint16_t addr)
{
if (gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1) {
/* Forbid reading the image while the camera is busy. */
return 0xFF;
}
uint8_t tile_x = addr / 0x10 % 0x10;
uint8_t tile_y = addr / 0x10 / 0x10;
uint8_t y = ((addr >> 1) & 0x7) + tile_y * 8;
uint8_t bit = addr & 1;
uint8_t ret = 0;
for (uint8_t x = tile_x * 8; x < tile_x * 8 + 8; x++) {
long color = get_processed_color(gb, x, y);
static const double edge_enhancement_ratios[] = {0.5, 0.75, 1, 1.25, 2, 3, 4, 5};
double edge_enhancement_ratio = edge_enhancement_ratios[(gb->camera_registers[GB_CAMERA_EDGE_ENHANCEMENT_INVERT_AND_VOLTAGE] >> 4) & 0x7];
if ((gb->camera_registers[GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS] & 0xE0) == 0xE0) {
color += (color * 4) * edge_enhancement_ratio;
color -= get_processed_color(gb, x - 1, y) * edge_enhancement_ratio;
color -= get_processed_color(gb, x + 1, y) * edge_enhancement_ratio;
color -= get_processed_color(gb, x, y - 1) * edge_enhancement_ratio;
color -= get_processed_color(gb, x, y + 1) * edge_enhancement_ratio;
}
/* The camera's registers are used as a threshold pattern, which defines the dithering */
uint8_t pattern_base = ((x & 3) + (y & 3) * 4) * 3 + GB_CAMERA_DITHERING_PATTERN_START;
if (color < gb->camera_registers[pattern_base]) {
color = 3;
}
else if (color < gb->camera_registers[pattern_base + 1]) {
color = 2;
}
else if (color < gb->camera_registers[pattern_base + 2]) {
color = 1;
}
else {
color = 0;
}
ret <<= 1;
ret |= (color >> bit) & 1;
}
return ret;
}
void GB_set_camera_get_pixel_callback(GB_gameboy_t *gb, GB_camera_get_pixel_callback_t callback)
{
gb->camera_get_pixel_callback = callback;
}
void GB_set_camera_update_request_callback(GB_gameboy_t *gb, GB_camera_update_request_callback_t callback)
{
gb->camera_update_request_callback = callback;
}
void GB_camera_updated(GB_gameboy_t *gb)
{
gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] &= ~1;
}
void GB_camera_write_register(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
{
addr &= 0x7F;
if (addr == GB_CAMERA_SHOOT_AND_1D_FLAGS) {
value &= 0x7;
noise_seed = rand();
if ((value & 1) && !(gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1) && gb->camera_update_request_callback) {
/* If no callback is set, ignore the write as if the camera is instantly done */
gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] |= 1;
gb->camera_update_request_callback(gb);
}
}
else {
if (addr >= 0x36) {
GB_log(gb, "Wrote invalid camera register %02x: %2x\n", addr, value);
return;
}
gb->camera_registers[addr] = value;
}
}
uint8_t GB_camera_read_register(GB_gameboy_t *gb, uint16_t addr)
{
if ((addr & 0x7F) == 0) {
return gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS];
}
return 0;
}

29
bsnes/gb/Core/camera.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef camera_h
#define camera_h
#include <stdint.h>
#include "gb_struct_def.h"
typedef uint8_t (*GB_camera_get_pixel_callback_t)(GB_gameboy_t *gb, uint8_t x, uint8_t y);
typedef void (*GB_camera_update_request_callback_t)(GB_gameboy_t *gb);
enum {
GB_CAMERA_SHOOT_AND_1D_FLAGS = 0,
GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS = 1,
GB_CAMERA_EXPOSURE_HIGH = 2,
GB_CAMERA_EXPOSURE_LOW = 3,
GB_CAMERA_EDGE_ENHANCEMENT_INVERT_AND_VOLTAGE = 4,
GB_CAMERA_DITHERING_PATTERN_START = 6,
GB_CAMERA_DITHERING_PATTERN_END = 0x35,
};
uint8_t GB_camera_read_image(GB_gameboy_t *gb, uint16_t addr);
void GB_set_camera_get_pixel_callback(GB_gameboy_t *gb, GB_camera_get_pixel_callback_t callback);
void GB_set_camera_update_request_callback(GB_gameboy_t *gb, GB_camera_update_request_callback_t callback);
void GB_camera_updated(GB_gameboy_t *gb);
void GB_camera_write_register(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
uint8_t GB_camera_read_register(GB_gameboy_t *gb, uint16_t addr);
#endif

2459
bsnes/gb/Core/debugger.c Normal file

File diff suppressed because it is too large Load Diff

43
bsnes/gb/Core/debugger.h Normal file
View File

@@ -0,0 +1,43 @@
#ifndef debugger_h
#define debugger_h
#include <stdbool.h>
#include <stdint.h>
#include "gb_struct_def.h"
#include "symbol_hash.h"
#ifdef GB_INTERNAL
#ifdef DISABLE_DEBUGGER
#define GB_debugger_run(gb) (void)0
#define GB_debugger_handle_async_commands(gb) (void)0
#define GB_debugger_ret_hook(gb) (void)0
#define GB_debugger_call_hook(gb, addr) (void)addr
#define GB_debugger_test_write_watchpoint(gb, addr, value) ((void)addr, (void)value)
#define GB_debugger_test_read_watchpoint(gb, addr) (void)addr
#else
void GB_debugger_run(GB_gameboy_t *gb);
void GB_debugger_handle_async_commands(GB_gameboy_t *gb);
void GB_debugger_call_hook(GB_gameboy_t *gb, uint16_t call_addr);
void GB_debugger_ret_hook(GB_gameboy_t *gb);
void GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
void GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, uint16_t addr);
const GB_bank_symbol_t *GB_debugger_find_symbol(GB_gameboy_t *gb, uint16_t addr);
#endif /* DISABLE_DEBUGGER */
#endif
#ifdef GB_INTERNAL
bool /* Returns true if debugger waits for more commands. Not relevant for non-GB_INTERNAL */
#else
void
#endif
GB_debugger_execute_command(GB_gameboy_t *gb, char *input); /* Destroys input. */
void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path);
const char *GB_debugger_name_for_address(GB_gameboy_t *gb, uint16_t addr);
bool GB_debugger_evaluate(GB_gameboy_t *gb, const char *string, uint16_t *result, uint16_t *result_bank); /* result_bank is -1 if unused. */
void GB_debugger_break(GB_gameboy_t *gb);
bool GB_debugger_is_stopped(GB_gameboy_t *gb);
void GB_debugger_set_disabled(GB_gameboy_t *gb, bool disabled);
void GB_debugger_clear_symbols(GB_gameboy_t *gb);
#endif /* debugger_h */

1227
bsnes/gb/Core/display.c Normal file

File diff suppressed because it is too large Load Diff

54
bsnes/gb/Core/display.h Normal file
View File

@@ -0,0 +1,54 @@
#ifndef display_h
#define display_h
#include "gb.h"
#include <stdbool.h>
#include <stdint.h>
#ifdef GB_INTERNAL
void GB_display_run(GB_gameboy_t *gb, uint8_t cycles);
void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint8_t index);
void GB_window_related_write(GB_gameboy_t *gb, uint8_t addr, uint8_t value);
void GB_STAT_update(GB_gameboy_t *gb);
void GB_lcd_off(GB_gameboy_t *gb);
#endif
typedef enum {
GB_PALETTE_NONE,
GB_PALETTE_BACKGROUND,
GB_PALETTE_OAM,
GB_PALETTE_AUTO,
} GB_palette_type_t;
typedef enum {
GB_MAP_AUTO,
GB_MAP_9800,
GB_MAP_9C00,
} GB_map_type_t;
typedef enum {
GB_TILESET_AUTO,
GB_TILESET_8800,
GB_TILESET_8000,
} GB_tileset_type_t;
typedef struct {
uint32_t image[128];
uint8_t x, y, tile, flags;
uint16_t oam_addr;
bool obscured_by_line_limit;
} GB_oam_info_t;
typedef enum {
GB_COLOR_CORRECTION_DISABLED,
GB_COLOR_CORRECTION_CORRECT_CURVES,
GB_COLOR_CORRECTION_EMULATE_HARDWARE,
GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS,
} GB_color_correction_mode_t;
void GB_draw_tileset(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index);
void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index, GB_map_type_t map_type, GB_tileset_type_t tileset_type);
uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *sprite_height);
uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color);
void GB_set_color_correction_mode(GB_gameboy_t *gb, GB_color_correction_mode_t mode);
#endif /* display_h */

1084
bsnes/gb/Core/gb.c Normal file

File diff suppressed because it is too large Load Diff

725
bsnes/gb/Core/gb.h Normal file
View File

@@ -0,0 +1,725 @@
#ifndef GB_h
#define GB_h
#define typeof __typeof__
#include <stdbool.h>
#include <stdint.h>
#include <time.h>
#include "gb_struct_def.h"
#include "save_state.h"
#include "apu.h"
#include "camera.h"
#include "debugger.h"
#include "display.h"
#include "joypad.h"
#include "mbc.h"
#include "memory.h"
#include "printer.h"
#include "timing.h"
#include "rewind.h"
#include "sm83_cpu.h"
#include "symbol_hash.h"
#include "sgb.h"
#define GB_STRUCT_VERSION 13
#define GB_MODEL_FAMILY_MASK 0xF00
#define GB_MODEL_DMG_FAMILY 0x000
#define GB_MODEL_MGB_FAMILY 0x100
#define GB_MODEL_CGB_FAMILY 0x200
#define GB_MODEL_PAL_BIT 0x1000
#define GB_MODEL_NO_SFC_BIT 0x2000
#ifdef GB_INTERNAL
#if __clang__
#define UNROLL _Pragma("unroll")
#elif __GNUC__
#define UNROLL _Pragma("GCC unroll 8")
#else
#define UNROLL
#endif
#endif
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define GB_BIG_ENDIAN
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define GB_LITTLE_ENDIAN
#else
#error Unable to detect endianess
#endif
typedef union {
struct {
uint8_t seconds;
uint8_t minutes;
uint8_t hours;
uint8_t days;
uint8_t high;
};
uint8_t data[5];
} GB_rtc_time_t;
typedef enum {
// GB_MODEL_DMG_0 = 0x000,
// GB_MODEL_DMG_A = 0x001,
GB_MODEL_DMG_B = 0x002,
// GB_MODEL_DMG_C = 0x003,
GB_MODEL_SGB = 0x004,
GB_MODEL_SGB_NTSC = GB_MODEL_SGB,
GB_MODEL_SGB_PAL = GB_MODEL_SGB | GB_MODEL_PAL_BIT,
GB_MODEL_SGB_NTSC_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT,
GB_MODEL_SGB_NO_SFC = GB_MODEL_SGB_NTSC_NO_SFC,
GB_MODEL_SGB_PAL_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT | GB_MODEL_PAL_BIT,
// GB_MODEL_MGB = 0x100,
GB_MODEL_SGB2 = 0x101,
GB_MODEL_SGB2_NO_SFC = GB_MODEL_SGB2 | GB_MODEL_NO_SFC_BIT,
// GB_MODEL_CGB_0 = 0x200,
// GB_MODEL_CGB_A = 0x201,
// GB_MODEL_CGB_B = 0x202,
GB_MODEL_CGB_C = 0x203,
// GB_MODEL_CGB_D = 0x204,
GB_MODEL_CGB_E = 0x205,
GB_MODEL_AGB = 0x206,
} GB_model_t;
enum {
GB_REGISTER_AF,
GB_REGISTER_BC,
GB_REGISTER_DE,
GB_REGISTER_HL,
GB_REGISTER_SP,
GB_REGISTERS_16_BIT /* Count */
};
/* Todo: Actually use these! */
enum {
GB_CARRY_FLAG = 16,
GB_HALF_CARRY_FLAG = 32,
GB_SUBSTRACT_FLAG = 64,
GB_ZERO_FLAG = 128,
};
#define GB_MAX_IR_QUEUE 256
enum {
/* Joypad and Serial */
GB_IO_JOYP = 0x00, // Joypad (R/W)
GB_IO_SB = 0x01, // Serial transfer data (R/W)
GB_IO_SC = 0x02, // Serial Transfer Control (R/W)
/* Missing */
/* Timers */
GB_IO_DIV = 0x04, // Divider Register (R/W)
GB_IO_TIMA = 0x05, // Timer counter (R/W)
GB_IO_TMA = 0x06, // Timer Modulo (R/W)
GB_IO_TAC = 0x07, // Timer Control (R/W)
/* Missing */
GB_IO_IF = 0x0f, // Interrupt Flag (R/W)
/* Sound */
GB_IO_NR10 = 0x10, // Channel 1 Sweep register (R/W)
GB_IO_NR11 = 0x11, // Channel 1 Sound length/Wave pattern duty (R/W)
GB_IO_NR12 = 0x12, // Channel 1 Volume Envelope (R/W)
GB_IO_NR13 = 0x13, // Channel 1 Frequency lo (Write Only)
GB_IO_NR14 = 0x14, // Channel 1 Frequency hi (R/W)
/* NR20 does not exist */
GB_IO_NR21 = 0x16, // Channel 2 Sound Length/Wave Pattern Duty (R/W)
GB_IO_NR22 = 0x17, // Channel 2 Volume Envelope (R/W)
GB_IO_NR23 = 0x18, // Channel 2 Frequency lo data (W)
GB_IO_NR24 = 0x19, // Channel 2 Frequency hi data (R/W)
GB_IO_NR30 = 0x1a, // Channel 3 Sound on/off (R/W)
GB_IO_NR31 = 0x1b, // Channel 3 Sound Length
GB_IO_NR32 = 0x1c, // Channel 3 Select output level (R/W)
GB_IO_NR33 = 0x1d, // Channel 3 Frequency's lower data (W)
GB_IO_NR34 = 0x1e, // Channel 3 Frequency's higher data (R/W)
/* NR40 does not exist */
GB_IO_NR41 = 0x20, // Channel 4 Sound Length (R/W)
GB_IO_NR42 = 0x21, // Channel 4 Volume Envelope (R/W)
GB_IO_NR43 = 0x22, // Channel 4 Polynomial Counter (R/W)
GB_IO_NR44 = 0x23, // Channel 4 Counter/consecutive, Inital (R/W)
GB_IO_NR50 = 0x24, // Channel control / ON-OFF / Volume (R/W)
GB_IO_NR51 = 0x25, // Selection of Sound output terminal (R/W)
GB_IO_NR52 = 0x26, // Sound on/off
/* Missing */
GB_IO_WAV_START = 0x30, // Wave pattern start
GB_IO_WAV_END = 0x3f, // Wave pattern end
/* Graphics */
GB_IO_LCDC = 0x40, // LCD Control (R/W)
GB_IO_STAT = 0x41, // LCDC Status (R/W)
GB_IO_SCY = 0x42, // Scroll Y (R/W)
GB_IO_SCX = 0x43, // Scroll X (R/W)
GB_IO_LY = 0x44, // LCDC Y-Coordinate (R)
GB_IO_LYC = 0x45, // LY Compare (R/W)
GB_IO_DMA = 0x46, // DMA Transfer and Start Address (W)
GB_IO_BGP = 0x47, // BG Palette Data (R/W) - Non CGB Mode Only
GB_IO_OBP0 = 0x48, // Object Palette 0 Data (R/W) - Non CGB Mode Only
GB_IO_OBP1 = 0x49, // Object Palette 1 Data (R/W) - Non CGB Mode Only
GB_IO_WY = 0x4a, // Window Y Position (R/W)
GB_IO_WX = 0x4b, // Window X Position minus 7 (R/W)
// Has some undocumented compatibility flags written at boot.
// Unfortunately it is not readable or writable after boot has finished, so research of this
// register is quite limited. The value written to this register, however, can be controlled
// in some cases.
GB_IO_DMG_EMULATION = 0x4c,
/* General CGB features */
GB_IO_KEY1 = 0x4d, // CGB Mode Only - Prepare Speed Switch
/* Missing */
GB_IO_VBK = 0x4f, // CGB Mode Only - VRAM Bank
GB_IO_BIOS = 0x50, // Write to disable the BIOS mapping
/* CGB DMA */
GB_IO_HDMA1 = 0x51, // CGB Mode Only - New DMA Source, High
GB_IO_HDMA2 = 0x52, // CGB Mode Only - New DMA Source, Low
GB_IO_HDMA3 = 0x53, // CGB Mode Only - New DMA Destination, High
GB_IO_HDMA4 = 0x54, // CGB Mode Only - New DMA Destination, Low
GB_IO_HDMA5 = 0x55, // CGB Mode Only - New DMA Length/Mode/Start
/* IR */
GB_IO_RP = 0x56, // CGB Mode Only - Infrared Communications Port
/* Missing */
/* CGB Paletts */
GB_IO_BGPI = 0x68, // CGB Mode Only - Background Palette Index
GB_IO_BGPD = 0x69, // CGB Mode Only - Background Palette Data
GB_IO_OBPI = 0x6a, // CGB Mode Only - Sprite Palette Index
GB_IO_OBPD = 0x6b, // CGB Mode Only - Sprite Palette Data
// 1 is written for DMG ROMs on a CGB. Does not appear to have an effect.
GB_IO_DMG_EMULATION_INDICATION = 0x6c, // (FEh) Bit 0 (Read/Write)
/* Missing */
GB_IO_SVBK = 0x70, // CGB Mode Only - WRAM Bank
GB_IO_UNKNOWN2 = 0x72, // (00h) - Bit 0-7 (Read/Write)
GB_IO_UNKNOWN3 = 0x73, // (00h) - Bit 0-7 (Read/Write)
GB_IO_UNKNOWN4 = 0x74, // (00h) - Bit 0-7 (Read/Write) - CGB Mode Only
GB_IO_UNKNOWN5 = 0x75, // (8Fh) - Bit 4-6 (Read/Write)
GB_IO_PCM_12 = 0x76, // Channels 1 and 2 amplitudes
GB_IO_PCM_34 = 0x77, // Channels 3 and 4 amplitudes
GB_IO_UNKNOWN8 = 0x7F, // Unknown, write only
};
typedef enum {
GB_LOG_BOLD = 1,
GB_LOG_DASHED_UNDERLINE = 2,
GB_LOG_UNDERLINE = 4,
GB_LOG_UNDERLINE_MASK = GB_LOG_DASHED_UNDERLINE | GB_LOG_UNDERLINE
} GB_log_attributes;
#ifdef GB_INTERNAL
#define LCDC_PERIOD 70224
#define CPU_FREQUENCY 0x400000
#define SGB_NTSC_FREQUENCY (21477272 / 5)
#define SGB_PAL_FREQUENCY (21281370 / 5)
#define DIV_CYCLES (0x100)
#define INTERNAL_DIV_CYCLES (0x40000)
#if !defined(MIN)
#define MIN(A,B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __a : __b; })
#endif
#if !defined(MAX)
#define MAX(A,B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __b : __a; })
#endif
#endif
typedef void (*GB_vblank_callback_t)(GB_gameboy_t *gb);
typedef void (*GB_log_callback_t)(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes);
typedef char *(*GB_input_callback_t)(GB_gameboy_t *gb);
typedef uint32_t (*GB_rgb_encode_callback_t)(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b);
typedef void (*GB_infrared_callback_t)(GB_gameboy_t *gb, bool on, long cycles_since_last_update);
typedef void (*GB_rumble_callback_t)(GB_gameboy_t *gb, bool rumble_on);
typedef void (*GB_serial_transfer_bit_start_callback_t)(GB_gameboy_t *gb, bool bit_to_send);
typedef bool (*GB_serial_transfer_bit_end_callback_t)(GB_gameboy_t *gb);
typedef void (*GB_update_input_hint_callback_t)(GB_gameboy_t *gb);
typedef void (*GB_joyp_write_callback_t)(GB_gameboy_t *gb, uint8_t value);
typedef void (*GB_icd_pixel_callback_t)(GB_gameboy_t *gb, uint8_t row);
typedef void (*GB_icd_hreset_callback_t)(GB_gameboy_t *gb);
typedef void (*GB_icd_vreset_callback_t)(GB_gameboy_t *gb);
typedef struct {
bool state;
long delay;
} GB_ir_queue_item_t;
struct GB_breakpoint_s;
struct GB_watchpoint_s;
typedef struct {
uint8_t pixel; // Color, 0-3
uint8_t palette; // Palette, 0 - 7 (CGB); 0-1 in DMG (or just 0 for BG)
uint8_t priority; // Sprite priority 0 in DMG, OAM index in CGB
bool bg_priority; // For sprite FIFO the BG priority bit. For the BG FIFO  the CGB attributes priority bit
} GB_fifo_item_t;
#define GB_FIFO_LENGTH 16
typedef struct {
GB_fifo_item_t fifo[GB_FIFO_LENGTH];
uint8_t read_end;
uint8_t write_end;
} GB_fifo_t;
/* When state saving, each section is dumped independently of other sections.
This allows adding data to the end of the section without worrying about future compatibility.
Some other changes might be "safe" as well.
This struct is not packed, but dumped sections exclusively use types that have the same alignment in both 32 and 64
bit platforms. */
#ifdef GB_INTERNAL
struct GB_gameboy_s {
#else
struct GB_gameboy_internal_s {
#endif
GB_SECTION(header,
/* The magic makes sure a state file is:
- Indeed a SameBoy state file.
- Has the same endianess has the current platform. */
volatile uint32_t magic;
/* The version field makes sure we don't load save state files with a completely different structure.
This happens when struct fields are removed/resized in an backward incompatible manner. */
uint32_t version;
);
GB_SECTION(core_state,
/* Registers */
uint16_t pc;
union {
uint16_t registers[GB_REGISTERS_16_BIT];
struct {
uint16_t af,
bc,
de,
hl,
sp;
};
struct {
#ifdef GB_BIG_ENDIAN
uint8_t a, f,
b, c,
d, e,
h, l;
#else
uint8_t f, a,
c, b,
e, d,
l, h;
#endif
};
};
uint8_t ime;
uint8_t interrupt_enable;
uint8_t cgb_ram_bank;
/* CPU and General Hardware Flags*/
GB_model_t model;
bool cgb_mode;
bool cgb_double_speed;
bool halted;
bool stopped;
bool boot_rom_finished;
bool ime_toggle; /* ei has delayed a effect.*/
bool halt_bug;
bool just_halted;
/* Misc state */
bool infrared_input;
GB_printer_t printer;
uint8_t extra_oam[0xff00 - 0xfea0];
uint32_t ram_size; // Different between CGB and DMG
);
/* DMA and HDMA */
GB_SECTION(dma,
bool hdma_on;
bool hdma_on_hblank;
uint8_t hdma_steps_left;
int16_t hdma_cycles; // in 8MHz units
uint16_t hdma_current_src, hdma_current_dest;
uint8_t dma_steps_left;
uint8_t dma_current_dest;
uint16_t dma_current_src;
int16_t dma_cycles;
bool is_dma_restarting;
uint8_t last_opcode_read; /* Required to emulte HDMA reads from Exxx */
bool hdma_starting;
);
/* MBC */
GB_SECTION(mbc,
uint16_t mbc_rom_bank;
uint8_t mbc_ram_bank;
uint32_t mbc_ram_size;
bool mbc_ram_enable;
union {
struct {
uint8_t bank_low:5;
uint8_t bank_high:2;
uint8_t mode:1;
} mbc1;
struct {
uint8_t rom_bank:4;
} mbc2;
struct {
uint8_t rom_bank:7;
uint8_t padding:1;
uint8_t ram_bank:4;
} mbc3;
struct {
uint8_t rom_bank_low;
uint8_t rom_bank_high:1;
uint8_t ram_bank:4;
} mbc5;
struct {
uint8_t bank_low:6;
uint8_t bank_high:3;
uint8_t mode:1;
} huc1;
struct {
uint8_t rom_bank;
uint8_t ram_bank;
} huc3;
};
uint16_t mbc_rom0_bank; /* For some MBC1 wirings. */
bool camera_registers_mapped;
uint8_t camera_registers[0x36];
bool rumble_state;
);
/* HRAM and HW Registers */
GB_SECTION(hram,
uint8_t hram[0xFFFF - 0xFF80];
uint8_t io_registers[0x80];
);
/* Timing */
GB_SECTION(timing,
GB_UNIT(display);
GB_UNIT(div);
uint16_t div_counter;
uint8_t tima_reload_state; /* After TIMA overflows, it becomes 0 for 4 cycles before actually reloading. */
uint16_t serial_cycles;
uint16_t serial_length;
uint8_t double_speed_alignment;
uint8_t serial_count;
);
/* APU */
GB_SECTION(apu,
GB_apu_t apu;
);
/* RTC */
GB_SECTION(rtc,
GB_rtc_time_t rtc_real, rtc_latched;
uint64_t last_rtc_second;
bool rtc_latch;
);
/* Video Display */
GB_SECTION(video,
uint32_t vram_size; // Different between CGB and DMG
uint8_t cgb_vram_bank;
uint8_t oam[0xA0];
uint8_t background_palettes_data[0x40];
uint8_t sprite_palettes_data[0x40];
uint8_t position_in_line;
bool stat_interrupt_line;
uint8_t effective_scx;
uint8_t wy_diff;
/* The LCDC will skip the first frame it renders after turning it on.
On the CGB, a frame is not skipped if the previous frame was skipped as well.
See https://www.reddit.com/r/EmuDev/comments/6exyxu/ */
/* TODO: Drop this and properly emulate the dropped vreset signal*/
enum {
GB_FRAMESKIP_LCD_TURNED_ON, // On a DMG, the LCD renders a blank screen during this state,
// on a CGB, the previous frame is repeated (which might be
// blank if the LCD was off for more than a few cycles)
GB_FRAMESKIP_FIRST_FRAME_SKIPPED, // This state is 'skipped' when emulating a DMG
GB_FRAMESKIP_SECOND_FRAME_RENDERED,
} frame_skip_state;
bool oam_read_blocked;
bool vram_read_blocked;
bool oam_write_blocked;
bool vram_write_blocked;
bool window_disabled_while_active;
uint8_t current_line;
uint16_t ly_for_comparison;
GB_fifo_t bg_fifo, oam_fifo;
uint8_t fetcher_x;
uint8_t fetcher_y;
uint16_t cycles_for_line;
uint8_t current_tile;
uint8_t current_tile_attributes;
uint8_t current_tile_data[2];
uint8_t fetcher_state;
bool bg_fifo_paused;
bool oam_fifo_paused;
bool in_window;
uint8_t visible_objs[10];
uint8_t obj_comparators[10];
uint8_t n_visible_objs;
uint8_t oam_search_index;
uint8_t accessed_oam_row;
uint8_t extra_penalty_for_sprite_at_0;
uint8_t mode_for_interrupt;
bool lyc_interrupt_line;
bool cgb_palettes_blocked;
uint8_t current_lcd_line; // The LCD can go out of sync since the vsync signal is skipped in some cases.
uint32_t cycles_in_stop_mode;
);
/* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */
/* This data is reserved on reset and must come last in the struct */
GB_SECTION(unsaved,
/* ROM */
uint8_t *rom;
uint32_t rom_size;
const GB_cartridge_t *cartridge_type;
enum {
GB_STANDARD_MBC1_WIRING,
GB_MBC1M_WIRING,
} mbc1_wiring;
unsigned pending_cycles;
/* Various RAMs */
uint8_t *ram;
uint8_t *vram;
uint8_t *mbc_ram;
/* I/O */
uint32_t *screen;
uint32_t background_palettes_rgb[0x20];
uint32_t sprite_palettes_rgb[0x20];
GB_color_correction_mode_t color_correction_mode;
bool keys[4][GB_KEY_MAX];
/* Timing */
uint64_t last_sync;
uint64_t cycles_since_last_sync; // In 8MHz units
/* Audio */
GB_apu_output_t apu_output;
/* Callbacks */
void *user_data;
GB_log_callback_t log_callback;
GB_input_callback_t input_callback;
GB_input_callback_t async_input_callback;
GB_rgb_encode_callback_t rgb_encode_callback;
GB_vblank_callback_t vblank_callback;
GB_infrared_callback_t infrared_callback;
GB_camera_get_pixel_callback_t camera_get_pixel_callback;
GB_camera_update_request_callback_t camera_update_request_callback;
GB_rumble_callback_t rumble_callback;
GB_serial_transfer_bit_start_callback_t serial_transfer_bit_start_callback;
GB_serial_transfer_bit_end_callback_t serial_transfer_bit_end_callback;
GB_update_input_hint_callback_t update_input_hint_callback;
GB_joyp_write_callback_t joyp_write_callback;
GB_icd_pixel_callback_t icd_pixel_callback;
GB_icd_vreset_callback_t icd_hreset_callback;
GB_icd_vreset_callback_t icd_vreset_callback;
GB_read_memory_callback_t read_memory_callback;
/* IR */
long cycles_since_ir_change; // In 8MHz units
long cycles_since_input_ir_change; // In 8MHz units
GB_ir_queue_item_t ir_queue[GB_MAX_IR_QUEUE];
size_t ir_queue_length;
/*** Debugger ***/
volatile bool debug_stopped, debug_disable;
bool debug_fin_command, debug_next_command;
/* Breakpoints */
uint16_t n_breakpoints;
struct GB_breakpoint_s *breakpoints;
bool has_jump_to_breakpoints;
void *nontrivial_jump_state;
bool non_trivial_jump_breakpoint_occured;
/* SLD (Todo: merge with backtrace) */
bool stack_leak_detection;
int debug_call_depth;
uint16_t sp_for_call_depth[0x200]; /* Should be much more than enough */
uint16_t addr_for_call_depth[0x200];
/* Backtrace */
unsigned backtrace_size;
uint16_t backtrace_sps[0x200];
struct {
uint16_t bank;
uint16_t addr;
} backtrace_returns[0x200];
/* Watchpoints */
uint16_t n_watchpoints;
struct GB_watchpoint_s *watchpoints;
/* Symbol tables */
GB_symbol_map_t *bank_symbols[0x200];
GB_reversed_symbol_map_t reversed_symbol_map;
/* Ticks command */
unsigned long debugger_ticks;
/* Rewind */
#define GB_REWIND_FRAMES_PER_KEY 255
size_t rewind_buffer_length;
struct {
uint8_t *key_state;
uint8_t *compressed_states[GB_REWIND_FRAMES_PER_KEY];
unsigned pos;
} *rewind_sequences; // lasts about 4 seconds
size_t rewind_pos;
/* SGB - saved and allocated optionally */
GB_sgb_t *sgb;
double sgb_intro_jingle_phases[7];
double sgb_intro_sweep_phase;
double sgb_intro_sweep_previous_sample;
/* Misc */
bool turbo;
bool turbo_dont_skip;
bool disable_rendering;
uint8_t boot_rom[0x900];
bool vblank_just_occured; // For slow operations involving syscalls; these should only run once per vblank
uint8_t cycles_since_run; // How many cycles have passed since the last call to GB_run(), in 8MHz units
double clock_multiplier;
);
};
#ifndef GB_INTERNAL
struct GB_gameboy_s {
char __internal[sizeof(struct GB_gameboy_internal_s)];
};
#endif
#ifndef __printflike
/* Missing from Linux headers. */
#define __printflike(fmtarg, firstvararg) \
__attribute__((__format__ (__printf__, fmtarg, firstvararg)))
#endif
void GB_init(GB_gameboy_t *gb, GB_model_t model);
bool GB_is_inited(GB_gameboy_t *gb);
bool GB_is_cgb(GB_gameboy_t *gb);
bool GB_is_sgb(GB_gameboy_t *gb); // Returns true if the model is SGB or SGB2
bool GB_is_hle_sgb(GB_gameboy_t *gb); // Returns true if the model is SGB or SGB2 and the SFC/SNES side is HLE'd
GB_model_t GB_get_model(GB_gameboy_t *gb);
void GB_free(GB_gameboy_t *gb);
void GB_reset(GB_gameboy_t *gb);
void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model);
/* Returns the time passed, in 8MHz ticks. */
uint8_t GB_run(GB_gameboy_t *gb);
/* Returns the time passed since the last frame, in nanoseconds */
uint64_t GB_run_frame(GB_gameboy_t *gb);
typedef enum {
GB_DIRECT_ACCESS_ROM,
GB_DIRECT_ACCESS_RAM,
GB_DIRECT_ACCESS_CART_RAM,
GB_DIRECT_ACCESS_VRAM,
GB_DIRECT_ACCESS_HRAM,
GB_DIRECT_ACCESS_IO, /* Warning: Some registers can only be read/written correctly via GB_memory_read/write. */
GB_DIRECT_ACCESS_BOOTROM,
GB_DIRECT_ACCESS_OAM,
GB_DIRECT_ACCESS_BGP,
GB_DIRECT_ACCESS_OBP,
GB_DIRECT_ACCESS_IE,
} GB_direct_access_t;
/* Returns a mutable pointer to various hardware memories. If that memory is banked, the current bank
is returned at *bank, even if only a portion of the memory is banked. */
void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t *size, uint16_t *bank);
void *GB_get_user_data(GB_gameboy_t *gb);
void GB_set_user_data(GB_gameboy_t *gb, void *data);
int GB_load_boot_rom(GB_gameboy_t *gb, const char *path);
void GB_load_boot_rom_from_buffer(GB_gameboy_t *gb, const unsigned char *buffer, size_t size);
int GB_load_rom(GB_gameboy_t *gb, const char *path);
void GB_load_rom_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size);
int GB_save_battery_size(GB_gameboy_t *gb);
int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size);
int GB_save_battery(GB_gameboy_t *gb, const char *path);
void GB_load_battery_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size);
void GB_load_battery(GB_gameboy_t *gb, const char *path);
void GB_set_turbo_mode(GB_gameboy_t *gb, bool on, bool no_frame_skip);
void GB_set_rendering_disabled(GB_gameboy_t *gb, bool disabled);
void GB_log(GB_gameboy_t *gb, const char *fmt, ...) __printflike(2, 3);
void GB_attributed_log(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, ...) __printflike(3, 4);
void GB_set_pixels_output(GB_gameboy_t *gb, uint32_t *output);
void GB_set_infrared_input(GB_gameboy_t *gb, bool state);
void GB_queue_infrared_input(GB_gameboy_t *gb, bool state, long cycles_after_previous_change); /* In 8MHz units*/
void GB_set_vblank_callback(GB_gameboy_t *gb, GB_vblank_callback_t callback);
void GB_set_log_callback(GB_gameboy_t *gb, GB_log_callback_t callback);
void GB_set_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback);
void GB_set_async_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback);
void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callback);
void GB_set_infrared_callback(GB_gameboy_t *gb, GB_infrared_callback_t callback);
void GB_set_rumble_callback(GB_gameboy_t *gb, GB_rumble_callback_t callback);
void GB_set_update_input_hint_callback(GB_gameboy_t *gb, GB_update_input_hint_callback_t callback);
/* These APIs are used when using internal clock */
void GB_set_serial_transfer_bit_start_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_start_callback_t callback);
void GB_set_serial_transfer_bit_end_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_end_callback_t callback);
/* These APIs are used when using external clock */
bool GB_serial_get_data_bit(GB_gameboy_t *gb);
void GB_serial_set_data_bit(GB_gameboy_t *gb, bool data);
void GB_disconnect_serial(GB_gameboy_t *gb);
/* For integration with SFC/SNES emulators */
void GB_set_joyp_write_callback(GB_gameboy_t *gb, GB_joyp_write_callback_t callback);
void GB_set_icd_pixel_callback(GB_gameboy_t *gb, GB_icd_pixel_callback_t callback);
void GB_set_icd_hreset_callback(GB_gameboy_t *gb, GB_icd_hreset_callback_t callback);
void GB_set_icd_vreset_callback(GB_gameboy_t *gb, GB_icd_vreset_callback_t callback);
#ifdef GB_INTERNAL
uint32_t GB_get_clock_rate(GB_gameboy_t *gb);
#endif
void GB_set_clock_multiplier(GB_gameboy_t *gb, double multiplier);
unsigned GB_get_screen_width(GB_gameboy_t *gb);
unsigned GB_get_screen_height(GB_gameboy_t *gb);
double GB_get_usual_frame_rate(GB_gameboy_t *gb);
unsigned GB_get_player_count(GB_gameboy_t *gb);
#endif /* GB_h */

View File

@@ -0,0 +1,5 @@
#ifndef gb_struct_def_h
#define gb_struct_def_h
struct GB_gameboy_s;
typedef struct GB_gameboy_s GB_gameboy_t;
#endif

92
bsnes/gb/Core/joypad.c Normal file
View File

@@ -0,0 +1,92 @@
#include "gb.h"
#include <assert.h>
void GB_update_joyp(GB_gameboy_t *gb)
{
if (gb->model & GB_MODEL_NO_SFC_BIT) return;
uint8_t key_selection = 0;
uint8_t previous_state = 0;
/* Todo: add delay to key selection */
previous_state = gb->io_registers[GB_IO_JOYP] & 0xF;
key_selection = (gb->io_registers[GB_IO_JOYP] >> 4) & 3;
gb->io_registers[GB_IO_JOYP] &= 0xF0;
uint8_t current_player = gb->sgb? (gb->sgb->current_player & (gb->sgb->player_count - 1) & 3) : 0;
switch (key_selection) {
case 3:
if (gb->sgb && gb->sgb->player_count > 1) {
gb->io_registers[GB_IO_JOYP] |= 0xF - current_player;
}
else {
/* Nothing is wired, all up */
gb->io_registers[GB_IO_JOYP] |= 0x0F;
}
break;
case 2:
/* Direction keys */
for (uint8_t i = 0; i < 4; i++) {
gb->io_registers[GB_IO_JOYP] |= (!gb->keys[current_player][i]) << i;
}
/* Forbid pressing two opposing keys, this breaks a lot of games; even if it's somewhat possible. */
if (!(gb->io_registers[GB_IO_JOYP] & 1)) {
gb->io_registers[GB_IO_JOYP] |= 2;
}
if (!(gb->io_registers[GB_IO_JOYP] & 4)) {
gb->io_registers[GB_IO_JOYP] |= 8;
}
break;
case 1:
/* Other keys */
for (uint8_t i = 0; i < 4; i++) {
gb->io_registers[GB_IO_JOYP] |= (!gb->keys[current_player][i + 4]) << i;
}
break;
case 0:
for (uint8_t i = 0; i < 4; i++) {
gb->io_registers[GB_IO_JOYP] |= (!(gb->keys[current_player][i] || gb->keys[current_player][i + 4])) << i;
}
break;
default:
break;
}
/* Todo: This assumes the keys *always* bounce, which is incorrect when emulating an SGB */
if (previous_state != (gb->io_registers[GB_IO_JOYP] & 0xF)) {
/* The joypad interrupt DOES occur on CGB (Tested on CGB-E), unlike what some documents say. */
gb->io_registers[GB_IO_IF] |= 0x10;
}
gb->io_registers[GB_IO_JOYP] |= 0xC0;
}
void GB_icd_set_joyp(GB_gameboy_t *gb, uint8_t value)
{
uint8_t previous_state = gb->io_registers[GB_IO_JOYP] & 0xF;
gb->io_registers[GB_IO_JOYP] &= 0xF0;
gb->io_registers[GB_IO_JOYP] |= value & 0xF;
if (previous_state & ~(gb->io_registers[GB_IO_JOYP] & 0xF)) {
gb->io_registers[GB_IO_IF] |= 0x10;
}
gb->io_registers[GB_IO_JOYP] |= 0xC0;
}
void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed)
{
assert(index >= 0 && index < GB_KEY_MAX);
gb->keys[0][index] = pressed;
GB_update_joyp(gb);
}
void GB_set_key_state_for_player(GB_gameboy_t *gb, GB_key_t index, unsigned player, bool pressed)
{
assert(index >= 0 && index < GB_KEY_MAX);
assert(player < 4);
gb->keys[player][index] = pressed;
GB_update_joyp(gb);
}

25
bsnes/gb/Core/joypad.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef joypad_h
#define joypad_h
#include "gb_struct_def.h"
#include <stdbool.h>
typedef enum {
GB_KEY_RIGHT,
GB_KEY_LEFT,
GB_KEY_UP,
GB_KEY_DOWN,
GB_KEY_A,
GB_KEY_B,
GB_KEY_SELECT,
GB_KEY_START,
GB_KEY_MAX
} GB_key_t;
void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed);
void GB_set_key_state_for_player(GB_gameboy_t *gb, GB_key_t index, unsigned player, bool pressed);
void GB_icd_set_joyp(GB_gameboy_t *gb, uint8_t value);
#ifdef GB_INTERNAL
void GB_update_joyp(GB_gameboy_t *gb);
#endif
#endif /* joypad_h */

154
bsnes/gb/Core/mbc.c Normal file
View File

@@ -0,0 +1,154 @@
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "gb.h"
const GB_cartridge_t GB_cart_defs[256] = {
// From http://gbdev.gg8.se/wiki/articles/The_Cartridge_Header#0147_-_Cartridge_Type
/* MBC SUBTYPE RAM BAT. RTC RUMB. */
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 00h ROM ONLY
{ GB_MBC1 , GB_STANDARD_MBC, false, false, false, false}, // 01h MBC1
{ GB_MBC1 , GB_STANDARD_MBC, true , false, false, false}, // 02h MBC1+RAM
{ GB_MBC1 , GB_STANDARD_MBC, true , true , false, false}, // 03h MBC1+RAM+BATTERY
[5] =
{ GB_MBC2 , GB_STANDARD_MBC, true , false, false, false}, // 05h MBC2
{ GB_MBC2 , GB_STANDARD_MBC, true , true , false, false}, // 06h MBC2+BATTERY
[8] =
{ GB_NO_MBC, GB_STANDARD_MBC, true , false, false, false}, // 08h ROM+RAM
{ GB_NO_MBC, GB_STANDARD_MBC, true , true , false, false}, // 09h ROM+RAM+BATTERY
[0xB] =
/* Todo: Not supported yet */
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Bh MMM01
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Ch MMM01+RAM
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Dh MMM01+RAM+BATTERY
[0xF] =
{ GB_MBC3 , GB_STANDARD_MBC, false, true, true , false}, // 0Fh MBC3+TIMER+BATTERY
{ GB_MBC3 , GB_STANDARD_MBC, true , true, true , false}, // 10h MBC3+TIMER+RAM+BATTERY
{ GB_MBC3 , GB_STANDARD_MBC, false, false, false, false}, // 11h MBC3
{ GB_MBC3 , GB_STANDARD_MBC, true , false, false, false}, // 12h MBC3+RAM
{ GB_MBC3 , GB_STANDARD_MBC, true , true , false, false}, // 13h MBC3+RAM+BATTERY
[0x19] =
{ GB_MBC5 , GB_STANDARD_MBC, false, false, false, false}, // 19h MBC5
{ GB_MBC5 , GB_STANDARD_MBC, true , false, false, false}, // 1Ah MBC5+RAM
{ GB_MBC5 , GB_STANDARD_MBC, true , true , false, false}, // 1Bh MBC5+RAM+BATTERY
{ GB_MBC5 , GB_STANDARD_MBC, false, false, false, true }, // 1Ch MBC5+RUMBLE
{ GB_MBC5 , GB_STANDARD_MBC, true , false, false, true }, // 1Dh MBC5+RUMBLE+RAM
{ GB_MBC5 , GB_STANDARD_MBC, true , true , false, true }, // 1Eh MBC5+RUMBLE+RAM+BATTERY
[0xFC] =
{ GB_MBC5 , GB_CAMERA , true , true , false, false}, // FCh POCKET CAMERA
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // FDh BANDAI TAMA5 (Todo: Not supported)
{ GB_HUC3 , GB_STANDARD_MBC, true , true , false, false}, // FEh HuC3 (Todo: Mapper support only)
{ GB_HUC1 , GB_STANDARD_MBC, true , true , false, false}, // FFh HuC1+RAM+BATTERY (Todo: No IR bindings)
};
void GB_update_mbc_mappings(GB_gameboy_t *gb)
{
switch (gb->cartridge_type->mbc_type) {
case GB_NO_MBC: return;
case GB_MBC1:
switch (gb->mbc1_wiring) {
case GB_STANDARD_MBC1_WIRING:
gb->mbc_rom_bank = gb->mbc1.bank_low | (gb->mbc1.bank_high << 5);
if (gb->mbc1.mode == 0) {
gb->mbc_ram_bank = 0;
gb->mbc_rom0_bank = 0;
}
else {
gb->mbc_ram_bank = gb->mbc1.bank_high;
gb->mbc_rom0_bank = gb->mbc1.bank_high << 5;
}
if ((gb->mbc_rom_bank & 0x1F) == 0) {
gb->mbc_rom_bank++;
}
break;
case GB_MBC1M_WIRING:
gb->mbc_rom_bank = (gb->mbc1.bank_low & 0xF) | (gb->mbc1.bank_high << 4);
if (gb->mbc1.mode == 0) {
gb->mbc_ram_bank = 0;
gb->mbc_rom0_bank = 0;
}
else {
gb->mbc_rom0_bank = gb->mbc1.bank_high << 4;
gb->mbc_ram_bank = 0;
}
if ((gb->mbc1.bank_low & 0x1F) == 0) {
gb->mbc_rom_bank++;
}
break;
}
break;
case GB_MBC2:
gb->mbc_rom_bank = gb->mbc2.rom_bank;
if ((gb->mbc_rom_bank & 0xF) == 0) {
gb->mbc_rom_bank = 1;
}
break;
case GB_MBC3:
gb->mbc_rom_bank = gb->mbc3.rom_bank;
gb->mbc_ram_bank = gb->mbc3.ram_bank;
if (gb->mbc_rom_bank == 0) {
gb->mbc_rom_bank = 1;
}
break;
case GB_MBC5:
gb->mbc_rom_bank = gb->mbc5.rom_bank_low | (gb->mbc5.rom_bank_high << 8);
gb->mbc_ram_bank = gb->mbc5.ram_bank;
break;
case GB_HUC1:
if (gb->huc1.mode == 0) {
gb->mbc_rom_bank = gb->huc1.bank_low | (gb->mbc1.bank_high << 6);
gb->mbc_ram_bank = 0;
}
else {
gb->mbc_rom_bank = gb->huc1.bank_low;
gb->mbc_ram_bank = gb->huc1.bank_high;
}
break;
case GB_HUC3:
gb->mbc_rom_bank = gb->huc3.rom_bank;
gb->mbc_ram_bank = gb->huc3.ram_bank;
break;
}
}
void GB_configure_cart(GB_gameboy_t *gb)
{
gb->cartridge_type = &GB_cart_defs[gb->rom[0x147]];
if (gb->rom[0x147] == 0 && gb->rom_size > 0x8000) {
GB_log(gb, "ROM header reports no MBC, but file size is over 32Kb. Assuming cartridge uses MBC3.\n");
gb->cartridge_type = &GB_cart_defs[0x11];
}
else if (gb->rom[0x147] != 0 && memcmp(gb->cartridge_type, &GB_cart_defs[0], sizeof(GB_cart_defs[0])) == 0) {
GB_log(gb, "Cartridge type %02x is not yet supported.\n", gb->rom[0x147]);
}
if (gb->cartridge_type->has_ram) {
if (gb->cartridge_type->mbc_type == GB_MBC2) {
gb->mbc_ram_size = 0x200;
}
else {
static const int ram_sizes[256] = {0, 0x800, 0x2000, 0x8000, 0x20000, 0x10000};
gb->mbc_ram_size = ram_sizes[gb->rom[0x149]];
}
gb->mbc_ram = malloc(gb->mbc_ram_size);
/* Todo: Some games assume unintialized MBC RAM is 0xFF. It this true for all cartridges types? */
memset(gb->mbc_ram, 0xFF, gb->mbc_ram_size);
}
/* MBC1 has at least 3 types of wiring (We currently support two (Standard and 4bit-MBC1M) of these).
See http://forums.nesdev.com/viewtopic.php?f=20&t=14099 */
/* Attempt to "guess" wiring */
if (gb->cartridge_type->mbc_type == GB_MBC1) {
if (gb->rom_size >= 0x44000 && memcmp(gb->rom + 0x104, gb->rom + 0x40104, 0x30) == 0) {
gb->mbc1_wiring = GB_MBC1M_WIRING;
}
}
/* Set MBC5's bank to 1 correctly */
if (gb->cartridge_type->mbc_type == GB_MBC5) {
gb->mbc5.rom_bank_low = 1;
}
}

32
bsnes/gb/Core/mbc.h Normal file
View File

@@ -0,0 +1,32 @@
#ifndef MBC_h
#define MBC_h
#include "gb_struct_def.h"
#include <stdbool.h>
typedef struct {
enum {
GB_NO_MBC,
GB_MBC1,
GB_MBC2,
GB_MBC3,
GB_MBC5,
GB_HUC1, /* Todo: HUC1 features are not emulated. Should be unified with the CGB IR sensor API. */
GB_HUC3,
} mbc_type;
enum {
GB_STANDARD_MBC,
GB_CAMERA,
} mbc_subtype;
bool has_ram;
bool has_battery;
bool has_rtc;
bool has_rumble;
} GB_cartridge_t;
#ifdef GB_INTERNAL
extern const GB_cartridge_t GB_cart_defs[256];
void GB_update_mbc_mappings(GB_gameboy_t *gb);
void GB_configure_cart(GB_gameboy_t *gb);
#endif
#endif /* MBC_h */

1015
bsnes/gb/Core/memory.c Normal file

File diff suppressed because it is too large Load Diff

18
bsnes/gb/Core/memory.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef memory_h
#define memory_h
#include "gb_struct_def.h"
#include <stdint.h>
typedef uint8_t (*GB_read_memory_callback_t)(GB_gameboy_t *gb, uint16_t addr, uint8_t data);
void GB_set_read_memory_callback(GB_gameboy_t *gb, GB_read_memory_callback_t callback);
uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr);
void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
#ifdef GB_INTERNAL
void GB_dma_run(GB_gameboy_t *gb);
void GB_hdma_run(GB_gameboy_t *gb);
void GB_trigger_oam_bug(GB_gameboy_t *gb, uint16_t address);
void GB_trigger_oam_bug_read_increase(GB_gameboy_t *gb, uint16_t address);
#endif
#endif /* memory_h */

216
bsnes/gb/Core/printer.c Normal file
View File

@@ -0,0 +1,216 @@
#include "gb.h"
/* TODO: Emulation is VERY basic and assumes the ROM correctly uses the printer's interface.
Incorrect usage is not correctly emulated, as it's not well documented, nor do I
have my own GB Printer to figure it out myself.
It also does not currently emulate communication timeout, which means that a bug
might prevent the printer operation until the GameBoy is restarted.
Also, field mask values are assumed. */
static void handle_command(GB_gameboy_t *gb)
{
switch (gb->printer.command_id) {
case GB_PRINTER_INIT_COMMAND:
gb->printer.status = 0;
gb->printer.image_offset = 0;
break;
case GB_PRINTER_START_COMMAND:
if (gb->printer.command_length == 4) {
gb->printer.status = 6; /* Printing */
uint32_t image[gb->printer.image_offset];
uint8_t palette = gb->printer.command_data[2];
uint32_t colors[4] = {gb->rgb_encode_callback(gb, 0xff, 0xff, 0xff),
gb->rgb_encode_callback(gb, 0xaa, 0xaa, 0xaa),
gb->rgb_encode_callback(gb, 0x55, 0x55, 0x55),
gb->rgb_encode_callback(gb, 0x00, 0x00, 0x00)};
for (unsigned i = 0; i < gb->printer.image_offset; i++) {
image[i] = colors[(palette >> (gb->printer.image[i] * 2)) & 3];
}
if (gb->printer.callback) {
gb->printer.callback(gb, image, gb->printer.image_offset / 160,
gb->printer.command_data[1] >> 4, gb->printer.command_data[1] & 7,
gb->printer.command_data[3] & 0x7F);
}
gb->printer.image_offset = 0;
}
break;
case GB_PRINTER_DATA_COMMAND:
if (gb->printer.command_length == GB_PRINTER_DATA_SIZE) {
gb->printer.image_offset %= sizeof(gb->printer.image);
gb->printer.status = 8; /* Received 0x280 bytes */
uint8_t *byte = gb->printer.command_data;
for (unsigned row = 2; row--; ) {
for (unsigned tile_x = 0; tile_x < 160 / 8; tile_x++) {
for (unsigned y = 0; y < 8; y++, byte += 2) {
for (unsigned x_pixel = 0; x_pixel < 8; x_pixel++) {
gb->printer.image[gb->printer.image_offset + tile_x * 8 + x_pixel + y * 160] =
((*byte) >> 7) | (((*(byte + 1)) >> 7) << 1);
(*byte) <<= 1;
(*(byte + 1)) <<= 1;
}
}
}
gb->printer.image_offset += 8 * 160;
}
}
case GB_PRINTER_NOP_COMMAND:
default:
break;
}
}
static void byte_reieve_completed(GB_gameboy_t *gb, uint8_t byte_received)
{
gb->printer.byte_to_send = 0;
switch (gb->printer.command_state) {
case GB_PRINTER_COMMAND_MAGIC1:
if (byte_received != 0x88) {
return;
}
gb->printer.status &= ~1;
gb->printer.command_length = 0;
gb->printer.checksum = 0;
break;
case GB_PRINTER_COMMAND_MAGIC2:
if (byte_received != 0x33) {
if (byte_received != 0x88) {
gb->printer.command_state = GB_PRINTER_COMMAND_MAGIC1;
}
return;
}
break;
case GB_PRINTER_COMMAND_ID:
gb->printer.command_id = byte_received & 0xF;
break;
case GB_PRINTER_COMMAND_COMPRESSION:
gb->printer.compression = byte_received & 1;
break;
case GB_PRINTER_COMMAND_LENGTH_LOW:
gb->printer.length_left = byte_received;
break;
case GB_PRINTER_COMMAND_LENGTH_HIGH:
gb->printer.length_left |= (byte_received & 3) << 8;
break;
case GB_PRINTER_COMMAND_DATA:
if (gb->printer.command_length != GB_PRINTER_MAX_COMMAND_LENGTH) {
if (gb->printer.compression) {
if (!gb->printer.compression_run_lenth) {
gb->printer.compression_run_is_compressed = byte_received & 0x80;
gb->printer.compression_run_lenth = (byte_received & 0x7F) + 1 + gb->printer.compression_run_is_compressed;
}
else if (gb->printer.compression_run_is_compressed) {
while (gb->printer.compression_run_lenth) {
gb->printer.command_data[gb->printer.command_length++] = byte_received;
gb->printer.compression_run_lenth--;
if (gb->printer.command_length == GB_PRINTER_MAX_COMMAND_LENGTH) {
gb->printer.compression_run_lenth = 0;
}
}
}
else {
gb->printer.command_data[gb->printer.command_length++] = byte_received;
gb->printer.compression_run_lenth--;
}
}
else {
gb->printer.command_data[gb->printer.command_length++] = byte_received;
}
}
gb->printer.length_left--;
break;
case GB_PRINTER_COMMAND_CHECKSUM_LOW:
gb->printer.checksum ^= byte_received;
break;
case GB_PRINTER_COMMAND_CHECKSUM_HIGH:
gb->printer.checksum ^= byte_received << 8;
if (gb->printer.checksum) {
gb->printer.status |= 1; /* Checksum error*/
gb->printer.command_state = GB_PRINTER_COMMAND_MAGIC1;
return;
}
gb->printer.byte_to_send = 0x81;
break;
case GB_PRINTER_COMMAND_ACTIVE:
if ((gb->printer.command_id & 0xF) == GB_PRINTER_INIT_COMMAND) {
/* Games expect INIT commands to return 0? */
gb->printer.byte_to_send = 0;
}
else {
gb->printer.byte_to_send = gb->printer.status;
}
break;
case GB_PRINTER_COMMAND_STATUS:
/* Printing is done instantly, but let the game recieve a 6 (Printing) status at least once, for compatibility */
if (gb->printer.status == 6) {
gb->printer.status = 4; /* Done */
}
gb->printer.command_state = GB_PRINTER_COMMAND_MAGIC1;
handle_command(gb);
return;
}
if (gb->printer.command_state >= GB_PRINTER_COMMAND_ID && gb->printer.command_state < GB_PRINTER_COMMAND_CHECKSUM_LOW) {
gb->printer.checksum += byte_received;
}
if (gb->printer.command_state != GB_PRINTER_COMMAND_DATA) {
gb->printer.command_state++;
}
if (gb->printer.command_state == GB_PRINTER_COMMAND_DATA) {
if (gb->printer.length_left == 0) {
gb->printer.command_state++;
}
}
}
static void serial_start(GB_gameboy_t *gb, bool bit_received)
{
gb->printer.byte_being_recieved <<= 1;
gb->printer.byte_being_recieved |= bit_received;
gb->printer.bits_recieved++;
if (gb->printer.bits_recieved == 8) {
byte_reieve_completed(gb, gb->printer.byte_being_recieved);
gb->printer.bits_recieved = 0;
gb->printer.byte_being_recieved = 0;
}
}
static bool serial_end(GB_gameboy_t *gb)
{
bool ret = gb->printer.bit_to_send;
gb->printer.bit_to_send = gb->printer.byte_to_send & 0x80;
gb->printer.byte_to_send <<= 1;
return ret;
}
void GB_connect_printer(GB_gameboy_t *gb, GB_print_image_callback_t callback)
{
memset(&gb->printer, 0, sizeof(gb->printer));
GB_set_serial_transfer_bit_start_callback(gb, serial_start);
GB_set_serial_transfer_bit_end_callback(gb, serial_end);
gb->printer.callback = callback;
}

63
bsnes/gb/Core/printer.h Normal file
View File

@@ -0,0 +1,63 @@
#ifndef printer_h
#define printer_h
#include <stdint.h>
#include <stdbool.h>
#include "gb_struct_def.h"
#define GB_PRINTER_MAX_COMMAND_LENGTH 0x280
#define GB_PRINTER_DATA_SIZE 0x280
typedef void (*GB_print_image_callback_t)(GB_gameboy_t *gb,
uint32_t *image,
uint8_t height,
uint8_t top_margin,
uint8_t bottom_margin,
uint8_t exposure);
typedef struct
{
/* Communication state machine */
enum {
GB_PRINTER_COMMAND_MAGIC1,
GB_PRINTER_COMMAND_MAGIC2,
GB_PRINTER_COMMAND_ID,
GB_PRINTER_COMMAND_COMPRESSION,
GB_PRINTER_COMMAND_LENGTH_LOW,
GB_PRINTER_COMMAND_LENGTH_HIGH,
GB_PRINTER_COMMAND_DATA,
GB_PRINTER_COMMAND_CHECKSUM_LOW,
GB_PRINTER_COMMAND_CHECKSUM_HIGH,
GB_PRINTER_COMMAND_ACTIVE,
GB_PRINTER_COMMAND_STATUS,
} command_state : 8;
enum {
GB_PRINTER_INIT_COMMAND = 1,
GB_PRINTER_START_COMMAND = 2,
GB_PRINTER_DATA_COMMAND = 4,
GB_PRINTER_NOP_COMMAND = 0xF,
} command_id : 8;
bool compression;
uint16_t length_left;
uint8_t command_data[GB_PRINTER_MAX_COMMAND_LENGTH];
uint16_t command_length;
uint16_t checksum;
uint8_t status;
uint8_t byte_to_send;
uint8_t image[160 * 200];
uint16_t image_offset;
GB_print_image_callback_t callback;
uint8_t compression_run_lenth;
bool compression_run_is_compressed;
uint8_t bits_recieved;
uint8_t byte_being_recieved;
bool bit_to_send;
} GB_printer_t;
void GB_connect_printer(GB_gameboy_t *gb, GB_print_image_callback_t callback);
#endif

38
bsnes/gb/Core/random.c Normal file
View File

@@ -0,0 +1,38 @@
#include "random.h"
#include <time.h>
static uint64_t seed;
static bool enabled = true;
uint8_t GB_random(void)
{
if (!enabled) return 0;
seed *= 0x27BB2EE687B0B0FDL;
seed += 0xB504F32D;
return seed >> 56;
}
uint32_t GB_random32(void)
{
GB_random();
return seed >> 32;
}
void GB_random_seed(uint64_t new_seed)
{
seed = new_seed;
}
void GB_random_set_enabled(bool enable)
{
enabled = enable;
}
static void __attribute__((constructor)) init_seed(void)
{
seed = time(NULL);
for (unsigned i = 64; i--;) {
GB_random();
}
}

12
bsnes/gb/Core/random.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef random_h
#define random_h
#include <stdint.h>
#include <stdbool.h>
uint8_t GB_random(void);
uint32_t GB_random32(void);
void GB_random_seed(uint64_t seed);
void GB_random_set_enabled(bool enable);
#endif /* random_h */

208
bsnes/gb/Core/rewind.c Normal file
View File

@@ -0,0 +1,208 @@
#include "gb.h"
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <math.h>
static uint8_t *state_compress(const uint8_t *prev, const uint8_t *data, size_t uncompressed_size)
{
size_t malloc_size = 0x1000;
uint8_t *compressed = malloc(malloc_size);
size_t counter_pos = 0;
size_t data_pos = sizeof(uint16_t);
bool prev_mode = true;
*(uint16_t *)compressed = 0;
#define COUNTER (*(uint16_t *)&compressed[counter_pos])
#define DATA (compressed[data_pos])
while (uncompressed_size) {
if (prev_mode) {
if (*data == *prev && COUNTER != 0xffff) {
COUNTER++;
data++;
prev++;
uncompressed_size--;
}
else {
prev_mode = false;
counter_pos += sizeof(uint16_t);
data_pos = counter_pos + sizeof(uint16_t);
if (data_pos >= malloc_size) {
malloc_size *= 2;
compressed = realloc(compressed, malloc_size);
}
COUNTER = 0;
}
}
else {
if (*data != *prev && COUNTER != 0xffff) {
COUNTER++;
DATA = *data;
data_pos++;
data++;
prev++;
uncompressed_size--;
if (data_pos >= malloc_size) {
malloc_size *= 2;
compressed = realloc(compressed, malloc_size);
}
}
else {
prev_mode = true;
counter_pos = data_pos;
data_pos = counter_pos + sizeof(uint16_t);
if (counter_pos >= malloc_size - 1) {
malloc_size *= 2;
compressed = realloc(compressed, malloc_size);
}
COUNTER = 0;
}
}
}
return realloc(compressed, data_pos);
#undef DATA
#undef COUNTER
}
static void state_decompress(const uint8_t *prev, uint8_t *data, uint8_t *dest, size_t uncompressed_size)
{
size_t counter_pos = 0;
size_t data_pos = sizeof(uint16_t);
bool prev_mode = true;
#define COUNTER (*(uint16_t *)&data[counter_pos])
#define DATA (data[data_pos])
while (uncompressed_size) {
if (prev_mode) {
if (COUNTER) {
COUNTER--;
*(dest++) = *(prev++);
uncompressed_size--;
}
else {
prev_mode = false;
counter_pos += sizeof(uint16_t);
data_pos = counter_pos + sizeof(uint16_t);
}
}
else {
if (COUNTER) {
COUNTER--;
*(dest++) = DATA;
data_pos++;
prev++;
uncompressed_size--;
}
else {
prev_mode = true;
counter_pos = data_pos;
data_pos += sizeof(uint16_t);
}
}
}
#undef DATA
#undef COUNTER
}
void GB_rewind_push(GB_gameboy_t *gb)
{
const size_t save_size = GB_get_save_state_size(gb);
if (!gb->rewind_sequences) {
if (gb->rewind_buffer_length) {
gb->rewind_sequences = malloc(sizeof(*gb->rewind_sequences) * gb->rewind_buffer_length);
memset(gb->rewind_sequences, 0, sizeof(*gb->rewind_sequences) * gb->rewind_buffer_length);
gb->rewind_pos = 0;
}
else {
return;
}
}
if (gb->rewind_sequences[gb->rewind_pos].pos == GB_REWIND_FRAMES_PER_KEY) {
gb->rewind_pos++;
if (gb->rewind_pos == gb->rewind_buffer_length) {
gb->rewind_pos = 0;
}
if (gb->rewind_sequences[gb->rewind_pos].key_state) {
free(gb->rewind_sequences[gb->rewind_pos].key_state);
gb->rewind_sequences[gb->rewind_pos].key_state = NULL;
}
for (unsigned i = 0; i < GB_REWIND_FRAMES_PER_KEY; i++) {
if (gb->rewind_sequences[gb->rewind_pos].compressed_states[i]) {
free(gb->rewind_sequences[gb->rewind_pos].compressed_states[i]);
gb->rewind_sequences[gb->rewind_pos].compressed_states[i] = 0;
}
}
gb->rewind_sequences[gb->rewind_pos].pos = 0;
}
if (!gb->rewind_sequences[gb->rewind_pos].key_state) {
gb->rewind_sequences[gb->rewind_pos].key_state = malloc(save_size);
GB_save_state_to_buffer(gb, gb->rewind_sequences[gb->rewind_pos].key_state);
}
else {
uint8_t *save_state = malloc(save_size);
GB_save_state_to_buffer(gb, save_state);
gb->rewind_sequences[gb->rewind_pos].compressed_states[gb->rewind_sequences[gb->rewind_pos].pos++] =
state_compress(gb->rewind_sequences[gb->rewind_pos].key_state, save_state, save_size);
free(save_state);
}
}
bool GB_rewind_pop(GB_gameboy_t *gb)
{
if (!gb->rewind_sequences || !gb->rewind_sequences[gb->rewind_pos].key_state) {
return false;
}
const size_t save_size = GB_get_save_state_size(gb);
if (gb->rewind_sequences[gb->rewind_pos].pos == 0) {
GB_load_state_from_buffer(gb, gb->rewind_sequences[gb->rewind_pos].key_state, save_size);
free(gb->rewind_sequences[gb->rewind_pos].key_state);
gb->rewind_sequences[gb->rewind_pos].key_state = NULL;
gb->rewind_pos = gb->rewind_pos == 0? gb->rewind_buffer_length - 1 : gb->rewind_pos - 1;
return true;
}
uint8_t *save_state = malloc(save_size);
state_decompress(gb->rewind_sequences[gb->rewind_pos].key_state,
gb->rewind_sequences[gb->rewind_pos].compressed_states[--gb->rewind_sequences[gb->rewind_pos].pos],
save_state,
save_size);
free(gb->rewind_sequences[gb->rewind_pos].compressed_states[gb->rewind_sequences[gb->rewind_pos].pos]);
gb->rewind_sequences[gb->rewind_pos].compressed_states[gb->rewind_sequences[gb->rewind_pos].pos] = NULL;
GB_load_state_from_buffer(gb, save_state, save_size);
free(save_state);
return true;
}
void GB_rewind_free(GB_gameboy_t *gb)
{
if (!gb->rewind_sequences) return;
for (unsigned i = 0; i < gb->rewind_buffer_length; i++) {
if (gb->rewind_sequences[i].key_state) {
free(gb->rewind_sequences[i].key_state);
}
for (unsigned j = 0; j < GB_REWIND_FRAMES_PER_KEY; j++) {
if (gb->rewind_sequences[i].compressed_states[j]) {
free(gb->rewind_sequences[i].compressed_states[j]);
}
}
}
free(gb->rewind_sequences);
gb->rewind_sequences = NULL;
}
void GB_set_rewind_length(GB_gameboy_t *gb, double seconds)
{
GB_rewind_free(gb);
if (seconds == 0) {
gb->rewind_buffer_length = 0;
}
else {
gb->rewind_buffer_length = (size_t) ceil(seconds * CPU_FREQUENCY / LCDC_PERIOD / GB_REWIND_FRAMES_PER_KEY);
}
}

14
bsnes/gb/Core/rewind.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef rewind_h
#define rewind_h
#include <stdbool.h>
#include "gb_struct_def.h"
#ifdef GB_INTERNAL
void GB_rewind_push(GB_gameboy_t *gb);
void GB_rewind_free(GB_gameboy_t *gb);
#endif
bool GB_rewind_pop(GB_gameboy_t *gb);
void GB_set_rewind_length(GB_gameboy_t *gb, double seconds);
#endif

377
bsnes/gb/Core/save_state.c Normal file
View File

@@ -0,0 +1,377 @@
#include "gb.h"
#include <stdio.h>
#include <errno.h>
static bool dump_section(FILE *f, const void *src, uint32_t size)
{
if (fwrite(&size, 1, sizeof(size), f) != sizeof(size)) {
return false;
}
if (fwrite(src, 1, size, f) != size) {
return false;
}
return true;
}
#define DUMP_SECTION(gb, f, section) dump_section(f, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section))
/* Todo: we need a sane and protable save state format. */
int GB_save_state(GB_gameboy_t *gb, const char *path)
{
FILE *f = fopen(path, "wb");
if (!f) {
GB_log(gb, "Could not open save state: %s.\n", strerror(errno));
return errno;
}
if (fwrite(GB_GET_SECTION(gb, header), 1, GB_SECTION_SIZE(header), f) != GB_SECTION_SIZE(header)) goto error;
if (!DUMP_SECTION(gb, f, core_state)) goto error;
if (!DUMP_SECTION(gb, f, dma )) goto error;
if (!DUMP_SECTION(gb, f, mbc )) goto error;
if (!DUMP_SECTION(gb, f, hram )) goto error;
if (!DUMP_SECTION(gb, f, timing )) goto error;
if (!DUMP_SECTION(gb, f, apu )) goto error;
if (!DUMP_SECTION(gb, f, rtc )) goto error;
if (!DUMP_SECTION(gb, f, video )) goto error;
if (GB_is_hle_sgb(gb)) {
if (!dump_section(f, gb->sgb, sizeof(*gb->sgb))) goto error;
}
if (fwrite(gb->mbc_ram, 1, gb->mbc_ram_size, f) != gb->mbc_ram_size) {
goto error;
}
if (fwrite(gb->ram, 1, gb->ram_size, f) != gb->ram_size) {
goto error;
}
if (fwrite(gb->vram, 1, gb->vram_size, f) != gb->vram_size) {
goto error;
}
errno = 0;
error:
fclose(f);
return errno;
}
#undef DUMP_SECTION
size_t GB_get_save_state_size(GB_gameboy_t *gb)
{
return GB_SECTION_SIZE(header)
+ GB_SECTION_SIZE(core_state) + sizeof(uint32_t)
+ GB_SECTION_SIZE(dma ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(mbc ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(hram ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(timing ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(apu ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(rtc ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(video ) + sizeof(uint32_t)
+ (GB_is_hle_sgb(gb)? sizeof(*gb->sgb) + sizeof(uint32_t) : 0)
+ gb->mbc_ram_size
+ gb->ram_size
+ gb->vram_size;
}
/* A write-line function for memory copying */
static void buffer_write(const void *src, size_t size, uint8_t **dest)
{
memcpy(*dest, src, size);
*dest += size;
}
static void buffer_dump_section(uint8_t **buffer, const void *src, uint32_t size)
{
buffer_write(&size, sizeof(size), buffer);
buffer_write(src, size, buffer);
}
#define DUMP_SECTION(gb, buffer, section) buffer_dump_section(&buffer, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section))
void GB_save_state_to_buffer(GB_gameboy_t *gb, uint8_t *buffer)
{
buffer_write(GB_GET_SECTION(gb, header), GB_SECTION_SIZE(header), &buffer);
DUMP_SECTION(gb, buffer, core_state);
DUMP_SECTION(gb, buffer, dma );
DUMP_SECTION(gb, buffer, mbc );
DUMP_SECTION(gb, buffer, hram );
DUMP_SECTION(gb, buffer, timing );
DUMP_SECTION(gb, buffer, apu );
DUMP_SECTION(gb, buffer, rtc );
DUMP_SECTION(gb, buffer, video );
if (GB_is_hle_sgb(gb)) {
buffer_dump_section(&buffer, gb->sgb, sizeof(*gb->sgb));
}
buffer_write(gb->mbc_ram, gb->mbc_ram_size, &buffer);
buffer_write(gb->ram, gb->ram_size, &buffer);
buffer_write(gb->vram, gb->vram_size, &buffer);
}
/* Best-effort read function for maximum future compatibility. */
static bool read_section(FILE *f, void *dest, uint32_t size)
{
uint32_t saved_size = 0;
if (fread(&saved_size, 1, sizeof(size), f) != sizeof(size)) {
return false;
}
if (saved_size <= size) {
if (fread(dest, 1, saved_size, f) != saved_size) {
return false;
}
}
else {
if (fread(dest, 1, size, f) != size) {
return false;
}
fseek(f, saved_size - size, SEEK_CUR);
}
return true;
}
#undef DUMP_SECTION
static bool verify_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t *save)
{
if (gb->magic != save->magic) {
GB_log(gb, "The file is not a save state, or is from an incompatible operating system.\n");
return false;
}
if (gb->version != save->version) {
GB_log(gb, "The save state is for a different version of SameBoy.\n");
return false;
}
if (gb->mbc_ram_size < save->mbc_ram_size) {
GB_log(gb, "The save state has non-matching MBC RAM size.\n");
return false;
}
if (gb->vram_size != save->vram_size) {
GB_log(gb, "The save state has non-matching VRAM size. Try changing the emulated model.\n");
return false;
}
if (GB_is_hle_sgb(gb) != GB_is_hle_sgb(save)) {
GB_log(gb, "The save state is %sfor a Super Game Boy. Try changing the emulated model.\n", GB_is_hle_sgb(save)? "" : "not ");
return false;
}
if (gb->ram_size != save->ram_size) {
if (gb->ram_size == 0x1000 * 8 && save->ram_size == 0x2000 * 8) {
/* A bug in versions prior to 0.12 made CGB instances allocate twice the ammount of RAM.
Ignore this issue to retain compatibility with older, 0.11, save states. */
}
else {
GB_log(gb, "The save state has non-matching RAM size. Try changing the emulated model.\n");
return false;
}
}
return true;
}
#define READ_SECTION(gb, f, section) read_section(f, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section))
int GB_load_state(GB_gameboy_t *gb, const char *path)
{
GB_gameboy_t save;
/* Every unread value should be kept the same. */
memcpy(&save, gb, sizeof(save));
/* ...Except ram size, we use it to detect old saves with incorrect ram sizes */
save.ram_size = 0;
FILE *f = fopen(path, "rb");
if (!f) {
GB_log(gb, "Could not open save state: %s.\n", strerror(errno));
return errno;
}
if (fread(GB_GET_SECTION(&save, header), 1, GB_SECTION_SIZE(header), f) != GB_SECTION_SIZE(header)) goto error;
if (!READ_SECTION(&save, f, core_state)) goto error;
if (!READ_SECTION(&save, f, dma )) goto error;
if (!READ_SECTION(&save, f, mbc )) goto error;
if (!READ_SECTION(&save, f, hram )) goto error;
if (!READ_SECTION(&save, f, timing )) goto error;
if (!READ_SECTION(&save, f, apu )) goto error;
if (!READ_SECTION(&save, f, rtc )) goto error;
if (!READ_SECTION(&save, f, video )) goto error;
if (save.ram_size == 0) {
/* Save doesn't have ram size specified, it's a pre 0.12 save state with potentially
incorrect RAM amount if it's a CGB instance */
if (GB_is_cgb(&save)) {
save.ram_size = 0x2000 * 8; // Incorrect RAM size
}
else {
save.ram_size = gb->ram_size;
}
}
if (!verify_state_compatibility(gb, &save)) {
errno = -1;
goto error;
}
if (GB_is_hle_sgb(gb)) {
if (!read_section(f, gb->sgb, sizeof(*gb->sgb))) goto error;
}
memset(gb->mbc_ram + save.mbc_ram_size, 0xFF, gb->mbc_ram_size - save.mbc_ram_size);
if (fread(gb->mbc_ram, 1, save.mbc_ram_size, f) != save.mbc_ram_size) {
fclose(f);
return EIO;
}
if (fread(gb->ram, 1, gb->ram_size, f) != gb->ram_size) {
fclose(f);
return EIO;
}
/* Fix for 0.11 save states that allocate twice the amount of RAM in CGB instances */
fseek(f, save.ram_size - gb->ram_size, SEEK_CUR);
if (fread(gb->vram, 1, gb->vram_size, f) != gb->vram_size) {
fclose(f);
return EIO;
}
size_t orig_ram_size = gb->ram_size;
memcpy(gb, &save, sizeof(save));
gb->ram_size = orig_ram_size;
errno = 0;
if (gb->cartridge_type->has_rumble && gb->rumble_callback) {
gb->rumble_callback(gb, gb->rumble_state);
}
for (unsigned i = 0; i < 32; i++) {
GB_palette_changed(gb, false, i * 2);
GB_palette_changed(gb, true, i * 2);
}
gb->bg_fifo.read_end &= 0xF;
gb->bg_fifo.write_end &= 0xF;
gb->oam_fifo.read_end &= 0xF;
gb->oam_fifo.write_end &= 0xF;
error:
fclose(f);
return errno;
}
#undef READ_SECTION
/* An read-like function for buffer-copying */
static size_t buffer_read(void *dest, size_t length, const uint8_t **buffer, size_t *buffer_length)
{
if (length > *buffer_length) {
length = *buffer_length;
}
memcpy(dest, *buffer, length);
*buffer += length;
*buffer_length -= length;
return length;
}
static bool buffer_read_section(const uint8_t **buffer, size_t *buffer_length, void *dest, uint32_t size)
{
uint32_t saved_size = 0;
if (buffer_read(&saved_size, sizeof(size), buffer, buffer_length) != sizeof(size)) {
return false;
}
if (saved_size > *buffer_length) return false;
if (saved_size <= size) {
if (buffer_read(dest, saved_size, buffer, buffer_length) != saved_size) {
return false;
}
}
else {
if (buffer_read(dest, size, buffer, buffer_length) != size) {
return false;
}
*buffer += saved_size - size;
*buffer_length -= saved_size - size;
}
return true;
}
#define READ_SECTION(gb, buffer, length, section) buffer_read_section(&buffer, &length, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section))
int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t length)
{
GB_gameboy_t save;
/* Every unread value should be kept the same. */
memcpy(&save, gb, sizeof(save));
if (buffer_read(GB_GET_SECTION(&save, header), GB_SECTION_SIZE(header), &buffer, &length) != GB_SECTION_SIZE(header)) return -1;
if (!READ_SECTION(&save, buffer, length, core_state)) return -1;
if (!READ_SECTION(&save, buffer, length, dma )) return -1;
if (!READ_SECTION(&save, buffer, length, mbc )) return -1;
if (!READ_SECTION(&save, buffer, length, hram )) return -1;
if (!READ_SECTION(&save, buffer, length, timing )) return -1;
if (!READ_SECTION(&save, buffer, length, apu )) return -1;
if (!READ_SECTION(&save, buffer, length, rtc )) return -1;
if (!READ_SECTION(&save, buffer, length, video )) return -1;
if (!verify_state_compatibility(gb, &save)) {
return -1;
}
if (GB_is_hle_sgb(gb)) {
if (!buffer_read_section(&buffer, &length, gb->sgb, sizeof(*gb->sgb))) return -1;
}
memset(gb->mbc_ram + save.mbc_ram_size, 0xFF, gb->mbc_ram_size - save.mbc_ram_size);
if (buffer_read(gb->mbc_ram, save.mbc_ram_size, &buffer, &length) != save.mbc_ram_size) {
return -1;
}
if (buffer_read(gb->ram, gb->ram_size, &buffer, &length) != gb->ram_size) {
return -1;
}
if (buffer_read(gb->vram,gb->vram_size, &buffer, &length) != gb->vram_size) {
return -1;
}
/* Fix for 0.11 save states that allocate twice the amount of RAM in CGB instances */
buffer += save.ram_size - gb->ram_size;
length -= save.ram_size - gb->ram_size;
memcpy(gb, &save, sizeof(save));
if (gb->cartridge_type->has_rumble && gb->rumble_callback) {
gb->rumble_callback(gb, gb->rumble_state);
}
for (unsigned i = 0; i < 32; i++) {
GB_palette_changed(gb, false, i * 2);
GB_palette_changed(gb, true, i * 2);
}
gb->bg_fifo.read_end &= 0xF;
gb->bg_fifo.write_end &= 0xF;
gb->oam_fifo.read_end &= 0xF;
gb->oam_fifo.write_end &= 0xF;
return 0;
}
#undef READ_SECTION

View File

@@ -0,0 +1,24 @@
/* Macros to make the GB_gameboy_t struct more future compatible when state saving */
#ifndef save_state_h
#define save_state_h
#include <stddef.h>
#define GB_PADDING(type, old_usage) type old_usage##__do_not_use
#define GB_SECTION(name, ...) __attribute__ ((aligned (8))) struct {} name##_section_start; __VA_ARGS__; struct {} name##_section_end
#define GB_SECTION_OFFSET(name) (offsetof(GB_gameboy_t, name##_section_start))
#define GB_SECTION_SIZE(name) (offsetof(GB_gameboy_t, name##_section_end) - offsetof(GB_gameboy_t, name##_section_start))
#define GB_GET_SECTION(gb, name) ((void*)&((gb)->name##_section_start))
#define GB_aligned_double __attribute__ ((aligned (8))) double
/* Public calls related to save states */
int GB_save_state(GB_gameboy_t *gb, const char *path);
size_t GB_get_save_state_size(GB_gameboy_t *gb);
/* Assumes buffer is big enough to contain the save state. Use with GB_get_save_state_size(). */
void GB_save_state_to_buffer(GB_gameboy_t *gb, uint8_t *buffer);
int GB_load_state(GB_gameboy_t *gb, const char *path);
int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t length);
#endif /* save_state_h */

842
bsnes/gb/Core/sgb.c Normal file
View File

@@ -0,0 +1,842 @@
#include "gb.h"
#include "random.h"
#include <math.h>
#include <assert.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#define INTRO_ANIMATION_LENGTH 200
enum {
PAL01 = 0x00,
PAL23 = 0x01,
PAL03 = 0x02,
PAL12 = 0x03,
ATTR_BLK = 0x04,
ATTR_LIN = 0x05,
ATTR_DIV = 0x06,
PAL_SET = 0x0A,
PAL_TRN = 0x0B,
DATA_SND = 0x0F,
MLT_REQ = 0x11,
CHR_TRN = 0x13,
PCT_TRN = 0x14,
ATTR_TRN = 0x15,
ATTR_SET = 0x16,
MASK_EN = 0x17,
};
typedef enum {
MASK_DISABLED,
MASK_FREEZE,
MASK_BLACK,
MASK_COLOR_0,
} mask_mode_t;
typedef enum {
TRANSFER_LOW_TILES,
TRANSFER_HIGH_TILES,
TRANSFER_BORDER_DATA,
TRANSFER_PALETTES,
TRANSFER_ATTRIBUTES,
} transfer_dest_t;
#define SGB_PACKET_SIZE 16
static inline void pal_command(GB_gameboy_t *gb, unsigned first, unsigned second)
{
gb->sgb->effective_palettes[0] = gb->sgb->effective_palettes[4] =
gb->sgb->effective_palettes[8] = gb->sgb->effective_palettes[12] =
gb->sgb->command[1] | (gb->sgb->command[2] << 8);
for (unsigned i = 0; i < 3; i++) {
gb->sgb->effective_palettes[first * 4 + i + 1] = gb->sgb->command[3 + i * 2] | (gb->sgb->command[4 + i * 2] << 8);
}
for (unsigned i = 0; i < 3; i++) {
gb->sgb->effective_palettes[second * 4 + i + 1] = gb->sgb->command[9 + i * 2] | (gb->sgb->command[10 + i * 2] << 8);
}
}
static inline void load_attribute_file(GB_gameboy_t *gb, unsigned file_index)
{
if (file_index > 0x2C) return;
uint8_t *output = gb->sgb->attribute_map;
for (unsigned i = 0; i < 90; i++) {
uint8_t byte = gb->sgb->attribute_files[file_index * 90 + i];
for (unsigned j = 4; j--;) {
*(output++) = byte >> 6;
byte <<= 2;
}
}
}
static const uint16_t built_in_palettes[] =
{
0x67BF, 0x265B, 0x10B5, 0x2866,
0x637B, 0x3AD9, 0x0956, 0x0000,
0x7F1F, 0x2A7D, 0x30F3, 0x4CE7,
0x57FF, 0x2618, 0x001F, 0x006A,
0x5B7F, 0x3F0F, 0x222D, 0x10EB,
0x7FBB, 0x2A3C, 0x0015, 0x0900,
0x2800, 0x7680, 0x01EF, 0x2FFF,
0x73BF, 0x46FF, 0x0110, 0x0066,
0x533E, 0x2638, 0x01E5, 0x0000,
0x7FFF, 0x2BBF, 0x00DF, 0x2C0A,
0x7F1F, 0x463D, 0x74CF, 0x4CA5,
0x53FF, 0x03E0, 0x00DF, 0x2800,
0x433F, 0x72D2, 0x3045, 0x0822,
0x7FFA, 0x2A5F, 0x0014, 0x0003,
0x1EED, 0x215C, 0x42FC, 0x0060,
0x7FFF, 0x5EF7, 0x39CE, 0x0000,
0x4F5F, 0x630E, 0x159F, 0x3126,
0x637B, 0x121C, 0x0140, 0x0840,
0x66BC, 0x3FFF, 0x7EE0, 0x2C84,
0x5FFE, 0x3EBC, 0x0321, 0x0000,
0x63FF, 0x36DC, 0x11F6, 0x392A,
0x65EF, 0x7DBF, 0x035F, 0x2108,
0x2B6C, 0x7FFF, 0x1CD9, 0x0007,
0x53FC, 0x1F2F, 0x0E29, 0x0061,
0x36BE, 0x7EAF, 0x681A, 0x3C00,
0x7BBE, 0x329D, 0x1DE8, 0x0423,
0x739F, 0x6A9B, 0x7293, 0x0001,
0x5FFF, 0x6732, 0x3DA9, 0x2481,
0x577F, 0x3EBC, 0x456F, 0x1880,
0x6B57, 0x6E1B, 0x5010, 0x0007,
0x0F96, 0x2C97, 0x0045, 0x3200,
0x67FF, 0x2F17, 0x2230, 0x1548,
};
static const struct {
char name[16];
unsigned palette_index;
} palette_assignments[] =
{
{"ZELDA", 5},
{"SUPER MARIOLAND", 6},
{"MARIOLAND2", 0x14},
{"SUPERMARIOLAND3", 2},
{"KIRBY DREAM LAND", 0xB},
{"HOSHINOKA-BI", 0xB},
{"KIRBY'S PINBALL", 3},
{"YOSSY NO TAMAGO", 0xC},
{"MARIO & YOSHI", 0xC},
{"YOSSY NO COOKIE", 4},
{"YOSHI'S COOKIE", 4},
{"DR.MARIO", 0x12},
{"TETRIS", 0x11},
{"YAKUMAN", 0x13},
{"METROID2", 0x1F},
{"KAERUNOTAMENI", 9},
{"GOLF", 0x18},
{"ALLEY WAY", 0x16},
{"BASEBALL", 0xF},
{"TENNIS", 0x17},
{"F1RACE", 0x1E},
{"KID ICARUS", 0xE},
{"QIX", 0x19},
{"SOLARSTRIKER", 7},
{"X", 0x1C},
{"GBWARS", 0x15},
};
static void command_ready(GB_gameboy_t *gb)
{
/* SGB header commands are used to send the contents of the header to the SNES CPU.
A header command looks like this:
Command ID: 0b1111xxx1, where xxx is the packet index. (e.g. F1 for [0x104, 0x112), F3 for [0x112, 0x120))
Checksum: Simple one byte sum for the following content bytes
0xE content bytes. The last command, FB, is padded with zeros, so information past the header is not sent. */
if ((gb->sgb->command[0] & 0xF1) == 0xF1) {
if(gb->boot_rom_finished) return;
uint8_t checksum = 0;
for (unsigned i = 2; i < 0x10; i++) {
checksum += gb->sgb->command[i];
}
if (checksum != gb->sgb->command[1]) {
GB_log(gb, "Failed checksum for SGB header command, disabling SGB features\n");
gb->sgb->disable_commands = true;
return;
}
unsigned index = (gb->sgb->command[0] >> 1) & 7;
if (index > 5) {
return;
}
memcpy(&gb->sgb->received_header[index * 14], &gb->sgb->command[2], 14);
if (gb->sgb->command[0] == 0xfb) {
if (gb->sgb->received_header[0x42] != 3 || gb->sgb->received_header[0x47] != 0x33) {
gb->sgb->disable_commands = true;
for (unsigned i = 0; i < sizeof(palette_assignments) / sizeof(palette_assignments[0]); i++) {
if (memcmp(palette_assignments[i].name, &gb->sgb->received_header[0x30], sizeof(palette_assignments[i].name)) == 0) {
gb->sgb->effective_palettes[0] = built_in_palettes[palette_assignments[i].palette_index * 4 - 4];
gb->sgb->effective_palettes[1] = built_in_palettes[palette_assignments[i].palette_index * 4 + 1 - 4];
gb->sgb->effective_palettes[2] = built_in_palettes[palette_assignments[i].palette_index * 4 + 2 - 4];
gb->sgb->effective_palettes[3] = built_in_palettes[palette_assignments[i].palette_index * 4 + 3 - 4];
break;
}
}
}
}
return;
}
/* Ignore malformed commands (0 length)*/
if ((gb->sgb->command[0] & 7) == 0) return;
switch (gb->sgb->command[0] >> 3) {
case PAL01:
pal_command(gb, 0, 1);
break;
case PAL23:
pal_command(gb, 2, 3);
break;
case PAL03:
pal_command(gb, 0, 3);
break;
case PAL12:
pal_command(gb, 1, 2);
break;
case ATTR_BLK: {
struct {
uint8_t count;
struct {
uint8_t control;
uint8_t palettes;
uint8_t left, top, right, bottom;
} data[];
} *command = (void *)(gb->sgb->command + 1);
if (command->count > 0x12) return;
for (unsigned i = 0; i < command->count; i++) {
bool inside = command->data[i].control & 1;
bool middle = command->data[i].control & 2;
bool outside = command->data[i].control & 4;
uint8_t inside_palette = command->data[i].palettes & 0x3;
uint8_t middle_palette = (command->data[i].palettes >> 2) & 0x3;
uint8_t outside_palette = (command->data[i].palettes >> 4) & 0x3;
if (inside && !middle && !outside) {
middle = true;
middle_palette = inside_palette;
}
else if (outside && !middle && !inside) {
middle = true;
middle_palette = outside_palette;
}
command->data[i].left &= 0x1F;
command->data[i].top &= 0x1F;
command->data[i].right &= 0x1F;
command->data[i].bottom &= 0x1F;
for (unsigned y = 0; y < 18; y++) {
for (unsigned x = 0; x < 20; x++) {
if (x < command->data[i].left || x > command->data[i].right ||
y < command->data[i].top || y > command->data[i].bottom) {
if (outside) {
gb->sgb->attribute_map[x + 20 * y] = outside_palette;
}
}
else if (x > command->data[i].left && x < command->data[i].right &&
y > command->data[i].top && y < command->data[i].bottom) {
if (inside) {
gb->sgb->attribute_map[x + 20 * y] = inside_palette;
}
}
else if(middle) {
gb->sgb->attribute_map[x + 20 * y] = middle_palette;
}
}
}
}
break;
}
case ATTR_LIN: {
struct {
uint8_t count;
uint8_t data[];
} *command = (void *)(gb->sgb->command + 1);
if (command->count > sizeof(gb->sgb->command) - 2) return;
for (unsigned i = 0; i < command->count; i++) {
bool horizontal = command->data[i] & 0x80;
uint8_t palette = (command->data[i] >> 5) & 0x3;
uint8_t line = (command->data[i]) & 0x1F;
if (horizontal) {
if (line > 18) continue;
for (unsigned x = 0; x < 20; x++) {
gb->sgb->attribute_map[x + 20 * line] = palette;
}
}
else {
if (line > 20) continue;
for (unsigned y = 0; y < 18; y++) {
gb->sgb->attribute_map[line + 20 * y] = palette;
}
}
}
break;
}
case ATTR_DIV: {
uint8_t high_palette = gb->sgb->command[1] & 3;
uint8_t low_palette = (gb->sgb->command[1] >> 2) & 3;
uint8_t middle_palette = (gb->sgb->command[1] >> 4) & 3;
bool horizontal = gb->sgb->command[1] & 0x40;
uint8_t line = gb->sgb->command[2] & 0x1F;
for (unsigned y = 0; y < 18; y++) {
for (unsigned x = 0; x < 20; x++) {
if ((horizontal? y : x) < line) {
gb->sgb->attribute_map[x + 20 * y] = low_palette;
}
else if ((horizontal? y : x) == line) {
gb->sgb->attribute_map[x + 20 * y] = middle_palette;
}
else {
gb->sgb->attribute_map[x + 20 * y] = high_palette;
}
}
}
break;
}
case PAL_SET:
memcpy(&gb->sgb->effective_palettes[0],
&gb->sgb->ram_palettes[4 * (gb->sgb->command[1] + (gb->sgb->command[2] & 1) * 0x100)],
8);
memcpy(&gb->sgb->effective_palettes[4],
&gb->sgb->ram_palettes[4 * (gb->sgb->command[3] + (gb->sgb->command[4] & 1) * 0x100)],
8);
memcpy(&gb->sgb->effective_palettes[8],
&gb->sgb->ram_palettes[4 * (gb->sgb->command[5] + (gb->sgb->command[6] & 1) * 0x100)],
8);
memcpy(&gb->sgb->effective_palettes[12],
&gb->sgb->ram_palettes[4 * (gb->sgb->command[7] + (gb->sgb->command[8] & 1) * 0x100)],
8);
gb->sgb->effective_palettes[12] = gb->sgb->effective_palettes[8] =
gb->sgb->effective_palettes[4] = gb->sgb->effective_palettes[0];
if (gb->sgb->command[9] & 0x80) {
load_attribute_file(gb, gb->sgb->command[9] & 0x3F);
}
if (gb->sgb->command[9] & 0x40) {
gb->sgb->mask_mode = MASK_DISABLED;
}
break;
case PAL_TRN:
gb->sgb->vram_transfer_countdown = 2;
gb->sgb->transfer_dest = TRANSFER_PALETTES;
break;
case DATA_SND:
// Not supported, but used by almost all SGB games for hot patching, so let's mute the warning for this
break;
case MLT_REQ:
if (gb->sgb->player_count == 1) {
gb->sgb->current_player = 0;
}
gb->sgb->player_count = (gb->sgb->command[1] & 3) + 1; /* Todo: When breaking save state comaptibility,
fix this to be 0 based. */
if (gb->sgb->player_count == 3) {
gb->sgb->current_player++;
}
gb->sgb->mlt_lock = true;
break;
case CHR_TRN:
gb->sgb->vram_transfer_countdown = 2;
gb->sgb->transfer_dest = (gb->sgb->command[1] & 1)? TRANSFER_HIGH_TILES : TRANSFER_LOW_TILES;
break;
case PCT_TRN:
gb->sgb->vram_transfer_countdown = 2;
gb->sgb->transfer_dest = TRANSFER_BORDER_DATA;
break;
case ATTR_TRN:
gb->sgb->vram_transfer_countdown = 2;
gb->sgb->transfer_dest = TRANSFER_ATTRIBUTES;
break;
case ATTR_SET:
load_attribute_file(gb, gb->sgb->command[0] & 0x3F);
if (gb->sgb->command[0] & 0x40) {
gb->sgb->mask_mode = MASK_DISABLED;
}
break;
case MASK_EN:
gb->sgb->mask_mode = gb->sgb->command[1] & 3;
break;
default:
if ((gb->sgb->command[0] >> 3) == 8 &&
(gb->sgb->command[1] & ~0x80) == 0 &&
(gb->sgb->command[2] & ~0x80) == 0) {
/* Mute/dummy sound commands, ignore this command as it's used by many games at startup */
break;
}
GB_log(gb, "Unimplemented SGB command %x: ", gb->sgb->command[0] >> 3);
for (unsigned i = 0; i < gb->sgb->command_write_index / 8; i++) {
GB_log(gb, "%02x ", gb->sgb->command[i]);
}
GB_log(gb, "\n");
}
}
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
{
if (!GB_is_sgb(gb)) return;
if (!GB_is_hle_sgb(gb)) {
/* Notify via callback */
return;
}
if (gb->sgb->disable_commands) return;
if (gb->sgb->command_write_index >= sizeof(gb->sgb->command) * 8) {
return;
}
uint16_t command_size = (gb->sgb->command[0] & 7 ?: 1) * SGB_PACKET_SIZE * 8;
if ((gb->sgb->command[0] & 0xF1) == 0xF1) {
command_size = SGB_PACKET_SIZE * 8;
}
if ((value & 0x20) == 0 && (gb->io_registers[GB_IO_JOYP] & 0x20) != 0) {
gb->sgb->mlt_lock ^= true;
}
switch ((value >> 4) & 3) {
case 3:
gb->sgb->ready_for_pulse = true;
if ((gb->sgb->player_count & 1) == 0 && !gb->sgb->mlt_lock) {
gb->sgb->current_player++;
gb->sgb->current_player &= 3;
gb->sgb->mlt_lock = true;
}
break;
case 2: // Zero
if (!gb->sgb->ready_for_pulse || !gb->sgb->ready_for_write) return;
if (gb->sgb->ready_for_stop) {
if (gb->sgb->command_write_index == command_size) {
command_ready(gb);
gb->sgb->command_write_index = 0;
memset(gb->sgb->command, 0, sizeof(gb->sgb->command));
}
gb->sgb->ready_for_pulse = false;
gb->sgb->ready_for_write = false;
gb->sgb->ready_for_stop = false;
}
else {
gb->sgb->command_write_index++;
gb->sgb->ready_for_pulse = false;
if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) == 0) {
gb->sgb->ready_for_stop = true;
}
}
break;
case 1: // One
if (!gb->sgb->ready_for_pulse || !gb->sgb->ready_for_write) return;
if (gb->sgb->ready_for_stop) {
GB_log(gb, "Corrupt SGB command.\n");
gb->sgb->ready_for_pulse = false;
gb->sgb->ready_for_write = false;
gb->sgb->command_write_index = 0;
memset(gb->sgb->command, 0, sizeof(gb->sgb->command));
}
else {
gb->sgb->command[gb->sgb->command_write_index / 8] |= 1 << (gb->sgb->command_write_index & 7);
gb->sgb->command_write_index++;
gb->sgb->ready_for_pulse = false;
if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) == 0) {
gb->sgb->ready_for_stop = true;
}
}
break;
case 0:
if (!gb->sgb->ready_for_pulse) return;
gb->sgb->ready_for_write = true;
gb->sgb->ready_for_pulse = false;
if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) != 0 ||
gb->sgb->command_write_index == 0 ||
gb->sgb->ready_for_stop) {
gb->sgb->command_write_index = 0;
memset(gb->sgb->command, 0, sizeof(gb->sgb->command));
gb->sgb->ready_for_stop = false;
}
break;
default:
break;
}
}
static uint32_t convert_rgb15(GB_gameboy_t *gb, uint16_t color)
{
return GB_convert_rgb15(gb, color);
}
static uint32_t convert_rgb15_with_fade(GB_gameboy_t *gb, uint16_t color, uint8_t fade)
{
uint8_t r = ((color) & 0x1F) - fade;
uint8_t g = ((color >> 5) & 0x1F) - fade;
uint8_t b = ((color >> 10) & 0x1F) - fade;
if (r >= 0x20) r = 0;
if (g >= 0x20) g = 0;
if (b >= 0x20) b = 0;
color = r | (g << 5) | (b << 10);
return GB_convert_rgb15(gb, color);
}
#include <stdio.h>
static void render_boot_animation (GB_gameboy_t *gb)
{
#include "sgb_animation_logo.inc"
uint32_t *output = &gb->screen[48 + 40 * 256];
uint8_t *input = animation_logo;
unsigned fade_blue = 0;
unsigned fade_red = 0;
if (gb->sgb->intro_animation < 80 - 32) {
fade_blue = 32;
}
else if (gb->sgb->intro_animation < 80) {
fade_blue = 80 - gb->sgb->intro_animation;
}
else if (gb->sgb->intro_animation > INTRO_ANIMATION_LENGTH - 32) {
fade_red = fade_blue = gb->sgb->intro_animation - INTRO_ANIMATION_LENGTH + 32;
}
uint32_t colors[] = {
convert_rgb15(gb, 0),
convert_rgb15_with_fade(gb, 0x14A5, fade_blue),
convert_rgb15_with_fade(gb, 0x54E0, fade_blue),
convert_rgb15_with_fade(gb, 0x0019, fade_red),
convert_rgb15(gb, 0x0011),
convert_rgb15(gb, 0x0009),
};
unsigned y_min = (144 - animation_logo_height) / 2;
unsigned y_max = y_min + animation_logo_height;
for (unsigned y = 0; y < 144; y++) {
for (unsigned x = 0; x < 160; x++) {
if (y < y_min || y >= y_max) {
*(output++) = colors[0];
}
else {
uint8_t color = *input;
if (color >= 3) {
if (color == gb->sgb->intro_animation / 2 - 3) {
color = 5;
}
else if (color == gb->sgb->intro_animation / 2 - 4) {
color = 4;
}
else if (color < gb->sgb->intro_animation / 2 - 4) {
color = 3;
}
else {
color = 0;
}
}
*(output++) = colors[color];
input++;
}
}
output += 256 - 160;
}
}
static void render_jingle(GB_gameboy_t *gb, size_t count);
void GB_sgb_render(GB_gameboy_t *gb)
{
if (gb->apu_output.sample_rate) {
render_jingle(gb, gb->apu_output.sample_rate / GB_get_usual_frame_rate(gb));
}
if (gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) gb->sgb->intro_animation++;
if (!gb->screen || !gb->rgb_encode_callback) return;
if (gb->sgb->mask_mode != MASK_FREEZE) {
memcpy(gb->sgb->effective_screen_buffer,
gb->sgb->screen_buffer,
sizeof(gb->sgb->effective_screen_buffer));
}
if (gb->sgb->vram_transfer_countdown) {
if (--gb->sgb->vram_transfer_countdown == 0) {
if (gb->sgb->transfer_dest == TRANSFER_LOW_TILES || gb->sgb->transfer_dest == TRANSFER_HIGH_TILES) {
uint8_t *base = &gb->sgb->pending_border.tiles[gb->sgb->transfer_dest == TRANSFER_HIGH_TILES ? 0x80 * 8 * 8 : 0];
for (unsigned tile = 0; tile < 0x80; tile++) {
unsigned tile_x = (tile % 10) * 16;
unsigned tile_y = (tile / 10) * 8;
for (unsigned y = 0; y < 0x8; y++) {
for (unsigned x = 0; x < 0x8; x++) {
base[tile * 8 * 8 + y * 8 + x] = gb->sgb->screen_buffer[(tile_x + x) + (tile_y + y) * 160] +
gb->sgb->screen_buffer[(tile_x + x + 8) + (tile_y + y) * 160] * 4;
}
}
}
}
else {
unsigned size = 0;
uint16_t *data = NULL;
switch (gb->sgb->transfer_dest) {
case TRANSFER_PALETTES:
size = 0x100;
data = gb->sgb->ram_palettes;
break;
case TRANSFER_BORDER_DATA:
size = 0x88;
data = gb->sgb->pending_border.raw_data;
break;
case TRANSFER_ATTRIBUTES:
size = 0xFE;
data = (uint16_t *)gb->sgb->attribute_files;
break;
default:
return; // Corrupt state?
}
for (unsigned tile = 0; tile < size; tile++) {
unsigned tile_x = (tile % 20) * 8;
unsigned tile_y = (tile / 20) * 8;
for (unsigned y = 0; y < 0x8; y++) {
static const uint16_t pixel_to_bits[4] = {0x0000, 0x0080, 0x8000, 0x8080};
*data = 0;
for (unsigned x = 0; x < 8; x++) {
*data |= pixel_to_bits[gb->sgb->screen_buffer[(tile_x + x) + (tile_y + y) * 160] & 3] >> x;
}
#ifdef GB_BIG_ENDIAN
if (gb->sgb->transfer_dest == TRANSFER_ATTRIBUTES) {
*data = __builtin_bswap16(*data);
}
#endif
data++;
}
}
if (gb->sgb->transfer_dest == TRANSFER_BORDER_DATA) {
gb->sgb->border_animation = 64;
}
}
}
}
uint32_t colors[4 * 4];
for (unsigned i = 0; i < 4 * 4; i++) {
colors[i] = convert_rgb15(gb, gb->sgb->effective_palettes[i]);
}
if (gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) {
render_boot_animation(gb);
}
else {
uint32_t *output = &gb->screen[48 + 40 * 256];
uint8_t *input = gb->sgb->effective_screen_buffer;
switch ((mask_mode_t) gb->sgb->mask_mode) {
case MASK_DISABLED:
case MASK_FREEZE: {
for (unsigned y = 0; y < 144; y++) {
for (unsigned x = 0; x < 160; x++) {
uint8_t palette = gb->sgb->attribute_map[x / 8 + y / 8 * 20] & 3;
*(output++) = colors[(*(input++) & 3) + palette * 4];
}
output += 256 - 160;
}
break;
}
case MASK_BLACK:
{
uint32_t black = convert_rgb15(gb, 0);
for (unsigned y = 0; y < 144; y++) {
for (unsigned x = 0; x < 160; x++) {
*(output++) = black;
}
output += 256 - 160;
}
break;
}
case MASK_COLOR_0:
{
for (unsigned y = 0; y < 144; y++) {
for (unsigned x = 0; x < 160; x++) {
*(output++) = colors[0];
}
output += 256 - 160;
}
break;
}
}
}
uint32_t border_colors[16 * 4];
if (gb->sgb->border_animation == 0 || gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) {
for (unsigned i = 0; i < 16 * 4; i++) {
border_colors[i] = convert_rgb15(gb, gb->sgb->border.palette[i]);
}
}
else if (gb->sgb->border_animation > 32) {
gb->sgb->border_animation--;
for (unsigned i = 0; i < 16 * 4; i++) {
border_colors[i] = convert_rgb15_with_fade(gb, gb->sgb->border.palette[i], 64 - gb->sgb->border_animation);
}
}
else {
gb->sgb->border_animation--;
for (unsigned i = 0; i < 16 * 4; i++) {
border_colors[i] = convert_rgb15_with_fade(gb, gb->sgb->border.palette[i], gb->sgb->border_animation);
}
}
if (gb->sgb->border_animation == 32) {
memcpy(&gb->sgb->border, &gb->sgb->pending_border, sizeof(gb->sgb->border));
}
for (unsigned tile_y = 0; tile_y < 28; tile_y++) {
for (unsigned tile_x = 0; tile_x < 32; tile_x++) {
bool gb_area = false;
if (tile_x >= 6 && tile_x < 26 && tile_y >= 5 && tile_y < 23) {
gb_area = true;
}
uint16_t tile = gb->sgb->border.map[tile_x + tile_y * 32];
uint8_t flip_x = (tile & 0x4000)? 0x7 : 0;
uint8_t flip_y = (tile & 0x8000)? 0x7 : 0;
uint8_t palette = (tile >> 10) & 3;
for (unsigned y = 0; y < 8; y++) {
for (unsigned x = 0; x < 8; x++) {
uint8_t color = gb->sgb->border.tiles[(tile & 0xFF) * 64 + (x ^ flip_x) + (y ^ flip_y) * 8] & 0xF;
if (color == 0) {
if (gb_area) continue;
gb->screen[tile_x * 8 + x + (tile_y * 8 + y) * 0x100] = colors[0];
}
else {
gb->screen[tile_x * 8 + x + (tile_y * 8 + y) * 0x100] = border_colors[color + palette * 16];
}
}
}
}
}
}
void GB_sgb_load_default_data(GB_gameboy_t *gb)
{
#include "sgb_border.inc"
memcpy(gb->sgb->border.map, tilemap, sizeof(tilemap));
memcpy(gb->sgb->border.palette, palette, sizeof(palette));
/* Expend tileset */
for (unsigned tile = 0; tile < sizeof(tiles) / 32; tile++) {
for (unsigned y = 0; y < 8; y++) {
for (unsigned x = 0; x < 8; x++) {
gb->sgb->border.tiles[tile * 8 * 8 + y * 8 + x] =
(tiles[tile * 32 + y * 2 + 0] & (1 << (7 ^ x)) ? 1 : 0) |
(tiles[tile * 32 + y * 2 + 1] & (1 << (7 ^ x)) ? 2 : 0) |
(tiles[tile * 32 + y * 2 + 16] & (1 << (7 ^ x)) ? 4 : 0) |
(tiles[tile * 32 + y * 2 + 17] & (1 << (7 ^ x)) ? 8 : 0);
}
}
}
if (gb->model != GB_MODEL_SGB2) {
/* Delete the "2" */
gb->sgb->border.map[25 * 32 + 25] = gb->sgb->border.map[25 * 32 + 26] =
gb->sgb->border.map[26 * 32 + 25] = gb->sgb->border.map[26 * 32 + 26] =
gb->sgb->border.map[27 * 32 + 25] = gb->sgb->border.map[27 * 32 + 26] =
gb->sgb->border.map[0];
/* Re-center */
memmove(&gb->sgb->border.map[25 * 32 + 1], &gb->sgb->border.map[25 * 32], (32 * 3 - 1) * sizeof(gb->sgb->border.map[0]));
}
gb->sgb->effective_palettes[0] = built_in_palettes[0];
gb->sgb->effective_palettes[1] = built_in_palettes[1];
gb->sgb->effective_palettes[2] = built_in_palettes[2];
gb->sgb->effective_palettes[3] = built_in_palettes[3];
}
static double fm_synth(double phase)
{
return (sin(phase * M_PI * 2) +
sin(phase * M_PI * 2 + sin(phase * M_PI * 2)) +
sin(phase * M_PI * 2 + sin(phase * M_PI * 3)) +
sin(phase * M_PI * 2 + sin(phase * M_PI * 4))) / 4;
}
static double fm_sweep(double phase)
{
double ret = 0;
for (unsigned i = 0; i < 8; i++) {
ret += sin((phase * M_PI * 2 + sin(phase * M_PI * 8) / 4) * pow(1.25, i)) * (8 - i) / 36;
}
return ret;
}
static double random_double(void)
{
return ((signed)(GB_random32() % 0x10001) - 0x8000) / (double) 0x8000;
}
static void render_jingle(GB_gameboy_t *gb, size_t count)
{
const double frequencies[7] = {
466.16, // Bb4
587.33, // D5
698.46, // F5
830.61, // Ab5
1046.50, // C6
1244.51, // Eb6
1567.98, // G6
};
assert(gb->apu_output.sample_callback);
if (gb->sgb->intro_animation < 0) {
GB_sample_t sample = {0, 0};
for (unsigned i = 0; i < count; i++) {
gb->apu_output.sample_callback(gb, &sample);
}
return;
}
if (gb->sgb->intro_animation >= INTRO_ANIMATION_LENGTH) return;
signed jingle_stage = (gb->sgb->intro_animation - 64) / 3;
double sweep_cutoff_ratio = 2000.0 * pow(2, gb->sgb->intro_animation / 20.0) / gb->apu_output.sample_rate;
double sweep_phase_shift = 1000.0 * pow(2, gb->sgb->intro_animation / 40.0) / gb->apu_output.sample_rate;
if (sweep_cutoff_ratio > 1) {
sweep_cutoff_ratio = 1;
}
GB_sample_t stereo;
for (unsigned i = 0; i < count; i++) {
double sample = 0;
for (signed f = 0; f < 7 && f < jingle_stage; f++) {
sample += fm_synth(gb->sgb_intro_jingle_phases[f]) *
(0.75 * pow(0.5, jingle_stage - f) + 0.25) / 5.0;
gb->sgb_intro_jingle_phases[f] += frequencies[f] / gb->apu_output.sample_rate;
}
if (gb->sgb->intro_animation > 100) {
sample *= pow((INTRO_ANIMATION_LENGTH - gb->sgb->intro_animation) / (INTRO_ANIMATION_LENGTH - 100.0), 3);
}
if (gb->sgb->intro_animation < 120) {
double next = fm_sweep(gb->sgb_intro_sweep_phase) * 0.3 + random_double() * 0.7;
gb->sgb_intro_sweep_phase += sweep_phase_shift;
gb->sgb_intro_sweep_previous_sample = next * (sweep_cutoff_ratio) +
gb->sgb_intro_sweep_previous_sample * (1 - sweep_cutoff_ratio);
sample += gb->sgb_intro_sweep_previous_sample * pow((120 - gb->sgb->intro_animation) / 120.0, 2) * 0.8;
}
stereo.left = stereo.right = sample * 0x7000;
gb->apu_output.sample_callback(gb, &stereo);
}
return;
}

66
bsnes/gb/Core/sgb.h Normal file
View File

@@ -0,0 +1,66 @@
#ifndef sgb_h
#define sgb_h
#include "gb_struct_def.h"
#include <stdint.h>
#include <stdbool.h>
typedef struct GB_sgb_s GB_sgb_t;
#ifdef GB_INTERNAL
struct GB_sgb_s {
uint8_t command[16 * 7];
uint16_t command_write_index;
bool ready_for_pulse;
bool ready_for_write;
bool ready_for_stop;
bool disable_commands;
/* Screen buffer */
uint8_t screen_buffer[160 * 144]; // Live image from the Game Boy
uint8_t effective_screen_buffer[160 * 144]; // Image actually rendered to the screen
/* Multiplayer Input */
uint8_t player_count, current_player;
/* Mask */
uint8_t mask_mode;
/* Data Transfer */
uint8_t vram_transfer_countdown, transfer_dest;
/* Border */
struct {
uint8_t tiles[0x100 * 8 * 8]; /* High nibble not used*/
union {
struct {
uint16_t map[32 * 32];
uint16_t palette[16 * 4];
};
uint16_t raw_data[0x440];
};
} border, pending_border;
uint8_t border_animation;
/* Colorization */
uint16_t effective_palettes[4 * 4];
uint16_t ram_palettes[4 * 512];
uint8_t attribute_map[20 * 18];
uint8_t attribute_files[0xFE0];
/* Intro */
int16_t intro_animation;
/* GB Header */
uint8_t received_header[0x54];
/* Multiplayer (cont) */
bool mlt_lock;
};
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value);
void GB_sgb_render(GB_gameboy_t *gb);
void GB_sgb_load_default_data(GB_gameboy_t *gb);
#endif
#endif

View File

@@ -0,0 +1,563 @@
static uint8_t animation_logo[] = {
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3, 0x3, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x1, 0x3, 0x3, 0x1, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x1, 0x1, 0x1, 0x1, 0x3, 0x3, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x3, 0x3, 0x3, 0x3, 0x3, 0x1, 0x1, 0x0, 0x0, 0x0, 0x3, 0x3, 0x3, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x3, 0x3, 0x3, 0x3, 0x3, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x3, 0x3, 0x3, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4,
0x4, 0x4, 0x4, 0x3, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x3, 0x3, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xE, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4,
0x4, 0x4, 0x4, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x3, 0x3, 0x1, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xE, 0xE, 0x1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4, 0x4,
0x4, 0x4, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x3, 0x3, 0x1, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE, 0xE, 0xE, 0x1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4,
0x4, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3, 0x3, 0x3, 0x1, 0x1, 0x0, 0x1,
0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x7, 0x0, 0x0, 0x1, 0x9, 0x9, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1, 0xC, 0xC, 0xC, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE, 0xE, 0xE, 0xE, 0x1,
0x0, 0x0, 0xE, 0xE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4,
0x4, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x3, 0x1, 0x1, 0x0, 0x0, 0x5,
0x5, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x6, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x7, 0x7, 0x7, 0x9, 0x9, 0x9, 0x9, 0x9, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0x0, 0x0, 0x0, 0x0, 0xE, 0xE, 0xE, 0xE, 0x1,
0x1, 0xE, 0xE, 0xE, 0xE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4,
0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3, 0x3, 0x1, 0x0, 0x0, 0x5, 0x5,
0x5, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6, 0x6, 0x6, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1,
0x7, 0x7, 0x7, 0x9, 0x1, 0x1, 0x1, 0x9, 0x9, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC,
0xC, 0xC, 0xC, 0xC, 0x1, 0x1, 0xC, 0xC, 0x0, 0x0, 0x0, 0xE, 0xE, 0xE, 0xE, 0xE,
0xE, 0xE, 0xE, 0xE, 0xE, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x1, 0x1, 0x0, 0x1, 0x5, 0x5,
0x5, 0x1, 0x0, 0x0, 0x0, 0x0, 0x6, 0x6, 0x6, 0x6, 0x1, 0x0, 0x0, 0x0, 0x0, 0x7,
0x7, 0x7, 0x9, 0x1, 0x1, 0x0, 0x0, 0x9, 0x9, 0x1, 0x0, 0x0, 0x0, 0x0, 0xC, 0xC,
0xC, 0xC, 0x1, 0x1, 0x0, 0xC, 0xC, 0xC, 0x1, 0x0, 0x0, 0xE, 0xE, 0xE, 0xE, 0xE,
0xE, 0xE, 0xE, 0xE, 0xE, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x5, 0x5, 0x5,
0x5, 0x1, 0x0, 0x0, 0x0, 0x1, 0x6, 0x6, 0x6, 0x1, 0x1, 0x0, 0x0, 0x0, 0x7, 0x7,
0x7, 0x7, 0x1, 0x1, 0x0, 0x0, 0x1, 0x9, 0x9, 0x9, 0x0, 0x0, 0x0, 0x1, 0xC, 0xC,
0xC, 0x1, 0x1, 0x0, 0x0, 0xC, 0xC, 0xC, 0x1, 0x0, 0x0, 0x1, 0xD, 0x1, 0x1, 0x1,
0x1, 0xE, 0xE, 0xE, 0xE, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4,
0x4, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x5, 0x5, 0x5,
0x1, 0x1, 0x0, 0x0, 0x0, 0x6, 0x6, 0x6, 0x6, 0x1, 0x0, 0x0, 0x0, 0x0, 0x7, 0x7,
0x7, 0x1, 0x1, 0x0, 0x0, 0x0, 0x9, 0x9, 0x9, 0x1, 0x0, 0x0, 0x0, 0xC, 0xC, 0xC,
0xC, 0x1, 0x0, 0x0, 0xC, 0xC, 0xC, 0xC, 0x1, 0x0, 0x0, 0xD, 0xD, 0x1, 0x0, 0x0,
0xE, 0xE, 0xF, 0xF, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4,
0x4, 0x4, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x5, 0x5, 0x5,
0x1, 0x0, 0x0, 0x0, 0x1, 0x6, 0x6, 0x6, 0x1, 0x1, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7,
0x7, 0x1, 0x0, 0x0, 0x0, 0x0, 0x9, 0x9, 0x9, 0x1, 0x0, 0x0, 0xC, 0xC, 0xC, 0xC,
0x1, 0x1, 0x0, 0x1, 0xC, 0xC, 0xC, 0x1, 0x1, 0x0, 0x1, 0xD, 0x1, 0x1, 0x0, 0xF,
0xF, 0xF, 0xF, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4,
0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x5, 0x5, 0x5, 0x1,
0x1, 0x0, 0x0, 0x0, 0x6, 0x6, 0x6, 0x6, 0x1, 0x0, 0x0, 0x0, 0x1, 0x7, 0x7, 0x7,
0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x9, 0x9, 0x9, 0x1, 0x0, 0x0, 0xC, 0xC, 0xC, 0xC,
0x1, 0x0, 0x1, 0xC, 0xC, 0xC, 0xC, 0x1, 0x0, 0x0, 0xD, 0xD, 0x1, 0x0, 0x1, 0xF,
0xF, 0xF, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x6, 0x6, 0x1, 0x1,
0x0, 0x0, 0x0, 0x6, 0x6, 0x6, 0x6, 0x1, 0x0, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7, 0x7,
0x1, 0x0, 0x0, 0x0, 0x0, 0x9, 0x9, 0x9, 0x9, 0x1, 0x0, 0x1, 0xC, 0xC, 0xB, 0xB,
0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0x1, 0x0, 0x0, 0xD, 0xD, 0x1, 0x1, 0x0, 0xF, 0xF,
0xF, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1, 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x0, 0x1, 0x6, 0x6, 0x6, 0x1, 0x0,
0x0, 0x0, 0x1, 0x6, 0x6, 0x7, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x7, 0x7, 0x7, 0x1,
0x1, 0x0, 0x0, 0x0, 0x0, 0x9, 0x9, 0x9, 0x1, 0x1, 0x0, 0xC, 0xC, 0xB, 0xB, 0x1,
0xC, 0xC, 0xC, 0xC, 0x1, 0x1, 0x1, 0x0, 0x1, 0xD, 0x1, 0x1, 0x0, 0x1, 0xF, 0xF,
0xF, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x6, 0x6, 0x6, 0x1, 0x1, 0x0,
0x0, 0x0, 0x6, 0x6, 0x7, 0x7, 0x1, 0x0, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7, 0x8, 0x1,
0x0, 0x0, 0x0, 0x0, 0x9, 0x9, 0x9, 0x9, 0x1, 0x0, 0x0, 0xC, 0xB, 0xB, 0xB, 0x1,
0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0xD, 0xD, 0x1, 0x0, 0x0, 0xF, 0xF, 0xF,
0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5, 0x5, 0x5, 0x5, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1, 0x4, 0x4, 0x4, 0x1, 0x0, 0x6, 0x6, 0x6, 0x1, 0x0, 0x0,
0x0, 0x6, 0x6, 0x7, 0x7, 0x1, 0x1, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7, 0x8, 0x8, 0x1,
0x0, 0x0, 0x0, 0x1, 0x9, 0x9, 0xA, 0x1, 0x1, 0x0, 0xC, 0xB, 0xB, 0xB, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD, 0xD, 0x1, 0x1, 0x0, 0x0, 0xF, 0xF, 0xF,
0x1, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5, 0x5, 0x5, 0x5, 0x1, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4, 0x4, 0x1, 0x1, 0x6, 0x6, 0x6, 0x1, 0x0, 0x0,
0x1, 0x6, 0x7, 0x7, 0x7, 0x1, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7, 0x8, 0x8, 0xA, 0x1,
0x0, 0x0, 0x0, 0x9, 0x9, 0xA, 0xA, 0x1, 0x0, 0x1, 0xB, 0xB, 0xB, 0xD, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD, 0xD, 0x1, 0x1, 0x0, 0x0, 0x0, 0xF, 0xF, 0xF,
0x1, 0x0, 0x0, 0x0, 0xF, 0xF, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5, 0x5, 0x5, 0x5, 0x5, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4, 0x4, 0x1, 0x1, 0x6, 0x6, 0x1, 0x1, 0x0, 0x1,
0x6, 0x1, 0x7, 0x7, 0x7, 0x1, 0x0, 0x0, 0x1, 0x7, 0x7, 0x8, 0x8, 0xA, 0xA, 0x1,
0x0, 0x0, 0xA, 0xA, 0xA, 0xA, 0x1, 0x1, 0x0, 0xB, 0xB, 0x1, 0xD, 0xD, 0xD, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xD, 0xD, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF, 0xF,
0x1, 0x0, 0x0, 0x0, 0xF, 0xF, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5, 0x5, 0x5, 0x5, 0x5,
0x5, 0x1, 0x0, 0x0, 0x1, 0x4, 0x4, 0x1, 0x1, 0x0, 0x6, 0x6, 0x1, 0x0, 0x1, 0x6,
0x1, 0x1, 0x7, 0x7, 0x7, 0x1, 0x0, 0x1, 0x7, 0x7, 0x8, 0x8, 0x1, 0x1, 0xA, 0xA,
0x1, 0xA, 0xA, 0xA, 0xA, 0x1, 0x1, 0xB, 0xB, 0xB, 0x1, 0x1, 0x1, 0xD, 0xD, 0x1,
0x0, 0x0, 0x0, 0x1, 0xD, 0xD, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF, 0xF,
0x1, 0x0, 0x1, 0xF, 0xF, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x5, 0x5, 0x5,
0x5, 0x5, 0x5, 0x4, 0x4, 0x4, 0x1, 0x1, 0x0, 0x0, 0x6, 0x6, 0x6, 0x6, 0x6, 0x1,
0x1, 0x0, 0x1, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x8, 0x8, 0x8, 0x1, 0x0, 0x1, 0xA,
0xA, 0xA, 0xA, 0xB, 0xB, 0xB, 0xB, 0xB, 0xB, 0x1, 0x1, 0x0, 0x0, 0xD, 0xD, 0xD,
0xD, 0xD, 0xD, 0xD, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF,
0xF, 0xF, 0xF, 0xF, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x5,
0x5, 0x5, 0x5, 0x5, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6, 0x1, 0x1, 0x1,
0x0, 0x0, 0x0, 0x1, 0x7, 0x7, 0x7, 0x1, 0x8, 0x8, 0x8, 0x1, 0x1, 0x0, 0x0, 0x0,
0xB, 0xB, 0xB, 0xB, 0xB, 0xB, 0xB, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0xD,
0xD, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
0xF, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x8, 0x8, 0x8, 0x1, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x8, 0x8, 0x8, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2,
0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x8, 0x8, 0x1, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x1, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0,
0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2,
0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x8, 0x8, 0x1, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0,
0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2,
0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2,
0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x8, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1,
0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2,
0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2,
0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2,
0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2,
0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x1,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2,
0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2,
0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2,
0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2,
0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2,
0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2,
0x1, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2,
0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2,
0x1, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2,
0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2,
0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x2, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2,
0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2,
0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0,
0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x1, 0x0, 0x1, 0x2, 0x2,
0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0,
0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2,
0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2,
0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x2,
0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0,
0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2,
0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1,
0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0,
0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
0x1, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1,
0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2,
0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0,
0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1,
0x1, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1,
0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2,
0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0,
0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1,
0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1,
0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2,
0x2, 0x1, 0x1, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2,
0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0,
0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1,
0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2,
0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0,
0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0,
0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2,
0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0,
0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0,
0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0,
0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0,
0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x0,
0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0,
0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1,
0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1,
0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
};
static const unsigned animation_logo_height = sizeof(animation_logo) / 160;

View File

@@ -0,0 +1,658 @@
static const uint16_t palette[] = {
0x0000, 0x0011, 0x18C6, 0x001A, 0x318C, 0x39CE, 0x5294, 0x5AD6,
0x739C, 0x45A8, 0x4520, 0x18A5, 0x4631, 0x2033, 0x20EC, 0x18B7
};
static const uint16_t tilemap[] = {
0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001,
0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001,
0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001,
0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001,
0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002,
0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002,
0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002,
0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002,
0x1001, 0x1003, 0x1004, 0x1005, 0x1005, 0x1005, 0x1005, 0x1005,
0x1005, 0x1005, 0x1005, 0x1005, 0x1005, 0x1005, 0x1005, 0x1005,
0x1005, 0x1005, 0x1005, 0x1005, 0x1005, 0x1005, 0x1005, 0x1005,
0x1005, 0x1005, 0x1005, 0x1005, 0x1005, 0x5004, 0x5003, 0x1001,
0x1001, 0x1006, 0x1007, 0x1007, 0x1007, 0x1008, 0x1009, 0x100A,
0x100B, 0x100C, 0x100D, 0x100E, 0x100F, 0x1010, 0x1011, 0x1012,
0x1013, 0x1014, 0x1015, 0x100E, 0x1016, 0x1017, 0x1018, 0x1019,
0x101A, 0x101B, 0x101C, 0x1007, 0x1007, 0x1007, 0x5006, 0x1001,
0x1001, 0x101D, 0x101E, 0x101E, 0x101E, 0x101F, 0x1020, 0x1021,
0x1022, 0x1023, 0x1024, 0x1025, 0x5024, 0x1026, 0x1025, 0x1025,
0x1027, 0x1028, 0x1029, 0x102A, 0x102B, 0x102C, 0x102D, 0x102E,
0x102F, 0x1030, 0x1031, 0x101E, 0x101E, 0x101E, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1034, 0x1035, 0x5034, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x8034, 0x1036, 0xC034, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0x1037, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0x1038, 0x1001,
0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1039, 0x103A, 0x1001,
0x1001, 0x103B, 0x103C, 0x1032, 0x1032, 0xC03C, 0x103D, 0x103D,
0x103D, 0x103D, 0x103D, 0x103D, 0x103D, 0x103D, 0x103D, 0x103D,
0x103D, 0x103D, 0x103D, 0x103D, 0x103D, 0x103D, 0x103D, 0x103D,
0x103D, 0x103D, 0x103E, 0x103F, 0x1040, 0x1041, 0x1001, 0x1001,
0x1001, 0x1042, 0x1043, 0x1044, 0x1044, 0x1044, 0x1044, 0x1044,
0x1044, 0x1044, 0x1044, 0x1044, 0x1044, 0x1044, 0x1044, 0x1044,
0x1044, 0x1044, 0x1044, 0x1044, 0x1044, 0x1044, 0x1044, 0x1044,
0x1044, 0x1044, 0x1045, 0x1046, 0x1001, 0x1001, 0x1001, 0x1001,
0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1047, 0x1048, 0x1049,
0x104A, 0x104B, 0x104C, 0x104D, 0x104E, 0x104F, 0x1050, 0x1051,
0x1052, 0x1053, 0x1054, 0x1055, 0x1056, 0x1057, 0x1058, 0x1059,
0x105A, 0x105B, 0x105C, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001,
0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x105D, 0x105E, 0x105F,
0x1060, 0x1061, 0x1062, 0x1063, 0x1064, 0x1065, 0x1066, 0x1067,
0x1068, 0x1069, 0x106A, 0x106B, 0x106C, 0x106D, 0x106E, 0x106F,
0x1070, 0x1071, 0x1072, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001,
0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1073, 0x1074, 0x1075,
0x1076, 0x1077, 0x1078, 0x1079, 0x107A, 0x107B, 0x107C, 0x107D,
0x107E, 0x107F, 0x1080, 0x1081, 0x1082, 0x1083, 0x507A, 0x1084,
0x1001, 0x1085, 0x507A, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001,
};
const uint8_t tiles[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x00, 0xFF, 0x01, 0xFE, 0x06, 0xF9, 0x08, 0xF7,
0x11, 0xEF, 0x22, 0xDB, 0x20, 0xDB, 0x40, 0xB7,
0xFF, 0x00, 0xFF, 0x00, 0xFE, 0x00, 0xF8, 0x00,
0xF1, 0x00, 0xE6, 0x04, 0xE4, 0x00, 0xC8, 0x00,
0x7F, 0x80, 0x80, 0x7F, 0x00, 0xFF, 0x7F, 0xFF,
0x80, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0x80, 0x00, 0x00, 0x00, 0x7F, 0x00,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x40, 0xB7, 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF,
0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF,
0xC8, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00,
0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x03, 0xFF, 0x02, 0xFF,
0x02, 0xFF, 0x02, 0xFF, 0x02, 0xFF, 0x02, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00,
0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0xC1, 0xDD, 0x00, 0xC9,
0x14, 0xFF, 0x14, 0xFF, 0x14, 0xFF, 0x00, 0xC9,
0x00, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x36, 0x22,
0x14, 0x00, 0x14, 0x00, 0x14, 0x00, 0x36, 0x22,
0x00, 0xFF, 0x00, 0xFF, 0xC7, 0xDF, 0x01, 0xCF,
0x11, 0xFF, 0x11, 0xFF, 0x11, 0xFF, 0x01, 0xCF,
0x00, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x31, 0x20,
0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x31, 0x20,
0x00, 0xFF, 0x00, 0xFF, 0xC2, 0xFF, 0x03, 0xFF,
0x02, 0xFE, 0x02, 0xFE, 0x02, 0xFF, 0x02, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x03, 0x00,
0x03, 0x01, 0x03, 0x00, 0x02, 0x00, 0x02, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x08, 0xFF, 0x18, 0xFF,
0x08, 0x4E, 0x08, 0x4E, 0x09, 0x1F, 0x08, 0x1C,
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x18, 0x00,
0xB9, 0x10, 0xB9, 0xA1, 0xE9, 0xA0, 0xEB, 0x41,
0x00, 0xFF, 0x00, 0xFF, 0x4F, 0xFF, 0x02, 0x1F,
0x02, 0x4F, 0x02, 0x4F, 0xF2, 0xFF, 0x02, 0xE7,
0x00, 0x00, 0x00, 0x00, 0x4F, 0x00, 0xE2, 0xA0,
0xB2, 0xA0, 0xB2, 0x10, 0xF2, 0x00, 0x1A, 0x10,
0x00, 0xFF, 0x00, 0xFF, 0xBC, 0xFD, 0x22, 0xFB,
0x22, 0xFB, 0x3C, 0xFD, 0x24, 0xFF, 0x26, 0xF9,
0x00, 0x00, 0x00, 0x00, 0xBE, 0x00, 0x26, 0x00,
0x26, 0x00, 0x3E, 0x00, 0x24, 0x00, 0x26, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x50, 0xFF, 0x49, 0xEF,
0x49, 0xF0, 0x46, 0xFF, 0x49, 0xF0, 0x49, 0xEF,
0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x59, 0x00,
0x4F, 0x06, 0x46, 0x00, 0x4F, 0x06, 0x59, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x88, 0xFF, 0x00, 0x72,
0x00, 0xF2, 0x05, 0xFF, 0x00, 0xF8, 0x00, 0x78,
0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x8D, 0x08,
0x0D, 0x05, 0x05, 0x00, 0x07, 0x05, 0x87, 0x02,
0x00, 0xFF, 0x00, 0xFF, 0x8A, 0xFF, 0x02, 0x27,
0x02, 0x27, 0x52, 0xFF, 0x02, 0x8F, 0x02, 0x8F,
0x00, 0x00, 0x00, 0x00, 0x8A, 0x00, 0xDA, 0x88,
0xDA, 0x50, 0x52, 0x00, 0x72, 0x50, 0x72, 0x20,
0x00, 0xFF, 0x00, 0xFF, 0xFA, 0xFF, 0x22, 0xFF,
0x22, 0xFF, 0x23, 0xFF, 0x22, 0xFF, 0x22, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x22, 0x00,
0x22, 0x00, 0x23, 0x00, 0x22, 0x00, 0x22, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x20, 0xFF, 0x20, 0xFF,
0x20, 0xFF, 0xE0, 0xFF, 0x20, 0xFF, 0x20, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x20, 0x00,
0x20, 0x00, 0xE0, 0x00, 0x20, 0x00, 0x20, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x33, 0x37, 0x00, 0x77,
0x80, 0xFF, 0x20, 0x27, 0x08, 0xFF, 0x00, 0x77,
0x00, 0x00, 0x00, 0x00, 0xFB, 0x40, 0x88, 0x88,
0x80, 0x00, 0xF8, 0x50, 0x08, 0x00, 0x88, 0x88,
0x00, 0xFF, 0x00, 0xFF, 0xEF, 0xFF, 0x88, 0xFF,
0x88, 0xFF, 0x8F, 0xFF, 0x88, 0xFF, 0x88, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xEF, 0x00, 0x88, 0x00,
0x88, 0x00, 0x8F, 0x00, 0x88, 0x00, 0x88, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0xF9, 0xFD, 0x80, 0xF9,
0x84, 0xFF, 0xF4, 0xFF, 0x84, 0xFF, 0x80, 0xF9,
0x00, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x86, 0x02,
0x84, 0x00, 0xF4, 0x00, 0x84, 0x00, 0x86, 0x02,
0x00, 0xFF, 0x00, 0xFF, 0xC0, 0xDF, 0x00, 0xCF,
0x10, 0xFF, 0x10, 0xFF, 0x10, 0xFF, 0x00, 0xCF,
0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x30, 0x20,
0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x30, 0x20,
0x00, 0xFF, 0x00, 0xFF, 0x30, 0x36, 0x00, 0x74,
0x82, 0xFF, 0x22, 0x27, 0x0A, 0xFF, 0x00, 0x74,
0x00, 0x00, 0x00, 0x00, 0xF9, 0x40, 0x8B, 0x89,
0x82, 0x00, 0xFA, 0x50, 0x0A, 0x00, 0x8B, 0x89,
0x00, 0xFF, 0x00, 0xFF, 0xE2, 0xEF, 0x02, 0xE7,
0x0A, 0xFF, 0x0A, 0xFF, 0x0A, 0xFF, 0x00, 0xE4,
0x00, 0x00, 0x00, 0x00, 0xF2, 0x00, 0x1A, 0x10,
0x0A, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x1B, 0x12,
0x00, 0xFF, 0x00, 0xFF, 0x14, 0xFF, 0x16, 0xFF,
0x14, 0xFC, 0x15, 0xFE, 0x14, 0xFF, 0x04, 0xCF,
0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x16, 0x00,
0x17, 0x01, 0x15, 0x00, 0x14, 0x00, 0x34, 0x10,
0x00, 0xFF, 0x00, 0xFF, 0x2F, 0xFF, 0x28, 0xFF,
0x28, 0xFF, 0xA8, 0x7F, 0x28, 0x3F, 0x68, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x28, 0x00,
0x28, 0x00, 0xA8, 0x00, 0xE8, 0x80, 0x68, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0x7F, 0x00, 0x3F,
0x40, 0xFF, 0x40, 0xFF, 0x40, 0xFF, 0x00, 0x3F,
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xC0, 0x80,
0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xC0, 0x80,
0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF,
0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF,
0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00,
0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0xFF,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0xC1, 0xDD, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0xE3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0xC1, 0xDF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0xE1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x02, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x4A, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0x4A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x0A, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x22, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x82, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x20, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x60, 0x67, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0xF8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x8F, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0x8F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0xA2, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0xF9, 0xFD, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0xC0, 0xDF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x60, 0x66, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0xF9, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0xE0, 0xEE, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0xF1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0xC4, 0xDF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0xE4, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x2F, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x00, 0x7F, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x80, 0xFF,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF,
0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x3C, 0xFF,
0x7E, 0xFF, 0xE7, 0xE7, 0xFF, 0x7E, 0xFF, 0x7E,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x7E,
0x81, 0xC3, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00,
0xFF, 0x7E, 0xFF, 0x3C, 0xFF, 0x00, 0x7E, 0x81,
0x3C, 0xC3, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0xC3, 0x81,
0x7E, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xF7, 0x00, 0xF7, 0x00, 0xF7, 0x00, 0xF7,
0x00, 0xF7, 0x00, 0xED, 0x00, 0xED, 0x00, 0xED,
0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00,
0x09, 0x00, 0x11, 0x02, 0x11, 0x02, 0x11, 0x02,
0x00, 0xED, 0x00, 0xDB, 0x00, 0xDB, 0x00, 0xDB,
0x00, 0xB7, 0x00, 0xB7, 0x00, 0x6F, 0x00, 0x6F,
0x11, 0x02, 0x23, 0x04, 0x23, 0x04, 0x23, 0x04,
0x47, 0x08, 0x47, 0x08, 0x8F, 0x10, 0x8F, 0x10,
0x00, 0xFE, 0x00, 0xFD, 0x00, 0xFB, 0x00, 0xF7,
0x00, 0xEE, 0x00, 0xDD, 0x00, 0xBB, 0x00, 0x77,
0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00,
0x10, 0x01, 0x21, 0x02, 0x43, 0x04, 0x87, 0x08,
0x00, 0xDF, 0x00, 0xBF, 0x00, 0xBF, 0x00, 0x7F,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x1F, 0x20, 0x3F, 0x40, 0x3F, 0x40, 0x7F, 0x80,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xB7,
0x00, 0xB7, 0x00, 0xDB, 0x00, 0xDD, 0x00, 0xEE,
0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x88, 0x40,
0x88, 0x40, 0xC4, 0x20, 0xC2, 0x20, 0xE1, 0x10,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x7F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00,
0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x7F, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFC,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFC, 0x00, 0xE3, 0x00, 0x1F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0x00, 0x1C, 0x00, 0xE0, 0x00,
0x00, 0xFE, 0x00, 0xFD, 0x00, 0xF3, 0x00, 0xEF,
0x00, 0x1C, 0x00, 0xF3, 0x00, 0xEF, 0x00, 0x1F,
0x01, 0x00, 0x02, 0x00, 0x0C, 0x00, 0x10, 0x00,
0xE0, 0x03, 0x03, 0x0C, 0x0F, 0x10, 0x1F, 0xE0,
0x00, 0xEF, 0x00, 0xDF, 0x00, 0xBF, 0x00, 0x7F,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x0F, 0x10, 0x1F, 0x20, 0x3F, 0x40, 0x7F, 0x80,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x00, 0xF7, 0x00, 0xF9, 0x00, 0xFE, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xF0, 0x08, 0xF8, 0x06, 0xFE, 0x01, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x00, 0x80, 0x00, 0xFF, 0x00, 0x7F, 0x00, 0x80,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x7F, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x7F,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x00, 0x03, 0x00, 0xFF, 0x00, 0xFC, 0x00, 0x03,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFC, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xFC,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x00, 0xFC, 0x00, 0xE3, 0x00, 0x1F, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0x03, 0x03, 0x1C, 0x1F, 0xE0, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x01, 0xFF, 0x01, 0xFD, 0x03, 0xFF, 0x03, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x01, 0xFE, 0x02, 0xFE, 0x02, 0xFC, 0x00,
0x0E, 0xEE, 0x3F, 0xFF, 0x75, 0x71, 0xFB, 0xE7,
0xE3, 0xCB, 0xC7, 0x9F, 0x07, 0x3E, 0x84, 0x7C,
0xFB, 0x1B, 0xE6, 0x26, 0x8E, 0x82, 0x3E, 0x22,
0x7C, 0x54, 0x7D, 0x25, 0xF9, 0x40, 0xFB, 0x01,
0x00, 0xFF, 0x00, 0xFF, 0x80, 0xFF, 0x80, 0xFF,
0x00, 0x7F, 0x80, 0x4F, 0x31, 0x7F, 0x71, 0xFD,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x80, 0xFF, 0x80,
0xFF, 0x00, 0xFF, 0x30, 0xFF, 0xB1, 0xDE, 0x52,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0x7B, 0x87, 0xFF, 0x8E, 0xFE,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x84, 0xFA, 0x82, 0xF9, 0x88,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0xC1, 0xFD, 0xE3, 0x7B,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xC3, 0xBC, 0x24,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x01, 0xFF, 0x03, 0xFF, 0xE3, 0xFB, 0xF7, 0xBF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x01, 0xFE, 0x02, 0x7C, 0x64, 0xFC, 0xB4,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0x7F, 0x80, 0xFF, 0xA0, 0x2F, 0xF0, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x70, 0x8F, 0x80,
0x00, 0xFF, 0x00, 0xFF, 0x02, 0xFD, 0x00, 0xF7,
0x00, 0xFF, 0x11, 0xEE, 0x11, 0xEE, 0x10, 0xEF,
0xFF, 0x00, 0xFF, 0x00, 0xFC, 0x03, 0xF8, 0x0F,
0xF0, 0x0F, 0xE0, 0x1F, 0xE1, 0x1E, 0xE0, 0x1F,
0x00, 0xFF, 0x00, 0xFF, 0x10, 0xE7, 0x00, 0xFB,
0xC4, 0x3B, 0x98, 0x03, 0x00, 0xEF, 0x80, 0x7F,
0xFF, 0x00, 0xFF, 0x00, 0x0F, 0xF8, 0x07, 0xFC,
0x07, 0xF8, 0xEF, 0x74, 0xFF, 0x10, 0x7F, 0x80,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xEF, 0x00, 0xFF, 0x22, 0xDD, 0x06, 0xB9,
0xFF, 0x00, 0xFF, 0x00, 0xF8, 0x07, 0xF0, 0x0F,
0xF0, 0x1F, 0xE0, 0x1F, 0xC0, 0x3F, 0xC4, 0x7B,
0x00, 0xFF, 0x00, 0xFF, 0x80, 0x7D, 0x02, 0xFD,
0x02, 0xBD, 0x40, 0xBF, 0x40, 0xBF, 0x40, 0xBF,
0xFF, 0x00, 0xFF, 0x00, 0x7E, 0x83, 0x7C, 0x83,
0x7C, 0xC3, 0x7C, 0x83, 0x3C, 0xC3, 0x3C, 0xC3,
0x00, 0xFF, 0x00, 0xFF, 0x10, 0xEF, 0x00, 0xFF,
0x00, 0xF7, 0x00, 0xF7, 0x48, 0xB6, 0x48, 0xB7,
0xFF, 0x00, 0xFF, 0x00, 0x0F, 0xF0, 0x0F, 0xF0,
0x0F, 0xF8, 0x0F, 0xF8, 0x07, 0xF9, 0x06, 0xF9,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xBF, 0x02, 0xFC,
0x02, 0x7D, 0x02, 0xFD, 0x02, 0xFD, 0x20, 0xDD,
0xFF, 0x00, 0xFF, 0x00, 0xC1, 0x7E, 0x81, 0x7F,
0x81, 0xFE, 0x01, 0xFE, 0x03, 0xFC, 0x03, 0xFE,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xBF, 0x40, 0xBF,
0x47, 0xB8, 0x08, 0xF0, 0x08, 0xF7, 0x0F, 0xF0,
0xFF, 0x00, 0xFF, 0x00, 0xC0, 0x7F, 0x80, 0x7F,
0x80, 0x7F, 0x87, 0x7F, 0x87, 0x78, 0x80, 0x7F,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFB, 0x24, 0xCB,
0xE4, 0x1B, 0x00, 0x1F, 0x00, 0xFF, 0x80, 0x3F,
0xFF, 0x00, 0xFF, 0x00, 0x1C, 0xE7, 0x18, 0xF7,
0x18, 0xE7, 0xF8, 0xE7, 0xF8, 0x07, 0x78, 0xC7,
0x00, 0xFF, 0x00, 0xFF, 0x04, 0xF9, 0x00, 0xFF,
0x71, 0x8E, 0x89, 0x06, 0x81, 0x7E, 0xE1, 0x1E,
0xFF, 0x00, 0xFF, 0x00, 0x03, 0xFE, 0x01, 0xFE,
0x00, 0xFF, 0x70, 0xFF, 0x70, 0x8F, 0x01, 0xFE,
0x00, 0xFF, 0x00, 0xFF, 0x02, 0xF9, 0x00, 0xFF,
0x00, 0xFF, 0x03, 0xFC, 0x06, 0xB9, 0x44, 0xBB,
0xFF, 0x00, 0xFF, 0x00, 0xFC, 0x07, 0xF0, 0x0F,
0xE0, 0x1F, 0xC1, 0x3E, 0xC3, 0x7C, 0x87, 0x78,
0x00, 0xFF, 0x00, 0xFF, 0x08, 0xF7, 0x00, 0xFD,
0xC0, 0x3F, 0x11, 0x0E, 0x00, 0xFF, 0x08, 0xF7,
0xFF, 0x00, 0xFF, 0x00, 0x07, 0xF8, 0x03, 0xFE,
0x01, 0xFE, 0xE0, 0xFF, 0xF0, 0x0F, 0xF0, 0x0F,
0x00, 0xFF, 0x00, 0xFF, 0x08, 0x77, 0x40, 0xBF,
0x04, 0xBB, 0x00, 0xFE, 0x00, 0xDD, 0x00, 0x7F,
0xFF, 0x00, 0xFF, 0x00, 0x87, 0xF8, 0x87, 0x78,
0xC3, 0x7C, 0xC3, 0x3D, 0xE2, 0x3F, 0xE0, 0x9F,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFD, 0x06, 0xF9,
0x0C, 0x73, 0x08, 0xF7, 0x10, 0xE7, 0x20, 0xCF,
0xFF, 0x00, 0xFF, 0x00, 0xC3, 0x3E, 0x83, 0x7C,
0x87, 0xF8, 0x0F, 0xF0, 0x1F, 0xE8, 0x3F, 0xD0,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xF7, 0x00, 0xFF,
0x01, 0xDE, 0x06, 0xF8, 0x1C, 0xC3, 0x00, 0xF3,
0xFF, 0x00, 0xFF, 0x00, 0xF8, 0x0F, 0xE0, 0x1F,
0xE0, 0x3F, 0xC3, 0x3D, 0xE7, 0x38, 0xFF, 0x0C,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xDF, 0x00, 0xFF,
0x00, 0xF7, 0x08, 0x77, 0x08, 0xF7, 0x08, 0xF7,
0xFF, 0x00, 0xFF, 0x00, 0x3F, 0xE0, 0x0F, 0xF0,
0x0F, 0xF8, 0x87, 0xF8, 0x87, 0x78, 0x07, 0xF8,
0x03, 0xFF, 0x03, 0xFF, 0x01, 0xFF, 0x00, 0xFE,
0x18, 0xDF, 0x1C, 0xFD, 0x0F, 0xEF, 0x07, 0xF7,
0xFC, 0x00, 0xFE, 0x02, 0xFF, 0x01, 0xFF, 0x01,
0xFF, 0x38, 0xF3, 0x12, 0xF9, 0x19, 0xFC, 0x0C,
0x02, 0x79, 0x80, 0xFD, 0xC0, 0xDF, 0xF0, 0xFE,
0x79, 0x3F, 0x19, 0xDB, 0x19, 0xFB, 0xF9, 0xF7,
0xFF, 0x84, 0xFF, 0x82, 0x7F, 0x60, 0x9F, 0x91,
0xEF, 0xA9, 0xF6, 0x34, 0xFE, 0x1C, 0x1F, 0x11,
0x63, 0xEF, 0xF3, 0xEB, 0xC6, 0xCE, 0xEF, 0xDE,
0x8C, 0x9C, 0xDE, 0xBD, 0x9C, 0x9D, 0xFF, 0xEF,
0x9E, 0x02, 0xBC, 0xA4, 0x3D, 0x14, 0x7B, 0x4A,
0x73, 0x21, 0xF7, 0x94, 0xF7, 0xF6, 0xFE, 0xEE,
0x8D, 0xEC, 0x9E, 0x7D, 0x1C, 0x5B, 0x38, 0xFA,
0x79, 0xF7, 0x71, 0x75, 0xF3, 0xF3, 0xEF, 0xCF,
0xF3, 0x90, 0xF7, 0x14, 0xEF, 0xA8, 0xEF, 0x2D,
0xCF, 0x41, 0x8E, 0x8A, 0x3C, 0x3C, 0x39, 0x19,
0x67, 0xFF, 0xEF, 0xFE, 0xEC, 0xDC, 0xCF, 0xCF,
0xDD, 0xDC, 0xDC, 0x9F, 0x2C, 0x2F, 0xD7, 0xC7,
0xB9, 0x21, 0xBB, 0xAA, 0xB3, 0x81, 0x76, 0x76,
0x77, 0x76, 0xE7, 0xA4, 0xD7, 0x44, 0xFB, 0xCB,
0xB3, 0x37, 0x73, 0x72, 0xF4, 0xEC, 0xEF, 0xCD,
0xCD, 0x09, 0x11, 0xF3, 0x29, 0xA7, 0xF1, 0xCF,
0xCD, 0x49, 0xDF, 0xDE, 0xBF, 0xA5, 0x7F, 0x5D,
0xF6, 0x32, 0xFE, 0x14, 0xFE, 0x70, 0xFF, 0xC1,
0xF0, 0x77, 0xF0, 0x67, 0xE0, 0xCF, 0x80, 0x97,
0xC8, 0xBB, 0x98, 0xBB, 0x90, 0xD3, 0xE8, 0xE7,
0xDF, 0x58, 0xBF, 0x28, 0x7F, 0x50, 0x7F, 0x28,
0xF7, 0x84, 0xFF, 0xDC, 0xEF, 0xA4, 0xDF, 0xC0,
0x00, 0xFF, 0x04, 0xF3, 0x03, 0xF8, 0x00, 0xFF,
0x08, 0xF7, 0x03, 0xFC, 0x00, 0xBF, 0x18, 0xC7,
0xF0, 0x0F, 0xF8, 0x0F, 0xFE, 0x05, 0xFF, 0x00,
0xE7, 0x18, 0xC0, 0x3F, 0xC0, 0x7F, 0xE0, 0x3F,
0x00, 0xFF, 0x00, 0xFF, 0x08, 0xF6, 0x08, 0x77,
0x08, 0xF5, 0x08, 0xF7, 0x10, 0xE7, 0x70, 0x87,
0x1F, 0xE0, 0x0F, 0xF0, 0x07, 0xF9, 0x86, 0xF9,
0x86, 0x7B, 0x0C, 0xF3, 0x08, 0xFF, 0x38, 0xCF,
0x0A, 0xF1, 0x88, 0x77, 0x0E, 0xF1, 0x00, 0xFF,
0x00, 0xFF, 0x7F, 0x80, 0x41, 0xBE, 0x81, 0x3E,
0x84, 0x7F, 0x0E, 0xF1, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x3E, 0xC1, 0x7E, 0x81, 0x7E, 0xC1,
0x04, 0xFB, 0x04, 0xDB, 0x24, 0xDB, 0x20, 0xDF,
0x20, 0xDF, 0x00, 0xFF, 0x08, 0xE7, 0x19, 0xE6,
0x38, 0xC7, 0x38, 0xE7, 0x38, 0xC7, 0x18, 0xE7,
0x18, 0xE7, 0x18, 0xE7, 0x10, 0xFF, 0x10, 0xEF,
0x48, 0xB5, 0x80, 0x3F, 0x84, 0x7B, 0x80, 0x7F,
0xA1, 0x5E, 0x21, 0x5E, 0x02, 0x7C, 0x02, 0x7D,
0x46, 0xBB, 0x44, 0xFB, 0x40, 0xBF, 0x40, 0xBF,
0xC0, 0x3F, 0xC1, 0xBE, 0xE1, 0x9F, 0xE3, 0x9C,
0x60, 0x9D, 0x64, 0x99, 0x84, 0x3B, 0x84, 0x7B,
0x04, 0x7B, 0x40, 0xBB, 0x41, 0xBA, 0x09, 0xF2,
0x03, 0xFE, 0x43, 0xBE, 0x43, 0xFC, 0xC3, 0x3C,
0xC7, 0xB8, 0x87, 0x7C, 0x86, 0x7D, 0x86, 0x7D,
0x80, 0x7F, 0x80, 0x7F, 0x8F, 0x70, 0x10, 0xEF,
0x10, 0xEF, 0x1F, 0xE0, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x0F, 0xF0, 0x0F, 0xF0,
0x0F, 0xF0, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x48, 0xB7, 0x48, 0xB7, 0xC0, 0x3F, 0x01, 0xFE,
0x01, 0xFE, 0x81, 0x2E, 0x50, 0xAF, 0x50, 0xAF,
0x30, 0xCF, 0x30, 0xCF, 0xF0, 0x0F, 0xF0, 0x0F,
0xF0, 0x0F, 0x70, 0xDF, 0x20, 0xDF, 0x60, 0x9F,
0x06, 0xF8, 0x00, 0xFD, 0xF0, 0x0F, 0x00, 0x7E,
0x00, 0xEE, 0xE2, 0x1C, 0x02, 0xFD, 0x0C, 0xF1,
0x03, 0xFD, 0x03, 0xFE, 0xE1, 0x1E, 0xF1, 0x8F,
0xF1, 0x1F, 0x01, 0xFF, 0x03, 0xFC, 0x07, 0xFA,
0x08, 0xF3, 0x08, 0xF7, 0x08, 0xF7, 0x00, 0xFF,
0x40, 0xBB, 0x01, 0xFE, 0x20, 0xDF, 0x18, 0xE7,
0x87, 0x7C, 0x87, 0x78, 0x87, 0x78, 0x87, 0x78,
0x87, 0x7C, 0xC0, 0x3F, 0xE0, 0x1F, 0xF0, 0x0F,
0x08, 0xF7, 0x08, 0xF7, 0x01, 0xFE, 0x11, 0xEE,
0x01, 0xDE, 0x82, 0x7C, 0x04, 0xF9, 0x38, 0xC3,
0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xE0, 0x1F,
0xE1, 0x3E, 0x03, 0xFD, 0x07, 0xFA, 0x0F, 0xF4,
0x10, 0x6F, 0x00, 0x7F, 0x01, 0x7E, 0x01, 0xFE,
0x01, 0xFE, 0x11, 0xEE, 0x10, 0xEE, 0x12, 0xEC,
0xE0, 0x9F, 0xF0, 0x8F, 0xF0, 0x8F, 0xF0, 0x0F,
0xF0, 0x0F, 0xE1, 0x1E, 0xE1, 0x1F, 0xE1, 0x1F,
0x40, 0x9F, 0x80, 0x3F, 0x80, 0x7F, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x7F, 0xA0, 0x7F, 0xC0, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x00, 0xFF, 0x00, 0xFB, 0x08, 0xF7, 0x00, 0xFF,
0x01, 0xBE, 0x03, 0xFC, 0x00, 0x7F, 0x80, 0x7F,
0xFE, 0x01, 0xFC, 0x07, 0xF0, 0x0F, 0xE0, 0x1F,
0xC1, 0x7E, 0x80, 0x7F, 0x80, 0xFF, 0x00, 0xFF,
0x08, 0xF7, 0x10, 0xE7, 0x60, 0x8F, 0xC0, 0x3F,
0x80, 0x7F, 0xE0, 0x0F, 0x00, 0xEF, 0x00, 0xEF,
0x0F, 0xF0, 0x1F, 0xE8, 0x3F, 0xD0, 0x7F, 0x80,
0xFF, 0x00, 0x1F, 0xF0, 0x1F, 0xF0, 0x1F, 0xF0,
0x02, 0xF8, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x04, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xD0, 0xC6, 0x00, 0x1F, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0xC9, 0xFF, 0xE0, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xE7, 0x86, 0x01, 0x39, 0x01, 0xFF, 0x03, 0xFF,
0x03, 0xFF, 0x00, 0xFC, 0x00, 0xFE, 0x00, 0xFF,
0xFF, 0x9E, 0xFF, 0xC7, 0xFE, 0x00, 0xFE, 0x02,
0xFF, 0x03, 0xFF, 0x02, 0xFF, 0x01, 0xFF, 0x00,
0xC3, 0xD3, 0xC0, 0xBC, 0x80, 0xBF, 0x00, 0x7F,
0x80, 0x7F, 0x00, 0x7F, 0x00, 0xFF, 0x00, 0xFF,
0x7F, 0x6B, 0x7F, 0x03, 0xFF, 0xC0, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x80, 0xFF, 0x00, 0xFF, 0x00,
0xC7, 0x1B, 0x00, 0x7C, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x23, 0xFF, 0x83, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xC0, 0x1F, 0x00, 0x7F, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x20, 0xFF, 0x80, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x50, 0x4F, 0x00, 0x9F, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x40, 0xFF, 0x60, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x07, 0xF0, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x08, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xC7, 0x18, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x20, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x80, 0x7F, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xF7, 0x08, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x0C, 0xE1, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x12, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x38, 0x87, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x40, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x8F, 0x30, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x40, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xF0, 0x07, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x08, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x03, 0xF0, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x0C, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x0E, 0xF1, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x7F, 0x80, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00
};

1521
bsnes/gb/Core/sm83_cpu.c Normal file

File diff suppressed because it is too large Load Diff

11
bsnes/gb/Core/sm83_cpu.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef sm83_cpu_h
#define sm83_cpu_h
#include "gb_struct_def.h"
#include <stdint.h>
void GB_cpu_disassemble(GB_gameboy_t *gb, uint16_t pc, uint16_t count);
#ifdef GB_INTERNAL
void GB_cpu_run(GB_gameboy_t *gb);
#endif
#endif /* sm83_cpu_h */

View File

@@ -0,0 +1,788 @@
#include <stdio.h>
#include <stdbool.h>
#include "gb.h"
typedef void GB_opcode_t(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc);
static void ill(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, ".BYTE $%02x\n", opcode);
(*pc)++;
}
static void nop(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "NOP\n");
(*pc)++;
}
static void stop(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
uint8_t next = GB_read_memory(gb, (*pc)++);
if (next) {
GB_log(gb, "CORRUPTED STOP (%02x)\n", next);
}
else {
GB_log(gb, "STOP\n");
}
}
static char *register_names[] = {"af", "bc", "de", "hl", "sp"};
static void ld_rr_d16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
uint16_t value;
register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1;
value = GB_read_memory(gb, (*pc)++);
value |= GB_read_memory(gb, (*pc)++) << 8;
const char *symbol = GB_debugger_name_for_address(gb, value);
if (symbol) {
GB_log(gb, "LD %s, %s ; =$%04x\n", register_names[register_id], symbol, value);
}
else {
GB_log(gb, "LD %s, $%04x\n", register_names[register_id], value);
}
}
static void ld_drr_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1;
GB_log(gb, "LD [%s], a\n", register_names[register_id]);
}
static void inc_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1;
GB_log(gb, "INC %s\n", register_names[register_id]);
}
static void inc_hr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
(*pc)++;
register_id = ((opcode >> 4) + 1) & 0x03;
GB_log(gb, "INC %c\n", register_names[register_id][0]);
}
static void dec_hr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
(*pc)++;
register_id = ((opcode >> 4) + 1) & 0x03;
GB_log(gb, "DEC %c\n", register_names[register_id][0]);
}
static void ld_hr_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
(*pc)++;
register_id = ((opcode >> 4) + 1) & 0x03;
GB_log(gb, "LD %c, $%02x\n", register_names[register_id][0], GB_read_memory(gb, (*pc)++));
}
static void rlca(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "RLCA\n");
}
static void rla(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "RLA\n");
}
static void ld_da16_sp(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc){
uint16_t addr;
(*pc)++;
addr = GB_read_memory(gb, (*pc)++);
addr |= GB_read_memory(gb, (*pc)++) << 8;
const char *symbol = GB_debugger_name_for_address(gb, addr);
if (symbol) {
GB_log(gb, "LD [%s], sp ; =$%04x\n", symbol, addr);
}
else {
GB_log(gb, "LD [$%04x], sp\n", addr);
}
}
static void add_hl_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
(*pc)++;
register_id = (opcode >> 4) + 1;
GB_log(gb, "ADD hl, %s\n", register_names[register_id]);
}
static void ld_a_drr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1;
GB_log(gb, "LD a, [%s]\n", register_names[register_id]);
}
static void dec_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1;
GB_log(gb, "DEC %s\n", register_names[register_id]);
}
static void inc_lr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1;
GB_log(gb, "INC %c\n", register_names[register_id][1]);
}
static void dec_lr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1;
GB_log(gb, "DEC %c\n", register_names[register_id][1]);
}
static void ld_lr_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1;
GB_log(gb, "LD %c, $%02x\n", register_names[register_id][1], GB_read_memory(gb, (*pc)++));
}
static void rrca(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "RRCA\n");
(*pc)++;
}
static void rra(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "RRA\n");
(*pc)++;
}
static void jr_r8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
uint16_t addr = *pc + (int8_t) GB_read_memory(gb, (*pc)) + 1;
const char *symbol = GB_debugger_name_for_address(gb, addr);
if (symbol) {
GB_attributed_log(gb, GB_LOG_UNDERLINE, "JR %s ; =$%04x\n", symbol, addr);
}
else {
GB_attributed_log(gb, GB_LOG_UNDERLINE, "JR $%04x\n", addr);
}
(*pc)++;
}
static const char *condition_code(uint8_t opcode)
{
switch ((opcode >> 3) & 0x3) {
case 0:
return "nz";
case 1:
return "z";
case 2:
return "nc";
case 3:
return "c";
}
return NULL;
}
static void jr_cc_r8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
uint16_t addr = *pc + (int8_t) GB_read_memory(gb, (*pc)) + 1;
const char *symbol = GB_debugger_name_for_address(gb, addr);
if (symbol) {
GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "JR %s, %s ; =$%04x\n", condition_code(opcode), symbol, addr);
}
else {
GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "JR %s, $%04x\n", condition_code(opcode), addr);
}
(*pc)++;
}
static void daa(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "DAA\n");
(*pc)++;
}
static void cpl(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "CPL\n");
(*pc)++;
}
static void scf(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "SCF\n");
(*pc)++;
}
static void ccf(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "CCF\n");
(*pc)++;
}
static void ld_dhli_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "LD [hli], a\n");
(*pc)++;
}
static void ld_dhld_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "LD [hld], a\n");
(*pc)++;
}
static void ld_a_dhli(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "LD a, [hli]\n");
(*pc)++;
}
static void ld_a_dhld(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "LD a, [hld]\n");
(*pc)++;
}
static void inc_dhl(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "INC [hl]\n");
(*pc)++;
}
static void dec_dhl(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
GB_log(gb, "DEC [hl]\n");
(*pc)++;
}
static void ld_dhl_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "LD [hl], $%02x\n", GB_read_memory(gb, (*pc)++));
}
static const char *get_src_name(uint8_t opcode)
{
uint8_t src_register_id;
uint8_t src_low;
src_register_id = ((opcode >> 1) + 1) & 3;
src_low = (opcode & 1);
if (src_register_id == GB_REGISTER_AF) {
return src_low? "a": "[hl]";
}
if (src_low) {
return register_names[src_register_id] + 1;
}
static const char *high_register_names[] = {"a", "b", "d", "h"};
return high_register_names[src_register_id];
}
static const char *get_dst_name(uint8_t opcode)
{
uint8_t dst_register_id;
uint8_t dst_low;
dst_register_id = ((opcode >> 4) + 1) & 3;
dst_low = opcode & 8;
if (dst_register_id == GB_REGISTER_AF) {
return dst_low? "a": "[hl]";
}
if (dst_low) {
return register_names[dst_register_id] + 1;
}
static const char *high_register_names[] = {"a", "b", "d", "h"};
return high_register_names[dst_register_id];
}
static void ld_r_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "LD %s, %s\n", get_dst_name(opcode), get_src_name(opcode));
}
static void add_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "ADD %s\n", get_src_name(opcode));
}
static void adc_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "ADC %s\n", get_src_name(opcode));
}
static void sub_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "SUB %s\n", get_src_name(opcode));
}
static void sbc_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "SBC %s\n", get_src_name(opcode));
}
static void and_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "AND %s\n", get_src_name(opcode));
}
static void xor_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "XOR %s\n", get_src_name(opcode));
}
static void or_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "OR %s\n", get_src_name(opcode));
}
static void cp_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "CP %s\n", get_src_name(opcode));
}
static void halt(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "HALT\n");
}
static void ret_cc(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "RET %s\n", condition_code(opcode));
}
static void pop_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
register_id = ((GB_read_memory(gb, (*pc)++) >> 4) + 1) & 3;
GB_log(gb, "POP %s\n", register_names[register_id]);
}
static void jp_cc_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8);
const char *symbol = GB_debugger_name_for_address(gb, addr);
if (symbol) {
GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "JP %s, %s ; =$%04x\n", condition_code(opcode), symbol, addr);
}
else {
GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "JP %s, $%04x\n", condition_code(opcode), addr);
}
(*pc) += 2;
}
static void jp_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8);
const char *symbol = GB_debugger_name_for_address(gb, addr);
if (symbol) {
GB_log(gb, "JP %s ; =$%04x\n", symbol, addr);
}
else {
GB_log(gb, "JP $%04x\n", addr);
}
(*pc) += 2;
}
static void call_cc_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8);
const char *symbol = GB_debugger_name_for_address(gb, addr);
if (symbol) {
GB_log(gb, "CALL %s, %s ; =$%04x\n", condition_code(opcode), symbol, addr);
}
else {
GB_log(gb, "CALL %s, $%04x\n", condition_code(opcode), addr);
}
(*pc) += 2;
}
static void push_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t register_id;
register_id = ((GB_read_memory(gb, (*pc)++) >> 4) + 1) & 3;
GB_log(gb, "PUSH %s\n", register_names[register_id]);
}
static void add_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "ADD $%02x\n", GB_read_memory(gb, (*pc)++));
}
static void adc_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "ADC $%02x\n", GB_read_memory(gb, (*pc)++));
}
static void sub_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "SUB $%02x\n", GB_read_memory(gb, (*pc)++));
}
static void sbc_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "SBC $%02x\n", GB_read_memory(gb, (*pc)++));
}
static void and_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "AND $%02x\n", GB_read_memory(gb, (*pc)++));
}
static void xor_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "XOR $%02x\n", GB_read_memory(gb, (*pc)++));
}
static void or_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "OR $%02x\n", GB_read_memory(gb, (*pc)++));
}
static void cp_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "CP $%02x\n", GB_read_memory(gb, (*pc)++));
}
static void rst(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "RST $%02x\n", opcode ^ 0xC7);
}
static void ret(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_attributed_log(gb, GB_LOG_UNDERLINE, "RET\n");
}
static void reti(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_attributed_log(gb, GB_LOG_UNDERLINE, "RETI\n");
}
static void call_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8);
const char *symbol = GB_debugger_name_for_address(gb, addr);
if (symbol) {
GB_log(gb, "CALL %s ; =$%04x\n", symbol, addr);
}
else {
GB_log(gb, "CALL $%04x\n", addr);
}
(*pc) += 2;
}
static void ld_da8_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
uint8_t addr = GB_read_memory(gb, (*pc)++);
const char *symbol = GB_debugger_name_for_address(gb, 0xff00 + addr);
if (symbol) {
GB_log(gb, "LDH [%s & $FF], a ; =$%02x\n", symbol, addr);
}
else {
GB_log(gb, "LDH [$%02x], a\n", addr);
}
}
static void ld_a_da8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
uint8_t addr = GB_read_memory(gb, (*pc)++);
const char *symbol = GB_debugger_name_for_address(gb, 0xff00 + addr);
if (symbol) {
GB_log(gb, "LDH a, [%s & $FF] ; =$%02x\n", symbol, addr);
}
else {
GB_log(gb, "LDH a, [$%02x]\n", addr);
}
}
static void ld_dc_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "LDH [c], a\n");
}
static void ld_a_dc(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "LDH a, [c]\n");
}
static void add_sp_r8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
int8_t temp = GB_read_memory(gb, (*pc)++);
GB_log(gb, "ADD SP, %s$%02x\n", temp < 0? "-" : "", temp < 0? -temp : temp);
}
static void jp_hl(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "JP hl\n");
}
static void ld_da16_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8);
const char *symbol = GB_debugger_name_for_address(gb, addr);
if (symbol) {
GB_log(gb, "LD [%s], a ; =$%04x\n", symbol, addr);
}
else {
GB_log(gb, "LD [$%04x], a\n", addr);
}
(*pc) += 2;
}
static void ld_a_da16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8);
const char *symbol = GB_debugger_name_for_address(gb, addr);
if (symbol) {
GB_log(gb, "LD a, [%s] ; =$%04x\n", symbol, addr);
}
else {
GB_log(gb, "LD a, [$%04x]\n", addr);
}
(*pc) += 2;
}
static void di(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "DI\n");
}
static void ei(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "EI\n");
}
static void ld_hl_sp_r8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
int8_t temp = GB_read_memory(gb, (*pc)++);
GB_log(gb, "LD hl, sp, %s$%02x\n", temp < 0? "-" : "", temp < 0? -temp : temp);
}
static void ld_sp_hl(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "LD sp, hl\n");
}
static void rlc_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "RLC %s\n", get_src_name(opcode));
}
static void rrc_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "RRC %s\n", get_src_name(opcode));
}
static void rl_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "RL %s\n", get_src_name(opcode));
}
static void rr_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "RR %s\n", get_src_name(opcode));
}
static void sla_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "SLA %s\n", get_src_name(opcode));
}
static void sra_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "SRA %s\n", get_src_name(opcode));
}
static void srl_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "SRL %s\n", get_src_name(opcode));
}
static void swap_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
(*pc)++;
GB_log(gb, "SWAP %s\n", get_src_name(opcode));
}
static void bit_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
uint8_t bit;
(*pc)++;
bit = ((opcode >> 3) & 7);
if ((opcode & 0xC0) == 0x40) { /* Bit */
GB_log(gb, "BIT %s, %d\n", get_src_name(opcode), bit);
}
else if ((opcode & 0xC0) == 0x80) { /* res */
GB_log(gb, "RES %s, %d\n", get_src_name(opcode), bit);
}
else if ((opcode & 0xC0) == 0xC0) { /* set */
GB_log(gb, "SET %s, %d\n", get_src_name(opcode), bit);
}
}
static void cb_prefix(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{
opcode = GB_read_memory(gb, ++*pc);
switch (opcode >> 3) {
case 0:
rlc_r(gb, opcode, pc);
break;
case 1:
rrc_r(gb, opcode, pc);
break;
case 2:
rl_r(gb, opcode, pc);
break;
case 3:
rr_r(gb, opcode, pc);
break;
case 4:
sla_r(gb, opcode, pc);
break;
case 5:
sra_r(gb, opcode, pc);
break;
case 6:
swap_r(gb, opcode, pc);
break;
case 7:
srl_r(gb, opcode, pc);
break;
default:
bit_r(gb, opcode, pc);
break;
}
}
static GB_opcode_t *opcodes[256] = {
/* X0 X1 X2 X3 X4 X5 X6 X7 */
/* X8 X9 Xa Xb Xc Xd Xe Xf */
nop, ld_rr_d16, ld_drr_a, inc_rr, inc_hr, dec_hr, ld_hr_d8, rlca, /* 0X */
ld_da16_sp, add_hl_rr, ld_a_drr, dec_rr, inc_lr, dec_lr, ld_lr_d8, rrca,
stop, ld_rr_d16, ld_drr_a, inc_rr, inc_hr, dec_hr, ld_hr_d8, rla, /* 1X */
jr_r8, add_hl_rr, ld_a_drr, dec_rr, inc_lr, dec_lr, ld_lr_d8, rra,
jr_cc_r8, ld_rr_d16, ld_dhli_a, inc_rr, inc_hr, dec_hr, ld_hr_d8, daa, /* 2X */
jr_cc_r8, add_hl_rr, ld_a_dhli, dec_rr, inc_lr, dec_lr, ld_lr_d8, cpl,
jr_cc_r8, ld_rr_d16, ld_dhld_a, inc_rr, inc_dhl, dec_dhl, ld_dhl_d8, scf, /* 3X */
jr_cc_r8, add_hl_rr, ld_a_dhld, dec_rr, inc_hr, dec_hr, ld_hr_d8, ccf,
ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, /* 4X */
ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r,
ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, /* 5X */
ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r,
ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, /* 6X */
ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r,
ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, halt, ld_r_r, /* 7X */
ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r,
add_a_r, add_a_r, add_a_r, add_a_r, add_a_r, add_a_r, add_a_r, add_a_r, /* 8X */
adc_a_r, adc_a_r, adc_a_r, adc_a_r, adc_a_r, adc_a_r, adc_a_r, adc_a_r,
sub_a_r, sub_a_r, sub_a_r, sub_a_r, sub_a_r, sub_a_r, sub_a_r, sub_a_r, /* 9X */
sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r,
and_a_r, and_a_r, and_a_r, and_a_r, and_a_r, and_a_r, and_a_r, and_a_r, /* aX */
xor_a_r, xor_a_r, xor_a_r, xor_a_r, xor_a_r, xor_a_r, xor_a_r, xor_a_r,
or_a_r, or_a_r, or_a_r, or_a_r, or_a_r, or_a_r, or_a_r, or_a_r, /* bX */
cp_a_r, cp_a_r, cp_a_r, cp_a_r, cp_a_r, cp_a_r, cp_a_r, cp_a_r,
ret_cc, pop_rr, jp_cc_a16, jp_a16, call_cc_a16,push_rr, add_a_d8, rst, /* cX */
ret_cc, ret, jp_cc_a16, cb_prefix, call_cc_a16,call_a16, adc_a_d8, rst,
ret_cc, pop_rr, jp_cc_a16, ill, call_cc_a16,push_rr, sub_a_d8, rst, /* dX */
ret_cc, reti, jp_cc_a16, ill, call_cc_a16,ill, sbc_a_d8, rst,
ld_da8_a, pop_rr, ld_dc_a, ill, ill, push_rr, and_a_d8, rst, /* eX */
add_sp_r8, jp_hl, ld_da16_a, ill, ill, ill, xor_a_d8, rst,
ld_a_da8, pop_rr, ld_a_dc, di, ill, push_rr, or_a_d8, rst, /* fX */
ld_hl_sp_r8,ld_sp_hl, ld_a_da16, ei, ill, ill, cp_a_d8, rst,
};
void GB_cpu_disassemble(GB_gameboy_t *gb, uint16_t pc, uint16_t count)
{
const GB_bank_symbol_t *function_symbol = GB_debugger_find_symbol(gb, pc);
if (function_symbol && pc - function_symbol->addr > 0x1000) {
function_symbol = NULL;
}
if (function_symbol && pc != function_symbol->addr) {
GB_log(gb, "%s:\n", function_symbol->name);
}
uint16_t current_function = function_symbol? function_symbol->addr : 0;
while (count--) {
function_symbol = GB_debugger_find_symbol(gb, pc);
if (function_symbol && function_symbol->addr == pc) {
if (current_function != function_symbol->addr) {
GB_log(gb, "\n");
}
GB_log(gb, "%s:\n", function_symbol->name);
}
if (function_symbol) {
GB_log(gb, "%s%04x <+%03x>: ", pc == gb->pc? " ->": " ", pc, pc - function_symbol->addr);
}
else {
GB_log(gb, "%s%04x: ", pc == gb->pc? " ->": " ", pc);
}
uint8_t opcode = GB_read_memory(gb, pc);
opcodes[opcode](gb, opcode, &pc);
}
}

110
bsnes/gb/Core/symbol_hash.c Normal file
View File

@@ -0,0 +1,110 @@
#include "gb.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
static size_t GB_map_find_symbol_index(GB_symbol_map_t *map, uint16_t addr)
{
if (!map->symbols) {
return 0;
}
ssize_t min = 0;
ssize_t max = map->n_symbols;
while (min < max) {
size_t pivot = (min + max) / 2;
if (map->symbols[pivot].addr == addr) return pivot;
if (map->symbols[pivot].addr > addr) {
max = pivot;
}
else {
min = pivot + 1;
}
}
return (size_t) min;
}
GB_bank_symbol_t *GB_map_add_symbol(GB_symbol_map_t *map, uint16_t addr, const char *name)
{
size_t index = GB_map_find_symbol_index(map, addr);
if (index < map->n_symbols && map->symbols[index].addr == addr) return NULL;
map->symbols = realloc(map->symbols, (map->n_symbols + 1) * sizeof(map->symbols[0]));
memmove(&map->symbols[index + 1], &map->symbols[index], (map->n_symbols - index) * sizeof(map->symbols[0]));
map->symbols[index].addr = addr;
map->symbols[index].name = strdup(name);
map->n_symbols++;
return &map->symbols[index];
}
const GB_bank_symbol_t *GB_map_find_symbol(GB_symbol_map_t *map, uint16_t addr)
{
if (!map) return NULL;
size_t index = GB_map_find_symbol_index(map, addr);
if (index < map->n_symbols && map->symbols[index].addr != addr) {
index--;
}
if (index < map->n_symbols) {
return &map->symbols[index];
}
return NULL;
}
GB_symbol_map_t *GB_map_alloc(void)
{
GB_symbol_map_t *map = malloc(sizeof(*map));
memset(map, 0, sizeof(*map));
return map;
}
void GB_map_free(GB_symbol_map_t *map)
{
for (unsigned i = 0; i < map->n_symbols; i++) {
free(map->symbols[i].name);
}
if (map->symbols) {
free(map->symbols);
}
free(map);
}
static int hash_name(const char *name)
{
int r = 0;
while (*name) {
r <<= 1;
if (r & 0x400) {
r ^= 0x401;
}
r += (unsigned char)*(name++);
}
return r & 0x3FF;
}
void GB_reversed_map_add_symbol(GB_reversed_symbol_map_t *map, uint16_t bank, GB_bank_symbol_t *bank_symbol)
{
int hash = hash_name(bank_symbol->name);
GB_symbol_t *symbol = malloc(sizeof(*symbol));
symbol->name = bank_symbol->name;
symbol->addr = bank_symbol->addr;
symbol->bank = bank;
symbol->next = map->buckets[hash];
map->buckets[hash] = symbol;
}
const GB_symbol_t *GB_reversed_map_find_symbol(GB_reversed_symbol_map_t *map, const char *name)
{
int hash = hash_name(name);
GB_symbol_t *symbol = map->buckets[hash];
while (symbol) {
if (strcmp(symbol->name, name) == 0) return symbol;
symbol = symbol->next;
}
return NULL;
}

View File

@@ -0,0 +1,38 @@
#ifndef symbol_hash_h
#define symbol_hash_h
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
typedef struct {
char *name;
uint16_t addr;
} GB_bank_symbol_t;
typedef struct GB_symbol_s {
struct GB_symbol_s *next;
const char *name;
uint16_t bank;
uint16_t addr;
} GB_symbol_t;
typedef struct {
GB_bank_symbol_t *symbols;
size_t n_symbols;
} GB_symbol_map_t;
typedef struct {
GB_symbol_t *buckets[0x400];
} GB_reversed_symbol_map_t;
#ifdef GB_INTERNAL
void GB_reversed_map_add_symbol(GB_reversed_symbol_map_t *map, uint16_t bank, GB_bank_symbol_t *symbol);
const GB_symbol_t *GB_reversed_map_find_symbol(GB_reversed_symbol_map_t *map, const char *name);
GB_bank_symbol_t *GB_map_add_symbol(GB_symbol_map_t *map, uint16_t addr, const char *name);
const GB_bank_symbol_t *GB_map_find_symbol(GB_symbol_map_t *map, uint16_t addr);
GB_symbol_map_t *GB_map_alloc(void);
void GB_map_free(GB_symbol_map_t *map);
#endif
#endif /* symbol_hash_h */

294
bsnes/gb/Core/timing.c Normal file
View File

@@ -0,0 +1,294 @@
#include "gb.h"
#ifdef _WIN32
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0500
#endif
#include <windows.h>
#else
#include <sys/time.h>
#endif
static const unsigned GB_TAC_TRIGGER_BITS[] = {512, 8, 32, 128};
#ifndef DISABLE_TIMEKEEPING
static int64_t get_nanoseconds(void)
{
#ifndef _WIN32
struct timeval now;
gettimeofday(&now, NULL);
return (now.tv_usec) * 1000 + now.tv_sec * 1000000000L;
#else
FILETIME time;
GetSystemTimeAsFileTime(&time);
return (((int64_t)time.dwHighDateTime << 32) | time.dwLowDateTime) * 100L;
#endif
}
static void nsleep(uint64_t nanoseconds)
{
#ifndef _WIN32
struct timespec sleep = {0, nanoseconds};
nanosleep(&sleep, NULL);
#else
HANDLE timer;
LARGE_INTEGER time;
timer = CreateWaitableTimer(NULL, true, NULL);
time.QuadPart = -(nanoseconds / 100L);
SetWaitableTimer(timer, &time, 0, NULL, NULL, false);
WaitForSingleObject(timer, INFINITE);
CloseHandle(timer);
#endif
}
bool GB_timing_sync_turbo(GB_gameboy_t *gb)
{
if (!gb->turbo_dont_skip) {
int64_t nanoseconds = get_nanoseconds();
if (nanoseconds <= gb->last_sync + (1000000000LL * LCDC_PERIOD / GB_get_clock_rate(gb))) {
return true;
}
gb->last_sync = nanoseconds;
}
return false;
}
void GB_timing_sync(GB_gameboy_t *gb)
{
if (gb->turbo) {
gb->cycles_since_last_sync = 0;
return;
}
/* Prevent syncing if not enough time has passed.*/
if (gb->cycles_since_last_sync < LCDC_PERIOD / 3) return;
uint64_t target_nanoseconds = gb->cycles_since_last_sync * 1000000000LL / 2 / GB_get_clock_rate(gb); /* / 2 because we use 8MHz units */
int64_t nanoseconds = get_nanoseconds();
int64_t time_to_sleep = target_nanoseconds + gb->last_sync - nanoseconds;
if (time_to_sleep > 0 && time_to_sleep < LCDC_PERIOD * 1000000000LL / GB_get_clock_rate(gb)) {
nsleep(time_to_sleep);
gb->last_sync += target_nanoseconds;
}
else {
gb->last_sync = nanoseconds;
}
gb->cycles_since_last_sync = 0;
if (gb->update_input_hint_callback) {
gb->update_input_hint_callback(gb);
}
}
#else
bool GB_timing_sync_turbo(GB_gameboy_t *gb)
{
return false;
}
void GB_timing_sync(GB_gameboy_t *gb)
{
}
#endif
static void GB_ir_run(GB_gameboy_t *gb)
{
if (gb->ir_queue_length == 0) return;
if (gb->cycles_since_input_ir_change >= gb->ir_queue[0].delay) {
gb->cycles_since_input_ir_change -= gb->ir_queue[0].delay;
gb->infrared_input = gb->ir_queue[0].state;
gb->ir_queue_length--;
memmove(&gb->ir_queue[0], &gb->ir_queue[1], sizeof(gb->ir_queue[0]) * (gb->ir_queue_length));
}
}
static void advance_tima_state_machine(GB_gameboy_t *gb)
{
if (gb->tima_reload_state == GB_TIMA_RELOADED) {
gb->tima_reload_state = GB_TIMA_RUNNING;
}
else if (gb->tima_reload_state == GB_TIMA_RELOADING) {
gb->io_registers[GB_IO_IF] |= 4;
gb->tima_reload_state = GB_TIMA_RELOADED;
}
}
static void increase_tima(GB_gameboy_t *gb)
{
gb->io_registers[GB_IO_TIMA]++;
if (gb->io_registers[GB_IO_TIMA] == 0) {
gb->io_registers[GB_IO_TIMA] = gb->io_registers[GB_IO_TMA];
gb->tima_reload_state = GB_TIMA_RELOADING;
}
}
static void GB_set_internal_div_counter(GB_gameboy_t *gb, uint32_t value)
{
/* TIMA increases when a specific high-bit becomes a low-bit. */
value &= INTERNAL_DIV_CYCLES - 1;
uint32_t triggers = gb->div_counter & ~value;
if ((gb->io_registers[GB_IO_TAC] & 4) && (triggers & GB_TAC_TRIGGER_BITS[gb->io_registers[GB_IO_TAC] & 3])) {
increase_tima(gb);
}
/* TODO: Can switching to double speed mode trigger an event? */
if (triggers & (gb->cgb_double_speed? 0x2000 : 0x1000)) {
GB_apu_run(gb);
GB_apu_div_event(gb);
}
gb->div_counter = value;
}
static void GB_timers_run(GB_gameboy_t *gb, uint8_t cycles)
{
GB_STATE_MACHINE(gb, div, cycles, 1) {
GB_STATE(gb, div, 1);
GB_STATE(gb, div, 2);
GB_STATE(gb, div, 3);
}
GB_set_internal_div_counter(gb, 0);
main:
GB_SLEEP(gb, div, 1, 3);
while (true) {
advance_tima_state_machine(gb);
GB_set_internal_div_counter(gb, gb->div_counter + 4);
gb->apu.apu_cycles += 4 << !gb->cgb_double_speed;
GB_SLEEP(gb, div, 2, 4);
}
/* Todo: This is ugly to allow compatibility with 0.11 save states. Fix me when breaking save compatibility */
{
div3:
/* Compensate for lack of prefetch emulation, as well as DIV's internal initial value */
GB_set_internal_div_counter(gb, 8);
goto main;
}
}
static void advance_serial(GB_gameboy_t *gb, uint8_t cycles)
{
if (gb->serial_length == 0) {
gb->serial_cycles += cycles;
return;
}
while (cycles > gb->serial_length) {
advance_serial(gb, gb->serial_length);
cycles -= gb->serial_length;
}
uint16_t previous_serial_cycles = gb->serial_cycles;
gb->serial_cycles += cycles;
if ((gb->serial_cycles & gb->serial_length) != (previous_serial_cycles & gb->serial_length)) {
gb->serial_count++;
if (gb->serial_count == 8) {
gb->serial_length = 0;
gb->serial_count = 0;
gb->io_registers[GB_IO_SC] &= ~0x80;
gb->io_registers[GB_IO_IF] |= 8;
}
gb->io_registers[GB_IO_SB] <<= 1;
if (gb->serial_transfer_bit_end_callback) {
gb->io_registers[GB_IO_SB] |= gb->serial_transfer_bit_end_callback(gb);
}
else {
gb->io_registers[GB_IO_SB] |= 1;
}
if (gb->serial_length) {
/* Still more bits to send */
if (gb->serial_transfer_bit_start_callback) {
gb->serial_transfer_bit_start_callback(gb, gb->io_registers[GB_IO_SB] & 0x80);
}
}
}
return;
}
void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
{
// Affected by speed boost
gb->dma_cycles += cycles;
if (!gb->stopped) {
GB_timers_run(gb, cycles);
advance_serial(gb, cycles); // TODO: Verify what happens in STOP mode
}
gb->debugger_ticks += cycles;
if (!gb->cgb_double_speed) {
cycles <<= 1;
}
// Not affected by speed boost
gb->double_speed_alignment += cycles;
gb->hdma_cycles += cycles;
gb->apu_output.sample_cycles += cycles;
gb->cycles_since_ir_change += cycles;
gb->cycles_since_input_ir_change += cycles;
gb->cycles_since_last_sync += cycles;
gb->cycles_since_run += cycles;
if (!gb->stopped) { // TODO: Verify what happens in STOP mode
GB_dma_run(gb);
GB_hdma_run(gb);
}
GB_apu_run(gb);
GB_display_run(gb, cycles);
GB_ir_run(gb);
}
/*
This glitch is based on the expected results of mooneye-gb rapid_toggle test.
This glitch happens because how TIMA is increased, see GB_set_internal_div_counter.
According to GiiBiiAdvance, GBC's behavior is different, but this was not tested or implemented.
*/
void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac)
{
/* Glitch only happens when old_tac is enabled. */
if (!(old_tac & 4)) return;
unsigned old_clocks = GB_TAC_TRIGGER_BITS[old_tac & 3];
unsigned new_clocks = GB_TAC_TRIGGER_BITS[new_tac & 3];
/* The bit used for overflow testing must have been 1 */
if (gb->div_counter & old_clocks) {
/* And now either the timer must be disabled, or the new bit used for overflow testing be 0. */
if (!(new_tac & 4) || gb->div_counter & new_clocks) {
increase_tima(gb);
}
}
}
void GB_rtc_run(GB_gameboy_t *gb)
{
if ((gb->rtc_real.high & 0x40) == 0) { /* is timer running? */
time_t current_time = time(NULL);
while (gb->last_rtc_second < current_time) {
gb->last_rtc_second++;
if (++gb->rtc_real.seconds == 60)
{
gb->rtc_real.seconds = 0;
if (++gb->rtc_real.minutes == 60)
{
gb->rtc_real.minutes = 0;
if (++gb->rtc_real.hours == 24)
{
gb->rtc_real.hours = 0;
if (++gb->rtc_real.days == 0)
{
if (gb->rtc_real.high & 1) /* Bit 8 of days*/
{
gb->rtc_real.high |= 0x80; /* Overflow bit */
}
gb->rtc_real.high ^= 1;
}
}
}
}
}
}
}

44
bsnes/gb/Core/timing.h Normal file
View File

@@ -0,0 +1,44 @@
#ifndef timing_h
#define timing_h
#include "gb_struct_def.h"
#ifdef GB_INTERNAL
void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles);
void GB_rtc_run(GB_gameboy_t *gb);
void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac);
bool GB_timing_sync_turbo(GB_gameboy_t *gb); /* Returns true if should skip frame */
void GB_timing_sync(GB_gameboy_t *gb);
enum {
GB_TIMA_RUNNING = 0,
GB_TIMA_RELOADING = 1,
GB_TIMA_RELOADED = 2
};
#define GB_HALT_VALUE (0xFFFF)
#define GB_SLEEP(gb, unit, state, cycles) do {\
(gb)->unit##_cycles -= (cycles) * __state_machine_divisor; \
if ((gb)->unit##_cycles <= 0) {\
(gb)->unit##_state = state;\
return;\
unit##state:; \
}\
} while (0)
#define GB_HALT(gb, unit) (gb)->unit##_cycles = GB_HALT_VALUE
#define GB_STATE_MACHINE(gb, unit, cycles, divisor) \
static const int __state_machine_divisor = divisor;\
(gb)->unit##_cycles += cycles; \
if ((gb)->unit##_cycles <= 0 || (gb)->unit##_cycles == GB_HALT_VALUE) {\
return;\
}\
switch ((gb)->unit##_state)
#endif
#define GB_STATE(gb, unit, state) case state: goto unit##state
#define GB_UNIT(unit) int32_t unit##_cycles, unit##_state
#endif /* timing_h */

25
bsnes/gb/GNUmakefile Normal file
View File

@@ -0,0 +1,25 @@
flags += -DGB_INTERNAL -DDISABLE_DEBUGGER -D_GNU_SOURCE -Wno-multichar
options += -I../sameboy
objects += gb-apu gb-camera gb-display gb-gb gb-joypad gb-mbc
objects += gb-memory gb-printer gb-random gb-rewind gb-save_state gb-sgb
objects += gb-sm83_cpu gb-symbol_hash gb-timing
#objects+= gb-debugger gb-sm83_disassembler
obj/gb-apu.o: gb/Core/apu.c
obj/gb-camera.o: gb/Core/camera.c
obj/gb-debugger.o: gb/Core/debugger.c
obj/gb-display.o: gb/Core/display.c
obj/gb-gb.o: gb/Core/gb.c
obj/gb-joypad.o: gb/Core/joypad.c
obj/gb-mbc.o: gb/Core/mbc.c
obj/gb-memory.o: gb/Core/memory.c
obj/gb-printer.o: gb/Core/printer.c
obj/gb-random.o: gb/Core/random.c
obj/gb-rewind.o: gb/Core/rewind.c
obj/gb-save_state.o: gb/Core/save_state.c
obj/gb-sgb.o: gb/Core/sgb.c
obj/gb-sm83_cpu.o: gb/Core/sm83_cpu.c
obj/gb-sm83_disassembler.o: gb/Core/sm83_disassembler.c
obj/gb-symbol_hash.o: gb/Core/symbol_hash.c
obj/gb-timing.o: gb/Core/timing.c

21
bsnes/gb/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2015-2019 Lior Halphon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

5
bsnes/gb/README Normal file
View File

@@ -0,0 +1,5 @@
Core: 2019-10-22 master snapshot
* has issues with SGB2 audio desynchronization
Core-new: 2019-11-05 master snapshot
* Game Boy inputs are not working

View File

@@ -0,0 +1,33 @@
namespace Heuristics {
struct BSMemory {
BSMemory(vector<uint8_t>& data, string location);
explicit operator bool() const;
auto manifest() const -> string;
private:
vector<uint8_t>& data;
string location;
};
BSMemory::BSMemory(vector<uint8_t>& data, string location) : data(data), location(location) {
}
BSMemory::operator bool() const {
return data.size() >= 0x8000;
}
auto BSMemory::manifest() const -> string {
if(!operator bool()) return {};
string output;
output.append("game\n");
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
output.append(" label: ", Location::prefix(location), "\n");
output.append(" name: ", Location::prefix(location), "\n");
output.append(" board\n");
output.append(Memory{}.type("Flash").size(data.size()).content("Program").text());
return output;
}
}

View File

@@ -0,0 +1,298 @@
namespace Heuristics {
struct GameBoy {
GameBoy(vector<uint8_t>& data, string location);
explicit operator bool() const;
auto manifest() const -> string;
private:
auto read(uint offset) const -> uint8_t { return data[headerAddress + offset]; }
vector<uint8_t>& data;
string location;
uint headerAddress = 0;
};
GameBoy::GameBoy(vector<uint8_t>& data, string location) : data(data), location(location) {
headerAddress = data.size() < 0x8000 ? data.size() : data.size() - 0x8000;
if(read(0x0104) == 0xce && read(0x0105) == 0xed && read(0x0106) == 0x66 && read(0x0107) == 0x66
&& read(0x0108) == 0xcc && read(0x0109) == 0x0d && read(0x0147) >= 0x0b && read(0x0147) <= 0x0d
) { //MMM01 stores header at bottom of data[]
} else { //all other mappers store header at top of data[]
headerAddress = 0;
}
}
GameBoy::operator bool() const {
return data.size() >= 0x4000;
}
auto GameBoy::manifest() const -> string {
if(!operator bool()) return {};
bool black = (read(0x0143) & 0xc0) == 0x80; //cartridge works in DMG+CGB mode
bool clear = (read(0x0143) & 0xc0) == 0xc0; //cartridge works in CGB mode only
bool ram = false;
bool battery = false;
bool eeprom = false;
bool flash = false;
bool rtc = false;
bool accelerometer = false;
bool rumble = false;
uint romSize = 0;
uint ramSize = 0;
uint eepromSize = 0;
uint flashSize = 0;
uint rtcSize = 0;
string mapper = "MBC0";
switch(read(0x0147)) {
case 0x00:
mapper = "MBC0";
break;
case 0x01:
mapper = "MBC1";
break;
case 0x02:
mapper = "MBC1";
ram = true;
break;
case 0x03:
mapper = "MBC1";
battery = true;
ram = true;
break;
case 0x05:
mapper = "MBC2";
ram = true;
break;
case 0x06:
mapper = "MBC2";
battery = true;
ram = true;
break;
case 0x08:
mapper = "MBC0";
ram = true;
break;
case 0x09:
mapper = "MBC0";
battery = true;
ram = true;
break;
case 0x0b:
mapper = "MMM01";
break;
case 0x0c:
mapper = "MMM01";
ram = true;
break;
case 0x0d:
mapper = "MMM01";
battery = true;
ram = true;
break;
case 0x0f:
mapper = "MBC3";
battery = true;
rtc = true;
break;
case 0x10:
mapper = "MBC3";
battery = true;
ram = true;
rtc = true;
break;
case 0x11:
mapper = "MBC3";
break;
case 0x12:
mapper = "MBC3";
ram = true;
break;
case 0x13:
mapper = "MBC3";
battery = true;
ram = true;
break;
case 0x19:
mapper = "MBC5";
break;
case 0x1a:
mapper = "MBC5";
ram = true;
break;
case 0x1b:
mapper = "MBC5";
battery = true;
ram = true;
break;
case 0x1c:
mapper = "MBC5";
rumble = true;
break;
case 0x1d:
mapper = "MBC5";
ram = true;
rumble = true;
break;
case 0x1e:
mapper = "MBC5";
battery = true;
ram = true;
rumble = true;
break;
case 0x20:
mapper = "MBC6";
flash = true;
battery = true;
ram = true;
break;
case 0x22:
mapper = "MBC7";
battery = true;
eeprom = true;
accelerometer = true;
rumble = true;
break;
case 0xfc:
mapper = "CAMERA";
break;
case 0xfd:
mapper = "TAMA";
battery = true;
ram = true;
rtc = true;
break;
case 0xfe:
mapper = "HuC3";
break;
case 0xff:
mapper = "HuC1";
battery = true;
ram = true;
break;
}
//Game Boy: title = $0134-0143
//Game Boy Color (early games): title = $0134-0142; model = $0143
//Game Boy Color (later games): title = $0134-013e; serial = $013f-0142; model = $0143
string title;
for(uint n : range(black || clear ? 15 : 16)) {
char byte = read(0x0134 + n);
if(byte < 0x20 || byte > 0x7e) byte = ' ';
title.append(byte);
}
string serial = title.slice(-4);
if(!black && !clear) serial = "";
for(auto& byte : serial) {
if(byte >= 'A' && byte <= 'Z') continue;
//invalid serial
serial = "";
break;
}
title.trimRight(serial, 1L); //remove the serial from the title, if it exists
title.strip(); //remove any excess whitespace from the title
switch(read(0x0148)) { default:
case 0x00: romSize = 2 * 16 * 1024; break;
case 0x01: romSize = 4 * 16 * 1024; break;
case 0x02: romSize = 8 * 16 * 1024; break;
case 0x03: romSize = 16 * 16 * 1024; break;
case 0x04: romSize = 32 * 16 * 1024; break;
case 0x05: romSize = 64 * 16 * 1024; break;
case 0x06: romSize = 128 * 16 * 1024; break;
case 0x07: romSize = 256 * 16 * 1024; break;
case 0x52: romSize = 72 * 16 * 1024; break;
case 0x53: romSize = 80 * 16 * 1024; break;
case 0x54: romSize = 96 * 16 * 1024; break;
}
switch(read(0x0149)) { default:
case 0x00: ramSize = 0 * 1024; break;
case 0x01: ramSize = 2 * 1024; break;
case 0x02: ramSize = 8 * 1024; break;
case 0x03: ramSize = 32 * 1024; break;
}
if(mapper == "MBC2" && ram) ramSize = 256;
if(mapper == "MBC6" && ram) ramSize = 32 * 1024;
if(mapper == "TAMA" && ram) ramSize = 32;
if(mapper == "MBC6" && flash) flashSize = 1024 * 1024;
//Game Boy header does not specify EEPROM size: detect via game title instead
//Command Master: EEPROM = 512 bytes
//Kirby Tilt 'n' Tumble: EEPROM = 256 bytes
//Korokoro Kirby: EEPROM = 256 bytes
if(mapper == "MBC7" && eeprom) {
eepromSize = 256; //fallback guess; supported values are 128, 256, 512
if(title == "CMASTER" && serial == "KCEJ") eepromSize = 512;
if(title == "KIRBY TNT" && serial == "KTNE") eepromSize = 256;
if(title == "KORO2 KIRBY" && serial == "KKKJ") eepromSize = 256;
}
if(mapper == "MBC3" && rtc) rtcSize = 13;
if(mapper == "TAMA" && rtc) rtcSize = 21;
string output;
output.append("game\n");
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
output.append(" label: ", Location::prefix(location), "\n");
output.append(" name: ", Location::prefix(location), "\n");
output.append(" title: ", title, "\n");
if(serial)
output.append(" serial: ", serial, "\n");
output.append(" board: ", mapper, "\n");
output.append(Memory{}.type("ROM").size(data.size()).content("Program").text());
if(ram && ramSize && battery)
output.append(Memory{}.type("RAM").size(ramSize).content("Save").text());
if(ram && ramSize && !battery)
output.append(Memory{}.type("RAM").size(ramSize).content("Save").isVolatile().text());
if(eeprom && eepromSize)
output.append(Memory{}.type("EEPROM").size(eepromSize).content("Save").text());
if(flash && flashSize)
output.append(Memory{}.type("Flash").size(flashSize).content("Download").text());
if(rtc && rtcSize)
output.append(Memory{}.type("RTC").size(rtcSize).content("Time").text());
if(accelerometer)
output.append(" accelerometer\n");
if(rumble)
output.append(" rumble\n");
return output;
}
}

View File

@@ -0,0 +1,34 @@
namespace Heuristics {
auto Memory::text() const -> string {
string output;
output.append(" memory\n");
output.append(" type: ", _type, "\n");
output.append(" size: 0x", hex(_size), "\n");
output.append(" content: ", _content, "\n");
if(_manufacturer)
output.append(" manufacturer: ", _manufacturer, "\n");
if(_architecture)
output.append(" architecture: ", _architecture, "\n");
if(_identifier)
output.append(" identifier: ", _identifier, "\n");
if(_volatile)
output.append(" volatile\n");
return output;
}
auto Oscillator::text() const -> string {
string output;
output.append(" oscillator\n");
output.append(" frequency: ", _frequency, "\n");
return output;
}
auto Slot::text() const -> string {
string output;
output.append(" slot\n");
output.append(" type: ", _type, "\n");
return output;
}
}

View File

@@ -0,0 +1,37 @@
namespace Heuristics {
struct Memory {
auto& type(string type) { _type = type; return *this; }
auto& size(natural size) { _size = size; return *this; }
auto& content(string content) { _content = content; return *this; }
auto& manufacturer(string manufacturer) { _manufacturer = manufacturer; return *this; }
auto& architecture(string architecture) { _architecture = architecture; return *this; }
auto& identifier(string identifier) { _identifier = identifier; return *this; }
auto& isVolatile() { _volatile = true; return *this; }
auto text() const -> string;
string _type;
boolean _battery;
natural _size;
string _content;
string _manufacturer;
string _architecture;
string _identifier;
boolean _volatile;
};
struct Oscillator {
auto& frequency(natural frequency) { _frequency = frequency; return *this; }
auto text() const -> string;
natural _frequency;
};
struct Slot {
auto& type(string type) { _type = type; return *this; }
auto text() const -> string;
string _type;
};
}

View File

@@ -0,0 +1,39 @@
namespace Heuristics {
struct SufamiTurbo {
SufamiTurbo(vector<uint8_t>& data, string location);
explicit operator bool() const;
auto manifest() const -> string;
private:
vector<uint8_t>& data;
string location;
};
SufamiTurbo::SufamiTurbo(vector<uint8_t>& data, string location) : data(data), location(location) {
}
SufamiTurbo::operator bool() const {
return data.size() >= 0x20000;
}
auto SufamiTurbo::manifest() const -> string {
if(!operator bool()) return {};
uint romSize = data[0x36] * 0x20000; //128KB
uint ramSize = data[0x37] * 0x800; // 2KB
string output;
output.append("game\n");
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
output.append(" label: ", Location::prefix(location), "\n");
output.append(" name: ", Location::prefix(location), "\n");
output.append(" board\n");
output.append(Memory{}.type("ROM").size(data.size()).content("Program").text());
if(ramSize)
output.append(Memory{}.type("RAM").size(ramSize).content("Save").text());
return output;
}
}

View File

@@ -0,0 +1,581 @@
namespace Heuristics {
struct SuperFamicom {
SuperFamicom(vector<uint8_t>& data, string location);
explicit operator bool() const;
auto manifest() const -> string;
auto region() const -> string;
auto videoRegion() const -> string;
auto revision() const -> string;
auto board() const -> string;
auto title() const -> string;
auto serial() const -> string;
auto romSize() const -> uint;
auto programRomSize() const -> uint;
auto dataRomSize() const -> uint;
auto expansionRomSize() const -> uint;
auto firmwareRomSize() const -> uint;
auto ramSize() const -> uint;
auto expansionRamSize() const -> uint;
auto nonVolatile() const -> bool;
private:
auto size() const -> uint { return data.size(); }
auto scoreHeader(uint address) -> uint;
auto firmwareARM() const -> string;
auto firmwareEXNEC() const -> string;
auto firmwareGB() const -> string;
auto firmwareHITACHI() const -> string;
auto firmwareNEC() const -> string;
vector<uint8_t>& data;
string location;
uint headerAddress = 0;
};
SuperFamicom::SuperFamicom(vector<uint8_t>& data, string location) : data(data), location(location) {
if((size() & 0x7fff) == 512) {
//remove header if present
memory::move(&data[0], &data[512], size() - 512);
data.resize(size() - 512);
}
if(size() < 0x8000) return; //ignore images too small to be valid
uint LoROM = scoreHeader( 0x7fb0);
uint HiROM = scoreHeader( 0xffb0);
uint ExLoROM = scoreHeader(0x407fb0);
uint ExHiROM = scoreHeader(0x40ffb0);
if(ExLoROM) ExLoROM += 4;
if(ExHiROM) ExHiROM += 4;
if(LoROM >= HiROM && LoROM >= ExLoROM && LoROM >= ExHiROM) headerAddress = 0x7fb0;
else if(HiROM >= ExLoROM && HiROM >= ExHiROM) headerAddress = 0xffb0;
else if(ExLoROM >= ExHiROM) headerAddress = 0x407fb0;
else headerAddress = 0x40ffb0;
}
SuperFamicom::operator bool() const {
return headerAddress;
}
auto SuperFamicom::manifest() const -> string {
if(!operator bool()) return {};
string output;
output.append("game\n");
output.append(" sha256: ", Hash::SHA256(data).digest(), "\n");
output.append(" label: ", Location::prefix(location), "\n");
output.append(" name: ", Location::prefix(location), "\n");
output.append(" title: ", title(), "\n");
output.append(" region: ", region(), "\n");
output.append(" revision: ", revision(), "\n");
output.append(" board: ", board(), "\n");
auto board = this->board().trimRight("#A", 1L).split("-");
if(auto size = romSize()) {
if(board(0) == "SPC7110" && size > 0x100000) {
output.append(Memory{}.type("ROM").size(0x100000).content("Program").text());
output.append(Memory{}.type("ROM").size(size - 0x100000).content("Data").text());
} else if(board(0) == "EXSPC7110" && size == 0x700000) {
//Tengai Maykou Zero (fan translation)
output.append(Memory{}.type("ROM").size(0x100000).content("Program").text());
output.append(Memory{}.type("ROM").size(0x500000).content("Data").text());
output.append(Memory{}.type("ROM").size(0x100000).content("Expansion").text());
} else {
output.append(Memory{}.type("ROM").size(size).content("Program").text());
}
}
if(auto size = ramSize()) {
output.append(Memory{}.type("RAM").size(size).content("Save").text());
}
if(auto size = expansionRamSize()) {
output.append(Memory{}.type("RAM").size(size).content("Save").text());
}
if(0) {
} else if(board(0) == "ARM") {
output.append(Memory{}.type("ROM").size(0x20000).content("Program").manufacturer("SETA").architecture("ARM6").identifier(firmwareARM()).text());
output.append(Memory{}.type("ROM").size( 0x8000).content("Data" ).manufacturer("SETA").architecture("ARM6").identifier(firmwareARM()).text());
output.append(Memory{}.type("RAM").size( 0x4000).content("Data" ).manufacturer("SETA").architecture("ARM6").identifier(firmwareARM()).isVolatile().text());
output.append(Oscillator{}.frequency(21'440'000).text());
} else if(board(0) == "BS" && board(1) == "MCC") {
output.append(Memory{}.type("RAM").size(0x80000).content("Download").text());
} else if(board(0) == "EXNEC") {
output.append(Memory{}.type("ROM").size(0xc000).content("Program").manufacturer("NEC").architecture("uPD96050").identifier(firmwareEXNEC()).text());
output.append(Memory{}.type("ROM").size(0x1000).content("Data" ).manufacturer("NEC").architecture("uPD96050").identifier(firmwareEXNEC()).text());
output.append(Memory{}.type("RAM").size(0x1000).content("Data" ).manufacturer("NEC").architecture("uPD96050").identifier(firmwareEXNEC()).text());
output.append(Oscillator{}.frequency(firmwareEXNEC() == "ST010" ? 11'000'000 : 15'000'000).text());
} else if(board(0) == "GB") {
output.append(Memory{}.type("ROM").size(0x100).content("Boot").manufacturer("Nintendo").architecture("LR35902").identifier(firmwareGB()).text());
if(firmwareGB() == "SGB2")
output.append(Oscillator{}.frequency(20'971'520).text());
} else if(board(0) == "GSU") {
//todo: MARIO CHIP 1 uses CPU oscillator
output.append(Oscillator{}.frequency(21'440'000).text());
} else if(board(0) == "HITACHI") {
output.append(Memory{}.type("ROM").size(0xc00).content("Data").manufacturer("Hitachi").architecture("HG51BS169").identifier(firmwareHITACHI()).text());
output.append(Memory{}.type("RAM").size(0xc00).content("Data").manufacturer("Hitachi").architecture("HG51BS169").identifier(firmwareHITACHI()).isVolatile().text());
output.append(Oscillator{}.frequency(20'000'000).text());
} else if(board(0) == "NEC") {
output.append(Memory{}.type("ROM").size(0x1800).content("Program").manufacturer("NEC").architecture("uPD7725").identifier(firmwareNEC()).text());
output.append(Memory{}.type("ROM").size( 0x800).content("Data" ).manufacturer("NEC").architecture("uPD7725").identifier(firmwareNEC()).text());
output.append(Memory{}.type("RAM").size( 0x200).content("Data" ).manufacturer("NEC").architecture("uPD7725").identifier(firmwareNEC()).isVolatile().text());
output.append(Oscillator{}.frequency(7'600'000).text());
} else if(board(0) == "SA1" || board(1) == "SA1") { //SA1-* or BS-SA1-*
output.append(Memory{}.type("RAM").size(0x800).content("Internal").isVolatile().text());
}
if(board.right() == "EPSONRTC") {
output.append(Memory{}.type("RTC").size(0x10).content("Time").manufacturer("Epson").text());
} else if(board.right() == "SHARPRTC") {
output.append(Memory{}.type("RTC").size(0x10).content("Time").manufacturer("Sharp").text());
}
return output;
}
auto SuperFamicom::region() const -> string {
//Unlicensed software (homebrew, ROM hacks, etc) often change the standard region code,
//and then neglect to change the extended header region code. Thanks to that, we can't
//decode and display the full game serial + region code.
return videoRegion();
string region;
char A = data[headerAddress + 0x02]; //game type
char B = data[headerAddress + 0x03]; //game code
char C = data[headerAddress + 0x04]; //game code
char D = data[headerAddress + 0x05]; //region code (new; sometimes ambiguous)
auto E = data[headerAddress + 0x29]; //region code (old)
auto valid = [](char n) { return (n >= '0' && n <= '9') || (n >= 'A' && n <= 'Z'); };
if(data[headerAddress + 0x2a] == 0x33 && valid(A) && valid(B) & valid(C) & valid(D)) {
string code{A, B, C, D};
if(D == 'B') region = {"SNS-", code, "-BRA"};
if(D == 'C') region = {"SNSN-", code, "-ROC"};
if(D == 'D') region = {"SNSP-", code, "-NOE"};
if(D == 'E') region = {"SNS-", code, "-USA"};
if(D == 'F') region = {"SNSP-", code, "-FRA"};
if(D == 'H') region = {"SNSP-", code, "-HOL"};
if(D == 'I') region = {"SNSP-", code, "-ITA"};
if(D == 'J') region = {"SHVC-", code, "-JPN"};
if(D == 'K') region = {"SNSN-", code, "-KOR"};
if(D == 'N') region = {"SNS-", code, "-CAN"};
if(D == 'P') region = {"SNSP-", code, "-EUR"};
if(D == 'S') region = {"SNSP-", code, "-ESP"};
if(D == 'U') region = {"SNSP-", code, "-AUS"};
if(D == 'X') region = {"SNSP-", code, "-SCN"};
}
if(!region) {
if(E == 0x00) region = {"JPN"};
if(E == 0x01) region = {"USA"};
if(E == 0x02) region = {"EUR"};
if(E == 0x03) region = {"SCN"};
if(E == 0x06) region = {"FRA"};
if(E == 0x07) region = {"HOL"};
if(E == 0x08) region = {"ESP"};
if(E == 0x09) region = {"NOE"};
if(E == 0x0a) region = {"ITA"};
if(E == 0x0b) region = {"ROC"};
if(E == 0x0d) region = {"KOR"};
if(E == 0x0f) region = {"CAN"};
if(E == 0x10) region = {"BRA"};
if(E == 0x11) region = {"AUS"};
if(E == 0x12) region = {"SCN"};
}
return region ? region : "NTSC";
}
auto SuperFamicom::videoRegion() const -> string {
auto region = data[headerAddress + 0x29];
if(region == 0x00) return "NTSC"; //JPN
if(region == 0x01) return "NTSC"; //USA
if(region == 0x0b) return "NTSC"; //ROC
if(region == 0x0d) return "NTSC"; //KOR
if(region == 0x0f) return "NTSC"; //CAN
if(region == 0x10) return "NTSC"; //BRA
return "PAL";
}
auto SuperFamicom::revision() const -> string {
string revision;
char A = data[headerAddress + 0x02]; //game type
char B = data[headerAddress + 0x03]; //game code
char C = data[headerAddress + 0x04]; //game code
char D = data[headerAddress + 0x05]; //region code (new; sometimes ambiguous)
auto E = data[headerAddress + 0x29]; //region code (old)
uint F = data[headerAddress + 0x2b]; //revision code
auto valid = [](char n) { return (n >= '0' && n <= '9') || (n >= 'A' && n <= 'Z'); };
if(data[headerAddress + 0x2a] == 0x33 && valid(A) && valid(B) & valid(C) & valid(D)) {
string code{A, B, C, D};
if(D == 'B') revision = {"SNS-", code, "-", F};
if(D == 'C') revision = {"SNSN-", code, "-", F};
if(D == 'D') revision = {"SNSP-", code, "-", F};
if(D == 'E') revision = {"SNS-", code, "-", F};
if(D == 'F') revision = {"SNSP-", code, "-", F};
if(D == 'H') revision = {"SNSP-", code, "-", F};
if(D == 'I') revision = {"SNSP-", code, "-", F};
if(D == 'J') revision = {"SHVC-", code, "-", F};
if(D == 'K') revision = {"SNSN-", code, "-", F};
if(D == 'N') revision = {"SNS-", code, "-", F};
if(D == 'P') revision = {"SNSP-", code, "-", F};
if(D == 'S') revision = {"SNSP-", code, "-", F};
if(D == 'U') revision = {"SNSP-", code, "-", F};
if(D == 'X') revision = {"SNSP-", code, "-", F};
}
if(!revision) {
revision = {"1.", F};
}
return revision ? revision : string{"1.", F};
}
//format: [slot]-[coprocessor]-[mapper]-[ram]-[rtc]
auto SuperFamicom::board() const -> string {
string board;
auto mapMode = data[headerAddress + 0x25];
auto cartridgeTypeLo = data[headerAddress + 0x26] & 15;
auto cartridgeTypeHi = data[headerAddress + 0x26] >> 4;
auto cartridgeSubType = data[headerAddress + 0x0f];
string mode;
if(mapMode == 0x20 || mapMode == 0x30) mode = "LOROM-";
if(mapMode == 0x21 || mapMode == 0x31) mode = "HIROM-";
if(mapMode == 0x22 || mapMode == 0x32) mode = "SDD1-";
if(mapMode == 0x23 || mapMode == 0x33) mode = "SA1-";
if(mapMode == 0x25 || mapMode == 0x35) mode = "EXHIROM-";
if(mapMode == 0x2a || mapMode == 0x3a) mode = "SPC7110-";
//many games will store an extra title character, overwriting the map mode
//further, ExLoROM mode is unofficial, and lacks a mapping mode value
if(!mode) {
if(headerAddress == 0x7fb0) mode = "LOROM-";
if(headerAddress == 0xffb0) mode = "HIROM-";
if(headerAddress == 0x407fb0) mode = "EXLOROM-";
if(headerAddress == 0x40ffb0) mode = "EXHIROM-";
}
//this game's title ovewrites the map mode with '!' (0x21), but is a LOROM game
if(title() == "YUYU NO QUIZ DE GO!GO") mode = "LOROM-";
if(mode == "LOROM-" && headerAddress == 0x407fb0) mode = "EXLOROM-";
bool epsonRTC = false;
bool sharpRTC = false;
if(serial() == "A9PJ") {
//Sufami Turbo (JPN)
board.append("ST-", mode);
} else if(serial() == "ZBSJ") {
//BS-X: Sore wa Namae o Nusumareta Machi no Monogatari (JPN)
board.append("BS-MCC-");
} else if(serial() == "042J") {
//Super Game Boy 2
board.append("GB-", mode);
} else if(serial().match("Z??J")) {
board.append("BS-", mode);
} else if(cartridgeTypeLo >= 0x3) {
if(cartridgeTypeHi == 0x0) board.append("NEC-", mode);
if(cartridgeTypeHi == 0x1) board.append("GSU-");
if(cartridgeTypeHi == 0x2) board.append("OBC1-", mode);
if(cartridgeTypeHi == 0x3) board.append("SA1-");
if(cartridgeTypeHi == 0x4) board.append("SDD1-");
if(cartridgeTypeHi == 0x5) board.append(mode), sharpRTC = true;
if(cartridgeTypeHi == 0xe && cartridgeTypeLo == 0x3) board.append("GB-", mode);
if(cartridgeTypeHi == 0xf && cartridgeTypeLo == 0x5 && cartridgeSubType == 0x00) board.append("SPC7110-");
if(cartridgeTypeHi == 0xf && cartridgeTypeLo == 0x9 && cartridgeSubType == 0x00) board.append("SPC7110-"), epsonRTC = true;
if(cartridgeTypeHi == 0xf && cartridgeSubType == 0x01) board.append("EXNEC-", mode);
if(cartridgeTypeHi == 0xf && cartridgeSubType == 0x02) board.append("ARM-", mode);
if(cartridgeTypeHi == 0xf && cartridgeSubType == 0x10) board.append("HITACHI-", mode);
}
if(!board) board.append(mode);
if(ramSize() || expansionRamSize()) board.append("RAM-");
if(epsonRTC) board.append("EPSONRTC-");
if(sharpRTC) board.append("SHARPRTC-");
board.trimRight("-", 1L);
if(board.beginsWith( "LOROM-RAM") && romSize() <= 0x200000) board.append("#A");
if(board.beginsWith("NEC-LOROM-RAM") && romSize() <= 0x100000) board.append("#A");
//Tengai Makyou Zero (fan translation)
if(board.beginsWith("SPC7110-") && data.size() == 0x700000) board.prepend("EX");
return board;
}
auto SuperFamicom::title() const -> string {
string label;
for(uint n = 0; n < 0x15; n++) {
auto x = data[headerAddress + 0x10 + n];
auto y = n == 0x14 ? 0 : data[headerAddress + 0x11 + n];
//null terminator (padding)
if(x == 0x00 || x == 0xff);
//ASCII
else if(x >= 0x20 && x <= 0x7e) label.append((char)x);
//Shift-JIS (half-width katakana)
else if(x == 0xa1) label.append("");
else if(x == 0xa2) label.append("");
else if(x == 0xa3) label.append("");
else if(x == 0xa4) label.append("");
else if(x == 0xa5) label.append("");
else if(x == 0xa6) label.append("");
else if(x == 0xa7) label.append("");
else if(x == 0xa8) label.append("");
else if(x == 0xa9) label.append("");
else if(x == 0xaa) label.append("");
else if(x == 0xab) label.append("");
else if(x == 0xac) label.append("");
else if(x == 0xad) label.append("");
else if(x == 0xae) label.append("");
else if(x == 0xaf) label.append("");
else if(x == 0xb0) label.append("");
else if(x == 0xb1) label.append( "");
else if(x == 0xb2) label.append( "");
else if(x == 0xb3) label.append(y == 0xde ? "" : "");
else if(x == 0xb4) label.append( "");
else if(x == 0xb5) label.append( "");
else if(x == 0xb6) label.append(y == 0xde ? "" : "");
else if(x == 0xb7) label.append(y == 0xde ? "" : "");
else if(x == 0xb8) label.append(y == 0xde ? "" : "");
else if(x == 0xb9) label.append(y == 0xde ? "" : "");
else if(x == 0xba) label.append(y == 0xde ? "" : "");
else if(x == 0xbb) label.append(y == 0xde ? "" : "");
else if(x == 0xbc) label.append(y == 0xde ? "" : "");
else if(x == 0xbd) label.append(y == 0xde ? "" : "");
else if(x == 0xbe) label.append(y == 0xde ? "" : "");
else if(x == 0xbf) label.append(y == 0xde ? "" : "");
else if(x == 0xc0) label.append(y == 0xde ? "" : "");
else if(x == 0xc1) label.append(y == 0xde ? "" : "");
else if(x == 0xc2) label.append(y == 0xde ? "" : "");
else if(x == 0xc3) label.append(y == 0xde ? "" : "");
else if(x == 0xc4) label.append(y == 0xde ? "" : "");
else if(x == 0xc5) label.append("");
else if(x == 0xc6) label.append("");
else if(x == 0xc7) label.append("");
else if(x == 0xc8) label.append("");
else if(x == 0xc9) label.append("");
else if(x == 0xca) label.append(y == 0xdf ? "" : y == 0xde ? "" : "");
else if(x == 0xcb) label.append(y == 0xdf ? "" : y == 0xde ? "" : "");
else if(x == 0xcc) label.append(y == 0xdf ? "" : y == 0xde ? "" : "");
else if(x == 0xcd) label.append(y == 0xdf ? "" : y == 0xde ? "" : "");
else if(x == 0xce) label.append(y == 0xdf ? "" : y == 0xde ? "" : "");
else if(x == 0xcf) label.append("");
else if(x == 0xd0) label.append("");
else if(x == 0xd1) label.append("");
else if(x == 0xd2) label.append("");
else if(x == 0xd3) label.append("");
else if(x == 0xd4) label.append("");
else if(x == 0xd5) label.append("");
else if(x == 0xd6) label.append("");
else if(x == 0xd7) label.append("");
else if(x == 0xd8) label.append("");
else if(x == 0xd9) label.append("");
else if(x == 0xda) label.append("");
else if(x == 0xdb) label.append("");
else if(x == 0xdc) label.append("");
else if(x == 0xdd) label.append("");
else if(x == 0xde) label.append("\xef\xbe\x9e"); //dakuten
else if(x == 0xdf) label.append("\xef\xbe\x9f"); //handakuten
//unknown
else label.append("?");
//(han)dakuten skip
if(y == 0xde && x == 0xb3) n++;
if(y == 0xde && x >= 0xb6 && x <= 0xc4) n++;
if(y == 0xde && x >= 0xca && x <= 0xce) n++;
if(y == 0xdf && x >= 0xca && y <= 0xce) n++;
}
return label.strip();
}
auto SuperFamicom::serial() const -> string {
char A = data[headerAddress + 0x02]; //game type
char B = data[headerAddress + 0x03]; //game code
char C = data[headerAddress + 0x04]; //game code
char D = data[headerAddress + 0x05]; //region code (new; sometimes ambiguous)
auto valid = [](char n) { return (n >= '0' && n <= '9') || (n >= 'A' && n <= 'Z'); };
if(data[headerAddress + 0x2a] == 0x33 && valid(A) && valid(B) & valid(C) & valid(D)) {
return {A, B, C, D};
}
return "";
}
auto SuperFamicom::romSize() const -> uint {
//subtract appended firmware size, if firmware is present
if((size() & 0x7fff) == 0x100) return size() - 0x100;
if((size() & 0x7fff) == 0xc00) return size() - 0xc00;
if((size() & 0x7fff) == 0x2000) return size() - 0x2000;
if((size() & 0xffff) == 0xd000) return size() - 0xd000;
if((size() & 0x3ffff) == 0x28000) return size() - 0x28000;
return size();
}
auto SuperFamicom::programRomSize() const -> uint {
if(board().beginsWith("SPC7110-")) return 0x100000;
if(board().beginsWith("EXSPC7110-")) return 0x100000;
return romSize();
}
auto SuperFamicom::dataRomSize() const -> uint {
if(board().beginsWith("SPC7110-")) return romSize() - 0x100000;
if(board().beginsWith("EXSPC7110-")) return 0x500000;
return 0;
}
auto SuperFamicom::expansionRomSize() const -> uint {
if(board().beginsWith("EXSPC7110-")) return 0x100000;
return 0;
}
auto SuperFamicom::firmwareRomSize() const -> uint {
return size() - romSize();
}
auto SuperFamicom::ramSize() const -> uint {
auto ramSize = data[headerAddress + 0x28] & 15;
if(ramSize > 8) ramSize = 8;
if(ramSize > 0) return 1024 << ramSize;
return 0;
}
auto SuperFamicom::expansionRamSize() const -> uint {
if(data[headerAddress + 0x2a] == 0x33) {
auto ramSize = data[headerAddress + 0x0d] & 15;
if(ramSize > 8) ramSize = 8;
if(ramSize > 0) return 1024 << ramSize;
}
if((data[headerAddress + 0x26] >> 4) == 1) {
//GSU: Starfox / Starwing lacks an extended header; but still has expansion RAM
return 0x8000;
}
return 0;
}
auto SuperFamicom::nonVolatile() const -> bool {
auto cartridgeTypeLo = data[headerAddress + 0x26] & 15;
return cartridgeTypeLo == 0x2 || cartridgeTypeLo == 0x5 || cartridgeTypeLo == 0x6;
}
auto SuperFamicom::scoreHeader(uint address) -> uint {
int score = 0;
if(size() < address + 0x50) return score;
uint8_t mapMode = data[address + 0x25] & ~0x10; //ignore FastROM bit
uint16_t complement = data[address + 0x2c] << 0 | data[address + 0x2d] << 8;
uint16_t checksum = data[address + 0x2e] << 0 | data[address + 0x2f] << 8;
uint16_t resetVector = data[address + 0x4c] << 0 | data[address + 0x4d] << 8;
if(resetVector < 0x8000) return score; //$00:0000-7fff is never ROM data
uint8_t opcode = data[(address & ~0x7fff) | (resetVector & 0x7fff)]; //first instruction executed
//most likely opcodes
if(opcode == 0x78 //sei
|| opcode == 0x18 //clc (clc; xce)
|| opcode == 0x38 //sec (sec; xce)
|| opcode == 0x9c //stz $nnnn (stz $4200)
|| opcode == 0x4c //jmp $nnnn
|| opcode == 0x5c //jml $nnnnnn
) score += 8;
//plausible opcodes
if(opcode == 0xc2 //rep #$nn
|| opcode == 0xe2 //sep #$nn
|| opcode == 0xad //lda $nnnn
|| opcode == 0xae //ldx $nnnn
|| opcode == 0xac //ldy $nnnn
|| opcode == 0xaf //lda $nnnnnn
|| opcode == 0xa9 //lda #$nn
|| opcode == 0xa2 //ldx #$nn
|| opcode == 0xa0 //ldy #$nn
|| opcode == 0x20 //jsr $nnnn
|| opcode == 0x22 //jsl $nnnnnn
) score += 4;
//implausible opcodes
if(opcode == 0x40 //rti
|| opcode == 0x60 //rts
|| opcode == 0x6b //rtl
|| opcode == 0xcd //cmp $nnnn
|| opcode == 0xec //cpx $nnnn
|| opcode == 0xcc //cpy $nnnn
) score -= 4;
//least likely opcodes
if(opcode == 0x00 //brk #$nn
|| opcode == 0x02 //cop #$nn
|| opcode == 0xdb //stp
|| opcode == 0x42 //wdm
|| opcode == 0xff //sbc $nnnnnn,x
) score -= 8;
if(checksum + complement == 0xffff) score += 4;
if(address == 0x7fb0 && mapMode == 0x20) score += 2;
if(address == 0xffb0 && mapMode == 0x21) score += 2;
return max(0, score);
}
auto SuperFamicom::firmwareARM() const -> string {
return "ST018";
}
auto SuperFamicom::firmwareEXNEC() const -> string {
if(title() == "EXHAUST HEAT2") return "ST010";
if(title() == "F1 ROC II") return "ST010";
if(title() == "2DAN MORITA SHOUGI") return "ST011";
return "ST010";
}
auto SuperFamicom::firmwareGB() const -> string {
if(title() == "Super GAMEBOY") return "SGB1";
if(title() == "Super GAMEBOY2") return "SGB2";
return "SGB1";
}
auto SuperFamicom::firmwareHITACHI() const -> string {
return "Cx4";
}
auto SuperFamicom::firmwareNEC() const -> string {
if(title() == "PILOTWINGS") return "DSP1";
if(title() == "DUNGEON MASTER") return "DSP2";
if(title() == "SDガンダムGX") return "DSP3";
if(title() == "PLANETS CHAMP TG3000") return "DSP4";
if(title() == "TOP GEAR 3000") return "DSP4";
return "DSP1B";
}
}

202
bsnes/lzma/7z.h Normal file
View File

@@ -0,0 +1,202 @@
/* 7z.h -- 7z interface
2017-04-03 : Igor Pavlov : Public domain */
#ifndef __7Z_H
#define __7Z_H
#include "7zTypes.h"
EXTERN_C_BEGIN
#define k7zStartHeaderSize 0x20
#define k7zSignatureSize 6
extern const Byte k7zSignature[k7zSignatureSize];
typedef struct
{
const Byte *Data;
size_t Size;
} CSzData;
/* CSzCoderInfo & CSzFolder support only default methods */
typedef struct
{
size_t PropsOffset;
UInt32 MethodID;
Byte NumStreams;
Byte PropsSize;
} CSzCoderInfo;
typedef struct
{
UInt32 InIndex;
UInt32 OutIndex;
} CSzBond;
#define SZ_NUM_CODERS_IN_FOLDER_MAX 4
#define SZ_NUM_BONDS_IN_FOLDER_MAX 3
#define SZ_NUM_PACK_STREAMS_IN_FOLDER_MAX 4
typedef struct
{
UInt32 NumCoders;
UInt32 NumBonds;
UInt32 NumPackStreams;
UInt32 UnpackStream;
UInt32 PackStreams[SZ_NUM_PACK_STREAMS_IN_FOLDER_MAX];
CSzBond Bonds[SZ_NUM_BONDS_IN_FOLDER_MAX];
CSzCoderInfo Coders[SZ_NUM_CODERS_IN_FOLDER_MAX];
} CSzFolder;
SRes SzGetNextFolderItem(CSzFolder *f, CSzData *sd);
typedef struct
{
UInt32 Low;
UInt32 High;
} CNtfsFileTime;
typedef struct
{
Byte *Defs; /* MSB 0 bit numbering */
UInt32 *Vals;
} CSzBitUi32s;
typedef struct
{
Byte *Defs; /* MSB 0 bit numbering */
// UInt64 *Vals;
CNtfsFileTime *Vals;
} CSzBitUi64s;
#define SzBitArray_Check(p, i) (((p)[(i) >> 3] & (0x80 >> ((i) & 7))) != 0)
#define SzBitWithVals_Check(p, i) ((p)->Defs && ((p)->Defs[(i) >> 3] & (0x80 >> ((i) & 7))) != 0)
typedef struct
{
UInt32 NumPackStreams;
UInt32 NumFolders;
UInt64 *PackPositions; // NumPackStreams + 1
CSzBitUi32s FolderCRCs; // NumFolders
size_t *FoCodersOffsets; // NumFolders + 1
UInt32 *FoStartPackStreamIndex; // NumFolders + 1
UInt32 *FoToCoderUnpackSizes; // NumFolders + 1
Byte *FoToMainUnpackSizeIndex; // NumFolders
UInt64 *CoderUnpackSizes; // for all coders in all folders
Byte *CodersData;
} CSzAr;
UInt64 SzAr_GetFolderUnpackSize(const CSzAr *p, UInt32 folderIndex);
SRes SzAr_DecodeFolder(const CSzAr *p, UInt32 folderIndex,
ILookInStream *stream, UInt64 startPos,
Byte *outBuffer, size_t outSize,
ISzAllocPtr allocMain);
typedef struct
{
CSzAr db;
UInt64 startPosAfterHeader;
UInt64 dataPos;
UInt32 NumFiles;
UInt64 *UnpackPositions; // NumFiles + 1
// Byte *IsEmptyFiles;
Byte *IsDirs;
CSzBitUi32s CRCs;
CSzBitUi32s Attribs;
// CSzBitUi32s Parents;
CSzBitUi64s MTime;
CSzBitUi64s CTime;
UInt32 *FolderToFile; // NumFolders + 1
UInt32 *FileToFolder; // NumFiles
size_t *FileNameOffsets; /* in 2-byte steps */
Byte *FileNames; /* UTF-16-LE */
} CSzArEx;
#define SzArEx_IsDir(p, i) (SzBitArray_Check((p)->IsDirs, i))
#define SzArEx_GetFileSize(p, i) ((p)->UnpackPositions[(i) + 1] - (p)->UnpackPositions[i])
void SzArEx_Init(CSzArEx *p);
void SzArEx_Free(CSzArEx *p, ISzAllocPtr alloc);
UInt64 SzArEx_GetFolderStreamPos(const CSzArEx *p, UInt32 folderIndex, UInt32 indexInFolder);
int SzArEx_GetFolderFullPackSize(const CSzArEx *p, UInt32 folderIndex, UInt64 *resSize);
/*
if dest == NULL, the return value specifies the required size of the buffer,
in 16-bit characters, including the null-terminating character.
if dest != NULL, the return value specifies the number of 16-bit characters that
are written to the dest, including the null-terminating character. */
size_t SzArEx_GetFileNameUtf16(const CSzArEx *p, size_t fileIndex, UInt16 *dest);
/*
size_t SzArEx_GetFullNameLen(const CSzArEx *p, size_t fileIndex);
UInt16 *SzArEx_GetFullNameUtf16_Back(const CSzArEx *p, size_t fileIndex, UInt16 *dest);
*/
/*
SzArEx_Extract extracts file from archive
*outBuffer must be 0 before first call for each new archive.
Extracting cache:
If you need to decompress more than one file, you can send
these values from previous call:
*blockIndex,
*outBuffer,
*outBufferSize
You can consider "*outBuffer" as cache of solid block. If your archive is solid,
it will increase decompression speed.
If you use external function, you can declare these 3 cache variables
(blockIndex, outBuffer, outBufferSize) as static in that external function.
Free *outBuffer and set *outBuffer to 0, if you want to flush cache.
*/
SRes SzArEx_Extract(
const CSzArEx *db,
ILookInStream *inStream,
UInt32 fileIndex, /* index of file */
UInt32 *blockIndex, /* index of solid block */
Byte **outBuffer, /* pointer to pointer to output buffer (allocated with allocMain) */
size_t *outBufferSize, /* buffer size for output buffer */
size_t *offset, /* offset of stream for required file in *outBuffer */
size_t *outSizeProcessed, /* size of file in *outBuffer */
ISzAllocPtr allocMain,
ISzAllocPtr allocTemp);
/*
SzArEx_Open Errors:
SZ_ERROR_NO_ARCHIVE
SZ_ERROR_ARCHIVE
SZ_ERROR_UNSUPPORTED
SZ_ERROR_MEM
SZ_ERROR_CRC
SZ_ERROR_INPUT_EOF
SZ_ERROR_FAIL
*/
SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream,
ISzAllocPtr allocMain, ISzAllocPtr allocTemp);
EXTERN_C_END
#endif

80
bsnes/lzma/7zAlloc.c Normal file
View File

@@ -0,0 +1,80 @@
/* 7zAlloc.c -- Allocation functions
2017-04-03 : Igor Pavlov : Public domain */
#include "Precomp.h"
#include <stdlib.h>
#include "7zAlloc.h"
/* #define _SZ_ALLOC_DEBUG */
/* use _SZ_ALLOC_DEBUG to debug alloc/free operations */
#ifdef _SZ_ALLOC_DEBUG
#ifdef _WIN32
#include <windows.h>
#endif
#include <stdio.h>
int g_allocCount = 0;
int g_allocCountTemp = 0;
#endif
void *SzAlloc(ISzAllocPtr p, size_t size)
{
UNUSED_VAR(p);
if (size == 0)
return 0;
#ifdef _SZ_ALLOC_DEBUG
fprintf(stderr, "\nAlloc %10u bytes; count = %10d", (unsigned)size, g_allocCount);
g_allocCount++;
#endif
return malloc(size);
}
void SzFree(ISzAllocPtr p, void *address)
{
UNUSED_VAR(p);
#ifdef _SZ_ALLOC_DEBUG
if (address != 0)
{
g_allocCount--;
fprintf(stderr, "\nFree; count = %10d", g_allocCount);
}
#endif
free(address);
}
void *SzAllocTemp(ISzAllocPtr p, size_t size)
{
UNUSED_VAR(p);
if (size == 0)
return 0;
#ifdef _SZ_ALLOC_DEBUG
fprintf(stderr, "\nAlloc_temp %10u bytes; count = %10d", (unsigned)size, g_allocCountTemp);
g_allocCountTemp++;
#ifdef _WIN32
return HeapAlloc(GetProcessHeap(), 0, size);
#endif
#endif
return malloc(size);
}
void SzFreeTemp(ISzAllocPtr p, void *address)
{
UNUSED_VAR(p);
#ifdef _SZ_ALLOC_DEBUG
if (address != 0)
{
g_allocCountTemp--;
fprintf(stderr, "\nFree_temp; count = %10d", g_allocCountTemp);
}
#ifdef _WIN32
HeapFree(GetProcessHeap(), 0, address);
return;
#endif
#endif
free(address);
}

19
bsnes/lzma/7zAlloc.h Normal file
View File

@@ -0,0 +1,19 @@
/* 7zAlloc.h -- Allocation functions
2017-04-03 : Igor Pavlov : Public domain */
#ifndef __7Z_ALLOC_H
#define __7Z_ALLOC_H
#include "7zTypes.h"
EXTERN_C_BEGIN
void *SzAlloc(ISzAllocPtr p, size_t size);
void SzFree(ISzAllocPtr p, void *address);
void *SzAllocTemp(ISzAllocPtr p, size_t size);
void SzFreeTemp(ISzAllocPtr p, void *address);
EXTERN_C_END
#endif

1771
bsnes/lzma/7zArcIn.c Normal file

File diff suppressed because it is too large Load Diff

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